Repository navigation
[PR] fix: stop stale session input baselines outliving their measurement context (#2683) - #4583
ranxianglei wants to merge 1 commit into
Conversation
…ontext (#2683) Two producers of one mechanism family - session input baselines that outlive the context that measured them: 1. armFailureShrink adopted the char-count UPPER BOUND of the outbound payload as session.stats.lastInputTokens (a 1.7-4x over-reading). The #1498-F1 bound floor predates the #1492/#1839 reader quarantine: no processTurn feed passes an estimate-grade arm into any kernel band anymore, so the bound's only live effect was phantom mass - inflated preflight budgets / output headroom / displays surviving a model or window change until the next usage report (live case: 441,193 armed against a 202,885-token payload; after a 1M->400k model switch the meter read 142% and two ~3-minute emergency folds ran on a payload already at 62% of the new window). The arm now adopts the billing- caliber reading (k-scaled chars/4 + wire overhead + image reserve), and its log names the provenance. 2. A usage-grade baseline measured under model X kept deciding requests for model Y (only ORIGIN changes demoted it, #1933 F2): after a mid-session model switch the foreign bill pinned the preflight loop's decisionTokens above the fold target forever - folding until budget exhaustion although the payload fit. A real model change (both sides known; unknown->known never counts) now retires the trusted-grade baseline at the per-request model-stamp site, before prepare sizes the nudge and before the trigger runs; the next usage report re-anchors. Estimate-grade values survive the switch (byte-scale properties of the payload); same-model window moves keep the bill valid, so genuine overflow still folds. Behavior changes (old -> new): - armed value on a no-usage failure: char-count upper bound -> billing- caliber estimate (it may now land BELOW a future bill on dense JSON/CJK routes where chars/4 undercounts, #553; acceptable because estimate-grade values are quarantined from every decision floor and k closes most of the gap where route+model provenance matches) - usage/overflow-arm baseline + usage anchor across a model switch: trusted verbatim -> retired until the next usage report (forensic state preserved: cumulative inputTokens meter, last-usage timestamp, calibration ring, cache-ledger history) Tests: tests/issue2683-cross-model-baseline.test.ts (new: G1 cross-model retirement e2e - zero summary calls + re-anchor; G2 arm scale band [chars/8, char-count) + provenance clause; G3 control - same-model over-window bill still folds via its floor), re-scaled #1498-F2 assertion in tests/issue1493-arm-outbound-payload.test.ts, and updated pins in tests/context-provenance-outbound.test.ts. Rebased onto master d95799d (2026-10-24, reopened after the stale-ticket cleanup closed this PR while #2683's fix was absent from master): resolved text conflicts in src/server/handle.ts (#4205 window-caliber stamps — the retirement block now sits after both stamps, before the lastModel stamp) and src/server/relay.ts (#3129 v1 framer insertion, #3701 close-1009 byte-shed branch — kept deliberately un-alerted, only the calibrated arm call applied); extended the calibrated-provenance flag to the two armFailureShrink call sites master added after this branch cut (#3148 circuit-open fail-fast, #3701 oversize resend). Verified on the rebased head: typecheck clean + 49/49 across the targeted baseline/meter suites (issue2683 G1/G2/G3, #1498-F2, context-provenance-outbound and adjacent anchor/floor suites).
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.5533-pr.4583.db9caafThis exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4583.tgz
npm install -g packageInstall instructions are refreshed on each push. |
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复。 按失败签名(stale baseline / armFailureShrink / #2683)检索 open+closed issue/PR:该机制族内唯一 open 条目即本 PR;前序 #2725(同分支 标签: bug, scope/kernel, p2(bugfix PR;改动面是 src 侧压缩决策输入——baseline 退役 + arm 口径,跨客户端核心管道、不涉及特定客户端;功能性缺陷、影响明确但有自愈绕行 → p2) 审核进行中,初步结论:
最终结构化八问结论待 CI 分诊后给出。 |
(ai: vllm-qwen/qwen3.8-27b) PR #4583 审核结论(独立复审:diff 逐行 + 测试全读 + master 漂移核对)审原 issue(方向关):#2683 两个 case 的复现数据完整(v0.1.191 实测日志),定位准确——修的是基线「测量语境失效后继续生效」这个根机制,不是症状。本 PR 与 #2683 提议的两条修法对应且更稳(不 clamp,直接换口径;退役只限 trusted-grade)。方向成立。 回归审查(逐路径清点改动前既有行为):
代码审查:退役点放在 handle.ts 中 #4205 窗口戳之后、 测试有效性:三个 case 都是 e2e 级(真实 startServer + fake SSE relay),断言有牙且双向;G1 的 CI 状态(诚实报告):head 74bfaef 的 11 个 test shard 全部红。判定为既有 master 红风暴而非本 PR,证据链:① base d95799d 自己的 CI run 就是 8/11 红,含 windows-22 s1/s3(本 PR head 亦红的两个 shard);② master 今日连续多 push 全红;③ 已有跟踪 issue #4591(今天 15:41Z 立项,open)实证了 3 个失败域并给出签名:A) error-dump #4297 字节上限( 结构化八问
合并判定代码审核通过,可合——但两点留给人工:① auto-merge gate 规定 fallback/默认语义变更属产品决策,必须人工合并;② CI 当前被 #4591 红风暴淹没(base 同红,非本 PR 引入),建议 #4591 三项修复落地或 test 车道转绿后由 owner 合并。另注:本 PR 由 #2725 延续而来(force-push 关闭后 GitHub 拒绝 reopen),分支/提交未变,已在首条回复说明查重结论。 |
Closes #2683
What
Two producers of ONE mechanism family — session input baselines that outlive the context that measured them — each verified against the issue's live logs and reproduced with e2e fixtures:
The failure arm adopts a bound, not a reading.
armFailureShrink(no-usage relay 5xx / network failure) persisted the char-count UPPER BOUND of the outbound payload (estimateCoreMessagesUpperclass, 1.7–4× real) assession.stats.lastInputTokens. The fix: arm emergency shrink on outbound payload size, not raw body (#1493) #1498-F1 bound floor was justified by the kernel's tool-result truncation loop consuming the arm as remaining depth (arm emergency shrink on upstream failure to break relay-5xx deadlock #604 rescue) — but since the emergency shrink / preflight 的本地估算用错了视图:超长会话(客户端历史 127 万,压缩后 outbound 仅 ~16 万)在上游失败无 usage report 时,bili 按原始历史估算 #1492/任何一轮没有拿到 usage 报告的请求,都会把上下文分母放大 12–20×(nudge 读到 >1000% usage、假 EMERGENCY、基线冻结数分钟) #1839 reader quarantine NOprocessTurnfeed passes an estimate-grade arm into any band (preflight runsnoEmergencyTruncate; every decision floor gates on source), so the bound's only live effect was phantom mass: inflated preflight entry budgets, responses-lane output headroom, displays — surviving model/window changes until the next usage report. (Issue's live case: 441,193 armed against a 202,885-token payload.)→ The arm now adopts the same billing-caliber reading the preflight trigger uses (k̂-scaled chars/4 + wire overhead + image reserve), and its log names the provenance:
(k̂-calibrated outbound reading)/(raw outbound reading)(issue proposal feat: OpenAI Responses API support (/v1/responses) #4).Trusted baselines survive model switches. A usage report measured under model X kept deciding requests for model Y — only ORIGIN changes demoted it (DSH: 切换模型后,preflight 在窗口远未满时提前触发压缩 #1933 F2). After a mid-session switch the foreign bill pinned the preflight loop's
decisionTokensabove the fold target forever (the floor survives folding — folding shrinks the payload, not the bill) → folds until summary-budget exhaustion although the payload fit. (Issue's live case: 566,322 billed under a 1M-window model judged against a 400k window → two ~3-minute emergency folds at 62% of new-window occupancy.)→ A real model change (both sides known; unknown→known never counts) now retires the trusted-grade baseline at the per-request model-stamp site — before prepare sizes the nudge and before the trigger runs — logged with both models and the retired value. The next usage report re-anchors. Precedents: [Bug] 上下文展示估算 206K,usage 返回后为 98K:希望校准估算并区分安全上界 #2117 B already gates k̂ by model ("a factor learned on another model acts as absent"); DSH 分叉会话后触发大量 preflight overflow compress #2383 names foreign-model baselines untrusted for regime selection.
Behavior changes (old → new)
inputTokensmeter, last-usage timestamp (contextTokensAt), calibration ring, cache-ledger history. Same-model window moves do NOT retire (the bill stays valid → genuine overflow still folds); estimate-grade arms survive (byte-scale property of the payload).Config surface
None — no new env/config/CLI keys.
Both compression modes
Retirement sits at the request-handling chokepoint before prepare/trigger, so it applies identically in plugin mode and proxy mode (the mode split happens downstream). The arm-caliber change affects both modes equally (both consume
outboundPayloadBreakdown). No wire-shape change: message rebuild, system handling, tool injection,acp_summary, preflight protocol all untouched — only session-state bookkeeping values.Tests
tests/issue2683-cross-model-baseline.test.ts: G1 cross-model retirement e2e (566K billed under claude-a → claude-b turn makes ZERO summarization calls, no phantom(566000/…)meter, re-anchored to the real 6K bill); G2 no-usage network failure arms inside[chars/8, char-count)with the provenance clause, retry forwards without folding; G3 control — a SAME-MODEL over-window bill STILL FOLDS via its trusted floor (floor semantics preserved for genuine overflow).#1498-F2assertion intests/issue1493-arm-outbound-payload.test.ts(raw-view measurement pinned at chars/4 scale; char-count adoption now explicitly forbidden).tests/context-provenance-outbound.test.ts(cross-model authority loss vs same-model retention; fixture growth sized so the billing-caliber arm still outgrows the fixture bill and the replace-stale-usage mechanic keeps firing).Known side effect (disclosed): a host that mixes multiple models under one UN-FORKED session id will see freshly established baselines wiped at each switch — self-healing (the next usage report re-anchors within one round trip); persona-style hosts are forked per KDD#9 and unaffected.