Refactor translator packages for OpenAI Chat Completions

- Renamed `openai` packages to `chat_completions` across translator modules.
- Introduced `openai_responses_handlers` with handlers for `/v1/models` and OpenAI-compatible chat completions endpoints.
- Updated constants and registry identifiers for OpenAI response type.
- Simplified request/response conversions and added detailed retry/error handling.
- Added `golang.org/x/crypto` for additional cryptographic functions.
This commit is contained in:
Luis Pater
2025-09-01 10:20:50 +08:00
parent 305916f5a9
commit bff6f6679b
3 changed files with 183 additions and 5 deletions

View File

@@ -1,5 +0,0 @@
package responses
func ConvertOpenAIResponsesRequestToOpenAIChatCompletions(modelName string, rawJSON []byte, stream bool) []byte {
return nil
}

View File

@@ -0,0 +1,183 @@
package responses
import (
"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, rawJSON []byte, stream bool) []byte {
// 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)
}
}
// Convert tool_choice if present
if toolChoice := root.Get("tool_choice"); toolChoice.Exists() {
out, _ = sjson.Set(out, "tool_choice", toolChoice.String())
}
return []byte(out)
}