Skip to content

fix(provider): support openai-compatible reasoning toggle - #44064

Closed
fancive wants to merge 2 commits into
anomalyco:devfrom
fancive:openai-thinking-toggle
Closed

fix(provider): support openai-compatible reasoning toggle#44064
fancive wants to merge 2 commits into
anomalyco:devfrom
fancive:openai-thinking-toggle

Conversation

@fancive

Copy link
Copy Markdown
Contributor

Intent

修复 anomalyco/opencode issue #42793:当 models.dev 为 @ai-sdk/openai-compatible 模型声明 reasoning toggle 时,ProviderTransform.reasoningVariants 应生成 none/high 变体,分别发送 thinking.type=disabled/enabled,使 GLM 4.7 与 GLM 5 Turbo 等兼容端点可关闭或启用 thinking。实现应严格限制在 models.dev 声明 toggle 且 npm 为 @ai-sdk/openai-compatible 的路径,不改变 effort、budget 或其他 provider 行为;添加可执行行为回归测试。先完成充分本地测试,再将单一小提交推送到 fancive fork 并创建以 dev 为 base 的 GitHub PR,关联 issue #42793

What Changed

  • ProviderTransform.reasoningToggle now also recognizes models with npm @ai-sdk/openai-compatible (previously only @ai-sdk/cohere), so a reasoning toggle declared in models.dev produces none/high variants that send thinking.type: "disabled"/"enabled" for compatible endpoints such as GLM 4.7 and GLM 5 Turbo.
  • Added a regression test asserting the openai-compatible toggle mapping in ProviderTransform.reasoningVariants.

Risk Assessment

⚠️ Medium: The core fix is small, correctly implements the intended GLM 4.7/GLM 5 Turbo thinking toggle (verified that @ai-sdk/openai-compatible 2.0.41 passes unknown provider options like thinking through into the request body), and ships a proper behavioral regression test, but it also silently shadows deliberate variants() fallback behavior for minimax-m3 (nvidia/lilac chat_template_kwargs) and toggle-only glm-5.2 gateway models, which the author should confirm is acceptable.

Testing

对 issue #42793 的修复做了定向验证:新增回归测试在 base commit 上失败、修复后通过;transform 全文件 412 测试通过;用实时 models.dev 数据端到端复现 GLM 4.7 / GLM 5 Turbo / GLM 5(openai-compatible + toggle)生成 none/high 变体,并经真实 AI SDK 捕获到发送到端点的请求体分别携带 thinking.type=disabled/enabled,同时确认 effort 声明模型(glm-5.2)仍走 reasoningEffort 变体、其它 provider 行为不变,满足用户意图的全部约束。

Evidence: 端到端证据:GLM 4.7 toggle 变体生成与 wire 请求体(真实 models.dev 数据 + 真实 @ai-sdk/openai-compatible SDK)

{ "source": "models.dev/api.json (fetched live)", "provider": "zai (ZhipuAI)", "npm": "@ai-sdk/openai-compatible", "model": "glm-4.7", "variants": { "none": { "thinking": { "type": "disabled" } }, "high": { "thinking": { "type": "enabled" } } }, "wire": [ { "variant": "none", "wireBody": { "thinking": { "type": "disabled" }, "model": "glm-4.7" } }, { "variant": "high", "wireBody": { "thinking": { "type": "enabled" }, "model": "glm-4.7" } } ], "effortModelCheck": { "model": "glm-5.2", "variants": { "high": { "reasoningEffort": "high" }, "max": { "reasoningEffort": "max" } } } }

{
"source": "models.dev/api.json (fetched live)",
"provider": "zai (ZhipuAI)",
"npm": "@ai-sdk/openai-compatible",
"model": "glm-4.7",
"variants": {
"none": {
"thinking": {
"type": "disabled"
}
},
"high": {
"thinking": {
"type": "enabled"
}
}
},
"wire": [
{
"variant": "none",
"providerOptions": {
"zai": {
"thinking": {
"type": "disabled"
}
}
},
"wireBody": {
"thinking": {
"type": "disabled"
},
"model": "glm-4.7"
}
},
{
"variant": "high",
"providerOptions": {
"zai": {
"thinking": {
"type": "enabled"
}
}
},
"wireBody": {
"thinking": {
"type": "enabled"
},
"model": "glm-4.7"
}
}
],
"effortModelCheck": {
"model": "glm-5.2",
"variants": {
"high": {
"reasoningEffort": "high"
},
"max": {
"reasoningEffort": "max"
}
}
}
}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 warning
  • ⚠️packages/opencode/src/provider/transform.ts:1711 - Adding @ai-sdk/openai-compatible to reasoningToggle makes reasoningVariants return a non-undefined value for every openai-compatible model that models.dev declares a toggle for, which shadows the variants() fallback special cases (reasoningVariants ?? variants in provider.ts:1289). Concrete regression: nvidia minimaxai/minimax-m3 and lilac minimaxai/minimax-m3 (models.dev declares [{type:"toggle"}], npm @ai-sdk/openai-compatible) previously fell back to the deliberate minimax-m3 branch in variants() (transform.ts:736-749, added by fix(provider): correct MiniMax M3 thinking variants #38330) producing {none: {chat_template_kwargs: {thinking_mode: "disabled"}}, thinking: {chat_template_kwargs: {thinking_mode: "enabled"}}} for nvidia/lilac, and {none: {thinking: {type: "disabled"}}, thinking: {thinking: {type: "adaptive"}}} for others. After this change they get {none: {thinking: {type: "disabled"}}, high: {thinking: {type: "enabled"}}} — the variant id 'thinking' disappears, and for nvidia/lilac the endpoint-specific chat_template_kwargs control is bypassed; if the NIM endpoint ignores the generic 'thinking' field (the premise of fix(provider): correct MiniMax M3 thinking variants #38330), selecting the high variant silently fails to enable thinking (wrong result without error). Similarly, toggle-only glm-5.2 gateway models (e.g. crossmodel z-ai/glm-5.2, nvidia z-ai/glm-5.2) lose the {high/max: reasoningEffort} variants from the glm52 fallback (transform.ts:758-761), changing user-visible options beyond the GLM 4.7/5 Turbo scope. This conflicts with the intent constraint '不改变 effort、budget 或其他 provider 行为'. Recommended boundary: in reasoningVariants/reasoningToggle, keep the existing special-cased models (minimax-m3, glm-5.2) on their prior fallback path (early return for those ids) so the fix only activates where no deliberate fallback behavior exists.
✅ **Test** - passed

✅ No issues found.

  • bun test test/provider/transform.test.ts -t reasoningVariants — 55 个变体测试全部通过,含新增 converts openai-compatible toggle options
  • 回归验证:临时将 src/provider/transform.ts 换回 base commit e00890c 版本后运行新测试 → 失败(返回 undefined,证明修复前无法生成变体);恢复修复版本后 → 通过
  • bun test test/provider/transform.test.ts — 全文件 412 测试全部通过,781 个断言,无回归
  • 端到端脚本:实时拉取 models.dev/api.json,zai(ZhipuAI,npm=@ai-sdk/openai-compatible)provider 的 glm-4.7 经 fromModelsDevProvider 生成 variants none/high(thinking disabled/enabled)
  • 端到端脚本:变体经 ProviderTransform.providerOptions 后送入真实 @ai-sdk/openai-compatible SDK(generateText + 捕获 fetch),断言 wire JSON body 含 thinking: {type: "disabled"} / {type: "enabled"}
  • 范围约束:zai 的 glm-5.2(effort 声明)仍生成 reasoningEffort high/max 变体,effort 路径未被 toggle 路径影响;glm-5-turbo、glm-5 同样正确生成 none/high 变体
  • diff 审查:transform.ts 仅 1 行改动(cohere 分支追加 || model.api.npm === "@ai-sdk/openai-compatible"),effort 优先分支与其它 provider 行为不变
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@github-actionsgithub-actionsBot added contributor needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Aug 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.

Feel free to open a new pull request that follows our guidelines.

@github-actionsgithub-actionsBot removed the needs:compliance This means the issue will auto-close after 2 hours. label Aug 22, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@fancive