From e0027bdc53c2612f6a8a7264d7d74ff145f81731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onat=20=C3=96zmen?= Date: Sun, 30 Aug 2026 17:12:30 +0300 Subject: [PATCH] fix(responses): reject unsupported reasoning effort --- .../llms/openai/responses/transformation.py | 32 ++++++++++++--- .../test_reasoning_effort_capability.py | 40 +++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 tests/test_litellm/llms/openai/responses/test_reasoning_effort_capability.py diff --git a/litellm/llms/openai/responses/transformation.py b/litellm/llms/openai/responses/transformation.py index eac844a790d..b4299d2c907 100644 --- a/litellm/llms/openai/responses/transformation.py +++ b/litellm/llms/openai/responses/transformation.py @@ -121,9 +121,8 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig): ) -> dict: """No mapping applied since inputs are in OpenAI spec already. - GPT-5 models have restrictions on temperature (only temperature=1 - is accepted unless reasoning_effort='none' on models that support it). - Apply the same validation used by the chat completions path. + GPT-5 models have restrictions on temperature and reasoning effort. + Apply the same capability checks used by the chat completions path. """ params: Final = dict(response_api_optional_params) @@ -131,10 +130,33 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig): params["max_output_tokens"] = self._enforce_min_max_output_tokens(params.get("max_output_tokens")) if self._is_gpt_5_model(model=model): + reasoning: Final = params.get("reasoning") or {} + effort: Final = reasoning.get("effort") if isinstance(reasoning, dict) else None + if isinstance(effort, str): + from litellm.llms.openai.chat.gpt_5_transformation import OpenAIGPT5Config + + unsupported_effort: Final = ( + effort == "xhigh" and not OpenAIGPT5Config._supports_reasoning_effort_level(model, effort) + ) or ( + effort in ("minimal", "low") + and OpenAIGPT5Config._is_reasoning_effort_level_explicitly_disabled(model, effort) + ) + if unsupported_effort: + if drop_params or litellm.drop_params: + updated_reasoning: Final = dict(reasoning) + updated_reasoning.pop("effort", None) + if updated_reasoning: + params["reasoning"] = updated_reasoning + else: + params.pop("reasoning", None) + else: + raise litellm.UnsupportedParamsError( + message=f"reasoning.effort={effort} is not supported for this model.", + status_code=400, + ) + temperature: Final = params.get("temperature") if temperature is not None and temperature != 1: - reasoning: Final = params.get("reasoning") or {} - effort: Final = reasoning.get("effort") if isinstance(reasoning, dict) else None supports_none: Final = self._supports_reasoning_effort_none(model=model) if supports_none and self._effort_resolves_to_none(model, effort): pass # flexible temperature allowed diff --git a/tests/test_litellm/llms/openai/responses/test_reasoning_effort_capability.py b/tests/test_litellm/llms/openai/responses/test_reasoning_effort_capability.py new file mode 100644 index 00000000000..e055f413faf --- /dev/null +++ b/tests/test_litellm/llms/openai/responses/test_reasoning_effort_capability.py @@ -0,0 +1,40 @@ +import pytest + +import litellm +from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig + + +@pytest.mark.parametrize("effort", ["minimal", "low"]) +def test_rejects_explicitly_unsupported_lower_reasoning_effort(effort: str) -> None: + config = OpenAIResponsesAPIConfig() + + with pytest.raises(litellm.UnsupportedParamsError, match=f"reasoning.effort={effort}"): + config.map_openai_params( + response_api_optional_params={"reasoning": {"effort": effort}}, + model="gpt-5.5-pro", + drop_params=False, + ) + + +def test_keeps_supported_reasoning_effort() -> None: + config = OpenAIResponsesAPIConfig() + + result = config.map_openai_params( + response_api_optional_params={"reasoning": {"effort": "medium"}}, + model="gpt-5.5-pro", + drop_params=False, + ) + + assert result["reasoning"] == {"effort": "medium"} + + +def test_drop_params_removes_only_unsupported_effort() -> None: + config = OpenAIResponsesAPIConfig() + + result = config.map_openai_params( + response_api_optional_params={"reasoning": {"effort": "minimal", "summary": "detailed"}}, + model="gpt-5.5-pro", + drop_params=True, + ) + + assert result["reasoning"] == {"summary": "detailed"}