package openai import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "iop/packages/go/config" iop "iop/proto/gen/iop" ) func TestResponsesDispatchesNonStreamingRequest(t *testing.T) { fake := &fakeRunService{events: make(chan *iop.RunEvent, 3)} fake.events <- &iop.RunEvent{Type: "delta", Delta: "hello"} fake.events <- &iop.RunEvent{Type: "delta", Delta: " world"} fake.events <- &iop.RunEvent{Type: "complete", Usage: &iop.Usage{InputTokens: 1, OutputTokens: 1}} srv := NewServer(config.EdgeOpenAIConf{ Adapter: "ollama", Target: "llama-fixed", SessionID: "configured-session", TimeoutSec: 42, }, fake, nil) req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{ "model":"client-model", "input":"say hello", "instructions":"Use short answers.", "max_output_tokens":16, "temperature":0.2, "top_p":0.9, "background":false }`)) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } if fake.req.Target != "llama-fixed" { t.Fatalf("target: got %q, want llama-fixed", fake.req.Target) } if fake.req.Prompt != "Use short answers.\n\nsay hello" { t.Fatalf("prompt: got %q", fake.req.Prompt) } options, ok := fake.req.Input["options"].(map[string]any) if !ok { t.Fatalf("options not passed: %+v", fake.req.Input) } if options["max_tokens"].(int) != 16 || options["temperature"].(float64) != 0.2 || options["top_p"].(float64) != 0.9 { t.Fatalf("unexpected response options: %+v", options) } if fake.req.TimeoutSec != 42 { t.Fatalf("timeout: got %d, want 42", fake.req.TimeoutSec) } if fake.req.SessionID != "configured-session" { t.Fatalf("session_id: got %q, want configured-session", fake.req.SessionID) } if fake.req.Background { t.Fatal("background should remain false for responses") } var resp responsesResponse if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("decode response: %v", err) } if resp.OutputText != "hello world" { t.Fatalf("output_text: got %q", resp.OutputText) } if len(resp.Output) == 0 || len(resp.Output[0].Content) == 0 || resp.Output[0].Content[0].Text != "hello world" { t.Fatalf("output[0].content[0].text wrong: %+v", resp.Output) } } func TestResponsesRejectsUnsupportedRequests(t *testing.T) { srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, &fakeRunService{}, nil) cases := []struct { name string method string body string want int }{ {"wrong method", http.MethodGet, "", http.StatusMethodNotAllowed}, {"stream=true", http.MethodPost, `{"model":"m","input":"hi","stream":true}`, http.StatusBadRequest}, {"empty input", http.MethodPost, `{"model":"m","input":""}`, http.StatusBadRequest}, {"non-string input", http.MethodPost, `{"model":"m","input":["a","b"]}`, http.StatusBadRequest}, {"options wrapper unsupported", http.MethodPost, `{"model":"m","input":"hi","options":{"max_output_tokens":16}}`, http.StatusBadRequest}, {"background unsupported", http.MethodPost, `{"model":"m","input":"hi","background":true}`, http.StatusBadRequest}, {"bad max_output_tokens", http.MethodPost, `{"model":"m","input":"hi","max_output_tokens":0}`, http.StatusBadRequest}, {"bad top_p", http.MethodPost, `{"model":"m","input":"hi","top_p":1.5}`, http.StatusBadRequest}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { req := httptest.NewRequest(tc.method, "/v1/responses", strings.NewReader(tc.body)) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != tc.want { t.Fatalf("got %d want %d body=%s", w.Code, tc.want, w.Body.String()) } }) } } func TestResponsesConfiguredTargetWinsOverRequestModel(t *testing.T) { fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)} fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"} fake.events <- &iop.RunEvent{Type: "complete"} srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "config-target"}, fake, nil) req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{ "model":"client-model", "input":"test" }`)) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } if fake.req.Target != "config-target" { t.Fatalf("target: got %q, want config-target", fake.req.Target) } var resp responsesResponse if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("decode: %v", err) } if resp.Model != "client-model" { t.Fatalf("model: got %q, want client-model", resp.Model) } } func TestResponsesReturnsOpenAICompatibleShape(t *testing.T) { fake := &fakeRunService{events: make(chan *iop.RunEvent, 3)} fake.events <- &iop.RunEvent{Type: "delta", Delta: "answer"} fake.events <- &iop.RunEvent{Type: "complete", Usage: &iop.Usage{InputTokens: 1, OutputTokens: 1}} srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, fake, nil) req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{ "model":"client-model", "input":"say hello" }`)) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } var resp responsesResponse if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("decode: %v", err) } if !strings.HasPrefix(resp.ID, "resp-") { t.Fatalf("id: got %q, want resp- prefix", resp.ID) } if resp.Model != "client-model" { t.Fatalf("model: got %q", resp.Model) } if resp.OutputText != "answer" { t.Fatalf("output_text: got %q", resp.OutputText) } if len(resp.Output) == 0 || len(resp.Output[0].Content) == 0 || resp.Output[0].Content[0].Text != "answer" { t.Fatalf("output[0].content[0].text wrong: %+v", resp.Output) } if resp.Usage.TotalTokens != 2 { t.Fatalf("usage.total_tokens: got %d", resp.Usage.TotalTokens) } } func TestResponsesSanitizesKnownSentinelToken(t *testing.T) { fake := &fakeRunService{events: make(chan *iop.RunEvent, 3)} fake.events <- &iop.RunEvent{Type: "reasoning_delta", Delta: "hidden <|mask_end|>"} fake.events <- &iop.RunEvent{Type: "delta", Delta: "answer <|mask_end|>"} fake.events <- &iop.RunEvent{Type: "complete", Usage: &iop.Usage{InputTokens: 1, OutputTokens: 1}} srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil) req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{ "model":"client-model", "input":"say hello" }`)) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } var resp responsesResponse if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("decode: %v", err) } if strings.Contains(resp.OutputText, "<|mask_end|>") || strings.Contains(w.Body.String(), "<|mask_end|>") { t.Fatalf("sentinel leaked in responses output: %s", w.Body.String()) } if resp.OutputText != "answer" { t.Fatalf("output_text: got %q, want answer", resp.OutputText) } } func TestResponsesUsageDefaultsToZeroObject(t *testing.T) { fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)} fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"} fake.events <- &iop.RunEvent{Type: "complete"} srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, fake, nil) req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{ "model":"m", "input":"hi" }`)) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } var body map[string]json.RawMessage if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil { t.Fatalf("decode: %v", err) } if _, ok := body["usage"]; !ok { t.Fatal("usage field missing from response") } var usage openAIUsage if err := json.Unmarshal(body["usage"], &usage); err != nil { t.Fatalf("usage is not an object: %s", body["usage"]) } } func TestResponsesStrictOutputNormalizesAgentResponse(t *testing.T) { fake := &fakeRunService{events: make(chan *iop.RunEvent, 3)} fake.events <- &iop.RunEvent{Type: "reasoning_delta", Delta: "hidden"} fake.events <- &iop.RunEvent{Type: "delta", Delta: "plain answer"} fake.events <- &iop.RunEvent{Type: "complete"} srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama", StrictOutput: true}, fake, nil) req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{ "model":"client-model", "input":"Once you've completed the user's task, you must use the attempt_completion tool to present the result.\n\n\ndone\n\n\nfinish" }`)) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } if fake.req.Metadata["strict_output"] != "true" { t.Fatalf("strict_output metadata: got %q", fake.req.Metadata["strict_output"]) } if fake.req.Input["think"] != false { t.Fatalf("strict output should disable thinking by default: %+v", fake.req.Input) } if !strings.Contains(fake.req.Prompt, "output exactly one block") { t.Fatalf("strict contract instruction not injected:\n%s", fake.req.Prompt) } var resp responsesResponse if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("decode: %v", err) } want := "\nplain answer\n" if resp.OutputText != want { t.Fatalf("output_text:\ngot %q\nwant %q", resp.OutputText, want) } if len(resp.Output) == 0 || len(resp.Output[0].Content) == 0 || resp.Output[0].Content[0].Text != want { t.Fatalf("output[0].content[0].text wrong: %+v", resp.Output) } }