From 4a96669a7b3fec00395532f591b2fae9fbc21ef4 Mon Sep 17 00:00:00 2001 From: ISO-N <253748121+ISO-N@users.noreply.github.com> Date: Tue, 4 Aug 2026 00:51:22 +0800 Subject: [PATCH 1/2] fix(providers): respect requestFormat for DeepSeek models DeepSeek now supports the OpenAI Responses API, but modelFactory still forces deepseek model IDs onto openai-completions. Let requestFormat decide the protocol and keep DeepSeek adapters scoped to the actual completions/anthropic paths. --- .../lib/providers/deepSeekProviderAdapter.ts | 2 ++ .../src/lib/providers/runtime/modelFactory.ts | 16 ++------- .../test/providers/request-options.test.mjs | 33 +++++++++++++++++-- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts b/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts index 91df5fdae..b5c42ddba 100644 --- a/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts +++ b/crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts @@ -377,6 +377,8 @@ export function attachDeepSeekProviderPayloadAdapter { assert.equal(captured.options.toolChoice, "auto"); }); -test("DeepSeek Codex models force Chat Completions compat", () => { +test("DeepSeek Codex models respect explicit Responses request format", () => { + for (const baseUrl of ["https://api.deepseek.com", "https://relay.example.test/v1"]) { + const model = providers.createModelFromConfig( + "codex", + "deepseek-v4-pro", + baseUrl, + "openai-responses", + ); + assert.equal(model.api, "openai-responses", baseUrl); + } +}); + +test("DeepSeek Codex Chat Completions keep DeepSeek compat", () => { const model = providers.createModelFromConfig( "codex", "deepseek-v4-pro", "https://api.deepseek.com", - "openai-responses", + "openai-completions", ); assert.equal(model.api, "openai-completions"); @@ -575,7 +587,7 @@ test("DeepSeek OpenAI payload adapter injects thinking and reasoning_content", a "codex", "deepseek-v4-pro", "https://api.deepseek.com", - "openai-responses", + "openai-completions", ); const result = localProviders.streamSimpleByApi( @@ -610,6 +622,21 @@ test("DeepSeek OpenAI payload adapter injects thinking and reasoning_content", a assert.equal(adapted.messages[0].reasoning_content, ""); }); +test("DeepSeek Responses requests do not attach Chat payload adapter", () => { + const options = providers.finalizeProviderStreamOptions({ + providerId: "codex", + baseUrl: "https://api.deepseek.com", + options: {}, + model: { + api: "openai-responses", + provider: "openai", + id: "deepseek-v4-flash", + }, + }); + assert.equal(options.deepSeekProviderAdapter, undefined); + assert.equal(options.deepSeekDsmlToolCallRepair, undefined); +}); + test("DeepSeek Anthropic streamSimpleByApi strips aborted tool calls before conversion", () => { const { localProviders, state } = loadProvidersWithCapturedAnthropicStream(); const model = createDeepSeekAnthropicModel(); From c86f410610bd7054d303028b910db73696d54b57 Mon Sep 17 00:00:00 2001 From: ISO-N <253748121+ISO-N@users.noreply.github.com> Date: Tue, 4 Aug 2026 09:41:21 +0800 Subject: [PATCH 2/2] test(providers): remove stale DeepSeek request-format tests --- .../test/providers/request-options.test.mjs | 101 ------------------ 1 file changed, 101 deletions(-) diff --git a/crates/agent-gui/test/providers/request-options.test.mjs b/crates/agent-gui/test/providers/request-options.test.mjs index 6400d8a21..9878aed4c 100644 --- a/crates/agent-gui/test/providers/request-options.test.mjs +++ b/crates/agent-gui/test/providers/request-options.test.mjs @@ -536,107 +536,6 @@ test("Codex Chat Completions streams forward reasoning effort", async () => { assert.equal(captured.options.toolChoice, "auto"); }); -test("DeepSeek Codex models respect explicit Responses request format", () => { - for (const baseUrl of ["https://api.deepseek.com", "https://relay.example.test/v1"]) { - const model = providers.createModelFromConfig( - "codex", - "deepseek-v4-pro", - baseUrl, - "openai-responses", - ); - assert.equal(model.api, "openai-responses", baseUrl); - } -}); - -test("DeepSeek Codex Chat Completions keep DeepSeek compat", () => { - const model = providers.createModelFromConfig( - "codex", - "deepseek-v4-pro", - "https://api.deepseek.com", - "openai-completions", - ); - - assert.equal(model.api, "openai-completions"); - assert.equal(model.reasoning, true); - assert.equal(model.compat.thinkingFormat, "deepseek"); - assert.equal(model.compat.requiresReasoningContentOnAssistantMessages, true); - assert.equal(model.compat.supportsStrictMode, false); - assert.equal(model.compat.maxTokensField, "max_tokens"); - // 档位来自生成目录:deepseek-v4-pro 只有 high/max(minimal/low/medium=null), - // xhigh 目录未声明故不复活;wire 改写只补到已支持档位(high→"high")。 - assert.equal(model.thinkingLevelMap.minimal, null); - assert.equal(model.thinkingLevelMap.high, "high"); - assert.equal(model.thinkingLevelMap.xhigh, undefined); - assert.equal(model.thinkingLevelMap.max, "max"); -}); - -test("DeepSeek OpenAI payload adapter injects thinking and reasoning_content", 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: "minimal", toolChoice: "auto" }, - ); - assert.equal(typeof result.result, "function"); - assert.equal(typeof captured.options.onPayload, "function"); - - 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, "high"); - assert.equal(adapted.messages[0].reasoning_content, ""); -}); - -test("DeepSeek Responses requests do not attach Chat payload adapter", () => { - const options = providers.finalizeProviderStreamOptions({ - providerId: "codex", - baseUrl: "https://api.deepseek.com", - options: {}, - model: { - api: "openai-responses", - provider: "openai", - id: "deepseek-v4-flash", - }, - }); - assert.equal(options.deepSeekProviderAdapter, undefined); - assert.equal(options.deepSeekDsmlToolCallRepair, undefined); -}); - test("DeepSeek Anthropic streamSimpleByApi strips aborted tool calls before conversion", () => { const { localProviders, state } = loadProvidersWithCapturedAnthropicStream(); const model = createDeepSeekAnthropicModel();