package convert import ( "encoding/json" "fmt" ) // ChatToResponses converts a Chat Completions request to Responses API format func ChatToResponses(req *ChatCompletionRequest) (*ResponsesRequest, error) { var inputItems []InputItem var instructions string for _, m := range req.Messages { if m.Role == "system" { if s, ok := m.Content.(string); ok { if instructions != "" { instructions += "\n\n" } instructions += s } continue } item := InputItem{ Role: m.Role, Content: m.Content, } inputItems = append(inputItems, item) } out := &ResponsesRequest{ Model: req.Model, Input: inputItems, Instructions: instructions, Stream: req.Stream, } if req.MaxTokens != nil { out.MaxOutputTokens = req.MaxTokens } if req.Temperature != nil { out.Temperature = req.Temperature } if req.TopP != nil { out.TopP = req.TopP } if req.Tools != nil { out.Tools = req.Tools } return out, nil } // ResponsesToChat converts a Responses API response to Chat Completions format func ResponsesToChat(resp *ResponsesResponse) (*ChatCompletionResponse, error) { choices := make([]Choice, 0) for _, output := range resp.Output { switch output.Type { case "message": for _, content := range output.Content { switch content.Type { case "output_text": choices = append(choices, Choice{ Index: len(choices), Message: Message{ Role: "assistant", Content: content.Text, }, FinishReason: "stop", }) case "function_call": toolCall := ToolCall{ ID: content.ID, Type: "function", Function: FunctionCall{ Name: content.Name, Arguments: toJSON(content.Input), }, } if len(choices) == 0 { choices = append(choices, Choice{ Index: 0, Message: Message{ Role: "assistant", ToolCalls: []ToolCall{toolCall}, }, FinishReason: "tool_calls", }) } else { choices[0].Message.ToolCalls = append(choices[0].Message.ToolCalls, toolCall) choices[0].FinishReason = "tool_calls" } } } case "function_call_output": // This would be in a user message context continue } } if len(choices) == 0 { choices = append(choices, Choice{ Index: 0, Message: Message{ Role: "assistant", Content: "", }, FinishReason: "stop", }) } return &ChatCompletionResponse{ ID: resp.ID, Object: "chat.completion", Model: resp.Model, Choices: choices, Usage: &Usage{ PromptTokens: resp.Usage.PromptTokens, CompletionTokens: resp.Usage.CompletionTokens, TotalTokens: resp.Usage.PromptTokens + resp.Usage.CompletionTokens, }, }, nil } // ResponsesStreamToChatStream converts Responses API streaming to Chat Completions format func ResponsesStreamToChatStream(events []ResponsesStreamEvent, model string) []ChatCompletionStreamChunk { var chunks []ChatCompletionStreamChunk id := fmt.Sprintf("chatcmpl-%d", len(events)) for _, event := range events { switch event.Type { case "response.created": chunks = append(chunks, ChatCompletionStreamChunk{ ID: id, Object: "chat.completion.chunk", Model: model, Choices: []StreamChoice{{ Index: 0, Delta: StreamDelta{ Role: "assistant", }, }}, }) case "response.output_item.added": if event.Item != nil && event.Item.Type == "message" { chunks = append(chunks, ChatCompletionStreamChunk{ ID: id, Object: "chat.completion.chunk", Model: model, Choices: []StreamChoice{{ Index: 0, Delta: StreamDelta{ Role: "assistant", }, }}, }) } case "response.content_part.delta": if event.Delta != "" { chunks = append(chunks, ChatCompletionStreamChunk{ ID: id, Object: "chat.completion.chunk", Model: model, Choices: []StreamChoice{{ Index: 0, Delta: StreamDelta{ Content: event.Delta, }, }}, }) } case "response.completed": finishReason := "stop" chunk := ChatCompletionStreamChunk{ ID: id, Object: "chat.completion.chunk", Model: model, Choices: []StreamChoice{{ Index: 0, FinishReason: &finishReason, }}, } chunks = append(chunks, chunk) } } return chunks } // MessagesToResponses converts an Anthropic Messages request to Responses API format func MessagesToResponses(req *MessagesRequest) (*ResponsesRequest, error) { var inputItems []InputItem var instructions string // Handle system message if req.System != nil { switch v := req.System.(type) { case string: instructions = v case []ContentPart: for _, p := range v { if p.Type == "text" { if instructions != "" { instructions += "\n\n" } instructions += p.Text } } } } for _, m := range req.Messages { item := InputItem{ Role: m.Role, Content: m.Content, } inputItems = append(inputItems, item) } out := &ResponsesRequest{ Model: req.Model, Input: inputItems, Instructions: instructions, Stream: req.Stream, } out.MaxOutputTokens = &req.MaxTokens if req.Temperature != nil { out.Temperature = req.Temperature } if req.TopP != nil { out.TopP = req.TopP } if req.Tools != nil { out.Tools = req.Tools } return out, nil } // ResponsesToMessages converts a Responses API response to Anthropic Messages format func ResponsesToMessages(resp *ResponsesResponse) (*MessagesResponse, error) { var content []ContentBlock for _, output := range resp.Output { switch output.Type { case "message": for _, c := range output.Content { switch c.Type { case "output_text": content = append(content, ContentBlock{ Type: "text", Text: c.Text, }) case "function_call": content = append(content, ContentBlock{ Type: "tool_use", ID: c.ID, Name: c.Name, }) } } } } var stopReason string if len(content) > 0 { last := content[len(content)-1] if last.Type == "tool_use" { stopReason = "tool_use" } else { stopReason = "end_turn" } } else { stopReason = "end_turn" } return &MessagesResponse{ ID: resp.ID, Type: "message", Role: "assistant", Content: content, Model: resp.Model, StopReason: stopReason, Usage: resp.Usage, }, nil } // toJSON is a helper to convert a value to JSON string func toJSONStr(v interface{}) string { b, err := json.Marshal(v) if err != nil { return "{}" } return string(b) }