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:
kimono381
2026-09-10 22:26:15 +07:00
committed by decolua
parent 781c18d837
commit 45ec1d30bb
3 changed files with 181 additions and 1 deletions

View File

@@ -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;
});