fix(responses): drop unsupported reasoning param for openai non-reasoning models

This commit is contained in:
mateo-berri 2026-08-29 16:36:05 -07:00
parent ec934c490b
commit 4ef5db7c91
2 changed files with 75 additions and 0 deletions

View file

@ -75,6 +75,19 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
return OpenAIGPT5Config.effort_resolves_to_none(model, effort)
@staticmethod
def _is_o_series_name(model: str) -> bool:
base: Final = model.split("/")[-1]
return len(base) > 1 and base[0] == "o" and base[1].isdigit()
def _supports_reasoning_param(self, model: str) -> bool:
if self._is_gpt_5_model(model=model) or self._is_o_series_name(model=model):
return True
base: Final = model.split("/")[-1]
if base not in litellm.open_ai_chat_completion_models:
return True
return litellm.supports_reasoning(model=base, custom_llm_provider=self.custom_llm_provider.value)
@staticmethod
def _enforce_min_max_output_tokens(max_output_tokens: "int | None") -> "int | None":
"""Raise sub-minimum max_output_tokens up to the OpenAI Responses API minimum.
@ -124,6 +137,22 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
if "max_output_tokens" in params:
params["max_output_tokens"] = self._enforce_min_max_output_tokens(params.get("max_output_tokens"))
if (
self.custom_llm_provider == LlmProviders.OPENAI
and params.get("reasoning") is not None
and not self._supports_reasoning_param(model=model)
):
if drop_params or litellm.drop_params:
params.pop("reasoning", None)
else:
raise litellm.UnsupportedParamsError(
message=(
f"{model} doesn't support the `reasoning` parameter. "
"To drop unsupported params set `litellm.drop_params = True`"
),
status_code=400,
)
if self._is_gpt_5_model(model=model):
temperature: Final = params.get("temperature")
if temperature is not None and temperature != 1:

View file

@ -1626,3 +1626,49 @@ class TestResponsesSurfaceSharesTheEffortRule:
drop_params=True,
)
assert ("temperature" in mapped) is temperature_survives
class TestReasoningFollowsModelSupport:
"""Responses API clients like Codex send `reasoning` on every request, and OpenAI 400s it
on non-reasoning models like gpt-4o. drop_params must strip it there, the same way the
chat completions surface already strips reasoning_effort for those models.
"""
@pytest.mark.parametrize(
"model, reasoning_survives",
[
("gpt-4o", False),
("gpt-4.1", False),
("gpt-4o-mini", False),
("gpt-5.6", True),
("o3", True),
("o3-deep-research", True),
("codex-mini-latest", True),
("computer-use-preview", True),
],
)
def test_drop_params_strips_reasoning_by_model(self, local_model_cost_map, model, reasoning_survives):
mapped = OpenAIResponsesAPIConfig().map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium", "summary": "auto"}},
model=model,
drop_params=True,
)
assert ("reasoning" in mapped) is reasoning_survives
def test_without_drop_params_the_error_is_litellms_400(self, local_model_cost_map, monkeypatch):
monkeypatch.setattr(litellm, "drop_params", False)
with pytest.raises(litellm.UnsupportedParamsError) as excinfo:
OpenAIResponsesAPIConfig().map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium"}},
model="gpt-4o",
drop_params=False,
)
assert excinfo.value.status_code == 400
def test_azure_deployments_keep_reasoning(self, local_model_cost_map):
mapped = AzureOpenAIResponsesAPIConfig().map_openai_params(
response_api_optional_params={"reasoning": {"effort": "medium"}},
model="my-o3-deployment",
drop_params=True,
)
assert mapped["reasoning"] == {"effort": "medium"}