Files
Codeman/src/local-model/format.ts
T
Codeman maintainer 492df776d0 feat(local-model): judge the fit on the measured peak and rank a tested Mac first
The catalogue rows carry the figures measured on a Mac (the MLX build of
qwen3.6:35b-a3b is 23,105,798,956 bytes, about 37 KB held per context token,
about 3 GB of runner overhead, about 5 GB more for a moment while a long
prompt is processed). A model's need is now its peak. Rows that were not
measured use the same per-token and overhead figures and say `estimated`.

qwen3.6:35b-a3b gets the tested entry for an Apple M5 Pro with 48 GB and
OpenCode, with its measured 67.7 tokens per second, so that row may say
"Tested on a Mac like this".

The ranking changes with it: a pair tested on a Mac like this one comes
first, then speed (ok, unknown, slow), then model rank, harness rank and the
larger context. The fit grade decides whether a row is listed and what it
says, it no longer sorts, so a model is offered at the largest context it
fits at. recommend() takes the budget the runtime measured
(`budgetSource: measured`), and modelFitsBudget() judges one model against it.

Candidate rows now always carry `needSource`, `harnesses`, `installBytes`,
`installGb` and, where one applies, `tag`. `kvBytesPerToken` and the prompt
cache tiers are gone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 22:16:37 +01:00

49 lines
2.3 KiB
TypeScript

/**
* @fileoverview The formatters of the local model recommendation, and its byte units.
*
* `formatGb()` is the ONE place a byte count becomes a size a person reads: decimal GB (the
* unit the Finder and the model registry use). `hardware.ts` and `recommend.ts` call it to fill
* the `...Gb` text fields of the payload (`memGb`, `freeDiskGb`, `needGb`, `downloadGb`,
* `budgetGb`, and the size slots of a `LocalModelMessage`). Nothing else may turn bytes into a
* size for display, the browser included: it prints the text it was sent.
*
* GiB and MiB exist only for the budget math (reserves, the wired limit, the device total the
* runtime reports) and never reach a person, with one exception: `memClassGb()`, the memory
* size a Mac is sold as, is how the card names the installed memory ("48 GB memory", what
* About This Mac shows). It is a name of the machine, not a computed size: `formatGb()` of the
* same bytes reads 51.5, which no owner recognises. Every computed size (need, budget,
* download, free disk) is `formatGb()`.
*
* Pure: no IO, no state.
*/
/** One decimal gigabyte, the unit a person reads. */
export const GB = 1_000_000_000;
/** One binary gigabyte. Budget math only. */
export const GIB = 1024 ** 3;
/** One binary megabyte. Budget math only. */
export const MIB = 1024 ** 2;
/**
* A byte count as decimal GB text, WITHOUT the unit: one decimal below 100 ('6.6', '17.7',
* '25.5'), a whole number from 100 ('212'). The caller's sentence supplies ' GB'.
* Anything that is not a positive finite number reads '0.0'.
*/
export function formatGb(bytes: number): string {
if (!Number.isFinite(bytes) || bytes <= 0) return '0.0';
const tenths = Math.round((bytes / GB) * 10) / 10;
return tenths >= 100 ? String(Math.round(bytes / GB)) : tenths.toFixed(1);
}
/**
* The memory size a Mac is sold as: whole binary gigabytes (17179869184 bytes is 16). This is
* `HardwareFacts.memClassGb`: the key of the tier rules ("below 36 GB") and of `TestedOn.memGb`,
* and what the card and the `noneMemory` sentence call the installed memory. It names a class
* of Mac; 0 means the size is unknown and is never shown. Any other size for display is
* `formatGb()`.
*/
export function memClassGb(memBytes: number): number {
if (!Number.isFinite(memBytes) || memBytes <= 0) return 0;
return Math.round(memBytes / GIB);
}