88
99 "github.com/gin-gonic/gin"
1010 "github.com/google/uuid"
11+ "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
12+ cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
1113 cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
1214 sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
1315 "github.com/tidwall/gjson"
@@ -27,7 +29,7 @@ func TestCodexExecutorCacheHelper_OpenAIChatCompletions_StablePromptCacheKeyFrom
2729 }
2830 url := "https://example.com/responses"
2931
30- httpReq , err := executor .cacheHelper (ctx , sdktranslator .FromString ("openai" ), url , req , rawJSON )
32+ httpReq , _ , _ , err := executor .cacheHelper (ctx , sdktranslator .FromString ("openai" ), url , nil , req , req . Payload , rawJSON )
3133 if err != nil {
3234 t .Fatalf ("cacheHelper error: %v" , err )
3335 }
@@ -45,11 +47,11 @@ func TestCodexExecutorCacheHelper_OpenAIChatCompletions_StablePromptCacheKeyFrom
4547 if gotConversation := httpReq .Header .Get ("Conversation_id" ); gotConversation != "" {
4648 t .Fatalf ("Conversation_id = %q, want empty" , gotConversation )
4749 }
48- if gotSession := httpReq .Header .Get ("Session_id" ); gotSession != expectedKey {
49- t .Fatalf ("Session_id = %q, want %q " , gotSession , expectedKey )
50+ if gotSession := httpReq .Header .Get ("Session_id" ); gotSession != "" {
51+ t .Fatalf ("Session_id = %q, want empty " , gotSession )
5052 }
5153
52- httpReq2 , err := executor .cacheHelper (ctx , sdktranslator .FromString ("openai" ), url , req , rawJSON )
54+ httpReq2 , _ , _ , err := executor .cacheHelper (ctx , sdktranslator .FromString ("openai" ), url , nil , req , req . Payload , rawJSON )
5355 if err != nil {
5456 t .Fatalf ("cacheHelper error (second call): %v" , err )
5557 }
@@ -62,3 +64,81 @@ func TestCodexExecutorCacheHelper_OpenAIChatCompletions_StablePromptCacheKeyFrom
6264 t .Fatalf ("prompt_cache_key (second call) = %q, want %q" , gotKey2 , expectedKey )
6365 }
6466}
67+
68+ func TestCodexExecutorCacheHelper_IdentityConfuseRemapsBodyAndHeaders (t * testing.T ) {
69+ recorder := httptest .NewRecorder ()
70+ ginCtx , _ := gin .CreateTestContext (recorder )
71+ ginCtx .Request = httptest .NewRequest ("POST" , "/v1/responses" , nil )
72+ ginCtx .Request .Header .Set ("X-Codex-Turn-Metadata" , `{"prompt_cache_key":"cache-1","turn_id":"turn-1"}` )
73+ ginCtx .Request .Header .Set ("X-Client-Request-Id" , "client-request-1" )
74+
75+ ctx := context .WithValue (context .Background (), "gin" , ginCtx )
76+ executor := & CodexExecutor {cfg : & config.Config {
77+ Routing : config.RoutingConfig {Strategy : "fill-first" },
78+ Codex : config.CodexConfig {IdentityConfuse : true },
79+ }}
80+ auth := & cliproxyauth.Auth {ID : "auth-1" , Provider : "codex" }
81+ rawJSON := []byte (`{"model":"gpt-5-codex","stream":true,"client_metadata":{"x-codex-turn-metadata":"{\"prompt_cache_key\":\"cache-1\",\"turn_id\":\"turn-1\"}","x-codex-window-id":"cache-1:0"}}` )
82+ req := cliproxyexecutor.Request {
83+ Model : "gpt-5-codex" ,
84+ Payload : []byte (`{"model":"gpt-5-codex","prompt_cache_key":"cache-1","client_metadata":{"x-codex-installation-id":"install-1"}}` ),
85+ }
86+ url := "https://example.com/responses"
87+
88+ httpReq , body , identityState , err := executor .cacheHelper (ctx , sdktranslator .FromString ("openai-response" ), url , auth , req , req .Payload , rawJSON )
89+ if err != nil {
90+ t .Fatalf ("cacheHelper error: %v" , err )
91+ }
92+ applyCodexHeaders (httpReq , auth , "oauth-token" , true , executor .cfg )
93+ applyCodexIdentityConfuseHeaders (httpReq .Header , identityState )
94+
95+ expectedPromptCacheKey := codexIdentityConfuseUUID ("auth-1" , "prompt-cache" , "cache-1" )
96+ if gotKey := gjson .GetBytes (body , "prompt_cache_key" ).String (); gotKey != expectedPromptCacheKey {
97+ t .Fatalf ("prompt_cache_key = %q, want %q" , gotKey , expectedPromptCacheKey )
98+ }
99+ expectedInstallationID := codexIdentityConfuseUUID ("auth-1" , "installation" , "install-1" )
100+ if gotID := gjson .GetBytes (body , "client_metadata.x-codex-installation-id" ).String (); gotID != expectedInstallationID {
101+ t .Fatalf ("installation id = %q, want %q" , gotID , expectedInstallationID )
102+ }
103+ if gotMetadata := gjson .GetBytes (body , "client_metadata.x-codex-turn-metadata" ).String (); gotMetadata != `{"prompt_cache_key":"` + expectedPromptCacheKey + `","turn_id":"turn-1"}` {
104+ t .Fatalf ("client_metadata.x-codex-turn-metadata = %s" , gotMetadata )
105+ }
106+ if gotWindowID := gjson .GetBytes (body , "client_metadata.x-codex-window-id" ).String (); gotWindowID != expectedPromptCacheKey + ":0" {
107+ t .Fatalf ("client_metadata.x-codex-window-id = %q, want %q" , gotWindowID , expectedPromptCacheKey + ":0" )
108+ }
109+ for _ , headerName := range []string {"Session-Id" , "X-Client-Request-Id" , "Thread-Id" } {
110+ if gotHeader := httpReq .Header .Get (headerName ); gotHeader != expectedPromptCacheKey {
111+ t .Fatalf ("%s = %q, want %q" , headerName , gotHeader , expectedPromptCacheKey )
112+ }
113+ }
114+ if gotSession := httpReq .Header .Get ("Session_id" ); gotSession != "" {
115+ t .Fatalf ("Session_id = %q, want empty" , gotSession )
116+ }
117+ if gotWindow := httpReq .Header .Get ("X-Codex-Window-Id" ); gotWindow != expectedPromptCacheKey + ":0" {
118+ t .Fatalf ("X-Codex-Window-Id = %q, want %q" , gotWindow , expectedPromptCacheKey + ":0" )
119+ }
120+ if gotMetadata := httpReq .Header .Get ("X-Codex-Turn-Metadata" ); gotMetadata != `{"prompt_cache_key":"` + expectedPromptCacheKey + `","turn_id":"turn-1"}` {
121+ t .Fatalf ("X-Codex-Turn-Metadata = %s" , gotMetadata )
122+ }
123+ }
124+
125+ func TestCodexIdentityConfuseKeepsClientBodySeparateFromUpstreamBody (t * testing.T ) {
126+ cfg := & config.Config {
127+ Routing : config.RoutingConfig {Strategy : "fill-first" },
128+ Codex : config.CodexConfig {IdentityConfuse : true },
129+ }
130+ auth := & cliproxyauth.Auth {ID : "auth-1" , Provider : "codex" }
131+ clientBody := []byte (`{"model":"gpt-5-codex","prompt_cache_key":"cache-1"}` )
132+
133+ upstreamBody , identityState := applyCodexIdentityConfuseBody (cfg , auth , clientBody , clientBody )
134+ expectedPromptCacheKey := codexIdentityConfuseUUID ("auth-1" , "prompt-cache" , "cache-1" )
135+ if identityState .promptCacheKey != expectedPromptCacheKey {
136+ t .Fatalf ("identity prompt_cache_key = %q, want %q" , identityState .promptCacheKey , expectedPromptCacheKey )
137+ }
138+ if gotKey := gjson .GetBytes (upstreamBody , "prompt_cache_key" ).String (); gotKey != expectedPromptCacheKey {
139+ t .Fatalf ("upstream prompt_cache_key = %q, want %q" , gotKey , expectedPromptCacheKey )
140+ }
141+ if gotKey := gjson .GetBytes (clientBody , "prompt_cache_key" ).String (); gotKey != "cache-1" {
142+ t .Fatalf ("client prompt_cache_key = %q, want cache-1" , gotKey )
143+ }
144+ }
0 commit comments