fix responses payload by filtering orphan tool outputs without matching call ids

This commit is contained in:
DBT
2026-02-24 09:50:11 +00:00
parent dd705e5e93
commit 4d3cf7024c

View File

@@ -106,8 +106,9 @@ func (p *HTTPProvider) callChatCompletions(ctx context.Context, messages []Messa
func (p *HTTPProvider) callResponses(ctx context.Context, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}) ([]byte, int, string, error) { func (p *HTTPProvider) callResponses(ctx context.Context, messages []Message, tools []ToolDefinition, model string, options map[string]interface{}) ([]byte, int, string, error) {
input := make([]map[string]interface{}, 0, len(messages)) input := make([]map[string]interface{}, 0, len(messages))
seenCalls := map[string]struct{}{}
for _, msg := range messages { for _, msg := range messages {
input = append(input, toResponsesInputItems(msg)...) input = append(input, toResponsesInputItemsWithState(msg, seenCalls)...)
} }
requestBody := map[string]interface{}{ requestBody := map[string]interface{}{
"model": model, "model": model,
@@ -207,6 +208,10 @@ func toChatCompletionsContent(msg Message) []map[string]interface{} {
} }
func toResponsesInputItems(msg Message) []map[string]interface{} { func toResponsesInputItems(msg Message) []map[string]interface{} {
return toResponsesInputItemsWithState(msg, nil)
}
func toResponsesInputItemsWithState(msg Message, seenCalls map[string]struct{}) []map[string]interface{} {
role := strings.ToLower(strings.TrimSpace(msg.Role)) role := strings.ToLower(strings.TrimSpace(msg.Role))
switch role { switch role {
case "system", "developer", "user": case "system", "developer", "user":
@@ -228,6 +233,9 @@ func toResponsesInputItems(msg Message) []map[string]interface{} {
if callID == "" { if callID == "" {
continue continue
} }
if seenCalls != nil {
seenCalls[callID] = struct{}{}
}
name := strings.TrimSpace(tc.Name) name := strings.TrimSpace(tc.Name)
argsRaw := "" argsRaw := ""
if tc.Function != nil { if tc.Function != nil {
@@ -259,12 +267,19 @@ func toResponsesInputItems(msg Message) []map[string]interface{} {
} }
return items return items
case "tool": case "tool":
if strings.TrimSpace(msg.ToolCallID) == "" { callID := strings.TrimSpace(msg.ToolCallID)
if callID == "" {
return []map[string]interface{}{responsesMessageItem("user", msg.Content, "input_text")} return []map[string]interface{}{responsesMessageItem("user", msg.Content, "input_text")}
} }
if seenCalls != nil {
if _, ok := seenCalls[callID]; !ok {
// Avoid invalid orphan tool outputs in /responses payload.
return []map[string]interface{}{responsesMessageItem("user", msg.Content, "input_text")}
}
}
return []map[string]interface{}{map[string]interface{}{ return []map[string]interface{}{map[string]interface{}{
"type": "function_call_output", "type": "function_call_output",
"call_id": msg.ToolCallID, "call_id": callID,
"output": msg.Content, "output": msg.Content,
}} }}
default: default:
@@ -681,8 +696,9 @@ func (p *HTTPProvider) BuildSummaryViaResponsesCompact(ctx context.Context, mode
if strings.TrimSpace(existingSummary) != "" { if strings.TrimSpace(existingSummary) != "" {
input = append(input, responsesMessageItem("system", "Existing summary:\n"+strings.TrimSpace(existingSummary), "input_text")) input = append(input, responsesMessageItem("system", "Existing summary:\n"+strings.TrimSpace(existingSummary), "input_text"))
} }
seenCalls := map[string]struct{}{}
for _, msg := range messages { for _, msg := range messages {
input = append(input, toResponsesInputItems(msg)...) input = append(input, toResponsesInputItemsWithState(msg, seenCalls)...)
} }
if len(input) == 0 { if len(input) == 0 {
return strings.TrimSpace(existingSummary), nil return strings.TrimSpace(existingSummary), nil