diff --git a/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts b/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts index b5c42ddba..f064b4e43 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) { diff --git a/crates/agent-gui/test/providers/request-options.test.mjs b/crates/agent-gui/test/providers/request-options.test.mjs index f64305120..39f2499e1 100644 --- a/crates/agent-gui/test/providers/request-options.test.mjs +++ b/crates/agent-gui/test/providers/request-options.test.mjs @@ -532,6 +532,67 @@ test("Codex Chat Completions streams forward reasoning effort", async () => { assert.equal(captured.options.toolChoice, "auto"); }); +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-completions", + ); + + 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();