diff --git a/frontend/src/lib/components/copilot/modelConfig.ts b/frontend/src/lib/components/copilot/modelConfig.ts index c957e1a06e..2f6dd61a49 100644 --- a/frontend/src/lib/components/copilot/modelConfig.ts +++ b/frontend/src/lib/components/copilot/modelConfig.ts @@ -150,6 +150,11 @@ export function buildModelMatchers( // a shipped default; `gpt-5-preview-pro` would be a different one again). // Off by default: for a context window an inherited value is a safe // approximation, for a price it is a wrong number. + // A further revision segment (`gpt-5` vs `gpt-5-4-mini`) is a different model + // too, and the entry-ends-on-a-digit guard above does not catch it once the + // separator is normalized. Only a short segment: a date is digits as well + // (`-20251101`) and stays a decoration. + strictVariants ? '(?!-\\d{1,3}(?:$|-))' : '', strictVariants ? `(?!-(?!(?:v\\d|${DECORATIVE_SUFFIXES.join('|')})$)[a-z])` : '' diff --git a/frontend/src/lib/components/copilot/modelPricing.test.ts b/frontend/src/lib/components/copilot/modelPricing.test.ts index 6f0362f1d0..b873379679 100644 --- a/frontend/src/lib/components/copilot/modelPricing.test.ts +++ b/frontend/src/lib/components/copilot/modelPricing.test.ts @@ -36,6 +36,11 @@ describe('resolveModelPrice', () => { expect(resolveModelPrice('openai', 'gpt-5-pro', undefined)).toBeUndefined() expect(resolveModelPrice('openai', 'gpt-5.6', undefined)).toBeUndefined() expect(resolveModelPrice('googleai', 'gemini-3.1', undefined)).toBeUndefined() + // A revision carrying a variant has to be caught by the matcher, not by an + // explicit entry: `gpt-5.4-mini` cannot match the `gpt-5.4` one (the `-mini` + // makes it a sub-model), so nothing but the guard stops it reaching `gpt-5`. + expect(resolveModelPrice('openai', 'gpt-5.4-mini', undefined)).toBeUndefined() + expect(resolveModelPrice('openai', 'gpt-5.5-pro', undefined)).toBeUndefined() }) it('still resolves the route decorations that name the same model', () => { @@ -43,6 +48,8 @@ describe('resolveModelPrice', () => { // not sub-models. `claude-3-5-haiku-latest` is a shipped picker default, so // unpricing it would silently disable cost tracking out of the box. expect(resolveModelPrice('anthropic', 'claude-opus-4-5-20251101', undefined)?.price.input).toBe(5) + // The revision guard must not swallow a date, which is digits too. + expect(resolveModelPrice('openai', 'gpt-5-2026-01-01', undefined)?.price.input).toBe(1.25) expect( resolveModelPrice('bedrock', 'anthropic.claude-sonnet-4-6-20250101-v1:0', undefined)?.price .input