fix(responses): 补齐 reasoning item 转换缺口 + 上游看门基线推进 - #16
Merged
Merged
Conversation
一、Responses 多轮历史的 reasoning item 此前被静默丢弃(真实缺口)
Responses 协议客户端(Codex CLI 等)会把上一轮的思维链以
`{"type":"reasoning", ...}` item 形式随多轮历史一并回传。本仓
`responses_compat._convert_input_items` 没有该类型的分支,而该 item 也不带
`role` → 两个条件都不命中,于是落进末尾「其他类型保底」的分支里,因
`if role:` 为假而被整个跳过:**无消息产出、无告警、无日志**。表现为多轮对话
后思维链凭空消失,客户端侧只能看到正文。
同生态上游 ardeyouxipianyi/workbuddy2api-hub v1.4.0(fixes #17)修的是同一
问题,本提交按同语义在本地独立落地(非搬运)。
实现(`responses_compat.py`):
- 新增 `_extract_reasoning_text(item)`:按 Responses 规范从 `summary`
(部件数组 / 字符串)提取,缺失时回退 `content`(字符串 / 部件数组,
兼容 reasoning_text / text / output_text / summary_text 四种部件名);
- 新增 `pending_reasoning` 暂存:reasoning 紧邻其所属 assistant 轮次之前,
由 `_flush_assistant` 随之落地为 `reasoning_content`;
- 同一轮多条 reasoning 按出现顺序 `\n` 拼接,不覆盖;
- 孤儿 reasoning(其后紧跟 user 消息、无归属轮次)在简单消息分支显式作废,
防止漂移附着到下游毫不相干的 assistant 上;
- 空 reasoning 不注入空 `reasoning_content`(避免制造伪字段)。
与既有 `deepseek_thinking.backfill_reasoning_content` 的关系:该函数在
「历史中已存在带 reasoning 的 assistant 消息」时才补齐同轮一致性。此前
reasoning 在转换阶段就被丢掉,回填无从触发;本修复让转换阶段先保住数据,
两条链路互补而非重复。
测试(`tests/test_responses_api.py`,+7 条,先写失败再实现):
- reasoning 在 assistant 之前 → 挂到该消息(规范输出序)
- assistant 仍 pending 时到来的 reasoning → 归属同一轮,不漂到下一轮
- 孤儿 reasoning → 作废,不漂到下游 assistant(防误附着)
- 同一轮多条 reasoning → 顺序拼接
- summary 数组逐段拼接 / summary 缺失时 content 回退
- 空 reasoning → 不注入空字段
- reasoning 后跟 function_call 轮次 → 挂到 tool_calls 消息上
RED 证据:实现前 4 条按预期失败于 `KeyError: 'reasoning_content'`。
变异验证:① 删掉 reasoning 分支 → 5 条变红;② 多条改回「只取最后一条」
→ 拼接用例变红;还原后 14 passed。
二、上游看门基线推进(收口 Issue #14)
`tools/upstream-sources.json`:`wb2api-upstream-ardeyou` 的
last_synced_commit / last_synced_sha 推进至 `67d2352a`,`absorbed` 与 `note`
记录该机制的借鉴出处与本地落地形态。
`updated_at` 保持 2026-09-18。
验证(LF 检出,本地全量)
- `pytest -q` 431 passed(423 → 431,+7 新用例 +1 既有)
- `tests/run_isolated_tests.py` 431 passed / exit 0(无网络、无 DNS)
- `npm test` 202 passed
- `cargo test` 85 passed
- `check_premarked_sync.py` ✓ 键集一致
- `check-upstream.py --lint` ✓ 19 个源
- `check-upstream.py`(实跑) 0 项待评估 / 0 项查询失败 → 满足工作流自动收口条件
未做:未打真实 CodeBuddy 上游请求(无法在本地复现该 wire 场景);Rust 侧本轮
未改动。
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.
这个 PR 做了什么
补齐一个真实的协议缺口,并顺手推进上游看门基线以收口 Issue #14。
一、Responses 多轮历史里的
reasoningitem 此前被静默丢弃Responses 协议客户端(Codex CLI 等)会把上一轮的思维链以
{"type":"reasoning", ...}item 形式随多轮历史一并回传。本仓responses_compat._convert_input_items原先没有该类型的分支,而该 item 也不带role—— 两个条件都不命中,于是落进末尾「其他类型保底」分支,因if role:为假被整个跳过:表现为多轮对话后思维链凭空消失,客户端侧只能看到正文。
复现(修复前):
同生态上游
ardeyouxipianyi/workbuddy2api-hubv1.4.0(fixes #17)修的是同一问题;本 PR 按同语义在本地独立实现(非搬运),并额外覆盖了上游用例未涉及的边界(孤儿 reasoning 防漂移、同轮多条拼接、空 reasoning 不注入伪字段)。二、实现要点(
responses_compat.py)_extract_reasoning_text(item)summary(部件数组 / 字符串)提取,缺失时回退content(字符串 / 部件数组,兼容reasoning_text/text/output_text/summary_text四种部件名)pending_reasoning暂存_flush_assistant随之落地为reasoning_content\n拼接,不覆盖reasoning_content,不制造伪字段与既有
deepseek_thinking.backfill_reasoning_content的关系:该函数在「历史中已存在带 reasoning 的 assistant 消息」时才补齐同轮一致性。此前 reasoning 在转换阶段就被丢掉,回填根本无从触发 —— 本修复让转换阶段先保住数据,两条链路互补而非重复。三、测试(+7 条,先写失败再实现)
tests/test_responses_api.py:summary数组逐段拼接 /summary缺失时content回退function_call轮次 → 挂到tool_calls消息上RED 证据:实现前 4 条按预期失败于
KeyError: 'reasoning_content'。变异验证(证明用例真能抓回归,不是「碰巧绿」):
四、上游看门基线推进(收口 Issue #14)
tools/upstream-sources.json:wb2api-upstream-ardeyou的last_synced_commit/last_synced_sha推进至67d2352a,absorbed与note记录借鉴出处与本地落地形态;updated_at保持 2026-09-18。五、验证(LF 检出,本地全量)
pytest -qtests/run_isolated_tests.pynpm testcargo testcheck_premarked_sync.pycheck-upstream.py --lintcheck-upstream.py(实跑)未做(如实声明):未打真实 CodeBuddy 上游请求(本地无法复现该 wire 场景);Rust 侧本轮未改动,
cargo test仅作为回归确认。评审时请留意
content回退路径的多部件名兼容是刻意的:上游 Issue 🔔 [Upstream Watch] 社区反代借鉴项目有新提交(9 项待评估) #17 的日志显示不同客户端/实现会在summary/content间摇摆,部件名也不统一;漏一个就又是一次静默丢失。cargo fmt或其它无关重排 —— 三个文件均以纯 LF 入库,与仓库既有约定一致。