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
This commit is contained in:
mxrain 2026-02-14 14:14:31 +08:00
parent 5872e0f55e
commit 39998a6618
3 changed files with 20 additions and 15 deletions

View file

@ -480,8 +480,9 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, messages []providers.M
// Build assistant message with tool calls // Build assistant message with tool calls
assistantMsg := providers.Message{ assistantMsg := providers.Message{
Role: "assistant", Role: "assistant",
Content: response.Content, Content: response.Content,
ReasoningContent: response.ReasoningContent, // Preserve for thinking models (e.g., GLM-Z1)
} }
for _, tc := range response.ToolCalls { for _, tc := range response.ToolCalls {
argumentsJSON, _ := json.Marshal(tc.Arguments) argumentsJSON, _ := json.Marshal(tc.Arguments)

View file

@ -126,7 +126,8 @@ func (p *HTTPProvider) parseResponse(body []byte) (*LLMResponse, error) {
var apiResponse struct { var apiResponse struct {
Choices []struct { Choices []struct {
Message 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 { ToolCalls []struct {
ID string `json:"id"` ID string `json:"id"`
Type string `json:"type"` Type string `json:"type"`
@ -185,10 +186,11 @@ func (p *HTTPProvider) parseResponse(body []byte) (*LLMResponse, error) {
} }
return &LLMResponse{ return &LLMResponse{
Content: choice.Message.Content, Content: choice.Message.Content,
ToolCalls: toolCalls, ReasoningContent: choice.Message.ReasoningContent,
FinishReason: choice.FinishReason, ToolCalls: toolCalls,
Usage: apiResponse.Usage, FinishReason: choice.FinishReason,
Usage: apiResponse.Usage,
}, nil }, nil
} }

View file

@ -16,10 +16,11 @@ type FunctionCall struct {
} }
type LLMResponse struct { type LLMResponse struct {
Content string `json:"content"` Content string `json:"content"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"` ReasoningContent string `json:"reasoning_content,omitempty"` // For thinking models (e.g., GLM-Z1)
FinishReason string `json:"finish_reason"` ToolCalls []ToolCall `json:"tool_calls,omitempty"`
Usage *UsageInfo `json:"usage,omitempty"` FinishReason string `json:"finish_reason"`
Usage *UsageInfo `json:"usage,omitempty"`
} }
type UsageInfo struct { type UsageInfo struct {
@ -29,10 +30,11 @@ type UsageInfo struct {
} }
type Message struct { type Message struct {
Role string `json:"role"` Role string `json:"role"`
Content string `json:"content"` Content string `json:"content"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"` ReasoningContent string `json:"reasoning_content,omitempty"` // For thinking models (e.g., GLM-Z1)
ToolCallID string `json:"tool_call_id,omitempty"` ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
} }
type LLMProvider interface { type LLMProvider interface {