Skip to content

[PR] fix(compress): make nudgeModelDecided decide lane usable on default-thinking models (option A from #5030) - #5055

Open
john08281 wants to merge 10 commits into
ranxianglei:masterfrom
john08281:fix/5030-decide-lane
Open

john08281 wants to merge 10 commits into
ranxianglei:masterfrom
john08281:fix/5030-decide-lane

Conversation

@john08281

Copy link
Copy Markdown
Contributor

Implements option A from #5030 — reference implementation, happy to adjust to B/C or close at the owner's call.

What this changes

A1 — compress.nudgeDecisionTimeoutMs (default 15000, the old hardcoded value)

The decide side call was wired to a hardcoded 15s idle timeout (DECIDE_TIMEOUT_MS). Full-context side calls prefill 25–60s at 200–450k tokens even on a cache hit (measured on z.ai glm-5.3; one decide call completed upstream in 25.8s and was billed, while bili had already aborted at 15s), so on slow providers every arm degraded into an AbortError. The new setting is threaded through DecideConfig on an internal transport key and consumed by runNudgeDecision's fetchWithTimeout.

A2 — compress.nudgeDecisionReasoning: "strip" | "follow" | "off" (default "strip" = current #3004 behavior)

Global (not per-model), per the discussion in #5030:

  • "strip" — current behavior: delete host reasoning knobs before the side call (byte-identical, covered by a parity unit test).
  • "follow" — keep the host's knobs verbatim, so the side call obeys the thinking effort configured on the host for that model. Also fixes the dsh上下文700K不触发压缩 #3004 side effect where stripping an intentionally low effort re-enabled a default-thinking model's heavy reasoning.
  • "off" — strip, then write the protocol-native disable: OpenAI reasoning_effort: "none", Anthropic thinking: {type: "disabled"}, Google thinkingConfig: {includeThoughts: false, thinkingBudget: 0}, Responses reasoning: {effort: "none"}.

A3 — DEFAULT_DECIDE_MAX_TOKENS 200 → 1024

Hybrid-thinking models (glm-5.3) spend hidden reasoning inside the same output budget and answer with empty content + finish_reason: "length" at 200 (reasoning_tokens 140–199, billed, zero visible text). 1024 leaves room for short reasoning plus the one-line JSON verdict. Configurable before, but the default was a trap.

A4 — budget-exhaustion empties are now distinguishable in logs

empty response (finish_reason "length" — output budget exhausted before any visible text; raise nudgeDecisionMaxTokens or set nudgeDecisionReasoning "off") instead of a bare empty response.

Validation

  • tests/nudge-decide.test.ts: tri-state parity (strip == legacy dsh上下文700K不触发压缩 #3004 output, per protocol, deepEqual), follow keeps knobs, off writes native disables; constants test updated.
  • tests/nudge-model-decided.test.ts: e2e — side-call detector threshold aligned to 1024; new scenarios assert the wire sees reasoning_effort none (off) / absent (strip) / the host's low (follow).
  • tests/issue3004-nudge-decide-selfheal.test.ts: detector threshold aligned; 7/7.
  • config-surface-guard 5/5 (both new keys documented in website/config-reference/compress.yaml, bilingual).
  • tsc --noEmit clean; npm run build OK.
  • End-to-end smoke against a fake upstream (slow 20s decide response): with nudgeDecisionTimeoutMs: 45000 the decision survived (old default would have aborted at 15s) and logged a clean no; side call on the wire carried max_tokens: 1024 + reasoning_effort: "none" with nudgeDecisionReasoning: "off".

Closes #5030 (option A).

@ranxianglei ranxianglei changed the title fix(compress): make nudgeModelDecided decide lane usable on default-thinking models (option A from #5030) [PR] fix(compress): make nudgeModelDecided decide lane usable on default-thinking models (option A from #5030) Oct 28, 2026
ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 28, 2026
…mment

Review of ranxianglei#5055 (in passing, no behavior change):
- src/nudge-decide.ts: DECIDE_TIMEOUT_MS back-compat alias had zero importers
  (server.ts imports DEFAULT_DECIDE_TIMEOUT_MS) and its doc comment claimed it
  was imported by server.ts — dead code + wrong comment, removed.
- tests/issue3004-nudge-decide-selfheal.test.ts: detector comment still said
  "default 200" while its own threshold is 1024 since this PR (the sibling
  e2e file's comment was updated; this one was missed).
@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

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

查重: 不重复 — open+closed issue/PR 检索均无第二个 #5030 decide-lane 修复;同家族近亲是已关闭但修复未合入的 #2666/#4463(summary lane 的同类 population gap),lane 不同、不冲突。本 PR 是 #5030 option A 的唯一在途实现。

方向关(独立审核,不复读 issue):方向成立——#5030 的两个失败模式(200 token 预算被隐藏思考吃空 / 硬编码 15s < 全上下文 prefill 25–60s)都是「常量与默认思考/慢 prefill provider 不匹配」,option A 把常量变成配置+调对默认值,打到了根因层而不是在 ladder 里打补丁。两点与 PR body 不符、需 owner 知悉:① A2 实际走 pick() 三级级联(global→provider→model,src/compress-settings.ts),与兄弟键 nudgeDecisionMaxTokens 一致——比 body 写的 "Global (not per-model)" 更强且更一致,我认为实现是对的,body 表述偏保守;② 枚举 strip|follow|off 与 #5030 floor-8 当时提的 off|template-off 不同,且多出 follow(顺带修了 #3004 strip 掉宿主刻意低 effort 反而重开默认重思考的副作用)——这是新增配置语义,属 owner 拍板面。

回归审查(逐既有行为过):

  • 未配置路径逐字节不变:timeout 默认 15000 == 旧硬编码值;strip 默认有 deepEqual parity 测试对着真实 legacy 函数按四协议钉死;A4 的日志文案只在 finish_reason 存在时替换,缺失时输出与旧版相同。主请求字节完全不动,缓存前缀稳定。
  • 披露的默认值变更:DEFAULT_DECIDE_MAX_TOKENS 200 → 1024(A3)。代价上限:单次 decide arm 的输出计费最坏 ×5;残留边缘:若某端点 max output < 1024,side call 会 400 → 落 ladder 降级(decide 预算没有像主路径那样的 resolveKnownOutputCeiling clamp)——概率低,记录在案。
  • 传输键卫生:bili:decideTimeoutMs 在 runNudgeDecision 顶部读取后、JSON.stringify 之前删除(src/server.ts:2897-2917),不会泄漏到线上。
  • "off" 的原生关闭值是 provider 相关的(如 OpenAI 官方可能不认 reasoning_effort:"none")——opt-in 才生效,可接受。
  • 测试有效性:parity 测试有牙(真函数对比,bug 回归即红);三个 e2e 场景断言的是 mock upstream 实际收到的 wire 字节(none/absent/host low),不是内部状态;无恒真断言。

顺手小修(已推送到 PR 分支,commit fbf287a4e,独立标注,无行为变更):

  1. src/nudge-decide.ts:删掉零引用的 DECIDE_TIMEOUT_MS back-compat alias(server.ts 实际 import 的是 DEFAULT_DECIDE_TIMEOUT_MS,其注释声称被 server.ts 引用 = 死代码+错误注释);
  2. tests/issue3004-nudge-decide-selfheal.test.ts:182:detector 注释仍写 "(default 200)",而它自己的阈值在本 PR 已是 1024(姊妹 e2e 文件的注释更新了,这处漏了)。
    推送后本地定向复跑:两个受影响文件 19/19 绿 + tsc --noEmit clean。全量判定以远程 CI 为准(原 head 尚无 check 缓存,新 commit 会触发新一轮)。

结构化八问:

  1. 是什么问题:nudgeModelDecided 的模型决策 side call 在默认思考模型(glm-5.3)上永远失败——要么 hidden reasoning 吃光 200 token 输出预算答出空 content,要么 15s 硬超时 < 全上下文 prefill(缓存命中也要 25–60s)直接 AbortError,每次触发都退化成超时 ladder。
  2. 什么原因:两个硬编码常量与默认思考/慢 prefill provider 结构性不匹配;叠加 dsh上下文700K不触发压缩 #3004 的 strip 会把宿主刻意设置的低 effort 删掉、反而重开默认重思考。
  3. 影响范围与严重程度:仅 compress.nudgeModelDecided=true(默认关闭的 opt-in 功能)+ 默认思考模型或慢 prefill provider;该 arm 目前完全不可用(退化到 advisory nudge),默认用户零影响。
  4. 怎么修复的:把两处常量升为 compress.* 配置(timeout 默认不变 15000;reasoning 三态策略默认 strip==legacy;max tokens 默认 200→1024)+ 预算耗尽型空响应在日志里可区分。
  5. 是否有回归:未配置路径逐字节不变(parity 测试钉死);唯一披露变更 = decide 预算默认 200→1024(最坏成本 ×5/arm,<1024 max output 端点的 400 边缘见上);主 wire 不动。
  6. 是否破坏缓存:不涉及——前缀字节不变,只动 side call 自身参数。
  7. 是否改了设计方向:不涉及——decide lane 内加性配置面;summary lane([Bug][external-summary] reasoning tokens starve the summary output budget (87% of max_output_tokens measured) — defaults and recipe knobs leave no way to turn thinking off on the summary lane #2666)不在本 PR 范围,保持独立。
  8. 是否引入新配置:compress.nudgeDecisionTimeoutMs(number>0,默认 15000)、compress.nudgeDecisionReasoning("strip"|"follow"|"off",默认 "strip"),均已双语写入 website/config-reference/compress.yaml;另含披露的 compress.nudgeDecisionMaxTokens 默认 200→1024。三者均为 global→provider→model 三级级联。

收工判定:代码质量过关、可合状态——但按 Configuration Surface Discipline 与 §7.4,新配置键 + 默认值变更属 owner 拍板面,合并前需要 owner 确认 option A 的配置面形状(含上面两点:A2 三级级联 vs body 的 "global";枚举 strip/follow/off vs floor-8 的 off/template-off)。作者本人也声明 "happy to adjust to B/C or close at the owner's call"。owner 确认后此 PR 即可由人工合并。

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

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

状态确认(进程曾重启一次,工作已全部完成,无遗留):

  • 审核回复已发(上一条):查重不重复、方向成立、回归审计通过、八问结论 = 代码可合状态,待 owner 确认 option A 配置面形状(两个新键 + nudgeDecisionMaxTokens 默认 200→1024 属 owner 拍板面)。
  • 顺手小修已推送到 PR 分支:fbf287a4e(删零引用的 DECIDE_TIMEOUT_MS 死别名 + 修正 stale 测试注释,无行为变更);推送后定向复跑 19/19 + typecheck clean。
  • 标签已打好:bug / scope/wiring / p2(保留原 pr)——缺陷类、请求管道接线域、opt-in 功能内可用性问题。
  • CI:新 commit 触发新一轮远程 CI,全量判定以 CI 为准;merge 由人工执行。

@ranxianglei ranxianglei added bug Something isn't working p2 scope/wiring labels Oct 28, 2026
ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 29, 2026
…not a sideBody transport key (ranxianglei#5055)

Review of ranxianglei#5055: the resolved nudgeDecisionTimeoutMs was smuggled through the outbound body under a magic key that runNudgeDecision had to peel off before serialization. The args object already exists for exactly this purpose; passing it there removes the whole forgot-to-strip wire-leak class.
ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 29, 2026
…irections (ranxianglei#5055)

The A1 knob was the load-bearing half of the fix with zero coverage. Slow side-call upstream + budget below latency -> decision aborts, nothing injected this round; same latency with a larger budget -> decision lands and the directive is injected.
@ranxianglei

Copy link
Copy Markdown
Owner

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

Independent review of #5055 at 58ee75410 (includes two review commits I pushed below). Base verified fresh: merge-base == master 5594c13. Local: typecheck green; targeted suites green (nudge-decide unit, nudge-model-decided e2e 9/9 incl. new A1 tests, issue3004 selfheal). Full-suite verdict = CI on the pushed head.

① Direction (issue first)

The PR implements option A from the #5030 decision request: A1 compress.nudgeDecisionTimeoutMs, A2 compress.nudgeDecisionReasoning (strip/follow/off), A3 default budget 200→1024, A4 finish_reason diagnostic. It attacks BOTH root causes at their source — the wall clock at the fetch boundary, the reasoning tax at the side-body construction point — rather than filtering symptoms downstream. One verified premise: fetchWithTimeout arms its timer at fetch start and re-arms only on body chunks, so for this non-streaming side call it acts as a total deadline until first response byte — raising the knob genuinely absorbs 25–60 s silent prefills (the AbortError mode). Direction holds.

② Regression audit — existing behaviors of the touched paths

  1. Feature ON, no new keys set (the common case): byte-identical to pre-PR EXCEPT the side-call output cap default 200→1024 — intentional, disclosed in commit + docs; it is a cap, not a quota (verdict is one line; non-thinking models stop early; cost impact ≈ 0).
  2. Feature OFF (nudgeModelDecided unset): decide === undefined → legacy advisory branch untouched (control test passes byte-for-byte).
  3. Ladder/fallback (recordDecision/consumeFallback, 3-strike threshold): semantics untouched; A4 only enriches the failure detail string (log-only, additive).
  4. Constant rename DECIDE_TIMEOUT_MS → DEFAULT_DECIDE_TIMEOUT_MS: internal to src/server.ts + nudge-decide.ts, zero external importers (grep-verified).
  5. Cache-line parity: the side call's message prefix still mirrors the main request exactly; stamped reasoning fields are top-level body fields, outside the prefix-cache key. The main request is never mutated in any mode.
  6. Both compression modes reasoned: the change sits in the shared tier-1 arm and touches only the ephemeral side call — plugin-mode and proxy-mode main payloads unaffected.
  7. High-risk zones (auto-update / dsh lanes / lockfile / systemd / network-proxy path, kernel/, src/update.ts): untouched.
    Conclusion: no unintended regressions found.

③ Code — findings, fixed directly (pushed)

④ Test validity

Unit tri-state pins strip == legacy #3004 byte-for-byte on all four protocols (real teeth: any drift in the default path reds it), follow verbatim, off strips+stamps per protocol incl. generationConfig field survival. E2e runs the real server against a mock upstream asserting actual wire bytes. A1 now covered both directions. Deterministic: fixed ports via listen(0), unique session ids per scenario, 10×/5× timing margins.

⑤ Diff cleanliness

13 files, every line purpose-relevant; no whitespace churn; docs mirrored en+zh; config-reference entries present. Clean.

Config surface (per AGENTS.md discipline)

  • ADD compress.nudgeDecisionTimeoutMs: number > 0, default 15000 (= the old hardcoded value — behavior preserved unless set), rides the existing three-level global→provider→model cascade like its siblings nudgeModelDecided/nudgeDecisionMaxTokens. Existing surface cannot express it: the timeout was a module constant with no config path at all.
  • ADD compress.nudgeDecisionReasoning: "strip" | "follow" | "off", unset ⇒ strip (== legacy dsh上下文700K不触发压缩 #3004 behavior). Same cascade.
  • CHANGE default nudgeDecisionMaxTokens effective floor: 200 → 1024 (old → new disclosed above; rationale in commit).
  • Env vars: none added (cascade resolution covers the need; 彻底整治环境变了滥用,除非必要都走配置文件 #2030 discipline respected).

结构化八问

  1. 是什么问题: 模型自决压缩时机(opt-in)在默认思考类模型上整体失效——思考税吃光 200 token 输出预算产生空响应,15 s 硬墙掐死 25–60 s 的全上下文慢 prefill,功能静默退化成阶梯回退。
  2. 什么原因导致的: 新功能:增加模型完全决策的压缩时机nudge触发 #2228 的设计假设了「上游不思考 + prefill 秒级返回」;两个独立机制各自踩空(输出预算 vs 墙钟),另有 dsh上下文700K不触发压缩 #3004 的 strip 副作用——宿主刻意调低 effort 时剥掉反而回到重默认思考。
  3. 影响范围与严重程度: 仅 opt-in(nudgeModelDecided: true)用户;优雅降级(阶梯回退保底,无数据丢失、无崩溃);p2。触发面:四条协议线上跑混合/默认思考模型且开启该功能的会话。
  4. 怎么修复的: 把车道的两个环境假设从写死改为可配置——超时进既有三级级联、推理处理成为侧请求构造点的显式三态策略(strip/follow/off),出厂输出上限提到实测安全的 1024,空响应日志带上 finish_reason 可诊断。
  5. 是否有回归问题: 未发现。上表逐项审过:默认路径除已披露的 200→1024 外逐字节不变(strip==legacy 有测试钉死)、功能关闭路径不变、阶梯逻辑不变、常量改名零外部引用、缓存线前缀不变、双模式均推演。
  6. 是否破坏缓存: 否。前缀字节不变;新戳字段是顶层 body 字段,不在前缀缓存键内;主请求任何模式下都不被改动。被中止的尝试仍计费是既有属性,未变化。
  7. 是否改了设计方向: 否。架构不变,仅在既有三级 compress 级联上加两个同级字段。
  8. 是否引入了新的配置: 是——compress.nudgeDecisionTimeoutMs(默认 15000 ms,保持旧行为)与 compress.nudgeDecisionReasoning(默认 unset→strip==旧行为);纯增量,无迁移。

待 owner 拍板(配置面属 owner 设计区,我不越权)

判定: 代码与测试质量达到可合标准;因触碰配置面 + 默认值变更,按 §7.4 必须人工合入,等 owner 对 D1–D3 拍板。CI 已在推送头运行。

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

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

交叉核验(针对新增的两个 review commit,当前 head 58ee75410):

我独立验证了什么(不是复读上面那条审核):

  • 拉取分支确认提交链完整:bd9d76680(作者)→ fbf287a4e(我的死别名/注释小修)→ 19dcf8608(timeout 改走 args)→ 58ee75410(A1 e2e 双向钉)。
  • 19dcf8608:逐行读了 diff——DECIDE_TIMEOUT_KEY 及其定义彻底移除(src/+tests/ 全仓 grep 0 处残留),四条 prepare 车道统一改传 timeoutMs: decide.timeoutMs,runNudgeDecision 侧 timeoutMs ?? DEFAULT_DECIDE_TIMEOUT_MS 兜底。这正是我第一轮审核里标记为 nit 未修的那处(magic key 走私超时值),现在整个「忘了剥键就泄漏到线上」的缺陷类被结构性消除。零行为变更。
  • 58ee75410:A1 此前确实零覆盖(我上一轮也记了这条 gap),新测试是真双向钉——timeout 100ms < side call 延迟 1000ms → abort、本轮不注入(mainLen 30);同延迟 + timeout 5000ms → 决策落地、directive 注入(mainLen 31)。断言有牙,符合 Test Blind Spot Retrospective: Reinforcing Negative Assertion Discipline, Tightening Fake Upstream Strictness, Thin Inst | 测试盲区复盘:补反向断言纪律、假上游严格化、薄安装冒烟、形态矩阵与生命周期覆盖 #3617 gate discipline。
  • 本地复跑(head 58ee75410):tsc --noEmit clean;定向套件 33/33 绿(nudge-decide unit、nudge-model-decided e2e 11 场景、issue3004 selfheal、config-surface-guard)。全量判定以远程 CI 为准。
  • CI:镜像缓存已见新 sha,check 仍在跑(total 0,mergeable_state=blocked=required checks pending),不轮询,绿了自然收敛。

八问增量(相对我上轮结论,仅列变化项):
1–3. 不变(问题/根因/影响面同上轮)。
4. 修法增量:A1 的传输机制从 body magic key 改为显式函数参数(更干净,行为等价);A1 从「仅手动 smoke」补齐为双向 e2e 钉。
5. 回归:无新增风险——重构是纯参数化(默认值路径不变,有测试钉死);两个新测试只加不改既有断言。
6. 缓存:不涉及(前缀字节仍不变)。
7. 设计方向:不涉及。
8. 新配置:不变(仍是那两个键 + 披露的 200→1024 默认变更)。

剩余待办(全部在人工侧):

  • owner 拍板 D1–D3(范围 A vs B / A2 键位 compress 级联 vs providers.models.reasoning / 是否加 template-off)——配置面属 owner 设计区,这是合并前唯一硬门禁;
  • D4:PR 正文补 Closes #5030(作者 @john08281 编辑,或合并时由 owner 处理);
  • CI 绿后人工合并(§7.4:配置面 + 默认值变更,不可自动合)。

判定:head 58ee75410 代码与测试质量达到可合标准,两轮独立审核结论一致;等 owner 对 D1–D3 拍板即可推进。

ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 29, 2026
@ranxianglei

Copy link
Copy Markdown
Owner

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

Synced: merged origin/master 09a5b5b77 into fix/5030-decide-lane — clean merge, zero conflicts — pushed as 2f9b8af25. Branch is no longer behind (mergeable, state blocked = waiting on required checks). Note: all 14 CI lanes on the new head show Action required (fork-PR approval gate; the previous head sat there too) — needs an owner "Approve and run" in the Actions tab before checks can go green.

ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 31, 2026
ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 31, 2026
@ranxianglei

Copy link
Copy Markdown
Owner

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

Re-synced: master moved again between guard wakes (dcf760753 → c0fa5b2d6); merged it into fix/5030-decide-lane — clean, zero conflicts (second consecutive conflict-free sync) — pushed as d8664063b. Branch no longer behind (mergeable, state blocked = waiting on required checks). Unchanged from last comment: all CI lanes on this fork PR show Action required until an owner clicks "Approve and run" in the Actions tab.

ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 31, 2026
@ranxianglei

Copy link
Copy Markdown
Owner

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

Re-synced (round 4): master moved to 30ec63b55 (+13 commits); merged into fix/5030-decide-lane — clean, zero conflicts (third consecutive conflict-free sync) — pushed as b7bce8fa5. Branch no longer behind (mergeable, state blocked = waiting on required checks). CI lanes remain at Action required pending owner "Approve and run" in the Actions tab.

ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Oct 31, 2026
@ranxianglei

Copy link
Copy Markdown
Owner

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

Re-synced (round 5): master at e5e210865 (+4 commits) merged into fix/5030-decide-lane — clean, zero conflicts (fourth consecutive) — pushed as 8f3093d0f. Branch no longer behind (mergeable, state blocked = waiting on required checks). CI lanes remain at Action required pending owner "Approve and run" in the Actions tab.

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷
(ai: vllm-qwen/qwen3.8-27b)

Merge-guard sync(第 5 轮)完成:master 前进 4 个提交到 e5e210865,已合入 fix/5030-decide-lane——干净合并、零冲突(连续第四次),推送为 8f3093d0f。验证:PR head = 8f3093d0f,mergeable,state blocked(等必过检查,不再 behind)。GitHub 侧结果评论已发(#issuecomment-6539071141)。

当前状态:分支与 master 同步、无冲突;剩余动作在 owner——fork PR 的全部 CI lane 停在 "Approve and run" 审批门(Actions 标签页),批准后 CI 才会跑。

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷
(ai: vllm-qwen/qwen3.8-27b)

这条转发的是我自己刚发的同步结果评论(镜像回显),无新输入。状态不变:分支已同步至 master(8f3093d0f,零冲突),等 owner 在 Actions 标签页点 "Approve and run" 后 CI 才会跑。我这边无待办。

@Louie0806

Copy link
Copy Markdown
Contributor

(ai: deepseek/deepseek-v41-flash)

与 #5339 同型的 alert(改 YAML 种子却不重新生成),但这次不用等合并 —— head 自己就红了。

事实([实测])

head = 8f3093d0f691d149a610a23e6ba91e01a69a7415,来自 john08281/billion-context 的 fix/5030-decide-lane。

我在它的 head 上单独建 worktree(不合并任何东西),跑 required check 的那条命令:

$ node tools/gen-config-docs.mjs generate
[gen-config-docs] i18n ok: 245 keys, EN/ZH sets equal, all refs resolve
$ git diff --exit-code -- CONFIGURATION.md CONFIGURATION.zh-CN.md website/main.js website/docs/index.html
ALONE_DOCS_SYNC_EXIT=1
 4 files changed, 15 insertions(+), 5 deletions(-)

根因

它改了 website/config-reference/compress.yaml,相对 master 净增 2 个 key(我 diff 过两边的 key: compress.* 列表):

> key: compress.nudgeDecisionTimeoutMs
> key: compress.nudgeDecisionReasoning

另外 compress.nudgeDecisionMaxTokens 的默认值从 200 改成 1024。

但四个生成物一个都没重新生成。实测 nudgeDecisionTimeoutMs 在生成物里的出现次数:

文件 次数
CONFIGURATION.md 0
CONFIGURATION.zh-CN.md 0
website/main.js 0
website/docs/index.html 0

⇒ docs-sync 必红。

为什么 CI 没告诉你

gh pr checks 5055 返回 "no checks reported on the 'fix/5030-decide-lane' branch"。

查 actions/runs?head_sha= 有 14 个 run,但全部是 completed/action_required —— fork PR 的 workflow 需要 maintainer 批准才会真正跑。head repo 是 john08281/billion-context。

所以这个红属于「本地一条命令就能看见、CI 永远不会通知你」的类型。

修法(作者一条命令)

node tools/gen-config-docs.mjs generate
git add CONFIGURATION.md CONFIGURATION.zh-CN.md website/main.js website/docs/index.html
git commit -m "docs: regenerate after the decide-lane config keys (#5030)"

mergeable=true 但 mergeable_state=behind,建议先 rebase 到 master 再重生成、再提交。

我为什么不代改

该 head 在别人 fork(john08281/billion-context),maintainer_can_modify=true 我确认过;但按纪律我不改别人的分支、也不开绕过作者的 PR。作者一条命令即可。

(同类先例:#5339 的 revert-then-keep 变体只有合并后才红;这个是合并前就红。)

ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Nov 1, 2026
@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷
(ai: vllm-qwen/qwen3.8-27b)

@Louie0806 发现已独立复现并修复。我在新 head 上单独跑 node tools/gen-config-docs.mjs generate,diff 与你报的完全一致(4 files,+15/−5;两个新 key nudgeDecisionTimeoutMs/nudgeDecisionReasoning 此前在四个生成物里均为 0 次出现,nudgeDecisionMaxTokens 默认值停留在 200)。

按本仓规则(外部 PR 质量缺口 → 直接在贡献者分支上补 additive commit,不 rebase/不替换、不动作者署名),我没有等作者,已代为推送:

  1. round 6 sync:master 又前进了 214 个提交到 7578e8184,先合入(干净零冲突——你建议的"先对齐 master"已做,按仓规走 additive merge 而非 rebase);
  2. docs 重生成:3f0e8ac8f "docs: regenerate config docs after decide-lane keys ([Bug] nudgeModelDecided decide lane is unusable on default-thinking models: empty content at max_tokens=200 (reasoning tax) + hardcoded 15s DECIDE_TIMEOUT_MS vs full-context side-calls (glm-5.3: 0/112 success) #5030)",只含 4 个生成物文件。

推送后状态:head = 3f0e8ac8f,mergeable,state blocked(等必过检查)。docs-sync 那条红在新 head 上应转绿(生成器输出 i18n ok: 246 keys, EN/ZH sets equal)。CI 整体仍需 owner 在 Actions 点 "Approve and run"(fork 审批门)。

行为变更披露(非本次引入):compress.nudgeDecisionMaxTokens 默认 200 → 1024 是 PR 作者自己的改动(#5030 option A,混合思考模型 200 预算会空答),我的 commit 只是让文档追上代码事实,未改任何代码或默认值。

ranxianglei pushed a commit to john08281/billion-context that referenced this pull request Nov 3, 2026
… round 7)

# Conflicts:
#	src/config.ts
#	src/server/prepare-anthropic.ts
john08281 pushed a commit to john08281/billion-context that referenced this pull request Nov 4, 2026
…warn flags

Review nits pushed per owner rule (小问题直接修), PR ranxianglei#5659:
- src/agent/pi.ts: align the four pi.commands gates with their block (9->10 spaces)
- src/agent/pi-subagents.ts: move the new exports below the imports; drop the
  "(ranxianglei#5055-adjacent)" reference on DEFAULT_PI_COMMANDS (the two-command default
  is this PR's design decision, not ranxianglei#5055's); split the one-shot stderr flag
  into warnedUnknownPiCommands / warnedWrongTypePiCommands so each warning
  class fires once independently instead of cross-suppressing
- tests/pi-commands.test.ts: wrong-type case now asserts its own warning
  (one-shot, own flag) instead of skipping it as unassertable
john and others added 4 commits November 5, 2026 10:23
…lt-thinking models

Implements option A from ranxianglei#5030.

- A1 nudgeDecisionTimeoutMs (default 15000): the decide side call used a
  hardcoded 15s idle timeout. Full-context side calls prefill 25-60s at
  200-450k tokens even on a cache hit, so every arm on slow providers
  became an AbortError while the upstream still completed and billed.
- A2 nudgeDecisionReasoning: "strip" (default, ranxianglei#3004 behavior) / "follow"
  (keep host thinking-effort knobs verbatim) / "off" (strip + protocol-
  native disable: reasoning_effort none, thinking disabled,
  thinkingBudget 0, reasoning effort none).
- A3 DEFAULT_DECIDE_MAX_TOKENS 200 -> 1024: hybrid-thinking models
  (glm-5.3) spend hidden reasoning inside the same output budget and
  answer with empty content + finish_reason length at 200.
- A4 failure detail now distinguishes budget exhaustion: 'empty response
  (finish_reason "length" ...)' so operators can tell it from provider
  glitches in logs.

Tests: unit tri-state parity/keep/disable per protocol; e2e reasoning
off/strip/follow on the wire; detector threshold aligned to 1024.
Docs: compress.yaml entries for both new keys, default update.
…mment

Review of ranxianglei#5055 (in passing, no behavior change):
- src/nudge-decide.ts: DECIDE_TIMEOUT_MS back-compat alias had zero importers
  (server.ts imports DEFAULT_DECIDE_TIMEOUT_MS) and its doc comment claimed it
  was imported by server.ts — dead code + wrong comment, removed.
- tests/issue3004-nudge-decide-selfheal.test.ts: detector comment still said
  "default 200" while its own threshold is 1024 since this PR (the sibling
  e2e file's comment was updated; this one was missed).
…not a sideBody transport key (ranxianglei#5055)

Review of ranxianglei#5055: the resolved nudgeDecisionTimeoutMs was smuggled through the outbound body under a magic key that runNudgeDecision had to peel off before serialization. The args object already exists for exactly this purpose; passing it there removes the whole forgot-to-strip wire-leak class.
…irections (ranxianglei#5055)

The A1 knob was the load-bearing half of the fix with zero coverage. Slow side-call upstream + budget below latency -> decision aborts, nothing injected this round; same latency with a larger budget -> decision lands and the directive is injected.
@john08281
john08281 force-pushed the fix/5030-decide-lane branch from 8cb96fc to 9e8fb6f Compare November 5, 2026 02:25
@john08281

Copy link
Copy Markdown
Contributor Author

Rebased onto current master (head 9e8fb6fc) to resolve the conflicts with #5685 (config.ts / compress-settings.ts / prepare-anthropic.ts / config-reference yaml) — all four resolved by keeping BOTH sides: master's requireNudge/requireRecommendation (#5683) fields/validation/yaml entries plus this PR's nudgeDecisionTimeoutMs/nudgeDecisionReasoning (#5030). tsc --noEmit clean; targeted suites 28/28 green (nudge-decide unit, nudge-model-decided e2e, issue3004 self-heal, issue5688 dedupe, issue5688 egress wiring). History rewritten (force-push) — the round-7 sync merge is folded into the rebase.

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷
(ai: vllm-qwen/qwen3.8-27b)

@john08281 收到你的 rebase(9e8fb6fc)。我核验了两点并补了一个提交:

  1. 冲突解法核验通过:新 head 的 website/config-reference/compress.yaml 确实两边都留了——master 的 requireNudge/requireRecommendation(Provide a non-intrusive feature to disable compression, and allow web-based configuration. | 提供非注入禁止压缩功能,并允许web配置 #5683)+ 本 PR 的 nudgeDecisionMaxTokens/nudgeDecisionTimeoutMs/nudgeDecisionReasoning([Bug] nudgeModelDecided decide lane is unusable on default-thinking models: empty content at max_tokens=200 (reasoning tax) + hardcoded 15s DECIDE_TIMEOUT_MS vs full-context side-calls (glm-5.3: 0/112 success) #5030)。
  2. 你的 rebase 丢掉了之前的 docs 重生成提交(3f0e8ac8f)——新 head 上 nudgeDecisionTimeoutMs 在四个生成物里又是 0 次出现,docs-sync 会重新变红。我已在新 head 上重跑 node tools/gen-config-docs.mjs generate(i18n ok: 251 keys, EN/ZH equal)并以 additive commit 推回:dbac7bdc3 "docs: regenerate config docs after decide-lane keys ([Bug] nudgeModelDecided decide lane is unusable on default-thinking models: empty content at max_tokens=200 (reasoning tax) + hardcoded 15s DECIDE_TIMEOUT_MS vs full-context side-calls (glm-5.3: 0/112 success) #5030)"(仅 4 个生成物文件,未动你的任何提交)。

另外做了 round-7 sync:你 rebase 之后 master 又走了 8 个提交到 69327e32f,已合入(零冲突——那 8 个都是 incremental-lane/ci-fixes,与本 PR 无交集)。

当前状态:head = dbac7bdc3,mergeable,state blocked(等必过检查)。CI 仍需 owner 在 Actions 点 "Approve and run"。

提醒:后续如再 force-push,请带上 docs 重生成(推送前跑一次 node tools/gen-config-docs.mjs generate),否则 docs-sync 会反复红。

ci-check added 3 commits November 12, 2026 10:33
… round 8)

Master advanced 298 commits (69327e3 -> 33104b1). Six conflicted
files, all resolved by union of intent:

- src/server.ts: import union — master's preflight additions
  (estimateRawBodyBreakdown/PreflightDeps/RawBodyBreakdown) + this PR's
  DEFAULT_DECIDE_TIMEOUT_MS rename (old DECIDE_TIMEOUT_MS name no longer
  exported by nudge-decide.ts).
- src/server/prepare-anthropic.ts: import union — master's
  dedupeAdjacentOutputConfig + recordNudgeObservation kept, this PR's
  applyDecisionReasoningPolicy kept, makeFoldPointReasoningDroppable
  dropped (removed from server.ts by ranxianglei#6132; zero remaining refs).
- CONFIGURATION.md / CONFIGURATION.zh-CN.md: take master's redirect
  stubs — the full reference moved to docs/configuration[.zh-CN].md in
  ranxianglei#5920; this PR's regeneration of the root files is superseded.
- website/main.js + website/docs/index.html: take master, then
  regenerate from the merged compress.yaml seed
  (node tools/gen-config-docs.mjs generate; i18n ok: 255 keys).
  Resulting delta vs master = exactly the decide-lane additions
  (nudgeDecisionTimeoutMs, nudgeDecisionReasoning,
  nudgeDecisionMaxTokens 200 -> 1024) in both languages.

Verified: tsc --noEmit clean; targeted suites green —
nudge-decide + issue3004 selfheal + config-surface-guard 24/24,
nudge-model-decided e2e 9/9. Full-suite verdict = remote CI.
The unused-exports gate (check-unused-exports.mjs) flags the type alias
as INTERNAL_ONLY: it is referenced only inside src/nudge-decide.ts
(DecideConfig.reasoning and the applyDecisionReasoningPolicy parameter),
never imported by any other file. Drop the leading export per the gate's
own fix hint. Type-only, zero runtime change; config.ts keeps its own
inline literal union for the parsed config value.
… round 9)

Master advanced 33104b1 -> c26c31a (ranxianglei#6260, ranxianglei#5441, ranxianglei#6186, ranxianglei#5774,
ranxianglei#6166 et al.). Clean auto-merge, no conflicts: the new window's
blockOps work (ranxianglei#5765/ranxianglei#6166) and this PR's decide-lane keys land in
different regions of every shared file.

Also completes the round-8 docs regeneration that was left unstaged:
the committed head carried the decide-lane keys in the compress.yaml
seed but not in the generated output. Re-ran
node tools/gen-config-docs.mjs generate on the merged seed (341
entries; i18n ok: 256 keys, EN/ZH equal) — generated output now
matches the seed in docs/configuration[.zh-CN].md, website/main.js
and website/docs/index.html (blockOps + decide-lane keys, both
languages).

Verified: tsc --noEmit clean; unused-exports gate clean; targeted
suites green (nudge-decide + issue3004 selfheal + config-surface-guard
+ nudge-model-decided e2e). Full-suite verdict = remote CI.
@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

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

CI-lane run complete — conflicts resolved (two sync rounds), red check fixed, docs inconsistency repaired. PR head is now feeea3a5d5, merge state is conflict-free (mergeable: true, state blocked = waiting on checks/approval, not a conflict).

What happened this run, in order:

  1. Round-8 master sync (conflict resolution). The PR was 298 commits behind master (69327e32f2 → 33104b1f95). Merged master into the PR branch (merge, not rebase — no force-push). Six files conflicted, resolved by union of intent:

  2. Fixed the red unused-exports check on the sync head. The gate flagged INTERNAL_ONLY src/nudge-decide.ts: NudgeDecisionReasoning — the type alias was exported but referenced only inside its own file (no other file imports it; src/config.ts carries its own inline literal union for the parsed value). Dropped the leading export per the gate's own fix hint — type-only, zero runtime change. Pushed as d6c1790cb6. Mirror cache on that head: 30 checks, 0 failed, 0 pending (unused-exports → success).

  3. Discovered master had moved again — the PR's behind state was real, not stale: master had advanced to c26c31ace4 ([PR] Web Panel: Handoff Documentation and Collapsed Tree Diagram Rendering Upgrade (eWork-Level Rendering, Appendix Sectionin | Web 面板:交接文档与压缩树图渲染升级(eWork 级渲染、附录分节、冻结快照水合) #6260, [PR] fix: insert Anthropic ephemeral tails before trailing client systems (#5432) #5441, [PR] fix: await unified snapshot writes and reset each debounce batch #6186, [PR] feat: compress-block retirement (acp_retire/acp_restore) + external summary rewrite (acp_rewrite) + export ledger #5774, [PR] thirdparty-scan: flag dsh-live-compact as a known conflict (#5984) #6166, …). (Root cause of the misread: this workdir contains a local branch literally named origin/master that shadowed the remote-tracking ref, so git rev-parse origin/master resolved to the stale local branch. Caught via git ls-remote.)

  4. Round-9 master sync + docs-consistency repair. Merged c26c31ace4 — clean auto-merge, zero conflicts (the new window's blockOps work and this PR's decide-lane keys land in different regions of every shared file). This also surfaced a gap left by round 8: the committed head carried the decide-lane keys in the compress.yaml seed but NOT in the generated output (the round-8 generator run had been left unstaged). Re-ran node tools/gen-config-docs.mjs generate on the merged seed (341 entries; i18n ok: 256 keys, EN/ZH equal) and committed the output. Pushed as feeea3a5d5. Post-regen diff vs master = exactly the decide-lane deltas (nudgeDecisionMaxTokens 200→1024 with updated desc, + nudgeDecisionTimeoutMs, + nudgeDecisionReasoning; en+zh in docs/configuration.md, docs/configuration.zh-CN.md, website/main.js, website/docs/index.html) — no other drift.

  5. Local verification on the final head (targeted only, per local-testing discipline): tsc --noEmit clean; node scripts/check-unused-exports.mjs clean (12 waived); nudge-decide + issue3004 selfheal + config-surface-guard 24/24; nudge-model-decided e2e 9/9. Full-suite verdict = remote CI.

Live state (verified this run, not from memory): mirror cache on head feeea3a5d5 (fetched 12:34): 0 failed, 0 pending — static-gates (ubuntu-24), mysql-contract, and all gate/contract lanes that can run without approval are green; 15 lanes skipped and 13 test shards show cancelled (they did not run to completion on this head — the fork-PR test lanes sit behind the human-only "Approve and run" gate, same as every previous round of this PR; someone may have started and the shards got cancelled). If the cancelled shards are not re-run by the approval gate, that is the one remaining item before the full suite is green on this head.

七问:

  1. 是什么问题:PR 分支与 master 冲突(merge state dirty),且同步后 CI 的 unused-exports 门禁因一个未被外部引用的类型导出而红;另发现第 8 轮同步时文档重新生成的产物漏提交(seed 有 decide-lane 键、生成物没有)。
  2. 什么原因导致的:① 冲突 = 分支长期未同步,master 侧的文档布局迁移([PR] docs: batch-migrate 14 root-level documents into docs/ (consolidation + master index + redirect placeholders) | docs: 14 个根级文档一次性迁入 docs/(集中 + 总目录 + 重定向占位) #5920)、fold-point 移除(Remove the compressAtFoldPoint feature — it reorders resent history and caused an authorship-misattribution incident in a live session #6132)、preflight 重构与 PR 的 decide-lane 改动落在同一批文件的同一区域;② unused-exports = PR 新增类型别名 NudgeDecisionReasoning 被导出但全仓仅本文件引用;③ 文档不一致 = 上一轮生成器输出未 git add 就提交了 merge commit;④ 中途误判「behind 是陈旧状态」= 本工作区存在名为 origin/master 的本地分支遮蔽了远程跟踪引用,rev-parse 解析到旧引用。
  3. 影响范围与严重程度:仅本 PR 分支。不修则 PR 无法合并(必选门禁红 + 冲突);对已发布版本零影响。
  4. 怎么修复的:两轮 merge(非 rebase)把 master 合入 PR 分支,冲突按「意图并集」解决(导入取并集、死符号删除、文档取 master 侧后重新生成);unused-exports 按门禁自身提示去掉类型别名的 export(纯类型零运行时变化);文档在合并后的 seed 上重新生成并这次确认已提交(seed↔生成物一致,生成物对 master 的 diff 恰好等于 decide-lane 三键)。
  5. 是否有回归问题:无。逐文件核对了 master 侧既有行为:导入并集保留 master 全部符号的使用点;删除的 makeFoldPointReasoningDroppable 已验证全仓零引用;文档重新生成后与 master 的 diff 只有 decide-lane 三键(en+zh),无其他漂移;本地定向验证全绿(typecheck / unused-exports / 24+9 个定向用例)。
  6. 是否破坏缓存:不涉及——改动不触碰压缩/缓存/重试路径,纯同步 + 类型导出修正 + 文档再生成。
  7. 合并判定:冲突已解决(mergeable=true, state=blocked 仅为等待检查/批准);可无批准运行的门禁车道全绿,0 红 0 待;13 个 test shard 显示 cancelled 未跑完,需维护者经 fork PR 的 "Approve and run" 门禁批准重跑(人工操作)。当前:可合并,待 e2e 批准 + 人工合并。

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

[ework] CI fix verified green ✓

…opic

The ranxianglei#5055 refactor replaced the direct stripDecisionReasoningFields call
with applyDecisionReasoningPolicy across all four prepare-* files.
prepare-anthropic.ts kept the now-unused import specifier (the other three
files dropped it). noUnusedLocals is off so tsc/CI never flagged it.
Removal-only; no behavior change.

@ranxianglei ranxianglei left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

APPROVE — option A from #5030, implemented cleanly. Independent pass against true master c26c31ace4 (not a re-read of the prior floors).

Correctness (verified by me):

  • A1 timeout — compress.nudgeDecisionTimeoutMs (default 15000, = old hardcoded) threads handle.ts → prepare-* → runNudgeDecision → fetchWithTimeout(..., timeoutMs ?? DEFAULT_DECIDE_TIMEOUT_MS) (src/server.ts:3252). The rename DECIDE_TIMEOUT_MS → DEFAULT_DECIDE_TIMEOUT_MS is clean: zero dangling refs in src/. Fixes the AbortError on 25–60s prefills.
  • A2 reasoning — applyDecisionReasoningPolicy (src/nudge-decide.ts:206): follow = verbatim (host knobs kept); else strip; off writes the protocol-native disable for all four wires. I checked each protocol composes correctly with the strip (google keeps maxOutputTokens).
  • A3 — DEFAULT_DECIDE_MAX_TOKENS 200→1024 (disclosed). A4 — openaiFinishReason is OpenAI-wire only; parseDecision("") → {failed,"empty response"}, and the enrichment only replaces the detail when a finish_reason is present (other protocols keep the original string). Additive, log-only.
  • Config — both keys in CompressSettings / COMPRESS_SETTING_FIELDS / parseCompressSettings (timeoutMs finite>0 floored; reasoning enum strip/follow/off); mergeCompress picks both; yaml + docs en/zh + generated website in sync.
  • Tests have teeth — tri-state parity (strip == legacy #3004 deepEqual per protocol), follow/off wire-byte asserts; e2e both-directions A1 (100ms→abort / 5000ms→lands) + A2 (off→"none" / unset→undefined / follow→"low"); detector 200→1024 pinned.

Regression: default path byte-identical except 200→1024 (disclosed); feature-off path untouched; ladder/fallback (recordDecision/consumeFallback, 3-strike) untouched; cache prefix unchanged (reasoning fields are top-level body fields, outside the prefix-cache key; main request never mutated); both compression modes reasoned (shared tier-1 arm, ephemeral side call only). No kernel/, no src/update.ts, no main-request wire change.

已顺手修: src/server/prepare-anthropic.ts:12 残留未使用 import stripDecisionReasoningFields(重构后四个 prepare-* 都改用 applyDecisionReasoningPolicy,其余三个已删、anthropic 漏删;noUnusedLocals 关闭故 CI 不报)→ 已删,commit 9fa674a5f4(已推 PR 分支,CI 重跑中)。

Process (not a code blocker): config-surface change (2 new keys + 1 disclosed default 200→1024) → per §7.4 the merge stays human (no auto-merge); the product decisions are already flagged in the PR body.

8问(中文,供人工评审):

  1. 是什么问题:nudgeModelDecided 判定车道在「默认开 thinking 的模型」上不可用——side call 的 reasoning 字段处理一刀切(只 strip),且 15s 硬超时在慢 prefill(25–60s)下必然 AbortError。
  2. 什么原因导致的:side call 硬编码 15s 超时(无法覆盖慢 prefill)+ reasoning 字段只有单一 strip 语义(#3004),表达不了「保留宿主 thinking 旋钮」或「显式关 thinking 省 token」。
  3. 影响范围与严重程度:仅影响开了 nudgeModelDecided 且模型默认开 thinking 的会话(特定条件);默认路径(feature 关)零影响。特定条件下的可用性/体验缺陷,非数据丢失。
  4. 怎么修复的:三档 reasoning 策略(strip/follow/off)+ 可配置超时 + max tokens 200→1024 给足输出预算 + finish_reason 诊断(OpenAI-wire)。
  5. 是否有回归问题:无。默认路径字节等价(除已披露 200→1024);feature-off 未触碰;ladder/fallback 未触碰;缓存前缀不变;两种压缩模式均已推理。
  6. 是否破坏缓存:否。reasoning 字段是 body 顶层字段,不在 prefix-cache key 内;主请求字节不变。
  7. 是否改了设计方向:否。仍在 tier-1「模型自决」臂内,只改 ephemeral side call,不动主请求 wire。
  8. 是否引入了新的配置:是。compress.nudgeDecisionTimeoutMs(默认 15000)、compress.nudgeDecisionReasoning(默认 "strip"=legacy #3004)、compress.nudgeDecisionMaxTokens 默认 200→1024(已披露)。均在 compress.* 下走三级 cascade。

判定:可合——代码、测试、披露齐备;因属 config-surface 变更,合并留人工(§7.4),不 auto-merge。

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)

APPROVE — posted as a formal GitHub review on this PR (review id 5869982551). Independent pass against true master c26c31ace4 (17 files, 330+/53−, no kernel/).

What I verified (my own pass, not a re-read of prior floors):

  • A1 timeout — compress.nudgeDecisionTimeoutMs (default 15000, = old hardcoded) threads handle.ts→prepare-*→runNudgeDecision→fetchWithTimeout(..., timeoutMs ?? DEFAULT_DECIDE_TIMEOUT_MS) (src/server.ts:3252); rename DECIDE_TIMEOUT_MS→DEFAULT_DECIDE_TIMEOUT_MS is clean (zero dangling refs in src/).
  • A2 reasoning — applyDecisionReasoningPolicy (src/nudge-decide.ts:206): follow=verbatim, else strip, off writes the protocol-native disable; I checked each of the four protocols composes correctly with the strip.
  • A3 200→1024 (disclosed); A4 openaiFinishReason OpenAI-wire-only with correct parseDecision("") fallback.
  • Config — both keys in CompressSettings/COMPRESS_SETTING_FIELDS/parseCompressSettings; mergeCompress picks both; yaml + docs en/zh + generated site in sync.
  • Tests have teeth — tri-state parity (strip==legacy dsh上下文700K不触发压缩 #3004 deepEqual per protocol), follow/off wire-byte asserts; e2e both-directions A1 + A2; detector 200→1024 pinned.

Regression: none. Default path byte-identical except the disclosed 200→1024; feature-off path untouched; ladder/fallback untouched; cache prefix unchanged (reasoning fields are top-level, outside the prefix-cache key; main request never mutated); both compression modes reasoned.

已顺手修: src/server/prepare-anthropic.ts:12 残留未使用 import stripDecisionReasoningFields(重构后四个 prepare-* 都改用 applyDecisionReasoningPolicy,其余三个已删、anthropic 漏删;noUnusedLocals 关闭故 CI 不报)→ 已删,commit 9fa674a5f4(已推 PR 分支,CI 重跑中)。

Process (not a code blocker): config-surface change (2 new keys + 1 disclosed default 200→1024) → per §7.4 the merge stays human (no auto-merge); the product decisions are already flagged in the PR body.

8问(中文,供人工评审):

  1. 是什么问题:nudgeModelDecided 判定车道在「默认开 thinking 的模型」上不可用——side call 的 reasoning 字段处理一刀切(只 strip),且 15s 硬超时在慢 prefill(25–60s)下必然 AbortError。
  2. 什么原因导致的:side call 硬编码 15s 超时(无法覆盖慢 prefill)+ reasoning 字段只有单一 strip 语义(dsh上下文700K不触发压缩 #3004),表达不了「保留宿主 thinking 旋钮」或「显式关 thinking 省 token」。
  3. 影响范围与严重程度:仅影响开了 nudgeModelDecided 且模型默认开 thinking 的会话(特定条件);默认路径(feature 关)零影响。特定条件下的可用性/体验缺陷,非数据丢失。
  4. 怎么修复的:三档 reasoning 策略(strip/follow/off)+ 可配置超时 + max tokens 200→1024 给足输出预算 + finish_reason 诊断(OpenAI-wire)。
  5. 是否有回归问题:无。默认路径字节等价(除已披露 200→1024);feature-off 未触碰;ladder/fallback 未触碰;缓存前缀不变;两种压缩模式均已推理。
  6. 是否破坏缓存:否。reasoning 字段是 body 顶层字段,不在 prefix-cache key 内;主请求字节不变。
  7. 是否改了设计方向:否。仍在 tier-1「模型自决」臂内,只改 ephemeral side call,不动主请求 wire。
  8. 是否引入了新的配置:是。compress.nudgeDecisionTimeoutMs(默认 15000)、compress.nudgeDecisionReasoning(默认 "strip"=legacy dsh上下文700K不触发压缩 #3004)、compress.nudgeDecisionMaxTokens 默认 200→1024(已披露)。均在 compress.* 下走三级 cascade。

判定:可合——代码、测试、披露齐备;因属 config-surface 变更,合并留人工(§7.4),不 auto-merge。

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/wiring

Projects

None yet

3 participants