fix: extract malformed tool calls from GLM-4.7 reasoning blocks #6883
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes a bug where GLM-4.7 occasionally outputs tool call XML tags directly within the
reasoning_contentthinking block instead of using the proper separatetool_callsfield. This malformed output causes tool calls to not be executed properly.Problem Statement
GLM-4.7 with interleaved thinking mode sometimes "leaks" tool call syntax into the reasoning/thinking block:
Example Malformed Output
Or with MCP tools:
Key Issue: These malformed tool calls in
reasoning_contentare stored as text and never executed. The client receives them as thinking text rather than executable tool calls.Root Cause Analysis
Enhanced Thinking Mechanism: GLM-4.7 implements a "think before acting" mechanism that sometimes causes tool call syntax to leak into the thinking block
Interleaved Thinking Complexity: When the model thinks before each tool call, there's a higher chance of thinking content "leaking" tool call syntax
API Processing: The OpenAI-compatible SDK looks for
function_callitems in response output, but malformed tool calls inreasoning_contentare just stored as text and never executedProviderTransform Behavior: When
interleaved.field === "reasoning_content", the transform moves reasoning text but doesn't extract tool calls from malformed reasoningSession Evidence
Real-world example from a development session:
pal_thinkdeepis an MCP server tool, NOT a built-in reasoning mechanism🚨 Alert: Sanitized Session Logs for GLM-4.7 Malformed Tool Calls
📢 Callout: These logs prove the issue where GLM-4.7 embeds malformed XML tool calls in reasoning blocks. The fix extracts and parses these into proper tool-call parts, as shown in the test cases. All sensitive data (paths, IDs) has been sanitized with placeholders.
🔍 View Sanitized Session Logs Hierarchy
Solution Architecture
Location:
packages/opencode/src/provider/transform.tsAdded GLM-specific normalization in
ProviderTransform.normalizeMessages(), following existing patterns for Claude and Mistral normalization:reasoning_contentusing regex pattern matching<tool_call>and<invoke>tags)tool-callparts in the messageTesting
Comprehensive test suite in
packages/opencode/test/provider/test_glm47_thinking_fix.test.ts:✅ Single tool call XML in reasoning - Extracts properly formatted bash command from malformed reasoning
✅ Multiple tool calls in reasoning - Extracts all tool invocations
✅ MCP tools in reasoning - Handles pal_thinkdeep and other MCP server tools correctly
✅ Properly formatted responses - Preserves existing structure without modification
Test Coverage
<invoke name="bash">patterns<tool_call>with multiple argumentsRisk Assessment
Low Risk:
References