Repository navigation
fix(protocol): convert the responses tool items instead of dropping them - #208
Merged
Merged
Conversation
`openai_responses_to_openai_chat_req` handed every `input` item to
`convert_responses_message`, which only understands `role` + `content`. A
Responses client's tool round trip is two items that carry no `role` -
`{"type":"function_call","call_id","name","arguments"}` and
`{"type":"function_call_output","call_id","output"}` - so both fell back to
`{"role":"user","content":null}`: the tool call and its result disappeared, the
upstream saw an empty user turn, the request succeeded, tokens were billed and
the answer was semantically empty.
- dispatch the `input` items by `type`; the tool items are translated into the
chat shapes the codebase already uses for the same round trip
(`assistant.tool_calls` + `role:"tool"`, mirroring `convert_message_to_openai`)
- consecutive `function_call` items (responses parallel tool calls) share one
assistant message, because that is the shape chat gives a `tool_calls` array
- every other item keeps the previous path, so plain-text input is unchanged
202 passed (was 199), fmt/clippy clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The gateway advertises three-protocol interop (
src/protocol.rs: OpenAI Chat / Responses / Anthropic 双向互转), but the requests it hands upstream had never been measured.openai_responses_to_openai_chat_reqpassed everyinputitem toconvert_responses_message, which only understandsrole+content.A Responses client's tool round trip is two items that carry no
role:{"type":"function_call","call_id":"ZZCALLMARK_R7","name":"ZZFNMARK_R7","arguments":"{\"city\":\"ZZARGMARK_R7\"}"} {"type":"function_call_output","call_id":"ZZCALLMARK_R7","output":"ZZOUTMARK_R7"}Both fell through to the default branch (
unwrap_or("user")+ missingcontent), so what the upstream actually received was measured as:The tool call and its result were silently destroyed - the upstream saw an empty user turn, the request succeeded, tokens were billed and the answer was semantically empty (silent data loss, worse than an error).
Reachability: 11 of the 12 plans in
config/config.toml(measured with the realdetermine_forwarding_protocol): aresponsesinbound is forwarded asanthropicon the 7openai_chat+anthropicplans and asopenai_chaton the 4openai_chat-only plans; onlydeepseek-paygopasses through. A Responses client's tool round trip is exactly these two item types, so any agent client hits this.Why this is an omission, not a chosen subset: the same round trip in the other directions is converted completely -
anthropic -> openai_chatproducedassistant.tool_calls[0].{id,name,arguments}+{role:"tool",tool_call_id,content}andopenai_chat -> anthropicproducedtool_use+tool_result(all markers true). There was also zero coverage:responses_req_to_openai_chatonly tested plain-text input items.Related Issue
Changes
openai_responses_to_openai_chat_req现在按type分派input条目;工具条目由新的convert_responses_tool_item处理,转成代码库已有的 chat 形状(assistant.tool_calls+role:"tool",与convert_message_to_openai的tool_use/tool_result分支同构 —— 形状单源,不另立一套){"type":"function_call","call_id","name","arguments"}→{"role":"assistant","content":null,"tool_calls":[{"id":call_id,"type":"function","function":{"name","arguments"}}]}(arguments非字符串时to_string()){"type":"function_call_output","call_id","output"}→{"role":"tool","tool_call_id":call_id,"content":<string 原样 / 非 string 转 JSON 字符串>}function_call合并进同一条 assistant 消息:Responses 的并行工具调用就是连续多个 item,而 chat 的形状是「一条消息一个tool_calls数组」(否则并行调用会被拆成多条 assistant 消息)role+content)走原路径,纯文本输入行为不变ui// i18n 改动)Tests
cargo test全部通过 — 202 passed(原 199,+3)cargo fmt --check通过responses_req_tool_items_to_openai_chat— 一次完整工具往返:逐项断言call_id/name/arguments/output 存活,并断言缺陷形状消失(不再出现content: null的 user 消息)responses_req_parallel_function_calls_share_one_assistant_message— 两个连续function_call⇒ 一条 assistant 消息两个tool_calls,两条 tool 结果按 id 对齐responses_req_tool_items_through_the_anthropic_chain— 链式responses → chat → anthropic(7 个 plan 的实际路径)得到tool_use+tool_resultresponses_req_to_openai_chat(纯文本)保持绿 —— 证明原路径未变A/B(红前绿后;每条变异自带推导出来的期望红集,还原放
finally并刷新 mtime——坑 177/178)convert_responses_message,即修复前的行为)function_call各起一条 assistant 消息)第二条变异只让「并行」那条红 —— 合并规则与分派规则各自被断言,而不是被同一条断言笼统覆盖。
修复前的实测输出(把分派还原后跑该测试):
Checklist
fix/responses-input-tool-items)fix(protocol): ...)src/protocol.rs的 responses 入站请求翻译;⛔tool_choice(同一轴的下一个面,单独处理)、⛔chat -> responses丢tool_calls(可达性未证)、⛔ 响应翻译器与status/截断语义均未触碰