groq: support reasoning_effort, update docs for clarity (#31754)

- There was some ambiguous wording that has been updated to hopefully
clarify the functionality of `reasoning_format` in ChatGroq.
- Added support for `reasoning_effort`
- Added links to see models capable of `reasoning_format` and
`reasoning_effort`
- Other minor nits
This commit is contained in:
Mason Daugherty 2025-06-27 09:43:40 -04:00 committed by GitHub
parent ea1345a58b
commit e1aff00cc1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 70 additions and 30 deletions

View File

@ -1 +0,0 @@
__pycache__

View File

@ -83,7 +83,7 @@ from langchain_groq.version import __version__
class ChatGroq(BaseChatModel): class ChatGroq(BaseChatModel):
"""`Groq` Chat large language models API. """Groq Chat large language models API.
To use, you should have the To use, you should have the
environment variable ``GROQ_API_KEY`` set with your API key. environment variable ``GROQ_API_KEY`` set with your API key.
@ -102,17 +102,27 @@ class ChatGroq(BaseChatModel):
Key init args completion params: Key init args completion params:
model: str model: str
Name of Groq model to use. E.g. "llama-3.1-8b-instant". Name of Groq model to use, e.g. ``llama-3.1-8b-instant``.
temperature: float temperature: float
Sampling temperature. Ranges from 0.0 to 1.0. Sampling temperature. Ranges from ``0.0`` to ``1.0``.
max_tokens: Optional[int] max_tokens: Optional[int]
Max number of tokens to generate. Max number of tokens to generate.
reasoning_format: Optional[Literal["parsed", "raw", "hidden]] reasoning_format: Optional[Literal["parsed", "raw", "hidden]]
The format for reasoning output. The format for reasoning output. Groq will default to ``raw`` if left
undefined.
- ``parsed``: Separates reasoning into a dedicated field while keeping the response concise. - ``'parsed'``: Separates reasoning into a dedicated field while keeping the
- ``raw``: Includes reasoning within think tags in the content. response concise. Reasoning will be returned in the
- ``hidden``: Returns only the final answer. ``additional_kwargs.reasoning_content`` field of the response.
- ``'raw'``: Includes reasoning within think tags (e.g.
``<think>{reasoning_content}</think>``).
- ``'hidden'``: Returns only the final answer content. Note: this only
supresses reasoning content in the response; the model will still perform
reasoning unless overridden in ``reasoning_effort``.
See the `Groq documentation
<https://console.groq.com/docs/reasoning#reasoning>`__ for more
details and a list of supported reasoning models.
model_kwargs: Dict[str, Any] model_kwargs: Dict[str, Any]
Holds any model parameters valid for create call not Holds any model parameters valid for create call not
explicitly specified. explicitly specified.
@ -123,7 +133,7 @@ class ChatGroq(BaseChatModel):
max_retries: int max_retries: int
Max number of retries. Max number of retries.
api_key: Optional[str] api_key: Optional[str]
Groq API key. If not passed in will be read from env var GROQ_API_KEY. Groq API key. If not passed in will be read from env var ``GROQ_API_KEY``.
base_url: Optional[str] base_url: Optional[str]
Base URL path for API requests, leave blank if not using a proxy Base URL path for API requests, leave blank if not using a proxy
or service emulator. or service emulator.
@ -168,11 +178,9 @@ class ChatGroq(BaseChatModel):
'logprobs': None}, id='run-ecc71d70-e10c-4b69-8b8c-b8027d95d4b8-0') 'logprobs': None}, id='run-ecc71d70-e10c-4b69-8b8c-b8027d95d4b8-0')
Stream: Stream:
Streaming `text` for each content chunk received:
.. code-block:: python .. code-block:: python
# Streaming `text` for each content chunk received
for chunk in llm.stream(messages): for chunk in llm.stream(messages):
print(chunk.text(), end="") print(chunk.text(), end="")
@ -188,10 +196,9 @@ class ChatGroq(BaseChatModel):
content='' response_metadata={'finish_reason': 'stop'} content='' response_metadata={'finish_reason': 'stop'}
id='run-4e9f926b-73f5-483b-8ef5-09533d925853 id='run-4e9f926b-73f5-483b-8ef5-09533d925853
Reconstructing a full response:
.. code-block:: python .. code-block:: python
# Reconstructing a full response
stream = llm.stream(messages) stream = llm.stream(messages)
full = next(stream) full = next(stream)
for chunk in stream: for chunk in stream:
@ -283,7 +290,7 @@ class ChatGroq(BaseChatModel):
See ``ChatGroq.with_structured_output()`` for more. See ``ChatGroq.with_structured_output()`` for more.
Response metadata Response metadata:
.. code-block:: python .. code-block:: python
ai_msg = llm.invoke(messages) ai_msg = llm.invoke(messages)
@ -302,7 +309,7 @@ class ChatGroq(BaseChatModel):
'system_fingerprint': 'fp_c5f20b5bb1', 'system_fingerprint': 'fp_c5f20b5bb1',
'finish_reason': 'stop', 'finish_reason': 'stop',
'logprobs': None} 'logprobs': None}
""" # noqa: E501 """
client: Any = Field(default=None, exclude=True) #: :meta private: client: Any = Field(default=None, exclude=True) #: :meta private:
async_client: Any = Field(default=None, exclude=True) #: :meta private: async_client: Any = Field(default=None, exclude=True) #: :meta private:
@ -312,23 +319,44 @@ class ChatGroq(BaseChatModel):
"""What sampling temperature to use.""" """What sampling temperature to use."""
stop: Optional[Union[list[str], str]] = Field(default=None, alias="stop_sequences") stop: Optional[Union[list[str], str]] = Field(default=None, alias="stop_sequences")
"""Default stop sequences.""" """Default stop sequences."""
reasoning_format: Optional[Literal["parsed", "raw", "hidden"]] = None reasoning_format: Optional[Literal["parsed", "raw", "hidden"]] = Field(default=None)
"""The format for reasoning output. """The format for reasoning output. Groq will default to raw if left undefined.
- ``parsed``: Separates reasoning into a dedicated field while keeping the response concise. - ``'parsed'``: Separates reasoning into a dedicated field while keeping the
- ``raw``: Includes reasoning within think tags in the content. response concise. Reasoning will be returned in the
- ``hidden``: Returns only the final answer. ``additional_kwargs.reasoning_content`` field of the response.
""" # noqa: E501 - ``'raw'``: Includes reasoning within think tags (e.g.
``<think>{reasoning_content}</think>``).
- ``'hidden'``: Returns only the final answer content. Note: this only supresses
reasoning content in the response; the model will still perform reasoning unless
overridden in ``reasoning_effort``.
See the `Groq documentation <https://console.groq.com/docs/reasoning#reasoning>`__
for more details and a list of supported reasoning models.
"""
reasoning_effort: Optional[Literal["none", "default"]] = Field(default=None)
"""The level of effort the model will put into reasoning. Groq will default to
enabling reasoning if left undefined. If set to ``none``, ``reasoning_format`` will
not apply and ``reasoning_content`` will not be returned.
- ``'none'``: Disable reasoning. The model will not use any reasoning tokens when
generating a response.
- ``'default'``: Enable reasoning.
See the `Groq documentation
<https://console.groq.com/docs/reasoning#options-for-reasoning-effort>`__ for more
details and a list of models that support setting a reasoning effort.
"""
model_kwargs: dict[str, Any] = Field(default_factory=dict) model_kwargs: dict[str, Any] = Field(default_factory=dict)
"""Holds any model parameters valid for `create` call not explicitly specified.""" """Holds any model parameters valid for `create` call not explicitly specified."""
groq_api_key: Optional[SecretStr] = Field( groq_api_key: Optional[SecretStr] = Field(
alias="api_key", default_factory=secret_from_env("GROQ_API_KEY", default=None) alias="api_key", default_factory=secret_from_env("GROQ_API_KEY", default=None)
) )
"""Automatically inferred from env var `GROQ_API_KEY` if not provided.""" """Automatically inferred from env var ``GROQ_API_KEY`` if not provided."""
groq_api_base: Optional[str] = Field( groq_api_base: Optional[str] = Field(
alias="base_url", default_factory=from_env("GROQ_API_BASE", default=None) alias="base_url", default_factory=from_env("GROQ_API_BASE", default=None)
) )
"""Base URL path for API requests, leave blank if not using a proxy or service """Base URL path for API requests. Leave blank if not using a proxy or service
emulator.""" emulator."""
# to support explicit proxy for Groq # to support explicit proxy for Groq
groq_proxy: Optional[str] = Field( groq_proxy: Optional[str] = Field(
@ -426,11 +454,11 @@ class ChatGroq(BaseChatModel):
self.async_client = groq.AsyncGroq( self.async_client = groq.AsyncGroq(
**client_params, **async_specific **client_params, **async_specific
).chat.completions ).chat.completions
except ImportError: except ImportError as exc:
raise ImportError( raise ImportError(
"Could not import groq python package. " "Could not import groq python package. "
"Please install it with `pip install groq`." "Please install it with `pip install groq`."
) ) from exc
return self return self
# #
@ -624,6 +652,7 @@ class ChatGroq(BaseChatModel):
"temperature": self.temperature, "temperature": self.temperature,
"stop": self.stop, "stop": self.stop,
"reasoning_format": self.reasoning_format, "reasoning_format": self.reasoning_format,
"reasoning_effort": self.reasoning_effort,
**self.model_kwargs, **self.model_kwargs,
} }
if self.max_tokens is not None: if self.max_tokens is not None:
@ -1227,7 +1256,7 @@ def _convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage:
for raw_tool_call in raw_tool_calls: for raw_tool_call in raw_tool_calls:
try: try:
tool_calls.append(parse_tool_call(raw_tool_call, return_id=True)) tool_calls.append(parse_tool_call(raw_tool_call, return_id=True))
except Exception as e: except Exception as e: # pylint: disable=broad-except
invalid_tool_calls.append( invalid_tool_calls.append(
make_invalid_tool_call(raw_tool_call, str(e)) make_invalid_tool_call(raw_tool_call, str(e))
) )

View File

@ -264,6 +264,19 @@ def test_reasoning_output_stream() -> None:
assert len(full_response.additional_kwargs["reasoning_content"]) > 0 assert len(full_response.additional_kwargs["reasoning_content"]) > 0
def test_reasoning_effort_none() -> None:
"""Test that no reasoning output is returned if effort is set to none."""
chat = ChatGroq(
model="qwen/qwen3-32b", # Only qwen3 currently supports reasoning_effort
reasoning_effort="none",
)
message = HumanMessage(content="What is the capital of France?")
response = chat.invoke([message])
assert isinstance(response, AIMessage)
assert "reasoning_content" not in response.additional_kwargs
assert "<think>" not in response.content and "<think/>" not in response.content
# #
# Misc tests # Misc tests
# #

View File

@ -450,7 +450,7 @@ class ChatOllama(BaseChatModel):
""" """
sync_client_kwargs: Optional[dict] = {} sync_client_kwargs: Optional[dict] = {}
"""Additional kwargs to merge with client_kwargs before passing to the httpx Client. """Additional kwargs to merge with client_kwargs before passing to the HTTPX Client.
For a full list of the params, see the `HTTPX documentation <https://www.python-httpx.org/api/#client>`__. For a full list of the params, see the `HTTPX documentation <https://www.python-httpx.org/api/#client>`__.
""" """

View File

@ -141,7 +141,7 @@ class OllamaEmbeddings(BaseModel, Embeddings):
""" """
sync_client_kwargs: Optional[dict] = {} sync_client_kwargs: Optional[dict] = {}
"""Additional kwargs to merge with client_kwargs before passing to the httpx Client. """Additional kwargs to merge with client_kwargs before passing to the HTTPX Client.
For a full list of the params, see the `HTTPX documentation <https://www.python-httpx.org/api/#client>`__. For a full list of the params, see the `HTTPX documentation <https://www.python-httpx.org/api/#client>`__.
""" """

View File

@ -1 +0,0 @@
__pycache__