mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 20:40:52 +08:00
206 lines
6.7 KiB
Go
206 lines
6.7 KiB
Go
package responses
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/tidwall/gjson"
|
|
"github.com/tidwall/sjson"
|
|
)
|
|
|
|
// ConvertOpenAIResponsesRequestToOpenAIChatCompletions converts OpenAI responses format to OpenAI chat completions format.
|
|
// It transforms the OpenAI responses API format (with instructions and input array) into the standard
|
|
// OpenAI chat completions format (with messages array and system content).
|
|
//
|
|
// The conversion handles:
|
|
// 1. Model name and streaming configuration
|
|
// 2. Instructions to system message conversion
|
|
// 3. Input array to messages array transformation
|
|
// 4. Tool definitions and tool choice conversion
|
|
// 5. Function calls and function results handling
|
|
// 6. Generation parameters mapping (max_tokens, reasoning, etc.)
|
|
//
|
|
// Parameters:
|
|
// - modelName: The name of the model to use for the request
|
|
// - rawJSON: The raw JSON request data in OpenAI responses format
|
|
// - stream: A boolean indicating if the request is for a streaming response
|
|
//
|
|
// Returns:
|
|
// - []byte: The transformed request data in OpenAI chat completions format
|
|
func ConvertOpenAIResponsesRequestToOpenAIChatCompletions(modelName string, inputRawJSON []byte, stream bool) []byte {
|
|
rawJSON := bytes.Clone(inputRawJSON)
|
|
// Base OpenAI chat completions template with default values
|
|
out := `{"model":"","messages":[],"stream":false}`
|
|
|
|
root := gjson.ParseBytes(rawJSON)
|
|
|
|
// Set model name
|
|
out, _ = sjson.Set(out, "model", modelName)
|
|
|
|
// Set stream configuration
|
|
out, _ = sjson.Set(out, "stream", stream)
|
|
|
|
// Map generation parameters from responses format to chat completions format
|
|
if maxTokens := root.Get("max_output_tokens"); maxTokens.Exists() {
|
|
out, _ = sjson.Set(out, "max_tokens", maxTokens.Int())
|
|
}
|
|
|
|
if parallelToolCalls := root.Get("parallel_tool_calls"); parallelToolCalls.Exists() {
|
|
out, _ = sjson.Set(out, "parallel_tool_calls", parallelToolCalls.Bool())
|
|
}
|
|
|
|
// Convert instructions to system message
|
|
if instructions := root.Get("instructions"); instructions.Exists() {
|
|
systemMessage := `{"role":"system","content":""}`
|
|
systemMessage, _ = sjson.Set(systemMessage, "content", instructions.String())
|
|
out, _ = sjson.SetRaw(out, "messages.-1", systemMessage)
|
|
}
|
|
|
|
// Convert input array to messages
|
|
if input := root.Get("input"); input.Exists() && input.IsArray() {
|
|
input.ForEach(func(_, item gjson.Result) bool {
|
|
itemType := item.Get("type").String()
|
|
|
|
switch itemType {
|
|
case "message":
|
|
// Handle regular message conversion
|
|
role := item.Get("role").String()
|
|
message := `{"role":"","content":""}`
|
|
message, _ = sjson.Set(message, "role", role)
|
|
|
|
if content := item.Get("content"); content.Exists() && content.IsArray() {
|
|
var messageContent string
|
|
var toolCalls []interface{}
|
|
|
|
content.ForEach(func(_, contentItem gjson.Result) bool {
|
|
contentType := contentItem.Get("type").String()
|
|
|
|
switch contentType {
|
|
case "input_text":
|
|
text := contentItem.Get("text").String()
|
|
if messageContent != "" {
|
|
messageContent += "\n" + text
|
|
} else {
|
|
messageContent = text
|
|
}
|
|
case "output_text":
|
|
text := contentItem.Get("text").String()
|
|
if messageContent != "" {
|
|
messageContent += "\n" + text
|
|
} else {
|
|
messageContent = text
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
|
|
if messageContent != "" {
|
|
message, _ = sjson.Set(message, "content", messageContent)
|
|
}
|
|
|
|
if len(toolCalls) > 0 {
|
|
message, _ = sjson.Set(message, "tool_calls", toolCalls)
|
|
}
|
|
}
|
|
|
|
out, _ = sjson.SetRaw(out, "messages.-1", message)
|
|
|
|
case "function_call":
|
|
// Handle function call conversion to assistant message with tool_calls
|
|
assistantMessage := `{"role":"assistant","tool_calls":[]}`
|
|
|
|
toolCall := `{"id":"","type":"function","function":{"name":"","arguments":""}}`
|
|
|
|
if callId := item.Get("call_id"); callId.Exists() {
|
|
toolCall, _ = sjson.Set(toolCall, "id", callId.String())
|
|
}
|
|
|
|
if name := item.Get("name"); name.Exists() {
|
|
toolCall, _ = sjson.Set(toolCall, "function.name", name.String())
|
|
}
|
|
|
|
if arguments := item.Get("arguments"); arguments.Exists() {
|
|
toolCall, _ = sjson.Set(toolCall, "function.arguments", arguments.String())
|
|
}
|
|
|
|
assistantMessage, _ = sjson.SetRaw(assistantMessage, "tool_calls.0", toolCall)
|
|
out, _ = sjson.SetRaw(out, "messages.-1", assistantMessage)
|
|
|
|
case "function_call_output":
|
|
// Handle function call output conversion to tool message
|
|
toolMessage := `{"role":"tool","tool_call_id":"","content":""}`
|
|
|
|
if callId := item.Get("call_id"); callId.Exists() {
|
|
toolMessage, _ = sjson.Set(toolMessage, "tool_call_id", callId.String())
|
|
}
|
|
|
|
if output := item.Get("output"); output.Exists() {
|
|
toolMessage, _ = sjson.Set(toolMessage, "content", output.String())
|
|
}
|
|
|
|
out, _ = sjson.SetRaw(out, "messages.-1", toolMessage)
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
// Convert tools from responses format to chat completions format
|
|
if tools := root.Get("tools"); tools.Exists() && tools.IsArray() {
|
|
var chatCompletionsTools []interface{}
|
|
|
|
tools.ForEach(func(_, tool gjson.Result) bool {
|
|
chatTool := `{"type":"function","function":{}}`
|
|
|
|
// Convert tool structure from responses format to chat completions format
|
|
function := `{"name":"","description":"","parameters":{}}`
|
|
|
|
if name := tool.Get("name"); name.Exists() {
|
|
function, _ = sjson.Set(function, "name", name.String())
|
|
}
|
|
|
|
if description := tool.Get("description"); description.Exists() {
|
|
function, _ = sjson.Set(function, "description", description.String())
|
|
}
|
|
|
|
if parameters := tool.Get("parameters"); parameters.Exists() {
|
|
function, _ = sjson.SetRaw(function, "parameters", parameters.Raw)
|
|
}
|
|
|
|
chatTool, _ = sjson.SetRaw(chatTool, "function", function)
|
|
chatCompletionsTools = append(chatCompletionsTools, gjson.Parse(chatTool).Value())
|
|
|
|
return true
|
|
})
|
|
|
|
if len(chatCompletionsTools) > 0 {
|
|
out, _ = sjson.Set(out, "tools", chatCompletionsTools)
|
|
}
|
|
}
|
|
|
|
if reasoningEffort := root.Get("reasoning.effort"); reasoningEffort.Exists() {
|
|
switch reasoningEffort.String() {
|
|
case "none":
|
|
out, _ = sjson.Set(out, "reasoning_effort", "none")
|
|
case "auto":
|
|
out, _ = sjson.Set(out, "reasoning_effort", "auto")
|
|
case "minimal":
|
|
out, _ = sjson.Set(out, "reasoning_effort", "low")
|
|
case "low":
|
|
out, _ = sjson.Set(out, "reasoning_effort", "low")
|
|
case "medium":
|
|
out, _ = sjson.Set(out, "reasoning_effort", "medium")
|
|
case "high":
|
|
out, _ = sjson.Set(out, "reasoning_effort", "high")
|
|
default:
|
|
out, _ = sjson.Set(out, "reasoning_effort", "auto")
|
|
}
|
|
}
|
|
|
|
// Convert tool_choice if present
|
|
if toolChoice := root.Get("tool_choice"); toolChoice.Exists() {
|
|
out, _ = sjson.Set(out, "tool_choice", toolChoice.String())
|
|
}
|
|
|
|
return []byte(out)
|
|
}
|