fix: sanitizeHistoryForProvider now repairs orphaned tool_use blocks

Calls repairOrphanedToolPairs as the first step so that assistant messages
with tool_calls that lack matching tool_results get synthetic results
injected. This prevents Anthropic API 400 errors.

Fixes #475
This commit is contained in:
Rahul Bansal 2026-02-21 11:32:25 +05:30
parent 46a5dde93a
commit 3664ab3914
2 changed files with 22 additions and 0 deletions

View file

@ -221,6 +221,9 @@ func sanitizeHistoryForProvider(history []providers.Message) []providers.Message
return history return history
} }
// Repair orphaned tool_use/tool_result pairs before further sanitization.
history = repairOrphanedToolPairs(history)
sanitized := make([]providers.Message, 0, len(history)) sanitized := make([]providers.Message, 0, len(history))
for _, msg := range history { for _, msg := range history {
switch msg.Role { switch msg.Role {

View file

@ -78,6 +78,25 @@ func TestRepairOrphanedToolPairs_OrphanToolResultDropped(t *testing.T) {
} }
} }
func TestSanitizeHistoryForProvider_OrphanToolUseRepaired(t *testing.T) {
history := []providers.Message{
{Role: "user", Content: "do something"},
{
Role: "assistant", Content: "calling tool",
ToolCalls: []providers.ToolCall{{ID: "call_99", Name: "exec"}},
},
}
sanitized := sanitizeHistoryForProvider(history)
if len(sanitized) != 3 {
t.Fatalf("expected 3 messages, got %d", len(sanitized))
}
if sanitized[2].Role != "tool" || sanitized[2].ToolCallID != "call_99" {
t.Errorf("expected synthetic tool_result for call_99, got role=%q id=%q",
sanitized[2].Role, sanitized[2].ToolCallID)
}
}
func TestRepairOrphanedToolPairs_EmptyInput(t *testing.T) { func TestRepairOrphanedToolPairs_EmptyInput(t *testing.T) {
repaired := repairOrphanedToolPairs(nil) repaired := repairOrphanedToolPairs(nil)
if len(repaired) != 0 { if len(repaired) != 0 {