diff --git a/litellm/completion_extras/litellm_responses_transformation/transformation.py b/litellm/completion_extras/litellm_responses_transformation/transformation.py index 579cf83bffa..0fc4aaab7ac 100644 --- a/litellm/completion_extras/litellm_responses_transformation/transformation.py +++ b/litellm/completion_extras/litellm_responses_transformation/transformation.py @@ -63,6 +63,9 @@ if TYPE_CHECKING: from litellm.types.utils import Choices +_REASONING_EFFORT_LEVELS: Final = frozenset({"none", "minimal", "low", "medium", "high", "xhigh", "max"}) + + class _ReasoningSummaryText(TypedDict): type: str text: str @@ -1001,23 +1004,11 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge): ) # If string is passed, map with optional summary based on flag/env var - if reasoning_effort == "none": - return Reasoning(effort="none", summary="detailed") if auto_summary_enabled else Reasoning(effort="none") - elif reasoning_effort == "high": - return Reasoning(effort="high", summary="detailed") if auto_summary_enabled else Reasoning(effort="high") - elif reasoning_effort == "xhigh": - return Reasoning(effort="xhigh", summary="detailed") if auto_summary_enabled else Reasoning(effort="xhigh") - elif reasoning_effort == "medium": - return ( - Reasoning(effort="medium", summary="detailed") if auto_summary_enabled else Reasoning(effort="medium") - ) - elif reasoning_effort == "low": - return Reasoning(effort="low", summary="detailed") if auto_summary_enabled else Reasoning(effort="low") - elif reasoning_effort == "minimal": - return ( - Reasoning(effort="minimal", summary="detailed") if auto_summary_enabled else Reasoning(effort="minimal") - ) - return None + if reasoning_effort not in _REASONING_EFFORT_LEVELS: + return None + if auto_summary_enabled: + return Reasoning(effort=reasoning_effort, summary="detailed") + return Reasoning(effort=reasoning_effort) def _add_web_search_tool( self, diff --git a/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py b/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py index b8bd5c951ee..6cf2be98f81 100644 --- a/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py +++ b/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py @@ -1521,7 +1521,7 @@ def test_map_reasoning_effort_adds_summary_detailed(): handler = LiteLLMResponsesTransformationHandler() # Test all string effort levels - DEFAULT BEHAVIOR (no summary) - effort_levels = ["none", "low", "medium", "high", "xhigh", "minimal"] + effort_levels = ["none", "minimal", "low", "medium", "high", "xhigh", "max"] # Save original flag value original_flag = litellm.reasoning_auto_summary @@ -3400,3 +3400,14 @@ def test_output_item_done_with_stream_map_keeps_empty_delta(): ) assert chunk.choices[0].delta.tool_calls is None assert chunk.choices[0].finish_reason is None + + +def test_map_reasoning_effort_rejects_unknown_level(): + """An unrecognized effort must map to None so it is not forwarded upstream.""" + from litellm.completion_extras.litellm_responses_transformation.transformation import ( + LiteLLMResponsesTransformationHandler, + ) + + handler = LiteLLMResponsesTransformationHandler() + + assert handler._map_reasoning_effort("banana") is None