fix: support Qwen3.5 thinking mode and fix tool-call loop in openai_chat#2713
fix: support Qwen3.5 thinking mode and fix tool-call loop in openai_chat#2713jianzhichun wants to merge 1 commit into
Conversation
Qwen3.5 (and compatible models served via vLLM/SGLang) have two issues when used through the openai_chat adapter: 1. Tool-call loop: when thinking is enabled, the model requires reasoning_content to be echoed back in the assistant message of subsequent tool_result turns. Without it the model repeats the same tool call indefinitely. 2. Token waste: thinking chain output is expensive and often unnecessary for agentic tasks. Changes: - provider.rs: add `preserveReasoningContent` and `disableThinking` fields to ProviderMeta - claude.rs: honour `preserveReasoningContent` meta flag in should_preserve_reasoning_content_for_openai_chat(), falling back to "tool call" when no thinking content is present - forwarder.rs: when `disableThinking` is set, inject chat_template_kwargs.enable_thinking=false into openai_chat requests - types.ts: expose the two new meta fields to the frontend - ClaudeFormFields.tsx + ProviderForm.tsx: add "思考模式" (Thinking Mode) toggle in Advanced options, visible only for openai_chat providers; ON → preserveReasoningContent, OFF → disableThinking
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 705761c977
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| </p> | ||
| </div> | ||
| <Switch | ||
| checked={thinkingEnabled ?? false} |
There was a problem hiding this comment.
Persist the shown OFF thinking state
The toggle renders undefined as unchecked (checked={thinkingEnabled ?? false}), but submit only writes disableThinking when the state is exactly false. This creates a hidden third state: users can see “OFF” and save without actually disabling thinking, so requests keep default behavior unexpectedly. In practice, a newly opened openai_chat provider can look disabled while no disableThinking flag is persisted.
Useful? React with 👍 / 👎.
| disableThinking: | ||
| appId === "claude" && | ||
| category !== "official" && | ||
| localApiFormat === "openai_chat" && | ||
| localThinkingEnabled === false |
There was a problem hiding this comment.
Reset thinking preference when preset changes
localThinkingEnabled is reused when building meta.disableThinking / meta.preserveReasoningContent, but preset changes reset other Claude-local options and never reset this one. If a user toggles thinking on one preset and then switches to another openai_chat preset, the previous thinking flag is silently carried into the new provider payload, causing unintended behavior to be saved.
Useful? React with 👍 / 👎.
|
Claude finished @farion1231's task in 2m 11s —— View job Claude PR Review
I confirmed both of the issues Codex raised — they reflect real UI/state inconsistencies, not pedantry. The Rust side looks clean. Important — “Thinking Mode OFF” is a silent no-op for new providers
disableThinking:
appId === "claude" &&
category !== "official" &&
localApiFormat === "openai_chat" &&
localThinkingEnabled === false // undefined fails this check
? true
: undefined,Concrete failure: open the dialog, pick any Cleanest fix: pick a real default (e.g. initialise Important —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 705761c977
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| </p> | ||
| </div> | ||
| <Switch | ||
| checked={thinkingEnabled ?? false} |
There was a problem hiding this comment.
Persist explicit OFF when thinking switch is shown unchecked
The switch renders unchecked when thinkingEnabled is undefined (checked={thinkingEnabled ?? false}), but the submit path only writes disableThinking when localThinkingEnabled === false. That means a user can see “Thinking Mode OFF” and save without changing the toggle, yet no disableThinking flag is persisted, so the backend may still run with thinking enabled. This UI/state mismatch can silently produce behavior opposite to what the form indicates.
Useful? React with 👍 / 👎.
| const [localThinkingEnabled, setLocalThinkingEnabled] = useState< | ||
| boolean | undefined | ||
| >(() => { | ||
| if (appId !== "claude") return undefined; | ||
| const meta = initialData?.meta; |
There was a problem hiding this comment.
Re-sync thinking state when provider data/preset changes
localThinkingEnabled is initialized once from initialData and then treated as source of truth for submission, but unlike other local Claude fields it is not reset when preset/form data changes. In flows where the same form instance is reused (e.g., switching presets or loading a different provider), stale thinking state can leak into meta.disableThinking/meta.preserveReasoningContent and save unintended flags.
Useful? React with 👍 / 👎.
Closes #2712
Summary
preserveReasoningContentmeta flag: when set, cc-switch echoesreasoning_contentback in tool_result turns, fixing the infinite tool-call loop with Qwen3.5 and other thinking models served via vLLM/SGLangdisableThinkingmeta flag: injectschat_template_kwargs.enable_thinking=falseinto openai_chat requests for deployments where thinking is unnecessary (saves tokens)openai_chat; ON →preserveReasoningContent, OFF →disableThinkingChanged Files
src-tauri/src/provider.rsProviderMetasrc-tauri/src/proxy/providers/claude.rspreserveReasoningContentflag inshould_preserve_reasoning_content_for_openai_chat()src-tauri/src/proxy/forwarder.rschat_template_kwargs.enable_thinking=falsewhendisableThinkingis setsrc/types.tssrc/components/providers/forms/ClaudeFormFields.tsxsrc/components/providers/forms/ProviderForm.tsxTest
Verified against Qwen3.5-397B-A17B-INT8 on vLLM 0.17 — tool calls complete correctly with
preserveReasoningContent: true.