| package services |
|
|
| import ( |
| "github.com/libaxuan/cursor2api-go/models" |
| "github.com/libaxuan/cursor2api-go/utils" |
| "encoding/json" |
| "strings" |
| ) |
|
|
| |
| 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 |
| } |
|
|