Files
Luminary/internal/provider/responses_adapter_test.go
T
renjue 76ba500417
CI / docker (push) Successful in 2m4s
Initial commit: Luminary AI Gateway
OpenAI-compatible AI gateway with Vue admin UI, multi-provider egress,
ingress key governance, monitoring, and security controls.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 21:46:16 +08:00

180 lines
4.9 KiB
Go

package provider
import (
"encoding/json"
"strings"
"testing"
)
func TestResponsesToChatCompletion_StringInput(t *testing.T) {
body := []byte(`{"model":"gpt-4o-mini","instructions":"Be brief","input":"Hello"}`)
out, err := ResponsesToChatCompletion(body)
if err != nil {
t.Fatal(err)
}
var req map[string]any
if err := json.Unmarshal(out, &req); err != nil {
t.Fatal(err)
}
if req["model"] != "gpt-4o-mini" {
t.Fatalf("model: %v", req["model"])
}
msgs := req["messages"].([]any)
if len(msgs) != 2 {
t.Fatalf("messages len: %d", len(msgs))
}
sys := msgs[0].(map[string]any)
user := msgs[1].(map[string]any)
if sys["role"] != "system" || sys["content"] != "Be brief" {
t.Fatalf("system: %#v", sys)
}
if user["role"] != "user" || user["content"] != "Hello" {
t.Fatalf("user: %#v", user)
}
}
func TestChatCompletionToResponses(t *testing.T) {
body := []byte(`{"id":"chatcmpl-abc123","model":"gpt-4o-mini","created":123,"choices":[{"message":{"role":"assistant","content":"Hi"}}],"usage":{"prompt_tokens":3,"completion_tokens":1,"total_tokens":4}}`)
out, err := ChatCompletionToResponses(body)
if err != nil {
t.Fatal(err)
}
var resp map[string]any
if err := json.Unmarshal(out, &resp); err != nil {
t.Fatal(err)
}
if resp["object"] != "response" {
t.Fatalf("object: %v", resp["object"])
}
if resp["id"] != "resp_abc123" {
t.Fatalf("id: %v", resp["id"])
}
if resp["output_text"] != "Hi" {
t.Fatalf("output_text: %v", resp["output_text"])
}
output := resp["output"].([]any)
msg := output[0].(map[string]any)
content := msg["content"].([]any)
text := content[0].(map[string]any)
if text["type"] != "output_text" || text["text"] != "Hi" {
t.Fatalf("text: %#v", text)
}
if _, ok := text["annotations"]; !ok {
t.Fatalf("missing annotations: %#v", text)
}
usage := resp["usage"].(map[string]any)
if usage["input_tokens"].(float64) != 3 {
t.Fatalf("usage: %#v", usage)
}
if _, ok := usage["input_tokens_details"]; !ok {
t.Fatalf("missing input_tokens_details")
}
}
func TestResponsesToChatCompletion_InputTextParts(t *testing.T) {
body := []byte(`{
"model":"gpt-4o-mini",
"input":[{"role":"user","content":[{"type":"input_text","text":"Hello"}]}]
}`)
out, err := ResponsesToChatCompletion(body)
if err != nil {
t.Fatal(err)
}
var req map[string]any
if err := json.Unmarshal(out, &req); err != nil {
t.Fatal(err)
}
msgs := req["messages"].([]any)
user := msgs[0].(map[string]any)
if user["content"] != "Hello" {
t.Fatalf("content: %#v", user["content"])
}
}
func TestResponsesToChatCompletion_InputTextAndImage(t *testing.T) {
body := []byte(`{
"model":"gpt-4o-mini",
"input":[{"role":"user","content":[
{"type":"input_text","text":"describe"},
{"type":"input_image","image_url":"https://example.com/a.png","detail":"auto"}
]}]
}`)
out, err := ResponsesToChatCompletion(body)
if err != nil {
t.Fatal(err)
}
var req map[string]any
if err := json.Unmarshal(out, &req); err != nil {
t.Fatal(err)
}
msgs := req["messages"].([]any)
user := msgs[0].(map[string]any)
parts := user["content"].([]any)
if len(parts) != 2 {
t.Fatalf("parts: %#v", parts)
}
textPart := parts[0].(map[string]any)
if textPart["type"] != "text" || textPart["text"] != "describe" {
t.Fatalf("text part: %#v", textPart)
}
imgPart := parts[1].(map[string]any)
if imgPart["type"] != "image_url" {
t.Fatalf("image part: %#v", imgPart)
}
}
func TestResponsesToChatCompletion_MessageItem(t *testing.T) {
body := []byte(`{
"model":"gpt-4o-mini",
"input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"Hi"}]}]
}`)
out, err := ResponsesToChatCompletion(body)
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(out), "input_text") {
t.Fatalf("should not contain input_text: %s", out)
}
var req map[string]any
if err := json.Unmarshal(out, &req); err != nil {
t.Fatal(err)
}
msgs := req["messages"].([]any)
user := msgs[0].(map[string]any)
if user["content"] != "Hi" {
t.Fatalf("content: %#v", user["content"])
}
}
func TestAdaptResponsesStream(t *testing.T) {
upstream := strings.NewReader("data: {\"id\":\"chatcmpl-1\",\"model\":\"gpt-4o-mini\",\"choices\":[{\"delta\":{\"content\":\"Hi\"}}]}\n\ndata: [DONE]\n\n")
var out strings.Builder
result, err := AdaptResponsesStream([]byte(`{"model":"gpt-4o-mini","stream":true}`), upstream, &out, nil)
if err != nil {
t.Fatal(err)
}
if result.StatusCode != 200 {
t.Fatalf("status: %d", result.StatusCode)
}
s := out.String()
for _, event := range []string{
"response.created",
"response.in_progress",
"response.output_item.added",
"response.output_text.delta",
"response.output_text.done",
"response.output_item.done",
"response.completed",
} {
if !strings.Contains(s, event) {
t.Fatalf("missing event %s: %s", event, s)
}
}
if !strings.Contains(s, `"delta":"Hi"`) {
t.Fatalf("missing delta text: %s", s)
}
if !strings.Contains(s, `"output_text":"Hi"`) {
t.Fatalf("missing output_text in completed: %s", s)
}
}