package services import ( "github.com/libaxuan/cursor2api-go/models" "github.com/libaxuan/cursor2api-go/utils" "encoding/json" "strings" ) // BuildResponseOutputFromMessage converts a chat completion message into response output items. func BuildResponseOutputFromMessage(message models.Message, adapter *ResponseToolAdapter) ([]interface{}, string) { output := make([]interface{}, 0, 1+len(message.ToolCalls)) var outputText string text := message.GetStringContent() if text != "" { msgItem := buildResponseMessageItem(text) output = append(output, msgItem) outputText = text } for _, toolCall := range message.ToolCalls { output = append(output, buildResponseToolCallItem(toolCall, adapter)) } return output, outputText } func buildResponseMessageItem(text string) models.ResponseOutputMessage { return models.ResponseOutputMessage{ ID: utils.GenerateResponseItemID("msg_"), Type: "message", Status: "completed", Role: "assistant", Content: []models.ResponseOutputTextContent{ { Type: "output_text", Text: text, Annotations: []interface{}{}, }, }, } } func buildResponseToolCallItem(toolCall models.ToolCall, adapter *ResponseToolAdapter) interface{} { name := strings.TrimSpace(toolCall.Function.Name) if name == "" { name = "tool" } if adapter == nil || adapter.ToolTypesByName == nil { return buildFunctionCallItem(toolCall, name) } switch adapterType := adapter.ToolTypesByName[name]; adapterType { case "apply_patch": return buildApplyPatchCallItem(toolCall) case "shell": return buildShellCallItem(toolCall, adapter.ShellEnvironment) case "local_shell": return buildLocalShellCallItem(toolCall) default: return buildFunctionCallItem(toolCall, name) } } func buildFunctionCallItem(toolCall models.ToolCall, name string) models.ResponseFunctionCall { args := strings.TrimSpace(toolCall.Function.Arguments) if args == "" { args = "{}" } callID := toolCall.ID if callID == "" { callID = utils.GenerateResponseItemID("call_") } return models.ResponseFunctionCall{ ID: utils.GenerateResponseItemID("fc_"), Type: "function_call", Status: "completed", CallID: callID, Name: name, Arguments: args, } } func buildApplyPatchCallItem(toolCall models.ToolCall) models.ResponseApplyPatchCall { op := parseOperation(toolCall.Function.Arguments) callID := toolCall.ID if callID == "" { callID = utils.GenerateResponseItemID("call_") } return models.ResponseApplyPatchCall{ ID: utils.GenerateResponseItemID("apc_"), Type: "apply_patch_call", Status: "completed", CallID: callID, Operation: op, } } func buildShellCallItem(toolCall models.ToolCall, environment interface{}) models.ResponseShellCall { action := parseShellAction(toolCall.Function.Arguments) callID := toolCall.ID if callID == "" { callID = utils.GenerateResponseItemID("call_") } return models.ResponseShellCall{ ID: utils.GenerateResponseItemID("sc_"), Type: "shell_call", Status: "completed", CallID: callID, Action: action, Environment: environment, } } func buildLocalShellCallItem(toolCall models.ToolCall) models.ResponseLocalShellCall { action := parseLocalShellAction(toolCall.Function.Arguments) callID := toolCall.ID if callID == "" { callID = utils.GenerateResponseItemID("call_") } return models.ResponseLocalShellCall{ ID: utils.GenerateResponseItemID("lsc_"), Type: "local_shell_call", Status: "completed", CallID: callID, Action: action, } } func parseOperation(arguments string) map[string]interface{} { args := strings.TrimSpace(arguments) if args == "" { return map[string]interface{}{} } var payload map[string]interface{} if err := json.Unmarshal([]byte(args), &payload); err == nil { if op, ok := payload["operation"].(map[string]interface{}); ok { return op } if hasKeys(payload, "type", "path") { return payload } return payload } return map[string]interface{}{ "diff": args, } } func parseShellAction(arguments string) map[string]interface{} { args := strings.TrimSpace(arguments) if args == "" { return map[string]interface{}{"commands": []string{}} } var payload map[string]interface{} if err := json.Unmarshal([]byte(args), &payload); err == nil { if action, ok := payload["action"].(map[string]interface{}); ok { return normalizeShellAction(action) } return normalizeShellAction(payload) } return map[string]interface{}{ "commands": []string{args}, } } func parseLocalShellAction(arguments string) map[string]interface{} { args := strings.TrimSpace(arguments) if args == "" { return map[string]interface{}{"command": ""} } var payload map[string]interface{} if err := json.Unmarshal([]byte(args), &payload); err == nil { if action, ok := payload["action"].(map[string]interface{}); ok { return normalizeLocalShellAction(action) } return normalizeLocalShellAction(payload) } return map[string]interface{}{ "command": args, } } func normalizeShellAction(payload map[string]interface{}) map[string]interface{} { action := map[string]interface{}{} if cmds, ok := payload["commands"]; ok { action["commands"] = normalizeStringSlice(cmds) } else if cmd, ok := payload["command"]; ok { action["commands"] = normalizeStringSlice(cmd) } if timeout, ok := payload["timeout_ms"]; ok { action["timeout_ms"] = timeout } if maxOut, ok := payload["max_output_length"]; ok { action["max_output_length"] = maxOut } if _, exists := action["commands"]; !exists { action["commands"] = []string{} } return action } func normalizeLocalShellAction(payload map[string]interface{}) map[string]interface{} { action := map[string]interface{}{} if cmd, ok := payload["command"]; ok { action["command"] = cmd } else if cmds, ok := payload["commands"]; ok { action["command"] = cmds } if timeout, ok := payload["timeout_ms"]; ok { action["timeout_ms"] = timeout } if wd, ok := payload["working_directory"]; ok { action["working_directory"] = wd } if env, ok := payload["env"]; ok { action["env"] = env } if maxOut, ok := payload["max_output_length"]; ok { action["max_output_length"] = maxOut } if _, exists := action["command"]; !exists { action["command"] = "" } return action } func normalizeStringSlice(value interface{}) []string { switch v := value.(type) { case []interface{}: result := make([]string, 0, len(v)) for _, item := range v { if text, ok := item.(string); ok { result = append(result, text) } } return result case []string: return v case string: return []string{v} default: return []string{} } } func hasKeys(payload map[string]interface{}, keys ...string) bool { for _, key := range keys { if _, ok := payload[key]; !ok { return false } } return true }