diff --git a/src/web-server/models-dev/pricing-resolver.ts b/src/web-server/models-dev/pricing-resolver.ts index daad1c92..08599e3c 100644 --- a/src/web-server/models-dev/pricing-resolver.ts +++ b/src/web-server/models-dev/pricing-resolver.ts @@ -91,6 +91,7 @@ function findModel(provider: ModelsDevProvider, model: string): ModelsDevModel | const normalizedEntries = new Map(); for (const [key, value] of Object.entries(models)) { + if (!value || typeof value !== 'object') continue; normalizedEntries.set(normalizeId(key), value); if (typeof value.id === 'string') normalizedEntries.set(normalizeId(value.id), value); } @@ -188,6 +189,7 @@ export function getKnownModelsDevModels(): string[] { const ids = new Set(); for (const provider of Object.values(registry)) { for (const model of Object.values(provider.models ?? {})) { + if (!model || typeof model !== 'object') continue; if (typeof model.id === 'string') ids.add(`${provider.id}/${model.id}`); } } diff --git a/tests/unit/model-pricing.test.ts b/tests/unit/model-pricing.test.ts index 779dfac9..b19e9f75 100644 --- a/tests/unit/model-pricing.test.ts +++ b/tests/unit/model-pricing.test.ts @@ -392,6 +392,26 @@ describe('model-pricing', () => { expect(pricing.outputPerMillion).toBe(0); }); + it('ignores malformed models.dev entries during provider-aware lookups', () => { + setCachedModelsDevRegistry({ + openai: { + id: 'openai', + name: 'OpenAI', + models: { + bad: null as unknown as never, + 'gpt-4o': { + id: 'gpt-4o', + name: 'GPT-4o', + cost: { input: 2.5, output: 10 }, + }, + }, + }, + }); + + expect(() => getModelPricing('gpt-4o', { provider: 'openai' })).not.toThrow(); + expect(getModelPricing('gpt-4o', { provider: 'openai' }).inputPerMillion).toBe(2.5); + }); + it('prefers provider-aware models.dev pricing over exact static table matches', () => { const pricing = getModelPricing('gpt-4o', { provider: 'github-copilot' }); expect(pricing.inputPerMillion).toBe(0);