feat: update providers and agent loop

This commit is contained in:
mxrain 2026-02-13 00:42:38 +08:00
parent 7e2050345f
commit 1d0e9b7b29
4 changed files with 80 additions and 23 deletions

View file

@ -380,6 +380,7 @@ func (al *AgentLoop) runLLMIteration(ctx context.Context, messages []providers.M
assistantMsg := providers.Message{ assistantMsg := providers.Message{
Role: "assistant", Role: "assistant",
Content: response.Content, Content: response.Content,
ThinkingContent: response.ThinkingContent,
} }
for _, tc := range response.ToolCalls { for _, tc := range response.ToolCalls {
argumentsJSON, _ := json.Marshal(tc.Arguments) argumentsJSON, _ := json.Marshal(tc.Arguments)

View file

@ -77,6 +77,14 @@ func buildClaudeParams(messages []Message, tools []ToolDefinition, model string,
case "assistant": case "assistant":
if len(msg.ToolCalls) > 0 { if len(msg.ToolCalls) > 0 {
var blocks []anthropic.ContentBlockParamUnion var blocks []anthropic.ContentBlockParamUnion
// Add thinking block if present (required for extended thinking mode)
if msg.ThinkingContent != "" {
blocks = append(blocks, anthropic.ContentBlockParamUnion{
OfThinking: &anthropic.ThinkingBlockParam{
Thinking: msg.ThinkingContent,
},
})
}
if msg.Content != "" { if msg.Content != "" {
blocks = append(blocks, anthropic.NewTextBlock(msg.Content)) blocks = append(blocks, anthropic.NewTextBlock(msg.Content))
} }
@ -85,9 +93,16 @@ func buildClaudeParams(messages []Message, tools []ToolDefinition, model string,
} }
anthropicMessages = append(anthropicMessages, anthropic.NewAssistantMessage(blocks...)) anthropicMessages = append(anthropicMessages, anthropic.NewAssistantMessage(blocks...))
} else { } else {
anthropicMessages = append(anthropicMessages, var blocks []anthropic.ContentBlockParamUnion
anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content)), if msg.ThinkingContent != "" {
) blocks = append(blocks, anthropic.ContentBlockParamUnion{
OfThinking: &anthropic.ThinkingBlockParam{
Thinking: msg.ThinkingContent,
},
})
}
blocks = append(blocks, anthropic.NewTextBlock(msg.Content))
anthropicMessages = append(anthropicMessages, anthropic.NewAssistantMessage(blocks...))
} }
case "tool": case "tool":
anthropicMessages = append(anthropicMessages, anthropicMessages = append(anthropicMessages,
@ -150,10 +165,14 @@ func translateToolsForClaude(tools []ToolDefinition) []anthropic.ToolUnionParam
func parseClaudeResponse(resp *anthropic.Message) *LLMResponse { func parseClaudeResponse(resp *anthropic.Message) *LLMResponse {
var content string var content string
var thinkingContent string
var toolCalls []ToolCall var toolCalls []ToolCall
for _, block := range resp.Content { for _, block := range resp.Content {
switch block.Type { switch block.Type {
case "thinking":
tb := block.AsThinking()
thinkingContent += tb.Thinking
case "text": case "text":
tb := block.AsText() tb := block.AsText()
content += tb.Text content += tb.Text
@ -183,6 +202,7 @@ func parseClaudeResponse(resp *anthropic.Message) *LLMResponse {
return &LLMResponse{ return &LLMResponse{
Content: content, Content: content,
ThinkingContent: thinkingContent,
ToolCalls: toolCalls, ToolCalls: toolCalls,
FinishReason: finishReason, FinishReason: finishReason,
Usage: &UsageInfo{ Usage: &UsageInfo{

View file

@ -60,9 +60,41 @@ func (p *HTTPProvider) Chat(ctx context.Context, messages []Message, tools []Too
} }
} }
// Convert messages to API format, including reasoning_content for thinking models
apiMessages := make([]map[string]interface{}, 0, len(messages))
for _, msg := range messages {
apiMsg := map[string]interface{}{
"role": msg.Role,
"content": msg.Content,
}
// Include reasoning_content for assistant messages with thinking content
if msg.Role == "assistant" && msg.ThinkingContent != "" {
apiMsg["reasoning_content"] = msg.ThinkingContent
}
if len(msg.ToolCalls) > 0 {
toolCalls := make([]map[string]interface{}, 0, len(msg.ToolCalls))
for _, tc := range msg.ToolCalls {
argsJSON, _ := json.Marshal(tc.Arguments)
toolCalls = append(toolCalls, map[string]interface{}{
"id": tc.ID,
"type": "function",
"function": map[string]interface{}{
"name": tc.Name,
"arguments": string(argsJSON),
},
})
}
apiMsg["tool_calls"] = toolCalls
}
if msg.ToolCallID != "" {
apiMsg["tool_call_id"] = msg.ToolCallID
}
apiMessages = append(apiMessages, apiMsg)
}
requestBody := map[string]interface{}{ requestBody := map[string]interface{}{
"model": model, "model": model,
"messages": messages, "messages": apiMessages,
} }
if len(tools) > 0 { if len(tools) > 0 {
@ -127,6 +159,7 @@ func (p *HTTPProvider) parseResponse(body []byte) (*LLMResponse, error) {
Choices []struct { Choices []struct {
Message struct { Message struct {
Content string `json:"content"` Content string `json:"content"`
ReasoningContent string `json:"reasoning_content"`
ToolCalls []struct { ToolCalls []struct {
ID string `json:"id"` ID string `json:"id"`
Type string `json:"type"` Type string `json:"type"`
@ -186,6 +219,7 @@ func (p *HTTPProvider) parseResponse(body []byte) (*LLMResponse, error) {
return &LLMResponse{ return &LLMResponse{
Content: choice.Message.Content, Content: choice.Message.Content,
ThinkingContent: choice.Message.ReasoningContent,
ToolCalls: toolCalls, ToolCalls: toolCalls,
FinishReason: choice.FinishReason, FinishReason: choice.FinishReason,
Usage: apiResponse.Usage, Usage: apiResponse.Usage,

View file

@ -20,6 +20,7 @@ type LLMResponse struct {
ToolCalls []ToolCall `json:"tool_calls,omitempty"` ToolCalls []ToolCall `json:"tool_calls,omitempty"`
FinishReason string `json:"finish_reason"` FinishReason string `json:"finish_reason"`
Usage *UsageInfo `json:"usage,omitempty"` Usage *UsageInfo `json:"usage,omitempty"`
ThinkingContent string `json:"thinking_content,omitempty"` // For Claude extended thinking
} }
type UsageInfo struct { type UsageInfo struct {
@ -33,6 +34,7 @@ type Message struct {
Content string `json:"content"` Content string `json:"content"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"` ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"` ToolCallID string `json:"tool_call_id,omitempty"`
ThinkingContent string `json:"thinking_content,omitempty"` // For Claude extended thinking
} }
type LLMProvider interface { type LLMProvider interface {