fix(core): eliminate OOM from debugResponses accumulation#4982
Conversation
DragonnZhang
left a comment
There was a problem hiding this comment.
No issues found. Clean dead-code removal: debugResponses array, getDebugResponses(), and extractUsageFromGeminiClient are fully removed with zero remaining references. Test updates are consistent — the usage test in nonInteractiveCli.test.ts correctly relies on computeUsageFromMetrics via setupMetricsMock, matching the production path. CI test failures (all 3 platforms) appear pre-existing and unrelated to this change; Lint and CodeQL pass. LGTM.
|
Thanks for the review! @DragonnZhang
|
wenshao
left a comment
There was a problem hiding this comment.
No issues found. Clean dead-code removal: debugResponses array, getDebugResponses(), and extractUsageFromGeminiClient are fully removed with zero remaining references (git grep confirmed). Test updates are consistent — usage test in nonInteractiveCli.test.ts correctly migrated from mockGetDebugResponses to setupMetricsMock / computeUsageFromMetrics path. Build passes, tsc 0 errors, eslint 0 errors, 120 tests pass across all three affected test files. CI 13/13 green. ✅ — qwen3.7-max via Qwen Code /review
|
@qwen-code /triage |
|
Thanks for the PR! @zzhenyao Template is mostly complete — has clear "What this PR does", "Why it is needed", "Reviewer Test Plan", and bilingual description. Minor gaps: missing the "Tested on" OS table and "Risk & Scope" section from the template, but the substantive content is solid. On direction: this is a clean fix for a real P1 bug (#4815 — OOM from On approach: scope is perfect. The PR removes the unused Moving on to code review. 🔍 中文说明感谢贡献! 模板基本完整——有清晰的"做什么"、"为什么"、"测试计划"和双语描述。小缺憾:缺少模板中的"Tested on"操作系统表格和"Risk & Scope"部分,但实质内容充分。 方向:这是修复 P1 OOM bug (#4815) 的正确方案。删除持有所有 streaming chunk 引用的死代码,方向毫无疑问。 方案:范围恰当。删除 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewIndependent proposal (before reading the diff): Remove Comparison with the diff: The PR matches my proposal exactly — same files, same lines, same approach. Six files touched: No correctness bugs, no security concerns, no AGENTS.md violations. This is a clean dead-code removal with zero residual references confirmed by Build & TestsReal-Scenario VerificationThis PR removes dead code that causes OOM — the bug manifests only in long-running sessions (minutes to hours of streaming). A quick tmux prompt can't reproduce 8GB heap exhaustion, but I verified the core claim: both symbols are completely absent from the PR's built output. Before (installed v0.18.0): After (this PR's bundle): Source-level verification on Source-level verification on PR branch (after): Both symbols fully removed — from source, tests, and the production bundle. 中文说明代码审查独立方案(读 diff 前): 删除 与 diff 对比: PR 与我的方案完全一致——同样的文件、同样的代码行、同样的方法。6 个文件: 无正确性 bug、无安全隐患、无 AGENTS.md 违规。干净的死代码删除, 构建与测试全部通过:build ✓、typecheck ✓、3 个测试文件共 120 个测试全部通过。 真实场景验证OOM bug 需要长时间会话才能复现(数分钟到数小时的 streaming)。快速 tmux 测试无法重现 8GB 堆溢出,但已验证核心声明:两个符号在 PR 构建产物中完全不存在。 源码级别验证:main 分支有 4 处 — Qwen Code · qwen3.7-max |
Final AssessmentThis PR is exactly what it should be: a surgical removal of dead code that causes a P1 OOM bug. The motivation is clear (users hitting 8GB heap exhaustion in long sessions), the fix is minimal (+2/-214, 6 files), and the execution is clean. My independent proposal matched the diff line-for-line. There's nothing to simplify further — you can't make "delete unused code" any simpler. Build, typecheck, and all 120 tests across the three affected test files pass. Both symbols confirmed absent from the production bundle. The source-level The PR description is one of the better ones I've seen — it explains both the root cause (chunk accumulation in the Approving. ✅ 中文说明最终评估这个 PR 恰如其分:精确删除导致 P1 OOM bug 的死代码。动机清晰(用户在长会话中遇到 8GB 堆溢出),修复最小化(+2/-214,6 个文件),执行干净。 我的独立方案与 diff 逐行一致。无法进一步简化——"删除未使用的代码"已经是最简形式。 构建、类型检查和全部 120 个测试通过。两个符号在生产产物中确认为零引用。PR 描述优秀——解释了根因和两种具体的 OOM 场景。双语格式和测试计划让审查很方便。 批准合并 ✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
Local verification report (maintainer)Verified this PR locally with real runs, not just the test suite. Since the PR's merge-base is ~50 commits behind 1. Dead-code claim — confirmed, plus one stronger finding
2. Build / typecheck / tests (merged tree)
On the
3. Real-run smoke tests (bundled CLI from the merged tree, driven via tmux)
4. Memory-retention benchmark — the OOM claim holds empiricallyDrove
Both builds emitted an identical event stream (2001 events) — behavior parity. A Verdict✅ Safe to merge. Dead code with zero references after merging into latest 中文版(Chinese version)本地验证报告(维护者)本 PR 在本地做了真实运行验证,而不仅是跑测试。由于 PR 的 merge-base 落后 1. 死代码声明 — 成立,且有一个更强的发现
2. 构建 / 类型检查 / 测试(合并树)
关于
3. 真实运行冒烟测试(合并树打包出的 CLI,tmux 驱动)
4. 内存滞留基准 — OOM 声明有实证支撑直接驱动
两个构建产出完全相同的事件流(2001 个事件)——行为等价。 结论✅ 可以安全合并。 死代码在合入最新 |
What this PR does
Removes
debugResponsesarray andextractUsageFromGeminiClient. Both are dead code.Turn.debugResponsespushed every streaming chunk into an array. Nothing in production ever read it.extractUsageFromGeminiClientwas exported but never imported by production code. Non-interactive mode usescomputeUsageFromMetricsfrom the telemetry pipeline instead.Closes: #4815
Follow-up to #4824
Why it is needed
Every streaming chunk was pushed into
debugResponsesand held in memory for the lifetime of theTurnobject. In goal mode, nested turns are not cleaned up between iterations, so the array keeps growing. A single long session can accumulate gigabytes of response objects that are never read, leading to OOM.debugResponsesholds a reference to every streaming chunk for the lifetime of the Turn object. Two scenarios cause OOM:GenerateContentResponse. The array grows unbounded within one turn.sendMessageStreamcalls hold inner Turn objects as references until the outer call completes. Each nested turn accumulates its owndebugResponses. The chain never releases until the top-level goal finishes, so memory grows across the entire nesting depth.Reviewer Test Plan
Before / After
Before:
Turnhas adebugResponsesarray that collects every chunk.extractUsageFromGeminiClientis exported but unused.After: Neither exists. Zero references left.
How to verify
中文
删除
debugResponses数组和extractUsageFromGeminiClient函数。每个 streaming chunk 都被 push 进
debugResponses并在 Turn 生命周期内一直持有。goal 模式下嵌套的 turn 不会被清理,数组持续增长,单次长会话可以累积 GB 级别的无用对象,最终 OOM。extractUsageFromGeminiClient从初始提交起就没有被生产代码调用过,非交互模式实际走的computeUsageFromMetrics。