fix(providers/anthropic): drop empty assistant messages
When using the Anthropic API, it expects `tool_result` blocks in a `user` message to immediately follow the `tool_use` block from the preceding `assistant` message. However, the agent's logic can sometimes produce empty `assistant` messages (with no text and no tool calls) between consecutive tool calls. This commit updates `buildRequestBody` in the Anthropic Messages provider to skip empty `assistant` messages, ensuring that the merged `user` tool results correctly follow their originating `assistant` tool uses without interruption. Co-authored-by: TanLuong <28281768+TanLuong@users.noreply.github.com>
This commit is contained in:
parent
ff7095e88e
commit
aa2eac9741
2 changed files with 37 additions and 0 deletions
|
|
@ -245,6 +245,14 @@ func buildRequestBody(
|
|||
content = append(content, toolUse)
|
||||
}
|
||||
|
||||
// Skip empty assistant messages that have no content and no valid tool calls.
|
||||
// This prevents breaking the Anthropic API requirement where a user message
|
||||
// containing tool results must immediately follow the assistant message
|
||||
// containing the corresponding tool calls.
|
||||
if len(content) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
apiMessages = append(apiMessages, map[string]any{
|
||||
"role": "assistant",
|
||||
"content": content,
|
||||
|
|
|
|||
|
|
@ -506,6 +506,25 @@ func TestBuildRequestBodyEdgeCases(t *testing.T) {
|
|||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "skip empty assistant messages",
|
||||
messages: []Message{
|
||||
{Role: "user", Content: "hello"},
|
||||
{Role: "assistant", Content: "", ToolCalls: []ToolCall{}}, // Should be skipped
|
||||
{Role: "assistant", Content: "valid message", ToolCalls: []ToolCall{}},
|
||||
{Role: "assistant", Content: "", ToolCalls: []ToolCall{
|
||||
{ID: "tool-valid", Name: "test_tool", Arguments: map[string]any{"arg": "value"}},
|
||||
}},
|
||||
{Role: "assistant", Content: "", ToolCalls: []ToolCall{
|
||||
{ID: "tool-empty", Name: "", Arguments: map[string]any{"ignored": true}},
|
||||
}}, // Should be skipped because tool call is empty and content is empty
|
||||
},
|
||||
model: "test-model",
|
||||
options: map[string]any{
|
||||
"max_tokens": 8192,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -558,6 +577,16 @@ func TestBuildRequestBodyEdgeCases(t *testing.T) {
|
|||
t.Fatalf("tool_use id = %v, want %q", gotID, "tool-valid")
|
||||
}
|
||||
}
|
||||
|
||||
if tt.name == "skip empty assistant messages" {
|
||||
messages, ok := got["messages"].([]any)
|
||||
if !ok {
|
||||
t.Fatalf("messages is not []any")
|
||||
}
|
||||
if len(messages) != 3 {
|
||||
t.Fatalf("expected 3 API messages (user, assistant with text, assistant with tool_use), got %d: %#v", len(messages), messages)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue