fix(openrouter): handle reasoning_details in Qwen3 stream parsing#66905
Conversation
Greptile SummaryThis PR fixes empty responses from OpenRouter/Qwen3 models by adding The implementation is targeted and consistent with existing reasoning-field handling. The new test covers the primary multi-chunk scenario and confirms correct assembly of thinking + text content blocks. Confidence Score: 5/5Safe to merge — fix is minimal, follows existing patterns, and is covered by a new unit test. No P0 or P1 issues found. The No files require special attention. Reviews (1): Last reviewed commit: "fix(openrouter): handle reasoning_detail..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 94cda6c34f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| @@ -455,7 +455,7 @@ export function buildChannelSetupWizardAdapterFromSetupWizard(params: { | |||
| }); | |||
| }, | |||
| }); | |||
| const trimmedValue = rawValue.trim(); | |||
| const trimmedValue = (rawValue ?? "").trim(); | |||
There was a problem hiding this comment.
Preserve required-input failure on undefined text responses
Coalescing rawValue to "" here turns an undefined prompt result into a silent empty value. In the required-field path, that empty value falls through to normalizedValue being falsy and the loop continue branch, so setup can proceed without collecting a required input. This is most visible when the prompt layer returns undefined on cancel/interruption: instead of failing fast, the wizard can now silently skip mandatory configuration.
Useful? React with 👍 / 👎.
| (await params.prompter.text({ | ||
| message: params.inputPrompt, | ||
| validate: (value) => (value?.trim() ? undefined : "Required"), | ||
| }) | ||
| })) ?? "" | ||
| ).trim(); |
There was a problem hiding this comment.
Keep token prompts from downgrading undefined to "keep"
Defaulting prompter.text() to "" causes interrupted/undefined token entry to be treated as an empty token rather than an error. Downstream, empty token values are interpreted as "keep existing token", which is incorrect for first-time setup (no configured token) and can leave the credential unset without any explicit failure or retry. This converts a hard failure into silent misconfiguration.
Useful? React with 👍 / 👎.
94cda6c to
4fb723f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4fb723f568
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bcfac3bfe7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Add support for the reasoning_details field returned by OpenRouter/Qwen3 models. Previously this field was not recognized, causing payloads=0 and incomplete turn errors. - Add reasoning_details handling in processOpenAICompletionsStream - Extract text from reasoning_details array items with type reasoning.text - Treat as thinking content, similar to other reasoning fields - Add test case for reasoning_details handling Fixes openclaw#66833
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 501561be5f
ℹ️ 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".
501561b to
35a2bc7
Compare
obviyus
left a comment
There was a problem hiding this comment.
Verified the OpenRouter Qwen3 empty-reply failure path and confirmed the stream parser now handles reasoning_details on the main completions path.
Maintainer follow-up: preserved same-chunk tool calls, kept streamed tool-call argument accumulation intact across repeated reasoning_details deltas, added regression coverage for both cases, and added the unreleased changelog entry.
Local gate: pnpm test src/agents/openai-transport-stream.test.ts (55/55). pnpm check was rerun and only hit the unrelated current-main src/media-understanding/runner.proxy.test.ts:231 tsgo failure.
… (thanks @bladin) * fix(openrouter): handle reasoning_details field in Qwen3 stream parsing Add support for the reasoning_details field returned by OpenRouter/Qwen3 models. Previously this field was not recognized, causing payloads=0 and incomplete turn errors. - Add reasoning_details handling in processOpenAICompletionsStream - Extract text from reasoning_details array items with type reasoning.text - Treat as thinking content, similar to other reasoning fields - Add test case for reasoning_details handling Fixes openclaw#66833 * fix(openrouter): keep tool calls with reasoning_details * fix: handle OpenRouter Qwen3 reasoning_details streams (openclaw#66905) (thanks @bladin) * fix: preserve streamed tool calls with reasoning deltas (openclaw#66905) (thanks @bladin) --------- Co-authored-by: bladin <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
… (thanks @bladin) * fix(openrouter): handle reasoning_details field in Qwen3 stream parsing Add support for the reasoning_details field returned by OpenRouter/Qwen3 models. Previously this field was not recognized, causing payloads=0 and incomplete turn errors. - Add reasoning_details handling in processOpenAICompletionsStream - Extract text from reasoning_details array items with type reasoning.text - Treat as thinking content, similar to other reasoning fields - Add test case for reasoning_details handling Fixes openclaw#66833 * fix(openrouter): keep tool calls with reasoning_details * fix: handle OpenRouter Qwen3 reasoning_details streams (openclaw#66905) (thanks @bladin) * fix: preserve streamed tool calls with reasoning deltas (openclaw#66905) (thanks @bladin) --------- Co-authored-by: bladin <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
… (thanks @bladin) * fix(openrouter): handle reasoning_details field in Qwen3 stream parsing Add support for the reasoning_details field returned by OpenRouter/Qwen3 models. Previously this field was not recognized, causing payloads=0 and incomplete turn errors. - Add reasoning_details handling in processOpenAICompletionsStream - Extract text from reasoning_details array items with type reasoning.text - Treat as thinking content, similar to other reasoning fields - Add test case for reasoning_details handling Fixes openclaw#66833 * fix(openrouter): keep tool calls with reasoning_details * fix: handle OpenRouter Qwen3 reasoning_details streams (openclaw#66905) (thanks @bladin) * fix: preserve streamed tool calls with reasoning deltas (openclaw#66905) (thanks @bladin) --------- Co-authored-by: bladin <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
… (thanks @bladin) * fix(openrouter): handle reasoning_details field in Qwen3 stream parsing Add support for the reasoning_details field returned by OpenRouter/Qwen3 models. Previously this field was not recognized, causing payloads=0 and incomplete turn errors. - Add reasoning_details handling in processOpenAICompletionsStream - Extract text from reasoning_details array items with type reasoning.text - Treat as thinking content, similar to other reasoning fields - Add test case for reasoning_details handling Fixes openclaw#66833 * fix(openrouter): keep tool calls with reasoning_details * fix: handle OpenRouter Qwen3 reasoning_details streams (openclaw#66905) (thanks @bladin) * fix: preserve streamed tool calls with reasoning deltas (openclaw#66905) (thanks @bladin) --------- Co-authored-by: bladin <[email protected]> Co-authored-by: Ayaan Zaidi <[email protected]>
Summary
Fix OpenRouter/Qwen3 models returning empty responses due to unrecognized
reasoning_detailsfield.Problem: When using⚠️ Agent couldn't generate a response."
openrouter/qwen/qwen3-235b-a22bor any Qwen3 model via OpenRouter, every response fails with "incomplete turn detected: payloads=0" and the user sees "Root Cause: The stream parser in
src/agents/openai-transport-stream.tshandledreasoning_content,reasoning, andreasoning_textbut notreasoning_details— which Qwen3 returns via OpenRouter. The unhandled field caused zero content blocks to be assembled, triggering the incomplete turn path.Solution: Add handling for
reasoning_detailsarray. Extract text from items withtype: "reasoning.text"and treat as thinking content, enabling successful turn completion.Additional defensive fixes:
?? ""guards insrc/channels/plugins/setup-wizard.ts:458andsrc/channels/plugins/setup-wizard-helpers.ts:994to prevent potential undefined trim errors from clack wrapper edge cases (belt-and-suspenders approach mentioned in issue comments).Changes
reasoning_detailshandling inprocessOpenAICompletionsStream, extracting reasoning text from array items and creating thinking blocks.Test plan
handles reasoning_details from OpenRouter/Qwen3 in completions streampassesopenai-transport-stream.test.tstests passAI Code Generation Disclosure
Environment
Checklist