fix(tools): scope Claude tool type defaulting to gateways that need it (#3905)
defaultClaudeToolType() stamped tools[].type = "custom" onto every
Claude-format request carrying tools since e08ac6da. That satisfied
MiniMax (error 2013) but broke Anthropic-compatible endpoints that only
accept the legacy typeless tool shape. DeepSeek's endpoint
(api.deepseek.com/anthropic/v1/messages) whitelists its tool `type` enum
to the web_search_* variants and answers HTTP 400 "unknown variant
`custom`", so every Claude Code request routed to a DeepSeek connection
failed and surfaced as a persistent 503.
Run the defaulting only when the target provider declares the new
requireClaudeToolType quirk (MiniMax, MiniMax-CN). Add
shouldDefaultClaudeToolType(provider, finalFormat, tools, PROVIDERS) in
translator/concerns/toolCall.js so the gate is unit-testable, and cover
MiniMax keeping the explicit type, DeepSeek/Anthropic staying typeless,
non-Claude formats and tool-less requests never defaulting.
Effectively a no-op for MiniMax and a restore of the pre-e08ac6da
behaviour everywhere else. Another strict gateway now only needs the same
one-line quirk instead of a global behavioural change.
This commit is contained in:
33
tests/translator/bugs-3905-deepseek-tool-type.test.js
Normal file
33
tests/translator/bugs-3905-deepseek-tool-type.test.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Regression for #3905: defaultClaudeToolType() (type:"custom") must only run for
|
||||
// gateways that declare the requireClaudeToolType quirk (MiniMax). Claude-format
|
||||
// endpoints that only accept the legacy typeless tool shape — e.g. DeepSeek's
|
||||
// Anthropic-compatible endpoint, which answers HTTP 400 "unknown variant `custom`" —
|
||||
// must never receive tools[].type = "custom".
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { PROVIDERS } from "../../open-sse/providers/index.js";
|
||||
import { FORMATS } from "../../open-sse/translator/formats.js";
|
||||
import { shouldDefaultClaudeToolType } from "../../open-sse/translator/concerns/toolCall.js";
|
||||
|
||||
const tools = [{ name: "get_weather", description: "weather", input_schema: { type: "object" } }];
|
||||
|
||||
describe("Claude tool `type` defaulting is provider-scoped (#3905)", () => {
|
||||
it("runs only for providers declaring requireClaudeToolType", () => {
|
||||
expect(shouldDefaultClaudeToolType("minimax", FORMATS.CLAUDE, tools, PROVIDERS)).toBe(true);
|
||||
expect(shouldDefaultClaudeToolType("minimax-cn", FORMATS.CLAUDE, tools, PROVIDERS)).toBe(true);
|
||||
// Endpoints accepting only the legacy typeless shape must NOT get type:"custom".
|
||||
expect(shouldDefaultClaudeToolType("deepseek", FORMATS.CLAUDE, tools, PROVIDERS)).toBe(false);
|
||||
expect(shouldDefaultClaudeToolType("claude", FORMATS.CLAUDE, tools, PROVIDERS)).toBe(false);
|
||||
});
|
||||
|
||||
it("never applies outside Claude-format requests or without tools", () => {
|
||||
expect(shouldDefaultClaudeToolType("minimax", FORMATS.OPENAI, tools, PROVIDERS)).toBe(false);
|
||||
expect(shouldDefaultClaudeToolType("minimax", FORMATS.CLAUDE, undefined, PROVIDERS)).toBe(false);
|
||||
expect(shouldDefaultClaudeToolType("minimax", FORMATS.CLAUDE, null, PROVIDERS)).toBe(false);
|
||||
});
|
||||
|
||||
it("declares the quirk only on the MiniMax providers (registry tripwire)", () => {
|
||||
expect(PROVIDERS.minimax?.quirks?.requireClaudeToolType).toBe(true);
|
||||
expect(PROVIDERS["minimax-cn"]?.quirks?.requireClaudeToolType).toBe(true);
|
||||
expect(PROVIDERS.deepseek?.quirks?.requireClaudeToolType).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user