Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion provider/anthropicprovider/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,11 @@ func buildMessageParam(msg *message.Message) (anthropic.MessageParam, error) {
for _, c := range msg.Contents {
switch c := c.(type) {
case *message.TextContent:
content = append(content, anthropic.NewTextBlock(c.Text))
// Anthropic rejects empty text blocks, so skip them (the system
// path applies the same guard).
if c.Text != "" {
content = append(content, anthropic.NewTextBlock(c.Text))
}
case *message.TextReasoningContent:
// Replay a prior assistant thinking block so its signature travels
// back with the request (Anthropic emits reasoning before the rest of
Expand Down
21 changes: 21 additions & 0 deletions provider/anthropicprovider/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,27 @@ func TestAssistantUnsignedReasoningIsSkipped(t *testing.T) {
}
}

// Anthropic rejects empty text blocks, so a TextContent with no text must not be
// forwarded. The system path already guards this; the message path must too,
// matching the Python client which skips empty text blocks.
func TestEmptyTextContentIsSkipped(t *testing.T) {
msgs := []*message.Message{
{Role: message.RoleUser, Contents: message.Contents{&message.TextContent{Text: "hi"}}},
{Role: message.RoleAssistant, Contents: message.Contents{
&message.TextContent{Text: ""},
&message.TextContent{Text: "hello"},
}},
}

blocks := assistantBlocksFromRequest(t, msgs)
if len(blocks) != 1 {
t.Fatalf("assistant content blocks = %d, want 1 (empty text block should be skipped) (%#v)", len(blocks), blocks)
}
if blocks[0]["type"] != "text" || blocks[0]["text"] != "hello" {
t.Errorf("block = %#v, want a text block %q", blocks[0], "hello")
}
}

// The tool's input_schema sent to Anthropic must carry the additionalProperties
// keyword emitted by functool's strict schema. functool.Call validates decoded
// arguments against the resolved schema (additionalProperties:false), so if the
Expand Down
Loading