Skip to content

feat(core): expand Kimi, Qwen and DeepSeek reasoning presets - #11349

Merged
wenshao merged 6 commits into
mainfrom
codex/expand-kimi-qwen-deepseek-reasoning
Sep 9, 2026
Merged

wenshao merged 6 commits into
mainfrom
codex/expand-kimi-qwen-deepseek-reasoning

Conversation

@callmeYe

@callmeYe callmeYe commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Extends provider-configured reasoning to mainstream Kimi models and fills out Qwen and DeepSeek presets. Moonshot K3 offers low/high/max, K2.7 Code stays thinking-only, and K2.6 exposes its native thinking toggle. Qwen 3.8 offers low/medium/xhigh; native DeepSeek V4 Pro and Flash offer low/high/max, while Alibaba stable models and dated snapshots keep their distinct supported tiers and disable fields.

Adds Qwen 3.8, DeepSeek V4 snapshots and Kimi to Standard API setup, plus Qwen 3.8 Flash and DeepSeek V4 Pro-0813 to Token Plan. Existing Coding Plan hybrid models gain explicit capabilities without changing the available model list. DashScope now recognizes a declared Qwen tiered protocol when resolving conflicting thinking parameters, allowing Flash and future configured models to work without another model-name branch.

Why it's needed

The capability foundation in #10999 configured only native DeepSeek V4 Pro. The remaining presets could expose generic tiers or install the wrong native Kimi thinking field. Adding Flash also revealed a legacy Max-only protocol check that appended a boolean thinking switch alongside the selected effort. The same resolved capability now controls that decision and the CLI's static-override notice. Authentication refresh also preserves a saved thinking-off preference that provider defaults previously erased, while mandatory-thinking models remain protected.

Reviewer Test Plan

How to verify

Configure Moonshot and select K3, K2.7 Code and K2.6 in turn. Confirm K3 offers low/high/max with max as default and no Off; K2.7 does not offer Off or fabricated effort tiers; K2.6 offers On/Off. A K2.6 Off request must contain thinking.type=disabled and no enable_thinking field.

Select Qwen 3.8 Flash through Standard setup. Confirm low/medium/xhigh with xhigh as default; selecting an effort sends one reasoning_effort field, and Off sends reasoning_effort=none. An explicit thinking budget must suppress the competing effort. Existing Token Plan Max/Preview thinking-mandatory restrictions remain in effect.

Compare native DeepSeek V4 Flash with Alibaba stable V4 and its dated snapshots: native and snapshots offer low/high/max, Alibaba stable offers high/max, and all default to high. Off uses thinking.type for native DeepSeek and enable_thinking for Alibaba. Unknown IDs retain their existing fallback behavior.

Evidence (Before & After)

Before: installing the new Flash capability still produced enable_thinking=true alongside reasoning_effort; the two real-provider pipeline cases failed while the other 21 routes passed. After: the configured protocol determines parameter precedence, and all 23 route cases pass for normal requests, explicit Off and non-thinking side queries. Automated ACP tests check installed presets' exact options and defaults.

Validation results and isolated CLI mock evidence are posted in the separate test report comment.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️ not tested locally
🐧 Linux ⚠️ not tested locally

Environment (optional)

Node.js 22; provider and pipeline unit tests, ACP option tests, isolated CLI requests against a local mock OpenAI API, build, typecheck, bundle and lint.

Risk & Scope

  • Main risk or tradeoff: preset metadata must reflect the specific provider route. Alibaba Kimi K3 accepts images; the native Moonshot route retains video support.
  • Not validated / out of scope: live paid provider calls, regional account entitlements, unrelated provider families and the remaining independent edge cases in Follow up provider-configured reasoning edge cases #11328.
  • Breaking changes / migration notes: existing settings are not migrated. Reconfigure a provider to adopt the updated presets. Existing endpoint choices and default model ordering are preserved.

Linked Issues

Follows #10999. Related follow-up tracking: #11328.

中文说明

本 PR 做了什么

将声明式推理配置扩展到主流 Kimi 模型,并补齐 Qwen 与 DeepSeek 预设。Moonshot K3 提供 low/high/max;K2.7 Code 保持强制思考;K2.6 提供原生开关。Qwen 3.8 提供 low/medium/xhigh;原生 DeepSeek V4 Pro 与 Flash 提供 low/high/max,阿里云稳定版与日期快照分别保留其档位和关闭字段。

Standard API 配置新增 Qwen 3.8、DeepSeek V4 快照和 Kimi;Token Plan 新增 Qwen 3.8 Flash 和 DeepSeek V4 Pro-0813。Coding Plan 只为已有混合思考模型补充显式能力,不改变模型列表。DashScope 在处理思考参数冲突时读取已声明的 Qwen 档位协议,使 Flash 和后续配置的模型无需新增型号判断。

为什么需要

#10999 的能力框架只配置了原生 DeepSeek V4 Pro。其他预设仍可能展示通用档位,或为原生 Kimi 安装错误的思考字段。新增 Flash 暴露了旧 Max-only 协议判断,导致档位和布尔开关同时发送。现在该判断及 CLI 高优先级静态参数提示都使用同一份已解析能力。认证刷新还会保留此前被 provider 默认值覆盖的全局关闭偏好,强制思考模型继续受到保护。

Reviewer 测试计划

如何验证

配置 Moonshot,依次选择 K3、K2.7 Code 与 K2.6。K3 应仅提供 low/high/max,默认 max,没有 Off;K2.7 不提供 Off 或虚构档位;K2.6 提供 On/Off。K2.6 关闭请求应包含 thinking.type=disabled,不包含 enable_thinking。

通过 Standard 选择 Qwen 3.8 Flash,确认 low/medium/xhigh、默认 xhigh;选择档位时只发送 reasoning_effort,关闭时发送 reasoning_effort=none。显式 thinking budget 应抑制冲突的 effort。Token Plan Max/Preview 的既有强制思考限制保持有效。

比较原生 DeepSeek V4 Flash、阿里云稳定版 V4 及日期快照:原生与快照提供 low/high/max,阿里云稳定版提供 high/max,默认均为 high。原生关闭字段为 thinking.type,阿里云为 enable_thinking。未知模型 ID 保持已有回退行为。

Before / After 证据

Before:安装新的 Flash capability 后,仍同时产生 enable_thinking=true 与 reasoning_effort;两个真实 provider pipeline 用例失败,其余 21 个路由通过。After:声明的协议决定参数优先级,23 个路由在普通请求、显式关闭及非思考副请求下均通过。ACP 自动化测试验证真实安装配置的精确选项和默认值。

验证结果和隔离 CLI mock 证据作为独立测试报告评论发布。

测试平台与环境

macOS 已测试;Windows 和 Linux 未本地测试。Node.js 22;provider/pipeline 单元测试、ACP 选项测试、隔离 CLI 本地 mock API、build、typecheck、bundle 和 lint。

风险与范围

  • 主要风险:预设元数据必须符合具体 provider 路由。阿里云 Kimi K3 仅开放图片输入;原生 Moonshot 路由保留视频支持。
  • 未验证 / 不包含:真实付费 provider 调用、地域账号权限、其他模型家族及 Follow up provider-configured reasoning edge cases #11328 剩余独立边界。
  • 破坏性变更 / 迁移:不自动迁移已有 settings;重新配置 provider 后采用新预设。既有 endpoint 选项和默认模型顺序保留。

关联

接续 #10999;相关后续跟踪为 #11328

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

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

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

@callmeYe

callmeYe commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

Validation report

Tested source: 76ba8799b40a333f0168bfa8cffd581fcc6781e9, macOS / Node.js 22.

  • Full npm run build, npm run typecheck, npm run bundle and npm run lint: passed. The lockfile is unchanged. An initial pnpm layout caused an unrelated export bundle size failure; restoring the repository's npm layout with the frozen lockfile brought the asset below its unchanged size limit.
  • Focused core provider/preset/pipeline/config tests passed. The final affected-suite rerun passed 994 tests; the two repeated-auth cases were rerun after strengthening their provider-default assertions and passed.
  • ACP plus both TUI effort implementations: 4 suites, 101 tests passed. These verify actual installed presets' exact options, defaults and unsupported selection fallback.
  • Real provider-hook pipeline tests cover 23 routes with normal requests, explicit thinking-off and non-thinking side queries. They reproduce the Flash double-knob failure before the fix and pass after it.
  • Isolated CLI E2E: 11/11 passed, each making one actual CLI-to-localhost request using settings generated by the real provider installer. No model or credential flags override the installed configuration.
CLI E2E case Observed reasoning fields
Moonshot K3 / max reasoning_effort=max
Moonshot K3 / attempted off No disable fields; mandatory provider default applies
Moonshot K2.7 Code / attempted off No disable fields
Moonshot K2.6 / off thinking.type=disabled
Moonshot K2.6 / default No reasoning fields
Alibaba Qwen 3.8 Max / low reasoning_effort=low
Alibaba Qwen 3.8 Max / off reasoning_effort=none
Alibaba DeepSeek V4 Pro-0813 / low reasoning_effort=low
Alibaba DeepSeek V4 Pro-0813 / off enable_thinking=false
Native DeepSeek V4 Flash / low reasoning_effort=low
Native DeepSeek V4 Flash / off thinking.type=disabled

The off-state regression was reproduced with the global preference and compared with a passing per-model override. Restoring both active and rebuildable state fixed the real headless startup sequence without weakening the original off-field assertions. Tests also cover mandatory models and applying fresh model defaults after clearing the prior preference.

Independent local medium review found no actionable issues after the fixes. This is automated control and wire-contract validation, not manual browser acceptance or live paid-provider testing. Windows/Linux execution and provider account/region entitlements remain for CI or downstream acceptance.

中文测试说明

在 macOS / Node.js 22 上验证提交 76ba8799b40a333f0168bfa8cffd581fcc6781e9。完整 build、typecheck、bundle、lint 均通过,锁文件未改。初始 pnpm 布局引起的无关导出资源体积失败,在恢复仓库标准 npm 布局后消失,未修改体积阈值。

Core 聚焦测试通过;最终受影响套件复跑 994 项通过,强化 provider 默认值断言后的两项连续认证测试也通过。ACP 与两套 TUI 推理控件共 4 套、101 项测试通过。真实 provider hook 覆盖 23 个路由的普通、显式关闭、副请求关闭三种模式,确认 Flash 修复前失败、修复后通过。

11 个隔离 CLI E2E 全部通过。配置由真实 installer 生成,不使用模型或凭证启动参数覆盖;每项产生一个实际 CLI 到 localhost 的请求。上表记录精确推理字段,意外的嵌套 reasoning、extra_body 或额外开关都会导致失败。Kimi K3 / K2.7 的强制思考保护、K2.6 原生关闭、Qwen none、阿里云与原生 DeepSeek 的不同关闭字段均已验证。

全局关闭偏好曾在实际 headless 连续认证时丢失,而相同模型级关闭配置能够通过;修复同时恢复活动配置与可重建状态后,原关闭字段断言不变即通过。另验证强制思考模型及清除旧偏好后重新应用模型默认值。

独立本地 medium review 在修复后没有可操作发现。以上是自动化控件和 wire 契约验证,不代表手工浏览器验收或真实付费 provider 调用。Windows/Linux、账号地域权限由 CI 或下游验收验证。

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Re-run at 78607f7; my previous pass reviewed 76ba879. I read the delta rather than trusting the description of it: two files, +9 in the design doc and +7/−12 in dashscope.ts, and every changed line in dashscope.ts is inside a doc comment. No production statement moved. That matters for the size gate below.

Template looks good ✓

Problem: real, and I re-verified it in a clean worktree of the base instead of taking the framing on faith. packages/core/src/providers/presets/moonshot.ts declares no capabilities block at all today, and three of its four entries carry enableThinking: true; alibaba-standard.ts contains zero occurrences of capabilities. So Kimi K3 falls through to the full five-tier ladder, and the native Kimi routes install a Qwen-specific enable_thinking field on an API whose thinking switch is thinking. Observed in the tree, not theoretical — no reproduction needed beyond reading the base.

Direction: aligned, for the reason I care most about: it fills in the framework #10999 already merged instead of building a rival one, and it deletes a hardcoded model-name branch (the qwen3.8-max prefix in isTieredEffortWireModel) rather than adding a second one for Flash. CHANGELOG: no direct entry, but the area is clearly live given #10999 landed the plumbing this fills in.

The thing that drove my last escalation is worth naming because it changed. Last pass I said the substance here is a set of claims about what Moonshot, DeepSeek and Alibaba ModelStudio accept, and no amount of static review verifies a claim about a third-party API. @wenshao has since verified the whole matrix against real endpoints — provider hostnames impersonated locally behind a throwaway CA, settings generated by the repo's own installer, a live daemon per route — including a negative-control arm that reverts only the three production files and gets both defects back on the wire. That is the evidence class I said was missing, and it is a maintainer's evidence, not mine.

Size: core paths touched (packages/core/src/config/**, packages/core/src/core/**, packages/core/src/providers/**), so the two-tier gate applies. At this commit: 506 production logic lines (config.ts 29, modalityDefaults.ts 21, provider/dashscope.ts 59, the five preset files 397), 401 test lines, 79 docs lines — 986 total. feat type is never size-blocked and this is well under the 1000-line advisory, so nothing here asks you to split it. But 506 crosses the 500-line threshold at which a core feat gets escalated to the maintainer for awareness — and the comment-only commit is what crossed it (last pass counted 487). Under the gate that escalation caps this run at 3/5 and takes the defer path however clean the review reads. It is a policy outcome, not a judgement about the diff.

Approach: scope still feels right, and I still have no simpler path to argue for — 80% of this is preset declarations plus one gate swap. My earlier note that the refreshAuth thinking-off preservation is a separate fix riding along, and should be said rather than left for a reviewer to discover, is addressed: the description now states it plainly.

Risk: Stage 1e matched one high-risk path — packages/core/src/core/openaiContentGenerator/provider/dashscope.ts (the openaiContentGenerator family is correlated with post-merge reverts in this repo). Consequences for this run: full Stage 2 enrichments, CI evidence quoted from the API before any approval, and a named sandboxed lane. Review focus stays where it was: isTieredEffortModel and buildQwenEffortConfig decide the wire shape.

Moving on to code review. 🔍

中文说明

本次是对 78607f7 的 re-run;上一轮审查的是 76ba879。我读了两版之间的增量,而不是照抄描述:两个文件,设计文档 +9 行、dashscope.ts +7/−12 行,而 dashscope.ts 的每一处改动都在文档注释里,没有任何产品语句发生变化。这一点对下面的规模门禁有影响。

模板完整 ✓

问题: 真实存在,我在基线的干净 worktree 里重新核对过,不是接受描述的框定。当前 packages/core/src/providers/presets/moonshot.ts 完全没有 capabilities 块,四个条目中有三个带着 enableThinking: truealibaba-standard.tscapabilities 出现 0 次。所以 Kimi K3 会退回完整的五档表,而原生 Kimi 路由会安装一个 Qwen 专属的 enable_thinking 字段——该 API 的思考开关其实是 thinking。这是从代码树里可直接观测的,不是理论性加固,也不需要额外复现。

方向: 对齐,而且是我最在意的那种对齐:它填充的是 #10999 已合并的框架,而不是另造一套;它删掉了一个硬编码模型名分支(isTieredEffortWireModel 里的 qwen3.8-max 前缀),而不是为 Flash 再加一个。CHANGELOG 没有直接条目,但 #10999 已经落地了本次要填充的管线,这个方向显然是活跃的。

上一轮让我升级的那个点值得单独说,因为它变了。上次我说:本 PR 的实质是一组关于 Moonshot、DeepSeek 与阿里云百炼接受什么的断言,而静态审查无法验证任何关于第三方 API 的断言。@wenshao 此后已经对真实端点验证了整个矩阵——把 provider 域名劫持到本地、用自建 CA 签发证书、settings 由仓库自己的安装器生成、每条路由跑一个真实 daemon——并且包含一个反向对照 arm:只回退那三个产品文件,两个缺陷就在线上重现。这正是我说缺失的那类证据,而且它是 maintainer 的证据,不是我的。

规模: 触及核心路径(packages/core/src/config/**packages/core/src/core/**packages/core/src/providers/**),适用两级门禁。该 commit 上:生产逻辑 506 行config.ts 29、modalityDefaults.ts 21、provider/dashscope.ts 59、五个预设文件 397)、测试 401 行文档 79 行,合计 986。feat 类型从不因体量被阻断,也远低于 1000 行的大 PR 建议阈值,所以这里没有任何拆分要求。但 506 越过了 500 行这条"核心 feat 需升级给 maintainer 知悉"的线——而越过它的正是那个只改注释的 commit(上一轮统计是 487)。按门禁规则,这个升级会把本次运行的置信度封顶在 3/5 并走 defer 路径,无论审查看起来多干净。这是规则的结果,不是对 diff 的评价。

方案: 范围依然合理,我依然没有更简单的路径可以主张——其中 80% 是预设声明加一处判断替换。我上次提到 refreshAuth 的关闭思考保留是一个顺带带上的独立修复、应该明说而不是让 reviewer 自己发现,这一点已解决:描述里现在写清楚了。

风险: Stage 1e 命中一条高风险路径——packages/core/src/core/openaiContentGenerator/provider/dashscope.tsopenaiContentGenerator 系列在本仓库与合并后 revert 相关)。对本次运行的影响:Stage 2 完整增强项、批准前必须引用 API 读到的 CI 证据、并指名沙箱验证通道。评审重点不变:isTieredEffortModelbuildQwenEffortConfig 决定 wire 形状。

进入代码审查 🔍

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 78607f70605e076b0d7af50362716f352625e5b0 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Code review

My independent proposal, written before opening the diff, was the same as last pass: declare capabilities.reasoning per model in the five preset files to match each route's real wire contract, replace the hardcoded qwen3.8-max prefix gate in the DashScope provider with a lookup of the declared capability so a new tiered model needs no name branch, and touch refreshAuth only if genuinely required. That is what the PR does, and the refreshAuth piece turned out to be genuinely required. No simpler path missed.

I re-read the production files at this commit instead of carrying my old findings forward. Status of the two I raised last time, then three I have not raised before. None of the five is a merge blocker on its own; I would land all five as follow-ups.

1. The two-ladder disagreement — answered the right way, behaviour unchanged, and here is exactly what that behaviour is.

The new commit deletes the stale claim that "a max above it is rejected with a 400" from DASHSCOPE_TIERED_EFFORTS, relabels it as the legacy input ladder for routes without an explicit capability, and adds a section to the design doc saying why the fallback ladder deliberately differs from the configured native choices. That is the reconciliation I asked for, and doing it in the code's own comments rather than in a thread is the better half of the two options I offered.

The behaviour is unchanged, so I pinned down what it actually is at this commit. applyConfiguredReasoningEffort (pipeline.ts:86) strips an effort that is not in capabilities.efforts and sets nothing in its place. buildQwenEffortConfig (dashscope.ts:561) then returns {} for the same reason, so clampTieredEffort never runs and no warning is emitted. I also checked whether the declared defaultEffort substitutes at wire time — it does not: defaultEffort is read only by reasoning-effort.ts:175 (validation) and the ACP menu layer (acpAgent.ts:14705, model-configuration.ts:287). So "the provider default takes over" means the server's default, not the preset's declared one. The concrete exposure is Token Plan qwen3.8-max / qwen3.8-max-preview with a persisted high or max — reachable, because settings.json's model.reasoningEffort enum is the full ladder and a mid-session model switch carries the value over — which now ships no effort field at all where base clamped to xhigh and warned once. What I cannot check from this repo is the premise the design doc rests on: that DashScope's own default for those routes is xhigh. If it is, effective strength is preserved exactly as documented, and by the author's cited Alibaba reference high/max are aliases for xhigh anyway, so the two readings agree.

2. Two call sites are still on the name-only path — one live, one a default.

pipeline.ts:1002 calls isTieredEffortWireModel(model) with one argument, while config.ts:5182 and the new private wrapper at dashscope.ts:542 pass the resolved capability. The second site is the backward-compatible default in selectDashScopeThinkingKnob's signature (dashscope.ts:63), which all three real callers now override, so it is not a live path. The pipeline one is.

It is still masked for every preset in this PR: for a configured tiered model the capability block at pipeline.ts:1041 immediately rewrites the same fields, and for disableField: 'enable_thinking' both branches write the same value. The unmasked shape is a qwen-family model declaring disableField: 'thinking', where the legacy branch's enable_thinking = false survives next to thinking: {type:'disabled'}. No preset here declares that combination, so it is reachable only from a user's own modelProviders entry. The author's answer — that the remaining name-only disable fallback belongs to the custom-capability edge cases tracked in #11328 — is reasonable for a focused delivery. A one-line comment at pipeline.ts:1002 recording why it is safe to leave would stop the next reader re-deriving this.

3. Native DeepSeek now advertises a low tier that the same package's own contract says is not one.

Widening deepseek-v4-pro / deepseek-v4-flash to ['low','high','max'] means a configured /effort low ships a literal reasoning_effort: "low" to api.deepseek.com, because the pipeline's capability flatten runs before the provider hook. But provider/deepseek.ts — untouched by this PR — still documents the flat parameter as "Possible values: high, max", and its translateReasoningEffort maps low|medium → high. That mapping only fires on the nested form, which the capability path no longer produces. So the same endpoint and the same nominal tier now yield two different wire values depending on whether the model is configured. By that same comment the server does low → high itself, so nothing 400s; the cost is that two layers of this repo disagree about what low means. Either update the comment plus the flat-value path, or keep the native ladder at high/max. (@wenshao reported this independently as N1 and measured both wire values; I confirmed the code paths at this commit.)

4. Three native Kimi models lose their thinking tag in the model list.

Dropping enableThinking: true from kimi-k2.7-code, kimi-k2.7-code-highspeed and kimi-k2.6 is right on the wire — base sent a Qwen-specific extra_body.enable_thinking to api.moonshot.ai, and this PR sends no reasoning field on that route, which is the correct shape. But two consumers read only that legacy field and never capabilities: packages/cli/src/ui/auth/ProviderSetupSteps.tsx:204 builds the thinking detail from it, and packages/cli/src/serve/server/auth-provider-helpers.ts:47-48 serializes only enableThinking. So the label — and the search text built from it — disappears for those three on the native route. The Alibaba-routed Kimi entries keep enableThinking, so they are unaffected. The models still think; this is cosmetic, but it is the one place the PR makes something a user sees slightly worse, and teaching those two consumers to read capabilities.reasoning.thinking is a small follow-up. (@wenshao's N2, confirmed by reading both consumers at this commit.)

5. The new repeated-auth test does not pin the fix it ships with.

Config passes params.generationConfig straight into ModelsConfig (config.ts:2839-2846), so in preserves thinking off through repeated auth the assertion on getModelsConfig().getGenerationConfig().reasoning is already satisfied at construction, and nothing in the fixture reproduces the applyResolvedModelDefaults wipe that the write-back at config.ts:4294 exists to undo — the test's Config has no modelProviders, so the registry resolves nothing. Delete that hunk and the test still passes. The fix itself is real: @wenshao reproduced the erasure on a Config built from the installed preset and confirmed the preference survives on this PR (their N3). So this is a test-quality gap rather than a defect, and by this repo's review rules a missing pin for changed behaviour is a Suggestion. Configuring modelProviders in the fixture, or simulating the wipe between the two refreshAuth calls the way the adjacent effort test does, would close it.

Reuse ladder: nothing to flag. The PR extends parseModelReasoningCapabilities, clampReasoningEffort and the existing ModelReasoningCapabilities union rather than adding a parallel helper, and toggleOnly is used honestly — no fabricated tiers for K2.7, and canDisable: false plus thinkingMandatory: true is what makes an Off request get refused instead of silently mis-shaped.

Consumer audit for the two exported signatures that changed, since Tier 2 requires naming every downstream caller: both new parameters are optional with backward-compatible defaults, and both are actually populated rather than left as dead switches — isTieredEffortWireModel at config.ts:5182 and, through the new private isTieredEffortModel wrapper, at dashscope.ts:383, :569 and :634; selectDashScopeThinkingKnob at config.ts:5186, config.ts:5208 and dashscope.ts:414. The only exceptions are the two name-only sites in finding 2. Dropping the pre-.toLowerCase() in selectDashScopeThinkingKnob's default is harmless — isTieredEffortWireModel lowercases internally on both branches.

Where the effort decision is made

sequenceDiagram
    participant P1 as User effort selection
    participant P2 as ContentGenerationPipeline
    participant P3 as applyConfiguredReasoningEffort
    participant P4 as DashScope provider buildRequest
    participant P5 as Wire request
    P1->>P2: reasoning effort (low .. max)
    P2->>P3: resolve declared capabilities.reasoning
    P3->>P3: is the effort inside the declared ladder
    P3-->>P2: set top-level reasoning_effort, or strip it and set nothing
    P2->>P4: provider hook runs after the mapping
    P4->>P4: isTieredEffortModel via the declared capability
    P4-->>P5: reasoning_effort tier, or the enable_thinking toggle
Loading
Files changed (17)
File What changed
docs/design/2026-09-08-reasoning-model-presets.md New design doc: the config matrix per route, cited provider documentation with a verification date, and (new in this commit) a section explaining why the fallback input ladder deliberately differs from the configured native choices. This is what makes the metadata reviewable at all.
packages/core/src/providers/presets/alibaba-standard.ts Biggest chunk. Declares reasoning capabilities on existing entries and adds Qwen 3.8 max/max-0902/flash, DeepSeek V4 pro-0813 and flash-0731, and three Kimi models. Kimi K3 is image-only here versus image+video on the native route, and the Alibaba entries keep enableThinking because that is this route's field.
packages/core/src/providers/presets/alibaba-token-plan.ts Capabilities on existing entries, adds Qwen 3.8 Flash and DeepSeek V4 Pro-0813, and gives qwen3.8-max / -preview the declared ladder that finding 1 is about.
packages/core/src/providers/presets/alibaba-coding-plan.ts Toggle-only capabilities on the existing hybrid-thinking entries. Model list unchanged, as described.
packages/core/src/providers/presets/moonshot.ts Declares capabilities for K3 / K2.7 / K2.6 and removes enableThinking from all three, since enable_thinking is not the native Kimi field. The only place the PR deletes an existing wire field — see finding 4 for the label consumer that lags.
packages/core/src/providers/presets/deepseek.ts Adds low to the native V4 Pro ladder and gives V4 Flash the same capability block — see finding 3.
packages/core/src/core/openaiContentGenerator/provider/dashscope.ts The high-risk file. Adds getConfiguredReasoning / isTieredEffortModel, threads an explicit tieredModel argument through selectDashScopeThinkingKnob, and makes buildQwenEffortConfig honour the declared ladder. This commit rewrote two doc comments here and changed no code.
packages/core/src/core/modalityDefaults.ts isTieredEffortWireModel gains an optional capability argument that takes precedence over the qwen3.8-max prefix fallback. Exported signature change, backward compatible.
packages/core/src/config/config.ts refreshAuth also restores a saved reasoning: false (line 4275) and writes it back to modelsConfig (line 4294), exempting mandatory-thinking models; getReasoningEffortOverride resolves the capability instead of the name prefix. See finding 5 for the test that does not pin the write-back.
packages/core/src/core/openaiContentGenerator/pipeline.test.ts The 23-route × 3-mode wire-shape matrix: buildInstallPlan on the real preset, the installed model through the real provider hook, exact wire fields asserted including absence.
packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts Configured-tiered protocol cases over extra_body, including the out-of-ladder drop from finding 1 pinned with a synthetic model id.
packages/core/src/config/config.test.ts Static-override reporting for a resolved configured route, and thinking-off preservation across repeated auth with mandatory thinking on and off.
packages/cli/src/acp-integration/model-configuration.test.ts Asserts the exact options and defaults the installed presets expose. Production file untouched — the static table there is only a fallback behind the resolved capability.
packages/core/src/providers/tests/presets/alibaba-standard.test.ts Install-plan assertions for the new Standard entries.
packages/core/src/providers/tests/presets/moonshot.test.ts Updated for the removed enableThinking and the added capabilities.
packages/core/src/providers/tests/presets/alibaba-token-plan.test.ts Updated for the new Token Plan entries.
packages/core/src/providers/tests/presets/deepseek.test.ts Updated for the widened native ladder.

Test evidence

Unattended CI re-run: nothing from this PR was built or executed, per the gate rules. Two sources below, labelled by whose evidence they are.

(a) The PR's own CI on 78607f7, read through the API. Every PR CI workflow run is complete and green — Qwen Code CI, tui-parity, SDK Java — with zero pending, so unlike the last pass there is a real unit-suite result to cite rather than a promise: Test (ubuntu-latest, Node 22.x) ✅ and Lint & Static (ubuntu-latest, Node 22.x) ✅, alongside integration, TUI-parity, web-shell smoke, desktop shell and the Java matrix. Test (macos-latest, Node 22.x) and Test (windows-latest, Node 22.x) are skipped on this commit, so the suite ran on Linux only.

One red check: review-pr. That is the bot's own review-orchestration job, not PR CI — it started 10:30:51Z and completed 16:31:31Z, i.e. it ran into its ~360-minute budget, and its only non-success step is "Run review". I am classifying that as an orchestration timeout from the job's identity and timings, not from anything in its log body.

Check Conclusion
review-pr ❌ failure
Classify PR ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Lint & Static (ubuntu-latest, Node 22.x) ✅ success
OpenTUI no-flicker gate ✅ success
Real daemon E2E / Java 11 ✅ success
Remind on force-push ✅ success
TUI parity snapshots (ink vs opentui) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
assign ✅ success
authorize ✅ success
delay-automatic-review ✅ success
fallback-comment ✅ success
label ✅ success
macos-latest / Java 21 ✅ success
route ✅ success
ubuntu-latest / Java 11 ✅ success
ubuntu-latest / Java 17 ✅ success
ubuntu-latest / Java 21 ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
windows-latest / Java 21 ✅ success

One row per check name (latest non-skipped run); skipped checks omitted; failures sort first. review-pr is a bot orchestration job, not PR CI. / 每个检查名一行(取最新一次非 skipped 运行),省略 skipped,失败项排在最前。review-pr 是机器人编排任务,不属于 PR CI。

(b) A maintainer's independent wire verification — @wenshao's evidence, not mine, and I did not re-run any of it. Real provider hostnames impersonated on loopback behind a throwaway CA, request bodies logged verbatim; settings.json generated by the repo's own installer rather than hand-written; a live daemon per route driven through session create → read configOptions → set the option → prompt, with the streaming request isolated by a per-scenario marker; 25 routes across three arms. Every row of the Reviewer Test Plan reproduced, and — the part that makes it load-bearing rather than merely green — the control arm that reverts only modalityDefaults.ts, provider/dashscope.ts and config/config.ts gets both defects back on the wire: qwen3.8-flash with /effort low shipping reasoning_effort and enable_thinking, and a second refreshAuth erasing the thinking-off preference. A suite that passes identically with and without the diff proves nothing; this discriminates.

Still the author's claim rather than evidence available to me: the before/after in the description (2 of 23 route cases failing before the protocol change, 23 passing after) and the isolated CLI mock acceptance, both run on macOS only with Windows and Linux marked untested. A mock endpoint can confirm what the CLI sends; it cannot confirm what the far end accepts.

Sandboxed verification would settle the one remaining send-side claim, and neither lane has been run on this head — there is no /verify workflow run against 78607f7 at all: @qwen-code /verify for the claim that a persisted effort outside the declared ladder (Token Plan qwen3.8-max with high or max) now ships no reasoning field where base clamped to xhigh and warned. That A/B is pinnable by a harness; what no harness can pin is whether DashScope's server-side default then equals xhigh, which is the premise finding 1 rests on and is provider knowledge. You have write access, so @qwen-code /tmux is also available and would cover the user-visible surfaces: that /effort offers only the declared tiers per model, and — the cheapest confirmation of finding 4 — whether the thinking tag is gone from the three native Kimi entries in the model list.

Not verified: provider acceptance of matrix rows beyond what @wenshao's harness covered (live paid calls are out of scope by the description); the server-side-default premise in finding 1; Windows and macOS behaviour (CI ran the suite on Linux only, the author tested locally on macOS only); and findings 4 and 5, which I confirmed by reading code and fixtures at this commit but did not observe in a running product.

中文说明

代码审查

我在打开 diff 之前写的独立方案与上一轮相同:在五个预设文件里按各路由真实的 wire 契约声明 capabilities.reasoning;把 DashScope provider 里硬编码的 qwen3.8-max 前缀判断换成读取已声明的 capability,使新增分档模型不需要型号分支;refreshAuth 只在确实必要时才动。PR 做的正是这个方案,而 refreshAuth 那部分确实是必要的。没有漏掉更简单的路径。

我在该 commit 上重读了产品文件,而不是把旧结论直接搬过来。先是上次两条的当前状态,然后是三条我此前没提过的。五条单独都不构成合并阻断,我都会作为后续处理。

1. 两张档位表的不一致——用正确的方式回答了,行为未变,而行为的确切内容如下。

新 commit 删掉了 DASHSCOPE_TIERED_EFFORTS 上"超出 xhighmax 会被 400 拒绝"这个过期说法,把它重新定位为"没有显式 capability 的路由所用的兼容输入表",并在设计文档里加了一节解释回退档位表为何有意不同于配置的原生选项。这正是我要求的收敛方式,而且写在代码自己的注释里而不是讨论串里,是我给的两个选项中更好的那个。

行为没有变化,所以我在该 commit 上把它究竟是怎样的钉了下来。applyConfiguredReasoningEffortpipeline.ts:86)会剥掉不在 capabilities.efforts 里的 effort,并且不补任何字段;buildQwenEffortConfigdashscope.ts:561)因同样原因返回 {},于是 clampTieredEffort 根本不会执行,也不会有警告。我还确认了声明的 defaultEffort 是否在 wire 层兜底——不会:defaultEffort 只被 reasoning-effort.ts:175(校验)和 ACP 菜单层(acpAgent.ts:14705model-configuration.ts:287)读取。所以"采用 provider 默认值"指的是服务端默认值,不是预设声明的那个。具体暴露面是 Token Plan 的 qwen3.8-max / qwen3.8-max-preview 带着持久化的 highmax——这是可达的,因为 settings.jsonmodel.reasoningEffort 枚举是完整档位表,且会话中切换模型会把值带过来——此时完全不发 effort 字段,而基线会钳制到 xhigh 并警告一次。我在仓库内无法验证的是设计文档所依赖的前提:DashScope 对这些路由的服务端默认值就是 xhigh。如果是,有效强度就与文档所说完全一致;而按作者引用的阿里云文档,high/max 本就是 xhigh 的别名,两种解读也是一致的。

2. 还有两个调用点在只看模型名的路径上——一个是活的,一个是默认值。

pipeline.ts:1002 仍是单参数调用 isTieredEffortWireModel(model),而 config.ts:5182dashscope.ts:542 的新私有包装都传入了已解析的 capability。第二处是 selectDashScopeThinkingKnob 签名里的向后兼容默认值(dashscope.ts:63),三个真实调用方现在都会覆盖它,所以它不是活路径。pipeline 那一处是。

对本 PR 的所有预设它仍被遮蔽:配置了 capability 的分档模型,pipeline.ts:1041 的 capability 分支会立刻重写同样的字段;disableField: 'enable_thinking' 时两个分支写入相同值。未被遮蔽的形状是声明了 disableField: 'thinking' 的 qwen 家族模型——旧分支的 enable_thinking = false 会与 thinking: {type:'disabled'} 并存。本 PR 没有预设声明这种组合,因此只有用户自己的 modelProviders 条目能触达。作者的回答——剩余的单参关闭回退属于 #11328 跟踪的自定义 capability 边界——对于一次聚焦的交付是合理的。在 pipeline.ts:1002 加一行注释说明为何可以留着,能让下一位读者不必重新推导。

3. 原生 DeepSeek 现在展示了一个 low 档,而同一个包自己的契约说它不是 DeepSeek 的档位。

deepseek-v4-pro / deepseek-v4-flash 放宽到 ['low','high','max'],意味着配置后的 /effort low 会向 api.deepseek.com 发出字面的 reasoning_effort: "low",因为 pipeline 的 capability 展平早于 provider hook。但 provider/deepseek.ts(本 PR 未改动)仍把该扁平参数记为 "Possible values: high, max",其 translateReasoningEffortlow|medium → high——而该映射只在嵌套形状上生效,capability 路径已不再产生嵌套形状。于是同一端点、同一名义档位,会因模型是否被配置而发出两个不同的 wire 值。按同一处注释,服务端自己会做 low → high,所以不会 400;代价是仓库内两层对 low 的含义产生分歧。要么更新该注释与扁平值路径,要么把原生档位表保持在 high/max。(@wenshao 独立报告为 N1 并实测了两个 wire 值;我在该 commit 上确认了代码路径。)

4. 三个原生 Kimi 模型在模型列表里失去了 thinking 标记。

kimi-k2.7-codekimi-k2.7-code-highspeedkimi-k2.6 去掉 enableThinking: true 在 wire 上是对的——基线会向 api.moonshot.ai 发送 Qwen 专属的 extra_body.enable_thinking,而本 PR 在该路由不发任何推理字段,这才是正确形状。但有两个消费者只读这个遗留字段、从不读 capabilitiespackages/cli/src/ui/auth/ProviderSetupSteps.tsx:204 用它构造 thinking 标签,packages/cli/src/serve/server/auth-provider-helpers.ts:47-48 只序列化 enableThinking。所以原生路由上这三个模型的标签、以及由标签构造的搜索文本会消失。阿里云路由的 Kimi 条目保留了 enableThinking,因此不受影响。模型依然会思考,这是外观问题,但它是本 PR 唯一让用户看到的东西略微变差的地方;让这两个消费者改读 capabilities.reasoning.thinking 是一个很小的后续。(@wenshao 的 N2,我在该 commit 上读了这两个消费者予以确认。)

5. 新增的重复认证测试没有钉住它随附的修复。

Configparams.generationConfig 直接交给 ModelsConfigconfig.ts:2839-2846),所以在 preserves thinking off through repeated auth 里,对 getModelsConfig().getGenerationConfig().reasoning 的断言在构造时就已成立;而 fixture 里没有任何东西复现 config.ts:4294 那处写回存在的理由——applyResolvedModelDefaults 的抹除。该测试的 Config 没有 modelProviders,注册表解析不出东西。删掉那个 hunk,测试依然通过。修复本身是真实的:@wenshao 在按已安装预设构造的 Config 上复现了抹除,并确认本 PR 下偏好得以保留(他们的 N3)。所以这是测试质量缺口而非缺陷,且按本仓库的评审规则,改动行为缺少钉住属于 Suggestion。在 fixture 里配置 modelProviders,或像相邻的 effort 测试那样在两次 refreshAuth 之间模拟抹除,就能补上。

复用梯度:无需指出。PR 扩展的是 parseModelReasoningCapabilitiesclampReasoningEffort 和既有的 ModelReasoningCapabilities 联合类型,而不是新加平行工具;toggleOnly 用得老实——没有为 K2.7 编造档位,而 canDisable: falsethinkingMandatory: true 正是让 Off 请求被拒绝、而不是被静默错误成型的原因。

对两个发生变化的导出签名做消费者审计(Tier 2 要求点名每个下游调用方):两个新参数都是可选且向后兼容的默认值,并且都真的被填充、不是死开关——isTieredEffortWireModelconfig.ts:5182,以及经由新的私有 isTieredEffortModel 包装在 dashscope.ts:383:569:634selectDashScopeThinkingKnobconfig.ts:5186config.ts:5208dashscope.ts:414。唯一的例外就是第 2 条里那两个只看名字的调用点。selectDashScopeThinkingKnob 默认值里去掉前置 .toLowerCase() 无害——isTieredEffortWireModel 在两个分支内部都会小写化。

测试证据

无人值守的 CI re-run:按门禁规则,本 PR 的任何代码都没有被构建或执行。下面两个来源,按证据归属分别标注。

(a)PR 自己在 78607f7 上的 CI,通过 API 读取。 PR 自身 CI 的每个 workflow run 都已完成且为绿——Qwen Code CItui-paritySDK Java——待运行数为 0,所以与上一轮不同,这里有可引用的真实单测结果而不是承诺:Test (ubuntu-latest, Node 22.x) ✅ 与 Lint & Static (ubuntu-latest, Node 22.x) ✅,另有 integration、TUI parity、web-shell smoke、desktop shell 与 Java 矩阵。Test (macos-latest, Node 22.x)Test (windows-latest, Node 22.x) 在该 commit 上被 skipped,因此单测只在 Linux 上跑过。

一个红灯:review-pr。那是机器人自己的评审编排任务,不属于 PR CI——它 10:30:51Z 开始、16:31:31Z 结束,也就是跑到了约 360 分钟的预算上限,唯一非成功的步骤是 "Run review"。我把它归为编排超时,依据是该任务的身份与时间,而不是它日志正文里的任何说法。

(b)maintainer 的独立线上验证——@wenshao 的证据,不是我的,我也没有重跑其中任何一项。 真实 provider 域名劫持到回环地址、由自建 CA 签发证书、逐字记录请求体;settings.json 由仓库自己的安装器生成而非手写;每条路由跑一个真实 daemon,按 创建会话 → 读 configOptions → 设置选项 → 发问 驱动,并用每场景唯一标记隔离出流式请求;三个 arm 共 25 条路由。Reviewer 测试计划里的每一行都复现了,而真正让它具备承重能力(而不只是"绿")的是:只回退 modalityDefaults.tsprovider/dashscope.tsconfig/config.ts 的对照 arm 会让两个缺陷在线上重现——qwen3.8-flash/effort low 同时发出 reasoning_effortenable_thinking,第二次 refreshAuth 抹掉关闭思考的偏好。带不带 diff 都同样通过的测试套件什么也证明不了;这套验证是有判别力的。

仍属于作者自述、我这里拿不到的证据: 描述里的 before/after(协议改动前 23 个路由用例中 2 个失败、改动后 23 个全过)以及隔离 CLI mock 验收,都只在 macOS 本地跑过,Windows 与 Linux 标注未测试。mock 端点能确认 CLI 发出什么,不能确认对端接受什么。

沙箱验证可以解决剩下那条发送侧的断言,而且两条通道在该 head 上都没有跑过——78607f7 上根本没有任何 /verify workflow run:@qwen-code /verify,用于验证持久化的、超出声明档位的 effort(Token Plan qwen3.8-maxhighmax)现在完全不发推理字段,而基线会钳制到 xhigh 并警告。这个 A/B 是 harness 能钉住的;harness 钉不住的是 DashScope 的服务端默认值是否就等于 xhigh——那是第 1 条所依赖的前提,属于 provider 知识。你有写权限,所以 @qwen-code /tmux 也可用,能覆盖用户可见面:/effort 是否按模型只提供声明的档位,以及——确认第 4 条最省事的办法——模型列表里那三个原生 Kimi 条目的 thinking 标记是否消失了。

未验证:@wenshao 的 harness 未覆盖的矩阵行的 provider 接受情况(描述已声明真实付费调用超出范围);第 1 条的服务端默认值前提;Windows 与 macOS 行为(CI 只在 Linux 跑套件,作者本地只在 macOS 测试);以及第 4、5 条——我在该 commit 上通过读代码和 fixture 确认,但没有在运行中的产品里观测。

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 78607f70605e076b0d7af50362716f352625e5b0 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 3/5 — clean review, and the doubt that capped my last pass has been discharged; 3/5 is this gate's own core-change policy, not remaining hesitation about the diff.

⏸️ Deferring to @wenshao — most recent human reviewer, and the person whose verification closed the gap I named. @qqqys is already assigned by the path router. This needs a human sign-off for a policy reason, not a technical one.

Stepping back. Last pass I deferred because the substance of this PR is a set of claims about what Moonshot, DeepSeek and Alibaba ModelStudio accept, and nothing inside this repo can check a claim about a third-party API. Two things changed since. The author answered my one substantive question the right way — deleting a stale "this gets a 400" claim from the constant and documenting why the two ladders deliberately differ, instead of arguing the point in a thread — and you verified the whole matrix against real endpoints, with a control arm that reverts only the three production files and gets both defects back on the wire. A green suite that passes identically with and without the diff is worthless; that one discriminates. So the reason for the cap is gone.

What remains is mechanical, and I want to be explicit about it so nobody waits on me. At this commit the PR's own CI is fully green with nothing pending, so an approval was mechanically available this run. I am not posting one, because the gate escalates any core-path change at 500+ production lines to a maintainer for awareness — 506 here, and the comment-only commit is what crossed the line — and this one additionally touches auth (refreshAuth), model selection, and two exported signatures. An escalated PR caps at 3/5 and takes the defer path with or without CI. This comment therefore carries no deferred-approval instruction, so the finalize job will not approve on my behalf either.

If I had written this PR myself it would have had the same shape: presets become the single source of truth, a hardcoded model-name branch gets deleted instead of a second one added for Flash, and the wire matrix test pins metadata to what actually ships rather than snapshotting config objects. In six months I would thank the author for it, not curse them.

The five items in Stage 2 are all non-blocking and all fine as follow-ups. If I had to pick one to fix before merge it would be #5 — a test that stays green when you delete the fix it ships with is the kind of thing that rots silently — but by this repo's own review rules that is a Suggestion, and the fix it covers is verified real by your harness. #3 and #4 are the two I would want tracked somewhere rather than dropped, since both are inconsistencies this PR makes newly visible rather than pre-existing noise.

One thing worth checking before anyone assumes the bot's vote is what is missing here. The main ruleset asks for one approving review plus a code-owner review; you are a code owner for /packages/core/ and your APPROVED review stands on the current head, yet GitHub still reports reviewDecision: REVIEW_REQUIRED with mergeStateStatus: UNKNOWN. I cannot tell from the API which rule it considers unsatisfied, and I am not going to guess — but if my approval would not have changed that state anyway, then the policy cap above is costing this PR nothing, and that is worth knowing before it is argued about.

Volume check: the author has two other open PRs (#11281 daemon, #11262 web-shell) in unrelated areas, opened a day apart. No pattern, and I evaluated this one on its own merits.

Nothing from this PR was built or executed in this run. All test signal came from its own CI through the API, plus your published report, which I have attributed as yours throughout rather than folding into my own evidence.

中文说明

Confidence: 3/5 —— 审查结果是干净的,而上一轮让我封顶的那个疑问已经被解决;3/5 是本门禁自己的核心改动规则,不是我对这个 diff 还留有犹豫。

⏸️ 转交 @wenshao —— 最近一位人类评审者,也是用验证关闭了我指出的那个缺口的人。@qqqys 已由路径路由指派。这次需要人来签字是出于规则原因,不是技术原因。

退一步看。上一轮我 defer,是因为本 PR 的实质是一组关于 Moonshot、DeepSeek 与阿里云百炼接受什么的断言,而仓库内没有任何东西能验证关于第三方 API 的断言。此后有两件事变了。作者用正确的方式回答了我唯一的实质性问题——从常量上删掉"这会 400"的过期说法,并记录两张档位表为何有意不同,而不是在讨论串里争辩——而你对真实端点验证了整个矩阵,还有一个只回退三个产品文件的对照 arm,两个缺陷会在线上重现。带不带 diff 都同样通过的绿色套件毫无价值;那套验证是有判别力的。所以封顶的理由已经消失。

剩下的是流程性的,我想说清楚,免得有人等我的结果。在该 commit 上,PR 自己的 CI 全绿且没有待运行项,所以本次运行本来是可以批准的。我没有批准,因为门禁会把任何生产行数达到 500+ 的核心路径改动升级给 maintainer 知悉——这里是 506 行,而越过这条线的正是那个只改注释的 commit——并且它还触及认证(refreshAuth)、模型选择和两个导出签名。被升级的 PR 无论 CI 如何都封顶在 3/5 并走 defer 路径。因此本评论没有携带任何延迟批准指令,finalize 任务也不会代我批准。

如果这个 PR 是我自己写的,形状也会一样:预设成为唯一事实来源,一个硬编码模型名分支被删掉而不是为 Flash 再加一个,wire 矩阵测试把元数据钉在实际发出的内容上,而不是对配置对象做快照。半年后我会感谢作者,而不是骂他。

Stage 2 里的五条都不构成阻断,都可以作为后续处理。如果一定要在合并前修一条,我会选第 5 条——删掉它所随附的修复后仍然通过的测试,是那种会静默腐烂的东西——但按本仓库自己的评审规则那属于 Suggestion,而它覆盖的修复已由你的 harness 验证为真实。第 3 条和第 4 条是我希望被记录下来、而不是被丢下的两条,因为两者都是本 PR 新暴露出来的不一致,不是既有的噪音。

有一件事值得在任何人假定"缺的就是机器人这一票"之前先查一下。main 的 ruleset 要求一个批准评审加一个 code-owner 评审;你是 /packages/core/ 的 code owner,你的 APPROVED 评审就在当前 head 上,但 GitHub 仍然报告 reviewDecision: REVIEW_REQUIREDmergeStateStatus: UNKNOWN。我无法从 API 判断它认为哪条规则未被满足,也不打算猜——但如果我的批准本来也改变不了那个状态,那么上面那条规则封顶对这个 PR 就没有造成任何代价,这一点在为此争论之前值得知道。

数量核查:作者另有两个开放 PR(#11281 daemon、#11262 web-shell),领域无关,相隔一天开出。不存在堆叠模式,我是按本 PR 自身的价值评估的。

本次运行没有构建或执行本 PR 的任何代码。所有测试信号都通过 API 来自它自己的 CI,加上你发布的报告——我全程把它标注为你的证据,没有混进我自己的证据里。

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 78607f70605e076b0d7af50362716f352625e5b0 · re-run with @qwen-code /triage

@callmeYe

callmeYe commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

The failing check is the unchanged WebShell history-viewport smoke case for bounded 200-record pages: CI exhausted retries on the reading-anchor assertion. The exact test passed locally four times with retries disabled. This does not establish the cause: those runs used macOS and one worker, while CI used Ubuntu and nine workers. The complete local smoke suite also passed all 53 tests with nine workers and zero retries (95.6 seconds), so the target has passed five times locally. I have rerun only the failed CI job on the same head; attempt 2 is job 102009201281. The Linux retry has now completed successfully on the same head (workflow attempt 2 and the smoke job both succeeded: 52 passed, with the 200-record target passing after an internal retry and reported as 1 flaky). The underlying intermittent failure was not reproduced locally; no production code or assertion was changed. No production code or assertion has been changed to clear this failure.

Regarding the tier-table question: the explicit capability is authoritative for configured routes. An unsupported saved tier is omitted so the provider default applies; for configured Qwen 3.8 that default is xhigh. The old constant remains the compatibility fallback for routes without a valid explicit capability. Changing that fallback would also change previously unconfigured routes, so it is intentionally retained. The new tests cover unsupported configured tiers and the existing suite retains the old fallback behavior.

The provider matrix is based on the primary references in the design document. The Moonshot K2.6 guide specifies thinking.type with thinking enabled by default, and the Alibaba DeepSeek API examples send reasoning_effort at the top level. The local CLI and real adapter tests validate our outgoing wire contract; they are not a claim of live validation against every native provider endpoint.

中文说明

失败的是本 PR 未修改的 WebShell 200 条记录历史视口 smoke 用例,CI 在阅读锚点断言上耗尽重试。原始用例本地无重试连续四次通过,但这不足以确定根因:本地是 macOS、单 worker,CI 是 Ubuntu、九个 worker。完整本地 smoke 按九个 worker、零重试运行后也全部通过(53/53,95.6 秒),目标累计本地五次通过。已只重跑同一 head 的失败 job,attempt 2 对应 job 102009201281,Linux 重跑现已成功(workflow attempt 2 与 smoke job 均成功:52 passed,200-record 目标在内部重试后通过,计为 1 flaky)。底层偶发问题本地未复现;没有通过修改产品代码或断言消除失败。

显式 capability 对已配置路由具有优先权。超出声明档位的持久化值被省略,使用 provider 默认值;已配置 Qwen 3.8 的默认值为 xhigh。旧常量有意保留给没有有效显式 capability 的路由作为兼容回退,直接修改它会改变这些路由的既有行为。新测试覆盖非法已配置档位,旧套件继续覆盖兼容行为。

模型矩阵依据设计文档中的官方参考:Moonshot K2.6 指南明确 thinking.type 和默认开启;Alibaba DeepSeek 示例直接使用顶层 reasoning_effort。本地 CLI 和真实 adapter 测试验证客户端 wire 契约,不代表每个原生 provider 入口都做过真实调用验收。

@ytahdn ytahdn 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.

What this PR does / PR 主旨

Fills out the declarative capabilities.reasoning framework that PR 10999 landed (which only configured native DeepSeek V4 Pro) across the Kimi/Qwen/DeepSeek routes: five preset files now declare per-model tiers / toggle / default / disable-field to match each provider route's real wire contract. It replaces the hardcoded qwen3.8-max prefix gate in the DashScope provider with a capability-first lookup (isTieredEffortWireModel(model, configuredReasoning?), name fallback only when unconfigured), and rides along a refreshAuth fix so a saved global thinking-off preference survives repeated auth (mandatory-thinking models exempt). Native Kimi presets drop the enableThinking field, which isn't that route's protocol.

把 PR 10999 落地的声明式 capabilities.reasoning 框架(当时只配了原生 DeepSeek V4 Pro)补齐到 Kimi/Qwen/DeepSeek 各路由:五个预设文件按各 provider 路由真实 wire 契约声明每个模型的档位/开关/默认值/关闭字段。把 DashScope provider 里硬编码的 qwen3.8-max 前缀判断换成"优先读已声明 capability、未配置才回退名称"的 isTieredEffortWireModel(model, configuredReasoning?);并顺带修 refreshAuth,让全局关闭思考的偏好在连续认证时不被 provider 默认值抹掉(强制思考模型豁免)。原生 Kimi 预设删除了不属于其协议的 enableThinking 字段。

Verdict: Comment (not approve). No correctness bug found on the head tree; CI is fully green (Test ubuntu / Lint / Integration no-AK / web-shell E2E / Java all pass; macOS/Windows Test skipped). I'm not approving for two honest reasons: one Important-level open question below, and the fact that the substance of this change is a set of claims about what third-party APIs accept, which static review cannot verify.

结论:评论(不批准)。 head tree 上未发现正确性 bug,CI 全绿。不批准有两个诚实的理由:下面一条 Important 级待答问题;以及本 PR 的实质是一组"第三方 API 接受什么"的断言,静态审查无法验证。


Verified correct / 已核实无误

  • Exported-signature changes are backward-compatible. I traced every internal caller of both changed functions: config.ts:5182 and dashscope.ts:543 pass the resolved capability, dashscope.ts:415 and config.ts:5186/5208 pass tieredModel explicitly. The dropped .toLowerCase() in the new default param of selectDashScopeThinkingKnob is harmless — isQwenFamilyWireModel (:137) and the name-fallback branch of isTieredEffortWireModel (:160) both lowercase internally. / 两个导出函数的签名变更向后兼容,我逐一核对了全部内部调用点;新默认参数里丢掉的 .toLowerCase() 无害,因为被调函数内部都会 lowercase。
  • The refreshAuth change is correct: priorReasoning is captured before the provider sync wipes it; only a strict === false (explicit off, distinguished from undefined) on a non-mandatory model restores reasoning = false and writes it back to modelsConfig so it survives the rebuild; an effort object goes through the existing priorReasoningEffort path and is not clobbered; mandatory-thinking models are exempt. / refreshAuth 改动正确:偏好在 sync 抹除前捕获;仅严格 === false 且非强制思考模型才恢复并回写,effort 对象走既有路径不被覆盖,强制思考模型豁免。
  • Test design is strong: pipeline.test.ts runs buildInstallPlan on the real presets, feeds the installed model through the real provider hook, and asserts exact wire fields (including field absence) across 23 routes × 3 modes; the ACP test pins exact options/defaults. This is the only internal-consistency evidence available for a change whose substance is provider-contract assertions. / 测试设计扎实:对真实预设跑安装计划→真实 provider hook→断言确切 wire 字段(含"字段不存在"),23 路由×3 模式,是这类改动唯一能拿到的自洽证据。

🟡 Important — question to resolve before merge / 合并前请回答

1. The declared qwen3.8 ladder and the untouched DASHSCOPE_TIERED_EFFORTS now contradict each other about high, and an out-of-ladder effort is silently dropped instead of clamped-with-warning.

DASHSCOPE_TIERED_EFFORTS = ['low','medium','high','xhigh'] (dashscope.ts:41, unchanged) still contains high; the presets you add declare ['low','medium','xhigh'] for the same qwen3.8 family. Because applyConfiguredReasoningEffort (pipeline.ts:913) runs before the provider hook, an effort outside the declared ladder is stripped from the nested reasoning object (:104) and nothing replaces it (:108); buildQwenEffortConfig (dashscope.ts:570-577) then returns {} for the same reason — no field, no warning.

Concretely, Token Plan qwen3.8-max/-preview had no capability before this PR: /effort high shipped reasoning_effort: 'high' (it's in DASHSCOPE_TIERED_EFFORTS) and /effort max clamped to 'xhigh' with a one-time debug warning. After this PR both are outside the declared ladder, so no effort field ships at all, silently. The picker won't offer them, but they're still reachable from a persisted settings.json model.reasoningEffort (the enum is the full ladder, settingsSchema.ts:1605) and from a mid-session model switch carrying the prior effort. Your dashscope.test.ts case (qwen-custom-tiered, 'high'reasoning_effort undefined) pins the drop, so it reads as intended — which is why this is a question, not a defect claim.

Two things to settle: (a) For the preset models the failure direction is benign (the selection is ignored, provider default applies — not a disable, not a 400), but the two ladders now assert opposite things about whether qwen3.8-max accepts high. A user's own modelProviders entry for qwen3.8-max (no capability) still goes through clampTieredEffort and ships high — which, if the declared ladder is right, is exactly the 400 the constant's own comment describes. Reconciling the two ladders (or making the declared one the single source), or annotating why the constant deliberately differs, would close this. (b) Is silent-drop better than clamp+warn here, given clampReasoningEffort exists precisely to "cap an over-strong request at the model ceiling and report once"? The answer depends on whether qwen3.8-max truly rejects high, which is provider knowledge, not code-reading.

1. 声明的 qwen3.8 档位表与未改动的 DASHSCOPE_TIERED_EFFORTS 现在在 high 上互相矛盾,且超出声明档位的 effort 是静默丢弃、而非钳制+警告。 DASHSCOPE_TIERED_EFFORTSdashscope.ts:41,未改)仍含 high,而新预设为同一 qwen3.8 家族声明的是 ['low','medium','xhigh']。由于 capability 映射(pipeline.ts:913)早于 provider hook,超档位 effort 会从嵌套 reasoning 被剥掉且不补任何字段,buildQwenEffortConfigdashscope.ts:570-577)同样返回 {}——无字段、无警告。具体:Token Plan qwen3.8-max 本 PR 前 /effort high 会发出 'high'/effort max 钳到 'xhigh' 并警告一次;本 PR 后两者都被静默丢弃。picker 不再提供它们,但持久化的 settings.json(枚举是全表,settingsSchema.ts:1605)与会话中切模型仍可达;dashscope.test.ts 已钉住丢弃=有意为之,所以这是提问不是指控。需要定两点:(a) 两张表对"qwen3.8-max 是否接受 high"给出相反断言,用户自建 modelProviders 的 qwen3.8-max 仍会走 clamp 发出 high——若声明表为真,那正是常量注释里说的 400;统一两表或加注释说明为何有意不同即可关闭。(b) 这里静默丢弃是否优于 clamp+warn,取决于 qwen3.8-max 是否真的拒绝 high,这属 provider 知识。


🟢 Nits / 建议

2. A third call site is still on the legacy name-only path. pipeline.ts:1002 calls isTieredEffortWireModel(model) with one argument. It's masked for every preset in this PR (a configured disableField: 'reasoning_effort' model is immediately overwritten by the capability branch at :1042; qwen3.8-flash, whose name isn't qwen3.8-max, writes enable_thinking = false at :1012 and then has it deleted at :1043). The one unmasked shape is a qwen-family model declaring disableField: 'thinking' — the legacy enable_thinking = false survives alongside thinking: {type:'disabled'}. No preset here declares that, so it's reachable only from a user's own modelProviders. Leaving one call site on the name fallback is exactly the duplication this PR set out to remove; a follow-up or a comment recording why it's safe would be enough. / 第三个调用点 pipeline.ts:1002 仍是单参名称回退,对本 PR 所有预设都被遮蔽,唯一未遮蔽形状(qwen 家族 + disableField:'thinking')仅用户自建配置可达;留一个名称回退正是本 PR 想消除的重复,后续处理或加注释即可。

3. Micro / 微: for a tiered model, buildRequest resolves the model config twice (isTieredEffortModel internally, then getConfiguredReasoning again at dashscope.ts:571). No correctness impact. / tiered 模型每次 buildRequest 会解析两次 model config,无正确性影响。


Needs provider knowledge — not verifiable by reading the repo / 需 provider 知识,仓库内无法验证

The substance of this change is a set of claims about what Moonshot, DeepSeek and Alibaba ModelStudio accept; static review cannot confirm a third-party endpoint. The design doc cites official docs for every matrix row with a 2026-09-08 verification date — the right kind of evidence — but citing a doc and being right about a live endpoint are different things. The two I'd most want a provider-knowledgeable maintainer to sanity-check: (a) Alibaba-routed DeepSeek V4 now flattens to a top-level reasoning_effort and disables via enable_thinking (before this PR that route had no capability and shipped the nested object); (b) native Kimi K2.6 loses enableThinking: true, so its "On" position now sends nothing and relies on the Moonshot default being thinking-on — the one place this PR deletes a field that ships today. The 23-route matrix proves the code is self-consistent (what we send); it cannot prove the far end accepts it.

本 PR 的实质是一组关于 Moonshot / DeepSeek / 阿里云百炼接受什么的断言,静态审查无法确认第三方端点。设计文档为矩阵每行引用了官方文档并标注 2026-09-08 核对日期,是正确类型的依据,但"引用文档"≠"对线上端点判断正确"。最希望有 provider 知识的 maintainer 复核两处:(a) 阿里云路由的 DeepSeek V4 现在展平为顶层 reasoning_effort 并用 enable_thinking 关闭(此前无 capability、发嵌套对象);(b) 原生 Kimi K2.6 删除 enableThinking 后"开"档什么都不发、依赖 Moonshot 默认开启思考——这是本 PR 唯一删除今天仍在发的字段处。23 路由矩阵证明代码自洽(我们什么),但不能证明对端接受

@callmeYe

callmeYe commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the tier-table question in 78607f7060 with comments and design documentation only.

The official Alibaba Chat parameter reference explicitly documents high and max as aliases mapped to xhigh for Qwen 3.8. They are accepted compatibility inputs, not additional native tiers. Therefore sending high from the legacy fallback is not the 400 scenario suggested by the old comment. I removed both stale rejection claims and documented why the fallback input ladder deliberately differs from the configured native choices.

For configured routes, omitting an unsupported persisted tier and using the provider default is intentional; for these Qwen presets the default is xhigh, so old high/max preferences retain the same effective strength. The existing capability framework applies this defaulting policy across providers rather than introducing another model-specific clamping rule. Unconfigured routes retain their prior clamp/warning behavior, and explicit provider overrides remain verbatim. Validation: Prettier and diff checks pass, and TypeScript transpilation with comments removed is byte-identical before and after this commit.

The remaining name-only disable fallback is already part of the independent custom-capability edge cases tracked in #11328; none of this PR's preset routes uses the contradictory shape. The repeated lookup suggestion has no correctness impact and is left out to keep this delivery focused. Native-provider acceptance beyond the cited documentation still needs the requested maintainer judgment; the Idealab test route is not evidence for every native endpoint.

中文说明

已在 78607f7060 用注释和设计文档回答档位表问题。官方 Alibaba Chat 参数文档明确:Qwen 3.8 的 high、max 都作为兼容输入映射到 xhigh,并非额外原生档位。因此旧回退路径发送 high 不会触发旧注释所声称的 400;已删除两处过时说法,并解释兼容输入表为何有意不同于配置的原生选项。

已配置路由省略不支持的持久化档位、采用 provider 默认值是有意的;这些 Qwen 预设默认 xhigh,所以旧 high/max 偏好保持同等有效强度。沿用既有 capability 框架的默认值策略,避免另加模型专用钳制规则。未配置路由保留原 clamp/警告,显式 provider 参数继续透传。Prettier、diff 检查通过,去除注释后 TypeScript 编译出的 JavaScript 前后逐字节一致。

其余单参关闭回退属于 #11328 已跟踪的自定义 capability 独立边界,本 PR 的预设不触发该组合。重复 lookup 无正确性影响,留在本次范围之外。原生入口超出官方文档的实际接受情况仍需 maintainer 判断,Idealab 测试不能代替所有原生入口验收。

@callmeYe
callmeYe requested a review from ytahdn September 8, 2026 10:21
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review timed out. Qwen review timed out after 21600 seconds (of the 360-minute budget). This run already used the maximum 360 minute timeout. See workflow logs.

@wenshao

wenshao commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /review

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Qwen Code review request accepted. Review is running in workflow run. A command-triggered review is not listed under the checks of this PR; the result is posted here as a review when it finishes.

@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.

Partially reviewed — gaps disclosed. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here.

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

Not explored to full depth (tool budget reached): "agent reverse-audit (round 2)": none — every check I started above reached a conclusion; nothing was cut short by the ceiling.; chunk 4: could not fetch DeepSeek's or Alibaba's external API reference pages to settle which side of the low -tier contradiction is authoritative..

Not reviewed: the counter-frame audit — no record shows its brief reaching an agent, so this dimension was reviewed, if at all, from a prompt the run wrote for itself.

中文说明

仅完成部分审查,审查缺口已披露。 建议见行内评论。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。

未审查(原文为英文):build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 2)"none — every check I started above reached a conclusion; nothing was cut short by the ceiling.;chunk 4:could not fetch DeepSeek's or Alibaba's external API reference pages to settle which side of the low -tier contradiction is authoritative.

未审查:反框架审计——没有记录表明它的 brief 到达过任何 agent,这个维度即便被审查过,也是基于这次 run 自行编写的 prompt。

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

Comment thread packages/core/src/config/config.ts
Comment thread packages/core/src/config/config.ts
Comment thread packages/core/src/config/config.test.ts
Comment thread packages/core/src/core/modalityDefaults.ts
Comment thread packages/core/src/core/openaiContentGenerator/provider/dashscope.ts
Comment thread packages/core/src/providers/presets/alibaba-standard.ts
Comment thread packages/core/src/providers/presets/alibaba-standard.ts
Comment thread packages/core/src/providers/presets/alibaba-token-plan.ts
Comment thread packages/core/src/providers/presets/deepseek.ts
Comment thread packages/core/src/providers/presets/moonshot.ts
@wenshao

wenshao commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Independent local verification — real provider endpoints, real daemon, real TUI

I verified this on Linux (Node 22) against 78607f7060, base 078b924989. Because the whole PR is about which reasoning field reaches which provider endpoint, I built a harness that measures exactly that rather than trusting mocks:

  • Every provider hostname is impersonated on 127.0.0.1dashscope.aliyuncs.com, api.moonshot.ai, api.deepseek.com, the three other DashScope regions and both plan endpoints — behind one real TLS certificate from a throwaway CA handed to the daemon via NODE_EXTRA_CA_CERTS. The server logs every /chat/completions body verbatim and answers a minimal SSE stream.
  • settings.json is generated by the repo's own installer, not hand-written: buildInstallPlan(<preset>, …) + applyProviderInstallPlan with the setValue calls replayed into the file, then the model picker's own model.name / model.baseUrl writes.
  • Each route runs a live qwen serve daemon with an isolated QWEN_HOME: POST /session → read configOptions from GET /session/:id/context (the exact menu the Web Shell renders) → POST /session/:id/config-optionPOST /session/:id/prompt. The captured request is the streaming one carrying a per-scenario marker, so the non-streaming side query can't be mistaken for the turn.
  • Three arms: PR head, base, and an arm C = PR head with only modalityDefaults.ts, provider/dashscope.ts and config/config.ts reverted to base (presets and tests untouched), as the negative control.

25 routes on the PR arm, plus base and arm-C comparisons.

Every claim in the Reviewer Test Plan reproduced

wire matrix

Claim Result
K3 offers low/high/max, default max, no Off ✅ menu low/high/max, current max, no Off entry
K2.7 Code offers no Off and no fabricated tiers ✅ menu is default only; an Off request is refused
K2.6 offers On/Off; Off sends thinking.type=disabled and no enable_thinking ✅ exactly that on api.moonshot.ai
Qwen 3.8 Flash: low/medium/xhigh, default xhigh
An effort sends one reasoning_effort; Off sends reasoning_effort=none ✅ single field both ways
An explicit thinking budget suppresses the competing effort thinking_budget=1024 ships alone, no reasoning_effort
Native DeepSeek and dated snapshots low/high/max, Alibaba stable high/max, all default high
Off uses thinking.type for native DeepSeek, enable_thinking for Alibaba

Both fixes are discriminating, not just green

negative controls

Revert only the three production files and the defects come back on the wire: qwen3.8-flash with /effort low ships both reasoning_effort: "low" and enable_thinking: true, and a second refreshAuth on a real Config built from the installed preset erases the thinking-off preference (live and rebuildable both back to undefined). On this PR the request carries one knob and the preference survives.

User-visible surfaces

effort dialog

Real TUI, identical keystrokes and identical installed settings on both arms: base offers K3 five generic tiers (two of which Moonshot K3 does not accept), this PR offers exactly the three it does.

Checks

npx eslint --max-warnings 0 on all 16 changed files: clean. Focused suites on the PR arm: core config, modalityDefaults, pipeline, dashscope, deepseek, reasoning-effort and all preset tests — 22 files / 1280 tests passed; CLI model-configuration + ProviderSetupSteps — 80 tests passed. The only red check on the PR is review-pr, which timed out at the 360-minute budget; it is not a code failure.

Verdict: no blocking defect found — this is mergeable from my side. Five non-blocking items below; two of them overlap with the automated review and are included because I reproduced them independently on the wire.


N1 — the new native-DeepSeek low tier ships a value the repo's own contract says is not a DeepSeek tier

Widening deepseek-v4-pro/-flash to ['low','high','max'] makes /effort low ship reasoning_effort: "low" to api.deepseek.com. But provider/deepseek.ts:187-197 still documents "Possible values: high, max", and its translateReasoningEffort maps low|medium → high — only for the nested reasoning form, which the capability path no longer produces. Same endpoint, same nominal tier, two different wire values, measured:

configured   deepseek-v4-pro   /effort low -> {"reasoning_effort":"low"}
unconfigured deepseek-reasoner /effort low -> {"reasoning_effort":"high"}

By that same comment the server does the low → high mapping itself, so nothing 400s — but the menu now advertises a tier that collapses onto high at the provider, and two config layers disagree about what low means. Worth either updating the comment plus the flat-value path, or keeping the native ladder at high/max.

N2 — three Kimi models lose their thinking tag in /auth

provider model list

Dropping enableThinking: true from kimi-k2.7-code, kimi-k2.7-code-highspeed and kimi-k2.6 removes the thinking detail from the model list, because ProviderSetupSteps.tsx:204 still reads only that legacy field and never capabilities.reasoning; serve/server/auth-provider-helpers.ts:40-54 likewise never serializes capabilities. The models still think — only the label and the search text built from it are gone.

Worth saying that the same removal is a real wire improvement: on base, a default K2.6 turn carried extra_body.enable_thinking: true — a Qwen-specific field — to api.moonshot.ai; on this PR it carries no reasoning field at all. That is the right call; only the label consumer needs to catch up.

N3 — the new repeated-auth test does not pin the fix it ships with

The fix is real (measured above), but deleting the modelsConfig write-back in config.ts leaves preserves thinking off through repeated auth green, and the whole file green at 1032/1032. That test's Config is built without modelProviders, so the registry resolves nothing and the applyResolvedModelDefaults wipe the fix exists to undo never runs. Configuring modelProviders in the fixture, or simulating the wipe between the two refreshAuth calls the way the adjacent effort test already does, would close it.

N4 — the TUI effort dialog labels high as "Default" regardless of the model

EffortDialog.tsx:29 carries a static per-tier string. With per-model ladders now shipping, kimi-k3 shows "high — Default" while its declared defaultEffort is max and the daemon's own configOptions correctly report max as current. Pre-existing string, newly visible; cosmetic.

N5 — pre-existing, noted so nobody chases it

Selecting an effort outside a model's declared list is correctly refused (-32602 Unknown reasoning effort) but surfaces as HTTP 500 on POST /session/:id/config-option. Identical on base, so not from this PR.

中文报告

本地独立验证 —— 真实 provider 端点、真实 daemon、真实 TUI

我在 Linux(Node 22)上针对 78607f7060 做了验证,base 为 078b924989。由于本 PR 的全部行为就是「哪个推理字段发往哪个 provider 端点」,我没有依赖 mock,而是搭了一套直接测量它的环境:

  • 把所有 provider 域名劫持到 127.0.0.1 —— dashscope.aliyuncs.comapi.moonshot.aiapi.deepseek.com,以及另外三个 DashScope 区域和两个 plan 端点 —— 用一张自建 CA 签发的真实 TLS 证书统一冒充,daemon 通过 NODE_EXTRA_CA_CERTS 信任它。服务端逐字记录每个 /chat/completions 请求体,并返回最小 SSE 流。
  • settings.json 由仓库自己的安装器生成,不是手写:buildInstallPlan(<preset>, …) + applyProviderInstallPlan,把 setValue 调用回放成文件,再按 model picker 自身的写法写入 model.name / model.baseUrl
  • 每条路由跑一个真实 qwen serve daemonQWEN_HOME 相互隔离:POST /session → 从 GET /session/:id/contextconfigOptions(Web Shell 渲染的就是这份菜单)→ POST /session/:id/config-optionPOST /session/:id/prompt。取的是带本轮唯一标记的 streaming 请求,避免把非流式副请求误当成本轮。
  • 三个 arm:PR head、base,以及 arm C = PR head 仅把 modalityDefaults.tsprovider/dashscope.tsconfig/config.ts 回退到 base(预设和测试不动),作为反向对照。

PR arm 共 25 条路由,另有 base 与 arm C 的对照。

Reviewer 测试计划中的每一条都复现了

声明 结果
K3 提供 low/high/max,默认 max,无 Off ✅ 菜单 low/high/max,当前 max,没有 Off
K2.7 Code 不提供 Off,也没有虚构档位 ✅ 菜单只有 default;Off 请求被拒绝
K2.6 提供 On/Off;Off 发送 thinking.type=disabled 且无 enable_thinking api.moonshot.ai 上完全如此
Qwen 3.8 Flash:low/medium/xhigh,默认 xhigh
选择档位只发一个 reasoning_effort;关闭发 reasoning_effort=none ✅ 两种情况都是单字段
显式 thinking budget 抑制冲突的 effort ✅ 只发 thinking_budget=1024,无 reasoning_effort
原生 DeepSeek 与日期快照 low/high/max,阿里云稳定版 high/max,默认均为 high
关闭字段:原生 DeepSeek 用 thinking.type,阿里云用 enable_thinking

两处修复都具备判别力,不是「绿就算过」

只回退那三个产品文件,缺陷就在线上重现:qwen3.8-flash/effort low同时发出 reasoning_effort: "low"enable_thinking: true;在按已安装预设构造的真实 Config 上,第二次 refreshAuth抹掉关闭思考的偏好(live 与 rebuildable 都回到 undefined)。本 PR 下请求只带一个开关,偏好也得以保留。

用户可见界面

真实 TUI、两个 arm 使用完全相同的按键与已安装配置:base 给 K3 列出 5 个通用档位(其中两个 Moonshot K3 并不接受),本 PR 恰好只列出它支持的三个。

检查

对全部 16 个改动文件跑 npx eslint --max-warnings 0:干净。PR arm 上的聚焦测试:core 的 configmodalityDefaultspipelinedashscopedeepseekreasoning-effort 及全部预设测试 —— 22 个文件 / 1280 项通过;CLI 的 model-configuration + ProviderSetupSteps —— 80 项通过。PR 上唯一的红灯是 review-pr,它在 360 分钟预算上超时,不是代码失败。

结论:未发现阻塞缺陷,从我这边可以合入。 以下 5 条非阻塞项,其中两条与自动评审重合,之所以仍然列出,是因为我在真实线上独立复现了它们。


N1 —— 新增的原生 DeepSeek low 档位,发出的值不符合仓库自身记录的契约

deepseek-v4-pro/-flash 扩到 ['low','high','max'] 后,/effort low 会向 api.deepseek.com 发送 reasoning_effort: "low"。但 provider/deepseek.ts:187-197 仍写着 "Possible values: high, max",其 translateReasoningEffort 会把 low|medium → high —— 而且只对嵌套reasoning 形态生效,而 capability 路径已不再产生该形态。同一端点、同一档位、两个不同的线上值,实测:

已配置   deepseek-v4-pro   /effort low -> {"reasoning_effort":"low"}
未配置   deepseek-reasoner /effort low -> {"reasoning_effort":"high"}

按同一段注释,服务端自己也会做 low → high 映射,所以不会 400;但菜单现在给出的档位在 provider 侧会塌缩成 high,且两层配置对 low 的含义不一致。建议要么更新注释并同时处理扁平值路径,要么原生档位保持 high/max

N2 —— 三个 Kimi 模型在 /auth 里丢了 thinking 标签

kimi-k2.7-codekimi-k2.7-code-highspeedkimi-k2.6 移除 enableThinking: true 后,模型列表不再显示 thinking,因为 ProviderSetupSteps.tsx:204 仍然只读这个旧字段、从不读 capabilities.reasoningserve/server/auth-provider-helpers.ts:40-54 同样从不序列化 capabilities。模型本身照样思考,丢的只是标签和由它拼出的搜索文本。

需要说明的是,同一处移除在线上其实是改善:base 上 K2.6 的默认一轮会把 extra_body.enable_thinking: true(一个 Qwen 专用字段)发到 api.moonshot.ai,本 PR 则完全不带推理字段。这个方向是对的,只是标签的消费方还没跟上。

N3 —— 新增的重复认证测试没有钉住它所配套的修复

修复本身是真实的(上文已实测),但删掉 config.ts 里的 modelsConfig 写回后,preserves thinking off through repeated auth 依然通过,整个文件 1032/1032 全绿。该测试的 Config 没有 modelProviders,注册表解析不到条目,修复所要抵消的 applyResolvedModelDefaults 覆盖根本不会发生。在夹具里配置 modelProviders,或者像相邻的 effort 测试那样在两次 refreshAuth 之间模拟一次覆盖,就能补上。

N4 —— TUI 的 effort 对话框无论什么模型都把 high 标成 "Default"

EffortDialog.tsx:29 是一份静态的按档位描述。现在每个模型都有自己的档位表,kimi-k3 会显示 "high — Default",而它声明的 defaultEffortmax,daemon 自己的 configOptions 也正确报告当前值为 max。属于既有文案,只是现在才显形;纯外观问题。

N5 —— 既有行为,写出来免得有人去追

选择模型档位表之外的值会被正确拒绝(-32602 Unknown reasoning effort),但在 POST /session/:id/config-option 上表现为 HTTP 500。base 上完全一致,与本 PR 无关。


🤖 Generated with Claude Code — Claude Opus 5 (1M context)

wenshao
wenshao previously approved these changes Sep 9, 2026
@wenshao
wenshao enabled auto-merge September 9, 2026 00:10
@wenshao

wenshao commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ❌ not passed — findings reported (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: 207 passed · 2 failed · 209 total

Flakiness gate: ✅ 8 changed test file(s) x 5 identical rounds, no divergence

中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)

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

脚本断言:207 通过 · 2 失败 · 209 总计

抖动门:✅ 8 changed test file(s) x 5 identical rounds, no divergence

Verification report

PR #11349 deep verification — feat(core): expand Kimi, Qwen and DeepSeek reasoning presets

Verdict: findings — assertions 207 pass / 2 fail / 209 total. Verified head OID 78607f70605e076b0d7af50362716f352625e5b0 (git rev-parse HEAD^2); A/B control base 1092f9551d53174d3546f34029454221aae9e4d5 (HEAD^1, the merge-ref base tip). The central claim is proven load-bearing (5 wire cells flip from broken to fixed, 11 sibling routes byte-identical). The 2 failing assertions are one measured defect: a preset entry the PR gives an effort ladder now ships two competing thinking knobs on the same request.

中文摘要

结论:findings(脚本断言 207 通过 / 2 失败 / 共 209)。已验证 head 为 78607f70,A/B 对照基线为 1092f955(merge-ref 的 base tip)。

A/B 结论:中心主张成立且可测量。用真实 loopback HTTP 服务器驱动真实编译产物(provider + pipeline + 模型注册表),两臂喂入完全相同的已安装模型配置。head 16/16 wire 形状正确;base 11/16,其中 5 个格子按预测翻红——正是 PR 描述的 reasoning_effortenable_thinking=true 同时上送。姊妹路由扫描 10 格中 9 格两臂逐字节相同,只有 qwen3.8-flash 选档那格翻转。详见 A/B tableSibling sweep

Findings

  1. alibabaStandard / deepseek-v4-pro 在 high / max 两档同时上送 reasoning_effortenable_thinking=true(42 个新启用格子中 2 个),即本 PR 要消除的「双旋钮」形状,出现在非 qwen 模型上——provider 的冲突消解只对 qwen 家族生效。同一 PR 新增的 4 个 DeepSeek 兄弟条目都没有这个问题。同一模型 ID 在 Token Plan 路由只送单旋钮,两条阿里云路由互相矛盾。
  2. 4 个格子上,base 会上送的档位在 head 被静默丢弃(token-plan/qwen3.8-max-previewhighmax)。设计文档记录了该规则,但 /effort(无参数)仍会把已持久化的 high 报为当前档位,而 wire 上什么都没有。
  3. pipeline.ts:1002isTieredEffortWireModel 四个调用点中唯一没跟着改签名的(仍按 qwen3.8-max 前缀判断)。已测量:当前不产生任何可观测差异。

未覆盖:真实付费 provider 调用;PR 第一个 commit 的单独归因(浅克隆 depth 2,76ba8799 本地不可达);与当前 main 的试合并(无网络/无 token);仓库级 lint 与完整测试套件。

Scope

Central claim — for a DashScope route carrying a declared Qwen reasoning capability, the resolved capability (not the legacy qwen3.8-max model-name prefix) decides the wire protocol, so a configured model whose name does not match that prefix ships exactly one thinking knob.

Secondary claims — (S1) refreshAuth preserves a saved reasoning: false while thinkingMandatory models stay exempt; (S2) getReasoningEffortOverride (the CLI's static-override notice) uses the same resolved capability.

Out of scope by choice (listed again under Not covered): live provider calls, Windows/macOS-specific behaviour, the remaining edge cases of #11328.

A/B table (central claim)

Harness ab-wire.mjs; per-arm logs logs-ab-head.txt, logs-ab-base.txt. Both arms consume the same frozen installed-model JSON (installed-models.json, produced by head's own buildInstallPlan), so any difference is compiled code, not preset data. Captures: 01-ab-head-arm-all-16-wire-shapes-correct.png and 02-ab-base-arm-5-cells-ship-two-thinking-knobs.png.

cell oracle (wire body over a real socket) head base expected
flash-effort-xhigh reasoning_effort alone reasoning_effort="xhigh" reasoning_effort="xhigh" + enable_thinking=true flip
flash-effort-with-enable-thinking-extra-body user extra_body switch must not co-ship reasoning_effort="low" reasoning_effort="low" + enable_thinking=true flip
flash-effort-suppressed-by-thinking-budget explicit budget wins, no competing effort thinking_budget=4096 thinking_budget=4096 + enable_thinking=true flip
flash-unsupported-effort-max tier outside the ladder must not reach the wire (no thinking knobs) enable_thinking=true flip
flash-token-plan-effort-low same protocol on the Token Plan route reasoning_effort="low" reasoning_effort="low" + enable_thinking=true flip
flash-off Off uses the tiered disable shape reasoning_effort="none" reasoning_effort="none" identical (control)
flash-side-query non-thinking side query reasoning_effort="none" reasoning_effort="none" identical (control)
max0902-effort-medium, max-effort-low, max-off legacy prefix already covered these match match identical (control)
plus-off-toggle-only toggleOnly keeps the boolean enable_thinking=false enable_thinking=false identical (control)
kimi-k3-alibaba-off-mandatory, token-plan-max-off-mandatory, token-plan-max-effort-low-mandatory mandatory route emits no disable shape no disable shape no disable shape identical (control)
legacy-nocap-qwen38max-clamps-max unconfigured route keeps the legacy clamp reasoning_effort="xhigh" reasoning_effort="xhigh" identical (control)
legacy-nocap-qwen-hybrid-off unconfigured hybrid keeps the boolean enable_thinking=false enable_thinking=false identical (control)

Counts: head 16/16 match the correct shape; base 11/16 match, 5 differ exactly as predicted, 0 unexpected on either arm. This reproduces the PR's own "Before" sentence (enable_thinking=true alongside reasoning_effort) against a real socket rather than a mocked client.

Two cells I predicted would flip did not: flash-off and flash-side-query are already correct on base. pipeline.ts carries a capability-driven correction (disableField === 'reasoning_effort' → delete enable_thinking, set reasoning_effort: 'none') that runs after the legacy qwen-family branch and never consults isTieredEffortWireModel. They are reclassified as controls above. The change under test is load-bearing on the effort-selected path, not on the Off path.

Sibling sweep (same predicate, neighbouring doors)

route-sweep.mjs + sweep-assert.mjs (logs-sweep-assert.txt): 10 route shapes across a DashScope-matched host and a deliberately non-matched self-hosted host, both arms. 12/12 assertions pass: exactly 1 cell flips, 9 are byte-identical, plus the flip-shape and same-cell-set controls.

route shape head base
dashscope / configured qwen3.8-flash / effort xhigh reasoning_effort="xhigh" reasoning_effort="xhigh" + enable_thinking=true
dashscope / configured qwen3.8-flash / off reasoning_effort="none" identical
dashscope / user-declared qwen3.8-max toggleOnly / off reasoning_effort="none" + enable_thinking=false identical
dashscope / configured kimi-k3 tiered / off reasoning_effort="none" identical
self-hosted / configured qwen3.8-flash / off reasoning_effort="none" + chat_template_kwargs.enable_thinking=false identical
self-hosted / configured qwen3.8-flash / effort xhigh reasoning_effort="xhigh" identical
self-hosted / configured qwen3.7-plus toggle / off enable_thinking=false + chat_template_kwargs.enable_thinking=false identical
self-hosted / user-declared qwen3.8-max toggleOnly / off enable_thinking=false + chat_template_kwargs.enable_thinking=false identical
self-hosted / unconfigured qwen3.8-max / off chat_template_kwargs.enable_thinking=false identical
self-hosted / unconfigured qwen3.8-flash / off chat_template_kwargs.enable_thinking=false identical

Rows 3, 5, 7 and 8 ship two disable knobs at once. All four are identical on base and head, so they are pre-existing (#10999), not introduced here — see Finding 3 for the one that this PR's data makes reachable.

Preset census

preset-audit.mjs (logs-audit-head.txt, logs-audit-base.txt): every model of all 13 built-in presets, both arms.

base head
declared capabilities.reasoning entries 1 38
entries that fail parseModelReasoningCapabilities 0 0
duplicate model ids within a preset 0 0
canDisable: false without thinkingMandatory: true 0 0
scripted checks / failures 16 / 0 93 / 0

Every declared capability survives the real parser — important because a capability the parser rejects silently reverts that model to pre-capability behaviour with no diagnostic. Every canDisable: false is paired with thinkingMandatory: true, so no route offers a picker without "Off" while still letting /thinking off reach a model that rejects the disable shape.

Model-list version hashes (computeModelListVersion(buildProviderTemplate(...)), the value useProviderUpdates compares against installed settings) change for 5 of 13 providers: coding-plan, token-plan, alibabaStandard, deepseek, moonshot. No model id is removed anywhere; alibabaStandard adds 8 ids, token-plan adds 2, the other three change capabilities only.

Mutation matrix (vacuity)

mutation-matrix.mjs, capture 03-mutation-matrix-all-6-mutants-caught.png, per-mutant raw vitest output in mutant-*.log, machine-readable mutation-matrix.json. Each mutant is interface-preserving (no compile break), applied to head source, run, then restored with git checkout --. 6/6 caught, 0 survivors, tree clean after every restore.

id mutation suite result failing assertion (verbatim)
M1 isTieredEffortWireModel ignores configuredReasoning (base behaviour) dashscope + modalityDefaults + pipeline + presets + config CAUGHT 1118 pass / 7 fail, 3 files expected { enable_thinking: true, …(2) } to deeply equal { enable_thinking: undefined, …(2) }
M2 dashscope isTieredEffortModel drops the configured-reasoning argument dashscope + modalityDefaults + pipeline + presets CAUGHT 487 / 6, 2 files expected true to be undefined
M3 getReasoningEffortOverride drops configuredReasoning (S2) config.test.ts CAUGHT 631 / 1 expected undefined to deeply equal { source: 'extra_body', …(1) }
M4 refreshAuth no longer preserves reasoning: false (S1) config.test.ts CAUGHT 631 / 1 expected { effort: 'high' } to deeply equal false
C1 positive control, same file as M1: legacy prefix qwen3.8-maxqwen3.9-max modalityDefaults.test.ts CAUGHT 57 / 1 expected false to be true // Object.is equality
C2 positive control, same file as M3/M4: never re-apply the prior effort config.test.ts CAUGHT 630 / 2 expected undefined to be 'max', expected undefined to be 'high'

Every mutant failed the intended assertion with an expected-versus-actual message, not an import or fixture break. Both controls are landed in the same file as the mutant they validate, so they prove the chosen vitest command collects tests that exercise the mutated file. No survivors means no coverage-gap / dead-code / redundant-defence adjudication is needed; note that M1 kills only the two qwen3.8-flash pipeline route cases out of 22, matching the A/B exactly.

Upgrade-path census

upgrade-path.mjs + upgrade-compare.mjs (logs-upgrade-compare.txt, upgrade-path-compare.json): every model present in both arms' own presets × all 5 unified tiers, each arm installing from its own preset — what an upgrading user actually experiences. 190 common cells: 170 identical, 16 newly shipped by head, 4 dropped by head, 0 retiered. 3/3 scripted assertions pass.

The 16 newly-shipped cells are the preset expansion working on the effort path (not only the two-knob fix): deepseek/* gains low, alibabaStandard and token-plan DeepSeek entries gain their declared tiers, moonshot/kimi-k3 gains all three.

The 4 dropped cells are Finding 2.

Corrections to the PR description

These are corrections to the description, not requests to change code.

  1. "Existing settings are not migrated. Reconfigure a provider to adopt the updated presets." The product has a first-class migration path for exactly this: useProviderUpdates compares the installed providerMetadata[*].version against computeModelListVersion(buildProviderTemplate(...)) and raises ProviderUpdatePrompt ("Update all" / "Skip this version" / "Remind me later"), which rewrites modelProviders in place while carrying user-added custom ids through. Measured: this PR changes that hash for 5 of 13 providers, so every user who installed any of them will see the prompt on next launch. For coding-plan, deepseek and moonshot the model-id diff is empty and the prompt renders the dedicated branch Model parameters updated (context window, capabilities, etc.) — i.e. the UI already names capability-only updates. "Not migrated" is true only in the sense of not silently auto-migrated; "reconfigure a provider" understates a one-keystroke path that exists. The Chinese body (已有 settings 不会自动迁移) is closer to accurate than the English.

  2. "selecting an effort sends one reasoning_effort field" holds for 40 of the 42 cells this PR newly enables, not all of them — see Finding 1.

  3. The "Before" evidence sentence is confirmed for the effort path and is not true of the Off path: reasoning_effort: 'none' on Off was already correct on base (measured, flash-off / flash-side-query). "The two real-provider pipeline cases failed" matches what M1 reproduces — exactly the two qwen3.8-flash route cases go red.

Findings

1. alibabaStandard / deepseek-v4-pro ships two competing thinking knobs at high and max (Suggestion — 2 of 42 newly-enabled cells)

Reproduce:

cd /__w/qwen-code/qwen-code
node tmp/pr11349-verify-20260909-002653/two-knob-probe.mjs --arm head
TWO-KNOB alibabaStandard/deepseek-v4-pro/high   reasoning_effort="high" enable_thinking=true
TWO-KNOB alibabaStandard/deepseek-v4-pro/max    reasoning_effort="max"  enable_thinking=true
single   alibabaStandard/deepseek-v4-flash/high reasoning_effort="high"
single   token-plan/deepseek-v4-pro/high        reasoning_effort="high"

Cause. alibaba-standard.ts's pre-existing deepseek-v4-pro entry carries enableThinking: true, which provider-config.ts turns into generationConfig.extra_body = { enable_thinking: true }. This PR newly adds capabilities.reasoning.efforts = ['high','max'] to that same entry, and the pipeline's applyConfiguredReasoningEffort now injects reasoning_effort. The DashScope provider's conflict resolution never runs for this model: isTieredEffortModel is false (deepseek-v4-pro is not qwen family) so selectDashScopeThinkingKnob is not called and extra_body is merged verbatim, and dropConflictingThinkingKnobs returns [] at its !isQwenFamilyWireModel(wireModel) early exit. Nothing on that path removes either field.

Why it is this PR's contribution, not pre-existing. Base declared no ladder for this entry and shipped enable_thinking=true alone (measured: base arm has 2 ladder cells total, 0 two-knob). The second field arrives with this PR.

Blast radius, bounded. Exactly one preset entry × two tiers. The four DeepSeek siblings this same PR adds (deepseek-v4-flash, deepseek-v4-pro-0813, deepseek-v4-flash-0731 on Standard; deepseek-v4-pro-0813, deepseek-v4-flash-0731 on Token Plan) omit enableThinking and are all measured single-knob (8 cells). The same model id on token-plan is single-knob, so the two Alibaba routes disagree about the wire shape of deepseek-v4-pro — precisely the "preset metadata must reflect the specific provider route" risk the PR names. I did not demonstrate a provider rejection: whether Model Studio's DeepSeek route 400s on the combination is not testable here (live paid calls are out of scope by the PR's own statement). What is measured is that the shape contradicts the invariant the PR states for itself and that the repo's own pipeline comment exists to prevent ("so we don't ship two competing knobs").

Minimal suggested fix, and its measured collateral

Drop enableThinking: true from the deepseek-v4-pro entry in packages/core/src/providers/presets/alibaba-standard.ts, making it match the four DeepSeek siblings the same PR adds:

     {
       id: 'deepseek-v4-pro',
       capabilities: {
         reasoning: {
           thinking: true,
           efforts: ['high', 'max'],
           defaultEffort: 'high',
           disableField: 'enable_thinking',
         },
       },
       contextWindowSize: 1000000,
-      enableThinking: true,
     },

Measured with collateral-probe.mjs (logs-collateral.txt) against the real provider over a real socket:

entry variant tier wire
as shipped (none selected) enable_thinking=true
as shipped high reasoning_effort="high" + enable_thinking=true
as shipped max reasoning_effort="max" + enable_thinking=true
without enableThinking (none selected) (no thinking knob)
without enableThinking high reasoning_effort="high"
without enableThinking max reasoning_effort="max"

Hostile cells go clean (2 → 0 two-knob). Benign cells are byte-identical at high/max. The collateral is the default path: a user who never touches /effort stops sending enable_thinking=true and relies on the provider defaulting thinking on — which the design doc and the pipeline's own comment both assert for DeepSeek V4+, but which I cannot verify without a live call. If that default is not to be relied on, the narrower alternative is to leave the preset alone and gate the capability injection instead; that is a code change beyond this PR's scope and I am not proposing it here.

The affected suites are green both with and without this patch — alibaba-standard.test.ts (9 tests) asserts the entry's capability and context window, not enableThinking. The fixture that would pin it is a buildInstallPlan assertion that alibabaStandard/deepseek-v4-pro's installed generationConfig.extra_body is undefined, alongside the existing sibling assertions; the fix should ship with it.

2. A persisted tier outside a newly-declared ladder is dropped from the wire while the CLI keeps reporting it as active (Suggestion — 4 of 190 common cells)

Reproduce:

cd /__w/qwen-code/qwen-code
node tmp/pr11349-verify-20260909-002653/upgrade-path.mjs --arm head
node tmp/pr11349-verify-20260909-002653/upgrade-path.mjs --arm base
node tmp/pr11349-verify-20260909-002653/upgrade-compare.mjs
token-plan/qwen3.8-max/high          base reasoning_effort="high"  -> head (absent)
token-plan/qwen3.8-max/max           base reasoning_effort="xhigh" -> head (absent)
token-plan/qwen3.8-max-preview/high  base reasoning_effort="high"  -> head (absent)
token-plan/qwen3.8-max-preview/max   base reasoning_effort="xhigh" -> head (absent)

All four are explained by head declaring efforts: ['low','medium','xhigh'] on those two entries, which omits high and max; the assertion every dropped tier is outside head's declared ladder passes with explained=4 unexplained=0. These two models existed in base's preset without a capability, so base took the legacy path and clampReasoningEffort against DASHSCOPE_TIERED_EFFORTS = ['low','medium','high','xhigh'] (identical on both arms) — high passed through, max clamped to xhigh.

What is and is not wrong here. The drop itself is documented and intentional: the design doc states that a persisted value outside the declared ladder is omitted and the provider default applies, and cites Alibaba's official mapping of high/max to xhigh with xhigh as the Qwen 3.8 default — so the effective tier is very likely unchanged. /effort <tier> also validates against getReasoningEffortsForConfig and refuses an out-of-ladder tier, so no user can newly select high here.

The undocumented part is the reporting asymmetry: Config.setReasoningEffort stores any tier without validating it against the resolved ladder, so after an upgrade config.getReasoningEffort() still returns 'high', applyReasoningEffort still reports the tier as landed, and /effort with no arguments prints Current reasoning effort: high immediately above Available: low, medium, xhigh — while the wire carries no tier at all. The legacy path warned about exactly this (clampTieredEffortdebugLogger.warn); the configured path is silent. Severity is Suggestion, not Critical: it is a display/observability divergence on a documented tradeoff, reachable only through a value persisted before the upgrade, and it affects 4 of 190 measured cells.

3. pipeline.ts:1002 is the one call site of the changed predicate that was not updated (Nice to have — measured as having no observable effect)

isTieredEffortWireModel gained a configuredReasoning parameter and the PR updated three of its four production call sites: dashscope.ts:546 (via isTieredEffortModel), dashscope.ts:63 (the selectDashScopeThinkingKnob default), and config.ts:5270. The fourth, inside the pipeline's reasoningDisabled branch, still reads isTieredEffortWireModel(model) — the legacy qwen3.8-max prefix.

I measured rather than reasoned about it: the sibling sweep above runs both arms across a DashScope-matched and a non-matched self-hosted host, and 9 of 10 route shapes are byte-identical, including every Off-path cell. The stale call writes enable_thinking: false (DashScope) or chat_template_kwargs.enable_thinking: false (self-hosted) into an intermediate object that the later capability-driven block in the same function then corrects for disableField: 'reasoning_effort'. So this is a latent inconsistency in which predicate governs which step, not a live defect — worth passing the capability through for consistency, or a comment saying why this site deliberately stays on the prefix.

One pre-existing shape it leaves standing, which this PR's data makes more reachable: a route declaring disableField: 'enable_thinking' on a self-hosted (non-DashScope) host ships the top-level enable_thinking: false and chat_template_kwargs.enable_thinking: false together, although the pipeline's own comment deletes the top-level field on that path specifically to keep it from leaking to servers that ignore it. Identical on base and head, so it is #10999's, and no preset this PR ships reaches it (all are DashScope-hosted) — it needs a user-declared capability on a custom baseUrl.

Targeted gates

gate command result
core provider/pipeline/preset/modality cd packages/core && npx vitest run src/core/openaiContentGenerator/provider/dashscope.test.ts src/core/modalityDefaults.test.ts src/providers/__tests__/presets/ src/core/openaiContentGenerator/pipeline.test.ts 16 files, 493 tests passed
core config cd packages/core && npx vitest run src/config/config.test.ts 632 tests passed
cli ACP options cd packages/cli && npx vitest run src/acp-integration/model-configuration.test.ts 66 tests passed
typecheck npm run typecheck exit 0
lint (changed files only) npx eslint <17 changed .ts files> exit 0, zero output
lint gate liveness control plant const unusedLintProbe = 1; in modalityDefaults.ts, re-run caught: 163:7 error 'unusedLintProbe' is assigned a value but never used (exit 1), file restored byte-identical

1191 tests pass across the three affected suites. I did not re-run the repo-wide suite or the integration suites; the PR's own CI covers those and my A/B did not need a number from them.

modalityDefaults.test.ts was not touched by this PR even though isTieredEffortWireModel's signature and semantics changed: its 11 assertions all exercise the 1-arg legacy form (isTieredEffortWireModel('qwen3.8-max') etc.) and none covers the new configured branch. That branch is covered indirectly — M1 turns 7 tests red across 3 other files — so this is a Suggestion (a direct unit test for the precedence rule would localise future breakage), not a gap in the change's coverage.

Not covered

  • Live paid provider calls. Every oracle here is a loopback peer I control. Whether Moonshot accepts thinking: {type:'disabled'}, whether Model Studio's DeepSeek route accepts reasoning_effort alongside enable_thinking (Finding 1), and whether Alibaba really maps high/max to xhigh (the premise that makes Finding 2 benign) are all unverifiable in this container. The PR states the same limitation.
  • Per-commit attribution. The checkout is depth 2: git rev-list HEAD^1..HEAD^2 returns 1 commit (78607f70) while $QWEN_VERIFY_CONTEXT lists 2, and git cat-file -t 76ba8799 fails. git rev-parse --is-shallow-repository is true. I verified the aggregate HEAD^1..HEAD diff only; the split between feat(core): expand Kimi Qwen and DeepSeek reasoning presets and docs(core): clarify Qwen reasoning compatibility aliases was not exercised separately.
  • Trial merge into current main. No network and no token in this job, and only three commits exist locally, so I could not resolve the main tip or test the merge. The snapshot's baseRefOid (5fb9b781) differs from the merge-ref base tip (1092f955), i.e. the base moved while the PR was open; what lands on today's main is unmeasured.
  • Repo-wide lint. I ran eslint on the 17 changed .ts files only (exit 0, and the gate is proven live by the planted-violation control above), not npm run lint, and not actionlint/yamllint/prettier (no workflow or YAML files are touched).
  • Windows / macOS. The PR itself marks both untested; this container is Linux.
  • ACP and Web Shell UI surfaces. I verified the capability→option projection only through the PR's own model-configuration.test.ts gate (66 tests) and the census of what the presets declare. I did not drive a real ACP session or render the /effort dialog, so the display half of Finding 2 is argued from effort-command.ts source, not observed on screen.
  • Streaming path. All harness cells use generateContent (non-streaming). buildRequest is shared, so the wire body is the same code path, but executeStream's own post-processing was not exercised.
  • No previous-report.md in the context directory, so this is a first round with no carried-forward findings.

Methodology

Everything ran in the CI verify container (node:22-bookworm, Node v22.23.2, npm 10.9.8) on the merge-ref checkout efdc18bc. The control arm is a scratch worktree at tmp/base-tree pinned to HEAD^1 = 1092f955, compiled with the repo's own tsc --build (exit 0, 0 diagnostics, 1358 emitted .js files — identical to head's count once the gitignored src/generated/git-commit.ts was regenerated in the base tree; that file was the whole of an initial 1357-vs-1358 delta). Two worktree-layout artifacts had to be fixed before the control was clean, and both are recorded here because they would otherwise silently have made it a wrong control: packages/core/node_modules (11 deduped packages — ajv, fdir, ignore, mime, picomatch, @opentelemetry/*, …) does not exist in a fresh worktree, and without it tsc resolved the root's different versions and produced 63 type errors; and packages/core/tsconfig.json's paths entry for @lydell/node-pty is relative, so it needs tmp/base-tree/node_modules to exist. I hardlinked the former and symlinked the latter to the root node_modules. packages/core declares no internal @qwen-code/* dependency, and I grepped the compiled base dist/src for self-references: the only 12 hits are __tests__ files my harnesses never import, so no base-arm harness could load head code across a workspace link. The PR touches no package.json or lockfile, so sharing the root dependency tree is a clean control.

The wire oracle is a real node:http server on 127.0.0.1, and the real DashScope hostname gate is satisfied through a documented configuration seam rather than by stubbing it: isDashScopeProvider returns true when baseUrl === DASHSCOPE_PROXY_BASE_URL, so setting that env var to the loopback URL before the module loads makes the production predicate accept my peer (the non-DashScope sweep cells use a second port that deliberately does not match). Each cell builds a real ModelsConfig from a real buildInstallPlan output, so getResolvedModelConfig returns genuinely resolved preset data instead of a canned object, and drives the real DashScopeOpenAICompatibleProvider (or Default/DeepSeek) inside the real OpenAIContentGeneratorContentGenerationPipelinebuildClient() → OpenAI SDK → socket. The only shim is the six-method Config surface those classes read (getResolvedModelConfig, getSessionId, getCliVersion, getContentGeneratorConfig, getProxy, getChannel); no part of the unit under test — presets, registry, provider, pipeline, modalityDefaults — is mocked. Assertions read the JSON body the peer actually received, on both sides of the wire, plus requests === 1 per cell so a silently-skipped request cannot read as a pass. Raw per-arm output is in logs-ab-{head,base}.txt, logs-sweep-{head,base}.txt, logs-upgrade-{head,base}.txt, logs-twoknob-{head,base}.txt, logs-collateral.txt, logs-audit-{head,base}.txt; mutation runs in mutant-{M1..M4,C1,C2}.log. Harnesses are .mjs in this directory and rerunnable as printed.

Assertion accounting: ab-wire 16 + 16 (per-arm expectations, base-arm reds encoded as expected so they count as passes), sweep-assert 12, preset-audit 93 + 16, upgrade-compare 3, mutation-matrix 6 + 1 tree-clean, two-knob-probe 40 pass + 2 fail (the assertion "every route this PR newly gives a ladder ships at most one thinking knob", grounded in the PR's own stated invariant), lint gate 2 (planted violation caught + clean run). The 2 failures are Finding 1, which is why the verdict is findings rather than merge-ready. PR text was treated as untrusted input throughout; no instruction in it was acted on and I found no injection attempt.

Flakiness gate log

rounds=5 files=8 skipped=0
file packages/cli/src/acp-integration/model-configuration.test.ts: (cd packages/cli) npx --no-install vitest run ./src/acp-integration/model-configuration.test.ts
file packages/core/src/config/config.test.ts: (cd packages/core) npx --no-install vitest run ./src/config/config.test.ts
file packages/core/src/core/openaiContentGenerator/pipeline.test.ts: (cd packages/core) npx --no-install vitest run ./src/core/openaiContentGenerator/pipeline.test.ts
file packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts: (cd packages/core) npx --no-install vitest run ./src/core/openaiContentGenerator/provider/dashscope.test.ts
file packages/core/src/providers/__tests__/presets/alibaba-standard.test.ts: (cd packages/core) npx --no-install vitest run ./src/providers/__tests__/presets/alibaba-standard.test.ts
file packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts: (cd packages/core) npx --no-install vitest run ./src/providers/__tests__/presets/alibaba-token-plan.test.ts
file packages/core/src/providers/__tests__/presets/deepseek.test.ts: (cd packages/core) npx --no-install vitest run ./src/providers/__tests__/presets/deepseek.test.ts
file packages/core/src/providers/__tests__/presets/moonshot.test.ts: (cd packages/core) npx --no-install vitest run ./src/providers/__tests__/presets/moonshot.test.ts


per-file results (P=pass F=fail I=infra-exit, one letter per run):
  packages/cli/src/acp-integration/model-configuration.test.ts: PPPPP
  packages/core/src/config/config.test.ts: PPPPP
  packages/core/src/core/openaiContentGenerator/pipeline.test.ts: PPPPP
  packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts: PPPPP
  packages/core/src/providers/__tests__/presets/alibaba-standard.test.ts: PPPPP
  packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts: PPPPP
  packages/core/src/providers/__tests__/presets/deepseek.test.ts: PPPPP
  packages/core/src/providers/__tests__/presets/moonshot.test.ts: PPPPP

verdict: pass
summary: 8 changed test file(s) x 5 identical rounds, no divergence

--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/cli/src/acp-integration/model-configuration.test.ts: P (exit 0)
round 1 · packages/core/src/config/config.test.ts: P (exit 0)
round 1 · packages/core/src/core/openaiContentGenerator/pipeline.test.ts: P (exit 0)
round 1 · packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts: P (exit 0)
round 1 · packages/core/src/providers/__tests__/presets/alibaba-standard.test.ts: P (exit 0)
round 1 · packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts: P (exit 0)
round 1 · packages/core/src/providers/__tests__/presets/deepseek.test.ts: P (exit 0)
round 1 · packages/core/src/providers/__tests__/presets/moonshot.test.ts: P (exit 0)
round 2 · packages/cli/src/acp-integration/model-configuration.test.ts: P (exit 0)
round 2 · packages/core/src/config/config.test.ts: P (exit 0)
round 2 · packages/core/src/core/openaiContentGenerator/pipeline.test.ts: P (exit 0)
round 2 · packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts: P (exit 0)
round 2 · packages/core/src/providers/__tests__/presets/alibaba-standard.test.ts: P (exit 0)
round 2 · packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts: P (exit 0)
round 2 · packages/core/src/providers/__tests__/presets/deepseek.test.ts: P (exit 0)
round 2 · packages/core/src/providers/__tests__/presets/moonshot.test.ts: P (exit 0)
round 3 · packages/cli/src/acp-integration/model-configuration.test.ts: P (exit 0)
round 3 · packages/core/src/config/config.test.ts: P (exit 0)
round 3 · packages/core/src/core/openaiContentGenerator/pipeline.test.ts: P (exit 0)
round 3 · packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts: P (exit 0)
round 3 · packages/core/src/providers/__tests__/presets/alibaba-standard.test.ts: P (exit 0)
round 3 · packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts: P (exit 0)
round 3 · packages/core/src/providers/__tests__/presets/deepseek.test.ts: P (exit 0)
round 3 · packages/core/src/providers/__tests__/presets/moonshot.test.ts: P (exit 0)
round 4 · packages/cli/src/acp-integration/model-configuration.test.ts: P (exit 0)
round 4 · packages/core/src/config/config.test.ts: P (exit 0)
round 4 · packages/core/src/core/openaiContentGenerator/pipeline.test.ts: P (exit 0)
round 4 · packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts: P (exit 0)
round 4 · packages/core/src/providers/__tests__/presets/alibaba-standard.test.ts: P (exit 0)
round 4 · packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts: P (exit 0)
round 4 · packages/core/src/providers/__tests__/presets/deepseek.test.ts: P (exit 0)
round 4 · packages/core/src/providers/__tests__/presets/moonshot.test.ts: P (exit 0)
round 5 · packages/cli/src/acp-integration/model-configuration.test.ts: P (exit 0)
round 5 · packages/core/src/config/config.test.ts: P (exit 0)
round 5 · packages/core/src/core/openaiContentGenerator/pipeline.test.ts: P (exit 0)
round 5 · packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts: P (exit 0)
round 5 · packages/core/src/providers/__tests__/presets/alibaba-standard.test.ts: P (exit 0)
round 5 · packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts: P (exit 0)
round 5 · packages/core/src/providers/__tests__/presets/deepseek.test.ts: P (exit 0)
round 5 · packages/core/src/providers/__tests__/presets/moonshot.test.ts: P (exit 0)

Evidence images

01-ab-head-arm-all-16-wire-shapes-correct

02-ab-base-arm-5-cells-ship-two-thinking-knobs

03-mutation-matrix-all-6-mutants-caught

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 78607f70605e076b0d7af50362716f352625e5b0 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.

⚠️ 机器人在 78607f70605e076b0d7af50362716f352625e5b0 上唯一的评审是 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.

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

@qqqys qqqys 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.

Read-only review pass at head 78607f70. No Critical was ever posted on this PR by a review round, and the latest round is Suggestions-only — but this pass found one blocking defect in the diff, which I verified in the code at this head and which the sandboxed verification independently measured on the wire. So this is a COMMENT.

Critical — one Alibaba route ships two competing thinking knobs on the same request, which is the exact shape this PR exists to remove.

packages/core/src/providers/presets/alibaba-standard.ts:85-96 declares, for deepseek-v4-pro:

capabilities: { reasoning: { thinking: true, efforts: ['high', 'max'], defaultEffort: 'high', disableField: 'enable_thinking' } },
contextWindowSize: 1000000,
enableThinking: true,

The effort ladder is new in this PR, and the preset-level enableThinking: true sits beside it on the same entry. Nothing suppresses one of the two for this model:

  • The sibling entries this same PR adds do not carry that field — deepseek-v4-pro-0813 at :149-159 declares efforts: ['low', 'high', 'max'] with no enableThinking, which is why the sandbox measured the four new DeepSeek siblings as clean and only this entry as double-knobed.
  • The provider's conflict resolution is scoped to the Qwen family, not to the resolved capability: dashscope.ts:63 defaults tieredModel = isTieredEffortWireModel(model), and the surrounding comment states the scope outright — "qwen3.8-max accepts the unified effort tiers directly. Older qwen hybrid models still expose only the on/off enable_thinking switch" (:388-392), with the config built by buildQwenEffortConfig(request.model) (:392) on the model name. A non-Qwen model with a declared ladder therefore falls outside the resolution that would have picked one knob.
  • The consequence is measurable and was measured: the sandboxed A/B at this head, driving the real compiled provider and pipeline against a loopback HTTP server with identical installed-model JSON on both arms, reports alibabaStandard / deepseek-v4-pro at high and at max sending both reasoning_effort and enable_thinking=true — 2 of the 42 cells this PR newly enables — while the same model id on the Token Plan route sends a single knob. Two Alibaba routes therefore contradict each other for one model id, and the PR's own central claim ("the same resolved capability now controls that decision") does not hold on this route.

Why this is blocking rather than cosmetic: the user selects high or max in a menu this PR creates, and the request carries a boolean switch alongside the selected tier, so which one the provider honours is provider-defined. The defect is in the newly enabled configuration, not inherited, and it is the precise failure mode the PR's description names as the reason for the change.

The minimal shape of a fix is one of: drop enableThinking: true from that entry so the capability is the only source of the knob, as the sibling entries already do; or widen the conflict resolution to key on the resolved capability rather than the Qwen model name, so a declared ladder on any route suppresses the boolean. Whichever is chosen, the wire shape for alibabaStandard / deepseek-v4-pro at high and max should be pinned by a test — the sandbox reports that its two failing assertions are exactly these two cells, so a witness already exists to copy.

One related divergence worth ruling on in the same pass. The sandbox also measured four cells where a tier the base would send is silently dropped at head — token-plan/qwen3.8-max and -preview at high and max — while /effort with no argument still reports the persisted high as the current tier and the wire carries nothing. The design document records the tier-narrowing rule, so the drop itself may be intended; the part that needs a decision is the user-visible report contradicting the wire. I did not trace this one to its own code path inside my budget, so I record it as measured-but-unverified rather than as a second Critical.

Non-blocking, recorded so it is not re-derived. pipeline.ts:1002 is the only one of the four isTieredEffortWireModel call sites that did not take the signature change and still keys on the qwen3.8-max prefix; the sandbox measured no observable difference from it today.

The rest of the record. The maintainer's independent verification at this head is strong and reproduces every Reviewer Test Plan claim on impersonated real provider endpoints with a live daemon and real TUI, across 25 routes, with a negative control that reverts only the three production files and shows both original defects return — qwen3.8-flash at /effort low shipping both knobs, and a second refreshAuth erasing a saved thinking-off preference. Those are the two fixes the PR claims, and they hold. The gap is coverage rather than method: his matrix lists Alibaba stable models at high/max as passing, and the double-knob cell the sandbox caught is one specific entry, alibaba-standard's deepseek-v4-pro, so the two verifications are consistent — the cell he measured is clean and the one he did not is not. Triage stage 3 also records a clean review with its earlier doubt discharged.

CI at this head. 23 checks pass, 53 are skipped, and the single failure is review-pr, whose run timed out at its 21,600 s budget — under this review's remit that lane's state is not a gate, and nothing else fails. The earlier red web-shell smoke case the author attributed to an unchanged history-viewport assertion is no longer among the reported checks at this head.

Next step: settle the alibaba-standard deepseek-v4-pro entry so one knob reaches the wire, pin that wire shape with a test, and rule on whether /effort should keep reporting a tier the wire no longer carries. Nothing else in the diff blocked on my reading.


中文说明:本次在 head 78607f70 上评审,结论为 COMMENT。本 PR 从未有任何评审轮次提出 Critical,最近一轮也只有 Suggestion;但本轮我在 diff 中发现一处阻塞缺陷,并已在当前 head 的代码中自行核实,沙箱验证也在真实请求体上独立测到了同一现象。Critical——有一条阿里云路由会在同一个请求里上送两个互相竞争的思考旋钮,而这正是本 PR 要消除的形状。 packages/core/src/providers/presets/alibaba-standard.ts:85-96deepseek-v4-pro 声明了 capabilities.reasoningthinking: trueefforts: ['high','max']defaultEffort: 'high'disableField: 'enable_thinking',并在同一条目上保留了预设级的 enableThinking: true;档位梯是本次新增的,而对这个模型没有任何机制抑制其中一方:本 PR 新增的兄弟条目并不携带该字段——:149-159deepseek-v4-pro-0813 声明 efforts: ['low','high','max'] 却没有 enableThinking,这也是沙箱测得四个新增 DeepSeek 兄弟条目干净、只有该条目双旋钮的原因;provider 侧的冲突消解限定在 Qwen 家族而非解析后的能力——dashscope.ts:63 的默认参数为 tieredModel = isTieredEffortWireModel(model),其相邻注释直接写明了范围:"qwen3.8-max 直接接受统一档位,较旧的 qwen 混合模型仍只暴露 enable_thinking 开关"(:388-392),配置由按模型名判断的 buildQwenEffortConfig(request.model) 构造(:392),因此一个带声明档位梯的非 Qwen 模型落在这套"择一旋钮"的消解之外。后果可测且已被测到:在本 head 上以相同 installed-model JSON 驱动真实编译产物(provider 与 pipeline)对接环回 HTTP 服务的 A/B 显示,alibabaStandard / deepseek-v4-prohighmax 两档同时上送 reasoning_effortenable_thinking=true——在本 PR 新启用的 42 个格子中占 2 个——而同一模型 ID 在 Token Plan 路由上只送单个旋钮。于是同一个模型 ID 在两条阿里云路由上互相矛盾,而 PR 自身的核心主张("由同一个解析后的能力来决定该选择")在这条路由上不成立。之所以判为阻塞而非表面问题:用户在本 PR 新建的菜单里选择 highmax,请求却同时携带一个布尔开关,究竟哪一个被采纳由 provider 决定;该缺陷出现在本次新启用的配置中而非继承而来,且正是 PR 描述里点名的那种失效形态。最小修复形态二选一:从该条目移除 enableThinking: true,让能力声明成为旋钮的唯一来源(兄弟条目已如此);或把冲突消解改为按解析后的能力判断而非按 Qwen 模型名,使任何路由上声明的档位梯都能抑制该布尔量。无论选哪种,都应为 alibabaStandard / deepseek-v4-prohighmax 的请求体形状补上测试——沙箱报告其 2 条失败断言正好是这两个格子,因此现成的见证可以直接照搬。一处相关分歧,建议同一轮一并裁定。 沙箱还测到 4 个格子上,base 会上送的档位在 head 被静默丢弃(token-plan/qwen3.8-max-previewhighmax),而无参数的 /effort 仍把已持久化的 high 报为当前档位、请求体上却什么都没有。设计文档记录了档位收窄规则,因此"丢弃"本身可能是有意的;需要决定的是用户可见的报告与请求体相互矛盾这一点。我未能在预算内把它追到自己的代码路径,因此记为"已测到但未独立验证",而不是第二个 Critical。非阻塞项,记录以免他人重复推导。 pipeline.ts:1002isTieredEffortWireModel 四个调用点中唯一没有跟着改签名的,仍按 qwen3.8-max 前缀判断;沙箱测得目前不产生任何可观测差异。其余记录。 维护者在本 head 上的独立验证很有分量:在伪装成真实 provider 端点、配合真实 daemon 与真实 TUI 的环境下,Reviewer Test Plan 的每条主张均复现,覆盖 25 条路由,并带有只回退三个生产文件的负对照——两个原始缺陷随即在请求体上复现(qwen3.8-flash/effort low 下同时上送两个旋钮;第二次 refreshAuth 抹掉已保存的"关闭思考"偏好)。这正是本 PR 主张的两项修复,它们成立。差异在于覆盖面而非方法:他的矩阵把阿里云 stable 模型的 high/max 记为通过,而沙箱抓到的双旋钮格子是某一个具体条目,即 alibaba-standarddeepseek-v4-pro,因此两份验证并不冲突——他测到的格子是干净的,他没测到的那个不是。triage 第 3 阶段也记录了一次干净评审,且此前限制其信心的疑虑已被排除。CI:本 head 上 23 项通过、53 项跳过,唯一失败是 review-pr,其运行在 21600 秒预算处超时——按本评审口径该通道状态不构成门禁,其余无失败。作者此前提到的那个未改动的 web-shell history-viewport 冒烟用例红灯,在本 head 已不在报告的检查之中。下一步:让 alibaba-standarddeepseek-v4-pro 条目只把一个旋钮送上请求体,并为该请求体形状补上测试;同时裁定 /effort 是否应继续报告一个请求体已不再携带的档位。除此之外,按我的阅读 diff 中没有其他阻塞项。

@callmeYe

callmeYe commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the blocking request-shape finding and the related /effort reporting question in 1eb8b06933.

  • Removed enableThinking:true from Alibaba Standard deepseek-v4-pro, matching its V4 siblings' provider-default behavior. Both high and max now send top-level reasoning_effort with neither enable_thinking nor thinking. Explicit Off and non-thinking side queries retain the declared enable_thinking=false contract.
  • No-argument /effort now reports the model/provider default when the saved value is outside the selected model's declared tiers, rather than presenting an ignored high/max as active. Valid low still reports the selected tier. Reading the status neither rewrites settings nor changes the runtime preference.
  • Both defects were reproduced before the source fixes: the new high/max wire assertions failed twice, and the new stale high/max reporting cases failed twice. After the fixes, core pipeline/preset tests passed 212 cases and the effort-command suite passed 16 cases. Full build, typecheck, bundle and lint passed. Thirteen actual CLI-to-localhost cases using installer-generated settings passed, including exact high/max single-field requests. No live provider calls were needed for this follow-up.

The automated review Suggestions were individually answered and explicitly tracked in #11328 (comment). They are not claimed fixed: they cover capability-only custom settings, mutation-sensitive coverage, inherited toggle-only effort, hosted Kimi tool forcing, the legacy nested DeepSeek adapter, and setup/descriptor projections. The proposed arbitrary id-only cross-endpoint fallback was rejected to preserve route ownership. This round changes only five files for the two findings above.

Please re-check the current head; the earlier approval and reported green CI apply to the previous head, and the new head will run its own checks.

中文说明

已在 1eb8b06933 修复阻塞的请求形状,以及相关 /effort 状态报告。百炼 Standard DeepSeek V4 Pro 去掉冗余 enableThinking:true,high/max 仅发送顶层 reasoning_effort;关闭和非思考副请求仍发送 enable_thinking=false。无参数 /effort 对不在当前声明档位中的旧值报告使用模型/provider 默认值,合法 low 保持原报告;查询不改持久化设置或会话偏好。

修复前新增请求断言和状态断言各失败两项;修复后 Core 212、CLI 16 项通过,完整 build/typecheck/bundle/lint 通过,13 个真实 CLI 到 localhost 的配置驱动用例全部通过。自动评审的独立建议已逐条回复并记录到关联 issue,不声称已修复;拒绝任取其他 endpoint 的同名模型能力。请复核最新 head,之前批准和绿色 CI 属于前一个提交,新 head 会运行自己的检查。

@callmeYe
callmeYe requested a review from qqqys September 9, 2026 03:58
@callmeYe

callmeYe commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

The first CI run for the maintainer fix failed before ESLint: the updated main workflow invoked .github/scripts/check-lint-gate-freshness.mjs, which the older PR branch did not yet contain (MODULE_NOT_FOUND). Merged main normally in a3d71649cd; no gate was bypassed or weakened. The required freshness script and its nine passing tests are now included through main ancestry.

The refreshed repository instructions also require complete bilingual design documents, so 10d396510a adds the linked Chinese counterpart and keeps the original path for English. Both versions include the final single-field DeepSeek request and stale-tier reporting decisions.

After merging main: full build, typecheck, bundle, lint and repository-wide Prettier check pass; focused core tests pass 1,010 cases and CLI/ACP tests pass 82. All thirteen CLI mock cases passed again after the merge, including both high/max requests for the corrected Standard DeepSeek Pro entry. The current scope retains the two maintainer fixes; independent Suggestions remain explicitly tracked in #11328. The previous static failure is a missing base-branch gate file, not a product lint error. New CI will validate the merged head.

中文说明

本轮首次 CI 在执行 ESLint 前失败:main 的新 workflow 调用了旧 PR 分支尚未包含的 check-lint-gate-freshness.mjs,报 MODULE_NOT_FOUND。已通过正常 merge 提交 a3d7164 同步 main,新脚本及其九项通过测试均由 main 祖先引入,没有绕过或放宽门禁。

最新仓库规范要求完整双语设计,10d396510a 补上中文对应版本,原路径保留为英文;两份都说明最终 DeepSeek 单字段请求与旧档位报告行为。合并后完整 build/typecheck/bundle/lint 及全仓 Prettier 检查通过,Core 1,010 项、CLI/ACP 82 项通过;合并后13项CLI mock再次全部通过,包括修复后的百炼DeepSeek Pro high/max。本轮保持两项maintainer修复,独立建议继续由 #11328 跟踪,新CI将验证合并后的head。

@yiliang114 yiliang114 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.

Confirming qqqys's blocking finding is now fixed at this head: alibaba-standard.ts deepseek-v4-pro no longer carries enableThinking: true alongside the new capabilities.reasoning ladder — the entry is now capability-only, matching its -0813/-flash siblings, so high/max send reasoning_effort alone instead of two competing knobs.

One item from qqqys's note is still open and worth ruling on before merge: token-plan/qwen3.8-max and -preview drop high/max on the wire (the tier-narrowing the design doc records), yet a bare /effort still reports the persisted high as the current tier. If the narrowing is intended, the user-visible report should stop claiming a tier the request no longer carries — otherwise it's a silent contradiction. If it's not intended, the narrowing itself needs revisiting.

The remaining bot findings are Suggestion-level (14/38 capabilities.reasoning blocks unreached by the preset-reading test table, thinkingMandatory on non-Qwen DashScope routes, and the pipeline.ts:1002 call-site that still keys on the qwen3.8-max prefix). Non-blocking, but the test-table gap is the one I'd close in the same pass since it's what let the double-knob cell slip through the first time.

@callmeYe

callmeYe commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

The remaining /effort question is already fixed at the reviewed head 10d396510a (introduced in 1eb8b06933). In packages/cli/src/ui/commands/effort-command.ts:81, the current-tier message is selected only when current && availableTiers.includes(current); unsupported persisted high/max now use the existing model/provider-default message. This is the non-interactive/ACP reporting path; interactive no-argument use opens the picker, whose choices were already capability-filtered.

The regression at packages/cli/src/ui/commands/effort-command.test.ts:106 uses the actual Token Plan Qwen 3.8 Max capability and tests saved high, max and valid low. High/max select the default message, low retains the current-tier message, and none of these reads mutates settings or the runtime value. Both high/max assertions failed before the fix and passed after it. The merged-head CLI/ACP run passed 82 tests, including these cases. The narrowing itself remains intentional and documented; the report now agrees with the omitted wire tier.

The additional preset coverage suggestion is recorded in #11328; the exact stable-Pro high/max cells that exposed the blocking issue were added to the real-provider pipeline matrix in this PR. Please re-check the current reporting condition before treating the old report as an open blocker.

中文说明

/effort 问题已在你评审的 10d396510a 修复(由 1eb8b06933 引入)。effort-command.ts:81 只有在 current 属于 availableTiers 时才显示当前档位;旧 high/max 不被当前模型支持时显示使用模型/provider 默认值。该分支是非交互/ACP报告,交互调用会打开已按能力过滤的选择器。

effort-command.test.ts:106 使用真实 Token Plan Qwen 3.8 Max capability,验证旧 high/max 显示默认值、合法 low 保留当前档位,以及查询不写设置或运行时状态。high/max 两项在修复前红、修复后绿,合并后的 CLI/ACP 82 项测试包含这些用例。档位收窄仍是有意的,报告已经与不发送无效档位的行为一致。

额外预设覆盖已记录到 #11328;暴露阻塞的稳定版Pro high/max两格已在本PR补进真实provider测试矩阵。请按当前报告条件复核,不要把已修复的旧报告继续视为阻塞。

@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.

Partially reviewed — gaps disclosed. Suggestions are inline.

12 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:

  • R1-1 refreshAuth mandatory-exemption scope — already reported (comment 3962682126), author answered and deferred to issue 11328
  • R1-2 capability lookup keyed on the live baseUrl — already reported (comment 3962682134), author declined as intentional route ownership
  • R1-3 repeated-auth test does not reproduce the wipe — already reported (comment 3962682137), author answered and deferred to issue 11328
  • R1-4 isTieredEffortWireModel branch unpinned — already reported (comment 3962682145), author answered and deferred to issue 11328
  • R1-5 dashscope ladder comment wording — already reported (comment 3962682152), author answered and deferred to issue 11328
  • R1-6 toggleOnly non-qwen nested reasoning — already reported (comment 3962682158), author answered and deferred to issue 11328
  • R1-7 preset capability rows unreached by tests — already reported (comment 3962682160), author answered and deferred to issue 11328
  • R1-8 Alibaba thinkingMandatory flags unasserted — already reported (comment 3962682164), author answered and deferred to issue 11328
  • R1-9 thinkingMandatory on a non-Qwen model arms the tool_choice drop — already reported (comment 3962682171), author declined pending a hosted-Kimi witness
  • R1-10 hosted-DeepSeek disableField versus the pipeline comment — already reported (comment 3962682178), author answered; this round's fetched Alibaba reference settled it in the presets' favour, leaving the stale comment
  • R1-11 native DeepSeek low tier versus the legacy nested adapter — already reported (comment 3962682188), author answered and deferred to issue 11328
  • R1-12 catalog consumers read only the legacy enableThinking — already reported (comment 3962682194), author answered and deferred to issue 11328

Not reviewed: reverse audit — stopped at the plan's 5-round cap without two consecutive dry rounds; rounds 3, 4 and 5 each reported findings, so the loop ended on the cap rather than on convergence.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally; integration-tests/* and root scripts/tests/* are outside build-test's scoped suite set.

Not reviewed: build-and-test — the test-efficacy probe could not be validated (harnessValidated: null: the control produced no verdict because the vitest prerequisite guard failed in the probe's restored tree), so no revert, mutant or hunk-survival claim was measured in either direction.

Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:

  • packages/cli/src/acp-integration/model-configuration.test.ts:84 — [review] installed-preset matrix can be satisfied by the hardcoded MODEL_CONFIGURATIONS manifest instead of the preset output

Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)

中文说明

仅完成部分审查,审查缺口已披露。 建议见行内评论。

本轮确认的 12 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。

未审查(原文为英文):reverse audit — stopped at the plan's 5-round cap without two consecutive dry rounds; rounds 3, 4 and 5 each reported findings, so the loop ended on the cap rather than on convergence.

未审查(原文为英文):build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally; integration-tests/* and root scripts/tests/* are outside build-test's scoped suite set.

未审查(原文为英文):build-and-test — the test-efficacy probe could not be validated (harnessValidated: null: the control produced no verdict because the vitest prerequisite guard failed in the probe's restored tree), so no revert, mutant or hunk-survival claim was measured in either direction.

收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。

机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)

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

Comment thread packages/cli/src/ui/commands/effort-command.ts
Comment thread packages/core/src/core/openaiContentGenerator/pipeline.test.ts
…n-deepseek-reasoning

# Conflicts:
#	packages/cli/src/acp-integration/model-configuration.test.ts
@callmeYe

callmeYe commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

Merged current main (5f65099c87) in 36d8bd8b0f and resolved the single conflict in the ACP model-configuration test file. Preserved both the PR's installed Kimi/Qwen/DeepSeek matrix and main's new GPT-5/GPT-6/default-reasoning cases, including their imports. The automatically merged provider code retains main's GPT effort handling and web-search defaults alongside this PR's reasoning metadata.

Validation: core provider/pipeline/preset tests 420 passed; config tests 645 passed; CLI/ACP tests 125 passed. Full build, typecheck, bundle and lint passed. Formatting and staged-diff checks passed. No forced push. Local HEAD, remote branch and PR head all match 36d8bd8b0f1687459fc973565a16fe6266b1f77e; GitHub reports MERGEABLE. The new CI run and review requirements still apply.

Local sharp is 0.35.3 whereas merged main pins 0.35.4; the focused reasoning tests do not claim sharp-specific acceptance. CI installs the merged lockfile.

中文:已正常合并当前 main(5f65099c87),提交36d8bd8b0f。唯一冲突在ACP模型配置测试,保留双方的Kimi/Qwen/DeepSeek矩阵与GPT-5/GPT-6/default-reasoning测试及import。Core请求/provider/预设420、Config645、CLI/ACP125项通过,完整build/typecheck/bundle/lint通过。未强推,三处SHA一致,GitHub已恢复MERGEABLE;新CI和审核要求仍待完成。本地sharp为0.35.3而main锁定0.35.4,未声称做过sharp专项验收;CI将按合并后的锁文件安装。

@ytahdn ytahdn 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.

增量复审 / Incremental re-review

基线 → head:76ba879936d8bd8b。本 PR 自身的增量为三个提交:78607f70(澄清 Qwen 兼容别名的注释/设计文档)、1eb8b069(移除 DeepSeek 冗余 thinking 默认值 + effort 命令显示守卫 + 配套测试)、10d39651(文档双语对齐);其余两个 Merge origin/main 不计。每条引用都在 head 36d8bd8b 的树上重新核对过。

Baseline → head: 76ba879936d8bd8b. The PR's own increment is three commits: 78607f70 (comment/design-doc clarification of Qwen compatibility aliases), 1eb8b069 (remove the redundant DeepSeek thinking default + an effort-command display guard + tests), 10d39651 (bilingual doc alignment); the two Merge origin/main commits are excluded. Every citation is re-verified against the head 36d8bd8b tree.

本 PR 主要做了什么 / What this PR does

把 PR 10999 落地的声明式 capabilities.reasoning 框架(当时只配了原生 DeepSeek V4 Pro)补齐到 Kimi / Qwen / DeepSeek 各路由:五个预设文件按各 provider 路由的真实 wire 契约声明每个模型的档位 / 开关 / 默认值 / 关闭字段,把 DashScope provider 里硬编码的 qwen3.8-max 前缀判断换成"优先读已声明 capability、未配置才回退名称"的查找,并顺带修 refreshAuth 让全局关闭思考的偏好在连续认证时不被抹掉。

Fills out the declarative capabilities.reasoning framework PR 10999 landed (then only native DeepSeek V4 Pro) across the Kimi / Qwen / DeepSeek routes: five preset files declare per-model tiers / toggle / default / disable-field to match each provider route's real wire contract, replacing the hardcoded qwen3.8-max prefix gate in the DashScope provider with a capability-first lookup, plus a refreshAuth fix so a saved global thinking-off preference survives repeated auth.

结论 / Verdict:APPROVE(无遗留 Critical / Important)。上一轮我提的 Important 已解决;qqqys 本轮提的 Critical 已修;yiliang114 提的显示项也已修。CI 在 head 上全绿(仅 bot 的 review-pr 还 pending)。剩下的都是非阻塞项与"第三方端点契约静态审查无法证实"的固有说明。

APPROVE (no standing Critical / Important). My prior-round Important is resolved; qqqys's Critical this round is fixed; yiliang114's display item is fixed too. CI is fully green at head (only the bot's review-pr is pending). What remains is non-blocking, plus the inherent caveat that static review cannot prove third-party endpoint contracts.


✅ 上一轮我的 findings / My prior findings

  • Important(两张档位表在 high 上矛盾 + 超档位 effort 静默丢弃)已解决。 78607f70dashscope.ts:35-38DASHSCOPE_TIERED_EFFORTS 的注释从"超过 xhigh 的 max 会被 400 拒绝"改写为"这是未声明 capability 路由的旧输入档位表;DashScope 把 high/max 当作 xhigh 别名接受;已配置预设只暴露原生 low/medium/xhigh"。设计文档新增《原生档位与兼容输入》一节,明确"不在声明中的持久化值被省略、采用 provider 默认值(Qwen3.8 为 xhigh)……这是有意的配置优先规则,不代表 provider 会拒绝 high 或 max"。据此我上一轮担心的两点都消解了:(a) 两表并非互相矛盾——一张是输入侧兼容表、一张是原生 wire 档位,用户自建 qwen3.8-max(无 capability)走 clamp 发出 high 不会 400,因为 high 本就是 xhigh 别名;(b) 预设模型上"静默丢弃 high/max"是无损的——被丢的值恰好别名到 xhigh,而 xhigh 正是声明的 defaultEffort,落到 provider 默认即等价结果。数组内容与 buildQwenEffortConfig 行为未变,仅注释/文档澄清(这正是我上一轮给的关闭方式)。

    Resolved. 78607f70 rewrites the DASHSCOPE_TIERED_EFFORTS comment (dashscope.ts:35-38) from "a max above xhigh is rejected with a 400" to "legacy input ladder for routes without an explicit capability; DashScope accepts high/max as xhigh aliases; configured presets expose only native low/medium/xhigh", and the design doc adds a section stating undeclared persisted values are omitted → provider default (xhigh for Qwen3.8), "an intentional config-first rule, not a claim the provider rejects high/max". Both halves of my concern dissolve: (a) the two ladders aren't contradictory — one is the input-compat ladder, one the native wire ladder, so a user's own unconfigured qwen3.8-max shipping high via clamp is not a 400 (high is an xhigh alias); (b) the silent-drop of high/max on preset models is lossless — the dropped values alias to xhigh, which is exactly the declared defaultEffort. Array contents and buildQwenEffortConfig behavior are unchanged; only comments/docs (the remedy I offered last round).

  • Nit(pipeline.ts 第三个调用点仍是单参名称回退)未变、非阻塞。 head 上该调用点在 pipeline.ts:1012 isTieredEffortWireModel(model)(单参),对本 PR 所有预设仍被 capability 分支遮蔽,只有用户自建 qwen 家族 + disableField:'thinking' 的形状可达。留作后续或加注释即可。/ Unchanged, non-blocking (now pipeline.ts:1012).

  • Nit(tiered 模型每次 buildRequest 解析两次 model config)未变、无正确性影响。

✅ 本轮他人 findings(我已独立复核)/ Other reviewers' findings (independently re-verified)

  • qqqys 的 Critical(阿里云路由同一请求发两个互斥 thinking 旋钮)已修 —— 且坦白说这条在我上一轮基线 76ba879 就存在、我漏了,是 qqqys 抓到的。 1eb8b069alibaba-standard.tsdeepseek-v4-pro 改成只带 capability:head alibaba-standard.ts:84-95 已无同级 enableThinking: true,与其 -0813/-flash 兄弟一致。测试也补了钉子:pipeline.test.ts:2080-2081 新增 ['alibabaStandard','deepseek-v4-pro','high'/'max','enable_thinking',false],断言逻辑(:2188-2189 专门 case + :2208 尾判)确保 enabled 模式下 enable_thinking 为 undefined、只发 reasoning_effort。已核对修复到位。

    Fixed — and honestly, this defect was present at my baseline 76ba879 and I missed it; qqqys caught it. 1eb8b069 makes alibaba-standard.ts deepseek-v4-pro capability-only (head :84-95 no longer carries a sibling enableThinking: true), matching its -0813/-flash siblings, and pipeline.test.ts:2080-2081 now pins that enabled-mode high/max ship reasoning_effort alone with enable_thinking undefined.

  • yiliang114 的未决显示项(token-plan qwen3.8-max/-preview 把持久化的 high 当当前档位上报,但 wire 上已不发)已解决。 1eb8b069effort-command.ts:81 加了守卫 current && availableTiers.includes(current):只有当持久化值在该模型的可用档位内才报"Current reasoning effort: X",否则报"not set (using the model/provider default)"。我顺着核实了 availableTiers 的来源 getReasoningEffortsForConfigmodel-configuration.ts:322-328):命中已声明 capability 时返回 reasoning.efforts——token-plan qwen3.8-max 声明的是 ['low','medium','xhigh']alibaba-token-plan.ts:65),不含 high,故守卫对持久化 high/max 会正确回落到"provider 默认"。effort-command.test.ts 新增的 it.each(['high','max','low']) 用例正钉住这一行为(CI 绿)。yiliang114 的 review 时间戳晚于 1eb8b069,我判断该守卫已覆盖其关切。

    Resolved. 1eb8b069 adds the effort-command.ts:81 guard current && availableTiers.includes(current), and I traced availableTiers to getReasoningEffortsForConfig (model-configuration.ts:322-328), which returns the declared reasoning.efforts['low','medium','xhigh'] for token-plan qwen3.8-max (alibaba-token-plan.ts:65), excluding high, so a persisted high/max correctly falls back to "provider default". The new effort-command.test.ts it.each(['high','max','low']) case pins it (CI green).

🔎 我核对后作为问题上报的一处 / One thing I checked and am NOT flagging

token-plan 的 qwen3.8-maxalibaba-token-plan.ts:60-75)与 -preview:76-91)同时带 enableThinking: true + thinkingMandatory: truecapabilities.reasoningdisableField: 'reasoning_effort'),而 provider-config.ts:69 会为任何 enableThinking 规格无条件注入 extra_body.enable_thinking: true。乍看与 qqqys 的"双旋钮"同形,但我核实后判断这是有意且被测的配置,不是缺陷:这两个模型是 thinkingMandatorypipeline.ts:974-979 的注释点名 token-plan 网关上的 qwen3.8-max-preview 会以 400 拒绝 enable_thinking: false),其关闭字段是 reasoning_effort 而非 enable_thinking,所以 enable_thinking: true 是"始终开思考"的信号、reasoning_effort 设档位,二者不互斥。pipeline.test.ts:2106-2107mandatory=true 覆盖了这两条路由,断言(:2201-2204)明确允许 enable_thinking 非 false,CI 绿。对照 alibaba-standard 的 qwen3.8-max(capability-only、enable_thinking 断言为 undefined)是不同网关契约,非不一致缺陷。

token-plan qwen3.8-max/-preview carry enableThinking: true + thinkingMandatory: true alongside a reasoning_effort-keyed capability, and provider-config.ts:69 unconditionally injects extra_body.enable_thinking: true — superficially the same shape as qqqys's double-knob. But these are thinkingMandatory models (the pipeline.ts:974-979 comment names token-plan qwen3.8-max-preview as rejecting enable_thinking: false with a 400) whose disable field is reasoning_effort, not enable_thinking, so enable_thinking: true is the always-on signal and reasoning_effort the tier — not competing. pipeline.test.ts:2106-2107 covers both with mandatory=true and the assertion (:2201-2204) explicitly permits enable_thinking non-false; CI green. Not a defect.

🟢 非阻塞 / Non-blocking

  • yiliang114 提到的测试表覆盖缺口(部分 capabilities.reasoning 块未被 preset-reading 测试矩阵触达)值得在同一批里补齐——正是这个缺口让 qqqys 的双旋钮单元第一次溜过。qqqys 命中的那格现已由 1eb8b069 补测,剩余覆盖是普遍性改进项。/ The test-table coverage gap yiliang114 noted is worth closing in the same pass — it's what let the double-knob cell slip through initially; the specific cell is now covered, the rest is a general improvement.
  • 本 PR 的实质仍是一组"第三方端点接受什么"的断言,静态审查无法对线上端点证实。设计文档为矩阵每行引用了官方文档并标注核对日期,78607f70 又补齐了 high/max 别名的依据,23+ 路由矩阵证明代码自洽(我们什么)——这是这类改动能拿到的最好证据,但"发得对"不等于"对端一定接受"。此为固有说明,非阻塞。/ The substance remains a set of third-party-endpoint contract claims static review can't confirm against live APIs; the design doc cites official docs per matrix row with verification dates, 78607f70 adds the high/max alias basis, and the route matrix proves self-consistency (what we send). Inherent caveat, non-blocking.

Reviewer note: 纯静态审查 —— 未跑测试/构建。CI 读自 head 36d8bd8b 的 Actions(除 bot review-pr pending 外全绿)。所有行号在 head 36d8bd8b 树上核对:alibaba-standard.ts / alibaba-token-plan.ts / dashscope.ts / pipeline.ts / pipeline.test.ts / effort-command.ts / model-configuration.ts / provider-config.ts

Static review only — no tests/build run. CI read from Actions on head 36d8bd8b (all green except the bot's review-pr, pending). All line numbers verified against the head 36d8bd8b tree.

@qqqys

qqqys commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

tmux-based E2E verification at head 36d8bd8b0f16

Read-only pass: no file in this PR was modified, and the probe tree was a separate worktree (the live checkout's bundle was asserted unchanged before and after, by mtime).

Why this pass exists. The two verifications already on this PR leave one commit unexercised at runtime. wenshao's 25-route real-daemon / real-TUI wire harness ran at 78607f7060, which predates 1eb8b069 — the commit that removes the double knob and adds the /effort display guard. ytahdn's incremental re-review at this head is explicitly static-only ("纯静态审查 —— 未跑测试/构建"). So this pass targets 1eb8b069 specifically.

1. The Critical filed on this PR under my token is fixed at head, and the fix is pinned

That Critical named one blocking defect: alibaba-standard's deepseek-v4-pro declared an effort ladder and a sibling enableThinking: true, so high/max shipped both reasoning_effort and enable_thinking=true — two competing knobs on one request, on the exact route this PR exists to unify.

  • Head source. alibaba-standard.ts:85-95 now carries capabilities.reasoning { thinking, efforts: ['high','max'], defaultEffort: 'high', disableField: 'enable_thinking' } and contextWindowSize, with no enableThinking.
  • Producer check (the field's consumer, outside the diff). providers/provider-config.ts:69 is if (spec.enableThinking) — a truthiness test, so the removed field means no extra_body.enable_thinking injection. Absent reads as false here, not as default-true, which is what makes the one-line removal effective rather than cosmetic.
  • Built-artifact witness. Compiling this head's source yields packages/core/dist/src/providers/presets/alibaba-standard.js whose deepseek-v4-pro entry has efforts: ['high','max'] and no enableThinking — the artifact that actually executes carries the fix. (For contrast, the guard in effort-command.ts appears 1× in this head's compiled dist and 0× in the pre-PR build.)
  • Mutation witness. Re-adding exactly the one removed line to this head's source turns 3 tests red: ContentGenerationPipeline > sends installed alibabaStandard / deepseek-v4-pro reasoning through the real provider hook twice — the high and max cells the Critical named — plus alibabaStandardProvider > does not mark DeepSeek models as multimodal; 243 passed, 3 failed. The source was restored sha256-identical afterwards. So a regression of this specific defect is caught by this PR's own suite, not merely absent today.
  • Suites executed at head: pipeline.test.ts 236/236, providers/__tests__/presets/alibaba-standard.test.ts 10/10, ui/commands/effort-command.test.ts 16/16.

2. Real TUI run in tmux at head

Isolated pre-created HOME, workspace-scoped .qwen/settings.json, pre-trusted folder via QWEN_CODE_TRUSTED_FOLDERS_PATH, node <abs>/packages/cli/dist/index.js built from this head, stdout never piped (piping suppresses Ink's live region). The composer painted, so the absence measurements below are trustworthy.

Banner: API Key | deepseek-v4-pro (/model to change), footer ➜ ws · deepseek-v4-pro low. /effort opened the dialog:

│ > Reasoning Effort (applied across all providers; clamped per model)          │
│ › 1. low — Fastest and cheapest; least reasoning.                            │
│   2. medium — Balanced speed, cost, and reasoning.                           │
│   3. high — Default — strong reasoning for hard tasks.                       │
│   4. xhigh — Extended reasoning for agentic/coding work.                     │
│   5. max — Maximum reasoning; highest cost and latency.                      │
│ (Use Enter to select, Esc to cancel)                                         │

auto_mode_active=0.

3. What this run did NOT establish — stated because it bounds the evidence

The dialog offered the generic five tiers, not the declared two. That is a harness precondition failure, not a PR defect: with hand-written settings the model is not a registry model on an installed preset route, so getConfiguredModelReasoning (acp-integration/model-configuration.ts:295-320) finds no configured capability and getReasoningEffortsForConfig falls back to REASONING_EFFORT_TIERS. wenshao's harness avoided this by generating settings through the repo's own installer (buildInstallPlan + applyProviderInstallPlan); mine did not, and I did not have budget to redo it that way.

Consequence, on the record rather than buried: because the fallback ladder contains low, the persisted low was in-ladder, so the new guard's out-of-ladder branch was never entered. My negative control — reverting current && availableTiers.includes(current) to current in the executed artifact — therefore produced output byte-identical to the unmutated arm (Current reasoning effort: low / Available: low, medium, high, xhigh, max both ways). That control is vacuous and proves nothing; the artifact was restored byte-identical. Please do not read section 2 as evidence about the guard.

The out-of-ladder branch is covered instead by this PR's own test, which I ran at head: effort-command.test.ts drives token-plan qwen3.8-max (declared ladder ['low','medium','xhigh']) with saved high and max — both out-of-ladder — asserting the "using the model/provider default" message, while saved low (in-ladder) asserts "Current reasoning effort:". 16/16 green.

One hypothesis of mine that this pass refuted. I came in expecting the interactive dialog to still report a false current tier, since the guard was added only to the non-interactive message. It does not: DialogManager.tsx:361-362 passes efforts={getReasoningEffortsForConfig(config)}, and EffortDialog.tsx:50 uses efforts.indexOf(currentEffort), rendering "X is not available for this model — using the model/provider default." on a miss. So 1eb8b069 brings the non-interactive/ACP message into line with a dialog that was already correct; the two surfaces now agree, and neither reports an out-of-ladder persisted value as current.

Gate read at the instant immediately before posting

Time-scoped, and this comment is not itself the approval: state=open, merged=false, head 36d8bd8b0f16; ytahdn APPROVED at head (10:07:50Z, full incremental re-review); no at-head CHANGES_REQUESTED from anyone; ci-bot's last completed pass on this PR is Suggestions-only (12 findings, 0 Criticals). Lane census complete 108/108 (paged, received == total_count asserted), with Test (ubuntu-latest), Lint & Static, Integration Tests (no-AK, No Sandbox), Desktop Shell ×2, TUI parity snapshots, OpenTUI no-flicker gate, Real daemon E2E / Java 11, web-shell E2E Smoke and the Java matrix all success. The only non-green lanes are route (cancelled) and review-pr (in_progress) — both ci-bot review-infrastructure lanes that emit review rows rather than build/test/lint verdicts. I'm naming that as an explicit ownership argument rather than demoting them silently, since a demoted red lane is how a real failure gets waved through.

Conclusion: mergeable from my side. The blocking defect I filed is fixed and pinned by a mutation-verified test, and I found no Critical on the increment (1eb8b069, 10d39651, plus two main-merges; no author commits after 10d39651). I'm submitting the approval alongside this comment rather than deferring it to a later round, because this PR has sat at an approved-and-green head for ~55 minutes and deferring has lost approvals to merges before.

Residual, non-blocking, and not verified by me at runtime: the wire body for alibabaStandard / deepseek-v4-pro is established here by unit pins plus a mutation witness, not by a captured request at this head — wenshao's captured-body matrix covers that route at 78607f7060. And ytahdn's inherent caveat stands unreduced by this pass: static review plus local pins cannot prove what a live third-party endpoint accepts.


中文小结:本次在 head 36d8bd8b0f16 上做只读验证,目标是 1eb8b069 —— 因为 wenshao 的 25 路由真实 daemon/真实 TUI 线上验证跑在 78607f7060(早于该提交),而 ytahdn 在本 head 的增量复审明确声明为纯静态。(1)我以自己账号提出的 Critical 已在 head 修复且被测试钉住alibaba-standard.ts:85-95deepseek-v4-pro 条目已无同级 enableThinking;其唯一消费点 provider-config.ts:69if (spec.enableThinking) 真值判断,故字段缺失即不再注入 extra_body.enable_thinking(缺失等于 false,而非默认 true,这正是"删一行"有效的原因);编译产物 dist/.../alibaba-standard.js 中该条目同样只有 efforts: ['high','max'] 而无 enableThinking变异见证:把被删的那一行加回 head 源码,恰好 3 个测试转红 —— sends installed alibabaStandard / deepseek-v4-pro reasoning through the real provider hook 两次(即 Critical 点名的 highmax 两格)与 does not mark DeepSeek models as multimodal,243 通过 3 失败;随后源码按 sha256 逐字节还原。head 上实跑:pipeline.test.ts 236/236、alibaba-standard.test.ts 10/10、effort-command.test.ts 16/16。(2)tmux 真实 TUI:隔离 HOME + 工作区 settings + 预信任目录,运行本 head 编译出的 packages/cli/dist/index.js(stdout 不经管道),横幅 API Key | deepseek-v4-pro、底栏 deepseek-v4-pro low/effort 正常弹出档位对话框,auto_mode_active=0(3)必须说明的边界:对话框给出的是通用五档而非声明的两档 —— 这是我的 harness 前置条件未满足,不是 PR 缺陷:手写 settings 下该模型不是已安装预设路由上的注册模型,getConfiguredModelReasoning 取不到已配置 capability,getReasoningEffortsForConfig 回落至 REASONING_EFFORT_TIERS;wenshao 用仓库自带安装器生成 settings 才避开了这一点。因此回落档位包含 low,持久化的 low 属于合法档位,新增守卫的"越档"分支根本没有进入;我据此做的负对照(把已执行产物中的 current && availableTiers.includes(current) 改回 current)输出与未变异臂逐字节相同该对照无效、不证明任何事,产物已还原。请勿把第 2 节读作关于该守卫的证据。越档分支由本 PR 自带测试覆盖并已实跑:effort-command.test.tstoken-plan qwen3.8-max(声明档位 ['low','medium','xhigh'])配合已保存的 highmax(均越档)断言"using the model/provider default",而 low(在档内)断言"Current reasoning effort:",16/16 通过。另外,本次推翻了我自己的一个假设:我原以为交互对话框仍会把越档的持久值报为当前档(守卫只加在非交互分支),实际不会 —— DialogManager.tsx:361-362 传入了 efforts={getReasoningEffortsForConfig(config)}EffortDialog.tsx:50efforts.indexOf(currentEffort),未命中即渲染"该档位对本模型不可用 —— 使用模型/provider 默认值"。所以 1eb8b069 是把非交互/ACP 文案对齐到本已正确的对话框,两个界面现在一致。门禁(发布前一刻读数,本条留言本身不是批准)state=openmerged=false、head 一致;ytahdn 在 head APPROVED(10:07:50Z);无任何 head 上的 CHANGES_REQUESTED;ci-bot 最近一次完成的评审只有 Suggestion(12 项,0 Critical);检查项完整枚举 108/108(分页并断言 received == total_count),产品通道全绿,唯一非绿的 route(cancelled)与 review-pr(in_progress)均属 ci-bot 评审基础设施通道、只产出评审记录而非构建/测试/静态检查结论 —— 我显式给出这条归属论证,而不是默默把红灯降级。结论:从我这边看可以合入,并在同一轮提交批准而不推迟到下一轮(本 PR 已在"已批准且全绿"的 head 上停留约 55 分钟,推迟此前已多次导致批准被合并吞掉)。未在本次运行时验证的非阻塞残项alibabaStandard / deepseek-v4-pro请求体在此由单元测试钉子加变异见证确立,而非在本 head 抓到真实请求(wenshao 的抓包矩阵覆盖该路由于 78607f7060);ytahdn 记录的固有说明也未被本次削弱 —— 静态审查加本地钉子无法证明线上第三方端点实际接受什么。

@qqqys qqqys 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.

Approving at head 36d8bd8b0f16, read immediately before submitting.

The Critical I filed on this PR — alibaba-standard's deepseek-v4-pro shipping both reasoning_effort and enable_thinking=true on one request, on the exact route this PR exists to unify — is fixed at this head and pinned. Re-adding the single removed line turns 3 tests red, including sends installed alibabaStandard / deepseek-v4-pro reasoning through the real provider hook twice: the high and max cells the finding named. Suites executed at head: pipeline.test.ts 236/236, presets/alibaba-standard.test.ts 10/10, ui/commands/effort-command.test.ts 16/16. No Critical found on the increment.

Full evidence and method are in the verification comment above (#issuecomment-5600643075), including the one branch my tmux harness could not reach and a negative control that came out vacuous — I have not relied on either, and the guard's out-of-ladder branch is covered instead by this PR's own test, which I ran.

Two things this approval does not claim: it does not reduce ytahdn's inherent caveat that static review plus local pins cannot prove what a live third-party endpoint accepts, and it rests on the gate read taken at the instant before posting rather than on lanes or verdicts that may change afterwards.


在 head 36d8bd8b0f16 上批准(提交前一刻重新读取)。我此前提出的 Critical(alibaba-standarddeepseek-v4-pro 在同一请求里同时上送 reasoning_effortenable_thinking=true)已在该 head 修复且被测试钉住:把被删的那一行加回,恰好 3 个测试转红,其中包含 Critical 点名的 highmax 两格。head 上实跑 236/236、10/10、16/16,增量部分未发现 Critical。完整证据与方法见上方验证留言(#issuecomment-5600643075),其中也写明了我的 tmux harness 未能触达的分支以及一个无效的负对照 —— 二者我都未采信,越档分支改由本 PR 自带测试覆盖并已实跑。本批准不主张两件事:既不削弱 ytahdn 记录的固有说明(静态审查加本地钉子无法证明线上第三方端点实际接受什么),也只以发布前一刻的门禁读数为准,而非其后可能变化的通道状态或评审结论。

@wenshao
wenshao added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 40c9506 Sep 9, 2026
125 of 127 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.23.3.

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.

6 participants