package promptcompat import ( "fmt" "strings" "ds2api/internal/config" "ds2api/internal/prompt" ) func normalizeResponsesInputItem(m map[string]any) map[string]any { return normalizeResponsesInputItemWithState(m, nil) } func normalizeResponsesInputItemWithState(m map[string]any, callNameByID map[string]string) map[string]any { if m == nil { return nil } role := strings.ToLower(strings.TrimSpace(asString(m["role"]))) if role != "" { if role == "assistant" { return normalizeResponsesAssistantMessage(m) } content := m["content"] if content == nil { if txt, _ := m["text"].(string); strings.TrimSpace(txt) != "" { content = txt } } if content == nil { return nil } out := map[string]any{ "role": normalizeOpenAIRoleForPrompt(role), "content": content, } if role == "tool" || role == "function" { if callID := strings.TrimSpace(asString(m["tool_call_id"])); callID != "" { out["tool_call_id"] = callID } if callID := strings.TrimSpace(asString(m["call_id"])); callID != "" { out["tool_call_id"] = callID } if name := strings.TrimSpace(asString(m["name"])); name != "" { out["name"] = name } } return out } itemType := strings.ToLower(strings.TrimSpace(asString(m["type"]))) switch itemType { case "message", "input_message": role := strings.ToLower(strings.TrimSpace(asString(m["role"]))) if role == "assistant" { return normalizeResponsesAssistantMessage(m) } content := m["content"] if content == nil { if txt, _ := m["text"].(string); strings.TrimSpace(txt) != "" { content = txt } } if content == nil { return nil } if role == "" { role = "user" } return map[string]any{ "role": normalizeOpenAIRoleForPrompt(role), "content": content, } case "function_call_output", "tool_result": content := m["output"] if content == nil { content = m["content"] } if content == nil { content = "" } out := map[string]any{ "role": "tool", "content": content, } if callID := strings.TrimSpace(asString(m["call_id"])); callID != "" { out["tool_call_id"] = callID } else if callID = strings.TrimSpace(asString(m["tool_call_id"])); callID != "" { out["tool_call_id"] = callID } if name := strings.TrimSpace(asString(m["name"])); name != "" { out["name"] = name } else if name = strings.TrimSpace(asString(m["tool_name"])); name != "" { out["name"] = name } else if callID := strings.TrimSpace(asString(out["tool_call_id"])); callID != "" { if inferred := strings.TrimSpace(callNameByID[callID]); inferred != "" { out["name"] = inferred } else { config.Logger.Warn( "[responses] unable to backfill tool result name from call_id", "call_id", callID, ) } } return out case "function_call", "tool_call": name := strings.TrimSpace(asString(m["name"])) var fn map[string]any if rawFn, ok := m["function"].(map[string]any); ok { fn = rawFn if name == "" { name = strings.TrimSpace(asString(fn["name"])) } } if name == "" { return nil } var argsRaw any if v, ok := m["arguments"]; ok { argsRaw = v } else if v, ok := m["input"]; ok { argsRaw = v } if argsRaw == nil && fn != nil { if v, ok := fn["arguments"]; ok { argsRaw = v } else if v, ok := fn["input"]; ok { argsRaw = v } } functionPayload := map[string]any{ "name": name, "arguments": prompt.StringifyToolCallArguments(argsRaw), } call := map[string]any{ "type": "function", "function": functionPayload, } if callID := strings.TrimSpace(asString(m["call_id"])); callID != "" { call["id"] = callID } else if callID = strings.TrimSpace(asString(m["id"])); callID != "" { call["id"] = callID } if callID := strings.TrimSpace(asString(call["id"])); callID != "" && callNameByID != nil { callNameByID[callID] = name } return map[string]any{ "role": "assistant", "tool_calls": []any{call}, } case "input_text": if txt, _ := m["text"].(string); strings.TrimSpace(txt) != "" { return map[string]any{ "role": "user", "content": txt, } } } if txt, _ := m["text"].(string); strings.TrimSpace(txt) != "" { return map[string]any{ "role": "user", "content": txt, } } if content, ok := m["content"]; ok { if strings.TrimSpace(NormalizeOpenAIContentForPrompt(content)) != "" { return map[string]any{ "role": "user", "content": content, } } } return nil } func normalizeResponsesAssistantMessage(m map[string]any) map[string]any { out := map[string]any{ "role": "assistant", } if toolCalls, ok := m["tool_calls"].([]any); ok && len(toolCalls) > 0 { out["tool_calls"] = toolCalls } content := m["content"] if content == nil { if txt, _ := m["text"].(string); strings.TrimSpace(txt) != "" { content = txt } } if content != nil { out["content"] = content } if reasoning := strings.TrimSpace(normalizeOpenAIReasoningContentForPrompt(m["reasoning_content"])); reasoning != "" { out["reasoning_content"] = m["reasoning_content"] } if _, hasToolCalls := out["tool_calls"]; hasToolCalls || out["content"] != nil || out["reasoning_content"] != nil { return out } return nil } func normalizeResponsesFallbackPart(m map[string]any) string { if m == nil { return "" } if t, _ := m["type"].(string); strings.EqualFold(strings.TrimSpace(t), "input_text") { if txt, _ := m["text"].(string); strings.TrimSpace(txt) != "" { return txt } } if txt, _ := m["text"].(string); strings.TrimSpace(txt) != "" { return txt } if content, ok := m["content"]; ok { if normalized := strings.TrimSpace(NormalizeOpenAIContentForPrompt(content)); normalized != "" { return normalized } } return strings.TrimSpace(fmt.Sprintf("%v", m)) }