Development
Model Normalisation
How raw model ids from tool logs resolve to canonical models with provider and family metadata.
Model Normalisation
tokenuse resolves every model identifier through one shared registry before it reaches an aggregate, report, TUI row, or desktop view. The registry gives each model a stable canonical id, display name, provider, and family. Tool adapters should preserve the model identifier they observe; they must not maintain their own display-name tables.
The implementation lives in:
src/models/registry.json: ordered data rules.src/models/mod.rs: canonicalisation, rule loading, provider ids, fallback naming, and tests.src/pricing/mod.rs: pricing lookup, which reuses the same canonical key function.
Resolution Order
models::resolve(tool_id, raw_model) applies these steps:
- Trim and lowercase the raw value.
- Remove an
@suffix, retain only the final vendor-path segment, and strip a trailing-YYYYMMDDdate. - Walk
registry.jsonfrom top to bottom. Rules withtoolonly match that adapter; the first matching exact or prefix rule wins. - If no rule matches, infer common GPT, Claude, and Gemini providers and produce a readable name. Other unknown ids are title-cased and assigned to
Otherrather than being rendered raw.
The returned identity has four fields:
| Field | Purpose |
|---|---|
canonical_id |
Aggregation key shared by equivalent raw identifiers. |
display |
Human-facing model name. |
provider |
Stable provider id and label used for grouping, colors, and icons. |
family |
Broader product family such as Opus, GPT-5 Codex, or Gemini. |
Tool-scoped rules must appear before broader rules. For example, Copilot’s openai-auto, anthropic-auto, and auto routers resolve to OpenAI, Anthropic, and GitHub identities respectively, while Cursor’s auto and default remain Cursor identities.
Registry Schema
Each entry in registry.json supports:
| Field | Required | Meaning |
|---|---|---|
tool |
No | Restrict the rule to one stable adapter id. |
match |
Yes | exact or prefix. |
keys |
Yes | Canonicalised keys tested in order. |
canonical |
No | Fold key; defaults to the first key. |
display |
Yes | Human-facing name. |
provider |
Yes | One of anthropic, openai, google, github, cursor, xai, or other. |
family |
Yes | Human-facing family label. |
Use exact when a suffix changes the product identity, and prefix when dated, preview, or tool-specific suffixes should remain in the same row. Put the most specific prefix first: gpt-5.3-codex-spark must precede gpt-5.3-codex, for example.
Adding Or Updating A Model
- Capture the raw identifier and adapter id from a fixture or parser test.
- Run it mentally through the canonicalisation rules above.
- Add the narrowest registry rule in specificity order. Reuse a canonical id only when the rows should genuinely aggregate together.
- Add or update tests in
src/models/mod.rsfor display, canonical folding, provider, and family behavior. Include vendor paths or dated ids when relevant. - Update pricing separately if the new model needs a rate. Display normalisation and pricing share canonicalisation, but a registry rule does not create a price row.
- Check the unified desktop Models page, a dedicated tool page, the TUI, and at least one report/export surface.
Adding an entirely new provider also requires a Provider variant in src/models/mod.rs, provider color tokens in DESIGN.md and desktop/src/styles/tokens.css, and an icon mapping in desktop/src/icons/. Use other until those shared surfaces are ready.
Run the focused and full checks after changing the registry:
cargo test models::tests
cargo fmt --check
cargo check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
From desktop/, also run CI=true pnpm run check and CI=true pnpm run build when provider grouping, icons, or serialized model fields change.
Pricing Boundary
models::canonical_key is the common normaliser for model identity and pricing. Pricing then applies its own tool-scoped effective rows, aliases, date windows, and fallback rules. Keep that separation deliberate:
- the model registry answers “what should this model be called and grouped with?”
- the pricing books answer “what did this model cost for this tool at this time?”
See Pricing and cache rates for pricing sources, refresh rules, and cache-rate behavior.