From 772f6a3d5ea95b09bdd0a3f2645a32769b739765 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 26 Aug 2026 10:24:03 -0700 Subject: [PATCH] fix(deepinfra): preserve manifest reasoning during live discovery (#129998) --- extensions/deepinfra/provider-models.test.ts | 83 ++++++++++++++++++++ extensions/deepinfra/provider-models.ts | 18 ++--- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/extensions/deepinfra/provider-models.test.ts b/extensions/deepinfra/provider-models.test.ts index 8e50b3252750..9fadbae9353c 100644 --- a/extensions/deepinfra/provider-models.test.ts +++ b/extensions/deepinfra/provider-models.test.ts @@ -263,6 +263,89 @@ describe("discoverDeepInfraModels (chat-only shim)", () => { }); }); + it("preserves bundled reasoning and compat while keeping live model facts authoritative", async () => { + const rows = [ + makeAgentModelEntry({ + id: "deepseek-ai/DeepSeek-V4-Pro", + metadata: { + context_length: 96000, + max_tokens: 4096, + pricing: { input_tokens: 4, output_tokens: 8, cache_read_tokens: 0.4 }, + tags: ["chat"], + }, + }), + makeAgentModelEntry({ + id: "stepfun-ai/Step-3.7-Flash", + metadata: { + context_length: 192000, + max_tokens: 16384, + pricing: { input_tokens: 0.2, output_tokens: 1.15 }, + tags: ["chat", "vlm", "vision"], + }, + }), + makeAgentModelEntry({ + id: "deepseek-ai/DeepSeek-V3.2", + metadata: { + context_length: 64000, + max_tokens: 8192, + pricing: { input_tokens: 1, output_tokens: 2 }, + tags: ["chat", "reasoning"], + }, + }), + makeAgentModelEntry({ + id: "unlisted/no-reasoning", + metadata: { context_length: 32000, max_tokens: 2048, pricing: {}, tags: ["chat"] }, + }), + makeAgentModelEntry({ + id: "unlisted/with-reasoning", + metadata: { + context_length: 48000, + max_tokens: 4096, + pricing: {}, + tags: ["chat", "reasoning_effort"], + }, + }), + ]; + const mockFetch = vi.fn().mockResolvedValue(jsonResponse({ data: rows })); + DEEPINFRA_MODEL_CATALOG.push(DEEPINFRA_MODEL_CATALOG[0]!); + + try { + await withFetchPathTest(mockFetch, { DEEPINFRA_API_KEY: "sk-test" }, async () => { + const models = await discoverDeepInfraModels(); + + expect(models.slice(0, rows.length)).toMatchObject([ + { + id: "deepseek-ai/DeepSeek-V4-Pro", + reasoning: true, + input: ["text"], + contextWindow: 96000, + maxTokens: 4096, + cost: { input: 4, output: 8, cacheRead: 0.4, cacheWrite: 0 }, + compat: { + codeMode: "capable", + supportsUsageInStreaming: true, + thinkingFormat: "deepseek", + }, + }, + { + id: "stepfun-ai/Step-3.7-Flash", + reasoning: true, + input: ["text", "image"], + contextWindow: 192000, + maxTokens: 16384, + cost: { input: 0.2, output: 1.15, cacheRead: 0, cacheWrite: 0 }, + }, + { id: "deepseek-ai/DeepSeek-V3.2", reasoning: false }, + { id: "unlisted/no-reasoning", reasoning: false }, + { id: "unlisted/with-reasoning", reasoning: true }, + ]); + expect(new Set(models.map((model) => model.id)).size).toBe(models.length); + }); + } finally { + DEEPINFRA_MODEL_CATALOG.pop(); + } + }); + it("skips entries with no metadata or no surface tag, and deduplicates ids", async () => { const mockFetch = vi.fn().mockResolvedValue( jsonResponse({ diff --git a/extensions/deepinfra/provider-models.ts b/extensions/deepinfra/provider-models.ts index 20aa29b85b0c..f39cc37df213 100644 --- a/extensions/deepinfra/provider-models.ts +++ b/extensions/deepinfra/provider-models.ts @@ -359,13 +359,15 @@ export function buildDeepInfraModelDefinition(model: ModelDefinitionConfig): Mod } function chatSurfaceModelToModelDefinition(model: DeepInfraSurfaceModel): ModelDefinitionConfig { + const manifestModel = DEEPINFRA_MODEL_CATALOG.find((entry) => entry.id === model.id); const input: Array<"text" | "image"> = model.tags.includes("vlm") ? ["text", "image"] : ["text"]; const reasoning = model.tags.includes("reasoning") || model.tags.includes("reasoning_effort"); return buildDeepInfraModelDefinition({ id: model.id, name: model.name, - reasoning, + reasoning: manifestModel?.reasoning ?? reasoning, input, + ...(manifestModel?.compat ? { compat: manifestModel.compat } : {}), contextWindow: model.contextWindow ?? DEEPINFRA_DEFAULT_CONTEXT_WINDOW, maxTokens: model.maxTokens ?? DEEPINFRA_DEFAULT_MAX_TOKENS, cost: { @@ -480,14 +482,10 @@ export async function discoverDeepInfraModels(options?: { } const liveModels = chatModels.map(chatSurfaceModelToModelDefinition); const seen = new Set(liveModels.map((model) => model.id)); - const manifestModels = DEEPINFRA_MODEL_CATALOG.map(buildDeepInfraModelDefinition).filter( - (model) => { - if (seen.has(model.id)) { - return false; - } - seen.add(model.id); - return true; - }, - ); + const manifestModels = DEEPINFRA_MODEL_CATALOG.filter((model) => { + const unseen = !seen.has(model.id); + seen.add(model.id); + return unseen; + }).map(buildDeepInfraModelDefinition); return [...liveModels, ...manifestModels]; }