fix(deepseek): keep Anthropic-only tool types when forwarding to /anthropic/v1/messages
DeepSeek's Anthropic-compatible endpoint accepts only the built-in web_search_20250305 / web_search_20260209 tools and rejects client-defined `custom` tools (MCP / Read / Bash) with HTTP 400 "unknown variant `custom`". The generic non-Claude filter in prepareClaudeRequest dropped the offending tools but also dropped the web_search_* ones DeepSeek does accept. - Add an opt-in per-provider transport quirk `claudeSupportedToolTypes`; when declared it becomes a strict allow-list for Anthropic tool `type` values - Stop stripping the `type` discriminator from surviving tools under that quirk, since DeepSeek needs it to route built-ins - Declare the quirk on the deepseek transport with the two web_search_* types - Providers without the quirk keep the previous filter and normalisation behaviour byte-for-byte; openai-format targets never reach this path
This commit is contained in:
@@ -461,8 +461,21 @@ export function prepareClaudeRequest(body, provider = null, apiKey = null, conne
|
||||
// Strip built-in tools (e.g. web_search_20250305) and normalize to Anthropic-native shape
|
||||
// (drop `type` field, fold `function.{name,description,parameters}`) for non-Anthropic providers
|
||||
if (provider !== "claude") {
|
||||
// Provider-specific whitelist of Anthropic tool `type` values that the
|
||||
// upstream actually accepts. When the provider declares it
|
||||
// (e.g. DeepSeek — only web_search_*), keep only listed types; otherwise
|
||||
// keep the prior behaviour of dropping every non-function tool, which is
|
||||
// correct for OpenAI-compatible targets reached through this Claude-format
|
||||
// pass (their tools get normalized below to function-style).
|
||||
const supportedTypes = PROVIDERS[provider]?.quirks?.claudeSupportedToolTypes;
|
||||
const hasWhitelist = Array.isArray(supportedTypes);
|
||||
body.tools = body.tools
|
||||
.filter(tool => !tool.type || tool.type === "function")
|
||||
.filter(tool => {
|
||||
const t = tool?.type;
|
||||
if (!t || t === "function") return true;
|
||||
if (hasWhitelist) return supportedTypes.includes(t);
|
||||
return false;
|
||||
})
|
||||
.map(tool => {
|
||||
if (tool.function) {
|
||||
return {
|
||||
@@ -471,6 +484,13 @@ export function prepareClaudeRequest(body, provider = null, apiKey = null, conne
|
||||
input_schema: tool.function.parameters,
|
||||
};
|
||||
}
|
||||
// When the provider declared a supportedToolTypes whitelist, keep
|
||||
// the surviving tools' `type` field intact — the upstream
|
||||
// Anthropic-compatible endpoint (e.g. DeepSeek) requires it to
|
||||
// route built-ins like web_search_* correctly. Without a
|
||||
// whitelist, preserve prior behaviour and strip `type` so the
|
||||
// tool is normalized to plain Anthropic shape.
|
||||
if (hasWhitelist) return tool;
|
||||
const { type, ...rest } = tool;
|
||||
return rest;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user