feat(config): support Qwen thinkingFormat values#79777
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: yes. Source inspection on current main shows the requested values are rejected by schema/types and dropped by catalog normalization before the OpenAI-completions request builder could use them. Real behavior proof Next step before merge Security Review detailsBest possible solution: Land a narrow config/runtime contract change that supports both Qwen compat formats consistently across schema, types, catalog normalization, request construction, docs, and regression tests while leaving provider-specific wrappers unchanged. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection on current main shows the requested values are rejected by schema/types and dropped by catalog normalization before the OpenAI-completions request builder could use them. Is this the best way to solve the issue? Yes. The PR follows the narrow maintainable path by updating the public config contract, catalog normalization, OpenAI-completions request shaping, focused tests, and user-facing docs together. What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 0f00244f10e8. |
4797cdb to
2404edc
Compare
|
Landed via temp rebase onto main.
Thanks @indulgeback! |
## Summary - allow configured OpenAI-compatible Qwen models to opt into `qwen` and `qwen-chat-template` thinking payloads - preserve those compat values through schema validation and catalog normalization - map OpenClaw `/think` levels to `enable_thinking` or `chat_template_kwargs.enable_thinking` without also sending `reasoning_effort` - clarify docs that request-level chat-template kwargs require compatible backends such as vLLM ## Verification - git diff --check - pnpm exec oxfmt --check --threads=1 CHANGELOG.md docs/gateway/config-agents.md docs/gateway/config-tools.md src/config/zod-schema.core.ts src/config/types.models.ts src/model-catalog/normalize.ts src/agents/openai-transport-stream.ts src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - pnpm config:schema:check - pnpm test src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - GitHub CI on 2404edc Thanks @indulgeback.
## Summary - allow configured OpenAI-compatible Qwen models to opt into `qwen` and `qwen-chat-template` thinking payloads - preserve those compat values through schema validation and catalog normalization - map OpenClaw `/think` levels to `enable_thinking` or `chat_template_kwargs.enable_thinking` without also sending `reasoning_effort` - clarify docs that request-level chat-template kwargs require compatible backends such as vLLM ## Verification - git diff --check - pnpm exec oxfmt --check --threads=1 CHANGELOG.md docs/gateway/config-agents.md docs/gateway/config-tools.md src/config/zod-schema.core.ts src/config/types.models.ts src/model-catalog/normalize.ts src/agents/openai-transport-stream.ts src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - pnpm config:schema:check - pnpm test src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - GitHub CI on 2404edc Thanks @indulgeback.
## Summary - allow configured OpenAI-compatible Qwen models to opt into `qwen` and `qwen-chat-template` thinking payloads - preserve those compat values through schema validation and catalog normalization - map OpenClaw `/think` levels to `enable_thinking` or `chat_template_kwargs.enable_thinking` without also sending `reasoning_effort` - clarify docs that request-level chat-template kwargs require compatible backends such as vLLM ## Verification - git diff --check - pnpm exec oxfmt --check --threads=1 CHANGELOG.md docs/gateway/config-agents.md docs/gateway/config-tools.md src/config/zod-schema.core.ts src/config/types.models.ts src/model-catalog/normalize.ts src/agents/openai-transport-stream.ts src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - pnpm config:schema:check - pnpm test src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - GitHub CI on 2404edc Thanks @indulgeback.
## Summary - allow configured OpenAI-compatible Qwen models to opt into `qwen` and `qwen-chat-template` thinking payloads - preserve those compat values through schema validation and catalog normalization - map OpenClaw `/think` levels to `enable_thinking` or `chat_template_kwargs.enable_thinking` without also sending `reasoning_effort` - clarify docs that request-level chat-template kwargs require compatible backends such as vLLM ## Verification - git diff --check - pnpm exec oxfmt --check --threads=1 CHANGELOG.md docs/gateway/config-agents.md docs/gateway/config-tools.md src/config/zod-schema.core.ts src/config/types.models.ts src/model-catalog/normalize.ts src/agents/openai-transport-stream.ts src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - pnpm config:schema:check - pnpm test src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts - GitHub CI on 2404edc Thanks @indulgeback.
Summary
compat.thinkingFormat: "qwen"and"qwen-chat-template", and catalog normalization/request building did not preserve or emit those Qwen thinking payloads./thinklevels to toggleenable_thinkingdynamically.enable_thinkingorchat_template_kwargs.enable_thinkingwithout also sendingreasoning_effort.params.qwenThinkingFormat, and non-Qwen thinking formats are unchanged.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Real behavior proof (required for external PRs)
qwenorqwen-chat-templatethinking payloads and have/thinklevels reflected in the generated request body.v25.7.0, pnpm10.33.2; no live model backend required because this change is config validation and request-body construction.node --import tsx -e 'import { OpenClawSchema } from "./src/config/zod-schema.ts"; import { buildOpenAICompletionsParams } from "./src/agents/openai-transport-stream.ts"; const cfg={models:{providers:{local:{baseUrl:"http://127.0.0.1:8080/v1",api:"openai-completions",models:[{id:"qwen3.5-32b",name:"Qwen",reasoning:true,input:["text"],compat:{thinkingFormat:"qwen-chat-template"}}]}}}}; const parsed=OpenClawSchema.safeParse(cfg); const params=buildOpenAICompletionsParams({id:"qwen3.5-32b",name:"Qwen",api:"openai-completions",provider:"local",baseUrl:"http://127.0.0.1:8080/v1",reasoning:true,input:["text"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:131072,maxTokens:8192,compat:{thinkingFormat:"qwen-chat-template"}},{systemPrompt:"system",messages:[],tools:[]},{reasoning:"off"}); console.log(JSON.stringify({schemaAccepted:parsed.success,chatTemplateKwargs:params.chat_template_kwargs,hasReasoningEffort:Object.hasOwn(params,"reasoning_effort")},null,2));'node --import tsx -e 'import { buildOpenAICompletionsParams } from "./src/agents/openai-transport-stream.ts"; const base={id:"qwen3.5-32b",name:"Qwen",api:"openai-completions",provider:"local",baseUrl:"http://127.0.0.1:8080/v1",reasoning:true,input:["text"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:131072,maxTokens:8192,compat:{thinkingFormat:"qwen"}}; const on=buildOpenAICompletionsParams(base,{systemPrompt:"system",messages:[],tools:[]},{reasoning:"medium"}); const off=buildOpenAICompletionsParams(base,{systemPrompt:"system",messages:[],tools:[]},{reasoning:"off"}); console.log(JSON.stringify({enabled:on.enable_thinking,disabled:off.enable_thinking,enabledHasReasoningEffort:Object.hasOwn(on,"reasoning_effort"),disabledHasReasoningEffort:Object.hasOwn(off,"reasoning_effort")},null,2));'{ "schemaAccepted": true, "chatTemplateKwargs": { "enable_thinking": false }, "hasReasoningEffort": false }{ "enabled": true, "disabled": false, "enabledHasReasoningEffort": false, "disabledHasReasoningEffort": false }qwen-chat-template; request params forqwen-chat-templateincludechat_template_kwargs.enable_thinking: false; request params forqwentoggle top-levelenable_thinkingtrue/false; neither path sendsreasoning_effort.qwen/qwen-chat-templateexcluded fromModelCompatSchema,SupportedThinkingFormat, and catalog normalization.Root Cause (if applicable)
qwenandqwen-chat-templateeven though the upstream OpenAI-completions compat type includes them.zai, OpenRouter, provider-native efforts) but not Qwen OpenAI-compatible thinking payloads.Regression Test Plan (if applicable)
src/config/config-misc.test.tssrc/model-catalog/normalize.test.tssrc/agents/openai-transport-stream.test.tsqwen-chat-template, and request building emits the expected Qwen thinking fields withoutreasoning_effort.User-visible / Behavior Changes
Configured OpenAI-compatible Qwen model entries can now set:
compat.thinkingFormat: "qwen"for top-levelenable_thinkingcompat.thinkingFormat: "qwen-chat-template"forchat_template_kwargs.enable_thinkingDiagram (if applicable)
Security Impact (required)
Yes/No) NoYes/No) NoYes/No) NoYes/No) NoYes/No) NoYes, explain risk + mitigation: N/ARepro + Verification
Environment
v25.7.0, pnpm10.33.2local/qwen3.5-32bin request-construction proof)models.providers.local.models[0].compat.thinkingFormatset toqwenorqwen-chat-templateSteps
compat.thinkingFormat: "qwen-chat-template"withOpenClawSchema.safeParse.qwen-chat-templatewithreasoning: "off".qwenwithreasoning: "medium"andreasoning: "off".Expected
qwen-chat-templateemitschat_template_kwargs.enable_thinking: falseand omitsreasoning_effort.qwenemits top-levelenable_thinking: true/falseand omitsreasoning_effort.Actual
Evidence
Attach at least one:
Additional verification run:
pnpm test src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.tspassed 3 Vitest shards: 188 tests total.pnpm config:schema:checkpassed.pnpm exec oxfmt --check --threads=1 CHANGELOG.md docs/gateway/config-agents.md docs/gateway/config-tools.md src/config/zod-schema.core.ts src/config/types.models.ts src/model-catalog/normalize.ts src/agents/openai-transport-stream.ts src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.tspassed.git diff --checkpassed.Note:
pnpm check:changedpassed conflict-marker checks, changelog attribution, duplicate coverage,tsgo:core, andtsgo:core:test, then failed in the repo-widelint:corelane on pre-existing unused-import errors in unrelated files such assrc/plugins/provider-auth-input.ts,src/plugins/registry.ts, andsrc/plugins/types.ts. I did not change those files.Human Verification (required)
What you personally verified (not just CI), and how:
qwen-chat-template; payload construction forqwen-chat-templatedisabled thinking; payload construction forqwenenabled/disabled thinking; targeted Vitest coverage; schema check; formatting; whitespace check.reasoning_effort; disabled thinking maps tofalse; enabled thinking maps totrue; catalog normalization preservesqwen-chat-template.Review Conversations
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
Compatibility / Migration
Yes/No) YesYes/No) Yes, two additional acceptedcompat.thinkingFormatvalues.Yes/No) NoRisks and Mitigations
compat.thinkingFormat: "qwen"or"qwen-chat-template".