package openai import ( "encoding/json" "testing" ) func TestParseResponsesRequest_StringInput(t *testing.T) { body := `{"model":"GLM-4.7","input":"hello world","stream":false}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } if req.Model != "GLM-4.7" { t.Fatalf("model = %q", req.Model) } if len(req.Messages) != 1 { t.Fatalf("messages = %d items", len(req.Messages)) } if req.Messages[0]["role"] != "user" { t.Fatalf("role = %v", req.Messages[0]["role"]) } if req.Messages[0]["content"] != "hello world" { t.Fatalf("content = %v", req.Messages[0]["content"]) } } func TestParseResponsesRequest_Instructions(t *testing.T) { body := `{"model":"GLM-4.7","instructions":"be helpful","input":"hi"}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } if len(req.Messages) != 2 { t.Fatalf("messages = %d items", len(req.Messages)) } if req.Messages[0]["role"] != "system" { t.Fatalf("first role = %v", req.Messages[0]["role"]) } if req.Messages[0]["content"] != "be helpful" { t.Fatalf("first content = %v", req.Messages[0]["content"]) } if req.Messages[1]["role"] != "user" { t.Fatalf("second role = %v", req.Messages[1]["role"]) } } func TestParseResponsesRequest_ArrayInput(t *testing.T) { body := `{"model":"GLM-4.7","input":[ {"role":"user","content":"hello"}, {"role":"assistant","content":"hi there"}, {"role":"user","content":"how are you?"} ]}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } if len(req.Messages) != 3 { t.Fatalf("messages = %d items", len(req.Messages)) } if req.Messages[0]["role"] != "user" || req.Messages[0]["content"] != "hello" { t.Fatalf("msg[0] = %v", req.Messages[0]) } if req.Messages[1]["role"] != "assistant" || req.Messages[1]["content"] != "hi there" { t.Fatalf("msg[1] = %v", req.Messages[1]) } if req.Messages[2]["role"] != "user" || req.Messages[2]["content"] != "how are you?" { t.Fatalf("msg[2] = %v", req.Messages[2]) } } func TestParseResponsesRequest_ContentParts(t *testing.T) { body := `{"model":"GLM-4.7","input":[ {"role":"user","content":[ {"type":"input_text","text":"describe this"}, {"type":"input_image","image_url":"data:image/png;base64,abc"} ]} ]}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } if len(req.Messages) != 1 { t.Fatalf("messages = %d items", len(req.Messages)) } content, ok := req.Messages[0]["content"].([]map[string]any) if !ok { t.Fatalf("content type = %T", req.Messages[0]["content"]) } if len(content) != 2 { t.Fatalf("content parts = %d", len(content)) } if content[0]["type"] != "text" || content[0]["text"] != "describe this" { t.Fatalf("content[0] = %v", content[0]) } if content[1]["type"] != "image_url" { t.Fatalf("content[1] type = %v", content[1]) } } func TestParseResponsesRequest_MaxOutputTokens(t *testing.T) { body := `{"model":"GLM-4.7","input":"hi","max_output_tokens":500}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } if v, ok := req.Extra["max_tokens"]; !ok { t.Fatal("max_tokens not in Extra") } else { var n int _ = json.Unmarshal(v, &n) if n != 500 { t.Fatalf("max_tokens = %d", n) } } } func TestParseResponsesRequest_Tools(t *testing.T) { body := `{"model":"GLM-4.7","input":"hi","tools":[ {"type":"function","name":"get_weather","description":"get weather","parameters":{"type":"object"}} ]}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } raw, ok := req.Extra["tools"] if !ok { t.Fatal("tools not in Extra") } var tools []map[string]any if err := json.Unmarshal(raw, &tools); err != nil { t.Fatal(err) } if len(tools) != 1 { t.Fatalf("tools = %d", len(tools)) } if tools[0]["type"] != "function" { t.Fatalf("tool type = %v", tools[0]["type"]) } fn, ok := tools[0]["function"].(map[string]any) if !ok { t.Fatalf("function type = %T", tools[0]["function"]) } if fn["name"] != "get_weather" { t.Fatalf("function name = %v", fn["name"]) } } func TestParseResponsesRequest_FunctionCallInput(t *testing.T) { body := `{"model":"GLM-4.7","input":[ {"role":"user","content":"what's the weather?"}, {"type":"function_call","call_id":"call_123","name":"get_weather","arguments":"{\"city\":\"NYC\"}"}, {"type":"function_call_output","call_id":"call_123","output":"sunny 72F"} ]}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } if len(req.Messages) != 3 { t.Fatalf("messages = %d items", len(req.Messages)) } // First message is user text if req.Messages[0]["role"] != "user" { t.Fatalf("msg[0] role = %v", req.Messages[0]["role"]) } // Second message is assistant with tool_calls if req.Messages[1]["role"] != "assistant" { t.Fatalf("msg[1] role = %v", req.Messages[1]["role"]) } tcs, ok := req.Messages[1]["tool_calls"].([]any) if !ok || len(tcs) != 1 { t.Fatalf("msg[1] tool_calls = %v", req.Messages[1]["tool_calls"]) } tc, _ := tcs[0].(map[string]any) if tc["id"] != "call_123" { t.Fatalf("tool call id = %v", tc["id"]) } fn, _ := tc["function"].(map[string]any) if fn["name"] != "get_weather" { t.Fatalf("function name = %v", fn["name"]) } // Third message is tool result if req.Messages[2]["role"] != "tool" { t.Fatalf("msg[2] role = %v", req.Messages[2]["role"]) } if req.Messages[2]["tool_call_id"] != "call_123" { t.Fatalf("msg[2] tool_call_id = %v", req.Messages[2]["tool_call_id"]) } if req.Messages[2]["content"] != "sunny 72F" { t.Fatalf("msg[2] content = %v", req.Messages[2]["content"]) } } func TestUsageToResponses(t *testing.T) { usage := map[string]any{ "prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30, "prompt_tokens_details": map[string]any{"cached_tokens": 4}, "completion_tokens_details": map[string]any{"reasoning_tokens": 5}, } result := UsageToResponses(usage) m, ok := result.(map[string]any) if !ok { t.Fatalf("result type = %T", result) } if m["input_tokens"] != float64(10) { t.Fatalf("input_tokens = %v", m["input_tokens"]) } if m["output_tokens"] != float64(20) { t.Fatalf("output_tokens = %v", m["output_tokens"]) } if m["total_tokens"] != float64(30) { t.Fatalf("total_tokens = %v", m["total_tokens"]) } if d, ok := m["input_tokens_details"].(map[string]any); !ok || d["cached_tokens"] != float64(4) { t.Fatalf("input_tokens_details = %v", m["input_tokens_details"]) } if d, ok := m["output_tokens_details"].(map[string]any); !ok || d["reasoning_tokens"] != float64(5) { t.Fatalf("output_tokens_details = %v", m["output_tokens_details"]) } } func TestParseResponsesRequest_TextFormat(t *testing.T) { body := `{"model":"GLM-4.7","input":"Jane, 54","text":{"format":{"type":"json_schema","name":"person","strict":true,"schema":{"type":"object"}}}}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } rf, ok := req.Extra["response_format"] if !ok { t.Fatal("response_format not in Extra") } var m map[string]any if err := json.Unmarshal(rf, &m); err != nil { t.Fatal(err) } if m["type"] != "json_schema" { t.Fatalf("type = %v", m["type"]) } js, ok := m["json_schema"].(map[string]any) if !ok { t.Fatalf("json_schema = %v", m["json_schema"]) } if js["name"] != "person" { t.Fatalf("name = %v", js["name"]) } if js["strict"] != true { t.Fatalf("strict = %v", js["strict"]) } } func TestParseResponsesRequest_TextFormatJsonObject(t *testing.T) { body := `{"model":"GLM-4.7","input":"hi","text":{"format":{"type":"json_object"}}}` req, err := ParseResponsesRequest([]byte(body)) if err != nil { t.Fatal(err) } rf, ok := req.Extra["response_format"] if !ok { t.Fatal("response_format not in Extra") } var m map[string]any if err := json.Unmarshal(rf, &m); err != nil { t.Fatal(err) } if m["type"] != "json_object" { t.Fatalf("type = %v", m["type"]) } } func TestParseResponsesMeta_Defaults(t *testing.T) { meta := ParseResponsesMeta([]byte(`{"model":"GLM-4.7","input":"hi"}`)) if meta.ParallelToolCalls != false { t.Fatalf("parallel_tool_calls = %v", meta.ParallelToolCalls) } if meta.ToolChoice != "auto" { t.Fatalf("tool_choice = %v", meta.ToolChoice) } tools, ok := meta.Tools.([]any) if !ok || len(tools) != 0 { t.Fatalf("tools = %v", meta.Tools) } } func TestParseResponsesMeta_EchoBack(t *testing.T) { body := `{"model":"GLM-4.7","input":"hi","parallel_tool_calls":true,"tool_choice":"required","tools":[{"type":"function","name":"get_weather"}],"temperature":0.7,"max_output_tokens":500}` meta := ParseResponsesMeta([]byte(body)) if meta.ParallelToolCalls != true { t.Fatalf("parallel_tool_calls = %v", meta.ParallelToolCalls) } if meta.ToolChoice != "required" { t.Fatalf("tool_choice = %v", meta.ToolChoice) } tools, ok := meta.Tools.([]any) if !ok || len(tools) != 1 { t.Fatalf("tools = %v", meta.Tools) } if meta.Temperature != 0.7 { t.Fatalf("temperature = %v", meta.Temperature) } if meta.MaxOutputTokens != float64(500) { t.Fatalf("max_output_tokens = %v", meta.MaxOutputTokens) } }