Repository navigation
[PR] fix: veto strict reasoning echo on gateways that reject the field as extra (#5260) - #5280
ranxianglei wants to merge 3 commits into
Conversation
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.8132-pr.5280.68f9dc8This exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr5280.tgz
npm install -g packageInstall instructions are refreshed on each push. |
|
(ai: vllm-qwen/qwen3.8-27b) First push tripped the |
(ai: vllm-qwen/qwen3.8-27b) 状态更新:新 head |
(ai: vllm-qwen/qwen3.8-27b) 收工摘要(本 PR 的 review 工作已全部完成,细节见上方结构化八问结论):
判定不变:可合(待 CI 绿确认)。 |
(ai: vllm-qwen/qwen3.8-27b) 路由澄清:本会话被派单为 #5288,但实际要处理的 issue「小窗口上下文 token 截断」的内容与讨论都在 #5289(本条 #5288 是另一个无关的 strict-echo veto PR,我不在这里展开)。完整排查与回复见 #5289。此处仅补一句结论,避免派单跟踪以为没产出。 做了什么:独立把代码逐行核了一遍,坐实了截断根因——bili 拦截 DSH 原生压缩请求时记下一个「只增不减、永不衰减」的输入下限( 结果:已在 #5289 给出①代码级机制确认(含上轮没点透的「两条路径都钉死下限」关键点)、②用户可立即执行的客户端侧缓解(让 DSH 别发原生压缩请求,#2474 覆盖)、③根治修法设计。根治动的是 #2490 的保护语义(行为变更),按纪律等用户日志确认 + owner 拍板 A/B 选项后再实现,本轮未改代码。当前状态:等用户在 #5289 回日志与接入方式。 |
Review finding: both learn sites nested the veto clear inside the learned-flag flip guard (relay demand branch / loop UpstreamHttpError else-if). A session holding BOTH evidence classes — learned ON from an earlier demand 400, later vetoed by an extra-forbidden 4xx — could never clear its veto: re-demand evidence arriving while the flag was already true did nothing, so every tool-call turn 400'd forever in the opposite direction (the mirror image of #5260's stuck loop). The veto clear is now an independent transition in src/server/relay.ts and src/loop/core.ts, with its own warn log and markDirty, so the two evidence classes keep flipping the session either way. Regression test drives the full flip through the real relay (demand → forbid → re-demand → accept) and fails on the pre-fix code.
8df9308 to
15d86b4
Compare
|
(ai: vllm-qwen/qwen3.8-27b) Verdict: APPROVE — clean fix, real regression tests, no kernel changes, CI green on head 结构化八问(中文结论)
测试有效性(真测试、有牙):① Wire-contract ledger (#1304):WC-023 永久入账(summary + provenance 齐),lane-scoped validator Non-blocking observations(不拦合、未改)
结论:可合。 CI 绿(head
|
(ai: vllm-qwen/qwen3.8-27b) 审核完成——结论 APPROVE(可合)。上面那条就是本 lane 提交的完整八问审核(GitHub 禁止对自有 PR 正式 approve,故以评论形式落在 PR #5280;正式合并由 owner 手动执行)。 一句话状态:修复正确、测试有牙、无 kernel 改动、无新配置面、无回归;CI 绿(head |
Closes #5257
Problem
pi plugin-native + Mistral serving a deepseek-named model (
deepseek-v4-1-flash-1017): from the first tool call onward every follow-up request 422s forever —The static
/deepseek/idetection (#1027) makes bili inject a blankreasoning_contenton assistant tool-call messages (#762). That is correct for DeepSeek's own API (which demands it) but Mistral's message schema forbids the field entirely — and nothing ever learned the gateway rejects it, so the session was stuck in a permanent inject→422 loop. The reporter's log proves it: switching the same session tomistral-large-4(no name match) succeeds immediately.Fix: learn-on-failure VETO (mirrors #684/#762/#2169 in the opposite direction)
src/strict-echo.ts: new pure predicateisReasoningEchoForbiddenReject(status, body)— true only for a 4xx whose body names the echo field (reasoning_content/reasoning_text) AND rejects it as extra/unknown (extra input(s),not permitted,unrecognized,unknown field). Demand-class wording (DeepSeek's own "must be passed back") never matches.session.metadata.strictReasoningEchoVeto, checked FIRST inisStrictReasoningEcho— it outranks both the learned flag and static detection.src/server/relay.ts) and loop re-request (src/loop/core.ts). The existing 400 demand-learner became anelse ifand clears the veto when the upstream re-demands the field, so the two evidence classes can flip the session either way.tests/wire-contract-fakes.ts(Mistral chat lane forbidsreasoning_contenton any message), enforced by a newmistralvalidation-parity lane on the openai-chat fake + a WC-023 row in the ledger self-check.tests/issue5260-strict-echo-veto.test.ts— unit truth table (exact Mistral 422 body vetoes; demand wording / no-marker / no-field / 200 / 500 never do; veto beats static and learned evidence) + main-path e2e through the mistral-lane fake: request 1 → 422 + exactly one WC-023 violation + visible injection, request 2 → 200 with a clean body and no new violation.Behavior change (old → new)
Old: once strict-echo evidence fired (static model match or learned flag) it stayed ON for the whole session — a gateway that forbids the field rejected every tool-call turn forever.
New: one 4xx extra-forbidden rejection of the field turns strict echo OFF for that session (cost: exactly one failed request per affected session — the same cost model as the original #684 learning). A subsequent demand-class 400 clears the veto and re-enables it. No change for gateways that accept or demand the field.
Config surface
None. No new config keys / env vars / CLI flags. The reporter proposed an explicit per-provider opt-out (
providers[...].strictReasoningEcho: false); that is owner-design territory per the Configuration Surface Discipline and was deliberately NOT built — automatic learning covers the case without a new knob. If an explicit opt-out is wanted, it should be decided separately on the config surface.Tests (local, targeted)
tests/issue5260-strict-echo-veto.test.ts tests/reasoning-echo.test.ts tests/reasoning-echo-400.test.ts→ 66/66tests/wire-contract.test.ts→ 22/22 (incl. WC-023 lane probe)tests/loop-strict-echo.test.ts tests/loop-strict-echo-responses.test.ts tests/loop-core.test.ts tests/reasoning-drop.test.ts→ 49/49No kernel changes.