fix(core): keep tool parameters on the MiniMax chat-completions wire - #11842
yiliang114 wants to merge 9 commits into
Conversation
Since #11431, convertLlmToolsToOpenAI() drops the `parameters` key for any tool that declares an empty argument list, so JSON.stringify sends a function declaration without it. `list_agents` is registered unconditionally with `{type:'object',properties:{},additionalProperties:false}`, which means every default interactive request carries one. MiniMax rejects that with `400 invalid params, function parameters is empty (2013)`, so a bare greeting fails before any tool call exists in the conversation (#11834). The omission is load-bearing for llama.cpp (#10080) and for strict OpenAI-contract validators such as LM Studio (#11410), so it stays the default. Add an opt-out that the MiniMax routing turns on, emitting `{type:'object',properties:{}}` -- the shape the Anthropic wire already substitutes for a missing `inputSchema`. The --openai-logging reconstruction follows the same gate so the logged body matches the body actually sent. Fixes #11834 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-issue-patrol/jmu12tvxm45
|
✅ Qwen Triage finished — CI landed green on ✅ Qwen Triage 已完成 —— |
|
Thanks for the PR — and for going back to fix your own regression rather than leaving it for someone else to untangle. Template looks good ✓ Problem. Observed, not theoretical. #11834 is open, the reporter pasted the exact gateway rejection ( Direction. Aligned. Per-routing wire-shape gating is already how this file handles gateway disagreement — Size. Core paths are touched. 36 production lines ( Approach. The scope feels right — an optional trailing Risk. Elevated. Moving on to code review. 🔍 中文说明感谢贡献 —— 也感谢你回头修自己引入的回归,而不是留给别人收拾。 模板完整 ✓ 问题:已观测,不是理论性的。#11834 仍处于 open,报告者贴出了确切的网关拒绝信息( 方向:对齐。按路由区分 wire 形态本来就是本文件处理网关分歧的既有做法 —— 规模:触及核心路径。生产代码 36 行( 方案:范围合理 —— 一个默认 风险:偏高。 进入代码审查 🔍 — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
Code reviewI wrote down my own approach before reading the diff: put a capability on the What I verified rather than assumed, all read at the base commit:
One finding worth your attention — not a blocker, but it sharpens the risk you already flagged. The shape this PR restores is not the shape MiniMax received before #11431. Tracing "parameters": {"type": "object"}— which is precisely the shape #11410 reports LM Studio rejecting, and which is why omitting the key satisfied both backends at once. Your That cuts both ways, and I think net in your favour. If MiniMax accepted This is exactly the discriminator the triage on #11834 asked @wangvhero for — the Small factual correction to the description. macOS and Windows are not "left to CI": Testing evidenceUnattended CI run — I did not build, run, or execute anything from this PR. Evidence below is the PR's own CI, read through the API for commit Nothing is red. Three substantive jobs are still in flight, so there is no test result to report yet: Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 The 579 passing tests quoted in the description are the author's own local run on Linux — attributed, not independently re-run here, and not a substitute for the Ubuntu job above. The Sandboxed verification would settle part of this: 中文说明代码审查 读 diff 之前我先写下了自己的方案:在 以下都是我在 base commit 上实际读过、而不是默认成立的:
一个值得你注意的发现 —— 不是阻塞项,但它让你已经点出的风险更精确。 本 PR 补回的形态,不是 #11431 之前 MiniMax 收到的那个形态。在 base 代码上跟一遍 "parameters": {"type": "object"}—— 这恰好就是 #11410 里 LM Studio 拒绝的那个形态,也正是「省略整个键」能同时满足两类后端的原因。你选的 这一点两面都成立,但我认为净效果对你有利。如果 MiniMax 在 v0.23.2 之前能接受 这正是 #11834 的 triage 向 @wangvhero 索要的判别信息( 描述里一个小的事实更正。 macOS 和 Windows 并不是「交给 CI」: 测试证据:本次为无人值守 CI 运行,我没有构建、运行或执行本 PR 的任何代码。下表是针对 commit 沙箱验证可以定论其中一部分: — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
|
Confidence: 4/5 — the mechanism, the blast radius and the default path are all verified; the only thing nobody can verify from here is which of two literals MiniMax's validator actually wants. Stepping back: my independent proposal was a provider capability method, and it died on the logging path — you cannot reach a provider instance from Everything else is proportionate. A ternary and a hostname gate at two sites, 36 production lines, no drive-by refactors, no formatting churn, nothing unrelated in the diff. The 148 test lines are not padding for a wire-shape regression that already shipped once in v0.23.2 — asserting on I re-derived the problem rather than accepting the framing: Two reservations, both stated plainly:
I am not approving this because I ran out of reasons to say no. Holding a guaranteed-broken P1 regression for an external validator none of us can query would be worse than shipping the canonical shape — the same one both sibling wires already use — with a retest request attached. Approval is deferred until CI lands green on the commit below. 中文说明置信度:4/5 —— 机制、影响范围与默认路径都已验证;唯一无法从这里验证的,是 MiniMax 的校验器究竟要两个字面量中的哪一个。 退一步看:我自己的方案是在 provider 上加能力位,而它死在日志路径上 —— 不额外铺管线, 其余部分都很得体。一个三元表达式加两处 hostname gate,36 行生产代码,没有顺手重构,没有格式化噪音,diff 里没有无关改动。148 行测试不是凑数 —— 一个已经在 v0.23.2 里真实发布过一次的 wire 形态回归,断言打在 我是重新推导了问题,而不是接受它的表述: 两点保留意见,直说:
我不是因为想不出反对理由才批准。为一个我们谁都查询不了的外部校验器,扣住一个必然损坏的 P1 回归,比发出这个规范形态(两条同级 wire 已在用的同一个)并附上一份重测请求要更糟。批准推迟到下面这个 commit 的 CI 全绿之后。 — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
…hint
Aggregating gateways proxy MiniMax backends under their own hostname and
forward the `invalid params, function parameters is empty (2013)`
rejection verbatim, so the hostname-only gate left those routings
broken: verified live against such a gateway, where the hostname-only
build still 400s on a bare greeting while the widened gate (hostname OR
model id containing "minimax") gets a normal response. Measured on the
same gateway: MiniMax-direct model ids reject the parameterless shape
and accept `parameters: {type: object, properties: {}}`, while
DashScope-backed routings accept both, so widening is side-effect free
there; llama.cpp / LM Studio model ids never contain the string and
keep the omission they require (#11834).
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Scope ledger — round 3, scope corrected after explicit maintainer direction. Original baseline ( Current head Verification: provider test 10/10; focused ESLint and Prettier clean; core build and core typecheck clean. Full repository build was attempted but stopped in untouched Scope verdict: corrected. Generic gateway detection is an explicit non-goal and can be handled separately if evidence warrants it. |
The gate read `contentGeneratorConfig.model`, but requests can carry a model override (`client.ts` `modelOverride`, set by the core tool scheduler), so a MiniMax-named config answering a non-MiniMax request flipped the shape while a MiniMax request under a non-MiniMax config kept the omission that 400s. Gate on the wire model instead — the same source the `enable_thinking` gate in pipeline.ts already documents for exactly this reason: the pipeline passes `context.model`, and the logging mirror passes `request.model` and falls back to the config model inside the predicate. Covered by new pins for both directions (#11834). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Two design corrections on top of the first commit, both from live measurement rather than review: 1. The gate was too narrow ( 2. The gate read the wrong model ( 584 tests pass across the five affected files. Blast radius is unchanged from the original patch — |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-conflict/jmu1aot5f4g
Local verification against a real bundled CLI — merge referenceI built both arms and ran them end to end against a host that really answers on Verified at head RigDocker 1. The reported failure, reproduced and fixedReal TUI in tmux, typed 2. What changes on the wire, and where
3. Does the suite hold the fix down?Six of seven mutants die. The one that matters: on the first head ( Also in that figure, and the reason to touch the branch once more:
One 4. Two corrections to claims made around the fix
Checks
Residuals (all consistent with your own risk section)
Machine: macOS 26.6.2 (arm64), Node v24.18.1, containers on 中文说明本地真实环境验证 —— 供合并参考我没有停在序列化层,而是把两条臂都打成 验证基于 head 装置Docker 1. 报告的故障:复现并修复tmux 里的真 TUI,输入 2. wire 上变了什么、在哪里变
3. 测试能不能把修复钉住7 个变异体杀 6。关键的一个:在第一个 head( 同一张图里还有必须再动一次分支的原因:
对该文件跑一次 4. 两处需要更正的说法
检查项
残留(与你自己的风险一节一致)
机器:macOS 26.6.2(arm64)、Node v24.18.1,容器用 |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
All three points acted on:
On hunk 2: agreed — the operative reason is keeping the no-capture fallback consistent, not the log/wire divergence I claimed. Keeping the hunk as-is. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. 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.
Test Plan (not a blocker): src/core/openaiContentGenerator/converter-parameterless-parameters.test.ts — no such file or directory; src/core/openaiContentGenerator/converter.test.ts — no such file or directory; src/core/openaiContentGenerator/pipeline.test.ts — no such file or directory; src/core/openaiContentGenerator/provider/minimax.test.ts — no such file or directory; src/core/loggingContentGenerator/loggingContentGenerator.test.ts — no such file or directory; and 1 more.
中文说明
已审查——无阻断问题。 建议见行内评论。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。
Test Plan(非阻断):src/core/openaiContentGenerator/converter-parameterless-parameters.test.ts — no such file or directory; src/core/openaiContentGenerator/converter.test.ts — no such file or directory; src/core/openaiContentGenerator/pipeline.test.ts — no such file or directory; src/core/openaiContentGenerator/provider/minimax.test.ts — no such file or directory; src/core/loggingContentGenerator/loggingContentGenerator.test.ts — no such file or directory; and 1 more。
— qwen3.8-max via Qwen Code /review (v0.23.3)
Re-verified at
|
| check | result |
|---|---|
end-to-end, strict gateway, api.minimaxi.com + MiniMax-M3 |
main exit 1 with the reporter's 400 … (2013); PR head exit 0, model answered |
gateway-proxy case (api.deepseek.com + MiniMax-M3) |
main exit 1, PR head exit 0 |
| host × model matrix, 14 runs of the real bundle | unchanged — 4 routings keep {}, the other 3 stay byte-identical (6e239f1f1bc5 → 209b979d306f only where the gate fires) |
| 5 test files named in the PR body | 584 / 584 |
whole packages/core suite |
26 429 passed / 26 467 — one failure, client.test.ts microcompaction, which fails identically on the merge base |
PR merged with today's main (10 commits ahead, no conflicts) |
tsc --noEmit clean · 584 / 584 · prettier --check clean |
eslint --max-warnings 0, both changed directories |
clean |
| 7-mutant matrix | same verdicts — 6 killed, only N5 survives |
| CI | every lane green, including Lint & Static (15m50s) |
Closed since my last report
- The Prettier failure is fixed.
node scripts/lint.js --prettieris clean repo-wide, and CI's lane went from red to pass. - The
excludeworkaround on > 你好 ✕ [API Error: 400 invalid params, function parameters is empty (2013)] > /update ●︎ Qwen Code 0.23.3 已是最新! #11834 is corrected — the advice there is now["list_agents", "cron_list"]. Thanks for turning that around quickly; that was the one thing actively misleading a user in the field. - The risk section now covers the local-engine case (line 96 of the body), with the
llama-serverb10621 numbers. Accurate as written. - Cosmetic leftover: the body still names only
list_agentsas the always-registered parameterless tool — two ship on every request.
Still open — and it is R1-1
Mutant N5 (gate the logging mirror on generatorConfig.model instead of request.model) still passes all 584 cases at this head, which is exactly what the bot's unresolved R1-1 thread says: the logging hunk is the one production change nothing pins.
One clarification I can add from measurement, since R1-1's rationale rests on the fallback being reachable: for a MiniMax chat-completions session it is not. The only generators that emit chat completions are OpenAIContentGenerator and its Qwen subclass, both run through ContentGenerationPipeline, and both logging entry points (generateContent :423 and generateContentStream :593) wrap the call in the capture context — so captured is always set and the reconstruction never runs. Measured: logged tools == wire tools on both arms, and still equal on a bundle rebuilt with the hunk deleted. The reconstruction is reached only if buildRequest() throws before pipeline.ts:1510, or for a generator that is not chat completions (Anthropic / Gemini / /responses). That does not make R1-1 wrong — it is still the one unpinned production change, and the suggested pair of argument assertions is cheap — it just narrows what a reader should expect the hunk to protect.
Verdict from this side: the fix is measured, the wire change is confined to the routings it is supposed to touch, non-matching routings are byte-identical, and CI is green. The only judgement call left is whether R1-1's test is required before merge.
中文说明
在 c889318356 上重新验证 —— 全部重跑,结论不变
整套装置在新 head 上重跑了一遍。没有任何回归,我上次报告里那个卡 CI 的问题也已经消失。 报告里还剩一项没关,而它正是评审机器人提的 R1-1。
真正变了什么
自我上次报告的那个 head 以来,分支只多了一个提交 —— style(core): format minimax provider test,就是那个 Prettier 修复。四个生产文件与我验证过的版本逐字节相同,所以行为结论原样成立;我仍然把它们全部重测了一遍。
有一点看着吓人但其实无害,值得记一笔:在两个 head 上重新构建 dist/,每个 chunk 的文件名都不一样。这不是测试文件混进了包 —— grep -rl "llama-3.1-8b-instruct" dist/ 什么也搜不到。原因是构建把 commit sha 嵌进了产物(c889318356 出现在 dist/chunks/chunk-TZK5FCHL.js,f4d8dd65a4 哪里都没有),这个共享字符串会让每个 chunk 的内容哈希连锁改变。同一个 commit 上连续构建两次,产物逐字节一致。
新 head 上的重跑结果
| 检查 | 结果 |
|---|---|
端到端,严格网关,api.minimaxi.com + MiniMax-M3 |
main exit 1,报告者的 400 … (2013);PR head exit 0,模型正常应答 |
网关代理场景(api.deepseek.com + MiniMax-M3) |
main exit 1,PR head exit 0 |
| 主机 × 模型矩阵,真包跑 14 次 | 与上次一致 —— 4 条路由保留 {},另外 3 条逐字节不变(只有命中 gate 时 6e239f1f1bc5 → 209b979d306f) |
| PR 正文点名的 5 个测试文件 | 584 / 584 |
packages/core 全量套件 |
26 429 通过 / 26 467 —— 仅 1 个失败 client.test.ts microcompaction,它在 merge base 上同样失败 |
PR 合到今天的 main(领先 10 个提交,无冲突) |
tsc --noEmit 干净 · 584 / 584 · prettier --check 干净 |
eslint --max-warnings 0,两个改动目录 |
干净 |
| 7 个变异体矩阵 | 判定不变 —— 杀 6,只有 N5 存活 |
| CI | 全部 lane 绿,包括 Lint & Static(15m50s) |
上次报告里已关掉的
- Prettier 失败已修复。
node scripts/lint.js --prettier全仓干净,CI 那条 lane 由红转绿。 - > 你好 ✕ [API Error: 400 invalid params, function parameters is empty (2013)] > /update ●︎ Qwen Code 0.23.3 已是最新! #11834 上的
exclude绕过方案已更正 —— 现在给的是["list_agents", "cron_list"]。这条修得很快,它是唯一一条正在实际误导用户的信息。 - 风险一节已补上本地引擎那条(正文第 96 行),带上了
llama-serverb10621 的实测数字,写法准确。 - 还剩一处外观问题:正文仍然只点名
list_agents是那个无条件注册的零参数工具,实际上每次请求都发两个。
仍未关闭 —— 就是 R1-1
变异体 N5(把日志镜像的判定改成 generatorConfig.model 而不是 request.model)在这个 head 上依旧跑过全部 584 个用例,这正是机器人那条未解决的 R1-1 所说的:日志那一段是本 PR 里唯一没有任何测试钉住的生产改动。
由于 R1-1 的论证依赖"兜底路径可达",我可以补一条实测澄清:对 MiniMax 的 chat-completions 会话来说它并不可达。会发出 chat completions 的生成器只有 OpenAIContentGenerator 及其 Qwen 子类,两者都走 ContentGenerationPipeline,而日志的两个入口(generateContent :423 与 generateContentStream :593)都把调用包在捕获上下文里 —— 所以 captured 必定有值,重建分支永远不会执行。实测:两条臂的日志工具数组与 wire 逐字节相同,把这一段整体删掉重新打包后仍然相同。只有当 buildRequest() 在 pipeline.ts:1510 之前抛错,或者生成器根本不是 chat completions(Anthropic / Gemini / /responses)时,才会走到重建。这并不否定 R1-1 —— 它依然是唯一没被钉住的生产改动,而且建议补的那对参数断言成本极低 —— 只是把"这段代码到底在保护什么"说得更准确一些。
我这边的结论: 修复有实测支撑,wire 的变化被限制在该动的路由上,未命中的路由逐字节不变,CI 全绿。剩下唯一需要判断的,是 R1-1 那条测试是否作为合并前置条件。
Reverting the MiniMax opt-out in the OpenAI request logger left the suite green, so both mutations survived: dropping the options object, and reading the configured model instead of the wire model. One case with two calls pins both directions, and each revert now fails it.
…ax-tool-parameters
|
Qwen Code review did not complete successfully. The review pipeline failed before a review could be posted. A transient error is retried automatically; if you are seeing this, retry with |
Picks up main's review-runner-schedule helper-test fix (#11933), which writes a commonjs package.json into the fake-gh temp dir so the Lint lane's helper tests stop failing with 'require is not defined in ES module scope' when TMPDIR resolves inside the repo. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-conflict/jmu2luc9n6d
doudouOUC
left a comment
There was a problem hiding this comment.
Verified the wire change and its blast radius at the PR head. No issues found.
- Default path is byte-identical.
optionsdefaults to{}, sokeepParameterlessParametersis falsy everywhere except the MiniMax gate, and theparameters = undefineddrop from #11431 is preserved for every other provider — the two pre-existingconverter.test.tspins for llama.cpp/LM Studio are unedited, and the opt-in literal ({type:'object',properties:{}}) is the same shape the Anthropic wire substitutes for a missinginputSchema. - Exactly two production call sites, both updated.
git grep convertLlmToolsToOpenAIfindspipeline.ts:1035(the only chat-completions wire builder) andloggingContentGenerator.ts:1046(the log-reconstruction path) — both pass the flag, so logged bodies match sent bodies. There is no third serializer. - The gate reads the right model.
pipeline.tspassescontext.model, whichcreateRequestContextsets torequest.model || contentGeneratorConfig.model— the true wire model, so a request-level override that points at MiniMax is honoured; the hostname branch and the/minimax/imodel-id fallback inisMiniMaxRoutingare both safe on a missing/invalidbaseUrl(try/catch insideisMiniMaxProvider). - The absent-tools case is unchanged. The
tools.length > 0guard still skips an emptytools: [], and nothing downstream mutatesbaseRequest.toolsafter conversion. - The
/minimax/imodel-id match also catching an llama.cpp/LM-Studio server that happens to serve a MiniMax-named model is disclosed and measured under Risk & Scope, and the restored literal'sadditionalProperties: falsedrop is disclosed as a static inference — both read as accepted scope rather than defects.
qqqys
left a comment
There was a problem hiding this comment.
APPROVE
核对基线:head fdf880a4ec41(8 个文件,+431/-1,最后提交 12:03:54Z)。
历史阻塞问题:无
本 PR 历史上没有出现过 CHANGES_REQUESTED:三次 review 分别是 DISMISSED(占位)、COMMENTED(2026-09-14T18:14:09Z)与 doudouOUC 在当前 head 上的 COMMENTED(2026-09-15T12:18:32Z)。1 条 review thread 且已 isResolved: true,Critical 级未解决数 0。
本轮独立扫描:未发现 Critical
生产改动共 4 个文件 +63 行,修的是 #11834:MiniMax 对不带 parameters 的零参数工具返回 400 invalid params, function parameters is empty (2013),而始终注册的 list_agents 正是这种工具。这类改动最大的风险是「为了一个 provider 把其他 provider 弄坏」,我按这条主线核对:
- 默认行为逐字未变,两个既有修复没有被回退。
convertLlmToolsToOpenAI新增的第三参是options: { keepParameterlessParameters?: boolean } = {},分支写成不传 option 时parameters = options.keepParameterlessParameters ? { type: 'object', properties: {} } : undefined;
undefined为 falsy,走parameters = undefined,与改动前完全一致。因此 llama.cpp 无法在空properties上编译 grammar(#10080)与 LM Studio 等严格校验器拒绝无properties的parameters(#11410)这两条修复都保持有效——新行为是显式 opt-in,不是改默认值。代码注释把这三个 issue 的相互制约关系写得很清楚,这一点做得好。 - 门禁的判定来源选对了。 两个调用点都传「线上实际使用的模型」而非配置模型:
pipeline.ts传context.model,loggingContentGenerator.ts传request.model,注释说明理由——请求级 model override 决定由哪个后端应答,与同文件里enable_thinking门禁用的是同一个来源。这也保证了日志侧与实际发送侧用同一个谓词,调试日志里的 body 就是真正发出去的 body,不会出现「日志显示省略、实际发送保留」的偏差。 isMiniMaxRouting的误判面足够窄。provider/minimax.ts:52-60是主机侧沿用既有的return MiniMaxOpenAICompatibleProvider.isMiniMaxProvider(config) || /minimax/i.test(wireModel ?? config.model ?? '');
isMiniMaxProvider(精确匹配api.minimaxi.com/api.minimax.io,外加.minimaxi.com/.minimax.io后缀,且该后缀宽松的取舍与其风险在:14-23已有注释说明,本 PR 未改动它);模型侧是对 wire model 做大小写不敏感的minimax子串匹配,用于覆盖「聚合网关用自己的域名代理 MiniMax 后端、只把 2013 拒绝原样转发」这种主机名毫无线索的情形。误判需要「模型 id 含 minimax 但后端不是 MiniMax」,而这种组合恰好就是网关代理 MiniMax 的常见命名;反过来,真正会被{type:'object',properties:{}}拒绝的 llama.cpp / LM Studio 是本地服务,其模型 id 不会含 minimax。?? ''的兜底也让 model 缺失时安全落到不匹配,不会抛错。- 改动没有越过 converter 的契约。 返回类型仍是
Promise<OpenAI.Chat.ChatCompletionTool[]>,替换出的{ type: 'object', properties: {} }正是 Anthropic wire 在缺少inputSchema时使用的形状(注释已点明),属于合法 JSON Schema,不引入新的类型分支。
测试侧新增 4 个文件共 +368 行,其中 converter-parameterless-parameters.test.ts(+148)是为本行为专设的;当前 head 的 Lint & Static、Test (ubuntu-latest, Node 22.x)、Integration Tests (no-AK, No Sandbox) 全部 pass,说明这些用例确实通过。
CI:上述三项 pass,review-pr pending,按策略不作为门禁,我没有等待或轮询;无失败项。
结论:无历史阻塞问题,本轮未发现可证明的 Critical,提交 APPROVE。
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "agent 1e": none — no check was cut short.; "agent 6c": I did not read baseLlmClient.ts:558 / client.ts:5042 to confirm whether request.model can actually be '' in production, which is the reachability premis….
Test Plan (not a blocker): src/core/openaiContentGenerator/converter-parameterless-parameters.test.ts — no such file or directory; src/core/openaiContentGenerator/converter.test.ts — no such file or directory; src/core/openaiContentGenerator/pipeline.test.ts — no such file or directory; src/core/openaiContentGenerator/provider/minimax.test.ts — no such file or directory; src/core/loggingContentGenerator/loggingContentGenerator.test.ts — no such file or directory; and 1 more.
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/core/src/core/openaiContentGenerator/provider/minimax.ts:57 — [review] host-blind model-id leg also changes the first-party Alibaba Coding Plan / Token Plan routing (MiniMax-M2.5 on coding.dashscope.aliyuncs.com), which the PR body…
中文说明
未探索到全部深度(达到工具调用预算):"agent 1e":none — no check was cut short.;"agent 6c":I did not read baseLlmClient.ts:558 / client.ts:5042 to confirm whether request.model can actually be '' in production, which is the reachability premis…。
Test Plan(非阻断):src/core/openaiContentGenerator/converter-parameterless-parameters.test.ts — no such file or directory; src/core/openaiContentGenerator/converter.test.ts — no such file or directory; src/core/openaiContentGenerator/pipeline.test.ts — no such file or directory; src/core/openaiContentGenerator/provider/minimax.test.ts — no such file or directory; src/core/loggingContentGenerator/loggingContentGenerator.test.ts — no such file or directory; and 1 more。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.23.3)
qwen-code-review-bot
left a comment
There was a problem hiding this comment.
COMMENT
Independent pass over head fdf880a4ec41 (+431/-1, CI all green). I reviewed the two open threads from the round-2 review rather than duplicating them — both reproduce, and I would not approve while R2-1 stands.
R2-1 (Critical) reproduces from first principles. The gate is isMiniMaxProvider(config) || /minimax/i.test(wireModel), and the model-id leg carries no host condition. A self-hosted MiniMax deployment — llama.cpp serving MiniMax-M2-GGUF, where the served model id derives from the filename or --alias, so containing "minimax" is the normal spelling — is exactly the deployment #10080 stripped the empty parameters key for: llama.cpp cannot compile a grammar over an empty properties map. This PR switches that deployment back onto the empty-object shape, i.e. it fixes a 400 from the hosted MiniMax API by re-introducing the grammar-compile failure on a local engine. Reachable, a regression, and the fails-closed direction (the local engine breaks, not the cloud one). The deferred note about the same leg also firing for MiniMax-M2.5 on first-party Alibaba Coding Plan routing belongs in the same fix.
The hard part is real: hosted MiniMax rejects the omission, llama.cpp rejects the presence, and the hostname genuinely carries no hint behind an aggregating gateway. But "model id matches /minimax/i" cannot distinguish "gateway proxying hosted MiniMax" from "local llama.cpp running MiniMax weights" — both look identical at this layer. The fix probably wants one of: (a) exclude loopback/private/known-local hosts from the model-id leg, accepting that a remote self-hosted engine still misfires; (b) make the model-id leg opt-in via config for gateway users; or (c) key off a stronger signal than the bare id. Any of the three beats the current unconditional leg.
R2-2 (Suggestion) also reproduces. Both new gate tests reach keepParameterlessParameters: true only through the model-id leg; the host leg — isMiniMaxProvider, which covers the official api.minimaxi.com / api.minimax.io routings, i.e. the most common way this gate fires — has no test at either gated call site. Narrowing the predicate to a bare model-id regex would stay green. One case with baseUrl: https://api.minimaxi.com/v1 and a non-MiniMax model id expecting true pins it.
Everything else checks out: the default path stays byte-identical (options defaults to {}, the two pre-existing llama.cpp/LM Studio pins are unedited), the converter assertions are made on the serialized wire body rather than the returned object (the right level for a JSON.stringify bug), the logging mirror correctly uses the request-level model so the logged body matches the sent body, and the R1-1 thread is resolved with the test it asked for.
CI note: Test / Lint / Integration / web-shell E2E / Desktop x2 all green on this head.







What this PR does
Keeps an explicit empty
parametersobject on zero-argument tools only after the request has been routed to the MiniMax provider. The shared OpenAI-compatible serializer and every other provider keep their existing behavior.Why it's needed
#11834 reports that MiniMax rejects requests containing a function declaration without a
parameterskey with400 invalid params, function parameters is empty (2013). Qwen Code intentionally omits that key on the shared path because llama.cpp and LM Studio require the omission, so the two contracts must be handled at the provider boundary.Reviewer Test Plan
How to verify
"parameters":{"type":"object","properties":{}}.Evidence (Before & After)
N/A — request serialization only.
Tested on
Environment (optional)
Focused provider tests, core build and core typecheck on macOS.
Risk & Scope
Linked Issues
Fixes #11834
Related: #10080 and #11410 require the shared OpenAI-compatible path to continue omitting empty tool parameters.
中文说明
这个 PR 做了什么
仅在请求已经被路由到 MiniMax provider 之后,为零参数工具保留显式的空
parameters对象。共享的 OpenAI-compatible 序列化逻辑和其他所有 provider 都保持现有行为。为什么需要
#11834 报告 MiniMax 会拒绝包含无
parameters键的函数声明,并返回400 invalid params, function parameters is empty (2013)。Qwen Code 的共享路径会有意省略该键,因为 llama.cpp 与 LM Studio 需要这种形态,因此两种不兼容要求应在 provider 边界分别处理。Reviewer Test Plan
如何验证
"parameters":{"type":"object","properties":{}}。前后对比证据
N/A —— 仅修改请求序列化。
测试平台
环境(可选)
在 macOS 上运行了聚焦 provider 测试、core build 与 core typecheck。
风险与范围
关联 Issue
Fixes #11834
相关:#10080 与 #11410 要求共享的 OpenAI-compatible 路径继续省略空工具参数。