fix(usage): clarify static pricing fallback

This commit is contained in:
Tam Nhu Tran
2026-04-28 16:26:27 -04:00
parent c7141b3d3a
commit 42fc5281a1
2 changed files with 45 additions and 17 deletions
+35 -17
View File
@@ -758,16 +758,25 @@ const UNKNOWN_MODEL_PRICING: ModelPricing = {
// ============================================================================ // ============================================================================
/** /**
* Normalize model name for matching * Strip provider prefixes used by routing/catalog metadata.
* Handles variations like provider prefixes and case differences * The CCS static table remains model-keyed, so static fallback normalizes
* provider-qualified model IDs before checking aliases.
*/
function stripProviderPrefix(model: string): string {
const trimmed = model.trim();
const slashIndex = trimmed.indexOf('/');
if (slashIndex <= 0) {
return trimmed;
}
return trimmed.slice(slashIndex + 1);
}
/**
* Normalize model name for matching.
* Handles variations like provider prefixes and case differences.
*/ */
function normalizeModelName(model: string): string { function normalizeModelName(model: string): string {
// Remove provider prefixes (e.g., "anthropic/claude-..." -> "claude-...") return stripProviderPrefix(model).toLowerCase();
const normalized = model
.trim()
.toLowerCase()
.replace(/^[^/]+\//, '');
return normalized;
} }
/** /**
@@ -835,6 +844,20 @@ function getDirectOrAliasPricing(model: string): ModelPricing | undefined {
return undefined; return undefined;
} }
function getCcsStaticPricing(model: string): ModelPricing | undefined {
const staticPricing = getDirectOrAliasPricing(model);
if (staticPricing !== undefined) {
return staticPricing;
}
const providerlessModel = stripProviderPrefix(model);
if (providerlessModel !== model.trim()) {
return getDirectOrAliasPricing(providerlessModel);
}
return undefined;
}
function hasProviderContext(model: string, options: PricingLookupOptions): boolean { function hasProviderContext(model: string, options: PricingLookupOptions): boolean {
return Boolean(options.provider || /^[^/]+\//.test(model.trim())); return Boolean(options.provider || /^[^/]+\//.test(model.trim()));
} }
@@ -852,14 +875,9 @@ export function getModelPricing(model: string, options: PricingLookupOptions = {
} }
} }
const exactPricing = PRICING_REGISTRY[model]; const ccsStaticPricing = getCcsStaticPricing(model);
if (exactPricing !== undefined) { if (ccsStaticPricing !== undefined) {
return exactPricing; return ccsStaticPricing;
}
const directOrAliasPricing = getDirectOrAliasPricing(model);
if (directOrAliasPricing !== undefined) {
return directOrAliasPricing;
} }
const modelsDevPricing = resolveModelsDevPricing(model, options); const modelsDevPricing = resolveModelsDevPricing(model, options);
@@ -914,7 +932,7 @@ export function getKnownModels(): string[] {
*/ */
export function hasCustomPricing(model: string, options: PricingLookupOptions = {}): boolean { export function hasCustomPricing(model: string, options: PricingLookupOptions = {}): boolean {
return ( return (
getDirectOrAliasPricing(model) !== undefined || getCcsStaticPricing(model) !== undefined ||
resolveModelsDevPricing(model, options) !== undefined resolveModelsDevPricing(model, options) !== undefined
); );
} }
+10
View File
@@ -388,6 +388,16 @@ describe('model-pricing', () => {
expect(getModelPricing('gpt-4o').inputPerMillion).toBe(2.5); expect(getModelPricing('gpt-4o').inputPerMillion).toBe(2.5);
}); });
it('falls back to CCS static pricing when provider-aware models.dev lookup misses a known model', () => {
const staticPricing = getModelPricing('claude-sonnet-4-5');
expect(getModelPricing('anthropic/claude-sonnet-4-5')).toEqual(staticPricing);
expect(getModelPricing('claude-sonnet-4-5', { provider: 'anthropic' })).toEqual(
staticPricing
);
expect(hasCustomPricing('anthropic/claude-sonnet-4-5')).toBe(true);
});
it('does not use ambiguous model-only models.dev matches', () => { it('does not use ambiguous model-only models.dev matches', () => {
const pricing = getModelPricing('gpt-5.5'); const pricing = getModelPricing('gpt-5.5');
expect(pricing).toEqual(getModelPricing('unknown-model-xyz')); expect(pricing).toEqual(getModelPricing('unknown-model-xyz'));