From 572c992aea91c16f0de26cb8b1bca965d9c98458 Mon Sep 17 00:00:00 2001 From: "Rick Culpepper (claude)" Date: Mon, 3 Aug 2026 18:39:56 -0500 Subject: [PATCH] models: name GPT-5.6, Grok 4.5 and ClinePass slugs instead of showing raw ids Several model ids price correctly but had no SHORT_NAMES entry, so the By Model panel rendered the raw slug next to properly named siblings: `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `grok-4.5`, `qwen3.7-max`, `minimax-m3` and `mimo-v2.5-pro`. All display-only; no dollar amounts move. Notes on the less obvious ones: - The GPT-5.6 variants are listed individually rather than as a bare `gpt-5.6`. A base entry would swallow every future `gpt-5.6-*` through the prefix match and hide the variant behind a sibling's label, which is exactly what getShortModelName's version-boundary rule prevents. An unlisted variant still falls through to its raw id, and there is a test pinning that. - `grok-4.5` is the model the Grok Build harness runs and reports as `current_model_id`, so it takes the model's own name. Ids that really are `grok-build*` keep the "Grok Build" label, also covered by a test. - ClinePass routes models as `cline-pass/`. No new prefix handling was needed: getShortModelName's path fallback already strips the prefix and re-resolves the bare slug, the same way it handles `accounts/fireworks/models/`. - MiniMax M3 is mapped under both the lowercase OpenRouter slug and the capitalized spelling sessions report, since SHORT_NAMES matching is case-sensitive (the case-insensitive index covers pricing only). `mimo-v2.5-pro` remains unpriced upstream; this only gives it a name. --- src/models.ts | 21 +++++++++++++++++++++ tests/models.test.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/models.ts b/src/models.ts index 03807be..ccc2d0c 100644 --- a/src/models.ts +++ b/src/models.ts @@ -922,6 +922,27 @@ const SHORT_NAMES: Record = { 'glm-5p2': 'GLM-5.2', 'qwen3p7-plus': 'Qwen 3.7 Plus', 'kimi-k2p7-code': 'Kimi K2.7 Code', + // Ids that price correctly but had no display entry, so reports showed the + // raw slug. All display-only. The GPT-5.6 variants are listed individually + // rather than as a bare `gpt-5.6`: a base entry would swallow every future + // `gpt-5.6-*` via the prefix match and hide the variant, which is exactly + // what getShortModelName's version-boundary rule is there to prevent. + 'gpt-5.6-sol': 'GPT-5.6 Sol', + 'gpt-5.6-terra': 'GPT-5.6 Terra', + 'gpt-5.6-luna': 'GPT-5.6 Luna', + // The Grok Build harness reports the model it runs (`grok-4.5`), so this is + // the model's own name; `grok-build*` ids still resolve to "Grok Build". + 'grok-4.5': 'Grok 4.5', + // ClinePass routes models as `cline-pass/`; getShortModelName's path + // fallback strips the prefix and re-resolves the bare slug through this + // table, the same way it handles `accounts/fireworks/models/`. + 'qwen3.7-max': 'Qwen 3.7 Max', + 'mimo-v2.5-pro': 'MiMo v2.5 Pro', + // Both spellings occur in the wild: OpenRouter gap-filled keys are lowercase + // slugs while sessions report the capitalized name (see the case-insensitive + // pricing index above). SHORT_NAMES matching is case-sensitive, so map both. + 'minimax-m3': 'MiniMax M3', + 'MiniMax-M3': 'MiniMax M3', } // Sorted longest-first so more-specific prefixes match before shorter ones. diff --git a/tests/models.test.ts b/tests/models.test.ts index 476eca7..adbf9fa 100644 --- a/tests/models.test.ts +++ b/tests/models.test.ts @@ -123,6 +123,36 @@ describe('getShortModelName', () => { expect(getShortModelName('accounts/fireworks/models/some-unlisted-slug')).toBe('some-unlisted-slug') }) + it('names GPT-5.6 variants individually rather than collapsing them', () => { + expect(getShortModelName('gpt-5.6-sol')).toBe('GPT-5.6 Sol') + expect(getShortModelName('gpt-5.6-terra')).toBe('GPT-5.6 Terra') + expect(getShortModelName('gpt-5.6-luna')).toBe('GPT-5.6 Luna') + // No bare `gpt-5.6` entry exists, so an unlisted future variant must still + // fall through to its raw id rather than borrow a sibling's label. + expect(getShortModelName('gpt-5.6-unlisted')).toBe('gpt-5.6-unlisted') + }) + + it('names grok-4.5 without disturbing the Grok Build harness label', () => { + // The Grok Build CLI reports the model it runs, so the model id gets the + // model's name; ids that really are grok-build keep the harness label. + expect(getShortModelName('grok-4.5')).toBe('Grok 4.5') + expect(getShortModelName('grok-build-0.1')).toBe('Grok Build') + }) + + it('names ClinePass-routed slugs through the path fallback', () => { + // ClinePass ids arrive as `cline-pass/`; the path fallback strips the + // prefix and re-resolves the bare slug, as it does for Fireworks ids. + expect(getShortModelName('cline-pass/qwen3.7-max')).toBe('Qwen 3.7 Max') + expect(getShortModelName('cline-pass/minimax-m3')).toBe('MiniMax M3') + expect(getShortModelName('cline-pass/mimo-v2.5-pro')).toBe('MiMo v2.5 Pro') + expect(getShortModelName('cline-pass/kimi-k3')).toBe('Kimi K3') + }) + + it('names MiniMax M3 in both the lowercase-slug and capitalized spellings', () => { + expect(getShortModelName('minimax-m3')).toBe('MiniMax M3') + expect(getShortModelName('MiniMax-M3')).toBe('MiniMax M3') + }) + it('resolves Fireworks-hosted fleet models to friendly names via the path fallback', () => { // Real ids are the full Fireworks path `accounts/fireworks/models/`. expect(getShortModelName('accounts/fireworks/models/glm-5p2')).toBe('GLM-5.2')