From 39998a6618b8a5405a536cd02c3573f0077786e8 Mon Sep 17 00:00:00 2001 From: mxrain Date: Sat, 14 Feb 2026 14:14:31 +0800 Subject: [PATCH] fix: preserve reasoning_content for thinking models in multi-turn conversations GLM-Z1 and other thinking models require reasoning_content to be preserved across tool call iterations. Without this, the API returns: 'thinking is enabled but reasoning_content is missing in assistant tool call message' Changes: - Add ReasoningContent field to LLMResponse and Message types - Parse reasoning_content from API response - Preserve ReasoningContent when building assistant messages with tool calls --- pkg/agent/loop.go | 5 +++-- pkg/providers/http_provider.go | 12 +++++++----- pkg/providers/types.go | 18 ++++++++++-------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index ac8da9ffd..c7a25bf65 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -480,8 +480,9 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, messages []providers.M // Build assistant message with tool calls assistantMsg := providers.Message{ - Role: "assistant", - Content: response.Content, + Role: "assistant", + Content: response.Content, + ReasoningContent: response.ReasoningContent, // Preserve for thinking models (e.g., GLM-Z1) } for _, tc := range response.ToolCalls { argumentsJSON, _ := json.Marshal(tc.Arguments) diff --git a/pkg/providers/http_provider.go b/pkg/providers/http_provider.go index fc78a182f..e52001910 100644 --- a/pkg/providers/http_provider.go +++ b/pkg/providers/http_provider.go @@ -126,7 +126,8 @@ func (p *HTTPProvider) parseResponse(body []byte) (*LLMResponse, error) { var apiResponse struct { Choices []struct { Message struct { - Content string `json:"content"` + Content string `json:"content"` + ReasoningContent string `json:"reasoning_content"` // For thinking models (e.g., GLM-Z1) ToolCalls []struct { ID string `json:"id"` Type string `json:"type"` @@ -185,10 +186,11 @@ func (p *HTTPProvider) parseResponse(body []byte) (*LLMResponse, error) { } return &LLMResponse{ - Content: choice.Message.Content, - ToolCalls: toolCalls, - FinishReason: choice.FinishReason, - Usage: apiResponse.Usage, + Content: choice.Message.Content, + ReasoningContent: choice.Message.ReasoningContent, + ToolCalls: toolCalls, + FinishReason: choice.FinishReason, + Usage: apiResponse.Usage, }, nil } diff --git a/pkg/providers/types.go b/pkg/providers/types.go index 88b62e975..11917806b 100644 --- a/pkg/providers/types.go +++ b/pkg/providers/types.go @@ -16,10 +16,11 @@ type FunctionCall struct { } type LLMResponse struct { - Content string `json:"content"` - ToolCalls []ToolCall `json:"tool_calls,omitempty"` - FinishReason string `json:"finish_reason"` - Usage *UsageInfo `json:"usage,omitempty"` + Content string `json:"content"` + ReasoningContent string `json:"reasoning_content,omitempty"` // For thinking models (e.g., GLM-Z1) + ToolCalls []ToolCall `json:"tool_calls,omitempty"` + FinishReason string `json:"finish_reason"` + Usage *UsageInfo `json:"usage,omitempty"` } type UsageInfo struct { @@ -29,10 +30,11 @@ type UsageInfo struct { } type Message struct { - Role string `json:"role"` - Content string `json:"content"` - ToolCalls []ToolCall `json:"tool_calls,omitempty"` - ToolCallID string `json:"tool_call_id,omitempty"` + Role string `json:"role"` + Content string `json:"content"` + ReasoningContent string `json:"reasoning_content,omitempty"` // For thinking models (e.g., GLM-Z1) + ToolCalls []ToolCall `json:"tool_calls,omitempty"` + ToolCallID string `json:"tool_call_id,omitempty"` } type LLMProvider interface {