Repository navigation
fix: side-request guard must not clamp on the host usage baseline (#1110) - #1111
Conversation
) The guard's effective window was fed session.stats.lastInputTokens whenever lastInputTokensSource was 'usage' — but that value is ALSO what a healthy compressed host turn writes as its nudge baseline. A low baseline (e.g. 3k after compression) permanently 413'd an unrelated in-process caller's fixed-size request (MemOS retrieval filter) even though it fit the model's real window, with no config/env escape hatch. Decouple the two: add stats.overflowArmTokens, set ONLY by an upstream context-overflow 400 (the one-shot emergency arm per #554/#987) and cleared by the next real usage report and on compression reset. The guard reads overflowArmTokens, never the baseline. Existing overflow-arm e2e still pins the arm path; new e2e regression reproduces the 413 pre-fix and passes post-fix.
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)npm install -g billion-context@pr-1111Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pr1111.tgz
npm install -g packageInstall instructions are refreshed on each push. |
[bot] 🏷 PR #1111 核对完成 — 全部通过,可以合并(合并由人工操作)。 分支状态:单 commit 代码审查(
本地 pre-flight(rebased head):
A/B 回归证据(我实际跑了):
CI:head 一点提示:PR 的 Scope note 里留了 bili 进程级 中文摘要:修复了 side-request guard 误用健康 host 的 nudge 基线(lastInputTokens)当上限、导致无关 in-process 请求被永久 413 的问题(根因是混用了"正常回合用量"与"overflow 一次性 arm"两个共享 source=usage 的量,现拆出独立字段 overflowArmTokens);已验证未修复时该 e2e 报 413、修复后全绿且 CI 8/8 通过,可以合并。 |
|
Real-world validation from the reporter (@Pasitearko, in #1110): built The triggering calls are memOS background retrieval-filter requests: no tools,
Confirms the healthy-baseline / one-shot-overflow-arm decoupling holds end-to-end against a real client. The residual process-level fetch-scope concern (unrelated in-process callers being proxied) is tracked separately in #1117 and is out of scope for this PR. |
[bot] 🏷 收到真实客户端验证数据——证据链闭环了,我补了两项独立确认:
验证结果与我此前的本地结论完全互证:
健康基线 / one-shot overflow arm 的解耦在真实客户端(Windows 11 / node v24.20.0 / DSH 0.1.5-rc.2 / memOS 2.0.20 in-process plugin)下端到端成立。本 PR 侧无需任何进一步动作,合并等你人工操作:#1111 中文摘要:真机验证(修复前 26/46 全部 413 死循环 → 修复后 20/20 通过)与此前本地 A/B 复现、CI 8/8 完全吻合,修复充分验证、无遗留动作,等待人工合并。 |
|
Independent Review: MERGE ✅ (human merge required — per AGENTS.md §7.4 this adds a persisted stats field + changes guard semantics, so it's "must stay human" territory; this review is that human gate's input.) Verified independently, not from the self-review claims:
Why the fix is root-cause level
Minor (non-blocking) — suggest a follow-up issueOne-shot release is untested. The e2e pins arm→block (deadlock repro) and #554 pins arm→clamp, but nothing asserts arm → real usage report → arm cleared → side request forwards again, nor the restart round-trip. The "one-shot" claim rests on three bare InformationalA session armed on the old code (arm persisted only inside |
[bot] 🏷 独立 review 收到——MERGE ✅ 作为人工合并门的输入已记录。你列的覆盖缺口我逐条核过了测试清单,完全一致: 已钉住(PR 分支上实际存在的测试):
未钉住:arm → 真实 usage report → 按你的建议已立后续 issue:#1129 — https://github.com/ranxianglei/billion-context/issues/1129(带来源标记,含完整测试设计:三阶段 e2e + restart round-trip 变体,并标注非阻塞、不 gating 本 PR)。查重过 open+closed,无重复项。 另注:master 已前进到 中文摘要:独立复核确认 one-shot 释放方向与 restart round-trip 无回归测试(pin 仅靠三处裸 delete),已按建议立项 #1129 跟踪补测(非阻塞);本 PR 证据链完整、rebase 已验证,等待人工合并。 |
What & why
Fixes #1110.
The side-request guard (
sideRequestGuard, #554) decides whether a verbatimpassthrough request would be a guaranteed upstream 400 by comparing its raw
body against an "effective context window". That effective window was being
clamped by
session.stats.lastInputTokenswheneverlastInputTokensSourcewas"usage":But
lastInputTokensis also the nudge baseline that every healthy host turnwrites from its real usage report. A compressed host conversation keeps that
baseline low (reporter observed 2.6k–3.4k). So an unrelated in-process caller's
fixed-size request (MemOS retrieval filter: no tools,
max_tokens <= 200,~4.3k tokens) was clamped to that low baseline and rejected with HTTP 413
side_request_payload_too_largepermanently — it fit the model's real window(256k) yet could never pass, with no config/env escape hatch. Reporter saw 26 of
37 filter calls fail over ~15h.
Root cause: the caller conflated two different quantities that share
source === "usage":The guard's own contract (#554/#987) only ever wanted the second.
Fix
Decouple them with a dedicated field,
session.stats.overflowArmTokens:non-parseable/payload-size arm),
src/loop/core.tsstreaming,src/server.tsnon-streaming,src/plugin.tsplugin-mode) and on compressionreset (
src/session.ts) — matching the documented one-shot semantics,lastInputTokens.A healthy compressed host baseline can no longer clamp any unrelated request,
while a genuine overflow arm still tightens the guard exactly as before.
Files:
src/session.ts(field + reset),src/server.ts(guard caller + bothoverflow writes + success clear),
src/loop/core.ts,src/plugin.ts(successclears),
src/persist.ts(optional persisted field — backward-compatible, legacyfiles lack it → treated as no arm).
Tests
tests/side-request-isolation.test.ts: "healthy hostbaseline must NOT clamp an unrelated side request (「BUG」sideRequestGuard 会永久拦截进程内第三方插件的 LLM 调用,导致与MemOS冲突 #1110)". It reproduces the
exact MemOS shape (host turn → 50k baseline, source
usage, no arm; then anunrelated tool-less side request whose body exceeds baseline×1.15 but fits the
200k window). Verified it fails pre-fix (
413 !== 200) and passes post-fix.oversized side request blocked locally) and passes unchanged.
Pre-flight on the rebased head (current master
d3bb77d):npm run typecheck✅npm test✅ (2190 pass / 0 fail / 2 skipped — default-skipped E2E)npm run build✅Note: the real-client
e2e-codexsuite needs a reachable upstream(
E2E_UPSTREAM_URL); none is reachable in this environment, so it was not runlocally. The change is confined to the side-request guard's input value plus one
new optional stats field (cleared in normal flow) and does not alter the main
compression pipeline; the in-process side-request e2e tests, the overflow-selfheal
suite, and the full 2190-test run cover the touched paths.
Scope note (not in this PR)
#1110 also surfaces a broader question — bili's process-level
globalThis.fetchintercept proxies unrelated in-process callers at all. This PR fixes the acute,
reproducible 413 (the guard). Whether/how to scope the fetch intercept away from
unrelated callers is a larger design decision and is left as a follow-up for owner
discussion rather than bundled here.