enforce pending-call matching to block orphan and duplicate function call outputs

This commit is contained in:
DBT
2026-02-24 10:46:42 +00:00
parent 8dd8eb23dc
commit 61d5c1b4c8

View File

@@ -106,9 +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{}{} pendingCalls := map[string]struct{}{}
for _, msg := range messages { for _, msg := range messages {
input = append(input, toResponsesInputItemsWithState(msg, seenCalls)...) input = append(input, toResponsesInputItemsWithState(msg, pendingCalls)...)
} }
requestBody := map[string]interface{}{ requestBody := map[string]interface{}{
"model": model, "model": model,
@@ -211,7 +211,7 @@ func toResponsesInputItems(msg Message) []map[string]interface{} {
return toResponsesInputItemsWithState(msg, nil) return toResponsesInputItemsWithState(msg, nil)
} }
func toResponsesInputItemsWithState(msg Message, seenCalls map[string]struct{}) []map[string]interface{} { func toResponsesInputItemsWithState(msg Message, pendingCalls 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":
@@ -252,8 +252,8 @@ func toResponsesInputItemsWithState(msg Message, seenCalls map[string]struct{})
argsRaw = string(argsJSON) argsRaw = string(argsJSON)
} }
} }
if seenCalls != nil { if pendingCalls != nil {
seenCalls[callID] = struct{}{} pendingCalls[callID] = struct{}{}
} }
items = append(items, map[string]interface{}{ items = append(items, map[string]interface{}{
"type": "function_call", "type": "function_call",
@@ -271,11 +271,12 @@ func toResponsesInputItemsWithState(msg Message, seenCalls map[string]struct{})
if callID == "" { 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 pendingCalls != nil {
if _, ok := seenCalls[callID]; !ok { if _, ok := pendingCalls[callID]; !ok {
// Avoid invalid orphan tool outputs in /responses payload. // Avoid invalid orphan/duplicate tool outputs in /responses payload.
return []map[string]interface{}{responsesMessageItem("user", msg.Content, "input_text")} return []map[string]interface{}{responsesMessageItem("user", msg.Content, "input_text")}
} }
delete(pendingCalls, callID)
} }
return []map[string]interface{}{map[string]interface{}{ return []map[string]interface{}{map[string]interface{}{
"type": "function_call_output", "type": "function_call_output",
@@ -696,9 +697,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{}{} pendingCalls := map[string]struct{}{}
for _, msg := range messages { for _, msg := range messages {
input = append(input, toResponsesInputItemsWithState(msg, seenCalls)...) input = append(input, toResponsesInputItemsWithState(msg, pendingCalls)...)
} }
if len(input) == 0 { if len(input) == 0 {
return strings.TrimSpace(existingSummary), nil return strings.TrimSpace(existingSummary), nil