fix(gpt5): treat missing supports_minimal_reasoning_effort as supported

Add _is_reasoning_effort_level_explicitly_disabled to use opt-out semantics
for minimal effort: unknown/unlisted models pass through, only blocked when
the model map explicitly sets supports_minimal_reasoning_effort=false.

xhigh keeps opt-in semantics (must be explicitly supported).

Adds test for unknown-model passthrough and explicit-disabled detection.

Made-with: Cursor
This commit is contained in:
Sameer Kankute 2026-03-18 09:17:57 +05:30
parent c20c465a02
commit 52bf372319
2 changed files with 73 additions and 4 deletions

View file

@ -3,7 +3,7 @@
from typing import Optional, Union
import litellm
from litellm.utils import _supports_factory
from litellm.utils import _get_model_cost_key, _get_model_info_helper, _supports_factory
from .gpt_transformation import OpenAIGPTConfig
@ -113,6 +113,28 @@ class OpenAIGPT5Config(OpenAIGPTConfig):
key=f"supports_{level}_reasoning_effort",
)
@classmethod
def _is_reasoning_effort_level_explicitly_disabled(
cls, model: str, level: str
) -> bool:
"""Return True only when the model map explicitly sets the capability to False.
Unlike ``_supports_reasoning_effort_level`` (which requires an explicit True),
this method returns True only when ``supports_{level}_reasoning_effort`` is
explicitly set to ``False`` in the model map. A missing key is treated as
supported (i.e. this method returns False = not disabled).
Use this for opt-out checks where unknown models should be allowed through.
"""
try:
key = f"supports_{level}_reasoning_effort"
cost_key = _get_model_cost_key(model)
entry = litellm.model_cost.get(cost_key or model) or {}
val = entry.get(key)
return val is False
except Exception:
return False
def get_supported_openai_params(self, model: str) -> list:
if self.is_model_gpt_5_search_model(model):
return [
@ -200,9 +222,8 @@ class OpenAIGPT5Config(OpenAIGPTConfig):
if "reasoning_effort" in optional_params:
optional_params["reasoning_effort"] = normalized
if effective_effort is not None and (
effective_effort == "xhigh" or effective_effort == "minimal"
):
if effective_effort == "xhigh":
# xhigh is an opt-in capability: only allow if model explicitly supports it.
if not self._supports_reasoning_effort_level(model, effective_effort):
if litellm.drop_params or drop_params:
non_default_params.pop("reasoning_effort", None)
@ -213,6 +234,19 @@ class OpenAIGPT5Config(OpenAIGPTConfig):
),
status_code=400,
)
elif effective_effort == "minimal":
# minimal is opt-out: unknown models pass through; only block when
# the model map explicitly sets supports_minimal_reasoning_effort=false.
if self._is_reasoning_effort_level_explicitly_disabled(model, effective_effort):
if litellm.drop_params or drop_params:
non_default_params.pop("reasoning_effort", None)
else:
raise litellm.utils.UnsupportedParamsError(
message=(
f"reasoning_effort={effective_effort} is not supported for this model."
),
status_code=400,
)
################################################################
# max_tokens is not supported for gpt-5 models on OpenAI API

View file

@ -409,6 +409,41 @@ def test_gpt5_supports_reasoning_effort_level_minimal(gpt5_config: OpenAIGPT5Con
assert not gpt5_config._supports_reasoning_effort_level("gpt-5.4-nano", "minimal")
def test_gpt5_minimal_explicitly_disabled_check(gpt5_config: OpenAIGPT5Config):
"""_is_reasoning_effort_level_explicitly_disabled returns True only for explicit False entries.
Models with supports_minimal_reasoning_effort=false disabled.
Models with supports_minimal_reasoning_effort=true (or missing) not disabled.
"""
assert gpt5_config._is_reasoning_effort_level_explicitly_disabled(
"gpt-5.4-mini", "minimal"
)
assert gpt5_config._is_reasoning_effort_level_explicitly_disabled(
"gpt-5.4-nano", "minimal"
)
assert not gpt5_config._is_reasoning_effort_level_explicitly_disabled(
"gpt-5.4", "minimal"
)
assert not gpt5_config._is_reasoning_effort_level_explicitly_disabled(
"gpt-5.4-pro", "minimal"
)
def test_gpt5_unknown_model_passes_through_minimal(config: OpenAIConfig):
"""Unknown/unlisted gpt-5 models should pass reasoning_effort='minimal' through.
Missing supports_minimal_reasoning_effort key is treated as supported,
not as unsupported, to avoid breaking custom or newly-announced models.
"""
params = config.map_openai_params(
non_default_params={"reasoning_effort": "minimal"},
optional_params={},
model="gpt-5.4-turbo-preview",
drop_params=False,
)
assert params["reasoning_effort"] == "minimal"
def test_gpt5_normalizes_reasoning_effort_dict_with_summary(config: OpenAIConfig):
"""Dict with summary/generate_summary is normalized for chat completions."""
params = config.map_openai_params(