Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/agent-gui/src/lib/providers/deepSeekProviderAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ export function attachDeepSeekProviderPayloadAdapter<TOptions extends DeepSeekSt
},
): TOptions {
const model = params.model;
// DeepSeek adapter 只服务实际选中的 chat/anthropic 协议;Responses 由用户配置决定。
if (params.providerId === "codex" && model && model.api !== "openai-completions") return options;
const isDeepSeek =
params.providerId === "codex"
? isDeepSeekCodexTarget({
Expand Down
16 changes: 2 additions & 14 deletions crates/agent-gui/src/lib/providers/runtime/modelFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
} from "../anthropicModels";
import {
applyDeepSeekModelDefaults,
isDeepSeekCodexTarget,
resolveDeepSeekOpenAICompletionsOverrides,
} from "../deepSeekProviderAdapter";
import { isXaiProviderTarget } from "./xaiResponsesPayload";
Expand Down Expand Up @@ -334,24 +333,13 @@ export function createModelFromConfig(

if (providerId === "codex" || providerId === "xai") {
const { baseUrl: normalizedBaseUrl, preferredApi } = normalizeCodexBaseUrl(baseUrl);
const isDeepSeekCodex =
providerId === "codex" &&
isDeepSeekCodexTarget({
providerId,
baseUrl: normalizedBaseUrl,
upstreamBaseUrl,
modelId,
});
// 正式 xai 供应商,或 Codex 直连 api.x.ai:固定 Responses(agentic 搜索等)。
const isXaiTarget = isXaiProviderTarget({
providerId,
baseUrl: upstreamBaseUrl?.trim() || baseUrl,
});
const api = isDeepSeekCodex
? "openai-completions"
: isXaiTarget
? "openai-responses"
: inferCodexApi(requestFormat, preferredApi);
// DeepSeek 不再按模型 ID 强制协议;requestFormat 优先,DeepSeek 适配只在最终走 completions 时生效。
const api = isXaiTarget ? "openai-responses" : inferCodexApi(requestFormat, preferredApi);
const responsesCompat =
api === "openai-responses"
? resolveCodexOpenAIResponsesCompat({
Expand Down
74 changes: 0 additions & 74 deletions crates/agent-gui/test/providers/request-options.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -536,80 +536,6 @@ test("Codex Chat Completions streams forward reasoning effort", async () => {
assert.equal(captured.options.toolChoice, "auto");
});

test("DeepSeek Codex models force Chat Completions compat", () => {
const model = providers.createModelFromConfig(
"codex",
"deepseek-v4-pro",
"https://api.deepseek.com",
"openai-responses",
);

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-responses",
);

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 Anthropic streamSimpleByApi strips aborted tool calls before conversion", () => {
const { localProviders, state } = loadProvidersWithCapturedAnthropicStream();
const model = createDeepSeekAnthropicModel();
Expand Down
Loading