
Claude 客户端经 OpenAI 兼容通道打 Gemini 时,CLIProxyAPI 的翻译层两处会把工具调用翻错:非流式回 end_turn 让工具不执行,流式把两路并行调用并成一个并退化成 max_tokens;本期给出代码位置、一眼判据和绕行方式。
tool_use 块,stop_reason 却是 end_turn,客户端按规矩不去执行工具。tool_use,参数拼在一起,stop_reason 退化成 max_tokens,看起来像回答被截断。gemini 通道走另一套翻译,第一条在它身上不会发生。openai-compatibility 上的,下面两节就是你要核的那几行;接在内置 gemini 上的,直接看处置那一节。/v1/messages,说的是 Anthropic 那一套;上游是一条 OpenAI 兼容通道,说的是 Chat Completions 那一套。https://generativelanguage.googleapis.com/v1beta/openai 是这类通道最常指向的地方。7finish_reason 写成 stop。OpenAI 自己的接口在同样的轮次写的是 tool_calls,差别就在这里。tool_calls 元素,而且不带 index。tool_calls 的轮次写 finish_reason: stop,非流式这条路就翻出 end_turn;只要流式的并行调用不带 index,两路就会被并成一路。internal/translator/openai/claude/openai_claude_response.go,v8.0.35 里 1010 行。convertOpenAINonStreamingToAnthropic,第 454 行起;另一个在 ConvertOpenAIResponseToClaudeNonStream,第 805 行起。
convertOpenAINonStreamingToAnthropic,v8.0.35:第 501 行把工具块收进 content,第 510 行起才决定 stop_reason,中间没有一处看这个回复里有没有工具。content 里一个完整的 tool_use 块,stop_reason 写着 end_turn。stop_reason。有 tool_use 块却是 end_turn 的,就是这一条。index,没有就退回数组下标。
id 会盖掉前一个。那几行只挡住了非字符串和空串,同位置换了个 id 这件事没有被当成新调用。content_block_start 用掉之后,第二路进来时已经有一块开着,就轮不到它自己开场。tool_use:id 是第一路的,input 是两段 JSON 首尾相接。tool_use,input 是两段 JSON 接着写,stop_reason 是 max_tokens。/v1/messages,Claude Code 与 Anthropic 的 SDK 都是。openai-compatibility 通道,比如 base-url 指着 Gemini 的 /v1beta/openai。17
resolveGeminiClaudeStopReason,v8.0.35:第 49–50 行见过工具就给 tool_use,第 57–58 行才轮到 STOP 映射 end_turn。openai-compatibility 上中招,接在内置 gemini 上走的是另一条路。这个差别决定了下面怎么处置。openai-compatibility 换回内置 gemini 通道。finish_reason 是 stop,非流式就必然回 end_turn,跟模型和工具都没关系。tool_use。gemini 或别的原生通道上最省事。go test ./internal/translator/...、go vet、gofmt 在他那边都干净。internal/translator/** 的改动会被 path guard 拦下。20github.com
github.com
github.com
github.com
github.com
github.com
ai.google.dev
github.com
github.com
github.com
platform.claude.com
github.com
github.com
github.com
github.com
github.com
github.com
github.com
github.com
github.com
github.com
github.com
status.openai.com
status.anthropic.com
status.cloud.google.com
codex-reset.com
opentherank.com
community.openai.com
reddit.com
reddit.com
This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.