Skip to content

Commit 3a3d80c

Browse files
intel352claude
andcommitted
fix: address 3 code review issues in Anthropic providers
1. Assistant ToolCalls now included in message conversion — creates ToolUseBlock for each ToolCall alongside text content. Multi-turn tool use conversations now work correctly. 2. JSON unmarshal errors for tool arguments are no longer silently swallowed — errors are surfaced in response content (Chat) or as error events (Stream) instead of producing nil Arguments. 3. foundryReadSSE now checks scanner.Err() after loop — large payloads exceeding 64KB buffer no longer cause silent stream termination. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent edc6d65 commit 3a3d80c

5 files changed

Lines changed: 38 additions & 9 deletions

File tree

provider/anthropic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (p *AnthropicProvider) Chat(ctx context.Context, messages []Message, tools
7171
if err != nil {
7272
return nil, fmt.Errorf("anthropic: %w", err)
7373
}
74-
return fromAnthropicMessage(msg), nil
74+
return fromAnthropicMessage(msg)
7575
}
7676

7777
func (p *AnthropicProvider) Stream(ctx context.Context, messages []Message, tools []ToolDef) (<-chan StreamEvent, error) {

provider/anthropic_bedrock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (p *anthropicBedrockProvider) Chat(ctx context.Context, messages []Message,
102102
if err != nil {
103103
return nil, fmt.Errorf("anthropic_bedrock: %w", err)
104104
}
105-
return fromAnthropicMessage(msg), nil
105+
return fromAnthropicMessage(msg)
106106
}
107107

108108
func (p *anthropicBedrockProvider) Stream(ctx context.Context, messages []Message, tools []ToolDef) (<-chan StreamEvent, error) {

provider/anthropic_convert.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package provider
33
import (
44
"bytes"
55
"encoding/json"
6+
"fmt"
67
"strings"
78

89
anthropic "github.com/anthropics/anthropic-sdk-go"
@@ -27,8 +28,25 @@ func toAnthropicParams(model string, maxTokens int, messages []Message, tools []
2728
continue
2829
}
2930
if msg.Role == RoleAssistant {
31+
var blocks []anthropic.ContentBlockParamUnion
32+
if msg.Content != "" {
33+
blocks = append(blocks, anthropic.NewTextBlock(msg.Content))
34+
}
35+
for _, tc := range msg.ToolCalls {
36+
inputJSON, _ := json.Marshal(tc.Arguments)
37+
blocks = append(blocks, anthropic.ContentBlockParamUnion{
38+
OfToolUse: &anthropic.ToolUseBlockParam{
39+
ID: tc.ID,
40+
Name: tc.Name,
41+
Input: inputJSON,
42+
},
43+
})
44+
}
45+
if len(blocks) == 0 {
46+
blocks = append(blocks, anthropic.NewTextBlock(""))
47+
}
3048
params.Messages = append(params.Messages,
31-
anthropic.NewAssistantMessage(anthropic.NewTextBlock(msg.Content)),
49+
anthropic.NewAssistantMessage(blocks...),
3250
)
3351
} else {
3452
params.Messages = append(params.Messages,
@@ -55,7 +73,7 @@ func toAnthropicParams(model string, maxTokens int, messages []Message, tools []
5573
}
5674

5775
// fromAnthropicMessage converts an SDK Message to a provider Response.
58-
func fromAnthropicMessage(msg *anthropic.Message) *Response {
76+
func fromAnthropicMessage(msg *anthropic.Message) (*Response, error) {
5977
resp := &Response{
6078
Usage: Usage{
6179
InputTokens: int(msg.Usage.InputTokens),
@@ -70,7 +88,9 @@ func fromAnthropicMessage(msg *anthropic.Message) *Response {
7088
case "tool_use":
7189
var args map[string]any
7290
if len(block.Input) > 0 {
73-
_ = json.Unmarshal(block.Input, &args)
91+
if err := json.Unmarshal(block.Input, &args); err != nil {
92+
return nil, fmt.Errorf("anthropic: unmarshal tool call arguments for %q: %w", block.Name, err)
93+
}
7494
}
7595
resp.ToolCalls = append(resp.ToolCalls, ToolCall{
7696
ID: block.ID,
@@ -80,7 +100,7 @@ func fromAnthropicMessage(msg *anthropic.Message) *Response {
80100
}
81101
}
82102
resp.Content = strings.Join(textParts, "")
83-
return resp
103+
return resp, nil
84104
}
85105

86106
// streamAnthropicEvents reads SDK stream events and sends them to ch.
@@ -120,7 +140,10 @@ func streamAnthropicEvents(stream *ssestream.Stream[anthropic.MessageStreamEvent
120140
if currentToolID != "" {
121141
var args map[string]any
122142
if toolInputBuf.Len() > 0 {
123-
_ = json.Unmarshal(toolInputBuf.Bytes(), &args)
143+
if err := json.Unmarshal(toolInputBuf.Bytes(), &args); err != nil {
144+
ch <- StreamEvent{Type: "error", Error: fmt.Sprintf("anthropic: unmarshal tool call arguments for %q: %v", currentToolName, err)}
145+
return
146+
}
124147
}
125148
ch <- StreamEvent{
126149
Type: "tool_call",

provider/anthropic_foundry.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,9 @@ func foundryReadSSE(body io.ReadCloser, ch chan<- StreamEvent) {
306306
if currentToolID != "" {
307307
var args map[string]any
308308
if toolInputBuf.Len() > 0 {
309-
_ = json.Unmarshal(toolInputBuf.Bytes(), &args)
309+
if err := json.Unmarshal(toolInputBuf.Bytes(), &args); err != nil {
310+
ch <- StreamEvent{Type: "error", Error: fmt.Sprintf("tool %s: malformed arguments: %v", currentToolName, err)}
311+
}
310312
}
311313
ch <- StreamEvent{
312314
Type: "tool_call",
@@ -335,4 +337,8 @@ func foundryReadSSE(body io.ReadCloser, ch chan<- StreamEvent) {
335337
return
336338
}
337339
}
340+
341+
if err := scanner.Err(); err != nil {
342+
ch <- StreamEvent{Type: "error", Error: fmt.Sprintf("foundry stream read: %v", err)}
343+
}
338344
}

provider/anthropic_vertex.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func (p *anthropicVertexProvider) Chat(ctx context.Context, messages []Message,
116116
if err != nil {
117117
return nil, fmt.Errorf("anthropic_vertex: %w", err)
118118
}
119-
return fromAnthropicMessage(msg), nil
119+
return fromAnthropicMessage(msg)
120120
}
121121

122122
func (p *anthropicVertexProvider) Stream(ctx context.Context, messages []Message, tools []ToolDef) (<-chan StreamEvent, error) {

0 commit comments

Comments
 (0)