From 93497d4f7f92ae9e97128f9e90a85e16c3710f01 Mon Sep 17 00:00:00 2001 From: "Md. Fazlul Karim" Date: Sun, 25 Oct 2026 15:42:35 +0600 Subject: [PATCH 1/2] fix(vertex_ai): honor drop_params for unsupported reasoning_effort in gemini --- .../vertex_and_google_ai_studio_gemini.py | 63 ++++++-- ...test_vertex_and_google_ai_studio_gemini.py | 2 +- .../test_vertex_gemini_reasoning_effort.py | 153 ++++++++++++++++++ 3 files changed, 205 insertions(+), 13 deletions(-) create mode 100644 tests/unit/llms/vertex_ai/gemini/test_vertex_gemini_reasoning_effort.py diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index d788ccb0f1d..7a1a868427d 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -1292,18 +1292,28 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): return None @staticmethod - def _map_reasoning_effort(effort: str, model: str, reasoning_effort: object) -> GeminiThinkingConfig: + def _map_reasoning_effort( + effort: str, + model: str, + reasoning_effort: object, + drop_params: bool = False, + ) -> GeminiThinkingConfig | None: include_thoughts: Final = VertexGeminiConfig._include_thoughts_requested_by_reasoning_effort(reasoning_effort) if VertexGeminiConfig._is_gemini_3_or_newer(model): - return VertexGeminiConfig._map_reasoning_effort_to_thinking_level(effort, model, include_thoughts) - return VertexGeminiConfig._map_reasoning_effort_to_thinking_budget(effort, model, include_thoughts) + return VertexGeminiConfig._map_reasoning_effort_to_thinking_level( + effort, model, include_thoughts, drop_params=drop_params + ) + return VertexGeminiConfig._map_reasoning_effort_to_thinking_budget( + effort, model, include_thoughts, drop_params=drop_params + ) @staticmethod def _map_reasoning_effort_to_thinking_budget( reasoning_effort: str, model: str | None = None, include_thoughts: bool | None = None, - ) -> GeminiThinkingConfig: + drop_params: bool = False, + ) -> GeminiThinkingConfig | None: sends_thoughts: Final = VertexGeminiConfig._include_thoughts(include_thoughts) if reasoning_effort == "minimal": # Use model-specific minimum thinking budget or fallback @@ -1347,6 +1357,14 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "includeThoughts": False, } else: + should_drop: Final[bool] = ( + drop_params is True + or getattr(litellm, "drop_params", False) is True + or drops_unsupported_params(drop_params) + ) + if should_drop: + verbose_logger.debug("Dropping unsupported reasoning_effort: %s", reasoning_effort) + return None raise _unsupported_reasoning_effort(reasoning_effort) @staticmethod @@ -1362,7 +1380,8 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): reasoning_effort: str, model: str | None = None, include_thoughts: bool | None = None, - ) -> GeminiThinkingConfig: + drop_params: bool = False, + ) -> GeminiThinkingConfig | None: """ Map reasoning_effort to thinking_level for Gemini 3+ models. Args: @@ -1391,6 +1410,14 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "includeThoughts": False, } else: + should_drop: Final[bool] = ( + drop_params is True + or getattr(litellm, "drop_params", False) is True + or drops_unsupported_params(drop_params) + ) + if should_drop: + verbose_logger.debug("Dropping unsupported reasoning_effort: %s", reasoning_effort) + return None raise _unsupported_reasoning_effort(reasoning_effort) @staticmethod @@ -1696,15 +1723,27 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): effort_value = value.get("effort") if effort_value is not None: - # Validate no conflict with thinking_level - VertexGeminiConfig._validate_thinking_config_conflicts( - optional_params=optional_params, - param_name="reasoning_effort", - param_description="thinking_budget", + should_drop: bool = ( + drop_params is True + or optional_params.get("drop_params") is True + or getattr(litellm, "drop_params", False) is True + or drops_unsupported_params(drop_params) + or drops_unsupported_params(optional_params.get("drop_params")) ) - optional_params["thinkingConfig"] = VertexGeminiConfig._map_reasoning_effort( - effort=effort_value, model=model, reasoning_effort=value + thinking_config = VertexGeminiConfig._map_reasoning_effort( + effort=effort_value, + model=model, + reasoning_effort=value, + drop_params=should_drop, ) + if thinking_config is not None: + # Validate no conflict with thinking_level + VertexGeminiConfig._validate_thinking_config_conflicts( + optional_params=optional_params, + param_name="reasoning_effort", + param_description="thinking_budget", + ) + optional_params["thinkingConfig"] = thinking_config elif param == "thinking": # Validate no conflict with thinking_level VertexGeminiConfig._validate_thinking_config_conflicts( diff --git a/tests/unit/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py b/tests/unit/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py index 3bc864cc047..1f111b847a9 100644 --- a/tests/unit/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py +++ b/tests/unit/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py @@ -7194,7 +7194,7 @@ def test_invalid_reasoning_effort_is_a_400_not_a_500(custom_llm_provider, model, model=model, custom_llm_provider=custom_llm_provider, reasoning_effort=reasoning_effort, - drop_params=True, + drop_params=False, ) assert exc_info.value.status_code == 400 diff --git a/tests/unit/llms/vertex_ai/gemini/test_vertex_gemini_reasoning_effort.py b/tests/unit/llms/vertex_ai/gemini/test_vertex_gemini_reasoning_effort.py new file mode 100644 index 00000000000..1531d3a9b24 --- /dev/null +++ b/tests/unit/llms/vertex_ai/gemini/test_vertex_gemini_reasoning_effort.py @@ -0,0 +1,153 @@ +from typing import Final + +import pytest + +import litellm +from litellm.exceptions import UnsupportedParamsError +from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import VertexGeminiConfig + + +def test_vertex_gemini_unsupported_reasoning_effort_with_drop_params_true() -> None: + """Verifies that unsupported reasoning_effort (e.g., 'xhigh') is safely dropped + + when drop_params=True is passed directly or inside optional_params. + """ + config: Final = VertexGeminiConfig() + + # 1. Directly passing drop_params=True (string format) + result_direct: Final = config.map_openai_params( + non_default_params={"reasoning_effort": "xhigh"}, + optional_params={}, + model="gemini-2.5-flash", + drop_params=True, + ) + assert "thinkingConfig" not in result_direct + assert "reasoning_effort" not in result_direct + + # 2. Directly passing drop_params=True (dict format from Agents SDK) + result_dict: Final = config.map_openai_params( + non_default_params={"reasoning_effort": {"effort": "xhigh", "summary": "auto"}}, + optional_params={}, + model="gemini-2.5-flash", + drop_params=True, + ) + assert "thinkingConfig" not in result_dict + + # 3. Passing drop_params=True in optional_params + result_optional: Final = config.map_openai_params( + non_default_params={"reasoning_effort": "xhigh"}, + optional_params={"drop_params": True}, + model="gemini-3-pro-preview", + drop_params=False, + ) + assert "thinkingConfig" not in result_optional + + +def test_vertex_gemini_unsupported_reasoning_effort_with_global_drop_params( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Verifies that unsupported reasoning_effort (e.g., 'max') is safely dropped + + when litellm.drop_params = True is globally active. + """ + monkeypatch.setattr(litellm, "drop_params", True) + config: Final = VertexGeminiConfig() + + # Test with Gemini 2.5 (budget-based thinking) + result_gemini_25: Final = config.map_openai_params( + non_default_params={"reasoning_effort": "max"}, + optional_params={}, + model="gemini-2.5-flash", + drop_params=False, + ) + assert "thinkingConfig" not in result_gemini_25 + + # Test with Gemini 3+ (level-based thinking) + result_gemini_3: Final = config.map_openai_params( + non_default_params={"reasoning_effort": "max"}, + optional_params={}, + model="gemini-3-pro-preview", + drop_params=False, + ) + assert "thinkingConfig" not in result_gemini_3 + + +def test_vertex_gemini_unsupported_reasoning_effort_raises_without_drop_params() -> None: + """Verifies that unsupported reasoning_effort values raise UnsupportedParamsError + + when drop_params is False. + """ + config: Final = VertexGeminiConfig() + + for effort in ("xhigh", "max", "unsupported_level"): + with pytest.raises(UnsupportedParamsError): + config.map_openai_params( + non_default_params={"reasoning_effort": effort}, + optional_params={}, + model="gemini-2.5-flash", + drop_params=False, + ) + + with pytest.raises(UnsupportedParamsError): + config.map_openai_params( + non_default_params={"reasoning_effort": effort}, + optional_params={}, + model="gemini-3-pro-preview", + drop_params=False, + ) + + +def test_vertex_gemini_direct_reasoning_effort_mappers_drop_params() -> None: + """Verifies direct mapper methods honor drop_params=True and return None.""" + # Test _map_reasoning_effort_to_thinking_budget directly + budget_result: Final = VertexGeminiConfig._map_reasoning_effort_to_thinking_budget( + reasoning_effort="xhigh", + model="gemini-2.5-flash", + drop_params=True, + ) + assert budget_result is None + + # Test _map_reasoning_effort_to_thinking_level directly + level_result: Final = VertexGeminiConfig._map_reasoning_effort_to_thinking_level( + reasoning_effort="xhigh", + model="gemini-3-pro-preview", + drop_params=True, + ) + assert level_result is None + + # Test _map_reasoning_effort directly + effort_result: Final = VertexGeminiConfig._map_reasoning_effort( + effort="max", + model="gemini-2.5-flash", + reasoning_effort="max", + drop_params=True, + ) + assert effort_result is None + + effort_level_result: Final = VertexGeminiConfig._map_reasoning_effort( + effort="max", + model="gemini-3-pro-preview", + reasoning_effort="max", + drop_params=True, + ) + assert effort_level_result is None + + +@pytest.mark.parametrize("effort", ["low", "medium", "high"]) +@pytest.mark.parametrize("model", ["gemini-2.5-flash", "gemini-3-pro-preview"]) +def test_vertex_gemini_valid_reasoning_effort_mapping(effort: str, model: str) -> None: + """Verifies that supported reasoning_effort values continue to map as expected.""" + config: Final = VertexGeminiConfig() + result: Final = config.map_openai_params( + non_default_params={"reasoning_effort": effort}, + optional_params={}, + model=model, + drop_params=False, + ) + assert "thinkingConfig" in result + thinking_config: Final = result["thinkingConfig"] + assert thinking_config is not None + if "gemini-3" in model: + assert thinking_config.get("thinkingLevel") == effort + else: + assert "thinkingBudget" in thinking_config From c036669276a7819c21a73b67c42dd274576a59db Mon Sep 17 00:00:00 2001 From: "Md. Fazlul Karim" Date: Sun, 25 Oct 2026 20:23:46 +0600 Subject: [PATCH 2/2] fix(vertex_ai): resolve basedpyright type errors in reasoning_effort drop_params mapping --- .../vertex_and_google_ai_studio_gemini.py | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index 7a1a868427d..e84ca7fb47d 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -1357,12 +1357,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "includeThoughts": False, } else: - should_drop: Final[bool] = ( - drop_params is True - or getattr(litellm, "drop_params", False) is True - or drops_unsupported_params(drop_params) - ) - if should_drop: + if drops_unsupported_params(drop_params): verbose_logger.debug("Dropping unsupported reasoning_effort: %s", reasoning_effort) return None raise _unsupported_reasoning_effort(reasoning_effort) @@ -1410,12 +1405,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "includeThoughts": False, } else: - should_drop: Final[bool] = ( - drop_params is True - or getattr(litellm, "drop_params", False) is True - or drops_unsupported_params(drop_params) - ) - if should_drop: + if drops_unsupported_params(drop_params): verbose_logger.debug("Dropping unsupported reasoning_effort: %s", reasoning_effort) return None raise _unsupported_reasoning_effort(reasoning_effort) @@ -1723,12 +1713,8 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): effort_value = value.get("effort") if effort_value is not None: - should_drop: bool = ( - drop_params is True - or optional_params.get("drop_params") is True - or getattr(litellm, "drop_params", False) is True - or drops_unsupported_params(drop_params) - or drops_unsupported_params(optional_params.get("drop_params")) + should_drop: bool = drops_unsupported_params(drop_params) or ( + "drop_params" in optional_params and optional_params["drop_params"] in (True, "true", "True") ) thinking_config = VertexGeminiConfig._map_reasoning_effort( effort=effort_value,