From 5a560fc9e1e3ed070040655f0bf66c56fcf3baaa Mon Sep 17 00:00:00 2001 From: TMW Date: Sun, 2 Aug 2026 15:00:22 +0800 Subject: [PATCH 1/2] fix(deepseek): map max thinking level to max instead of high MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeepSeek adapter has a bug where the catalog declares 'max' as a supported thinking level for deepseek-v4-flash and deepseek-v4-pro, but both DEEPSEEK_THINKING_LEVEL_MAP and mapDeepSeekReasoningEffort only map 'xhigh' → 'max'. Any non-xhigh value (including 'max') falls through to 'high', making the UI's '最高' setting behave identically to '高'. Fix: - Add max: 'max' to DEEPSEEK_THINKING_LEVEL_MAP so the wire value is correct when the catalog declares max support. - Update mapDeepSeekReasoningEffort to treat both 'xhigh' and 'max' as 'max', so the payload adapter sends reasoning_effort: 'max' when the user selects the highest level. This affects all three call sites: 1. normalizeDeepSeekOpenAIPayload (OpenAI-compat path) 2. normalizeDeepSeekAnthropicPayload (Anthropic-compat path) 3. resolveDeepSeekAnthropicThinkingRuntime in streamByApi.ts --- crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts b/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts index 91df5fdae..f5ccc381a 100644 --- a/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts +++ b/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts @@ -20,6 +20,7 @@ export const DEEPSEEK_THINKING_LEVEL_MAP: Model<"openai-completions">["thinkingL low: "high", medium: "high", high: "high", + max: "max", xhigh: "max", }; @@ -150,7 +151,7 @@ export function mapDeepSeekReasoningEffort( reasoning: SimpleStreamOptions["reasoning"] | undefined, ) { if (!reasoning) return undefined; - return reasoning === "xhigh" ? "max" : "high"; + return reasoning === "xhigh" || reasoning === "max" ? "max" : "high"; } function sanitizeTextValue(value: string) { From 39fa0bd6ff66adcb7ee05fe541eeb726f1f9bbfc Mon Sep 17 00:00:00 2001 From: TMW Date: Sun, 2 Aug 2026 16:01:16 +0800 Subject: [PATCH 2/2] test(deepseek): add regression for reasoning=max -> reasoning_effort=max --- .../test/providers/request-options.test.mjs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/crates/agent-gui/test/providers/request-options.test.mjs b/crates/agent-gui/test/providers/request-options.test.mjs index ca5ad7f5a..aad5703c4 100644 --- a/crates/agent-gui/test/providers/request-options.test.mjs +++ b/crates/agent-gui/test/providers/request-options.test.mjs @@ -610,6 +610,64 @@ test("DeepSeek OpenAI payload adapter injects thinking and reasoning_content", a assert.equal(adapted.messages[0].reasoning_content, ""); }); +test("DeepSeek OpenAI payload adapter maps reasoning=max to reasoning_effort=max (regression)", async () => { + let captured; + const localLoader = createTsModuleLoader({ + mocks: { + "@earendil-works/pi-ai/api/openai-completions": { + stream(model, context, options) { + captured = { model, context, options }; + return createMockAssistantStream(); + }, + }, + }, + }); + const localProviders = localLoader.loadModule("src/lib/providers/llm.ts"); + const model = localProviders.createModelFromConfig( + "codex", + "deepseek-v4-pro", + "https://api.deepseek.com", + "openai-responses", + ); + + // 选 max 档 -> clampOpenAIReasoningEffort 保留 max -> payload 发 max + const result = localProviders.streamSimpleByApi( + model, + { messages: [] }, + { reasoning: "max", toolChoice: "auto" }, + ); + assert.equal(typeof result.result, "function"); + assert.equal(typeof captured.options.onPayload, "function"); + assert.equal( + captured.options.reasoningEffort, + "max", + "clampOpenAIReasoningEffort should preserve max for deepseek-v4-pro", + ); + + const adapted = await captured.options.onPayload( + { + messages: [ + { + role: "assistant", + content: null, + tool_calls: [ + { + id: "call_1", + type: "function", + function: { name: "Read", arguments: "{}" }, + }, + ], + }, + ], + }, + model, + ); + + assert.deepEqual(adapted.thinking, { type: "enabled" }); + assert.equal(adapted.reasoning_effort, "max", "wire reasoning_effort should be max when UI selects max"); + assert.equal(adapted.messages[0].reasoning_content, ""); +}); + test("DeepSeek Anthropic streamSimpleByApi strips aborted tool calls before conversion", () => { const { localProviders, state } = loadProvidersWithCapturedAnthropicStream(); const model = createDeepSeekAnthropicModel();