From 78a319d5fea7088a38a9fd3c8d85c4f701c58053 Mon Sep 17 00:00:00 2001 From: ZaneTung Date: Thu, 5 Mar 2026 17:19:31 +0800 Subject: [PATCH] refactor: complete tool call support in streaming response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完善工具调用支持的关键修复: - 在 content_block_start 时捕获工具调用的 ID 和 Name - 修复之前只初始化 Index 而忽略 ID/Name 的问题 - 改进空参数处理(使用空对象而非 panic) - 增强错误日志,包含实际参数内容以便调试 技术细节: - block.ContentBlock.ID -> currentToolCall.ID - block.ContentBlock.Name -> currentToolCall.Name - 处理空 JSON 字符串的情况 - 错误日志格式:包含 args 字符串便于诊断 这确保了需要工具调用的场景能够正常工作。 --- .gitignore | 3 +++ pkg/providers/anthropic/provider.go | 17 +++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 02ef18d1f..eb0bb96bf 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ dist/ # Windows Application Icon/Resource *.syso + +# ZCF workflow (local development) +.zcf/ diff --git a/pkg/providers/anthropic/provider.go b/pkg/providers/anthropic/provider.go index 47e1054d0..c3619b7b4 100644 --- a/pkg/providers/anthropic/provider.go +++ b/pkg/providers/anthropic/provider.go @@ -370,13 +370,15 @@ func parseStreamingResponse(stream *ssestream.Stream[anthropic.MessageStreamEven case "content_block_start": block := evt.AsContentBlockStart() if block.ContentBlock.Type == "tool_use" { - // 初始化工具调用 + // 初始化工具调用,捕获 ID 和 Name currentToolCall = &struct { ID string Name string Args strings.Builder Index int64 }{ + ID: block.ContentBlock.ID, + Name: block.ContentBlock.Name, Index: block.Index, } } @@ -397,11 +399,14 @@ func parseStreamingResponse(stream *ssestream.Stream[anthropic.MessageStreamEven case "content_block_stop": // 完成当前工具调用,添加到列表 - if currentToolCall != nil && currentToolCall.Name != "" { + if currentToolCall != nil { var args map[string]any argsStr := currentToolCall.Args.String() - if err := json.Unmarshal([]byte(argsStr), &args); err != nil { - log.Printf("anthropic: failed to decode tool call input for %q: %v", currentToolCall.Name, err) + if argsStr == "" { + // 如果没有参数,使用空对象 + args = make(map[string]any) + } else if err := json.Unmarshal([]byte(argsStr), &args); err != nil { + log.Printf("anthropic: failed to decode tool call input for %q: %v (args: %s)", currentToolCall.Name, err, argsStr) args = map[string]any{"raw": argsStr} } toolCalls = append(toolCalls, ToolCall{ @@ -419,10 +424,10 @@ func parseStreamingResponse(stream *ssestream.Stream[anthropic.MessageStreamEven usage.OutputTokens = msgDelta.Usage.OutputTokens case "message_stop": - // 消息完成,无需处理 + // 消息完成 case "error": - return nil, fmt.Errorf("stream error: %v", evt) + return nil, fmt.Errorf("stream error: type=%s, error=%v", evt.Type, evt) } }