fix(apicompat): 补齐 Codex 0.14x 工具链经 chat 回退桥的转换(custom/tool_search/namespace)#3989
Open
xlplbo wants to merge 9 commits into
Open
fix(apicompat): 补齐 Codex 0.14x 工具链经 chat 回退桥的转换(custom/tool_search/namespace)#3989xlplbo wants to merge 9 commits into
xlplbo wants to merge 9 commits into
Conversation
Codex 0.14x 的核心执行工具 exec 是 type=custom(freeform)工具,
responsesToolsToChatTools 丢弃所有非 function 工具,导致模型工具列表中
没有 exec、无法执行任何命令;SSE 序列化层 responsesItemWire 也缺少
custom_tool_call 分支,即使产出该类型 item,call_id/name/input 也会被
白名单剥离。
- custom 工具降级为单一 input:string 参数的 function 工具
- 回程(非流式+流式)按名字还原 custom_tool_call 项/事件
- 历史 custom_tool_call / custom_tool_call_output 项转换(此前被跳过)
- responsesItemWire 补 custom_tool_call 分支
- tools 全部被丢弃时不再转发 tool_choice(修复上游 400
"'tool_choice' is only allowed when 'tools' are specified")
feat(apicompat): chat 桥补齐 tool_search/namespace/字符串工具声明
对齐参考实现(cc-switch transform_codex_chat.rs)的工具类型覆盖:
- tool_search → 固定 function 代理工具(query 必填/limit 可选),
历史项 tool_search_call/tool_search_output 一并转换
- namespace → 子 function 工具摊平为 <namespace>__<name>,
超 64 字符截断加 sha256 短哈希;历史项带 namespace 字段时同规则摊平
- tools: ["name"] 字符串简写按 custom 工具解析
fix(apicompat): 工具调用宣告延迟到类型可判定,兼容 name 迟到的上游
output_item.added 的 custom/function 判定依赖工具名。名字晚于首个分片
到达时,此前会按 function_call 宣告造成 added/done 项类型不一致。
改为存在 custom 工具且名字未到时延迟宣告,宣告时补发已累积的参数增量;
请求无 custom 工具时行为不变。
PR 标题与正文(可直接粘贴)
标题:fix(apicompat): responses→chat 桥丢弃 custom 工具导致 Codex 无法执行命令
正文:
## 问题
OpenAI APIKey 账号接入仅支持 /v1/chat/completions 的第三方上游
(DeepSeek/GLM/Kimi 等)时,Codex 0.14x 客户端文本对话正常,但无法执行
任何命令:exec 调用全部 "aborted",spawn_agent 派出的子智能体永远卡在
running,多轮后模型自述"环境无法执行文件系统命令"放弃。
## 根因
Codex 0.14x 的核心执行工具 exec 是 Responses API 的 custom(freeform)
工具,而非普通 function:
{"type": "custom", "name": "exec", "format": {"type": "grammar", ...}}
两处缺陷叠加:
1. `responsesToolsToChatTools` 丢弃所有非 function 工具
(chatcompletions_responses_bridge.go),模型工具列表中从未出现 exec;
2. SSE 序列化 `responsesItemWire` 按 item 类型白名单重组字段,无
custom_tool_call 分支,call_id/name/input 会被剥离
(responses_stream_event_wire.go)。
次生缺陷:工具全部被丢弃时 tool_choice 仍被转发,部分上游报 400
"'tool_choice' is only allowed when 'tools' are specified"。
## 改动
(三个提交的要点清单,见各提交说明)
设计与 cc-switch 的 Codex chat 转换层(transform_codex_chat.rs /
streaming_codex_chat.rs)对齐:custom 工具降级为 {"input": string}
function 工具;custom 调用的参数增量不可流式还原为自由文本,缓冲至
收尾一次性下发 custom_tool_call_input.delta/done。
## 测试
- 新增 17 个单元测试:请求工具转换、tool_choice 兜底、历史项转换、
非流式/流式回程还原、wire 序列化字段完整性、name 迟到宣告、命名摊平
- `go test ./internal/pkg/apicompat/...` 与 service 相关测试全绿
- 真机验证:Codex CLI 0.142.5 → 本桥 → GLM/DeepSeek 系 chat-only 上游,
exec 实际执行命令并多轮续作正常
## 影响面
仅影响 "APIKey 账号 + 判定不支持 Responses" 的 chat 直转路径:
- OAuth 账号与透传路径不经过本桥(透传为字节级原文转发)
- CC→Responses 反方向仅构造 ResponsesRequest,新增字段带 omitempty
且不赋值,序列化输出不变
- 探测逻辑未动,路由判定不变
- 共享类型改动均为增量容错(新字段 omitempty;UnmarshalJSON 只新增
字符串工具的解析成功路径)
解决 backend/internal/service/openai_gateway_responses_chat_fallback.go 冲突: 将本地 tool_search/custom 工具改动(customTools/toolSearch 穿参与流状态标记) 移植到上游重构后的 CC 公共管线;同步适配上游新增调用点 openai_gateway_messages_chat_fallback.go 的 ChatCompletionsResponseToResponses 签名。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8zMzeFAumSddraEw1FUeG
Codex 0.14x 将 MCP 工具声明为 namespace 工具,chat 桥去程摊平为 "<namespace>__<name>" function 工具后,回程仅原样回传平铺名的 function_call 项;codex 按 namespace+name 路由查不到该名字,所有 MCP 工具调用被判为 unsupported call。 - NamespaceToolNames 构建摊平名 →(namespace, 子工具名)反查表 (摊平名超长带截断哈希,无法按字符串切分还原) - 非流式/流式回程命中映射时还原为裸子工具名 + namespace 字段, ResponsesOutput 新增 Namespace 字段并同步 wire 层白名单 - 回退桥入口将映射与 CustomTools/ToolSearchDeclared 一并穿入 已在测试机经 codex exec + MCP server 端到端验证。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWQyEgFKKbdve67G6qCoAU
摊平名与顶层 function/custom 工具撞名、或跨 namespace 摊平出同名时, chat 上游无法按 namespace 区分调用归属:此前重复声明照发上游、回程 固定命中其中一条映射,调用可能被还原到错误工具。这类请求在原生 Responses 上游合法,歧义由摊平转换制造且不可消除,改为在请求转换 阶段直接报错(网关返回 400 invalid_request_error 并点名冲突双方); 同一 (namespace, 子工具) 的重复声明去重后不拒绝。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWQyEgFKKbdve67G6qCoAU
Contributor
|
All contributors have signed the CLA. ✅ |
Author
|
I have read the CLA Document and I hereby sign the CLA |
tool_search 服务端工具降级后的代理 function 不能改名(codex 的模型侧按 tool_search 这个名字调用),与客户端声明的同名 function/custom 工具无法 区分:去程产生重复声明,回程会把普通工具的调用劫持还原成 tool_search_call。 与 namespace 摊平撞名的处理一致,在请求转换阶段显式报错(网关 400); 重复声明 type=tool_search 去重后只产出一个代理,不拒绝。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWQyEgFKKbdve67G6qCoAU
此前只检查转换后是否还剩任意工具,不校验 tool_choice 指向的工具是否幸存: 强制选择被丢弃的服务端工具(如 web_search)或指向不存在名字时,选择项被 原样转发,chat 上游因 tool_choice 指向未声明工具而 400。改为具名选择项仅 在目标存在于转换后工具集时转发,服务端工具类选择项随工具本身丢弃; "auto" 等字符串形式保持原样转发。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWQyEgFKKbdve67G6qCoAU
e48d4bc to
e2b68d1
Compare
tool_search 工具未被丢弃而是降级为同名 function 代理,此前 tool_choice 过滤 把它连同其他服务端工具类型一起静默丢弃,强制工具搜索被退化为自动选择, 违反调用方明确指定的工具选择语义。改为映射到指向代理的 function 选择; 未声明 type=tool_search 时无可指向的代理,维持丢弃。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWQyEgFKKbdve67G6qCoAU
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.
概述
补齐 Codex 0.14x 客户端经 chat completions 回退桥(仅支持
/v1/chat/completions的第三方上游)的工具链转换,共 4 个提交:问题背景
Codex 0.14x 把核心执行工具 exec 改为
type=custom、把 MCP 工具声明为type=namespace,并对 gpt-5.4+ 模型名启用 tool_search 延迟加载。此前回退桥会丢弃这些工具类型、或只做去程摊平而不做回程还原,表现为:unsupported call: mcp__<server>__<tool>(回程 function_call 项缺独立namespace字段,codex 按 namespace+name 路由失败)。主要改动
<namespace>__<name>(超 64 字符截断加 sha256 短哈希);custom_tool_call/tool_search_call(wire 层强制execution=client)/ 带namespace字段的function_call;ResponsesOutput新增Namespace字段,同步 wire 层白名单与序列化断言。验证
go test -tags=unit ./...全绿;golangci-lint 0 issues;force_chat_completions,上游 glm-5.2):🤖 Generated with Claude Code
https://claude.ai/code/session_01NWQyEgFKKbdve67G6qCoAU