Skip to content

[Bug] Codex 内置工具 oneOf schema 使 DeepSeek 路由 /responses 整请求 422:建议 fail-open + 探测误选回查 #87

Description

@rvvrvrv

摘要 / Summary

Codex 桌面版内置插件 codex-app-tools 的工具 codex_app__automation_update 携带 parameters.anyOf[1].oneOf(分支不可证明互斥)的工具 schema。当路由目标为 DeepSeek 这类"严格 OpenAI 兼容"上游、且协议探测选用了 Moonshot MFJS 工具 schema 方言时,MFJS 编译器对整个请求 fail-closed,返回 422——一个默认插件的单个工具 schema 形态会杀死经代理的所有工具会话,与 v3.18 修复的 parameters: null 属同一类问题(本轮为 Codex 新版把该工具升级为真实 schema 后复发)。

English: One Codex built-in tool (codex_app__automation_update) carries a non-disjoint anyOf/oneOf parameter schema. Under the Moonshot MFJS tool-schema dialect chosen by protocol probing for strict upstreams like DeepSeek, the MFJS compiler fails the entire /responses request with 422. This is the same class as the v3.18 parameters: null fix (#4706/#5315), now recurring because a newer Codex build gives the tool a real schema. Suggest fail-open-with-event for unrepresentable tool schemas, plus a probe recheck (below).

版本 / Version

  • CCSwitchMulti: v3.19.2-26(Windows Portable)
  • Codex 桌面版: 26.901.x(内置插件 codex-app-tools@openai-bundled 0.1.3)
  • 操作系统: Windows
  • 涉及应用: Codex

重现步骤 / Steps to Reproduce

  1. MultiRouter 路由(如 "Codex GPT + DeepSeek")目标供应商选 DeepSeek,模型 deepseek-v4-flash-vision-exp;进行过协议探测,探测档案记录的工具 schema 方言为 Moonshot MFJS(或该卡协议探测后自动采用 MFJS)。
  2. Codex 桌面版启用内置插件 codex-app-tools@openai-bundled(默认启用),注册 MCP 服务 codex_app(工具:automation_updatecreate_threadsend_message_to_threadfork_threadhandoff_thread)。
  3. 任意会话发一条消息(请求会自动携带该工具列表)。

实际结果:请求重连 5/5 后返回:

unexpected status 422 Unprocessable Entity: CC Switch local proxy failed while handling
Codex endpoint /responses. Provider: Deepseek; model: deepseek-v4-flash-vision-exp;
cause: 请求/响应转换错误: tool `automation_update` schema at
`$.tools[9].tools[0].parameters.anyOf[1].oneOf` is incompatible with Moonshot MFJS:
oneOf can only be represented as MFJS anyOf when every branch is provably disjoint

期望行为:单个工具 schema 无法安全表示时,不应让整个请求 422。应降级/豁免该工具(如非互斥 oneOfanyOf + 该工具 strict: false),并记录结构化事件;会话应当照常可用。

根因 / Root Cause

  • codex_tool_schema.rsMfjsCompiler::compile_objectoneOf 分支若不能证明两两互斥,直接 Err(fail-closed),错误穿透到整个请求。
  • 该工具 schema 来源是 Codex 侧(openai-bundled codex-app-tools/desktop-mcp.json),其形态不受 CCSM 控制;Codex 桌面版更新即可改变形态(v3.18 时该工具还是 parameters: null,本轮已是真实 anyOf + oneOf,因此同类问题会随 Codex 更新复发)。

建议 1:fail-open + 事件(治标且可发布)

与现有"down-level 注释/格式标注则置 strict: false 保留工具"的哲学一致(codex_tool_schema.rs 已有先例,见 annotations 注释),建议把"不可证明互斥的 oneOf"同样降级为 anyOf + relaxed(工具 strict: false)而不是报错;空 oneOf 仍可保留 fail-closed。改动很小:

// codex_tool_schema.rs: compile_object() 内 oneOf 分支
if !one_of_branches_are_pairwise_disjoint(branches) {
    self.relaxed = true; // 降级为 anyOf + strict:false,替换原 Err
}

格式更激进的“整请求 fail”可改为:记录 redacted 事件(参考已有的 hosted_tool_not_called / dropped-tool 结构化日志),仅豁免该工具,不让 422 打到 Codex。

(我本地已按此改过并验证思路:DeepSeek 路由切手动覆盖 + 工具 Schema=OpenAI JSON Schema 直传可用,说明 MFJS 收紧并非 DeepSeek 必需。)

建议 2:回查探测为何对 DeepSeek 选 MFJS(可能的根治)

protocol_compatibility/runner.rs should_retry_with_moonshot 的触发条件之一是"forced tool 调用未返回有效结果"(模型未调用),而不只是"上游拒绝 schema"(400/422)。DeepSeek 已实证接受原始 OpenAI schema(手动 openai 直传正常),MFJS 很可能是当初探测时模型未返回工具调用被误升的。建议:

  • 仅当上游明确拒绝 schema(400/422)时才升级到 Moonshot MFJS;
  • 模型未调用强制工具时重试但不切换方言。

若此点修正,DeepSeek 等严格上游可能根本不需要 MFJS 编译器,该类 422 自然消失。

临时 Workaround(已在生产验证)

目标供应商卡 → 高级选项 →「协议配置方式」= 手动覆盖(高级) →「工具 Schema」= OpenAI JSON Schema,保存后重启 Codex。注意:仅当协议配置方式为手动且「最终使用协议」与探测 transport 一致时才写入 meta(codexProtocolSettings.ts buildCodexProtocolMeta!matchesProbeTransport 会静默不写入);表单默认显示的 "OpenAI JSON Schema" 是读取缺省回退值,不代表已生效。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions