fix(core): harden Qwen 3.8 reasoning effort wire shape - #8488
Conversation
…ollow-up) Follow-up to QwenLM#8472, addressing the post-merge review findings: - Drop enable_thinking/thinking_budget after the extra_body merge whenever reasoning_effort ships: the Token Plan preset made qwen3.8-max-preview carry both thinking knobs, and DashScope rejects reasoning_effort combined with thinking_budget - Family-gate the new tool_choice=required strip clause to qwen wire models: reasoning_effort is an opaque sampling override on non-qwen DashScope models, and dropping forced tool selection degraded their structured side queries - Prefix-match the qwen3.8-max family so dated snapshots and -latest aliases receive the selected tier instead of silently collapsing to enable_thinking - Log the tool_choice strip; restore the effort-config JSDoc and comment the request-level override copy
|
@qwen-code /takeover |
|
Gate re-run — the head moved since my last pass (autofix round 1 has landed), so all three stages run again against the new code. Template looks good ✓ Problem: observed, not theoretical. I verified this against #8472's merged commit ( Direction: aligned — wire-shape correctness for the effort tiers #8472 just landed. Finishing that feature, not adding scope. Size: core paths touched ( Approach: minimal — every edit closes one named finding, no drive-by changes. Autofix round 1 landed exactly the shape my Stage 3 hold asked for (shared Risk: Moving on to code review. 🔍 中文说明Gate 重跑——距上一轮 head 已变化(autofix 第 1 轮已合入),三个阶段对新代码全部重跑。 模板完整 ✓ 问题:已观测而非理论。上一轮已对照 #8472 的合入提交( 方向:对齐——为 #8472 刚落地的档位修正出网形态,是收尾而非扩 scope。 规模:触及核心路径( 方案:最小化——每处改动对应一个已点名发现,无夹带改动。autofix 第 1 轮落地的正是我 Stage 3 暂缓所要求的形态(共享 风险: 进入代码审查。🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewRe-run context: head moved Prior blockers — all resolved as asked:
The author's second self-review (post-autofix) — my independent read of each point:
Net: no correctness bug, security hole, or regression at this head. The four fixes do what the PR says, and the gating is exactly what the hold asked for. The residual points above are follow-ups, and I'd ask the author to file the one for point 3 (and ideally 1/2/4 together) so they don't evaporate. Test evidenceCI on
The three skips are by design, not a gap: What I cannot evidence from here: I don't run PR code myself. The approving maintainer's comment on this head reports 230/230 tests on the changed files and 721/721 across the whole Sandboxed verification would settle the one open behavioral claim: Real-scenario testing: N/A — unattended CI run, and the change is a wire-shape fix with no TUI surface to drive. 中文说明代码审查重跑背景:head 从 此前的阻断项——全部按要求解决:
作者第二轮自审(autofix 后)——我对每条的独立判断:
结论:本 head 无正确性 bug、安全漏洞或回归。四项修复如 PR 所述生效,门控正是暂缓清单所要求的形态。上述残留各点是跟进项——请作者为第 3 点(最好连同 1/2/4)建 issue,以免遗失。 测试证据
我无法在此提供的证据:我本人不运行 PR 代码。本 head 上的 maintainer 批准评论报告改动文件 230/230、整个 沙箱验证可以落定唯一开放的行为主张: 真实场景测试:N/A——无人值守 CI 运行,且本改动是出网形态修复,没有可驱动的 TUI 面。 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — the hold's three asks are implemented exactly as asked and independently re-verified at this head; what remains from the author's second review is real but second-order — contradictory-input edges and pre-#8472 behavior — follow-up material, not merge blockers. Stepping back: my previous pass held this PR because the knob drop was ungated, and green CI couldn't settle that. The honest question this pass is whether I'm now approving because the fix is genuinely right, or because the thread wore the objection down. I checked by re-reading the code rather than the autofix summary: the drop is family-gated through a shared predicate, split-scoped (only The author's second self-review raised seven points, and a gate should take those seriously — so I triaged each one independently (detail in Stage 2). The pattern: none is a regression introduced by this diff. Points 1–2 are edges where the user supplies contradictory knobs and the PR's declared tie-break decides; point 3 and 4 are behavior that predates #8472 or the PR itself (I verified #8472's diff added only the What I'm still not claiming: live DashScope acceptance of the single-knob shape is unverified — the PR says so itself, and the earlier ✅ My Follow-ups to file (author): ungated 中文说明退一步看:上一轮我因旋钮剥除未加门控而暂缓,绿色 CI 无法落定那个问题。本轮的诚实问题是:我现在批准,是因为修复确实正确,还是因为线程把反对磨没了。我的核对方式是重读代码而不是读 autofix 的总结:剥除已按家族门控、走共享谓词、分级处理( 作者第二轮自审提了七点,gate 应当认真对待——我逐条独立研判(详见 Stage 2)。共性是:没有一条是本 diff 引入的回归。第 1–2 点是用户提供矛盾旋钮、由 PR 声明的取舍裁决的边角;第 3、4 点早于 #8472 或本 PR 就存在(我已核实 #8472 的 diff 只新增了 我仍不主张的:DashScope 服务端对单旋钮形态的接受性未经验证——PR 自己也这么说,且此前 ✅ 的 我上一轮在旧 head 上的 待建跟进 issue(作者):非 qwen 预设的 — Qwen Code · qwen3.8-max Reviewed at |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. This is a fork PR, so the first round comes from the next scheduled scan (usually within minutes). Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 |
doudouOUC
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
中文说明
已审查。 建议见行内评论。
— qwen3.7-max via Qwen Code /review (v0.21.4)
doudouOUC
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Implementation is correct and well-tested; all three post-merge findings from #8472 are faithfully addressed with thorough regression tests. Sole suggestion (observability: add a debug log to dropConflictingThinkingKnobs) is inline and non-blocking.
Review:
|
|
@qwen-code /triage |
1 similar comment
|
@qwen-code /triage |
|
Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 2055 passed · 0 failed · 2055 total 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:2055 通过 · 0 失败 · 2055 总计 Verification reportPR #8488 Deep Verification — fix(core): harden Qwen 3.8 reasoning effort wire shapeVerdict: 中文摘要
Scope selection
Central claim — A/B wire-oracle proofHarness: Witness:
Counts: head 106/106 assertions pass (exit 0); base 104/104 pass against the encoded pre-fix expectations (exit 0) — the base arm reproduced every predicted pre-fix shape, including both competing-knobs forms and the ungated Test gatesWitness:
Non-vacuity of the new testsWitness: The HEAD test files were overlaid on the base worktree and run against the pre-fix source: exactly 7 failed | 218 passed, matching the PR's "seven new regression tests fail on the pre-fix code" claim one-for-one. Failure messages are intended behavioral mismatches, not setup errors — e.g. Mutation matrixWitness:
Every guard the PR introduces is pinned; no survivors. Positive control: M3's single red is the same test that fails in the base-source vacuity run. Each mutant's reds match the predicted attribution with no collateral tests going red. Reviewer Test Plan walkthrough (per step)Every step of the PR's own plan was executed and held:
No step was unperformable. CorrectionsNone (first verification round; no prior-round claims to correct). FindingsNo blocking findings. Observations (not findings):
Not covered
MethodologyEnvironment: Evidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Holding this one — item 1 (dropConflictingThinkingKnobs) needs another round. Full detail in my Stage 2/3 comments above; the short version:
- The drop is not family-gated: on the glm/kimi and legacy-qwen presets that inject
enable_thinkingviaextra_body, a usersamplingParams.reasoning_effortoverride silently deletes the thinking switch — the same "opaque sampling override, not a thinking switch" reasoning this PR uses to gate thetool_choicestrip. - On legacy qwen models the conflict resolves to no thinking signal on the wire, and the new tests lock that shape in.
'reasoning_effort' in mergedalso fires on'none', against the pipeline's own value-check semantics.
Items 2–4 look mergeable as-is. Suggested fixes (family gate / shared predicate helper, 'none' value check, one combined-shape test, docs row) are in the Stage 3 comment. 🙏
中文说明
先暂缓——item 1(dropConflictingThinkingKnobs)还需要一轮。细节见上方 Stage 2/3 评论,简述:
- 剥除未加家族门控:对经
extra_body注入enable_thinking的 glm/kimi 与旧 qwen 预设,用户的samplingParams.reasoning_effort覆盖会静默删掉思考开关——与本 PR 为tool_choice剥除加门控时"原样透传的采样覆盖项、不是思考开关"的理由自相矛盾。 - 对旧 qwen 模型,冲突解决为出网不携带任何思考信号,且新测试将该形态固化。
'reasoning_effort' in merged对'none'同样触发,与 pipeline 自身的值检查语义不一致。
Items 2–4 本身即可合并。建议的修复(家族门控 / 共享谓词 helper、'none' 值检查、一个组合形态测试、文档行)见 Stage 3 评论。🙏
— Qwen Code · qwen3.8-max
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Autofix round summary — PR #8488 (address review feedback)All blocking findings on item 1 ( Feedback points and dispositions
Changed files
Conflict notesNone — branch merges cleanly against Verification
中文说明Autofix 本轮摘要 — PR #8488(处理评审反馈)item 1( 反馈点与处理
变更文件
冲突说明无——分支与 验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
Review:
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
中文说明
已审查——无阻断问题。 建议见行内评论。
— qwen3.8-max via Qwen Code /review (v0.21.5)
yiliang114
left a comment
There was a problem hiding this comment.
LGTM — verified: 230/230 tests pass on changed files, 721/721 on full openaiContentGenerator dir, typecheck + ESLint clean. The four fixes do what they claim: competing-knob drop is correct (post-merge, both branches), prefix match covers snapshots/aliases consistently with modalityDefaults, tool_choice gate correctly family-scoped, disable-path refactor semantically identical.
Non-blocking:
- (P2) PR body says 'the explicit tier wins and the boolean is dropped — the wire never carries both knobs' for the legacy-model override, but the code/test/docs all say enable_thinking is KEPT on legacy qwen models. Code is correct; fix the PR body wording before squash so the commit record isn't misleading.
- (P2) The enable_thinking===true clause in pipeline.ts still strips tool_choice for non-qwen presets (glm-5.2). The PR's own rationale for gating the reasoning_effort clause ('opaque sampling override, not a thinking switch') applies equally here. Confirm DashScope actually rejects tool_choice=required + enable_thinking=true for non-qwen models, or family-gate that clause too.
- (P3) 'none' exemption can ship reasoning_effort:'none' + enable_thinking:true together — add a comment confirming server rejection is tier-scoped.
- (P3) No direct unit test for isQwenFamilyWireModel edge cases (undefined/case).
|
@qwen-code /triage |
|
Sandboxed verification: The verification job did not complete (checkout, runner, or setup error) and produced no report. See the workflow run for details. 中文 — 判定:
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
…M#8488 round 2) Second review round on QwenLM#8488: - Honour an explicit extra_body enable_thinking: false on the qwen3.8-max family as reasoning_effort: 'none' instead of silently deleting it and re-enabling thinking - Legacy qwen hybrids now drop the inert reasoning_effort override when it conflicts with a meaningful thinking_budget, keeping the knobs the model actually reads - Family-gate the pipeline's enable_thinking tool_choice clause like the reasoning_effort clause: on non-qwen models sharing the endpoint the field is an opaque no-op (GLM reads thinking.enabled), and stripping forced tool selection there degraded their side queries - The tier-native disable path emits reasoning_effort: 'none' — the knob the family reads — instead of enable_thinking: false; the required-thinking retry trigger recognises the new shape so runtime learning still fires - Warn once per generator (not debug per request) when user extra_body knobs are dropped; hoist the wire-model family predicates to modalityDefaults.ts and share the provider's extra_body merge tail - Tests for every behavior above (all load-bearing, verified by targeted mutation); docs attribute the vendor rejection to thinking_budget only and document the extra_body exceptions
Round 2 response —
|
|
The round-2 fixes referenced in my response comment above ( 上面回复中引用的 round 2 修复( |
…M#8488 round 2) (QwenLM#8536) Second review round on QwenLM#8488: - Honour an explicit extra_body enable_thinking: false on the qwen3.8-max family as reasoning_effort: 'none' instead of silently deleting it and re-enabling thinking - Legacy qwen hybrids now drop the inert reasoning_effort override when it conflicts with a meaningful thinking_budget, keeping the knobs the model actually reads - Family-gate the pipeline's enable_thinking tool_choice clause like the reasoning_effort clause: on non-qwen models sharing the endpoint the field is an opaque no-op (GLM reads thinking.enabled), and stripping forced tool selection there degraded their side queries - The tier-native disable path emits reasoning_effort: 'none' — the knob the family reads — instead of enable_thinking: false; the required-thinking retry trigger recognises the new shape so runtime learning still fires - Warn once per generator (not debug per request) when user extra_body knobs are dropped; hoist the wire-model family predicates to modalityDefaults.ts and share the provider's extra_body merge tail - Tests for every behavior above (all load-bearing, verified by targeted mutation); docs attribute the vendor rejection to thinking_budget only and document the extra_body exceptions
|
Released in v0.21.6. |






What this PR does
Follow-up to #8472 (Qwen 3.8 reasoning effort), addressing the review findings posted after merge. Four fixes to the DashScope wire shape:
reasoning_effort, the request now dropsenable_thinkingandthinking_budgetafter the userextra_bodymerge. Previously the bundled Token Plan preset made the flagship preview model shipreasoning_effortandenable_thinking: truetogether — the exact "two competing knobs" shape the nested-reasoningstrip exists to prevent — and DashScope rejectsreasoning_effortcombined withthinking_budget.tool_choicestrip: the clause added by feat(core): support Qwen 3.8 reasoning effort #8472 (droptool_choice: 'required'while thinking is enabled) is now gated to qwen-family wire models, mirroring the disable path's gate. On non-qwen models sharing the DashScope endpoint,reasoning_effortis an opaque sampling override, not a thinking switch, and dropping forced tool selection there degraded structured side queries (permission classifier, compaction, title generation).qwen3.8-max*), consistent with the family match in the modality defaults. Dated snapshots and-latestaliases previously fell back to the booleanenable_thinkingcollapse and silently lost the selected tier.tool_choicestrip now logs a debug line with the model and triggering condition; the effort-config JSDoc and the request-level-override copy comment are restored; the provider docs row now reflects the enforcedthinking_budgetdrop and the prefix-matched family.The deliberate decision on the asymmetric request-level override (a request-level
reasoning_effortreaching a legacy qwen model that also getsenable_thinkingfrom the effort config): the explicit tier wins and the boolean is dropped — the wire never carries both knobs.Why it's needed
The preset shape in #8472 meant every Token Plan user who selected an
/efforttier on the bundled preview model shipped two competing thinking knobs, with no live verification that the preview model accepts the pair. The ungatedtool_choicestrip was a behavioral regression for non-qwen models routed through the same endpoint, and the exact-match family gate silently dropped tiers for snapshot/alias model ids — reintroducing the exact bug #2876 describes.Reviewer Test Plan
How to verify
All changes are covered by unit tests; the seven new regression tests fail on the pre-fix code (verified by reverting the production files to the merged commit and re-running) and pass with the fix:
qwen3.8-max-preview+ preset-styleextra_body: { enable_thinking: true }+ effort tier → wire carriesreasoning_effortalone (plus the vision-branch and legacy-model variants of the same drop).glm-5.2+ userextra_body: { reasoning_effort: 'high' }in forced-tool mode keepstool_choice: 'required'(was dropped before this fix).qwen3.8-max-2026-01-15/qwen3.8-max-latestnow pass the tier through asreasoning_effort.reasoning: falseand per-requestincludeThoughts: falseboth strip the tier forqwen3.8-maxand keeptool_choice: 'required'.thinking_budget: dropped when a tier ships; kept alongsideenable_thinkingon legacy hybrid models (a valid pair).Evidence (Before & After)
N/A (wire-shape change, not user-visible; unit-test evidence above).
Tested on
Environment (optional)
Unit tests only (
vitest); fullnpm run typecheckand targetedeslintclean.Risk & Scope
enable_thinkingandreasoning_effortviaextra_bodynow ships onlyreasoning_effort— that combination is contradictory (and DashScope-rejected forthinking_budget), so the tier wins.qwen3.8-max-preview(no API access in this change); the pipeline disable path intentionally still strips the tier for thinking-mandatory models under an opt-out, leaving the model default.Linked Issues
Follow-up to #8472 (review comments posted post-merge); original user request #2876.
中文说明
本 PR 做了什么
#8472(Qwen 3.8 reasoning effort)的后续修复,处理合并后提交的 review 发现。对 DashScope 出网形态做四项修复:
reasoning_effort发出档位时,请求现在会在用户extra_body合并之后丢弃enable_thinking与thinking_budget。此前内置 Token Plan 预设会让旗舰 preview 模型同时携带reasoning_effort和enable_thinking: true——正是嵌套reasoning剥除逻辑要避免的"两个竞争旋钮"形态——且 DashScope 拒绝reasoning_effort与thinking_budget的组合。tool_choice剥除加模型家族门控:feat(core): support Qwen 3.8 reasoning effort #8472 新增的条件(思考开启时剥掉tool_choice: 'required')现在门控到 qwen 家族 wire 模型,与 disable 路径的门控一致。对共享 DashScope 端点的非 qwen 模型,reasoning_effort是原样透传的采样覆盖项而非思考开关,在那里剥掉强制工具选择会退化结构化侧查询(权限分类器、压缩、标题生成)。qwen3.8-max*),与 modality defaults 中的家族匹配一致。带日期的快照与-latest别名此前退回布尔enable_thinking折叠,所选档位被静默丢弃。tool_choice剥除现在记录带模型名与触发条件的 debug 日志;恢复 effort 配置的 JSDoc 与请求级覆盖拷贝的注释;provider 文档行更新为反映强制剥除thinking_budget与前缀匹配的家族。对不对称的请求级覆盖(请求级
reasoning_effort到达同时从 effort 配置获得enable_thinking的旧 qwen 模型)的明确决策:显式档位优先,布尔开关被剥掉——出网永远不同时携带两个旋钮。为什么需要
#8472 的预设形态意味着每个在内置 preview 模型上选择
/effort档位的 Token Plan 用户都会同时发出两个竞争的思考旋钮,而 preview 模型是否接受该组合没有任何真实验证。未加门控的tool_choice剥除对路由到同一端点的非 qwen 模型是行为回归;精确匹配的家族门控会让快照/别名模型 ID 静默丢失档位——重新引入 #2876 所描述的原始缺陷。Reviewer 测试计划
如何验证
所有改动均有单元测试覆盖;七个新回归测试在修复前代码上失败(已通过将生产文件还原到合并提交并复跑验证),修复后通过:
qwen3.8-max-preview+ 预设形态extra_body: { enable_thinking: true }+ effort 档位 → 出网仅携带reasoning_effort(另有 vision 分支与旧模型变体的同类剥除测试)。glm-5.2+ 用户extra_body: { reasoning_effort: 'high' }、强制工具模式下保留tool_choice: 'required'(修复前被剥掉)。qwen3.8-max-2026-01-15/qwen3.8-max-latest现在以reasoning_effort透传档位。reasoning: false与请求级includeThoughts: false都会为qwen3.8-max剥掉档位并保留tool_choice: 'required'。thinking_budget:与档位同发时被剥掉;在旧 hybrid 模型上与enable_thinking并存(合法组合)。前后对比证据
N/A(出网形态变更,非用户可见;证据为上述单元测试)。
测试环境
环境(可选)
仅单元测试(
vitest);全仓npm run typecheck与定向eslint干净。风险与范围
extra_body显式同时设置enable_thinking与reasoning_effort,现在只会发出reasoning_effort——该组合本身互相矛盾(且thinking_budget组合会被 DashScope 拒绝),档位优先。qwen3.8-max-preview上单旋钮形态的真实 DashScope 接受性(本次改动无 API 访问);pipeline 的 disable 路径在 thinking-mandatory 模型遇到 opt-out 时仍按原逻辑剥掉档位,交由模型默认值。关联 Issue
#8472 的后续(review 评论提交于合并后);原始用户请求 #2876。