package ai import ( "encoding/json" "fmt" ) func repairChatCompletionBody(body []byte) ([]byte, error) { var req map[string]any if err := json.Unmarshal(body, &req); err != nil { return body, fmt.Errorf("repair chat completion body: %w", err) } rawMessages, ok := req["messages"].([]any) if !ok || len(rawMessages) == 0 { return body, nil } repaired := repairToolMessages(rawMessages) if sameMessages(rawMessages, repaired) { return body, nil } req["messages"] = repaired return json.Marshal(req) } func repairToolMessages(messages []any) []any { out := make([]any, 0, len(messages)+1) for _, raw := range messages { msg, ok := raw.(map[string]any) if !ok { out = append(out, raw) continue } if messageRole(msg) == "tool" && !previousMessageHasToolCalls(out) { toolCallID := messageToolCallID(msg) if toolCallID == "" { toolCallID = "call_repaired" } out = append(out, map[string]any{ "role": "assistant", "content": "", "tool_calls": []any{ map[string]any{ "id": toolCallID, "type": "function", "function": map[string]any{ "name": "tool", "arguments": "{}", }, }, }, }) } out = append(out, msg) } return out } func previousMessageHasToolCalls(messages []any) bool { if len(messages) == 0 { return false } msg, ok := messages[len(messages)-1].(map[string]any) if !ok { return false } if messageRole(msg) != "assistant" { return false } toolCalls, ok := msg["tool_calls"].([]any) return ok && len(toolCalls) > 0 } func messageRole(msg map[string]any) string { role, _ := msg["role"].(string) return role } func messageToolCallID(msg map[string]any) string { id, _ := msg["tool_call_id"].(string) return id } func sameMessages(before, after []any) bool { if len(before) != len(after) { return false } b, err := json.Marshal(before) if err != nil { return false } a, err := json.Marshal(after) if err != nil { return false } return string(b) == string(a) } func chatCompletionStreamRequested(body []byte) bool { var probe struct { Stream *bool `json:"stream"` } if err := json.Unmarshal(body, &probe); err != nil { return false } return probe.Stream != nil && *probe.Stream }