Skip to content

fix(overflow): cap output-headroom reservation at a fraction of the window (closes #207) - #301

Merged
ranxianglei merged 2 commits into
masterfrom
2026-09-05_issue207-headroom-cap
Sep 8, 2026
Merged

fix(overflow): cap output-headroom reservation at a fraction of the window (closes #207)#301
ranxianglei merged 2 commits into
masterfrom
2026-09-05_issue207-headroom-cap

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

问题

reserveOutputHeadroom 按模型注册表的最大输出能力ctx.model.maxTokens)全额预留输出预算。maxTokens 占窗口比例大的模型(qwen3.8-27b:262144 窗口 / 131072 maxTokens)输入预算被砍半,kernel 的 75% 强制压缩带在完整窗口 ~37% 处触发,而 host pct 显示 ~34%——两个口径不同,表现为「才 30% 就强制压缩」(#207)。

验证结论(triage)

  • 问题属实:代码路径 src/index.ts context transform → reserveOutputHeadroom(config.modelContextLimit, ctx.model.maxTokens) → kernel decideNudgeusage = tokenCount / modelContextLimitmaxContextLimitPct=0.75)。qwen3.8-27b 算例:limit=131072 → 98304 tokens 即触发,= 完整窗口的 37.5%,与 issue 日志一致。
  • 层次判断:表面现象是「过早压缩 + pct/limit 双口径误导」;根因是预留量取的是注册表输出能力而非有界预算——对 maxTokens≈window/2 的模型不成比例地削减输入预算。
  • 方向取舍
    • 方向 1a(min(注册值, 请求实际配置)):扩展侧不可实现——pi 没有独立于 model 条目的 per-request max_tokens 配置,ctx.model.maxTokens 就是 pi 实际使用的值(registry 或 models.json override),两者恒等,取 min 无意义;
    • 方向 1b(按窗口比例封顶):本 PR 采用,默认 25%;
    • 方向 2(阈值相对完整窗口计算):需改 acp-kernel 语义(跨仓库、发布顺序约束),且 limit 同时承载 truncate/emergency 带,改动面大,不采用;
    • 方向 3(nudge 文案说明):nudge 文案属 kernel prompts(跨仓库);本地以日志消歧替代(见下)。

变更

  • src/overflow-selfheal.tsreserveOutputHeadroom(window, maxOutput, capPct = 1) —— reserved = min(maxOutput, capPct × window)。capPct 语义:0=禁用预留、(0,1)=封顶、≥1/non-finite=旧全额行为(向后兼容,原两参调用不变)。新增导出 DEFAULT_OUTPUT_HEADROOM_MAX_PCT = 0.25
  • src/config.ts + src/user-config.ts:新配置键 outputHeadroomMaxPct(acp.json,number 或 "N%",默认 0.25)
  • src/index.ts:context transform 传入 cap;output-headroom 事件日志新增 cap 字段;[turn] 日志新增 fullWindow 字段(仅当 limit 被预留削减时出现,= 本轮 recenter 后的完整窗口),消除 pct(完整窗口口径)vs limit(预留后口径)混淆
  • 测试:cap 封顶/边界语义/no-op 不复活三组用例(含 issue 的 qwen 算例 262144/131072/0.25 → 196608)+ user-config 键透传
  • 文档:CONFIGURATION.md / CONFIGURATION.zh-CN.md 新键条目;CHANGELOG

效果(qwen3.8-27b)

修复前 修复后
有效 limit 131072(50%) 196608(75%)
75% 带触发点(占完整窗口) ~37.5% ~56.25%

小预留不受影响(int4 版 32K/256K 保持原样);超出预留的超长回复溢出一次后由既有 overflow self-heal(learned window + armed emergency)下一轮恢复。需要旧行为的用户可设 "outputHeadroomMaxPct": 1

验证

npm run typecheck ✓ · npm test 466 pass / 0 fail ✓ · npm run build

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

📦 Built Extension Artifact

Branch: 2026-09-05_issue207-headroom-cap (6f01988)

Option A — Install from npm PR tag (recommended)

pi install npm:billion-context-pi@pr-301

Each push to this PR publishes a new version under the pr-301 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pi-pr301.tgz
pi install ./package

This comment is automatically updated on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 收到 PR,开始独立验证:已拉取分支 2026-09-05_issue207-headroom-cap(单 commit b1b1273),正在核对问题复现、逐文件审查 diff、并在 PR 分支上跑 typecheck / test / build。结果稍后回帖。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 ework agent · qwen3.8-27b

[bot] 🏷 独立验证完成,LGTM ✅(PR b1b1273,单 commit,基于 master cbb753a

查重

无重复。#207 是原始 bug 报告(open),本 PR 是其指定修复;open 的 issue/PR 列表中无其他涉及 output headroom 封顶的条目(相关但不同的:#215 overflow arm、#269 nudge 被忽略,均非同一问题)。

Triage 复核(我独立验证的部分)

  1. 问题属实:master 代码路径 src/index.ts context transform → reserveOutputHeadroom(limit, maxOutput)window - maxOutput,全额预留注册表能力。kernel 默认 maxContextLimitPct: 0.75 已在 acp-kernel dist 中确认(node_modules/acp-kernel/dist/index.js:333)。qwen 算例独立重算:limit = 262144−131072 = 131072,75% 带在 98304 tokens = 完整窗口 37.50% 触发——与 输出预留(output headroom)按最大输出能力全额预留,输入预算被砍半,75% 强制压缩过早触发 #207 日志(tokens=98900 pct=34.42 limit=131072 … usage 75%)完全吻合。双口径误导(host pct 按完整窗口、kernel usage 按削减后 limit)在该日志中同样可见。
  2. 层次判断认同:根因是「预留量取输出能力而非有界预算」,PR 修的是根因(封顶),日志消歧(fullWindow 字段)作为可观测性补充单独处理,没有把症状当病因修。
  3. 方向取舍认同:1a 扩展侧不可实现(pi 无独立于 model 条目的 per-request max_tokens);2/3 跨仓库改 kernel 语义或 prompts,改动面和发布顺序代价大;1b 是扩展侧最小正确解。

代码审查

  • src/overflow-selfheal.ts:cap clamp 到 [0,1],非 finite → 1(legacy);reserved = min(maxOutput, cap×window);degenerate guard(maxOutput ≤ 0 或 ≥ window 不预留)不变——注意 maxTokens≥window 的模型即使有 cap 也不预留,与既有语义一致且有测试锁定。两参调用默认 capPct=1,向后兼容成立。
  • src/index.tsheadroomCap 取自 runtime.adapter.outputHeadroomMaxPct(已核实 runtime.adapter getter 返回经 applyUserConfig 合并后的 adapterRef,user-config 键透传链路成立);parsePercent 支持 number/"N%",NaN 优雅降级为 legacy。fullWindow 在 recenter 之后、预留之前捕获,仅在被削减时出现在 [turn] 日志——口径说明准确。Anthropic 豁免路径未动。
  • src/user-config.ts:接口 + KNOWN 集两处齐全,applyUserConfig 泛型展开自动透传。
  • 测试算术独立验算全部正确(196608 / 229376 / 83616 / 边界 / no-op 不复活)。
  • 文档中英双语 + CHANGELOG(Unreleased 段,未动 version,符合内容提交规范);分支命名符合 YYYY-MM-DD_short-title

两个非阻塞小点:① src/index.tslet fullWindow 从未重新赋值,可用 const;② 默认 0.25 是对所有用户生效的行为变更(非 opt-in)——我认为可接受,因为只有 maxTokens>25% 窗口的模型受影响(恰是出问题的群体),且 "outputHeadroomMaxPct": 1 逃生门已在文档写明。

验证结果(PR 分支实测)

检查 结果
npm run typecheck ✅ clean
npm test ✅ 466 pass / 0 fail(与 PR 声称一致)
npm run build ✅ dist/index.js 543.16 KB(自包含)
效果表复算 ✅ 修复前 131072→带@37.50%,修复后 196608→带@56.25%,与 PR 表格逐格一致

结论:可以合并。⚠️ 按系统规则 PR 合并只能由人工执行——请自行合并:#301 (合并后建议顺手把 #207 关掉,若 GitHub 的 closes #207 自动关闭未生效)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

冲突解决一下

# Conflicts:
#	CHANGELOG.md
#	src/config.ts
#	src/index.ts
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 冲突已解决并推送到 PR 分支(merge commit 6f01988,PR 现已 mergeable)。

冲突点(3 个文件)

合并时发现的一个语义问题(已处理):master 的 #267 修复新增了 applyOutputHeadroom(config, model)src/overflow-selfheal.ts),内部按两参调用 reserveOutputHeadroom(= 旧全额预留),且被 /acp 面板和 acp_status 用作百分比分母。如果只做文本级冲突解决,会出现:nudge 带用封顶后 limit,而面板分母仍是未封顶 limit——正好把 #207 抱怨的「双口径」又引回来。

处理

  • applyOutputHeadroom 增加可选第三参 capPct(默认 1,旧调用点向后兼容),透传给 reserveOutputHeadroom
  • 新增 resolveOutputHeadroomCap(value)(undefined → 默认 0.25,ratio/"N%" 透传),context transform、src/status-tool.tssrc/commands.ts 三个调用点统一用它取用户配置值——面板/acp_status 分母与 nudge 带始终同口径
  • 本 PR 的 cap 日志字段、fullWindow [turn] 字段全部保留,并入 master 的 applyOutputHeadroom 结构
  • 新增测试:applyOutputHeadroom cap 透传(含 qwen 算例 262144/131072/0.25→196608、anthropic 豁免、不 mutate 输入)+ resolveOutputHeadroomCap 语义
  • 顺带修了 tests/commands-kit-panel.test.ts 的 fakeRuntime 缺 adapter 字段(真实 AcpRuntime 有,mock 没跟上)

验证npm run typecheck ✓ · npm test 632 例 629 pass / 0 fail(3 skipped)✓ · npm run build ✓(dist 728.83 KB,master 新增功能所致)

PR 现在 mergeable: true(state blocked 是等 CI/审批,非冲突)。合并仍由你操作。

@ranxianglei
ranxianglei merged commit e3a5bc3 into master Sep 8, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant