288 lines · 11.0 KB · Go
RawDownload
package executor import (	"context"	"io"	"net/http"	"net/http/httptest"	"testing" 	"github.com/router-for-me/CLIProxyAPI/v7/internal/config"	cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"	cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"	sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"	"github.com/tidwall/gjson") func TestCodexExecutorExecuteResponsesLiteHeaderDoesNotInjectImageGenerationTool(t *testing.T) {	var gotBody []byte	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {		body, errRead := io.ReadAll(r.Body)		if errRead != nil {			t.Fatalf("read request body: %v", errRead)		}		gotBody = body		w.Header().Set("Content-Type", "text/event-stream")		_, _ = w.Write([]byte("data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_1\",\"object\":\"response\",\"status\":\"completed\",\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0}}}\n\n"))	}))	defer server.Close() 	executor := NewCodexExecutor(&config.Config{})	auth := &cliproxyauth.Auth{		Provider: "codex",		Attributes: map[string]string{			"api_key":   "test",			"base_url":  server.URL,			"plan_type": "pro",		},	}	headers := make(http.Header)	headers.Set("X-OpenAI-Internal-Codex-Responses-Lite", "true") 	_, err := executor.Execute(context.Background(), auth, cliproxyexecutor.Request{		Model:   "gpt-5.6-sol",		Payload: []byte(`{"model":"gpt-5.6-sol","input":"hello"}`),	}, cliproxyexecutor.Options{		SourceFormat: sdktranslator.FromString("openai-response"),		Headers:      headers,	})	if err != nil {		t.Fatalf("Execute() error = %v", err)	}	if tools := gjson.GetBytes(gotBody, "tools"); tools.Exists() {		t.Fatalf("unexpected tools in responses-lite upstream payload: %s", tools.Raw)	}	parallelToolCalls := gjson.GetBytes(gotBody, "parallel_tool_calls")	if !parallelToolCalls.Exists() || parallelToolCalls.Bool() {		t.Fatalf("responses-lite parallel_tool_calls should be false: %s", gotBody)	}} func TestCodexExecutorExecuteStreamResponsesLiteHeaderForcesParallelToolCallsFalse(t *testing.T) {	var gotBody []byte	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {		body, errRead := io.ReadAll(r.Body)		if errRead != nil {			t.Fatalf("read request body: %v", errRead)		}		gotBody = body		w.Header().Set("Content-Type", "text/event-stream")		_, _ = w.Write([]byte("data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp_1\",\"object\":\"response\",\"status\":\"completed\",\"output\":[],\"usage\":{\"input_tokens\":0,\"output_tokens\":0,\"total_tokens\":0}}}\n\n"))	}))	defer server.Close() 	executor := NewCodexExecutor(&config.Config{})	auth := &cliproxyauth.Auth{		Provider: "codex",		Attributes: map[string]string{			"api_key":   "test",			"base_url":  server.URL,			"plan_type": "pro",		},	}	headers := make(http.Header)	headers.Set(codexResponsesLiteHeader, "true") 	result, errExecute := executor.ExecuteStream(context.Background(), auth, cliproxyexecutor.Request{		Model:   "gpt-5.6-luna",		Payload: []byte(`{"model":"gpt-5.6-luna","input":"hello"}`),	}, cliproxyexecutor.Options{		SourceFormat: sdktranslator.FromString("openai-response"),		Headers:      headers,	})	if errExecute != nil {		t.Fatalf("ExecuteStream() error = %v", errExecute)	}	for chunk := range result.Chunks {		if chunk.Err != nil {			t.Fatalf("stream chunk error = %v", chunk.Err)		}	} 	parallelToolCalls := gjson.GetBytes(gotBody, "parallel_tool_calls")	if !parallelToolCalls.Exists() || parallelToolCalls.Bool() {		t.Fatalf("responses-lite parallel_tool_calls should be false: %s", gotBody)	}} func TestEnsureImageGenerationTool_ResponsesLiteMetadataDoesNotInjectTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.6-sol","client_metadata":{"ws_request_header_x_openai_internal_codex_responses_lite":"true"},"input":[{"role":"user","content":"hello"}]}`)	result := ensureImageGenerationTool(body, "gpt-5.6-sol", nil, nil) 	if string(result) != string(body) {		t.Fatalf("expected responses-lite body to be unchanged, got %s", string(result))	}	if gjson.GetBytes(result, "tools").Exists() {		t.Fatalf("expected no injected tools for responses-lite request, got %s", gjson.GetBytes(result, "tools").Raw)	}} func TestEnsureImageGenerationTool_ResponsesLiteBooleanMetadataDoesNotInjectTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.6-sol","client_metadata":{"ws_request_header_x_openai_internal_codex_responses_lite":true},"input":"hello"}`)	result := ensureImageGenerationTool(body, "gpt-5.6-sol", nil, nil) 	if string(result) != string(body) {		t.Fatalf("expected responses-lite body to be unchanged, got %s", string(result))	}} func TestEnsureImageGenerationTool_ResponsesLiteHeaderDoesNotInjectTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.6-sol","input":"hello"}`)	headers := make(http.Header)	headers.Set("X-OpenAI-Internal-Codex-Responses-Lite", "true")	result := ensureImageGenerationTool(body, "gpt-5.6-sol", nil, headers) 	if string(result) != string(body) {		t.Fatalf("expected responses-lite body to be unchanged, got %s", string(result))	}} func TestEnsureImageGenerationTool_ResponsesLiteFalseMetadataStillInjectsTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.6-sol","client_metadata":{"ws_request_header_x_openai_internal_codex_responses_lite":"false"},"input":"hello"}`)	result := ensureImageGenerationTool(body, "gpt-5.6-sol", nil, nil) 	if got := gjson.GetBytes(result, "tools.0.type").String(); got != "image_generation" {		t.Fatalf("tools.0.type = %q, want image_generation; body=%s", got, result)	}} func TestEnsureImageGenerationTool_NoTools(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","input":"draw a cat"}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	tools := gjson.GetBytes(result, "tools")	if !tools.IsArray() {		t.Fatalf("expected tools array, got %v", tools.Type)	}	arr := tools.Array()	if len(arr) != 1 {		t.Fatalf("expected 1 tool, got %d", len(arr))	}	if arr[0].Get("type").String() != "image_generation" {		t.Fatalf("expected type=image_generation, got %s", arr[0].Get("type").String())	}	if arr[0].Get("output_format").String() != "png" {		t.Fatalf("expected output_format=png, got %s", arr[0].Get("output_format").String())	}} func TestEnsureImageGenerationTool_ExistingToolsWithoutImageGen(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","tools":[{"type":"function","name":"get_weather","parameters":{}}]}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	tools := gjson.GetBytes(result, "tools")	arr := tools.Array()	if len(arr) != 2 {		t.Fatalf("expected 2 tools, got %d", len(arr))	}	if arr[0].Get("type").String() != "function" {		t.Fatalf("expected first tool type=function, got %s", arr[0].Get("type").String())	}	if arr[1].Get("type").String() != "image_generation" {		t.Fatalf("expected second tool type=image_generation, got %s", arr[1].Get("type").String())	}} func TestEnsureImageGenerationTool_AlreadyPresent(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","tools":[{"type":"image_generation","output_format":"webp"},{"type":"function","name":"f1"}]}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	tools := gjson.GetBytes(result, "tools")	arr := tools.Array()	if len(arr) != 2 {		t.Fatalf("expected 2 tools (no duplicate), got %d", len(arr))	}	if arr[0].Get("output_format").String() != "webp" {		t.Fatalf("expected original output_format=webp preserved, got %s", arr[0].Get("output_format").String())	}} func TestEnsureImageGenerationTool_ImageGenNamespaceDoesNotInjectTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","tools":[{"type":"namespace","name":"image_gen","tools":[{"type":"function","name":"imagegen","parameters":{}}]}]}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	if string(result) != string(body) {		t.Fatalf("expected body to be unchanged, got %s", string(result))	}} func TestEnsureImageGenerationTool_FlattenedImageGenFunctionDoesNotInjectTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","tools":[{"type":"function","name":"image_gen.imagegen","parameters":{}}]}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	if string(result) != string(body) {		t.Fatalf("expected body to be unchanged, got %s", string(result))	}} func TestEnsureImageGenerationTool_SimilarNamespaceStillInjectsTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","tools":[{"type":"namespace","name":"image_tools","tools":[{"type":"function","name":"imagegen","parameters":{}}]}]}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	tools := gjson.GetBytes(result, "tools").Array()	if len(tools) != 2 {		t.Fatalf("expected 2 tools, got %d", len(tools))	}	if tools[1].Get("type").String() != "image_generation" {		t.Fatalf("expected second tool type=image_generation, got %s", tools[1].Get("type").String())	}} func TestEnsureImageGenerationTool_EmptyToolsArray(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","tools":[]}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	tools := gjson.GetBytes(result, "tools")	arr := tools.Array()	if len(arr) != 1 {		t.Fatalf("expected 1 tool, got %d", len(arr))	}	if arr[0].Get("type").String() != "image_generation" {		t.Fatalf("expected type=image_generation, got %s", arr[0].Get("type").String())	}} func TestEnsureImageGenerationTool_WebSearchAndImageGen(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","tools":[{"type":"web_search"}]}`)	result := ensureImageGenerationTool(body, "gpt-5.4", nil, nil) 	tools := gjson.GetBytes(result, "tools")	arr := tools.Array()	if len(arr) != 2 {		t.Fatalf("expected 2 tools, got %d", len(arr))	}	if arr[0].Get("type").String() != "web_search" {		t.Fatalf("expected first tool type=web_search, got %s", arr[0].Get("type").String())	}	if arr[1].Get("type").String() != "image_generation" {		t.Fatalf("expected second tool type=image_generation, got %s", arr[1].Get("type").String())	}} func TestEnsureImageGenerationTool_GPT53CodexSparkDoesNotInjectTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.3-codex-spark","input":"draw a cat"}`)	result := ensureImageGenerationTool(body, "gpt-5.3-codex-spark", nil, nil) 	if string(result) != string(body) {		t.Fatalf("expected body to be unchanged, got %s", string(result))	}	if gjson.GetBytes(result, "tools").Exists() {		t.Fatalf("expected no tools for gpt-5.3-codex-spark, got %s", gjson.GetBytes(result, "tools").Raw)	}} func TestEnsureImageGenerationTool_FreeCodexAuthDoesNotInjectTool(t *testing.T) {	body := []byte(`{"model":"gpt-5.4","input":"draw a cat"}`)	freeAuth := &cliproxyauth.Auth{		Provider:   "codex",		Attributes: map[string]string{"plan_type": "free"},	}	result := ensureImageGenerationTool(body, "gpt-5.4", freeAuth, nil) 	if string(result) != string(body) {		t.Fatalf("expected body to be unchanged, got %s", string(result))	}	if gjson.GetBytes(result, "tools").Exists() {		t.Fatalf("expected no tools for free codex auth, got %s", gjson.GetBytes(result, "tools").Raw)	}}