Skip to content

feat(config): support Qwen thinkingFormat values#79777

Merged
steipete merged 2 commits into
openclaw:mainfrom
indulgeback:codex/qwen-thinking-format-config
May 9, 2026
Merged

feat(config): support Qwen thinkingFormat values#79777
steipete merged 2 commits into
openclaw:mainfrom
indulgeback:codex/qwen-thinking-format-config

Conversation

@indulgeback

Copy link
Copy Markdown
Contributor

Summary

  • Problem: JSON config rejected compat.thinkingFormat: "qwen" and "qwen-chat-template", and catalog normalization/request building did not preserve or emit those Qwen thinking payloads.
  • Why it matters: local OpenAI-compatible Qwen backends such as llama.cpp/llama-server could not use OpenClaw /think levels to toggle enable_thinking dynamically.
  • What changed: the config schema/type and catalog normalizer now allow both values, and OpenAI-completions request building maps them to top-level enable_thinking or chat_template_kwargs.enable_thinking without also sending reasoning_effort.
  • What did NOT change (scope boundary): default provider detection, bundled Qwen plugin behavior, vLLM plugin-specific params.qwenThinkingFormat, and non-Qwen thinking formats are unchanged.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: configured OpenAI-compatible Qwen model entries can opt into qwen or qwen-chat-template thinking payloads and have /think levels reflected in the generated request body.
  • Real environment tested: macOS, local OpenClaw checkout, Node v25.7.0, pnpm 10.33.2; no live model backend required because this change is config validation and request-body construction.
  • Exact steps or command run after this patch:
    • 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));'
  • Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
{
  "schemaAccepted": true,
  "chatTemplateKwargs": {
    "enable_thinking": false
  },
  "hasReasoningEffort": false
}
{
  "enabled": true,
  "disabled": false,
  "enabledHasReasoningEffort": false,
  "disabledHasReasoningEffort": false
}
  • Observed result after fix: schema validation accepts qwen-chat-template; request params for qwen-chat-template include chat_template_kwargs.enable_thinking: false; request params for qwen toggle top-level enable_thinking true/false; neither path sends reasoning_effort.
  • What was not tested: a live llama.cpp/llama-server HTTP request against an actual Qwen model.
  • Before evidence (optional but encouraged): source inspection on main showed qwen/qwen-chat-template excluded from ModelCompatSchema, SupportedThinkingFormat, and catalog normalization.

Root Cause (if applicable)

  • Root cause: OpenClaw's public config allowlists intentionally excluded qwen and qwen-chat-template even though the upstream OpenAI-completions compat type includes them.
  • Missing detection / guardrail: existing schema/catalog/request tests covered other thinking formats (zai, OpenRouter, provider-native efforts) but not Qwen OpenAI-compatible thinking payloads.
  • Contributing context (if known): bundled Qwen and vLLM provider wrappers already had provider-specific Qwen thinking support, but custom JSON provider config could not express the generic compat mode.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • src/config/config-misc.test.ts
    • src/model-catalog/normalize.test.ts
    • src/agents/openai-transport-stream.test.ts
  • Scenario the test should lock in: schema accepts both Qwen thinking formats, catalog normalization preserves qwen-chat-template, and request building emits the expected Qwen thinking fields without reasoning_effort.
  • Why this is the smallest reliable guardrail: the behavior lives entirely in schema/catalog normalization/request construction; no live provider is needed to catch regressions in those gates.
  • Existing test that already covers this (if any): none for these exact Qwen compat values.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

Configured OpenAI-compatible Qwen model entries can now set:

  • compat.thinkingFormat: "qwen" for top-level enable_thinking
  • compat.thinkingFormat: "qwen-chat-template" for chat_template_kwargs.enable_thinking

Diagram (if applicable)

Before:
JSON config -> schema/catalog rejects or drops qwen thinkingFormat -> /think cannot reach backend-specific enable_thinking

After:
JSON config -> schema/catalog preserve qwen thinkingFormat -> OpenAI-completions payload includes enable_thinking or chat_template_kwargs.enable_thinking

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local checkout, Node v25.7.0, pnpm 10.33.2
  • Model/provider: configured OpenAI-compatible local Qwen-style provider (local/qwen3.5-32b in request-construction proof)
  • Integration/channel (if any): none
  • Relevant config (redacted): models.providers.local.models[0].compat.thinkingFormat set to qwen or qwen-chat-template

Steps

  1. Validate a config object containing compat.thinkingFormat: "qwen-chat-template" with OpenClawSchema.safeParse.
  2. Build OpenAI-completions params for qwen-chat-template with reasoning: "off".
  3. Build OpenAI-completions params for qwen with reasoning: "medium" and reasoning: "off".

Expected

  • Schema accepts the config.
  • qwen-chat-template emits chat_template_kwargs.enable_thinking: false and omits reasoning_effort.
  • qwen emits top-level enable_thinking: true/false and omits reasoning_effort.

Actual

  • Matches expected.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Additional verification run:

  • pnpm test src/config/config-misc.test.ts src/model-catalog/normalize.test.ts src/agents/openai-transport-stream.test.ts passed 3 Vitest shards: 188 tests total.
  • pnpm config:schema:check passed.
  • 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 passed.
  • git diff --check passed.

Note: pnpm check:changed passed conflict-marker checks, changelog attribution, duplicate coverage, tsgo:core, and tsgo:core:test, then failed in the repo-wide lint:core lane on pre-existing unused-import errors in unrelated files such as src/plugins/provider-auth-input.ts, src/plugins/registry.ts, and src/plugins/types.ts. I did not change those files.

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: schema acceptance for qwen-chat-template; payload construction for qwen-chat-template disabled thinking; payload construction for qwen enabled/disabled thinking; targeted Vitest coverage; schema check; formatting; whitespace check.
  • Edge cases checked: both Qwen compat modes avoid sending reasoning_effort; disabled thinking maps to false; enabled thinking maps to true; catalog normalization preserves qwen-chat-template.
  • What you did not verify: live llama.cpp/llama-server request/response behavior.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

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

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) Yes, two additional accepted compat.thinkingFormat values.
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: sending Qwen-specific thinking fields to a backend that does not support them.
    • Mitigation: behavior only activates when the model entry explicitly opts in with compat.thinkingFormat: "qwen" or "qwen-chat-template".

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation gateway Gateway runtime agents Agent runtime and tooling size: S proof: supplied External PR includes structured after-fix real behavior proof. labels May 9, 2026
@clawsweeper

clawsweeper Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

Summary
The PR expands model compat config to accept qwen and qwen-chat-template, preserves those values through catalog normalization, maps OpenAI-completions /think settings to Qwen request fields, and updates docs, changelog, and targeted tests.

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
Sufficient (live_output): The PR body includes copied after-fix Node output from a local checkout showing schema acceptance and generated Qwen request payloads for both new formats.

Next step before merge
No repair lane is needed because the PR already contains the focused code, docs, changelog, tests, and after-fix behavior proof for maintainer review.

Security
Cleared: The diff only changes config allowlists, request payload shaping, docs, tests, and changelog; no new dependency, workflow, permission, secret, download, or package execution surface was introduced.

Review details

Best 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:

  • Current main schema gap: Current main ModelCompatSchema.thinkingFormat accepts openai, openrouter, deepseek, and zai, but not qwen or qwen-chat-template. (src/config/zod-schema.core.ts:203, d7b73d8d94f8)
  • Current main type gap: Current main explicitly excludes qwen and qwen-chat-template from the public config SupportedThinkingFormat type. (src/config/types.models.ts:53, d7b73d8d94f8)
  • Current main normalization gap: Catalog normalization currently preserves only openai, openrouter, deepseek, and zai, so Qwen thinking formats are dropped before runtime use. (src/model-catalog/normalize.ts:221, d7b73d8d94f8)
  • PR request mapping: The PR adds Qwen-specific OpenAI-completions request shaping and suppresses the generic reasoning_effort path after handling Qwen formats. (src/agents/openai-transport-stream.ts:1843, 4797cdb43369)
  • Upstream dependency contract: The pinned @mariozechner/pi-ai package documents and implements qwen and qwen-chat-template thinking formats, including enable_thinking request fields. (package.json:1703, d7b73d8d94f8)
  • Related issue context: The linked issue discussion and prior ClawSweeper review identified the same schema/type/normalization gaps and noted that transport mapping was needed in addition to opening the allowlist.

Likely related people:

  • steipete: Recent GitHub commit history and local blame show heavy ownership of the OpenAI-completions transport plus the Qwen/vLLM thinking wrappers that define the closest existing behavior. (role: recent maintainer; confidence: high; commits: 280798166931, 836d4b410575, 252c63429ecf; files: src/agents/openai-transport-stream.ts, extensions/vllm/stream.ts, extensions/qwen/stream.ts)
  • shakkernerd: GitHub commit history for src/model-catalog/normalize.ts points to this handle for the model catalog normalizer that the PR extends. (role: introduced adjacent behavior; confidence: medium; commits: b9da9408139c, 8a14328c69e6; files: src/model-catalog/normalize.ts)

Remaining risk / open question:

  • The PR body did not include a live llama.cpp or llama-server HTTP request against an actual Qwen model, so backend acceptance is inferred from request-construction proof and upstream docs/source.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 0f00244f10e8.

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 9, 2026
@steipete
steipete force-pushed the codex/qwen-thinking-format-config branch from 4797cdb to 2404edc Compare May 9, 2026 11:55
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 9, 2026
@steipete
steipete merged commit c91fffd into openclaw:main May 9, 2026
113 checks passed
@steipete

steipete commented May 9, 2026

Copy link
Copy Markdown
Contributor

Landed via temp rebase onto main.

  • Local gate: 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: green on 2404edc
  • Source head: 2404edc
  • Landed commit: c91fffd

Thanks @indulgeback!

lykeion-dev pushed a commit to lykeion-dev/openclaw--rev that referenced this pull request May 14, 2026
## 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.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
## 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.
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
## 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.
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
## 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation gateway Gateway runtime proof: supplied External PR includes structured after-fix real behavior proof. size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Allow qwen-chat-template and qwen as thinkingFormat values in JSON config

2 participants