From 3e688379dfc0f667cee7569b7063a23f80384f84 Mon Sep 17 00:00:00 2001 From: Rohan-Dedhia Date: Tue, 3 Nov 2026 22:16:38 +0530 Subject: [PATCH 1/2] fix(groq): support reasoning_format and handle reasoning_effort on reasoning models --- litellm/llms/groq/chat/transformation.py | 39 ++++++++++- .../chat/test_groq_chat_transformation.py | 69 +++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/litellm/llms/groq/chat/transformation.py b/litellm/llms/groq/chat/transformation.py index fd6b81fce8a..1b764b25dc2 100644 --- a/litellm/llms/groq/chat/transformation.py +++ b/litellm/llms/groq/chat/transformation.py @@ -57,6 +57,7 @@ class GroqChatConfig(OpenAILikeChatConfig): response_format: dict | None = None tools: list | None = None tool_choice: str | dict | None = None + reasoning_format: str | None = None def __init__( self, @@ -73,6 +74,7 @@ class GroqChatConfig(OpenAILikeChatConfig): response_format: dict | None = None, tools: list | None = None, tool_choice: str | dict | None = None, + reasoning_format: str | None = None, ) -> None: locals_: Final[Mapping[str, object]] = dict(locals()) for key, value in locals_.items(): @@ -113,13 +115,30 @@ class GroqChatConfig(OpenAILikeChatConfig): base_params.remove("web_search_options") try: - if litellm.supports_reasoning(model=model, custom_llm_provider=self.custom_llm_provider): - base_params.append("reasoning_effort") + if self._supports_reasoning(model): + base_params.append("reasoning_format") + if not self._is_deepseek_r1_model(model): + base_params.append("reasoning_effort") except Exception as e: verbose_logger.debug("Error checking if model supports reasoning: %s", e) return base_params + @staticmethod + def _is_deepseek_r1_model(model: str) -> bool: + clean_model = model.removeprefix("groq/").lower() + return "deepseek-r1" in clean_model + + @classmethod + def _supports_reasoning(cls, model: str) -> bool: + clean_model = model.removeprefix("groq/").lower() + if "deepseek-r1" in clean_model or "qwq" in clean_model: + return True + try: + return litellm.supports_reasoning(model=model, custom_llm_provider="groq") + except Exception: + return False + @staticmethod def _is_compound_model(model: str) -> bool: return model.removeprefix("groq/") in GROQ_COMPOUND_MODELS @@ -278,6 +297,22 @@ class GroqChatConfig(OpenAILikeChatConfig): "response_format", None ) # only remove if it's a json_schema - handled via using groq's tool calling params. # else: model supports native json_schema, let response_format pass through + reasoning_format: Final = non_default_params.pop("reasoning_format", None) + if reasoning_format is not None: + optional_params["reasoning_format"] = reasoning_format + + if self._is_deepseek_r1_model(model) and "reasoning_effort" in non_default_params: + if drop_params or litellm.drop_params: + verbose_logger.warning( + f"Dropping reasoning_effort={non_default_params['reasoning_effort']} for model={model} because Groq does not support reasoning_effort on DeepSeek R1 models." + ) + non_default_params.pop("reasoning_effort", None) + else: + raise litellm.UnsupportedParamsError( + message=f"reasoning_effort is not supported for {model} on Groq. To drop unsupported params set `litellm.drop_params = True`", + status_code=400, + ) + web_search_options: Final = non_default_params.pop("web_search_options", None) optional_params = super().map_openai_params(non_default_params, optional_params, model, drop_params) if web_search_options is None: diff --git a/tests/unit/llms/groq/chat/test_groq_chat_transformation.py b/tests/unit/llms/groq/chat/test_groq_chat_transformation.py index a493a259f39..5e9b280edbc 100644 --- a/tests/unit/llms/groq/chat/test_groq_chat_transformation.py +++ b/tests/unit/llms/groq/chat/test_groq_chat_transformation.py @@ -606,3 +606,72 @@ def test_groq_request_drops_null_assistant_fields_without_touching_the_callers_m assert body["messages"][1] == {"role": "assistant", "content": "Hello"} assert messages == messages_before + + +class TestGroqReasoningParams: + """Tests for Groq reasoning_effort and reasoning_format handling (Issue #49875).""" + + @pytest.mark.parametrize( + "model", + [ + "groq/deepseek-r1-distill-llama-70b", + "deepseek-r1-distill-llama-70b", + "groq/qwen/qwen3.8-27b", + "groq/openai/gpt-oss-120b", + ], + ) + def test_reasoning_format_in_supported_params_for_reasoning_models(self, model: str): + config = GroqChatConfig() + supported = config.get_supported_openai_params(model=model) + assert "reasoning_format" in supported + + def test_reasoning_format_not_in_supported_params_for_non_reasoning_models(self): + config = GroqChatConfig() + supported = config.get_supported_openai_params(model="llama-3.3-70b-versatile") + assert "reasoning_format" not in supported + + @pytest.mark.parametrize( + "model", + [ + "groq/deepseek-r1-distill-llama-70b", + "deepseek-r1-distill-llama-70b", + ], + ) + def test_reasoning_effort_not_in_supported_params_for_deepseek_r1(self, model: str): + config = GroqChatConfig() + supported = config.get_supported_openai_params(model=model) + assert "reasoning_effort" not in supported + + def test_deepseek_r1_reasoning_effort_raises_without_drop_params(self): + config = GroqChatConfig() + with pytest.raises(litellm.UnsupportedParamsError) as exc_info: + config.map_openai_params( + non_default_params={"reasoning_effort": "low"}, + optional_params={}, + model="groq/deepseek-r1-distill-llama-70b", + drop_params=False, + ) + assert "reasoning_effort is not supported" in str(exc_info.value) + + def test_deepseek_r1_reasoning_effort_dropped_with_drop_params(self): + config = GroqChatConfig() + optional_params = {} + result = config.map_openai_params( + non_default_params={"reasoning_effort": "low"}, + optional_params=optional_params, + model="groq/deepseek-r1-distill-llama-70b", + drop_params=True, + ) + assert "reasoning_effort" not in result + + @pytest.mark.parametrize("format_value", ["raw", "parsed", "hidden"]) + def test_reasoning_format_maps_to_optional_params(self, format_value: str): + config = GroqChatConfig() + optional_params = {} + result = config.map_openai_params( + non_default_params={"reasoning_format": format_value}, + optional_params=optional_params, + model="groq/deepseek-r1-distill-llama-70b", + drop_params=False, + ) + assert result.get("reasoning_format") == format_value From 64b00d819012bb8309d725ca5d6ae1d022d25af9 Mon Sep 17 00:00:00 2001 From: Rohan-Dedhia Date: Wed, 4 Nov 2026 12:13:13 +0530 Subject: [PATCH 2/2] fix(groq): consult model catalog for reasoning_effort support --- litellm/llms/groq/chat/transformation.py | 25 +++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/litellm/llms/groq/chat/transformation.py b/litellm/llms/groq/chat/transformation.py index 1b764b25dc2..d1f536c6ca0 100644 --- a/litellm/llms/groq/chat/transformation.py +++ b/litellm/llms/groq/chat/transformation.py @@ -117,13 +117,32 @@ class GroqChatConfig(OpenAILikeChatConfig): try: if self._supports_reasoning(model): base_params.append("reasoning_format") - if not self._is_deepseek_r1_model(model): + if self._model_supports_reasoning_effort(model): base_params.append("reasoning_effort") except Exception as e: verbose_logger.debug("Error checking if model supports reasoning: %s", e) return base_params + @classmethod + def _model_supports_reasoning_effort(cls, model: str) -> bool: + """ + Check if the model accepts reasoning_effort on Groq. + First consults the model catalog (if an explicit flag like supports_reasoning_effort is present), + and falls back to excluding DeepSeek R1 models which reason unconditionally and reject this parameter. + """ + try: + from litellm.utils import get_model_info + + model_info = get_model_info(model=model, custom_llm_provider="groq") + if model_info.get("supports_reasoning_effort") is False: + return False + if model_info.get("supports_reasoning_effort") is True: + return True + except Exception: + pass + return not cls._is_deepseek_r1_model(model) + @staticmethod def _is_deepseek_r1_model(model: str) -> bool: clean_model = model.removeprefix("groq/").lower() @@ -301,10 +320,10 @@ class GroqChatConfig(OpenAILikeChatConfig): if reasoning_format is not None: optional_params["reasoning_format"] = reasoning_format - if self._is_deepseek_r1_model(model) and "reasoning_effort" in non_default_params: + if not self._model_supports_reasoning_effort(model) and "reasoning_effort" in non_default_params: if drop_params or litellm.drop_params: verbose_logger.warning( - f"Dropping reasoning_effort={non_default_params['reasoning_effort']} for model={model} because Groq does not support reasoning_effort on DeepSeek R1 models." + f"Dropping reasoning_effort={non_default_params['reasoning_effort']} for model={model} because Groq does not support reasoning_effort on this model." ) non_default_params.pop("reasoning_effort", None) else: