package gemini import "strings" func geminiMessagesFromRequest(req map[string]any) []any { out := make([]any, 0, 8) if sys := normalizeGeminiSystemInstruction(req["systemInstruction"]); strings.TrimSpace(sys) != "" { out = append(out, map[string]any{ "role": "system", "content": sys, }) } contents, _ := req["contents"].([]any) for _, item := range contents { content, ok := item.(map[string]any) if !ok { continue } role := mapGeminiRole(content["role"]) if role == "" { role = "user" } parts, _ := content["parts"].([]any) if len(parts) == 0 { if text := strings.TrimSpace(asString(content["text"])); text != "" { out = append(out, map[string]any{ "role": role, "content": text, }) } continue } textParts := make([]string, 0, len(parts)) flushText := func() { if len(textParts) == 0 { return } out = append(out, map[string]any{ "role": role, "content": strings.Join(textParts, "\n"), }) textParts = textParts[:0] } for _, rawPart := range parts { part, ok := rawPart.(map[string]any) if !ok { continue } if text := strings.TrimSpace(asString(part["text"])); text != "" { textParts = append(textParts, text) continue } if fnCall, ok := part["functionCall"].(map[string]any); ok { flushText() if name := strings.TrimSpace(asString(fnCall["name"])); name != "" { callID := strings.TrimSpace(asString(fnCall["id"])) if callID == "" { callID = "call_gemini" } out = append(out, map[string]any{ "role": "assistant", "tool_calls": []any{ map[string]any{ "id": callID, "type": "function", "function": map[string]any{ "name": name, "arguments": stringifyJSON(fnCall["args"]), }, }, }, }) } continue } if fnResp, ok := part["functionResponse"].(map[string]any); ok { flushText() name := strings.TrimSpace(asString(fnResp["name"])) callID := strings.TrimSpace(asString(fnResp["id"])) if callID == "" { callID = strings.TrimSpace(asString(fnResp["callId"])) } if callID == "" { callID = strings.TrimSpace(asString(fnResp["tool_call_id"])) } if callID == "" { callID = "call_gemini" } content := fnResp["response"] if content == nil { content = fnResp["output"] } if content == nil { content = "" } msg := map[string]any{ "role": "tool", "tool_call_id": callID, "content": content, } if name != "" { msg["name"] = name } out = append(out, msg) } } flushText() } return out } func normalizeGeminiSystemInstruction(raw any) string { switch v := raw.(type) { case string: return strings.TrimSpace(v) case map[string]any: if parts, ok := v["parts"].([]any); ok { texts := make([]string, 0, len(parts)) for _, item := range parts { part, ok := item.(map[string]any) if !ok { continue } if text := strings.TrimSpace(asString(part["text"])); text != "" { texts = append(texts, text) } } return strings.Join(texts, "\n") } if text := strings.TrimSpace(asString(v["text"])); text != "" { return text } } return "" } func mapGeminiRole(v any) string { switch strings.ToLower(strings.TrimSpace(asString(v))) { case "user": return "user" case "model", "assistant": return "assistant" case "system": return "system" default: return "" } }