diff --git a/backend/internal/handler/openai_gateway_handler.go b/backend/internal/handler/openai_gateway_handler.go index 70131417..5400da3f 100644 --- a/backend/internal/handler/openai_gateway_handler.go +++ b/backend/internal/handler/openai_gateway_handler.go @@ -11,7 +11,6 @@ import ( "time" "github.com/Wei-Shaw/sub2api/internal/config" - "github.com/Wei-Shaw/sub2api/internal/pkg/openai" middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware" "github.com/Wei-Shaw/sub2api/internal/service" @@ -92,17 +91,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) { return } - // For non-Codex CLI requests, set default instructions userAgent := c.GetHeader("User-Agent") - if !openai.IsCodexCLIRequest(userAgent) { - reqBody["instructions"] = openai.DefaultInstructions - // Re-serialize body - body, err = json.Marshal(reqBody) - if err != nil { - h.errorResponse(c, http.StatusInternalServerError, "api_error", "Failed to process request") - return - } - } // Track if we've started streaming (for error handling) streamStarted := false diff --git a/backend/internal/service/openai_codex_transform.go b/backend/internal/service/openai_codex_transform.go index a52c88b5..e6c71775 100644 --- a/backend/internal/service/openai_codex_transform.go +++ b/backend/internal/service/openai_codex_transform.go @@ -135,7 +135,7 @@ func codexModeEnabled() bool { } } -func applyCodexOAuthTransform(reqBody map[string]any, codexMode bool) codexTransformResult { +func applyCodexOAuthTransform(reqBody map[string]any) codexTransformResult { result := codexTransformResult{} model := "" @@ -151,16 +151,13 @@ func applyCodexOAuthTransform(reqBody map[string]any, codexMode bool) codexTrans result.NormalizedModel = normalizedModel } - reqBody["store"] = false - reqBody["stream"] = true - result.Modified = true - - instructions := getCodexInstructions(normalizedModel) - if instructions != "" { - if existing, ok := reqBody["instructions"].(string); !ok || existing != instructions { - reqBody["instructions"] = instructions - result.Modified = true - } + if v, ok := reqBody["store"].(bool); !ok || v { + reqBody["store"] = false + result.Modified = true + } + if v, ok := reqBody["stream"].(bool); !ok || !v { + reqBody["stream"] = true + result.Modified = true } if _, ok := reqBody["max_output_tokens"]; ok { @@ -180,49 +177,30 @@ func applyCodexOAuthTransform(reqBody map[string]any, codexMode bool) codexTrans result.PromptCacheKey = strings.TrimSpace(v) } + instructions := strings.TrimSpace(getCodexInstructions(normalizedModel)) + existingInstructions, _ := reqBody["instructions"].(string) + existingInstructions = strings.TrimSpace(existingInstructions) + + if instructions != "" { + if existingInstructions != "" && existingInstructions != instructions { + if input, ok := reqBody["input"].([]any); ok { + reqBody["input"] = prependSystemInstruction(input, existingInstructions) + result.Modified = true + } + } + if existingInstructions != instructions { + reqBody["instructions"] = instructions + result.Modified = true + } + } + if input, ok := reqBody["input"].([]any); ok { input = filterCodexInput(input) - if codexMode { - cachedPrompt := getOpenCodeCodexPrompt() - input = filterOpenCodeSystemPromptsWithCachedPrompt(input, cachedPrompt) - if hasTools(reqBody) { - input = addCodexBridgeMessage(input) - } - } else if hasTools(reqBody) { - input = addToolRemapMessage(input) - } input = normalizeOrphanedToolOutputs(input) reqBody["input"] = input result.Modified = true } - effort, summary := resolveCodexReasoning(reqBody, normalizedModel) - if effort != "" || summary != "" { - reasoning := ensureMap(reqBody["reasoning"]) - if effort != "" { - reasoning["effort"] = effort - } - if summary != "" { - reasoning["summary"] = summary - } - reqBody["reasoning"] = reasoning - result.Modified = true - } - - textVerbosity := resolveTextVerbosity(reqBody) - if textVerbosity != "" { - text := ensureMap(reqBody["text"]) - text["verbosity"] = textVerbosity - reqBody["text"] = text - result.Modified = true - } - - include := resolveInclude(reqBody) - if include != nil { - reqBody["include"] = include - result.Modified = true - } - return result } @@ -487,6 +465,19 @@ func filterCodexInput(input []any) []any { return filtered } +func prependSystemInstruction(input []any, instructions string) []any { + message := map[string]any{ + "role": "system", + "content": []any{ + map[string]any{ + "type": "input_text", + "text": instructions, + }, + }, + } + return append([]any{message}, input...) +} + func filterOpenCodeSystemPromptsWithCachedPrompt(input []any, cachedPrompt string) []any { if len(input) == 0 { return input diff --git a/backend/internal/service/openai_gateway_service.go b/backend/internal/service/openai_gateway_service.go index 8f59110d..33244330 100644 --- a/backend/internal/service/openai_gateway_service.go +++ b/backend/internal/service/openai_gateway_service.go @@ -21,6 +21,7 @@ import ( "time" "github.com/Wei-Shaw/sub2api/internal/config" + "github.com/Wei-Shaw/sub2api/internal/pkg/openai" "github.com/Wei-Shaw/sub2api/internal/util/responseheaders" "github.com/Wei-Shaw/sub2api/internal/util/urlvalidator" "github.com/gin-gonic/gin" @@ -530,20 +531,28 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco reqModel, _ := reqBody["model"].(string) reqStream, _ := reqBody["stream"].(bool) promptCacheKey := "" + if v, ok := reqBody["prompt_cache_key"].(string); ok { + promptCacheKey = strings.TrimSpace(v) + } // Track if body needs re-serialization bodyModified := false originalModel := reqModel - // Apply model mapping - mappedModel := account.GetMappedModel(reqModel) - if mappedModel != reqModel { - reqBody["model"] = mappedModel - bodyModified = true + isCodexCLI := openai.IsCodexCLIRequest(c.GetHeader("User-Agent")) + + // Apply model mapping (skip for Codex CLI for transparent forwarding) + mappedModel := reqModel + if !isCodexCLI { + mappedModel = account.GetMappedModel(reqModel) + if mappedModel != reqModel { + reqBody["model"] = mappedModel + bodyModified = true + } } - if account.Type == AccountTypeOAuth { - codexResult := applyCodexOAuthTransform(reqBody, codexModeEnabled()) + if account.Type == AccountTypeOAuth && !isCodexCLI { + codexResult := applyCodexOAuthTransform(reqBody) if codexResult.Modified { bodyModified = true }