diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index 435506831dc..77975c6a9a0 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -931,10 +931,12 @@ class AmazonConverseConfig(BaseConfig): litellm.verbose_logger.warning(DROP_UNSUPPORTED_ADAPTIVE_THINKING_WARNING, model) else: optional_params["thinking"] = value - elif param == "reasoning_effort" and isinstance(value, str): - self._handle_reasoning_effort_parameter( - model=model, reasoning_effort=value, optional_params=optional_params - ) + elif param == "reasoning_effort": + effort_value = value.get("effort") if isinstance(value, dict) else value + if isinstance(effort_value, str): + self._handle_reasoning_effort_parameter( + model=model, reasoning_effort=effort_value, optional_params=optional_params + ) elif param == "output_config" and isinstance(value, dict): mapped_output_config = dict(value) normalize_bedrock_opus_output_config_effort(model=model, output_config=mapped_output_config) diff --git a/litellm/llms/ollama/chat/transformation.py b/litellm/llms/ollama/chat/transformation.py index d6aa1f1743b..901dd579ade 100644 --- a/litellm/llms/ollama/chat/transformation.py +++ b/litellm/llms/ollama/chat/transformation.py @@ -170,10 +170,12 @@ class OllamaChatConfig(BaseConfig): if value.get("json_schema") and value["json_schema"].get("schema"): optional_params["format"] = value["json_schema"]["schema"] if param == "reasoning_effort" and value is not None: - if model.startswith("gpt-oss"): - optional_params["think"] = value - else: - optional_params["think"] = value in {"low", "medium", "high"} + effort_value = value.get("effort") if isinstance(value, dict) else value + if effort_value is not None: + if model.startswith("gpt-oss"): + optional_params["think"] = effort_value + else: + optional_params["think"] = effort_value in {"low", "medium", "high"} ### FUNCTION CALLING LOGIC ### # Ollama 0.4+ supports native tool calling - pass tools directly # and let Ollama handle model capability detection diff --git a/litellm/llms/ollama/completion/transformation.py b/litellm/llms/ollama/completion/transformation.py index 65edd5cb718..26faa491ea6 100644 --- a/litellm/llms/ollama/completion/transformation.py +++ b/litellm/llms/ollama/completion/transformation.py @@ -178,10 +178,12 @@ class OllamaConfig(BaseConfig): elif param == "stop": optional_params["stop"] = value elif param == "reasoning_effort" and value is not None: - if model.startswith("gpt-oss"): - optional_params["think"] = value - else: - optional_params["think"] = value in {"low", "medium", "high"} + effort_value = value.get("effort") if isinstance(value, dict) else value + if effort_value is not None: + if model.startswith("gpt-oss"): + optional_params["think"] = effort_value + else: + optional_params["think"] = effort_value in {"low", "medium", "high"} elif param == "response_format" and isinstance(value, dict): if value["type"] == "json_object": optional_params["format"] = "json" diff --git a/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py b/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py index 8f3dbf7b0d9..854424483bf 100644 --- a/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py +++ b/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py @@ -904,3 +904,40 @@ class TestOllamaToolCallTransformation: assert tool_msg["content"] == "Sunny, 72°F" assert "tool_call_id" in tool_msg, "tool_call_id must be forwarded to Ollama" assert tool_msg["tool_call_id"] == "call_abc123" + + +class TestOllamaReasoningEffortMapping: + """ + Test reasoning_effort mapping with string and dict formats (Responses API bridge). + Regression: https://github.com/BerriAI/litellm/issues/37452 + """ + + def test_map_openai_params_reasoning_effort_string(self): + config = OllamaChatConfig() + assert config.map_openai_params({"reasoning_effort": "low"}, {}, "qwen3", False) == {"think": True} + assert config.map_openai_params({"reasoning_effort": "medium"}, {}, "qwen3", False) == {"think": True} + assert config.map_openai_params({"reasoning_effort": "high"}, {}, "qwen3", False) == {"think": True} + assert config.map_openai_params({"reasoning_effort": "none"}, {}, "qwen3", False) == {"think": False} + + def test_map_openai_params_reasoning_effort_dict_with_effort(self): + config = OllamaChatConfig() + result = config.map_openai_params( + {"reasoning_effort": {"effort": "medium", "summary": "auto"}}, {}, "qwen3", False + ) + assert result == {"think": True} + + result_low = config.map_openai_params({"reasoning_effort": {"effort": "low"}}, {}, "qwen3", False) + assert result_low == {"think": True} + + result_none = config.map_openai_params({"reasoning_effort": {"effort": "none"}}, {}, "qwen3", False) + assert result_none == {"think": False} + + def test_map_openai_params_reasoning_effort_dict_summary_only(self): + config = OllamaChatConfig() + result = config.map_openai_params({"reasoning_effort": {"summary": "auto"}}, {}, "qwen3", False) + assert "think" not in result + + def test_map_openai_params_reasoning_effort_gpt_oss(self): + config = OllamaChatConfig() + result = config.map_openai_params({"reasoning_effort": {"effort": "medium"}}, {}, "gpt-oss-123", False) + assert result == {"think": "medium"} diff --git a/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py b/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py index eadc2bc9541..26a3caa4ae6 100644 --- a/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py +++ b/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py @@ -502,3 +502,27 @@ class TestOllamaTextCompletionResponseIterator: assert result["usage"]["prompt_tokens"] == 10 assert result["usage"]["completion_tokens"] == 5 assert result["usage"]["total_tokens"] == 15 + + +class TestOllamaCompletionReasoningEffortMapping: + @pytest.mark.parametrize( + ("reasoning_effort", "model", "expected"), + [ + ("medium", "qwen3", {"think": True}), + ("none", "qwen3", {"think": False}), + ({"effort": "medium", "summary": "auto"}, "qwen3", {"think": True}), + ({"effort": "medium"}, "gpt-oss-123", {"think": "medium"}), + ({"summary": "auto"}, "qwen3", {}), + ], + ) + def test_map_openai_params_reasoning_effort(self, reasoning_effort, model, expected): + config = OllamaConfig() + + result = config.map_openai_params( + {"reasoning_effort": reasoning_effort}, + {}, + model, + False, + ) + + assert result == expected