From bb0f4aa602f332838015ce66bdb0a94d8206dc93 Mon Sep 17 00:00:00 2001 From: Vegard Stikbakke Date: Fri, 18 Sep 2026 10:43:20 +0200 Subject: [PATCH] fix(ai): preserve DeepSeek V4 effort metadata closes #9485 --- packages/ai/CHANGELOG.md | 1 + packages/ai/scripts/generate-models.ts | 16 +++++++++++----- packages/ai/test/supports-xhigh.test.ts | 12 ++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index a3b63e880..03bb51b87 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixed +- Fixed DeepSeek V4.1 thinking levels on OpenRouter and OpenCode Go preserving provider effort metadata ([#9485](https://github.com/earendil-works/pi/issues/9485)). - Fixed Vercel AI Gateway replaying unsigned thinking as assistant text ([#9676](https://github.com/earendil-works/pi/issues/9676)). - Fixed Google Generative AI and Vertex AI using unsupported thinking levels when reasoning is omitted or when model capabilities differ within a Gemini family ([#9455](https://github.com/earendil-works/pi/issues/9455)). - Fixed Anthropic-compatible relays breaking signed thinking replay when they report a different response model, while preserving fallback pricing ([#9188](https://github.com/earendil-works/pi/issues/9188)). diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index b1dfcf10f..13dc43252 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -1024,7 +1024,11 @@ function applyThinkingLevelMetadata(model: Model): void { if (model.api === "anthropic-messages" && isAnthropicTemperatureUnsupportedModel(model.id)) { mergeAnthropicMessagesCompat(model, { supportsTemperature: false }); } - if (model.api === "openai-completions" && model.id.includes("deepseek-v4")) { + if ( + model.api === "openai-completions" && + model.id.includes("deepseek-v4") && + model.thinkingLevelMap === undefined + ) { mergeThinkingLevelMap( model, model.provider === "openrouter" @@ -2181,10 +2185,12 @@ async function loadModelsDevData(): Promise[]> { } } - const thinkingLevelMap = - api === "google-generative-ai" - ? getGoogleThinkingLevelMap(modelId, m.reasoning_options ?? []) - : undefined; + let thinkingLevelMap: NonNullable["thinkingLevelMap"]> | undefined; + if (api === "google-generative-ai") { + thinkingLevelMap = getGoogleThinkingLevelMap(modelId, m.reasoning_options ?? []); + } else if (variant.provider === "opencode-go" && modelId === "deepseek-v4.1-flash") { + thinkingLevelMap = getEffortThinkingLevelMap(m.reasoning_options ?? []); + } models.push({ id: modelId, name: m.name || modelId, diff --git a/packages/ai/test/supports-xhigh.test.ts b/packages/ai/test/supports-xhigh.test.ts index a1e43c40b..e0ae2629f 100644 --- a/packages/ai/test/supports-xhigh.test.ts +++ b/packages/ai/test/supports-xhigh.test.ts @@ -94,6 +94,18 @@ describe("getSupportedThinkingLevels", () => { expect(getSupportedThinkingLevels(model!)).toEqual(["off", "low", "high", "max"]); }); + it("preserves low/high/max metadata for DeepSeek V4.1 Flash on OpenRouter", () => { + const model = getModel("openrouter", "deepseek/deepseek-v4.1-flash"); + expect(model).toBeDefined(); + expect(getSupportedThinkingLevels(model!)).toEqual(["off", "low", "high", "max"]); + }); + + it("preserves low/high/max metadata for DeepSeek V4.1 Flash on opencode-go", () => { + const model = getModel("opencode-go", "deepseek-v4.1-flash"); + expect(model).toBeDefined(); + expect(getSupportedThinkingLevels(model!)).toEqual(["low", "high", "max"]); + }); + it("includes only high plus off for OpenCode Go Kimi K2.6", () => { const model = getModel("opencode-go", "kimi-k2.6"); expect(model).toBeDefined();