diff --git a/src/web-server/model-pricing.ts b/src/web-server/model-pricing.ts index dc05d2a6..935ecccf 100644 --- a/src/web-server/model-pricing.ts +++ b/src/web-server/model-pricing.ts @@ -734,7 +734,7 @@ const UNKNOWN_MODEL_PRICING: ModelPricing = { /** * Normalize model name for matching - * Handles variations like provider prefixes, case differences, and date suffixes + * Handles variations like provider prefixes and case differences */ function normalizeModelName(model: string): string { // Remove provider prefixes (e.g., "anthropic/claude-..." -> "claude-...") @@ -747,10 +747,14 @@ function normalizeModelName(model: string): string { /** * Strip trailing date suffix from model name (e.g., "-20260101") - * Only strips 8-digit YYYYMMDD suffixes at the end of the string. + * Claude session IDs can place dates either at the end or before "-thinking". */ function stripDateSuffix(model: string): string { - return model.replace(/-\d{8}$/, ''); + if (!model.startsWith('claude-')) { + return model; + } + + return model.replace(/-\d{8}(?=-thinking(?:$|:))/g, '').replace(/-\d{8}(?=$|:)/g, ''); } const NORMALIZED_PRICING_REGISTRY: Record = Object.entries( diff --git a/tests/unit/model-pricing.test.ts b/tests/unit/model-pricing.test.ts index 0317b995..ecbbdd42 100644 --- a/tests/unit/model-pricing.test.ts +++ b/tests/unit/model-pricing.test.ts @@ -120,6 +120,18 @@ describe('model-pricing', () => { expect(opus46.inputPerMillion).toBe(5.0); expect(opus46.outputPerMillion).toBe(25.0); }); + + it('should match date-stamped thinking Claude Opus 4.6 to correct pricing', () => { + const opus46 = getModelPricing('claude-opus-4-6-20260101-thinking'); + expect(opus46.inputPerMillion).toBe(5.0); + expect(opus46.outputPerMillion).toBe(25.0); + }); + + it('should match provider-prefixed date-stamped thinking model to correct pricing', () => { + const opus46 = getModelPricing('anthropic/claude-opus-4-6-20260101-thinking'); + expect(opus46.inputPerMillion).toBe(5.0); + expect(opus46.outputPerMillion).toBe(25.0); + }); }); describe('calculateCost', () => { @@ -209,6 +221,10 @@ describe('model-pricing', () => { expect(hasCustomPricing('qwen3-coder')).toBe(true); }); + it('should not treat date-stamped non-Claude IDs as deterministic aliases', () => { + expect(hasCustomPricing('qwen3-32b-20260101')).toBe(false); + }); + it('should return false for unknown models', () => { expect(hasCustomPricing('unknown-model-xyz')).toBe(false); });