问题描述
通过 /responses(及 /v1/responses、/backend-api/codex/responses)接入 codex 等 Responses API 客户端时,在多轮工具调用场景下,请求在 apicompat 协议转换阶段失败:
- 转发到 Anthropic 上游:返回
502 Bad Gateway,客户端报 unexpected status 502 Bad Gateway: Upstream request failed。
- 转发到 OpenAI-compat 上游(只支持
/v1/chat/completions 的第三方中转):不报错,但工具调用结果被静默丢弃,表现为模型"思考完没有输出"。
后台账号"测试连接"显示正常(测试连接走自构造的简单请求,不经过 Responses input 解析),掩盖了该问题。
复现
curl -N -X POST http://<host>/responses \
-H "Authorization: Bearer <KEY>" -H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-x",
"stream": true,
"input": [
{"role": "user", "content": [{"type": "input_text", "text": "hi"}]},
{"type": "function_call", "call_id": "c1", "name": "foo", "arguments": {"x": 1}},
{"type": "function_call_output", "call_id": "c1",
"output": [{"type": "output_text", "text": "result"}]}
]
}'
实测日志:
ERROR handler/gateway_handler_responses.go gateway.responses.forward_failed
platform=anthropic
error="convert responses to anthropic: parse responses input:
json: cannot unmarshal array into Go struct field
ResponsesInputItem.output of type string"
→ HTTP 502
arguments 为对象时同理:cannot unmarshal object into Go struct field ResponsesInputItem.arguments of type string。
根因
backend/internal/pkg/apicompat/types.go 中 ResponsesInputItem 的两个字段定义为 string:
type ResponsesInputItem struct {
// ...
Content json.RawMessage `json:"content,omitempty"` // 已正确用 RawMessage
Arguments string `json:"arguments,omitempty"` // BUG:codex 可能发对象
// ...
Output string `json:"output,omitempty"` // BUG:codex 新版发数组
}
而新版 OpenAI Responses API / codex 实际发送:
function_call.arguments:可能是 stringified JSON,也可能是 JSON 对象。
function_call_output.output:新版是数组 [{"type":"output_text","text":"..."}],旧版才是纯字符串。
强类型 string 字段遇到对象/数组,json.Unmarshal 直接报错。
两条转换路径表现不同(同一根因)
| 路径 |
入口 |
文件 |
遇到对象/数组 |
| Responses→Anthropic |
convertResponsesInputToAnthropic |
responses_to_anthropic_request.go |
502 崩溃 |
| Responses→ChatCompletions |
responsesInputToChatMessages |
chatcompletions_responses_bridge.go |
不崩,但 rawString() 对非字符串返回 "",工具结果静默丢失 |
建议修复
将 ResponsesInputItem.Arguments 与 .Output 改为 json.RawMessage,并在取值处归一化:
- arguments:object / stringified JSON / 空 → JSON 对象(供 Anthropic
tool_use.input);→ stringified JSON(供 chat/completions function.arguments)。
- output:string / array(提取
output_text/text 文本)/ 空 → 文本(供 tool_result.content / tool message)。
- 反向构造
ResponsesInputItem 的位置(anthropic_to_responses.go、chatcompletions_to_responses.go)需把原 string 包成 JSON-string RawMessage,保持 wire 格式不变。
json.RawMessage 对旧的纯字符串输入仍向后兼容。
版本:基于 v0.1.133 / upstream main,核实字段定义未变。本地按上述方案修复并实测后,codex → /responses → Anthropic 上游恢复正常 200,返回完整 Responses SSE 流。如果方案 OK 我可以提 PR。
问题描述
通过
/responses(及/v1/responses、/backend-api/codex/responses)接入 codex 等 Responses API 客户端时,在多轮工具调用场景下,请求在apicompat协议转换阶段失败:502 Bad Gateway,客户端报unexpected status 502 Bad Gateway: Upstream request failed。/v1/chat/completions的第三方中转):不报错,但工具调用结果被静默丢弃,表现为模型"思考完没有输出"。后台账号"测试连接"显示正常(测试连接走自构造的简单请求,不经过 Responses input 解析),掩盖了该问题。
复现
实测日志:
arguments为对象时同理:cannot unmarshal object into Go struct field ResponsesInputItem.arguments of type string。根因
backend/internal/pkg/apicompat/types.go中ResponsesInputItem的两个字段定义为string:而新版 OpenAI Responses API / codex 实际发送:
function_call.arguments:可能是 stringified JSON,也可能是 JSON 对象。function_call_output.output:新版是数组[{"type":"output_text","text":"..."}],旧版才是纯字符串。强类型
string字段遇到对象/数组,json.Unmarshal直接报错。两条转换路径表现不同(同一根因)
convertResponsesInputToAnthropicresponses_to_anthropic_request.goresponsesInputToChatMessageschatcompletions_responses_bridge.gorawString()对非字符串返回"",工具结果静默丢失建议修复
将
ResponsesInputItem.Arguments与.Output改为json.RawMessage,并在取值处归一化:tool_use.input);→ stringified JSON(供 chat/completionsfunction.arguments)。output_text/text文本)/ 空 → 文本(供tool_result.content/toolmessage)。ResponsesInputItem的位置(anthropic_to_responses.go、chatcompletions_to_responses.go)需把原 string 包成 JSON-string RawMessage,保持 wire 格式不变。json.RawMessage对旧的纯字符串输入仍向后兼容。版本:基于
v0.1.133/upstream main,核实字段定义未变。本地按上述方案修复并实测后,codex →/responses→ Anthropic 上游恢复正常 200,返回完整 Responses SSE 流。如果方案 OK 我可以提 PR。