Repository navigation
fix(strict-echo): clear blank reasoning content on strict-echo Responses wire (#2169) - #2174
Conversation
…t 400s on the Responses wire (#2169) DeepSeek thinking-mode validation rejects any reasoning item carrying content it did not issue — whitespace-only reasoning_text included ('reasoning_text must be passed back'). Blank items of that shape ride the client's resent history into bili requests and were forwarded verbatim, 400-ing every request of a long session. bili never fabricates them: the kernel round-trip re-emits raw reasoning items verbatim, patchOriginalItem passes them through, the #1479 injection is content-free, and the #1029 marker strip is role-gated — all verified against source, tag v0.1.184 and the published npm dist. - normalizeStrictEchoResponsesInput clears 'content' from reasoning items whose parts are exclusively whitespace-only reasoning_text (lossless; DeepSeek accepts the content-less form per #2169 control experiments). Same gate as the existing injection; unknown/mixed shapes untouched. - Both 400 learners (main path, compress loop) match the reasoning_text spelling in addition to reasoning_content, so DeepSeek-compatible gateways behind non-deepseek origins/models arm the flag instead of re-paying 400s. Tests: unit suite for the sanitize predicate (identity/no-mutation/conservative-shape cases) + full pipeline test driving DeepSeek's exact error wording end-to-end. Closes #2169
📦 Built Package ArtifactBranch: Option A — Install this exact build (recommended)npm install -g billion-context@0.0.2228-pr.2174.336552bThis exact version is immutable and reproducible. The rolling Option B — Download artifact
tar xzf billion-context-pr2174.tgz
npm install -g packageInstall instructions are refreshed on each push. |
[bot] 🏷 Review 进行中(不重做,核对已完成的工作):
CI 剩 2 个 windows 测试矩阵 job 在跑,完成后给结构化七问结论。 |
[bot] 🏷 ## Review 结论(结构化七问) ① 是什么问题:DeepSeek Responses wire 上,客户端重发历史里带着 ② 什么原因导致的:两层。表层:bili 把客户端 inbound 的 reasoning item 原样转发,缺出站闸门。根层:DeepSeek 校验拒绝任何它没发过的 reasoning content(空白也算——issue 对照实验证明:换真文本仍 400,清空/content:[]/仅 summary 全 200);而 bili 自身从不制造这个形状(#1479 注入是 content-free、kernel round-trip verbatim、#1029 marker strip role-gated,已在 master/v0.1.184/npm dist 三处核实)——所以缺的就是 strict-echo 条件下的出站净化。第二面是 learner regex 拼写覆盖不全,纯代码缺陷。 ③ 怎么修复的:在源头决策点修,不在传输中遮症状——出站重建处(主路径 ④ 是否有回归问题:无。逐项审过的既有行为:
⑤ 是否破坏缓存:否。不改任何缓存键/重试游标/去重键/persistence 格式;归一化只作用于每请求出站重建体。前缀缓存稳定性(head system 消息字节稳定)不受影响——且该场景下旧行为是每个请求都 400,本就不存在可破坏的工作缓存。issue 里第二个观察(fold-drift 请求无注入也 400)由同一机制覆盖:闸门对每次出站请求生效,与 #1479 注入是否触发无关。 ⑥ 是否改了设计方向:否。沿用既有架构(KDD #7 双压缩模式纪律下的 proxy-mode 出站净化),无新子系统、无 kernel 变更、无 config surface 变更。 ⑦ 是否引入了新的配置:无。无新 env var、无新 config key、无默认值变化。 证据链
判定:可合。 披露的行为变更(见 PR 表格)与代码事实一致,无隐藏漂移。 |
…sion fixture The issue author provided a redacted copy of the exact outbound body DeepSeek rejected (89 items; the 5 blank whitespace-content reasoning echoes preserved verbatim, all other text replaced by length placeholders, item order/types/call_ids unchanged). Drives the real shape through normalizeStrictEchoResponsesInput: the 5 blanks lose their content key, every other item stays the same reference (byte-exact), no #1479 injection needed for this body. Closes #2169
📦 Merged Build PublishedThis merge to master was published as npm install -g billion-context@0.0.47-master.8bca8bf # this exact build
npm install -g billion-context@master # always the newest merged build
|
Closes #2169
Problem
Long Codex CLI sessions against DeepSeek's Responses wire 400'd on EVERY request (
The 'reasoning_text' in the thinking mode must be passed back) once the client's resent history carried reasoning items whosecontentwas whitespace-onlyreasoning_text. DeepSeek's validation rejects ANY reasoning content it did not issue — whitespace included (issue control experiments: replacing the blank text with real text still 400s; content-less /content:[]/ summary-only forms all 200).Provenance audit: bili never fabricates that shape. Verified against source, tag v0.1.184 and the published npm dist: the #1479 injection pushes a content-free item, the kernel round-trip re-emits raw reasoning items verbatim (
coreToResponses,patchOriginalItempassthrough), and the #1029 marker strip is role-gated so it cannot touch reasoning items. The items ride INBOUND from the client's history and were forwarded verbatim — the missing piece was an outbound gate for them.Second facet, same failure domain: both 400 learners matched only
reasoning_content(chat-wire spelling); DeepSeek's Responses error spells itreasoning_text, so behind non-deepseek origins/models the learned flag never armed and every fresh session re-paid the 400.Changes
src/strict-echo.ts:normalizeStrictEchoResponsesInputnow also clearscontentfrom reasoning items whose parts are exclusively whitespace-onlyreasoning_text(lossless — whitespace carries no information). Conservative by design: missing/non-array content, empty arrays, foreign part types, non-string text, or any part with real text leave the item byte-exact. Same strict-echo gate as the existing [Bug] #762's strict-echo repair is chat-completions-only: Responses-wire requests still 400 with reasoning_content_missing after a compress fold #1479 injection; applies to both the main path and loop re-request bodies (both call this function).src/server.ts+src/loop/core.ts: learner regex/reasoning_content/i→/reasoning_(?:content|text)/i.Behavior changes (disclosed)
contentforwarded verbatim → guaranteed 400, session stuckcontentkey removed at the outbound gate → accepted (issue control experiments)enabledgate)reasoning_contentwordingreasoning_textwordingNo config-surface change. No kernel change. No persistence change. No new env vars.
Tests
tests/reasoning-echo.test.ts(8 tests): predicate semantics, identity/no-mutation guarantees, conservative shape handling, disabled passthrough, combined orphan-run + blank-item pipeline,normalizeStrictEchoBodyintegration.tests/loop-strict-echo-responses.test.ts: drives DeepSeek's exact error wording through the proxy — req1 400 arms the learner (body verified byte-exact while the gate is off), req2 sanitized at the outbound gate with the [Bug] #762's strict-echo repair is chat-completions-only: Responses-wire requests still 400 with reasoning_content_missing after a compress fold #1479 blank injection still landing and the sentinel firing exactly once total.loop-strict-echo,strict-echo-model-criterion,reasoning-echo-400) 4/4 pass; typecheck clean; build clean. Full regression left to CI.