package convert import ( "encoding/json" "strings" ) // --------------------------------------------------------------------------- // 请求:Responses → Chat // responsesToChatReq 将 OpenAI Responses 请求转为 Chat 请求。 func responsesToChatReq(body []byte) ([]byte, error) { var m map[string]json.RawMessage if err := json.Unmarshal(body, &m); err != nil { return nil, err } out := map[string]any{"model": str(rawJSON(m, "model"))} if v, ok := m["stream"]; ok && string(v) == "true" { out["stream"] = true } if v, ok := m["temperature"]; ok { out["temperature"] = v } if v, ok := m["top_p"]; ok { out["top_p"] = v } if v, ok := m["max_output_tokens"]; ok { out["max_tokens"] = v } var msgs []any if ins := str(rawJSON(m, "instructions")); ins != "" { msgs = append(msgs, map[string]any{"role": "system", "content": ins}) } msgs = append(msgs, responsesInputToChat(rawJSON(m, "input"))...) out["messages"] = msgs if raw := rawJSON(m, "tools"); raw != nil { var tools []map[string]any if json.Unmarshal(raw, &tools) == nil { chatTools := make([]any, 0, len(tools)) for _, t := range tools { chatTools = append(chatTools, map[string]any{ "type": "function", "function": map[string]any{ "name": t["name"], "description": t["description"], "parameters": t["parameters"], }, }) } out["tools"] = chatTools } } return json.Marshal(out) } // responsesInputToChat 把 Responses input 转成 Chat messages。 func responsesInputToChat(raw json.RawMessage) []any { if len(raw) == 0 || string(raw) == "null" { return nil } // 字符串输入 if s := str(raw); s != "" { return []any{map[string]any{"role": "user", "content": s}} } var items []map[string]any if err := json.Unmarshal(raw, &items); err != nil || items == nil { return nil } var out []any for _, item := range items { switch item["type"] { case "function_call": var args any _ = json.Unmarshal([]byte(strField(item["arguments"])), &args) out = append(out, map[string]any{ "role": "assistant", "content": "", "tool_calls": []any{map[string]any{ "id": strField(item["call_id"]), "type": "function", "function": map[string]any{ "name": strField(item["name"]), "arguments": strField(item["arguments"]), }, }}, }) case "function_call_output": out = append(out, map[string]any{ "role": "tool", "tool_call_id": strField(item["call_id"]), "content": strField(item["output"]), }) default: // message 条目 role, _ := item["role"].(string) if role == "" { role = "user" } if content, ok := item["content"].(string); ok { out = append(out, map[string]any{"role": role, "content": content}) } else if blocks, ok := item["content"].([]any); ok { var text []string for _, b := range blocks { if bm, ok := b.(map[string]any); ok { if t, _ := bm["text"].(string); t != "" { text = append(text, t) } } } out = append(out, map[string]any{"role": role, "content": strings.Join(text, "")}) } } } return out } // --------------------------------------------------------------------------- // 请求:Chat → Responses // chatToResponsesReq 将 Chat 请求转为 Responses 请求。 func chatToResponsesReq(body []byte) ([]byte, error) { var req chatReq if err := json.Unmarshal(body, &req); err != nil { return nil, err } out := map[string]any{"model": req.Model} if req.Stream { out["stream"] = true } if req.Temperature != nil { out["temperature"] = *req.Temperature } if req.TopP != nil { out["top_p"] = *req.TopP } if req.MaxTokens != nil { out["max_output_tokens"] = *req.MaxTokens } var system []string var input []any for _, m := range req.Messages { if m.Role == "system" { if s := str(m.Content); s != "" { system = append(system, s) } continue } switch m.Role { case "tool": input = append(input, map[string]any{ "type": "function_call_output", "call_id": m.ToolCallID, "output": str(m.Content), }) case "assistant": if len(m.ToolCalls) > 0 { for _, tc := range m.ToolCalls { input = append(input, map[string]any{ "type": "function_call", "call_id": tc.ID, "name": tc.Function.Name, "arguments": tc.Function.Arguments, }) } } else if s := str(m.Content); s != "" { input = append(input, map[string]any{"type": "message", "role": "assistant", "content": []any{ map[string]any{"type": "input_text", "text": s}, }}) } default: if s := str(m.Content); s != "" { input = append(input, map[string]any{"type": "message", "role": "user", "content": []any{ map[string]any{"type": "input_text", "text": s}, }}) } } } if len(system) > 0 { out["instructions"] = strings.Join(system, "\n") } if len(input) == 1 { out["input"] = input[0] // 单条消息项 } else { out["input"] = input } if len(req.Tools) > 0 { tools := make([]any, 0, len(req.Tools)) for _, t := range req.Tools { tools = append(tools, map[string]any{ "type": "function", "name": t.Function.Name, "description": t.Function.Description, "parameters": rawOrObject(t.Function.Parameters), }) } out["tools"] = tools } return json.Marshal(out) } // --------------------------------------------------------------------------- // 响应:Responses → Chat // responsesToChatResp 将 Responses 响应(非流式)转为 Chat 响应。 func responsesToChatResp(body []byte) ([]byte, error) { var m map[string]json.RawMessage if err := json.Unmarshal(body, &m); err != nil { return nil, err } var text string var toolCalls []any if raw := rawJSON(m, "output"); raw != nil { var outputs []map[string]any if json.Unmarshal(raw, &outputs) == nil { for _, o := range outputs { switch o["type"] { case "message": if content, ok := o["content"].([]any); ok { for _, c := range content { if cm, ok := c.(map[string]any); ok { if t, _ := cm["text"].(string); t != "" { text += t } } } } case "function_call": toolCalls = append(toolCalls, map[string]any{ "id": strField(o["call_id"]), "type": "function", "function": map[string]any{ "name": strField(o["name"]), "arguments": strField(o["arguments"]), }, }) } } } } msg := map[string]any{"role": "assistant", "content": text} if len(toolCalls) > 0 { msg["tool_calls"] = toolCalls } finish := "stop" if string(rawJSON(m, "status")) == `"incomplete"` { finish = "length" } var prompt, completion int64 if u := rawJSON(m, "usage"); u != nil { var us struct { InputTokens int64 `json:"input_tokens"` OutputTokens int64 `json:"output_tokens"` } _ = json.Unmarshal(u, &us) prompt, completion = us.InputTokens, us.OutputTokens } return json.Marshal(map[string]any{ "id": "chatcmpl-" + strings.TrimPrefix(str(rawJSON(m, "id")), "resp_"), "object": "chat.completion", "model": str(rawJSON(m, "model")), "choices": []any{map[string]any{"index": 0, "message": msg, "finish_reason": finish}}, "usage": map[string]any{ "prompt_tokens": prompt, "completion_tokens": completion, "total_tokens": prompt + completion, }, }) } // --------------------------------------------------------------------------- // 响应:Chat → Responses // chatToResponsesResp 将 Chat 响应(非流式)转为 Responses 响应。 func chatToResponsesResp(body []byte) ([]byte, error) { var r chatResp if err := json.Unmarshal(body, &r); err != nil { return nil, err } output := make([]any, 0, 2) var finish = "completed" if len(r.Choices) > 0 { msg := r.Choices[0].Message if msg.Content != "" { output = append(output, map[string]any{ "type": "message", "role": "assistant", "content": []any{map[string]any{"type": "output_text", "text": msg.Content}}, }) } for _, tc := range msg.ToolCalls { output = append(output, map[string]any{ "type": "function_call", "call_id": tc.ID, "name": tc.Function.Name, "arguments": tc.Function.Arguments, }) } if r.Choices[0].FinishReason == "length" { finish = "incomplete" } } status := finish return json.Marshal(map[string]any{ "id": "resp_" + strings.TrimPrefix(r.ID, "chatcmpl-"), "object": "response", "model": r.Model, "status": status, "output": output, "usage": map[string]any{ "input_tokens": r.Usage.PromptTokens, "output_tokens": r.Usage.CompletionTokens, "total_tokens": r.Usage.PromptTokens + r.Usage.CompletionTokens, }, }) }