From b4ed387d24b55b8ff7da0a7f7b77a666e1b89bc4 Mon Sep 17 00:00:00 2001 From: Cesar Garcia <128240629+Chesars@users.noreply.github.com> Date: Wed, 21 Jan 2026 00:31:25 -0300 Subject: [PATCH] fix(vertex_ai): handle reasoning_effort as dict from OpenAI Agents SDK (#19419) The OpenAI Agents SDK (v0.6.9+) now passes reasoning_effort as a dict when summary is specified: {"effort": "high", "summary": "auto"} This change extracts the "effort" value from the dict for Vertex AI, which only supports thinkingLevel (not summary). Before: reasoning_effort={"effort": "high"} was silently ignored After: reasoning_effort={"effort": "high"} correctly maps to thinkingLevel Fixes #19411 --- .../vertex_and_google_ai_studio_gemini.py | 43 +++++++----- ...test_vertex_and_google_ai_studio_gemini.py | 65 +++++++++++++++++++ 2 files changed, 91 insertions(+), 17 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 f65a19ac46f..46ea78ac9ef 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 @@ -988,25 +988,34 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): optional_params["parallel_tool_calls"] = value elif param == "seed": optional_params["seed"] = value - elif param == "reasoning_effort" and isinstance(value, str): - # Validate no conflict with thinking_level - VertexGeminiConfig._validate_thinking_config_conflicts( - optional_params=optional_params, - param_name="reasoning_effort", - param_description="thinking_budget", - ) - if VertexGeminiConfig._is_gemini_3_or_newer(model): - optional_params["thinkingConfig"] = ( - VertexGeminiConfig._map_reasoning_effort_to_thinking_level( - value, model - ) + elif param == "reasoning_effort": + # Extract effort value - handle both string and dict formats + # Dict format comes from OpenAI Agents SDK: {"effort": "high", "summary": "auto"} + effort_value: Optional[str] = None + if isinstance(value, str): + effort_value = value + elif isinstance(value, dict): + 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", ) - else: - optional_params["thinkingConfig"] = ( - VertexGeminiConfig._map_reasoning_effort_to_thinking_budget( - value, model + if VertexGeminiConfig._is_gemini_3_or_newer(model): + optional_params["thinkingConfig"] = ( + VertexGeminiConfig._map_reasoning_effort_to_thinking_level( + effort_value, model + ) + ) + else: + optional_params["thinkingConfig"] = ( + VertexGeminiConfig._map_reasoning_effort_to_thinking_budget( + effort_value, model + ) ) - ) elif param == "thinking": # Validate no conflict with thinking_level VertexGeminiConfig._validate_thinking_config_conflicts( diff --git a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py index 810769023bd..fe262b8d840 100644 --- a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py +++ b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py @@ -1887,6 +1887,71 @@ def test_reasoning_effort_maps_to_thinking_level_gemini_3(): assert result["thinkingConfig"]["includeThoughts"] is False +def test_reasoning_effort_dict_format_gemini_3(): + """ + Test that reasoning_effort works when passed as dict format from OpenAI Agents SDK. + + The OpenAI Agents SDK passes reasoning_effort as {"effort": "high", "summary": "auto"} + instead of just a string. This test verifies that we correctly extract the effort value. + + Related issue: https://github.com/BerriAI/litellm/issues/19411 + """ + from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import ( + VertexGeminiConfig, + ) + + v = VertexGeminiConfig() + model = "gemini-3-pro-preview" + + # Test dict format with effort="high" (OpenAI Agents SDK format) + optional_params = {} + non_default_params = {"reasoning_effort": {"effort": "high", "summary": "auto"}} + result = v.map_openai_params( + non_default_params=non_default_params, + optional_params=optional_params, + model=model, + drop_params=False, + ) + assert result["thinkingConfig"]["thinkingLevel"] == "high" + assert result["thinkingConfig"]["includeThoughts"] is True + + # Test dict format with effort="low" + optional_params = {} + non_default_params = {"reasoning_effort": {"effort": "low"}} + result = v.map_openai_params( + non_default_params=non_default_params, + optional_params=optional_params, + model=model, + drop_params=False, + ) + assert result["thinkingConfig"]["thinkingLevel"] == "low" + assert result["thinkingConfig"]["includeThoughts"] is True + + # Test dict format with effort="medium" + optional_params = {} + non_default_params = {"reasoning_effort": {"effort": "medium"}} + result = v.map_openai_params( + non_default_params=non_default_params, + optional_params=optional_params, + model=model, + drop_params=False, + ) + assert result["thinkingConfig"]["thinkingLevel"] == "high" + assert result["thinkingConfig"]["includeThoughts"] is True + + # Test dict format without effort key - should fall back to Gemini 3 default (low) + optional_params = {} + non_default_params = {"reasoning_effort": {"summary": "auto"}} + result = v.map_openai_params( + non_default_params=non_default_params, + optional_params=optional_params, + model=model, + drop_params=False, + ) + # Gemini 3 defaults to thinkingLevel="low" when no explicit effort is set + assert result["thinkingConfig"]["thinkingLevel"] == "low" + + def test_temperature_default_for_gemini_3(): """Test that temperature defaults to 1.0 for Gemini 3+ models when not specified""" from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import (