From e53ce7a30e86c7a8f67d8aad87a5d96d0e737784 Mon Sep 17 00:00:00 2001 From: dibanez Date: Mon, 19 Oct 2026 22:51:12 +0200 Subject: [PATCH] fix(chatgpt): an in-sequence system message reaches Codex as role=developer The Codex backend answers 400 {"detail":"System messages are not allowed"} to any role=system input item, which is what the /v1/messages adapter makes of a mid-turn system message. role=developer is accepted. Co-Authored-By: Claude Code --- litellm/llms/chatgpt/responses/transformation.py | 7 +++++++ .../test_chatgpt_responses_transformation.py | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/litellm/llms/chatgpt/responses/transformation.py b/litellm/llms/chatgpt/responses/transformation.py index 3e7db2981ef..514e2f0f391 100644 --- a/litellm/llms/chatgpt/responses/transformation.py +++ b/litellm/llms/chatgpt/responses/transformation.py @@ -90,6 +90,13 @@ class ChatGPTResponsesAPIConfig(OpenAIResponsesAPIConfig): request["instructions"] = f"{base_instructions}\n\n{existing_instructions}" else: request["instructions"] = base_instructions + # The Codex backend rejects role=system input items ("System messages are not allowed") + # but accepts role=developer, which is what an in-sequence system message means there. + if isinstance(request.get("input"), list): + request["input"] = [ + {**item, "role": "developer"} if isinstance(item, dict) and item.get("role") == "system" else item + for item in request["input"] + ] request["store"] = False request["stream"] = True include: Final = list(request.get("include") or []) diff --git a/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py b/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py index e47d7fc702c..77002011c62 100644 --- a/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py +++ b/tests/unit/llms/chatgpt/responses/test_chatgpt_responses_transformation.py @@ -71,6 +71,18 @@ class TestChatGPTResponsesAPITransformation: assert "service_tier" not in request + def test_system_input_items_become_developer(self): + system_item = {"type": "message", "role": "system", "content": [{"type": "input_text", "text": "be brief"}]} + user_item = {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "hi"}]} + request = ChatGPTResponsesAPIConfig().transform_responses_api_request( + model="gpt-6.1-sol", + input=[user_item, system_item], + response_api_optional_request_params={}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + assert request["input"] == [user_item, {**system_item, "role": "developer"}] + @pytest.mark.parametrize( "model_name", [