implemented stateless ant/oai messages/responses apis

This commit is contained in:
Luxferre
2026-08-05 08:33:51 +03:00
parent d734a5cdce
commit 274873f9d3
5 changed files with 1387 additions and 1 deletions
+279
View File
@@ -988,6 +988,285 @@ func TestFibonacciBackoff(t *testing.T) {
}
}
func TestAnthropicMessagesEndpoint(t *testing.T) {
var receivedOpenAIBody map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v1/chat/completions" {
t.Errorf("Expected request to be routed to /v1/chat/completions, got %s", r.URL.Path)
}
var body map[string]any
_ = json.NewDecoder(r.Body).Decode(&body)
receivedOpenAIBody = body
isStream, _ := body["stream"].(bool)
w.Header().Set("Content-Type", "application/json")
if isStream {
w.Header().Set("Content-Type", "text/event-stream")
w.WriteHeader(http.StatusOK)
flusher, _ := w.(http.Flusher)
_, _ = w.Write([]byte("data: {\"choices\":[{\"delta\":{\"content\":\"Hello \"}}]}\n\n"))
flusher.Flush()
_, _ = w.Write([]byte("data: {\"choices\":[{\"delta\":{\"content\":\"Anthropic!\"},\"finish_reason\":\"stop\"}]}\n\n"))
flusher.Flush()
_, _ = w.Write([]byte("data: [DONE]\n\n"))
flusher.Flush()
return
}
w.WriteHeader(http.StatusOK)
resp := map[string]any{
"id": "chatcmpl-test",
"model": "claude-model",
"choices": []any{
map[string]any{
"index": 0,
"message": map[string]any{
"role": "assistant",
"content": "Hello from Anthropic proxy",
},
"finish_reason": "stop",
},
},
"usage": map[string]any{
"prompt_tokens": 10,
"completion_tokens": 15,
"total_tokens": 25,
},
}
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
configs := []ModelConfig{
{Model: "claude-model", Key: "test-key", Endpoint: server.URL},
}
cm := &ConfigManager{
configs: configs,
uniqueModels: []string{"claude-model"},
}
handler := handleMessages(cm, nil, 0)
// 1. Non-streaming test
t.Run("Non-streaming Anthropic message", func(t *testing.T) {
reqObj := map[string]any{
"model": "claude-model",
"system": "You are a helpful bot",
"messages": []any{
map[string]any{
"role": "user",
"content": "Hello",
},
},
"max_tokens": 100,
}
reqBytes, _ := json.Marshal(reqObj)
req := httptest.NewRequest("POST", "/v1/messages", bytes.NewReader(reqBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Result().StatusCode != http.StatusOK {
t.Fatalf("Expected 200 OK, got %d", w.Result().StatusCode)
}
var anthropicResp map[string]any
_ = json.NewDecoder(w.Body).Decode(&anthropicResp)
if anthropicResp["type"] != "message" {
t.Errorf("Expected response type 'message', got %v", anthropicResp["type"])
}
if anthropicResp["role"] != "assistant" {
t.Errorf("Expected role 'assistant', got %v", anthropicResp["role"])
}
content, ok := anthropicResp["content"].([]any)
if !ok || len(content) == 0 {
t.Fatalf("Expected non-empty content array in Anthropic response, got %+v", anthropicResp)
}
textVal := content[0].(map[string]any)["text"].(string)
if textVal != "Hello from Anthropic proxy" {
t.Errorf("Unexpected text in response: %s", textVal)
}
// Verify system message was prepended into OpenAI messages format
openAIMsgs, ok := receivedOpenAIBody["messages"].([]any)
if !ok || len(openAIMsgs) != 2 {
t.Fatalf("Expected 2 messages (system + user) in OpenAI body, got %+v", receivedOpenAIBody)
}
if openAIMsgs[0].(map[string]any)["role"] != "system" {
t.Errorf("Expected first message to be system prompt")
}
})
// 2. Streaming test
t.Run("Streaming Anthropic message", func(t *testing.T) {
reqObj := map[string]any{
"model": "claude-model",
"stream": true,
"messages": []any{
map[string]any{
"role": "user",
"content": "Stream me",
},
},
}
reqBytes, _ := json.Marshal(reqObj)
req := httptest.NewRequest("POST", "/v1/messages", bytes.NewReader(reqBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Result().StatusCode != http.StatusOK {
t.Fatalf("Expected 200 OK, got %d", w.Result().StatusCode)
}
bodyStr := w.Body.String()
if !strings.Contains(bodyStr, "event: message_start") || !strings.Contains(bodyStr, "event: content_block_delta") || !strings.Contains(bodyStr, "event: message_stop") {
t.Errorf("Expected Anthropic SSE stream events in response, got: %s", bodyStr)
}
})
}
func TestOpenAIResponsesEndpoint(t *testing.T) {
var receivedOpenAIBody map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v1/chat/completions" {
t.Errorf("Expected request to be routed to /v1/chat/completions, got %s", r.URL.Path)
}
var body map[string]any
_ = json.NewDecoder(r.Body).Decode(&body)
receivedOpenAIBody = body
isStream, _ := body["stream"].(bool)
w.Header().Set("Content-Type", "application/json")
if isStream {
w.Header().Set("Content-Type", "text/event-stream")
w.WriteHeader(http.StatusOK)
flusher, _ := w.(http.Flusher)
_, _ = w.Write([]byte("data: {\"choices\":[{\"delta\":{\"content\":\"Response \"}}]}\n\n"))
flusher.Flush()
_, _ = w.Write([]byte("data: {\"choices\":[{\"delta\":{\"content\":\"Chunk\"},\"finish_reason\":\"stop\"}]}\n\n"))
flusher.Flush()
_, _ = w.Write([]byte("data: [DONE]\n\n"))
flusher.Flush()
return
}
w.WriteHeader(http.StatusOK)
resp := map[string]any{
"id": "chatcmpl-resp-test",
"model": "gpt-5-model",
"choices": []any{
map[string]any{
"index": 0,
"message": map[string]any{
"role": "assistant",
"content": "Hello from Responses proxy",
},
"finish_reason": "stop",
},
},
"usage": map[string]any{
"prompt_tokens": 12,
"completion_tokens": 18,
"total_tokens": 30,
},
}
_ = json.NewEncoder(w).Encode(resp)
}))
defer server.Close()
configs := []ModelConfig{
{Model: "gpt-5-model", Key: "test-key", Endpoint: server.URL},
}
cm := &ConfigManager{
configs: configs,
uniqueModels: []string{"gpt-5-model"},
}
handler := handleResponses(cm, nil, 0)
// 1. Non-streaming test
t.Run("Non-streaming OpenAI response", func(t *testing.T) {
reqObj := map[string]any{
"model": "gpt-5-model",
"instructions": "Be accurate",
"input": "What is 2+2?",
}
reqBytes, _ := json.Marshal(reqObj)
req := httptest.NewRequest("POST", "/v1/responses", bytes.NewReader(reqBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Result().StatusCode != http.StatusOK {
t.Fatalf("Expected 200 OK, got %d", w.Result().StatusCode)
}
var respObj map[string]any
_ = json.NewDecoder(w.Body).Decode(&respObj)
if respObj["object"] != "response" {
t.Errorf("Expected object 'response', got %v", respObj["object"])
}
if respObj["status"] != "completed" {
t.Errorf("Expected status 'completed', got %v", respObj["status"])
}
output, ok := respObj["output"].([]any)
if !ok || len(output) == 0 {
t.Fatalf("Expected output array in Responses API response, got %+v", respObj)
}
firstOutput := output[0].(map[string]any)
contentSlice, ok := firstOutput["content"].([]any)
if !ok || len(contentSlice) == 0 {
t.Fatalf("Expected output message content slice, got %+v", firstOutput)
}
textVal := contentSlice[0].(map[string]any)["text"].(string)
if textVal != "Hello from Responses proxy" {
t.Errorf("Unexpected text in response output: %s", textVal)
}
// Verify instructions and input were parsed into OpenAI messages format
openAIMsgs, ok := receivedOpenAIBody["messages"].([]any)
if !ok || len(openAIMsgs) != 2 {
t.Fatalf("Expected 2 messages in OpenAI body, got %+v", receivedOpenAIBody)
}
if openAIMsgs[0].(map[string]any)["role"] != "system" {
t.Errorf("Expected system role for instructions")
}
})
// 2. Streaming test
t.Run("Streaming OpenAI response", func(t *testing.T) {
reqObj := map[string]any{
"model": "gpt-5-model",
"stream": true,
"input": "Stream test",
}
reqBytes, _ := json.Marshal(reqObj)
req := httptest.NewRequest("POST", "/v1/responses", bytes.NewReader(reqBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Result().StatusCode != http.StatusOK {
t.Fatalf("Expected 200 OK, got %d", w.Result().StatusCode)
}
bodyStr := w.Body.String()
if !strings.Contains(bodyStr, "event: response.created") || !strings.Contains(bodyStr, "event: response.output_text.delta") || !strings.Contains(bodyStr, "event: response.completed") {
t.Errorf("Expected OpenAI Responses SSE events in response stream, got: %s", bodyStr)
}
})
}