mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 02:15:57 +00:00
fix(providers): only send Codex prompt cache params to native OpenAI endpoints (#1342)
buildRequestBody emitted prompt_cache_key and prompt_cache_retention for every Codex request. The ChatGPT subscription OAuth backend (chatgpt.com/backend-api) rejects prompt_cache_retention with HTTP 400 "Unsupported parameter", so every LLM call from an agent using a ChatGPT OAuth Codex provider failed at the first think step. Gate both params on isOpenAINativeEndpoint(apiBase), the same native-only policy already documented and applied by CacheMiddleware. This covers both CodexProvider and CodexAdapter, since the adapter delegates to the same builder. Server-side prefix caching still works on the OAuth backend without these params. Update the two prompt-cache tests to assert both cases: native endpoints include the params, the OAuth backend omits them.
This commit is contained in:
1 parent
4d3c6bcb05
commit
d913eb2149
3 files changed
+53
-15
No files matched your search
@@ -388,18 +388,20 @@ func TestCodexAdapter_FromResponse_PromptTokensDetailsAlias(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCodexAdapter_ToRequestPromptCacheControls(t *testing.T) {
|
||||
a, _ := NewCodexAdapter(ProviderConfig{})
|
||||
body, _, err := a.ToRequest(ChatRequest{
|
||||
req := ChatRequest{
|
||||
Messages: []Message{{Role: "user", Content: "hi"}},
|
||||
Options: map[string]any{
|
||||
OptPromptCacheKey: "tenant/agent/session",
|
||||
OptPromptCacheRetention: "1h",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Native OpenAI endpoint accepts prompt cache params.
|
||||
nativeAdapter, _ := NewCodexAdapter(ProviderConfig{BaseURL: "https://api.openai.com/v1"})
|
||||
body, _, err := nativeAdapter.ToRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("ToRequest: %v", err)
|
||||
}
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
t.Fatalf("decode body: %v", err)
|
||||
@@ -410,6 +412,23 @@ func TestCodexAdapter_ToRequestPromptCacheControls(t *testing.T) {
|
||||
if got := payload["prompt_cache_retention"]; got != "1h" {
|
||||
t.Fatalf("prompt_cache_retention = %v, want 1h", got)
|
||||
}
|
||||
|
||||
// Default ChatGPT OAuth backend rejects these params with HTTP 400 — must omit.
|
||||
oauthAdapter, _ := NewCodexAdapter(ProviderConfig{})
|
||||
oauthBody, _, err := oauthAdapter.ToRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("ToRequest (oauth): %v", err)
|
||||
}
|
||||
var oauthPayload map[string]any
|
||||
if err := json.Unmarshal(oauthBody, &oauthPayload); err != nil {
|
||||
t.Fatalf("decode oauth body: %v", err)
|
||||
}
|
||||
if _, ok := oauthPayload["prompt_cache_key"]; ok {
|
||||
t.Fatal("prompt_cache_key must not be sent to the ChatGPT OAuth backend")
|
||||
}
|
||||
if _, ok := oauthPayload["prompt_cache_retention"]; ok {
|
||||
t.Fatal("prompt_cache_retention must not be sent to the ChatGPT OAuth backend")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodexAdapter_CapabilitiesCacheControl(t *testing.T) {
|
||||
|
||||
@@ -140,11 +140,18 @@ func (p *CodexProvider) buildRequestBody(req ChatRequest, stream bool) map[strin
|
||||
body["reasoning"] = map[string]any{"effort": level}
|
||||
}
|
||||
|
||||
if cacheKey, ok := req.Options[OptPromptCacheKey]; ok {
|
||||
body["prompt_cache_key"] = cacheKey
|
||||
}
|
||||
if retention, ok := req.Options[OptPromptCacheRetention]; ok {
|
||||
body["prompt_cache_retention"] = retention
|
||||
// Prompt caching params (prompt_cache_key, prompt_cache_retention) are accepted
|
||||
// only by native OpenAI endpoints. The ChatGPT subscription OAuth backend
|
||||
// (chatgpt.com/backend-api) rejects them with HTTP 400, so gate on the endpoint —
|
||||
// the same native-only policy CacheMiddleware already applies. Server-side prefix
|
||||
// caching still works on the OAuth backend without these params.
|
||||
if isOpenAINativeEndpoint(p.apiBase) {
|
||||
if cacheKey, ok := req.Options[OptPromptCacheKey]; ok {
|
||||
body["prompt_cache_key"] = cacheKey
|
||||
}
|
||||
if retention, ok := req.Options[OptPromptCacheRetention]; ok {
|
||||
body["prompt_cache_retention"] = retention
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
|
||||
@@ -1131,21 +1131,33 @@ func TestCodexBuildRequestBody_NilFunction_HandlesGracefully(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCodexBuildRequestBodyPromptCacheControls(t *testing.T) {
|
||||
p := NewCodexProvider("test", &staticTokenSource{token: "tok"}, "", "gpt-4o")
|
||||
|
||||
body := p.buildRequestBody(ChatRequest{
|
||||
req := ChatRequest{
|
||||
Messages: []Message{{Role: "user", Content: "Hello"}},
|
||||
Options: map[string]any{
|
||||
OptPromptCacheKey: "agent/session/provider",
|
||||
OptPromptCacheRetention: "24h",
|
||||
},
|
||||
}, true)
|
||||
}
|
||||
|
||||
// Native OpenAI endpoint accepts prompt cache params.
|
||||
native := NewCodexProvider("test", &staticTokenSource{token: "tok"}, "https://api.openai.com/v1", "gpt-4o")
|
||||
body := native.buildRequestBody(req, true)
|
||||
if got := body["prompt_cache_key"]; got != "agent/session/provider" {
|
||||
t.Fatalf("prompt_cache_key = %v, want agent/session/provider", got)
|
||||
t.Fatalf("native prompt_cache_key = %v, want agent/session/provider", got)
|
||||
}
|
||||
if got := body["prompt_cache_retention"]; got != "24h" {
|
||||
t.Fatalf("prompt_cache_retention = %v, want 24h", got)
|
||||
t.Fatalf("native prompt_cache_retention = %v, want 24h", got)
|
||||
}
|
||||
|
||||
// ChatGPT subscription OAuth backend (default apiBase) rejects these params
|
||||
// with HTTP 400, so they must be omitted.
|
||||
oauth := NewCodexProvider("test", &staticTokenSource{token: "tok"}, "", "gpt-4o")
|
||||
oauthBody := oauth.buildRequestBody(req, true)
|
||||
if _, ok := oauthBody["prompt_cache_key"]; ok {
|
||||
t.Fatal("prompt_cache_key must not be sent to the ChatGPT OAuth backend")
|
||||
}
|
||||
if _, ok := oauthBody["prompt_cache_retention"]; ok {
|
||||
t.Fatal("prompt_cache_retention must not be sent to the ChatGPT OAuth backend")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user