package httpapi import ( "encoding/json" "strings" ) func normalizeStoredToolCall(tc ToolCall) (string, map[string]any, string) { name := tc.Name args := tc.Arguments thoughtSignature := "" if name == "" && tc.Function != nil { name = tc.Function.Name thoughtSignature = tc.Function.ThoughtSignature } else if tc.Function != nil { thoughtSignature = tc.Function.ThoughtSignature } if args == nil { args = map[string]any{} } if len(args) == 0 && tc.Function != nil && tc.Function.Arguments != "" { var parsed map[string]any if err := json.Unmarshal([]byte(tc.Function.Arguments), &parsed); err == nil && parsed != nil { args = parsed } } return name, args, thoughtSignature } func resolveToolResponseName(toolCallID string, toolCallNames map[string]string) string { if toolCallID == "" { return "" } if name, ok := toolCallNames[toolCallID]; ok && name != "" { return name } return inferToolNameFromCallID(toolCallID) } func inferToolNameFromCallID(toolCallID string) string { if !strings.HasPrefix(toolCallID, "call_") { return toolCallID } rest := strings.TrimPrefix(toolCallID, "call_") if idx := strings.LastIndex(rest, "_"); idx > 0 { candidate := rest[:idx] if candidate != "" { return candidate } } return toolCallID } func extractPartThoughtSignature(thoughtSignature string, thoughtSignatureSnake string) string { if thoughtSignature != "" { return thoughtSignature } if thoughtSignatureSnake != "" { return thoughtSignatureSnake } return "" } var geminiUnsupportedKeywords = map[string]bool{ "patternProperties": true, "additionalProperties": true, "$schema": true, "$id": true, "$ref": true, "$defs": true, "definitions": true, "examples": true, "minLength": true, "maxLength": true, "minimum": true, "maximum": true, "multipleOf": true, "pattern": true, "format": true, "minItems": true, "maxItems": true, "uniqueItems": true, "minProperties": true, "maxProperties": true, } func sanitizeSchemaForGemini(schema map[string]any) map[string]any { if schema == nil { return nil } result := make(map[string]any) for k, v := range schema { if geminiUnsupportedKeywords[k] { continue } switch val := v.(type) { case map[string]any: result[k] = sanitizeSchemaForGemini(val) case []any: sanitized := make([]any, len(val)) for i, item := range val { if m, ok := item.(map[string]any); ok { sanitized[i] = sanitizeSchemaForGemini(m) } else { sanitized[i] = item } } result[k] = sanitized default: result[k] = v } } if _, hasProps := result["properties"]; hasProps { if _, hasType := result["type"]; !hasType { result["type"] = "object" } } return result }