diff --git a/internal/translator/codex/openai/responses/codex_openai-responses_request.go b/internal/translator/codex/openai/responses/codex_openai-responses_request.go index f0407149..1161c515 100644 --- a/internal/translator/codex/openai/responses/codex_openai-responses_request.go +++ b/internal/translator/codex/openai/responses/codex_openai-responses_request.go @@ -26,6 +26,8 @@ func ConvertOpenAIResponsesRequestToCodex(modelName string, inputRawJSON []byte, rawJSON, _ = sjson.DeleteBytes(rawJSON, "temperature") rawJSON, _ = sjson.DeleteBytes(rawJSON, "top_p") rawJSON, _ = sjson.DeleteBytes(rawJSON, "service_tier") + rawJSON, _ = sjson.DeleteBytes(rawJSON, "truncation") + rawJSON = applyResponsesCompactionCompatibility(rawJSON) // Delete the user field as it is not supported by the Codex upstream. rawJSON, _ = sjson.DeleteBytes(rawJSON, "user") @@ -36,6 +38,23 @@ func ConvertOpenAIResponsesRequestToCodex(modelName string, inputRawJSON []byte, return rawJSON } +// applyResponsesCompactionCompatibility handles OpenAI Responses context_management.compaction +// for Codex upstream compatibility. +// +// Codex /responses currently rejects context_management with: +// {"detail":"Unsupported parameter: context_management"}. +// +// Compatibility strategy: +// 1) Remove context_management before forwarding to Codex upstream. +func applyResponsesCompactionCompatibility(rawJSON []byte) []byte { + if !gjson.GetBytes(rawJSON, "context_management").Exists() { + return rawJSON + } + + rawJSON, _ = sjson.DeleteBytes(rawJSON, "context_management") + return rawJSON +} + // convertSystemRoleToDeveloper traverses the input array and converts any message items // with role "system" to role "developer". This is necessary because Codex API does not // accept "system" role in the input array. diff --git a/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go b/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go index 4f562486..65732c3f 100644 --- a/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go +++ b/internal/translator/codex/openai/responses/codex_openai-responses_request_test.go @@ -280,3 +280,41 @@ func TestUserFieldDeletion(t *testing.T) { t.Errorf("user field should be deleted, but it was found with value: %s", userField.Raw) } } + +func TestContextManagementCompactionCompatibility(t *testing.T) { + inputJSON := []byte(`{ + "model": "gpt-5.2", + "context_management": [ + { + "type": "compaction", + "compact_threshold": 12000 + } + ], + "input": [{"role":"user","content":"hello"}] + }`) + + output := ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON, false) + outputStr := string(output) + + if gjson.Get(outputStr, "context_management").Exists() { + t.Fatalf("context_management should be removed for Codex compatibility") + } + if gjson.Get(outputStr, "truncation").Exists() { + t.Fatalf("truncation should be removed for Codex compatibility") + } +} + +func TestTruncationRemovedForCodexCompatibility(t *testing.T) { + inputJSON := []byte(`{ + "model": "gpt-5.2", + "truncation": "disabled", + "input": [{"role":"user","content":"hello"}] + }`) + + output := ConvertOpenAIResponsesRequestToCodex("gpt-5.2", inputJSON, false) + outputStr := string(output) + + if gjson.Get(outputStr, "truncation").Exists() { + t.Fatalf("truncation should be removed for Codex compatibility") + } +}