iop/apps/edge/internal/openai/provider_selection_test.go
toki 01dc2ef78b refactor: readability baseline 및 테스트 구조 개선
- agent-readable-repository-refactor 완료: 기존 테스트 파일 아카이브 이동
- 새 테스트 파일 추가 (edge, node, client, config, readability)
- readability_audit 스크립트 및 baseline 추가
- roadmap/SDD 문서 갱신
- agent-client/pi/extensions/openai-sampling-parameters 추가
2026-07-17 16:02:12 +09:00

850 lines
30 KiB
Go

package openai
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
edgeservice "iop/apps/edge/internal/service"
"iop/packages/go/config"
iop "iop/proto/gen/iop"
)
// TestResponsesProviderPoolTunnelSelectionUsesPassthrough verifies that when a
// provider-pool catalog match is resolved and the selected provider is an
// OpenAI-compatible type (tunnel executionPath), the Responses handler
// dispatches via tunnel passthrough with the raw body preserved (model rewrite
// only). This mirrors TestChatCompletionsProviderPoolTunnelSelectionUsesPassthrough
// for /v1/responses.
func TestResponsesProviderPoolTunnelSelectionUsesPassthrough(t *testing.T) {
frames := make(chan *iop.ProviderTunnelFrame, 3)
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: 200}
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(`{"ok":true}`)}
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
close(frames)
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel),
tunnelFrames: frames,
}
catalog := []config.ModelCatalogEntry{
{ID: "vllm-resp-model", Providers: map[string]string{"prov-vllm": "Qwen3-35B"}},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"vllm-resp-model",
"input":"hello world"
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// Tunnel passthrough must use SubmitProviderPool tunnel path.
tunnelReqs := fake.tunnelReqsSnapshot()
reqs := fake.reqsSnapshot()
if len(tunnelReqs) != 1 {
t.Fatalf("expected 1 tunnel dispatch for vLLM path, got %d", len(tunnelReqs))
}
if len(reqs) != 0 {
t.Fatalf("expected 0 normalized SubmitRun for vLLM tunnel path, got %d", len(reqs))
}
if !tunnelReqs[0].ProviderPool {
t.Error("tunnel dispatch must have ProviderPool=true")
}
if tunnelReqs[0].Path != "/v1/responses" {
t.Fatalf("tunnel path: got %q want /v1/responses", tunnelReqs[0].Path)
}
}
// TestResponsesProviderPoolOllamaSelectionUsesNormalizedRun verifies that when a
// provider-pool catalog match is resolved and the selected provider is an
// Ollama type (normalized executionPath), the Responses handler dispatches via
// normalized RunEvent path with the decoded prompt/input, not via tunnel. This
// mirrors TestChatCompletionsProviderPoolOllamaSelectionUsesNormalizedRun for
// /v1/responses.
func TestResponsesProviderPoolOllamaSelectionUsesNormalizedRun(t *testing.T) {
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
}
catalog := []config.ModelCatalogEntry{
{ID: "ollama-resp-model", Providers: map[string]string{"prov-ollama": "qwen3.6:35b"}},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"ollama-resp-model",
"input":"hello world"
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// Normalized Ollama path must use SubmitProviderPool normalized path.
reqs := fake.reqsSnapshot()
tunnelReqs := fake.tunnelReqsSnapshot()
if len(reqs) != 1 {
t.Fatalf("expected 1 normalized SubmitRun for Ollama path, got %d", len(reqs))
}
if len(tunnelReqs) != 0 {
t.Fatalf("expected 0 tunnel dispatch for Ollama normalized path, got %d", len(tunnelReqs))
}
if !reqs[0].ProviderPool {
t.Error("normalized dispatch must have ProviderPool=true")
}
if reqs[0].ModelGroupKey != "ollama-resp-model" {
t.Fatalf("expected model group key %q, got %q", "ollama-resp-model", reqs[0].ModelGroupKey)
}
}
// TestResponsesMixedProviderPoolTunnelSelection verifies S05: a mixed
// provider-pool model group that selects an OpenAI-compatible (vLLM) provider
// dispatches via tunnel passthrough for /v1/responses.
func TestResponsesMixedProviderPoolTunnelSelection(t *testing.T) {
tunnelFrames := make(chan *iop.ProviderTunnelFrame, 3)
tunnelFrames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: 200}
tunnelFrames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(`{"id":"resp-mixed","object":"response"}`)}
tunnelFrames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
close(tunnelFrames)
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel),
tunnelFrames: tunnelFrames,
}
catalog := []config.ModelCatalogEntry{
{
ID: "mixed-resp-model",
Providers: map[string]string{
"prov-vllm": "served-vllm",
"prov-ollama": "served-ollama",
},
},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"mixed-resp-model",
"input":"hello mixed"
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
tunnelReqs := fake.tunnelReqsSnapshot()
if len(tunnelReqs) != 1 {
t.Fatalf("expected 1 tunnel dispatch, got %d", len(tunnelReqs))
}
if !tunnelReqs[0].ProviderPool {
t.Error("tunnel request must have ProviderPool=true")
}
if tunnelReqs[0].Path != "/v1/responses" {
t.Fatalf("tunnel path: got %q want /v1/responses", tunnelReqs[0].Path)
}
runReqs := fake.reqsSnapshot()
if len(runReqs) != 0 {
t.Errorf("expected 0 normalized SubmitRun calls, got %d", len(runReqs))
}
}
// TestResponsesMixedProviderPoolOllamaSelection verifies S05: when the mixed
// model group selects an Ollama provider, the request is dispatched over the
// normalized path (SubmitRun), and no tunnel dispatch is issued for /v1/responses.
func TestResponsesMixedProviderPoolOllamaSelection(t *testing.T) {
events := bufferedRunEvents(
&iop.RunEvent{Type: "delta", Delta: "ollama-resp"},
&iop.RunEvent{Type: "complete"},
)
fake := &providerFakeRunService{
fakeRunService: fakeRunService{
events: events,
},
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
}
catalog := []config.ModelCatalogEntry{
{
ID: "mixed-resp-model",
Providers: map[string]string{
"prov-vllm": "served-vllm",
"prov-ollama": "served-ollama",
},
},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"mixed-resp-model",
"input":"hello mixed"
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
tunnelReqs := fake.tunnelReqsSnapshot()
if len(tunnelReqs) != 0 {
t.Fatalf("expected 0 tunnel dispatches for Ollama selection, got %d", len(tunnelReqs))
}
runReqs := fake.reqsSnapshot()
if len(runReqs) != 1 {
t.Fatalf("expected 1 normalized SubmitRun, got %d", len(runReqs))
}
if runReqs[0].ModelGroupKey != "mixed-resp-model" {
t.Errorf("normalized ModelGroupKey: got %q, want mixed-resp-model", runReqs[0].ModelGroupKey)
}
if !runReqs[0].ProviderPool {
t.Error("normalized run must have ProviderPool=true")
}
}
func TestChatCompletionsProviderPoolOllamaSelectionUsesNormalizedRun(t *testing.T) {
runEvents := make(chan *iop.RunEvent, 2)
runEvents <- &iop.RunEvent{Type: "complete"}
close(runEvents)
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
poolRunFrames: runEvents,
}
catalog := []config.ModelCatalogEntry{
{ID: "ollama-model", Providers: map[string]string{"prov-ollama": "qwen3.6:35b"}},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
"model":"ollama-model",
"messages":[{"role":"user","content":"hello"}]
}`))
w := httptest.NewRecorder()
srv.handleChatCompletions(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// Ollama-selected provider-pool should use normalized path (SubmitRun),
// NOT tunnel passthrough.
tunnelReqs := fake.tunnelReqsSnapshot()
reqs := fake.reqsSnapshot()
if len(tunnelReqs) != 0 {
t.Fatalf("expected 0 tunnel dispatches for Ollama-normalized path, got %d", len(tunnelReqs))
}
if len(reqs) != 1 {
t.Fatalf("expected exactly 1 normalized SubmitRun for Ollama path, got %d", len(reqs))
}
}
// TestChatCompletionsProviderPoolTunnelSelectionUsesPassthrough verifies that
// when a provider-pool catalog match is resolved and the selected provider is
// an OpenAI-compatible type (tunnel executionPath), the Chat handler dispatches
// via tunnel passthrough.
func TestChatCompletionsProviderPoolTunnelSelectionUsesPassthrough(t *testing.T) {
frames := make(chan *iop.ProviderTunnelFrame, 3)
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: 200}
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(`{"ok":true}`)}
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
close(frames)
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel),
tunnelFrames: frames,
}
catalog := []config.ModelCatalogEntry{
{ID: "vllm-model", Providers: map[string]string{"prov-vllm": "Qwen3-35B"}},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
"model":"vllm-model",
"messages":[{"role":"user","content":"hello"}]
}`))
w := httptest.NewRecorder()
srv.handleChatCompletions(w, req)
// OpenAI-compatible provider-pool should use tunnel passthrough.
tunnelReqs := fake.tunnelReqsSnapshot()
reqs := fake.reqsSnapshot()
if len(tunnelReqs) != 1 {
t.Fatalf("expected 1 tunnel dispatch for vLLM path, got %d", len(tunnelReqs))
}
if !tunnelReqs[0].ProviderPool {
t.Error("tunnel dispatch must have ProviderPool=true")
}
if len(reqs) != 0 {
t.Fatalf("expected 0 normalized SubmitRun for vLLM tunnel path, got %d", len(reqs))
}
}
// TestChatCompletionsMixedProviderPoolTunnelSelection verifies S05: a mixed
// model group containing both OpenAI-compatible and Ollama providers dispatches
// the selected tunnel-capable provider over the provider tunnel (passthrough)
// path. Only one tunnel request is sent, no normalized SubmitRun is called.
func TestChatCompletionsMixedProviderPoolTunnelSelection(t *testing.T) {
// Tunnel frames for the tunnel path.
tunnelFrames := make(chan *iop.ProviderTunnelFrame, 3)
tunnelFrames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: 200}
tunnelFrames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(`{"id":"cmpl-mixed","choices":[{"index":0,"message":{"role":"assistant","content":"tunnel"}}],"finish_reason":"stop"}`)}
tunnelFrames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
close(tunnelFrames)
// Fake service selects tunnel path (simulating vLLM provider selection).
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel),
tunnelFrames: tunnelFrames,
}
// Mixed catalog: OpenAI-compatible (vLLM) + Ollama in same group.
catalog := []config.ModelCatalogEntry{
{
ID: "mixed-model",
Providers: map[string]string{
"prov-vllm": "served-vllm",
"prov-ollama": "served-ollama",
},
},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
"model":"mixed-model",
"messages":[{"role":"user","content":"hello"}]
}`))
w := httptest.NewRecorder()
srv.handleChatCompletions(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// Tunnel path: exactly 1 tunnel dispatch, 0 normalized SubmitRun.
tunnelReqs := fake.tunnelReqsSnapshot()
if len(tunnelReqs) != 1 {
t.Fatalf("expected 1 tunnel dispatch, got %d", len(tunnelReqs))
}
if !tunnelReqs[0].ProviderPool {
t.Error("tunnel request must have ProviderPool=true")
}
if tunnelReqs[0].ModelGroupKey != "mixed-model" {
t.Errorf("tunnel ModelGroupKey: got %q, want mixed-model", tunnelReqs[0].ModelGroupKey)
}
runReqs := fake.reqsSnapshot()
if len(runReqs) != 0 {
t.Errorf("expected 0 normalized SubmitRun calls, got %d", len(runReqs))
}
}
// TestChatCompletionsMixedProviderPoolOllamaSelection verifies S05: when the
// mixed model group selects an Ollama provider, the request is dispatched over
// the normalized path (SubmitRun), and no tunnel dispatch is issued.
func TestChatCompletionsMixedProviderPoolOllamaSelection(t *testing.T) {
// Normalized path events.
events := bufferedRunEvents(
&iop.RunEvent{Type: "delta", Delta: "ollama"},
&iop.RunEvent{Type: "complete"},
)
// Fake service selects normalized path (simulating Ollama provider selection).
fake := &providerFakeRunService{
fakeRunService: fakeRunService{
events: events,
},
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
}
// Same mixed catalog: vLLM + Ollama.
catalog := []config.ModelCatalogEntry{
{
ID: "mixed-model",
Providers: map[string]string{
"prov-vllm": "served-vllm",
"prov-ollama": "served-ollama",
},
},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
"model":"mixed-model",
"messages":[{"role":"user","content":"hello"}]
}`))
w := httptest.NewRecorder()
srv.handleChatCompletions(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// Normalized path: 0 tunnel dispatches, 1 normalized SubmitRun.
tunnelReqs := fake.tunnelReqsSnapshot()
if len(tunnelReqs) != 0 {
t.Fatalf("expected 0 tunnel dispatches for Ollama selection, got %d", len(tunnelReqs))
}
runReqs := fake.reqsSnapshot()
if len(runReqs) != 1 {
t.Fatalf("expected 1 normalized SubmitRun, got %d", len(runReqs))
}
// Adapter/Target are rewritten by the service's admitted candidate; the
// fake here records the pre-admission request so the handler-level path
// selection (tunnel vs normalized) is the assertion surface.
if runReqs[0].ModelGroupKey != "mixed-model" {
t.Errorf("normalized ModelGroupKey: got %q, want mixed-model", runReqs[0].ModelGroupKey)
}
if !runReqs[0].ProviderPool {
t.Error("normalized run must have ProviderPool=true")
}
}
// TestChatCompletionsOllamaOnlyProviderPoolUsesNormalizedPath verifies S05:
// a model group containing only Ollama providers always dispatches over the
// normalized path. No tunnel request is ever issued.
func TestChatCompletionsOllamaOnlyProviderPoolUsesNormalizedPath(t *testing.T) {
events := bufferedRunEvents(
&iop.RunEvent{Type: "delta", Delta: "ollama-only"},
&iop.RunEvent{Type: "complete"},
)
// Fake service selects normalized path.
fake := &providerFakeRunService{
fakeRunService: fakeRunService{
events: events,
},
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
}
// Ollama-only catalog.
catalog := []config.ModelCatalogEntry{
{
ID: "ollama-only-model",
Providers: map[string]string{
"prov-ollama": "qwen3.6:35b",
},
},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
"model":"ollama-only-model",
"messages":[{"role":"user","content":"hello"}]
}`))
w := httptest.NewRecorder()
srv.handleChatCompletions(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// Must not dispatch tunnel.
tunnelReqs := fake.tunnelReqsSnapshot()
if len(tunnelReqs) != 0 {
t.Fatalf("ollama-only must not dispatch tunnel, got %d", len(tunnelReqs))
}
// Must dispatch exactly one normalized run.
runReqs := fake.reqsSnapshot()
if len(runReqs) != 1 {
t.Fatalf("expected 1 normalized SubmitRun, got %d", len(runReqs))
}
if runReqs[0].ModelGroupKey != "ollama-only-model" {
t.Errorf("ollama-only ModelGroupKey: got %q, want ollama-only-model", runReqs[0].ModelGroupKey)
}
if !runReqs[0].ProviderPool {
t.Error("ollama-only normalized run must have ProviderPool=true")
}
}
// TestChatCompletionsProviderPoolTunnelPreservesUnknownFields verifies that
// when the provider-pool dispatch selects the tunnel path, the caller's raw
// body is forwarded with model rewrite only: unknown top-level fields
// (store, codex_trace, custom_provider_options, nested unknown object) and
// Codex/provider-specific fields survive into the tunnel request body. No
// IOP sideband key is injected into the pure response payload (SURFACE_FIELD).
func TestChatCompletionsProviderPoolTunnelPreservesUnknownFields(t *testing.T) {
frames := make(chan *iop.ProviderTunnelFrame, 3)
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: 200}
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(`{"ok":true}`)}
frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
close(frames)
fake := &providerFakeRunService{
tunnelFrames: frames,
tunnelServedTarget: "served-model",
}
catalog := []config.ModelCatalogEntry{
{ID: "pool-model", Providers: map[string]string{"prov-vllm": "served-model"}},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
reqBody := `{
"model":"pool-model",
"messages":[{"role":"user","content":"hello"}],
"store":true,
"parallel_tool_calls":true,
"codex_trace":{"enabled":true},
"custom_provider_options":{"timeout_ms":5000},
"nested":{"unknown":{"deep":{"value":42}}},
"thinking_token_budget":1024
}`
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(reqBody))
w := httptest.NewRecorder()
srv.handleChatCompletions(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
tunnelReqs := fake.tunnelReqsSnapshot()
if len(tunnelReqs) != 1 {
t.Fatalf("expected 1 tunnel dispatch, got %d", len(tunnelReqs))
}
// Parse the tunnel body as a JSON map and verify model rewrite + unknown preservation.
tunnelBodies := fake.tunnelBodiesSnapshot()
if len(tunnelBodies) != 1 {
t.Fatalf("expected 1 tunnel body, got %d", len(tunnelBodies))
}
var bodyMap map[string]json.RawMessage
if err := json.Unmarshal(tunnelBodies[0], &bodyMap); err != nil {
t.Fatalf("decode tunnel body: %v body=%s", err, string(tunnelBodies[0]))
}
// Model must be rewritten to the served target.
var bodyModel string
if err := json.Unmarshal(bodyMap["model"], &bodyModel); err != nil {
t.Fatalf("decode model field: %v", err)
}
if bodyModel != "served-model" {
t.Errorf("model rewrite: got %q, want served-model", bodyModel)
}
// Known fields must survive.
if _, ok := bodyMap["messages"]; !ok {
t.Error("messages must be preserved in tunnel body")
}
if _, ok := bodyMap["store"]; !ok {
t.Error("store field must be preserved in tunnel body")
}
if _, ok := bodyMap["parallel_tool_calls"]; !ok {
t.Error("parallel_tool_calls field must be preserved in tunnel body")
}
if _, ok := bodyMap["codex_trace"]; !ok {
t.Error("codex_trace field must be preserved in tunnel body")
}
if _, ok := bodyMap["custom_provider_options"]; !ok {
t.Error("custom_provider_options field must be preserved in tunnel body")
}
if _, ok := bodyMap["nested"]; !ok {
t.Error("nested unknown object must be preserved in tunnel body")
}
if _, ok := bodyMap["thinking_token_budget"]; !ok {
t.Error("thinking_token_budget must be preserved in tunnel body")
}
// No IOP-internal metadata keys should leak into the pure passthrough body.
for _, key := range []string{"estimated_input_tokens", "context_class"} {
if _, ok := bodyMap[key]; ok {
t.Errorf("IOP internal key %q must not be in tunnel body", key)
}
}
if len(fake.reqsSnapshot()) != 0 {
t.Errorf("normalized SubmitRun must not be called for tunnel path, got %d", len(fake.reqsSnapshot()))
}
}
// TestChatCompletionsProviderPoolNormalizedIgnoresUnknownFields verifies that
// when the provider-pool dispatch selects the normalized path, the lenient
// ingress accepts unknown/provider-specific fields without rejecting the
// request, but the normalized RunRequest.Input only reflects standard supported
// fields. Unknown fields must never be invented or propagated into the adapter
// native request (SURFACE_FIELD).
func TestChatCompletionsProviderPoolNormalizedIgnoresUnknownFields(t *testing.T) {
completeEvent := &iop.RunEvent{
Type: "delta", Delta: "ok",
}
completeEvent2 := &iop.RunEvent{Type: "complete"}
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
poolRunFrames: bufferedRunEvents(completeEvent, completeEvent2),
}
catalog := []config.ModelCatalogEntry{
{ID: "ollama-pool-model", Providers: map[string]string{"prov-ollama": "qwen3.6:35b"}},
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog(catalog)
reqBody := `{
"model":"ollama-pool-model",
"messages":[{"role":"user","content":"hello"}],
"temperature":0.7,
"store":true,
"codex_trace":{"enabled":true},
"custom_provider_options":{"timeout_ms":5000},
"nested_unknown":{"deep":true},
"reasoning_effort":"high"
}`
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(reqBody))
w := httptest.NewRecorder()
srv.handleChatCompletions(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// The lenient ingress must NOT reject the request due to unknown fields.
runReqs := fake.reqsSnapshot()
if len(runReqs) != 1 {
t.Fatalf("expected 1 normalized SubmitRun, got %d", len(runReqs))
}
runReq := runReqs[0]
if !runReq.ProviderPool {
t.Error("normalized pool run must have ProviderPool=true")
}
// Standard supported fields must be present in Input.
input := runReq.Input
if _, ok := input["prompt"]; !ok {
t.Error("prompt must be in normalized Input")
}
if _, ok := input["messages"]; !ok {
t.Error("messages must be in normalized Input")
}
options, ok := input["options"].(map[string]any)
if !ok {
t.Fatalf("options must be in normalized Input, got %T", input["options"])
}
if options["temperature"] != 0.7 {
t.Errorf("temperature must be forwarded to normalized options, got %+v", options["temperature"])
}
// Unknown/provider-specific fields must NOT be in the normalized Input.
for _, key := range []string{"store", "codex_trace", "custom_provider_options", "nested_unknown"} {
if _, ok := input[key]; ok {
t.Errorf("unknown field %q must not be in normalized Input", key)
}
if _, ok := options[key]; ok {
t.Errorf("unknown field %q must not be in normalized Input options", key)
}
}
// The tunnel path must not be taken.
tunnelReqs := fake.tunnelReqsSnapshot()
if len(tunnelReqs) != 0 {
t.Errorf("tunnel must not be dispatched for normalized path, got %d", len(tunnelReqs))
}
}
// TestResponsesProviderPoolTunnelUnknownFieldsUsesPoolSelection asserts that
// when the selected provider is tunnel-passthrough (OpenAI-compatible),
// unknown fields (tools, parallel_tool_calls, store, custom field) are
// preserved verbatim in the tunnel body, and that the Run sent to
// SubmitProviderPool carries a non-empty ModelGroupKey.
func TestResponsesProviderPoolTunnelUnknownFieldsUsesPoolSelection(t *testing.T) {
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel),
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog([]config.ModelCatalogEntry{
{ID: "pool-model", Providers: map[string]string{"prov-tunnel": "served-tunnel"}},
})
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"pool-model",
"input":"hello world",
"tools":[{"type":"function","function":{"name":"lookup"}}],
"parallel_tool_calls":true,
"store":false,
"custom_field":"keep-me"
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// SubmitProviderPool must have been called with a populated Run.
lastRun := fake.poolLastRunSnapshot()
if lastRun.ModelGroupKey == "" {
t.Error("SubmitProviderPool was called with empty Run.ModelGroupKey")
}
// The tunnel body must preserve unknown fields verbatim.
if len(fake.tunnelBodiesSnapshot()) != 1 {
t.Fatalf("expected exactly one tunnel body, got %d", len(fake.tunnelBodiesSnapshot()))
}
body := fake.tunnelBodiesSnapshot()[0]
if !strings.Contains(string(body), "\"tools\"") {
t.Error("tunnel body must preserve tools field")
}
if !strings.Contains(string(body), "\"parallel_tool_calls\"") {
t.Error("tunnel body must preserve parallel_tool_calls field")
}
if !strings.Contains(string(body), "\"store\"") {
t.Error("tunnel body must preserve store field")
}
if !strings.Contains(string(body), "\"custom_field\"") {
t.Error("tunnel body must preserve custom_field")
}
}
// TestResponsesProviderPoolOllamaSelectionRejectsUnknownFieldsWithoutTunnel
// asserts that when the selected provider is normalized (Ollama/CLI), an
// unknown-field request returns HTTP 400 and no tunnel or run dispatch occurs.
func TestResponsesProviderPoolOllamaSelectionRejectsUnknownFieldsWithoutTunnel(t *testing.T) {
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog([]config.ModelCatalogEntry{
{ID: "ollama-model", Providers: map[string]string{"prov-ollama": "served-ollama"}},
})
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"ollama-model",
"input":"hello",
"unknown_field":"reject-me"
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("status: got %d body=%s, want 400", w.Code, w.Body.String())
}
// No tunnel or run dispatch should have occurred.
if len(fake.tunnelReqsSnapshot()) != 0 {
t.Error("normalized path must not dispatch tunnel for unknown fields")
}
if len(fake.reqsSnapshot()) != 0 {
t.Error("normalized path must not dispatch run for unknown fields")
}
// Verify pool dispatch was made.
if fake.poolSubmitCountSnapshot() != 1 {
t.Fatalf("pool submit count: got %d, want 1", fake.poolSubmitCountSnapshot())
}
// Verify PrepareRun was called on the normalized path.
prepareRunCalled := fake.poolPrepareRunCalledSnapshot()
if !prepareRunCalled {
t.Errorf("PrepareRun must be called on normalized path (poolSubmitCount=%d, poolLastRun.ModelGroupKey=%q)", fake.poolSubmitCountSnapshot(), fake.poolLastRunSnapshot().ModelGroupKey)
}
}
// TestResponsesProviderPoolOllamaSelectionRejectsStreamingWithoutTunnel asserts
// that when the selected provider is normalized, stream:true returns HTTP 400
// and no tunnel dispatch occurs.
func TestResponsesProviderPoolOllamaSelectionRejectsStreamingWithoutTunnel(t *testing.T) {
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathNormalized),
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog([]config.ModelCatalogEntry{
{ID: "ollama-model", Providers: map[string]string{"prov-ollama": "served-ollama"}},
})
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"ollama-model",
"input":"hello",
"stream":true
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("status: got %d body=%s, want 400", w.Code, w.Body.String())
}
// No tunnel dispatch for normalized path with stream:true.
if len(fake.tunnelReqsSnapshot()) != 0 {
t.Error("normalized path must not dispatch tunnel for stream:true")
}
if len(fake.reqsSnapshot()) != 0 {
t.Error("normalized path must not dispatch run for stream:true")
}
if !fake.poolPrepareRunCalledSnapshot() {
t.Error("PrepareRun must be called on normalized path")
}
}
// TestResponsesProviderPoolTunnelStreamingUsesPoolSelection asserts that when
// the selected provider is tunnel-passthrough, stream:true is relayed through
// SubmitProviderPool with the raw SSE body preserved.
func TestResponsesProviderPoolTunnelStreamingUsesPoolSelection(t *testing.T) {
fake := &providerFakeRunService{
poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel),
tunnelFrames: staticProviderTunnelFrames(`{"id":"resp-1","output":[{"type":"message","role":"assistant","content":[{"type":"output_text","text":"hello"}]}],"stream":true}\n`),
}
srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
srv.SetModelCatalog([]config.ModelCatalogEntry{
{ID: "pool-model", Providers: map[string]string{"prov-tunnel": "served-tunnel"}},
})
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"pool-model",
"input":"hello",
"stream":true
}`))
w := httptest.NewRecorder()
srv.handleResponses(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
}
// SubmitProviderPool must have been called.
if fake.poolSubmitCountSnapshot() != 1 {
t.Fatalf("SubmitProviderPool call count: got %d, want 1", fake.poolSubmitCountSnapshot())
}
// The tunnel body must preserve the raw SSE payload.
if len(fake.tunnelBodiesSnapshot()) != 1 {
t.Fatalf("expected one tunnel body, got %d", len(fake.tunnelBodiesSnapshot()))
}
body := fake.tunnelBodiesSnapshot()[0]
if !strings.Contains(string(body), "\"stream\":true") {
t.Error("tunnel body must preserve stream:true")
}
// PrepareRun must NOT be called on the tunnel path.
if fake.poolPrepareRunCalledSnapshot() {
t.Error("PrepareRun must NOT be called on tunnel path")
}
}