Skip to content

fix(cli): enable ToolSearch by default for DeepSeek - #8331

Draft
DragonnZhang wants to merge 3 commits into
QwenLM:mainfrom
DragonnZhang:dragon/enable-tool-search-deepseek
Draft

fix(cli): enable ToolSearch by default for DeepSeek#8331
DragonnZhang wants to merge 3 commits into
QwenLM:mainfrom
DragonnZhang:dragon/enable-tool-search-deepseek

Conversation

@DragonnZhang

Copy link
Copy Markdown
Collaborator

What this PR does

This draft enables ToolSearch by default for DeepSeek models, while preserving the existing explicit opt-out and the 10% deferred-tool preload threshold. It also updates the user-facing setting descriptions so they no longer recommend disabling ToolSearch for prefix-cache stability.

This PR depends on #8276 and must not merge first. Once #8276 lands, revealing a deferred schema through ToolSearch no longer changes the provider-facing tool declarations, so the DeepSeek-specific default-off workaround is no longer necessary.

Why it's needed

#4069 intentionally disabled ToolSearch for DeepSeek because the old reveal path rewrote the provider tool list and invalidated prefix-based prompt caches. #8276 removes that failure mode by keeping deferred schemas in conversation content and executing them through a stable proxy declaration. Keeping the model-specific disable after that change would prevent DeepSeek users from benefiting from deferred tool discovery when their catalog exceeds the existing preload budget.

The prompt-cache A/B attached to #8276 measured 30 paired runs across five deferred tools. The post-reveal weighted cache hit rate increased from 0.00% on the old path to 96.97% on the stable-proxy path: #8276 (comment)

Reviewer Test Plan

How to verify

Start Qwen Code with a DeepSeek v3, v4, or provider-prefixed DeepSeek model and no explicit ToolSearch setting. Confirm ToolSearch remains available and the existing 10% threshold still decides whether deferred schemas are preloaded or discovered on demand. Then set tools.toolSearch.enabled to false, restart, and confirm ToolSearch is disabled and deferred tools are declared eagerly.

Evidence (Before & After)

Before: DeepSeek model names automatically placed ToolSearch in the deny list even when the user had not disabled it.

After: Model identity no longer changes the ToolSearch default. Explicit enabled: false remains authoritative, and threshold behavior is unchanged.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Environment (optional)

Node.js 24.18.0, local macOS worktree without sandboxing.

Risk & Scope

Linked Issues

Depends on #8276. Follow-up to #4069 and discussion #4065.

中文说明

本 PR 的改动

此 Draft PR 默认对 DeepSeek 模型启用 ToolSearch,同时保留现有的显式关闭能力和 10% 延迟工具预加载阈值。它还会更新面向用户的设置说明,不再建议为了保持前缀缓存稳定而关闭 ToolSearch。

此 PR 依赖 #8276,不能先于它合并。#8276 落地后,通过 ToolSearch 展示延迟工具 schema 不再改变面向 provider 的工具声明,因此不再需要针对 DeepSeek 默认关闭的临时规避方案。

为什么需要此改动

#4069 有意为 DeepSeek 关闭 ToolSearch,因为旧的 reveal 路径会重写 provider 工具列表并使基于前缀的提示词缓存失效。#8276 通过将延迟 schema 放入 conversation,并使用稳定的代理声明执行工具,消除了这个问题。继续保留模型特定的关闭逻辑,会使 DeepSeek 用户在工具目录超过现有预加载预算时无法受益于延迟工具发现。

#8276 中附带的提示词缓存 A/B 覆盖五种延迟工具的 30 次配对运行。schema reveal 后的加权缓存命中率从旧路径的 0.00% 提升至稳定代理路径的 96.97%:#8276 (comment)

Reviewer 测试计划

验证方式

在没有显式 ToolSearch 设置的情况下,分别使用 DeepSeek v3、v4 或带 provider 前缀的 DeepSeek 模型启动 Qwen Code。确认 ToolSearch 默认可用,并且现有的 10% 阈值仍决定延迟 schema 是预加载还是按需发现。随后将 tools.toolSearch.enabled 设为 false,重启并确认 ToolSearch 被禁用且延迟工具会被预先声明。

前后对比证据

改动前:即使用户没有关闭 ToolSearch,DeepSeek 模型名也会自动将其加入 deny list。

改动后:模型身份不再改变 ToolSearch 的默认值。显式 enabled: false 仍然生效,threshold 行为保持不变。

测试环境

OS 状态
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

环境(可选)

Node.js 24.18.0,macOS 本地 worktree,未启用 sandbox。

风险与范围

关联事项

依赖 #8276;是 #4069 和 discussion #4065 的后续改动。

@DragonnZhang
DragonnZhang marked this pull request as ready for review August 1, 2026 17:07
@DragonnZhang
DragonnZhang enabled auto-merge August 1, 2026 17:07
@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

🖼️ web-shell visual preview

Rendered against a mock daemon (no real backend): the PR base vs this PR head dc68957. Only screenshots that changed are shown (flows below, if any, are head-only) — refreshes on every push.

Screenshots · before / after

⚠️ No preview: one or more scenarios failed to render on this head — see the workflow run. This is not "no visual change" — a scenario that times out or throws produces no image. Fix the failing scenario (or a genuine regression it caught) and the preview returns on the next push.

Full-resolution recordings (.webm) are attached to the workflow run.

Qwen Code · web-shell visuals

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: Real and well-evidenced. #4069 added a DeepSeek-specific ToolSearch disable because the old reveal path rewrote the provider tool list and invalidated prefix caches. #8276 fixes that root cause by keeping deferred schemas in conversation content via a stable proxy declaration — the A/B attached there measured 0.00% → 96.97% post-reveal cache hit rate across 30 paired runs. Once #8276 lands, the model-specific workaround here is genuinely dead weight.

Direction: Aligned — removing model-specific defaults simplifies the config path and lets DeepSeek users benefit from deferred tool discovery. One important note: #8276 is still open. This PR explicitly depends on it and must not merge first. The code is correct given that dependency, but merging order matters here.

Size: 21 production logic lines (17 config.ts, 2 settingsSchema.ts, 2 i18n.tsx), 48 test lines, 4 docs/schema lines. Well under any threshold. Core paths touched (packages/cli/src/config/**) but the change is a straightforward conditional removal — every downstream consumer (settings dialog, VS Code schema, web-shell i18n, docs) is updated consistently.

Approach: Minimal and focused. The diff does exactly one thing: removes the /deepseek-(v3|v4|chat)/i regex check and keeps only the explicit enabled === false path. Description updates across all four surfaces are consistent. No scope creep, no drive-by changes.

Risk: No elevated risk signals — no high-risk paths matched.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题: 真实且有充分证据。#4069 为 DeepSeek 添加了特定的 ToolSearch 禁用逻辑,因为旧的 reveal 路径会重写 provider 工具列表并使前缀缓存失效。#8276 通过稳定代理声明修复了根本原因——其 A/B 测试在 30 次配对运行中测得 reveal 后缓存命中率从 0.00% 提升至 96.97%。#8276 落地后,此处的模型特定规避方案确实不再需要。

方向: 对齐——移除模型特定默认值简化了配置路径,让 DeepSeek 用户受益于延迟工具发现。重要提示:#8276 仍未合并。 此 PR 明确依赖它且不能先于它合并。代码在满足该依赖的前提下是正确的,但合并顺序很重要。

规模: 21 行生产逻辑(config.ts 17 行、settingsSchema.ts 2 行、i18n.tsx 2 行),48 行测试,4 行文档/schema。远低于任何阈值。触及核心路径(packages/cli/src/config/**),但改动是简单的条件移除——所有下游消费者(设置对话框、VS Code schema、web-shell i18n、文档)均已一致更新。

方案: 最小且聚焦。diff 只做一件事:移除 /deepseek-(v3|v4|chat)/i 正则检查,仅保留显式 enabled === false 路径。四个界面的描述更新一致。无范围蔓延,无顺手改动。

风险: 无升级风险信号——未匹配高风险路径。

进入代码审查 🔍

Qwen Code · qwen3.8-max-preview

Reviewed at 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Independent proposal: Given the goal (remove the DeepSeek-specific ToolSearch disable after #8276 fixes the cache invalidation), I would: (1) delete the shouldDisableToolSearch regex logic in config.ts, keeping only the explicit enabled === false check; (2) update the setting description everywhere it appears to remove DeepSeek-specific guidance; (3) replace the model-specific auto-disable tests with a parametrized test confirming no model is auto-denied. That is exactly what this PR does.

Code review: Clean. The 16-line removal in config.ts is the core change — the regex, the comment block, and the two-branch conditional all go, replaced by a single settings.tools?.toolSearch?.enabled === false guard. The explicit-disable test at line 2434 is preserved untouched. The five old model-specific tests collapse into one it.each covering four model names (deepseek-v4-flash, deepseek-v3, openrouter/deepseek/deepseek-chat, qwen-max), all asserting tool_search is not denied. The description updates across settingsSchema.ts, settings.schema.json, i18n.tsx, and settings.md are consistent and no longer mention DeepSeek or prefix caching. No correctness issues, no convention violations, no scope creep.

Testing evidence (CI):

Check Conclusion
Test (ubuntu-latest, Node 22.x) ✅ success
Test (windows-latest, Node 22.x) skipped
Test (macos-latest, Node 22.x) skipped
Integration Tests (CLI, No Sandbox) skipped
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
precheck-pr / precheck ✅ success
Capture web-shell visuals ✅ success
Classify PR ✅ success

Ubuntu unit tests and web-shell E2E pass. Windows/macOS tests and integration tests are skipped (typical for fork PRs). No failures.

The unit tests pin the config behavior (tool_search no longer auto-denied for any model). The cache-safety claim — that removing the disable won't regress DeepSeek prefix caching — rests entirely on #8276's stable-proxy mechanism, which is still open. Sandboxed verification (@qwen-code /verify) could confirm the config path end-to-end, but the cache-safety dependency on #8276 is not something any test on this PR can settle — it's a merge-ordering concern.

中文说明

独立方案: 给定目标(在 #8276 修复缓存失效后移除 DeepSeek 特定的 ToolSearch 禁用),我会:(1) 删除 config.ts 中的 shouldDisableToolSearch 正则逻辑,仅保留显式 enabled === false 检查;(2) 在所有出现处更新设置描述,移除 DeepSeek 特定指引;(3) 用参数化测试替换模型特定的自动禁用测试。这正是此 PR 所做的。

代码审查: 干净。config.ts 中 16 行的移除是核心改动——正则、注释块和双分支条件全部移除,替换为单一的 settings.tools?.toolSearch?.enabled === false 守卫。第 2434 行的显式禁用测试保持不变。五个旧的模型特定测试合并为一个 it.each,覆盖四个模型名,均断言 tool_search 不被拒绝。settingsSchema.tssettings.schema.jsoni18n.tsxsettings.md 中的描述更新一致,不再提及 DeepSeek 或前缀缓存。无正确性问题,无规范违反,无范围蔓延。

测试证据(CI): Ubuntu 单元测试和 web-shell E2E 通过。Windows/macOS 测试和集成测试被跳过(fork PR 常见)。无失败。

单元测试固定了配置行为(tool_search 不再对任何模型自动拒绝)。缓存安全性声明——移除禁用不会导致 DeepSeek 前缀缓存回退——完全依赖于 #8276 的稳定代理机制,而该 PR 仍未合并。沙箱验证(@qwen-code /verify)可以端到端确认配置路径,但对 #8276 的缓存安全依赖不是此 PR 的任何测试能解决的——这是合并顺序问题。

Qwen Code · qwen3.8-max-preview

Reviewed at 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 3/5 — clean, minimal code that does exactly what it says; deferred because the dependency #8276 hasn't landed and approving now would greenlight a merge that regresses DeepSeek prefix caching.

The code itself is about as clean as a removal PR gets. The regex, the comment block, the two-branch conditional — all gone, replaced by one guard. Tests collapse from five to one parametrized case. Descriptions updated consistently across four surfaces. My independent proposal matched the PR's approach exactly; I didn't find a simpler path it missed.

The reason this isn't a 5/5 is not the code — it's the merge dependency. #8276 ("preserve prompt cache across deferred tool discovery") is still open. This PR removes the DeepSeek-specific cache protection because #8276 replaces it with a provider-agnostic mechanism. Merge this first and DeepSeek users lose prefix-cache stability with no replacement. The PR body says this plainly ("must not merge first"), but GitHub doesn't enforce merge ordering — an approval here is a green light.

Deferring to a maintainer to coordinate the merge order with #8276. Once #8276 lands, this is ready to go.

中文说明

置信度:3/5 — 代码干净、最小化,完全做到了它所说的;推迟是因为依赖的 #8276 尚未合并,现在批准会放行一个导致 DeepSeek 前缀缓存回退的合并。

代码本身几乎是一个移除 PR 的典范。正则、注释块、双分支条件——全部移除,替换为一个守卫。测试从五个合并为一个参数化用例。描述在四个界面一致更新。我的独立方案与 PR 的方案完全一致;没有找到它遗漏的更简路径。

不是 5/5 的原因不在于代码——而在于合并依赖。#8276("跨延迟工具发现保持提示词缓存")仍未合并。此 PR 移除 DeepSeek 特定的缓存保护,因为 #8276 用 provider 无关的机制替代了它。先合并此 PR 会让 DeepSeek 用户失去前缀缓存稳定性且没有替代方案。PR 正文对此说明清楚("不能先于它合并"),但 GitHub 不强制合并顺序——此处的批准就是绿灯。

转交 maintainer 协调与 #8276 的合并顺序。#8276 落地后,此 PR 即可合并。

Qwen Code · qwen3.8-max-preview

Reviewed at 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

中文说明

已审查。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

— qwen3.8-max-preview via Qwen Code /review

@wenshao

wenshao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 332 passed · 0 failed · 332 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:332 通过 · 0 失败 · 332 总计

Verification report

PR #8331 Deep Verification Report

Verdict: merge-ready — 332/332 assertions passed, 0 failed. Verified head: 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1.

中文摘要

结论:merge-ready — 332/332 断言全部通过。

A/B 结论:中心声明已证实。在 BASE (8bdb058) 上,3 个 DeepSeek 模型(deepseek-v3deepseek-v4-flashopenrouter/deepseek/deepseek-chat)会自动将 tool_search 加入 deny list;在 HEAD (9ad1571) 上,这 3 个模型不再被自动拒绝,与 qwen-max 等非 DeepSeek 模型行为一致。显式 enabled: false 在两侧均正常生效。

Findings:无阻塞性问题。一个注意事项:PR 声明依赖 #8276(稳定代理声明机制),当前 base 中未包含该机制(见 Not covered)。

未覆盖范围#8276 依赖的合并顺序无法在此环境验证;真实 DeepSeek API 下的工具选择质量未测试(无凭证);threshold 行为未单独 A/B(代码路径未被本 PR 修改)。

Central claim + A/B table

Central claim: Model identity no longer changes the ToolSearch default — DeepSeek models get ToolSearch enabled by default, same as all other models.

Secondary claims:

  1. Explicit enabled: false remains authoritative (still denies tool_search for any model).
  2. The 10% threshold behavior is unchanged (code path untouched by this PR).

A/B load-bearing proof

The PR's own it.each test (4 model cases) plus the pre-existing explicit-disable test were run against both HEAD and BASE builds. The BASE worktree at tmp/base-tree was created from HEAD^1 (8bdb0586a); HEAD's config.test.ts was copied into it so the same assertions ran against the old config.ts. The base tree's config.ts was confirmed to be its own file (realpath: /__w/qwen-code/qwen-code/tmp/base-tree/packages/cli/src/config/config.ts) containing the DeepSeek regex at line 1956.

Cell Model Settings Oracle: tool_search in deny list? HEAD (9ad1571) BASE (8bdb058)
1 deepseek-v4-flash {} should NOT be denied ✅ pass ❌ fail (denied)
2 deepseek-v3 {} should NOT be denied ✅ pass ❌ fail (denied)
3 openrouter/deepseek/deepseek-chat {} should NOT be denied ✅ pass ❌ fail (denied)
4 qwen-max {} should NOT be denied ✅ pass ✅ pass
5 (no model flag) {enabled: false} should BE denied ✅ pass ✅ pass

HEAD: 5/5 pass. BASE: 3/5 fail — exactly the 3 DeepSeek models flip from auto-denied to not-denied. The qwen-max control and the explicit-disable control pass on both arms, confirming the change is scoped to the DeepSeek auto-disable logic and nothing else.

Evidence: 01-ab-head.png, 02-ab-base.png.

Vacuity check

Reverted the key hunk in config.ts (restored the base's shouldDisableToolSearch conditional with the /deepseek-(v3|v4|chat)/i regex) and re-ran the 5 toolSearch tests. Result: 3/5 fail with AssertionError: expected [ 'tool_search' ] to not include 'tool_search' — the intended behavioral assertion, not an import or setup failure. The tests are load-bearing.

Targeted gate

Full config.test.ts suite: 321/321 pass (0 fail, 0 skip). Evidence: 03-full-suite.png.

Description consistency

All four description locations were updated consistently:

Location Updated? DeepSeek reference removed?
settingsSchema.ts (EN)
settings.schema.json (VS Code)
docs/users/configuration/settings.md
packages/web-shell/client/i18n.tsx (ZH)

The EN i18n section has no toolSearch entry (English descriptions come from the schema). The settings.schema.json was verified by re-running the generator (scripts/generate-settings-schema.ts): zero diff against the committed file.

Residual "KV cach" / "prefix" matches in the changed files are all in the unchanged threshold setting description and the unrelated ui.enableCacheSharing setting — correct and intentional.

config.ts has zero remaining DeepSeek references. The resolvedModel variable (destructured at line 1940) is still consumed at line 2195 — not dead code.

Findings

No blocking findings.

Not covered

  1. fix(core): preserve prompt cache across deferred tool discovery #8276 dependency / merge ordering: The PR states it depends on fix(core): preserve prompt cache across deferred tool discovery #8276 (stable-proxy deferred tool declarations) and must not merge first. Grep of the codebase found no evidence of fix(core): preserve prompt cache across deferred tool discovery #8276's proxy-declaration mechanism: revealDeferredTool() still adds to a set, and getFunctionDeclarations() includes revealed tools in the provider-facing declaration list (confirmed in tool-registry.ts:741-743 and client.ts:748-750). ToolSearch calls setTools() after revealing (tool-search.ts:358), which rebuilds declarations. Without fix(core): preserve prompt cache across deferred tool discovery #8276, revealing a deferred tool changes the declaration list and busts the prefix cache — the exact failure mode this PR's description cites. The dependency is real; the merge-ordering constraint must be enforced by the maintainer. This environment has no GitHub token to verify fix(core): preserve prompt cache across deferred tool discovery #8276's merge status.

  2. Live DeepSeek API tool-selection quality: No DeepSeek credentials in this environment. The PR explicitly scopes this out.

  3. Threshold behavior A/B: The tools.toolSearch.threshold code path (preloadDeferredToolsWithinBudget in tool-registry.ts) is untouched by this PR. The threshold tests (lines 2457-2490) pass in the full suite but were not separately A/B'd.

  4. Per-commit attribution: The PR has a single commit (9ad1571), matching the commits array in the metadata snapshot. Depth-2 checkout is sufficient.

  5. Repo-wide gates: Only packages/cli tests were run. No cross-package changes exist in this PR.

Methodology

Environment: node:22-bookworm CI container, merge-ref checkout at depth 2 (HEAD = merge commit 41e0739, HEAD^1 = base 8bdb058, HEAD^2 = PR head 9ad1571). npm ci and npm run build completed before verification started.

A/B: base worktree created at tmp/base-tree from HEAD^1; per-package node_modules symlinked from the head tree (PR leaves package.json/package-lock.json untouched). HEAD's config.test.ts copied into the base worktree; vitest compiled the base tree's own config.ts (confirmed via realpath and grep for the DeepSeek regex). The @qwen-code/qwen-code-core workspace link resolves to the head tree's packages/core — acceptable because this PR does not modify packages/core.

Vacuity: key hunk reverted in-place via edit, tests re-run, file restored from .bak (zero git diff confirmed).

Schema regeneration: npx tsx scripts/generate-settings-schema.ts produced zero diff against the committed settings.schema.json.

Raw logs: vitest output captured via scripts/verify-capture.mjs into evidence/. Base worktree removed after A/B capture (git worktree remove --force).

Evidence images

01-ab-head

02-ab-base

03-full-suite

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Triage re-run completed without a new review.

⚠️ The bot's only review on 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 is a COMMENTED one, which carries no vote — so it has no verdict of its own on this commit, and main needs two approving reviews: an approval left by another account is a separate vote and does not count as the bot's own. Two different things look like this, and the stage-3 comment above says which: the triage skill deferring on purpose at 3/5 — a fork refactor hitting the approval guardrail, or a core change escalated for maintainer awareness, both normal outcomes — or an earlier approval that a push dismissed, leaving only the comment behind, which needs a fresh review.

⚠️ 机器人在 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 上唯一的评审是 COMMENTED不带票 —— 因此它在该 commit 上没有自己的裁决,而 main 需要两个批准(其他账号的批准是另一张票)。有两种情况长这样,上方的 stage-3 评论会说明是哪一种:triage skill 在 3/5 时有意 defer(fork refactor 命中审批护栏,或核心改动被升级交由维护者把关,两者都是正常结果);或者更早的批准被一次推送作废、只剩下这条评论,此时需要重新评审。

The stage comments above were updated with the latest result. View workflow run.

上方各阶段评论已更新为最新结果。查看工作流运行

@wenshao

wenshao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /review

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor
_Qwen Code review request accepted. Review is queued in [workflow run](https://github.com/QwenLM/qwen-code/actions/runs/30729640117)._

@wenshao

wenshao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Local real-environment verification (maintainer)

Verified this PR end-to-end on a local build with a real DeepSeek API session (deepseek-v4-flash via https://api.deepseek.com/anthropic). All requests went through a local logging reverse proxy so the tools array actually sent to the provider is captured verbatim. Each case ran in an isolated QWEN_HOME with a clean fixture working directory.

Environment: macOS 26.6, Node v24.18.1 · PR head 9ad1571ec (based on 8bdb0586a) · baseline = 8bdb0586a (main) · both built from source (npm run bundle), interactive TUI driven under tmux.

Unit tests

packages/cli src/config/config.test.ts: 321/321 passed on the PR head, including the new parameterized cases (deepseek-v4-flash, deepseek-v3, openrouter/deepseek/deepseek-chat, qwen-max).

Results

Case Build Settings (tools.toolSearch) tool_search declared to provider Deferred tools
A PR 9ad1571ec default enabled, threshold: 0 ✅ yes hidden; discovered on demand
A′ PR 9ad1571ec all defaults (threshold: 10) ✅ yes preloaded upfront (catalog fits the 10% budget) — threshold logic unchanged
B PR 9ad1571ec enabled: false ❌ no all 68 tools declared eagerly
C baseline main default (no setting) ❌ no (auto-denied by model name) all 68 tools declared eagerly

Case A ≠ Case C with identical settings and identical model is exactly this PR's change: model identity no longer flips the ToolSearch default. Case B confirms the explicit opt-out still produces the old eager-declaration behavior.

Wire-level evidence (proxy log of provider requests)

Case A (PR, default enabled, threshold: 0) — first TUI request declares tool_search and no deferred tools; after the model runs ToolSearch select:web_fetch, the next request grows by exactly the revealed tool:

tools=20  msgs=1  HAS_tool_search  no_web_fetch    <- session start
tools=21  msgs=3  HAS_tool_search  HAS_web_fetch   <- after ToolSearch reveal

Case A′ (PR, all defaults) — 10% preload budget fires, deferred tools declared upfront and tool_search stays available:

tools=64  tool_search=True  web_fetch=True  computer_use=True

Case B (PR, enabled: false) and Case C (baseline, default) — identical wire behavior, tool_search gone, everything eager:

tools=68  tool_search=False  web_fetch=True  computer_use=True  monitor=True

End-to-end on-demand discovery (real model)

Asked the real deepseek-v4-flash session to fetch a page. It called ToolSearch select:web_fetch, loaded the schema, then executed WebFetch successfully:

Case A ToolSearch flow

Screenshots: /tools listing per case (model-echoed case labels on screen)

Case A — PR build, default settings: ToolSearch present between TodoList and UpdateGoal:

Case A /tools

Case B — PR build, tools.toolSearch.enabled: false: ToolSearch absent:

Case B /tools

Case C — baseline main, same default settings as Case A: ToolSearch absent (the old auto-deny):

Case C /tools

Caveats for merging

Functionally this PR does exactly what it says. LGTM once #8276 is merged.

中文版本(Chinese version)

本地真实环境验证(维护者)

在本地构建上用真实 DeepSeek API 会话deepseek-v4-flash,经 https://api.deepseek.com/anthropic)端到端验证了本 PR。所有请求都经过本地日志反向代理,逐字记录实际发给 provider 的 tools 数组。每个用例使用独立的 QWEN_HOME 和干净的 fixture 工作目录。

环境: macOS 26.6,Node v24.18.1 · PR head 9ad1571ec(基于 8bdb0586a)· 基线 = 8bdb0586a(main)· 均从源码构建(npm run bundle),tmux 驱动交互式 TUI。

单元测试

packages/cli src/config/config.test.ts:PR head 上 321/321 通过,包含新增的参数化用例(deepseek-v4-flashdeepseek-v3openrouter/deepseek/deepseek-chatqwen-max)。

结果

用例 构建 设置(tools.toolSearch 是否向 provider 声明 tool_search 延迟工具
A PR 9ad1571ec 默认启用,threshold: 0 ✅ 是 隐藏,按需发现
A′ PR 9ad1571ec 全默认(threshold: 10 ✅ 是 预先全量声明(目录未超 10% 预算)—— 阈值逻辑未变
B PR 9ad1571ec enabled: false ❌ 否 68 个工具全部预先声明
C 基线 main 默认(未设置) ❌ 否(按模型名自动禁用) 68 个工具全部预先声明

用例 A 与用例 C 设置相同、模型相同而结果不同,正是本 PR 的改动:模型身份不再改变 ToolSearch 默认值。用例 B 确认显式关闭仍会回到旧的全量声明行为。

Wire 层证据(代理记录的 provider 请求)

用例 A(PR,默认启用,threshold: 0)—— TUI 首个请求声明 tool_search 且不含延迟工具;模型执行 ToolSearch select:web_fetch 后,下一请求恰好多出被 reveal 的工具:

tools=20  msgs=1  HAS_tool_search  no_web_fetch    <- 会话开始
tools=21  msgs=3  HAS_tool_search  HAS_web_fetch   <- ToolSearch reveal 之后

用例 A′(PR,全默认)—— 10% 预载预算生效,延迟工具预先声明,同时 tool_search 仍然可用:

tools=64  tool_search=True  web_fetch=True  computer_use=True

用例 B(PR,enabled: false)与用例 C(基线,默认)—— wire 行为完全一致,tool_search 消失,全部预先声明:

tools=68  tool_search=False  web_fetch=True  computer_use=True  monitor=True

端到端按需发现(真实模型)

让真实的 deepseek-v4-flash 会话抓取网页:它先调用 ToolSearch select:web_fetch 加载 schema,随后成功执行 WebFetch(见上方截图)。各用例 /tools 截图见上方折叠区(屏幕内有模型回显的用例标签)。

合并注意事项

功能上本 PR 与描述完全一致。待 #8276 合并后 LGTM。

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

中文说明

已审查。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

— qwen3.8-max-preview via Qwen Code /review (v0.21.3)

wenshao
wenshao previously approved these changes Aug 2, 2026
@wenshao

wenshao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Follow-up: measured cache-hit impact on the current PR head (old reveal path)

Supplementing the verification above with real usage numbers from the DeepSeek API (cache_read_input_tokens / input_tokens captured by the same logging proxy), answering "does this PR hurt cache hit rate?" with data. This ran on the current PR head, i.e. without #8276 — deliberately, to quantify the risk if merge order were violated.

Mid-session reveal does invalidate the prefix from the insertion point (old path)

One interactive session, four turns, threshold: 0 so deferred tools stay hidden. Main-conversation requests only (in = uncached tokens, cache_read = cached tokens):

Point in session Tools declared Uncached Cached
Turns 1–2 (declarations stable) 20 25–126 ~20.6k
Turn 3, request right after ToolSearch select:web_fetch 20 → 21 5109 17.0k
Turn 4 (declarations stable again at 21) 21 115 ~22.1k

The reveal rewrites the provider tool array, so everything after the insertion point is invalidated. web_fetch happens to sort near the end of the array, so this run only lost the tail schemas + system prompt + all messages (~5.1k tokens); a tool sorting earlier in the array would invalidate much more — consistent with the #8276 A/B that measured ~0% post-reveal hit rate on the old path in its scenario. The penalty is paid once per newly discovered tool; the very next turn is back to ~99% hit.

Scope of the risk is narrow

  • Default configurations never reach a mid-session reveal. With the default threshold: 10 and no/few MCP tools, the whole catalog preloads at session start (Case A′ above: 64 tools declared upfront, tool_search included) and the declaration list never changes — cache behavior identical to today. Only sessions whose tool catalog exceeds the 10% budget (typically heavy MCP setups) take the on-demand path.
  • With fix(core): preserve prompt cache across deferred tool discovery #8276 landed, the failure mode is gone entirely — reveals stop touching the declaration list (schemas travel in conversation content behind a stable proxy declaration), measured there at 96.97% post-reveal weighted hit rate.
  • Side benefit: the on-demand initial prefix is ~20.6k tokens vs ~32.7k with eager declaration (measured headless) — ~37% smaller stable prefix.

One more observation: a brand-new session's first request already showed cache_read=20480 — DeepSeek's prefix cache is account-level and cross-session, so the stable "system prompt + tool declarations" prefix is reused across sessions either way. The only differential cost is the reveal moment itself.

Conclusion

As long as #8276 merges first (as the PR description requires), this change does not degrade cache hit rate — and defaults-path users see no behavioral difference at all. If this PR were merged alone, DeepSeek users with over-budget tool catalogs would pay a one-time prefix invalidation (~5k+ uncached tokens in this measurement, potentially more depending on insertion point) per newly discovered tool. Recommend a merge-order guard: keep this in draft or blocked until #8276 lands.

中文版本(Chinese version)

补充:当前 PR head(旧 reveal 路径)上实测的缓存命中影响

用同一日志代理抓取 DeepSeek API 真实 usagecache_read_input_tokens / input_tokens),以数据回答"本 PR 是否影响缓存命中率"。本组实验特意跑在当前 PR head、即不含 #8276 的构建上,用于量化违反合并顺序时的风险。

旧路径下 mid-session reveal 确实会从插入点起作废前缀

单个交互式会话四个回合,threshold: 0 保持延迟工具隐藏。仅统计主对话链请求(in = 未命中 tokens,cache_read = 命中 tokens):

会话时点 声明工具数 未命中 命中
第 1–2 轮(声明稳定) 20 25–126 ~20.6k
第 3 轮 ToolSearch select:web_fetch 之后的请求 20 → 21 5109 17.0k
第 4 轮(声明再度稳定在 21) 21 115 ~22.1k

reveal 会重写 provider 工具数组,插入点之后的前缀全部作废。本次 web_fetch 按字母序恰好靠近数组尾部,只损失了尾部 schema + 系统提示 + 全部消息(约 5.1k tokens);若被发现的工具排序更靠前,作废范围会大得多 —— 与 #8276 A/B 在其场景下测得旧路径 reveal 后命中率约 0% 一致。该代价每个新发现的工具只付一次,下一轮立即恢复约 99% 命中。

风险范围很窄

  • 默认配置根本走不到 mid-session reveal。 默认 threshold: 10 且无/少量 MCP 工具时,整个目录在会话开始即预载(上文用例 A′:64 个工具预先声明,含 tool_search),声明列表全程不变,缓存行为与现状完全一致。只有工具目录超过 10% 预算的会话(典型为重度 MCP 配置)才走按需路径。
  • fix(core): preserve prompt cache across deferred tool discovery #8276 落地后该失效模式整体消失 —— reveal 不再触碰声明列表(schema 经稳定代理声明进入会话内容),其 A/B 实测 reveal 后加权命中率 96.97%。
  • 附带收益: 按需模式初始前缀约 20.6k tokens,eager 全量声明约 32.7k(headless 实测),稳定前缀小约 37%。

另一个观察:全新会话首个请求即有 cache_read=20480 —— DeepSeek 前缀缓存为账号级、跨会话,稳定的"系统提示 + 工具声明"段两种模式下都能跨会话复用,差异只集中在 reveal 那一条请求。

结论

只要按 PR 描述的要求让 #8276 先合并,本改动不会降低缓存命中率,默认配置用户行为完全无变化。若本 PR 被单独合并,超预算工具目录的 DeepSeek 用户会为每个新发现的工具付一次前缀失效代价(本次实测约 5k+ 未命中 tokens,视插入点可能更多)。建议保持 draft 或 blocked 状态直到 #8276 落地,作为合并顺序护栏。

@wenshao

wenshao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 343 passed · 0 failed · 343 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:343 通过 · 0 失败 · 343 总计

Verification report

PR #8331 Deep Verification Report (follow-up round)

Verdict: merge-ready — 343/343 scripted assertions passed, 0 failed. Verified head: 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 (git rev-parse HEAD^2).

This is a follow-up round on an unchanged PR head. The verified head 9ad1571… is byte-identical to round 1's, and the base tip HEAD^1 = 8bdb0586a… is unchanged. Commit OIDs are content-addressed hashes of the whole tree, so the diff, lockfile, config, and fixtures are identical to what round 1 measured; git rev-list HEAD^1..HEAD^2 returns the single commit 9ad1571…, matching the snapshot's commits array. Per the follow-up rule, every carried-forward measurement below was re-run fresh at this head (new artifact dir, new captures), not diffed from the old report.

中文摘要

结论:merge-ready — 343/343 脚本断言全部通过,0 失败。验证 head:9ad1571(与上一轮完全相同,base 仍为 8bdb058)。

A/B 结论:中心声明再次证实(见 A/B table)。在 BASE 上 3 个 DeepSeek 模型(deepseek-v3deepseek-v4-flashopenrouter/deepseek/deepseek-chat)会自动把 tool_search 加入 deny list;在 HEAD 上这 3 个模型不再被自动拒绝,qwen-max 与显式 enabled: false 两个对照在两侧均稳定。

Findings:无阻塞性问题。

未覆盖范围 / 携带项#8276 依赖仍未并入 base(重新实测确认,见下表第 1 行)——合并顺序须由 maintainer 强制;真实 DeepSeek API 工具选择质量未测(无凭证);threshold 路径未被本 PR 修改,未单独 A/B。

Previous-finding status table

Round 1 reported no blocking findings; its open items were all in Not covered. Their status at the (unchanged) head, re-measured this round:

# Item from round 1 Severity Status at head 9ad1571
1 #8276 (stable-proxy deferred declarations) not in base; merge-ordering must be enforced Note — maintainer action Stands. Re-measured, not diffed: packages/core is byte-identical base↔head (git diff --stat HEAD^1..HEAD -- packages/core empty). revealDeferredTool() still just adds to a Set (tool-registry.ts:755), and getFunctionDeclarations() still admits a revealed tool into the provider-facing list (tool-registry.ts:733-746, filter passes once isDeferredAndHidden() is false). No stable-proxy mechanism exists (the only "stable declaration" reference, line 231, is about sort order). The dependency is real; the constraint is unchanged.
2 Live DeepSeek API tool-selection quality untested Out of scope Stands — still no DeepSeek credentials in this environment.
3 Threshold behavior not separately A/B'd Out of scope StandspreloadDeferredToolsWithinBudget path untouched by this PR; threshold tests pass in the full suite (below).
4 Per-commit attribution N/A Stands — single commit 9ad1571, depth-2 checkout sufficient.
5 Repo-wide gates (only packages/cli tested) Out of scope Stands — no cross-package production changes (diff touches docs/, packages/cli, packages/web-shell only).

No round-1 item was fixed/worsened/superseded — the head did not move.

Central claim + A/B table

Central claim: Model identity no longer changes the ToolSearch default — DeepSeek models get ToolSearch enabled by default, same as every other model.

Secondary claims: (1) explicit enabled: false remains authoritative; (2) the 10% threshold behavior is unchanged (code path untouched).

The PR's own it.each (4 model cases) plus the pre-existing explicit-disable test were run against HEAD and a BASE worktree at HEAD^1 (8bdb058), with HEAD's config.test.ts copied in so identical assertions ran against the old config.ts. The base tree's config.ts was confirmed to be its own file (realpath under tmp/base-tree/…, DeepSeek regex at line 1956). Witness: 01-ab-head-vs-base.png.

Cell Model Settings Oracle: tool_search denied? HEAD (9ad1571) BASE (8bdb058)
1 deepseek-v4-flash {} should NOT be denied ✅ pass ❌ denied (expected red)
2 deepseek-v3 {} should NOT be denied ✅ pass ❌ denied (expected red)
3 openrouter/deepseek/deepseek-chat {} should NOT be denied ✅ pass ❌ denied (expected red)
4 qwen-max {} should NOT be denied ✅ pass ✅ pass
5 (no model flag) {enabled: false} should BE denied ✅ pass ✅ pass

HEAD: 5/5 green. BASE: exactly the 3 DeepSeek cells red, qwen-max and explicit-disable green on both arms. The 3 DeepSeek cells flip red→green; both controls stable. The A/B harness encodes the per-arm expectation, so BASE's 3 reds are expected failures and count as passing assertions (ab-harness.mjs: SCRIPTED ASSERTIONS: pass=10 fail=0).

Vacuity check

Reverted the key hunk in HEAD's config.ts (restored BASE's shouldDisableToolSearch conditional with /deepseek-(v3|v4|chat)/i), re-ran the 5 cells, then restored. The 3 DeepSeek cells failed the intended behavioral assertion — AssertionError: expected [ 'tool_search' ] to not include 'tool_search' — not an import/compile/setup error; qwen-max and explicit-disable stayed green; config.ts returned to zero git diff (vacuity-harness.mjs: 11/11). Witness: 02-vacuity.png. The tests are load-bearing.

Targeted gate

Full config.test.ts suite on HEAD: 321 passed · 0 failed · 0 pending (vitest run src/config/config.test.ts). Witness: 03-full-suite.png. The cli workspace build (tsc, noUnusedLocals) passed at HEAD per the environment contract; resolvedModel (destructured at config.ts:1940) is still read at config.ts:2195, so removing the DeepSeek logic left no dead local.

Description consistency (re-verified)

All four user-facing locations updated; DeepSeek appears 0 times in each (grep -c -i deepseek = 0):

Location DeepSeek removed? Regenerated cleanly?
packages/cli/src/config/settingsSchema.ts (EN source)
packages/vscode-ide-companion/schemas/settings.schema.json npm run generate:settings-schemabyte-identical (zero diff)
docs/users/configuration/settings.md
packages/web-shell/client/i18n.tsx (ZH)

Residual prefix/KV cach matches in these files are all in the unchanged threshold setting description (settingsSchema.ts:2447: "keeping the prompt prefix stable for KV caching") and unrelated settings (Anthropic cache_control, path/key prefixes) — correct and intentional.

Corrections

None. Round 1's descriptions of the code (mechanism, ARIA-irrelevant here, cause attribution) were accurate; re-reading the diff and the core sources this round found nothing to correct.

Findings

No blocking findings. The single carried-forward note (#8276 not yet in base) is a maintainer merge-ordering action, not a defect in this PR's code — the PR explicitly declares the dependency and "must not merge first."

Not covered

  1. fix(core): preserve prompt cache across deferred tool discovery #8276 merge ordering — cannot be enforced or observed from this token-free sandbox; re-measured as standing (table row 1). The maintainer must ensure fix(core): preserve prompt cache across deferred tool discovery #8276 lands first; without it, revealing a deferred tool changes the provider-facing declaration list and busts the prefix cache — the exact failure mode this PR's description cites.
  2. Live DeepSeek API tool-selection quality — no credentials; PR scopes this out.
  3. Threshold A/B — path untouched by the PR; covered only by the full suite, not separately A/B'd.
  4. Repo-wide gates — only packages/cli tested; no cross-package production changes exist.
  5. Uncalibrated items — none added this round; all carried-forward measurements were re-run, not inferred.

Methodology

Environment: node:22-bookworm CI container, merge-ref checkout at depth 2 (HEAD = merge 41e0739, HEAD^1 = base 8bdb058, HEAD^2 = PR head 9ad1571); npm ci and npm run build completed before verification.

A/B: base worktree at tmp/base-tree from HEAD^1; root, packages/cli, and packages/core node_modules symlinked from the head tree (the PR leaves package.json/package-lock.json untouched). The cli vite config aliases @qwen-code/qwen-code-core to source (../core/src), so the base arm loads the base tree's own core; the workspace link therefore resolves core into the head tree, which is acceptable because git diff --stat HEAD^1..HEAD -- packages/core is empty (core is byte-identical base↔head) — asserted in the table above. (The first base run failed collection on ajv/dist/2020.js until packages/core/node_modules was also symlinked — a harness wiring issue, not a PR defect; the A/A behavior of the control was confirmed once wired.) HEAD's config.test.ts was copied into the base tree; vitest compiled the base tree's own config.ts (confirmed via realpath + grep for the regex). Base worktree removed after capture (git worktree remove --force).

Vacuity: key hunk reverted in-place by vacuity-harness.mjs, which backs up, mutates, runs, and restores in a finally, then asserts zero git diff.

Schema: npm run generate:settings-schema output diff -q'd against the committed file (byte-identical).

Assertion composition (assertions.json = 343): A/B harness 10 + vacuity harness 11 + full vitest suite 321 + schema zero-diff 1. The 5 HEAD A/B cells overlap the suite's 5 toolSearch tests but are distinct scripted executions (per-arm expected-status assertions vs. behavioral assertions); both ran. Raw logs: ab-output.log, vacuity-output.log, and the vitest JSON reports under tmp/. Evidence images rendered via scripts/verify-capture.mjs.

Evidence images

01-ab-head-vs-base

02-vacuity

03-full-suite

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

⏸️ Deferring to @wenshao — the code review is clean (3/5, no blockers), but this PR explicitly depends on #8276 which is still open. Approving now would greenlight a merge that removes DeepSeek's prefix-cache protection before the replacement mechanism lands. Needs a maintainer to coordinate merge ordering: #8276 first, then this PR.

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Triage re-run completed without a new review.

⚠️ The bot's only review on 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 is a COMMENTED one, which carries no vote — so it has no verdict of its own on this commit, and main needs two approving reviews: an approval left by another account is a separate vote and does not count as the bot's own. Two different things look like this, and the stage-3 comment above says which: the triage skill deferring on purpose at 3/5 — a fork refactor hitting the approval guardrail, or a core change escalated for maintainer awareness, both normal outcomes — or an earlier approval that a push dismissed, leaving only the comment behind, which needs a fresh review.

⚠️ 机器人在 9ad1571ecfe379d5d2cbb70dc40a2dae172decd1 上唯一的评审是 COMMENTED不带票 —— 因此它在该 commit 上没有自己的裁决,而 main 需要两个批准(其他账号的批准是另一张票)。有两种情况长这样,上方的 stage-3 评论会说明是哪一种:triage skill 在 3/5 时有意 defer(fork refactor 命中审批护栏,或核心改动被升级交由维护者把关,两者都是正常结果);或者更早的批准被一次推送作废、只剩下这条评论,此时需要重新评审。

The stage comments above were updated with the latest result. View workflow run.

上方各阶段评论已更新为最新结果。查看工作流运行

@wenshao

wenshao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /reivew

@doudouOUC doudouOUC left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at 9ad1571. The diff is correct and I have no objection to the change itself — I am requesting changes purely to put a mechanical block behind the merge-order constraint this PR states in prose.

The dependency is unmerged, and nothing currently prevents this from landing first

The body says "This PR depends on #8276 and must not merge first." I verified the current state rather than taking the note at face value:

  • #8276 is OPEN, not merged.
  • Its stable-proxy mechanism is not in main: packages/core/src/core/deferred-tool-call-normalization.ts — a file #8276 adds — is absent from origin/main, and main's client.ts still carries the old path verbatim: "when ToolSearch is not registered … every deferred tool is eagerly revealed here so it lands in the declaration list."
  • Meanwhile this PR is isDraft=false, mergeable=MERGEABLE, and already carries a maintainer approval.

So the only thing standing between the repo and an out-of-order merge is the sentence in the description. If this lands first, DeepSeek models get ToolSearch enabled while the reveal path still rewrites the provider tool declarations — which is exactly the prefix-cache invalidation #4069 introduced the DeepSeek default-off for. On models where cached tokens are discounted up to 1/120, that is the 0.00% post-reveal hit rate your own A/B measured on the old path, silently restored for every DeepSeek user who has not set tools.toolSearch.enabled explicitly.

A CHANGES_REQUESTED flips reviewDecision and keeps the merge button unavailable until it is dismissed, which is the enforcement the constraint deserves. Dismiss this the moment #8276 lands (or convert this PR to draft and I will withdraw it instead) — I am not asking for a code change. Adding a do-not-merge label would work equally well if the project prefers that; I went with the review because it is the only signal that actually blocks.

The change itself checks out

The removal is minimal and correct: the whole model-sniffing block collapses to if (settings.tools?.toolSearch?.enabled === false), so the explicit opt-out survives intact and only the implicit DeepSeek default is dropped — no ^-anchor regex left behind, no dead branch. The setting descriptions in all four surfaces (CLI schema, vscode JSON schema, settings.md, web-shell i18n) are updated consistently and no longer advise disabling ToolSearch for prefix-cache stability, which would be stale advice after #8276.

Test coverage is a fair trade rather than a net loss: the five should auto-disable tool_search for deepseek-* cases are replaced by one parameterised should enable tool_search by default for %s over deepseek-v4-flash, deepseek-v3, openrouter/deepseek/deepseek-chat, and qwen-max — the same model shapes including the provider-prefixed one, asserting the inverted expectation. The explicit enabled: false deny-list case and the threshold tests (default 10, safe/bare-mode forcing to 0) are untouched, so the surviving behaviour stays pinned.

Ran both touched test files locally at this HEAD: 359/359. CI green.

Process note: this touches packages/cli/src/config/**, which AGENTS.md counts as core, from a non-maintainer author. At +1/-16 of production logic it is firmly Tier-2 small-scope, the behaviour change is a single collapsed conditional whose downstream consumers I can name in full (the mergedDeny push and the four description surfaces), and a maintainer has already approved — so the gate is satisfied. Flagging only for awareness.

@DragonnZhang

Copy link
Copy Markdown
Collaborator Author

Re the merge-order block: this PR is now isDraft=true, which is the second option you offered ("convert this PR to draft and I will withdraw it instead"). No code change was requested and none was made — the change itself is unchanged since 9ad1571.

The dependency is still unmet: #8276 is open and its stable-proxy path is not in main. This stays a draft until #8276 lands, so an out-of-order merge is now blocked mechanically by the draft state rather than only by the sentence in the description.

中文说明

关于合并顺序的阻塞:本 PR 现已转为 draft(isDraft=true),即你提出的第二个选项("convert this PR to draft and I will withdraw it instead")。你没有要求代码改动,代码也未改动——自 9ad1571 起内容不变。

依赖仍未满足:#8276 仍是 open 状态,其 stable-proxy 路径尚未进入 main。因此本 PR 会保持 draft 直到 #8276 合入,乱序合并现在由 draft 状态从机制上阻止,而不再只依赖描述里的一句话。

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.

4 participants