Skip to content

[PR] fix: stop stale session input baselines outliving their measurement context (#2683) - #4583

Open
ranxianglei wants to merge 1 commit into
masterfrom
2026-10-11_cross-model-baseline
Open

ranxianglei wants to merge 1 commit into
masterfrom
2026-10-11_cross-model-baseline

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Continuation of #2725 — same branch (2026-10-11_cross-model-baseline), same single commit, rebased onto current master (d95799d86, 2026-10-24).

#2725 was closed unmerged by stale-ticket cleanup after #2683 was closed "completed" — but verification on 2026-10-24 showed the fix never landed in master (both producers of #2683 are still live there; evidence in the #2725 thread). GitHub refuses to reopen a closed PR whose head branch was force-pushed, so this PR carries the identical fix forward from the same branch.

Rebase notes: text conflicts resolved in src/server/handle.ts (#4205 window-caliber stamps — retirement block 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); the calibrated-provenance flag was extended 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).

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:

  1. 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 (estimateCoreMessagesUpper class, 1.7–4× real) as session.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 NO processTurn feed passes an estimate-grade arm into any band (preflight runs noEmergencyTruncate; 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).

  2. 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 decisionTokens above 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)

  • 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 (fork 会话 lastInputTokens=0 时 preflight 退化为字符估算,大载荷被低估后不压缩直接裸转上游 #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 (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

  • New 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).
  • Re-scaled #1498-F2 assertion in tests/issue1493-arm-outbound-payload.test.ts (raw-view measurement pinned at chars/4 scale; char-count adoption now explicitly forbidden).
  • Updated pins in 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).
  • Targeted batch green on the rebased head: typecheck + 58 tests across 11 suites covering every touched consumer path (issue1493, issue1839, issue2313, issue2484, issue1595, issue1569, issue2033, silent-backend-nudge, issue857, context-provenance-outbound, issue2683). Full suite to CI.

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.

…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).
@ranxianglei ranxianglei changed the title fix: stop stale session input baselines outliving their measurement context (#2683) [PR] fix: stop stale session input baselines outliving their measurement context (#2683) Oct 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-11_cross-model-baseline (74bfaef)

Latest build — install this exact version

npm install -g billion-context@0.0.5533-pr.4583.db9caaf

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Recent builds (commit → version)

commit version build
74bfaef billion-context@0.0.5533-pr.4583.db9caaf build

Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr4583.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

查重: 不重复。 按失败签名(stale baseline / armFailureShrink / #2683)检索 open+closed issue/PR:该机制族内唯一 open 条目即本 PR;前序 #2725(同分支 2026-10-11_cross-model-baseline、同一单提交)已被 stale-ticket 清理关闭未合入,本 PR 按设计承载同一修复(GitHub 不允许重开 head 被 force-push 的已关闭 PR);其余历史 baseline 类 PR(#1503/#1940/#2316/#2032/#4443 等)均已合并且机制不同。

标签: bug, scope/kernel, p2(bugfix PR;改动面是 src 侧压缩决策输入——baseline 退役 + arm 口径,跨客户端核心管道、不涉及特定客户端;功能性缺陷、影响明确但有自愈绕行 → p2)

审核进行中,初步结论:

最终结构化八问结论待 CI 分诊后给出。

@ranxianglei ranxianglei added bug Something isn't working p2 scope/kernel labels Oct 24, 2026
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

PR #4583 审核结论(独立复审:diff 逐行 + 测试全读 + master 漂移核对)

审原 issue(方向关):#2683 两个 case 的复现数据完整(v0.1.191 实测日志),定位准确——修的是基线「测量语境失效后继续生效」这个根机制,不是症状。本 PR 与 #2683 提议的两条修法对应且更稳(不 clamp,直接换口径;退役只限 trusted-grade)。方向成立。

回归审查(逐路径清点改动前既有行为):

代码审查:退役点放在 handle.ts 中 #4205 窗口戳之后、lastModel 更新之前,顺序正确(先按新模型算窗口再退役旧账);unknown→known 不触发、同模型仅变窗口不触发,条件收紧到位;arm 侧复用 preflight trigger 的同一 billing-caliber 公式(k̂ 缩放 + overhead + image reserve),armCalibrated 标志纯增量。无 as any、无吞错、日志措辞带来源子句可 grep。

测试有效性:三个 case 都是 e2e 级(真实 startServer + fake SSE relay),断言有牙且双向;G1 的 nonStreamCalls() === 0 在修复前必红(trusted floor 会钉死 decisionTokens ≥ target → 强制折叠),是真正的回归钉。小缺口(非阻塞):circuit-open/5xx 两条 arm 路径没有各自的 calibrated-flag 直测(与已测 network 路径同形代码);退役后无持久化 roundtrip 断言(序列化守卫已覆盖 undefined)。

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 字节上限('0: 1111 bytes',tests/error-dump.test.ts:200,ubuntu 22,2/24,2 确定性红)、B) generation-boundary 测试自身位置参数错位(tests/generation-boundary.test.ts:130,win 22,3 + ubuntu 22,3/24,3)、C) stale kernel dist mtime 守卫(tests/golden-storage-drive.ts:73);④ 用仓库自己的 partition 脚本算出本 PR 的三个测试文件落在 shard 5 和 shard 2,不在 base 绿/head 红的 shard 1。残留缺口:check-runs logs API 对本 PAT 返回 404(已完成 job 也取不到),无法逐测试点名对比——若 owner 要求确定性证据,#4591 修复落地后重跑即可一锤定音。作者声称的定向本地绿(typecheck + 11 suite 58 tests @ rebased head)与上述静态核查一致。


结构化八问

  1. 是什么问题:会话输入基线活过了它的测量语境——①无 usage 的 relay 失败把 char-count 上界(超读 1.7–4×)持久化成会话基线;②模型 X 下测得的 trusted usage bill 继续替模型 Y 做折叠决策。
  2. 什么原因导致的:armFailureShrink 采用上界估计而非计费口径读数;模型切换时没有任何机制退役模型相关的 trusted 基线(此前只有 ORIGIN 变化会降级,DSH: 切换模型后,preflight 在窗口远未满时提前触发压缩 #1933 F2)。
  3. 影响范围与严重程度:中会话切模型或遇无 usage relay 失败的会话——幻影紧急折叠、单次最长 ~180s 停顿(live case 两轮共 ~3.6min)、预算/headroom/展示虚高直到下次 usage 报告。p2:可用、自愈、无数据丢失。
  4. 怎么修复的:arm 改采 billing-caliber reading(与 preflight trigger 同一公式,k̂ 缩放);真实模型切换(双向已知 id)在请求入口退役 trusted-grade 基线;estimate-grade arm 保留(字节尺度属性跨模型有效);取证数据保留;下次 usage 报告重锚。
  5. 是否有回归问题:审过的既有行为均保持(见上回归审查五条)。已披露行为变更(旧→新+原因):a) armed 值从 char-count 上界 → billing-caliber 读数——密集 JSON/CJK 路由上可能低于未来账单(fork 会话 lastInputTokens=0 时 preflight 退化为字符估算,大载荷被低估后不压缩直接裸转上游 #553 chars/4 低估面),可接受,因 estimate-grade 本就隔离于所有决策地板;b) 切模型后首轮真溢出依赖 overflow-arm 救援而非 proactive fold——fail-soft 取舍,代价是一次性而非循环;c) 多模型未 fork 宿主每次切换清掉刚建立的基线——一轮内自愈,persona 宿主按 KDD#9 已 fork 不受影响。高危模式逐项过:无 fail-fast→吞错、无未披露阈值/时序漂移、日志仅追加来源后缀。
  6. 是否破坏缓存:不涉及——无缓存键/格式/账本变更,prompt-cache routing 不动,calibration ring 与 cache-ledger 历史保留。
  7. 是否改了设计方向:不变——扩展现有 provenance/quarantine 体系(emergency shrink / preflight 的本地估算用错了视图:超长会话(客户端历史 127 万,压缩后 outbound 仅 ~16 万)在上游失败无 usage report 时,bili 按原始历史估算 #1492/任何一轮没有拿到 usage 报告的请求,都会把上下文分母放大 12–20×(nudge 读到 >1000% usage、假 EMERGENCY、基线冻结数分钟) #1839/DSH: 切换模型后,preflight 在窗口远未满时提前触发压缩 #1933/[Bug] 上下文展示估算 206K,usage 返回后为 98K:希望校准估算并区分安全上界 #2117 B)补两个缺口,无新边界。
  8. 是否引入了新的配置:无。

合并判定

代码审核通过,可合——但两点留给人工:① auto-merge gate 规定 fallback/默认语义变更属产品决策,必须人工合并;② CI 当前被 #4591 红风暴淹没(base 同红,非本 PR 引入),建议 #4591 三项修复落地或 test 车道转绿后由 owner 合并。另注:本 PR 由 #2725 延续而来(force-push 关闭后 GitHub 拒绝 reopen),分支/提交未变,已在首条回复说明查重结论。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working p2 scope/kernel

Projects

None yet

1 participant