fix(agent): drop malformed empty-id tool call rounds
This commit is contained in:
parent
da6a862079
commit
2f885c6133
2 changed files with 33 additions and 4 deletions
|
|
@ -696,8 +696,10 @@ func sanitizeHistoryForProvider(history []providers.Message) []providers.Message
|
||||||
// within the same assistant turn.
|
// within the same assistant turn.
|
||||||
expectedOrder := make([]string, 0, len(msg.ToolCalls))
|
expectedOrder := make([]string, 0, len(msg.ToolCalls))
|
||||||
expectedSet := make(map[string]struct{}, len(msg.ToolCalls))
|
expectedSet := make(map[string]struct{}, len(msg.ToolCalls))
|
||||||
|
hasEmptyToolCallID := false
|
||||||
for _, tc := range msg.ToolCalls {
|
for _, tc := range msg.ToolCalls {
|
||||||
if tc.ID == "" {
|
if tc.ID == "" {
|
||||||
|
hasEmptyToolCallID = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, exists := expectedSet[tc.ID]; exists {
|
if _, exists := expectedSet[tc.ID]; exists {
|
||||||
|
|
@ -707,10 +709,21 @@ func sanitizeHistoryForProvider(history []providers.Message) []providers.Message
|
||||||
expectedOrder = append(expectedOrder, tc.ID)
|
expectedOrder = append(expectedOrder, tc.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If provider output is malformed and tool calls have no IDs, keep the
|
// A tool-call round with any empty tool_call ID is malformed. Drop the
|
||||||
// assistant turn as-is and let provider-level validation handle it.
|
// entire round, including any contiguous tool results, so we do not
|
||||||
if len(expectedOrder) == 0 {
|
// preserve provider-invalid history.
|
||||||
final = append(final, msg)
|
if hasEmptyToolCallID {
|
||||||
|
logger.DebugCF("agent", "Dropping assistant tool-call round with empty tool_call ID", map[string]any{
|
||||||
|
"tool_call_count": len(msg.ToolCalls),
|
||||||
|
})
|
||||||
|
|
||||||
|
j := i + 1
|
||||||
|
for ; j < len(sanitized); j++ {
|
||||||
|
if sanitized[j].Role != "tool" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = j - 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -335,3 +335,19 @@ func TestSanitizeHistoryForProvider_ReusedToolCallIDAcrossRounds(t *testing.T) {
|
||||||
t.Fatalf("expected both rounds to keep tool_call_id call_1, got %q and %q", result[2].ToolCallID, result[6].ToolCallID)
|
t.Fatalf("expected both rounds to keep tool_call_id call_1, got %q and %q", result[2].ToolCallID, result[6].ToolCallID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSanitizeHistoryForProvider_DropsMalformedMixedToolCallIDs(t *testing.T) {
|
||||||
|
history := []providers.Message{
|
||||||
|
msg("user", "do two things"),
|
||||||
|
assistantWithTools("A", ""),
|
||||||
|
toolResult("A"),
|
||||||
|
msg("user", "next question"),
|
||||||
|
msg("assistant", "answer"),
|
||||||
|
}
|
||||||
|
|
||||||
|
result := sanitizeHistoryForProvider(history)
|
||||||
|
if len(result) != 3 {
|
||||||
|
t.Fatalf("expected 3 messages, got %d: %+v", len(result), roles(result))
|
||||||
|
}
|
||||||
|
assertRoles(t, result, "user", "user", "assistant")
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue