mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(deepseek-web): return 400 when client sends tools[] - chat.deepseek.com has no tool support (#2854)
Integrated into release/v3.8.6.
This commit is contained in:
@@ -683,6 +683,30 @@ export class DeepSeekWebExecutor extends BaseExecutor {
|
||||
|
||||
async execute({ model, body, stream, credentials, signal, log }: ExecuteInput) {
|
||||
const bodyObj = (body || {}) as Record<string, unknown>;
|
||||
|
||||
// chat.deepseek.com's web API only accepts {prompt, ref_file_ids,
|
||||
// thinking_enabled, search_enabled} - no tools field. Silently dropping
|
||||
// tools[] is misleading: models reply as if no tools were ever offered,
|
||||
// causing agentic clients (OpenAI-compatible) to hallucinate "I don't
|
||||
// have that tool". Fail fast with a clear error so callers route to the
|
||||
// official DeepSeek API (provider: 'deepseek') or a different provider
|
||||
// for tool-using requests. See #2848.
|
||||
const requestedTools = bodyObj.tools;
|
||||
if (Array.isArray(requestedTools) && requestedTools.length > 0) {
|
||||
return {
|
||||
response: errorResponse(
|
||||
400,
|
||||
"deepseek-web upstream (chat.deepseek.com) does not support function calling. " +
|
||||
"The web interface only accepts text + thinking_enabled + search_enabled flags. " +
|
||||
"Use provider 'deepseek' (official api.deepseek.com) for tool-using requests, " +
|
||||
"or route through a different provider."
|
||||
),
|
||||
url: COMPLETION_URL,
|
||||
headers: {},
|
||||
transformedBody: body,
|
||||
};
|
||||
}
|
||||
|
||||
const messages = (Array.isArray(bodyObj.messages) ? bodyObj.messages : []) as Array<{
|
||||
role: string;
|
||||
content: string;
|
||||
|
||||
@@ -58,6 +58,63 @@ test("execute returns 400 with empty apiKey", async () => {
|
||||
assert.equal(result.response.status, 400);
|
||||
});
|
||||
|
||||
test("execute returns 400 when client sends non-empty tools[] (chat.deepseek.com has no tool support) - issue #2848", async () => {
|
||||
const executor = new DeepSeekWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "default",
|
||||
body: {
|
||||
messages: [{ role: "user", content: "call my_tool" }],
|
||||
tools: [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "my_tool",
|
||||
description: "test",
|
||||
parameters: { type: "object", properties: {} },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
stream: false,
|
||||
credentials: { apiKey: "any-valid-looking-token" },
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
assert.equal(result.response.status, 400);
|
||||
const text = await result.response.text();
|
||||
assert.ok(
|
||||
/does not support function calling/i.test(text),
|
||||
`expected explanatory error body, got: ${text}`
|
||||
);
|
||||
assert.ok(
|
||||
/provider 'deepseek'|api\.deepseek\.com/i.test(text),
|
||||
`expected guidance to use official deepseek provider, got: ${text}`
|
||||
);
|
||||
});
|
||||
|
||||
test("execute does NOT 400 on tools[]=[] (empty array, equivalent to no tools)", async () => {
|
||||
const executor = new DeepSeekWebExecutor();
|
||||
const result = await executor.execute({
|
||||
model: "default",
|
||||
body: {
|
||||
messages: [{ role: "user", content: "hi" }],
|
||||
tools: [],
|
||||
},
|
||||
stream: false,
|
||||
credentials: {},
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
assert.equal(
|
||||
result.response.status,
|
||||
400,
|
||||
"still returns 400 but for missing userToken, NOT for tools[]"
|
||||
);
|
||||
const text = await result.response.text();
|
||||
assert.ok(
|
||||
text.includes("userToken"),
|
||||
`expected userToken error (not tools error) for empty tools[], got: ${text}`
|
||||
);
|
||||
});
|
||||
|
||||
// ─── Test connection ─────────────────────────────────────────────────────
|
||||
|
||||
test("testConnection returns false with empty credentials", async () => {
|
||||
|
||||
Reference in New Issue
Block a user