fix(core): resolve DashScope thinking-knob conflicts by family (#8488 round 2) - #8536
Conversation
…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
|
Thanks for the PR — second round on #8488. Template ✓ all required sections present. Problem: real and verified, not theoretical. I traced all three defects in the merged round-1 code myself: an explicit Direction: aligned — this completes the fix #8488 set out to make: stop silently discarding explicit user configuration, and stop degrading forced-tool side queries for non-qwen models on the shared endpoint. No claude-code CHANGELOG signal for this area, which is expected for a vendor-specific DashScope wire shape; the area is squarely qwen-code's own mission. Size: core paths ( Approach: scope matches the three findings plus the three inline suggestions from round-1 review; nothing unrelated rides along. The consolidation (family predicates moved to Risk: revert-history signal matches high-risk paths: Moving on to code review. 🔍 中文说明感谢贡献——#8488 的第二轮处理。 模板 ✓ 各必填部分齐全。 问题: 真实存在且已核实,并非理论性问题。我在已合并的第 1 轮代码中逐一确认了三处缺陷: 方向: 对齐——这是在完成 #8488 的既定目标:不再静默丢弃用户显式配置,不再退化共享端点上非 qwen 模型的强制工具侧查询。claude-code CHANGELOG 无此领域信号,对 DashScope 供应商专属出网形态属预期之内;该领域正是 qwen-code 自己的核心使命。 规模: 触及核心路径( 方案: 范围与三项发现外加第 1 轮 review 的三条行内建议匹配,无夹带改动。收拢(家族谓词移入 风险: revert 历史信号命中高风险路径: 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewBefore reading the diff I sketched my own fix for the three round-2 findings from the PR description alone; the implementation matches that independent proposal point for point, so the assessment below isn't anchored-on-arrival. What I verified by reading the code:
No critical blockers found. One cosmetic nit, non-blocking: the once-per-generator warn reads Test evidence (the PR's own CI, fetched via API — no PR code was run in this review)The Linux leg of
Sandboxed verification would settle the remaining gap: 中文说明代码审查读 diff 之前,我先仅凭 PR 描述独立草拟了三项发现的修法;实现与独立方案逐点一致,因此以下评估不是先入为主的确认。 逐条核实:
未发现阻断性问题。 一处非阻断的小瑕疵:每 generator 一次的 warn 在删除循环之后读取 测试证据(PR 自身 CI,经 API 获取——本审查未运行任何 PR 代码)
(CI 明细见上表) 沙箱验证可以补齐剩余缺口: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean, focused second round; the problem was verified in the merged code before reading the diff; the two open items are a cosmetic warn-log nit and a vendor-side fact no sandbox here can settle. Stepping back: this is a follow-up whose motivation is auditable — each of the three defects is observable in the merged round-1 code, so the PR is not fixing a phantom. The implementation is what I would have written: translate rather than delete where user intent can be preserved, flip the drop priority toward the knob the model actually reads, and put both Reservations, stated plainly: (1) the warn-log nit from the review — payload timing on the legacy branch and wording on the translation path; cosmetic, fixable later; (2) live DashScope acceptance of Approving, pinned to the reviewed commit. The Stage 1 high-risk-path flag stands: worth a maintainer's glance at the new disable shape if a live 中文说明置信度:4/5 —— 干净、聚焦的第二轮处理;问题在读 diff 之前已在已合并代码中核实;两个未决项是一处 warn 日志的小瑕疵,和一个本环境沙箱无法验证的供应商侧事实。 退一步看:这是一份动机可审计的后续——三个缺陷在已合并的第 1 轮代码中均可观察到,不是在修一个幽灵。实现与我独立草拟的方案一致:能保留用户意图就翻译而非删除、剥除优先级倒向模型真正读取的旋钮、两个 保留意见,直说:(1)审查中指出的 warn 日志小瑕疵——旧家族分支的载荷时序与翻译路径的措辞;外观问题,可后续修;(2) 已按被审 commit 钉住批准。第 1 阶段的高风险路径标记保持有效:若方便跑一次真实 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
yiliang114
left a comment
There was a problem hiding this comment.
LGTM — round-2 fixes are correct: enable_thinking:false → reasoning_effort:'none' translation honours explicit opt-out; legacy hybrids correctly drop inert reasoning_effort and keep thinking_budget; tool_choice enable_thinking clause now family-gated consistent with its sibling (GLM/DeepSeek read different fields, stripping tool_choice only degraded their side queries); disable shape emits the knob the family actually reads; family predicates centralized in modalityDefaults.ts (single source). Warn-once for user extra_body drops is good observability. Tests cover all six fixes including edge cases (undefined input, case-insensitivity, snapshot/alias matching).
|
Released in v0.21.6. |
What this PR does
Second review round on #8488 (merged). Resolves the remaining thinking-knob conflicts on the DashScope wire shape by model family:
qwen3.8-maxfamily, an explicitextra_body.enable_thinking: falseno longer gets silently deleted (which turned thinking back on) — it is translated into the family's canonical disable,reasoning_effort: 'none', which the pipeline's disable strip already preserves.reasoning_effortoverride conflicting with athinking_budgetnow drops the inertreasoning_effortinstead of the meaningful budget;enable_thinkingandthinking_budgetsurvive.tool_choicegate made consistent: theenable_thinking === trueclause is family-gated like itsreasoning_effortsibling. On non-qwen models sharing the endpoint the field is an opaque no-op (GLM readsthinking.enabled, DeepSeekthinking.type) that does not put the request in thinking mode, so strippingtool_choice: 'required'there only degraded their forced-tool side queries — the exact harm fix(core): harden Qwen 3.8 reasoning effort wire shape #8488 set out to fix.thinkingMandatorystays ungated.reasoning_effort: 'none'(the knob it reads) instead ofenable_thinking: false(the boolean it doesn't), and also drops a conflictingthinking_budget. The required-thinking retry trigger recognises the new shape so runtime learning still fires.extra_bodyknob drops nowwarnonce per generator (they discard explicit user config); the wire-model family predicates moved tomodalityDefaults.ts(single source, also used by the pipeline's non-DashScope branch), and the provider's vision/text merge tails share one helper.thinking_budgetonly (theenable_thinking+reasoning_effortpair was live-accepted in feat(core): support Qwen 3.8 reasoning effort #8472's E2E), and theextra_bodyexceptions are documented.Why it's needed
The round-1 shape still silently discarded explicit user configuration in two places (findings 1–2 of the #8488 second review) and left the two
tool_choiceclauses on opposite sides of the family-gating argument (finding 3) — non-qwen Token Plan presets (glm-5.*,kimi-k2.*) shipenable_thinking: true, so their forced-tool side queries kept losingtool_choice: 'required'.Reviewer Test Plan
How to verify
All changes are covered by unit tests; every new/flipped test was verified load-bearing by targeted mutation (reverting the corresponding fix turns exactly its tests red, nothing else):
qwen3.8-max+ effort tier +extra_body: { enable_thinking: false }→ wire carriesreasoning_effort: 'none'alone.qwen3.7-max+extra_body: { thinking_budget, reasoning_effort }→ wire keepsenable_thinking/thinking_budget, drops the inertreasoning_effort(text and vision branches).glm-5.2+ preset shape end-to-end through the real provider:tool_choice: 'required'survives.qwen3.8-maxopt-outs shipreasoning_effort: 'none'; the retry tests re-pin runtime learning with the new shape.Evidence (Before & After)
N/A (wire-shape change, not user-visible; unit-test + mutation evidence above).
Tested on
Environment (optional)
Unit tests only; full
npm run typecheckand targetedeslint/prettier --checkclean.Risk & Scope
enable_thinkingon kimi presets and rejectsrequiredin thinking mode, kimi side queries would 400 instead of silently degrading — no codebase or live evidence supports that reading, while the status quo observably discarded forced tools for non-qwen presets. Stated in the fix(core): harden Qwen 3.8 reasoning effort wire shape #8488 thread for the record.reasoning_effort: 'none'as a disable onqwen3.8-max(no API access); the non-DashScope (chat_template_kwargs) disable branch is unchanged.Linked Issues
Follow-up to #8488 (second-round review findings); original user request #2876.
中文说明
本 PR 做了什么
#8488(已合并)的第二轮 review 处理。按模型家族消解 DashScope 出网形态上剩余的思考旋钮冲突:
qwen3.8-max家族上,显式的extra_body.enable_thinking: false不再被静默删除(那会把思考重新打开),而是翻译为家族的标准禁用形态reasoning_effort: 'none'——pipeline 的 disable 剥除本就保留该值。thinking_budget冲突的reasoning_effort覆盖现在剥掉惰性的reasoning_effort而非有意义的 budget;enable_thinking与thinking_budget保留。tool_choice门控一致化:enable_thinking === true条款与reasoning_effort兄弟条款一样加上家族门控。对共享端点的非 qwen 模型,该字段是不透明的空操作(GLM 读thinking.enabled、DeepSeek 读thinking.type),不会把请求置入思考模式,在那里剥掉tool_choice: 'required'只会退化其强制工具侧查询——正是 fix(core): harden Qwen 3.8 reasoning effort wire shape #8488 要修复的损害。thinkingMandatory保持不加门控。reasoning_effort: 'none'(家族读取的旋钮)而非enable_thinking: false(家族不读的布尔),并同时剥掉冲突的thinking_budget。required-thinking 重试触发条件识别新形态,运行时学习照常触发。extra_body旋钮现在每 generator 记录一次warn(丢弃的是用户显式配置);wire 模型家族谓词移入modalityDefaults.ts(单源,pipeline 非 DashScope 分支也在使用);provider 的 vision/文本合并尾部共享同一 helper。thinking_budget(enable_thinking+reasoning_effort组合在 feat(core): support Qwen 3.8 reasoning effort #8472 的真实 E2E 中被接受),并补充extra_body例外说明。为什么需要
第 1 轮形态仍在两处静默丢弃用户显式配置(#8488 第二轮 review 的发现 1–2),且两个
tool_choice条款在家族门控上立场相反(发现 3)——非 qwen Token Plan 预设(glm-5.*、kimi-k2.*)都携带enable_thinking: true,其强制工具侧查询持续丢失tool_choice: 'required'。Reviewer 测试计划
如何验证
所有改动均有单元测试覆盖;每个新增/反转测试都经定向变异验证承重(还原对应修复恰好使其变红,不波及其他):
qwen3.8-max+ effort 档位 +extra_body: { enable_thinking: false }→ 出网仅携带reasoning_effort: 'none'。qwen3.7-max+extra_body: { thinking_budget, reasoning_effort }→ 出网保留enable_thinking/thinking_budget,剥掉惰性reasoning_effort(文本与视觉分支)。glm-5.2预设形态经真实 provider 端到端:tool_choice: 'required'保留。qwen3.8-maxopt-out 发出reasoning_effort: 'none';重试测试以新形态重新钉住运行时学习。前后对比证据
N/A(出网形态变更,非用户可见;证据为上述单测与变异验证)。
测试环境
环境(可选)
仅单元测试;全仓
npm run typecheck与定向eslint/prettier --check干净。风险与范围
enable_thinking并在思考模式下拒绝required,kimi 侧查询将以 400 替代静默退化——代码库与实测均无证据支持该解读,而现状可观察地在非 qwen 预设上丢弃强制工具。已在 fix(core): harden Qwen 3.8 reasoning effort wire shape #8488 线程中明确记录。reasoning_effort: 'none'作为qwen3.8-max禁用形态的真实 DashScope 接受性(无 API 访问);非 DashScope(chat_template_kwargs)disable 分支未改动。关联 Issue
#8488 的后续(第二轮 review 发现);原始用户请求 #2876。