Skip to content

v1.2.0 的 DeepSeek → GPT reasoning 清洗已失效:DeepSeek 现在返回非 null 的 encrypted_content,导致切回 GPT 仍持续 400 #23

Description

@zhuzw1990

现象

同一 Codex 任务内先跑 DeepSeek,再切回 GPT 模型,下一次 GPT 请求立即 400:

Invalid 'input[112].content': array too long. Expected an array with maximum length 0, but got an array with length 1 instead.

错误 item 已落盘在任务历史,因此每轮重放都会再失败一次;切回 DeepSeek 可继续,再切 GPT 又炸。与 #17 描述完全一致,但在 v1.2.1 上仍可复现。

环境

  • DSCodex:v1.2.1(6e4b290)
  • 切换方向:gpt-5.6-terra(官方 GPT)→ deepseek/deepseek-flash → 切回 gpt-5.6-terra
  • 同一 thread,Codex Desktop,macOS
  • 路由日志对应行:chatgpt /v1/responses -> 400 1827ms

根因:v1.2.0 的判据依赖「第三方不返回 encrypted_content」,该假设已不成立

src/proxy.mjs:150-156(buildChatGptBody)的清洗判据是:

if (item?.type === "reasoning"
    && !item.encrypted_content          // ← 要求加密字段为空
    && Array.isArray(item.content)
    && item.content.some((part) => part?.type === "reasoning_text")) {
  changed = true;
  return [];
}

test/proxy.test.mjs:705-725 的断言印证了这一设计意图:带 encrypted_content 的 reasoning 被当作官方 native 保留,encrypted_content: null 的才当作 foreign 丢弃。

但 DeepSeek 现在返回的 reasoning item 带有非 null 的 encrypted_content。实测两方形状对比(同一任务历史内):

来源 content encrypted_content
DeepSeek 产 [{"type":"reasoning_text","text":"<omitted>"}] "7567b785-…-0" —— 38 字符 UUID 占位串(非 base64、非加密块形状)
官方 GPT 产 字段不存在 "gAAAAAB…" —— 1594 字符真 base64

于是:

  1. !item.encrypted_content 对 38 字符占位串求值为 false → 判据不命中;
  2. DeepSeek 的 reasoning 被误判为 native,原样转发给 chatgpt.com;
  3. 官方 schema 要求 reasoning 的 content 为空数组(官方自己的 reasoning 根本不带 content)→ array_above_max_length 400。

#17 报告当时的形状是 encrypted_content: null,v1.2.0 的修复与该形状严格对应。DeepSeek 侧补上该字段(假值)后,修复即失效——这属于外部行为变化引发的回归,不是原修复写错。

复现步骤

  1. 启动 DSCodex v1.2.1,路由正常(node src/cli.mjs doctor 六项 ok)。
  2. 新建任务,模型选 gpt-5.6-terra,完成至少一轮响应,使历史中产生官方 reasoning item。
  3. 同一任务内切到 deepseek/deepseek-flash,完成至少一轮响应(历史中产生带 reasoning_text 的 reasoning item)。
  4. 同一任务内切回 gpt-5.6-terra,发送任意消息。
  5. GPT 请求返回 HTTP 400,param 为 input[N].content。
  6. 切回 DeepSeek 恢复正常;再切 GPT 再次 400。

期望行为

同一任务在 DeepSeek 与 GPT 之间可安全切换,与 #17 的期望一致。

建议修复方向

把判据从「encrypted_content 为空」放宽为「reasoning 且 content 非空」:

if (item?.type === "reasoning"
    && Array.isArray(item.content)
    && item.content.length > 0) {
  changed = true;
  return [];
}

依据:官方 reasoning item 不含 content 字段(本次实测:历史中全部官方 reasoning 的 content 均为「字段缺失」,只带 summary + 真 base64 encrypted_content)。因此「reasoning 且 content 非空」只可能来自第三方,按此判据剥离不会误伤 native reasoning,也不再依赖 encrypted_content 的具体取值。

其余约束按 #17 原议保持不变:只在实际发生迁移时解码/重建请求体,不含此类 item 的普通 GPT 流量仍字节透传,不修改用户 rollout JSONL。

备注

  • 本地仅做了诊断与形状核实,未改动任何上游文件;如果维护者需要,我可以补一个带回归测试的 PR(新增一条 encrypted_content 为非空占位串的 foreign reasoning 用例,断言它必须被剥离)。
  • 本报告未包含任何对话内容、账号标识或 OAuth 信息。

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