From f716ebfc38595cae6a6e149559334fa2474ed267 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 01:42:45 +0000 Subject: [PATCH 01/23] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 516dbe09..9ac361b3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 15 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-d1588e103a6ae0234752b8e54a746fb1e4c93a0ee51ede294017bcd4f0ee4ac0.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-e08e9ad549e042c93beeb744e18876d6738dd400889552b924e1242cdecf8205.yml From e8911a43d64861153306bd03cfdaa06670b335f5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 03:19:07 +0000 Subject: [PATCH 02/23] feat(client): send `X-Stainless-Read-Timeout` header (#193) --- src/groq/_base_client.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/groq/_base_client.py b/src/groq/_base_client.py index c3f36095..ed833036 100644 --- a/src/groq/_base_client.py +++ b/src/groq/_base_client.py @@ -418,10 +418,17 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key or self._idempotency_key() - # Don't set the retry count header if it was already set or removed by the caller. We check + # Don't set these headers if they were already set or removed by the caller. We check # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. - if "x-stainless-retry-count" not in (header.lower() for header in custom_headers): + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) return headers From bcb025668a8b7279b8dbfb79384b1a20b95cd57f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 03:18:23 +0000 Subject: [PATCH 03/23] chore(internal): fix type traversing dictionary params (#195) --- src/groq/_utils/_transform.py | 12 +++++++++++- tests/test_transform.py | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/groq/_utils/_transform.py b/src/groq/_utils/_transform.py index a6b62cad..18afd9d8 100644 --- a/src/groq/_utils/_transform.py +++ b/src/groq/_utils/_transform.py @@ -25,7 +25,7 @@ is_annotated_type, strip_annotated_type, ) -from .._compat import model_dump, is_typeddict +from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -164,9 +164,14 @@ def _transform_recursive( inner_type = annotation stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return _transform_typeddict(data, stripped_type) + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + if ( # List[T] (is_list_type(stripped_type) and is_list(data)) @@ -307,9 +312,14 @@ async def _async_transform_recursive( inner_type = annotation stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return await _async_transform_typeddict(data, stripped_type) + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + if ( # List[T] (is_list_type(stripped_type) and is_list(data)) diff --git a/tests/test_transform.py b/tests/test_transform.py index e29e15cf..af72c409 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -2,7 +2,7 @@ import io import pathlib -from typing import Any, List, Union, TypeVar, Iterable, Optional, cast +from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict @@ -388,6 +388,15 @@ def my_iter() -> Iterable[Baz8]: } +@parametrize +@pytest.mark.asyncio +async def test_dictionary_items(use_async: bool) -> None: + class DictItems(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} + + class TypedDictIterableUnionStr(TypedDict): foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] From 3ff53df5cc754090a645babff1f3e5d636f9e71c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 03:21:29 +0000 Subject: [PATCH 04/23] chore(internal): minor type handling changes (#196) --- src/groq/_models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/groq/_models.py b/src/groq/_models.py index 12c34b7d..c4401ff8 100644 --- a/src/groq/_models.py +++ b/src/groq/_models.py @@ -426,10 +426,16 @@ def construct_type(*, value: object, type_: object) -> object: If the given value does not match the expected type then it is returned as-is. """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + # we allow `object` as the input type because otherwise, passing things like # `Literal['value']` will be reported as a type error by type checkers type_ = cast("type[object]", type_) if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` @@ -446,7 +452,7 @@ def construct_type(*, value: object, type_: object) -> object: if is_union(origin): try: - return validate_type(type_=cast("type[object]", type_), value=value) + return validate_type(type_=cast("type[object]", original_type or type_), value=value) except Exception: pass From 2f0d2c475a11d7162dffbb31008f01b17b2c8ef4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Feb 2025 03:53:31 +0000 Subject: [PATCH 05/23] chore(internal): update client tests (#197) --- tests/test_client.py | 110 ++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 1615f63b..1dc985ab 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,10 +23,12 @@ from groq import Groq, AsyncGroq, APIResponseValidationError from groq._types import Omit +from groq._utils import maybe_transform from groq._models import BaseModel, FinalRequestOptions from groq._constants import RAW_RESPONSE_HEADER from groq._exceptions import GroqError, APIStatusError, APITimeoutError, APIResponseValidationError from groq._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options +from groq.types.chat.completion_create_params import CompletionCreateParams from .utils import update_env @@ -706,18 +708,21 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> No "/openai/v1/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "system", - "content": "You are a helpful assistant.", - }, - { - "role": "user", - "content": "Explain the importance of low latency LLMs", - }, - ], - model="llama3-8b-8192", + maybe_transform( + dict( + messages=[ + { + "role": "system", + "content": "You are a helpful assistant.", + }, + { + "role": "user", + "content": "Explain the importance of low latency LLMs", + }, + ], + model="llama3-8b-8192", + ), + CompletionCreateParams, ), ), cast_to=httpx.Response, @@ -736,18 +741,21 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> Non "/openai/v1/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "system", - "content": "You are a helpful assistant.", - }, - { - "role": "user", - "content": "Explain the importance of low latency LLMs", - }, - ], - model="llama3-8b-8192", + maybe_transform( + dict( + messages=[ + { + "role": "system", + "content": "You are a helpful assistant.", + }, + { + "role": "user", + "content": "Explain the importance of low latency LLMs", + }, + ], + model="llama3-8b-8192", + ), + CompletionCreateParams, ), ), cast_to=httpx.Response, @@ -1526,18 +1534,21 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) "/openai/v1/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "system", - "content": "You are a helpful assistant.", - }, - { - "role": "user", - "content": "Explain the importance of low latency LLMs", - }, - ], - model="llama3-8b-8192", + maybe_transform( + dict( + messages=[ + { + "role": "system", + "content": "You are a helpful assistant.", + }, + { + "role": "user", + "content": "Explain the importance of low latency LLMs", + }, + ], + model="llama3-8b-8192", + ), + CompletionCreateParams, ), ), cast_to=httpx.Response, @@ -1556,18 +1567,21 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) "/openai/v1/chat/completions", body=cast( object, - dict( - messages=[ - { - "role": "system", - "content": "You are a helpful assistant.", - }, - { - "role": "user", - "content": "Explain the importance of low latency LLMs", - }, - ], - model="llama3-8b-8192", + maybe_transform( + dict( + messages=[ + { + "role": "system", + "content": "You are a helpful assistant.", + }, + { + "role": "user", + "content": "Explain the importance of low latency LLMs", + }, + ], + model="llama3-8b-8192", + ), + CompletionCreateParams, ), ), cast_to=httpx.Response, From 49387fe83c2886f6c623f7718f041c0065829a2b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2025 03:44:40 +0000 Subject: [PATCH 06/23] fix: asyncify on non-asyncio runtimes (#198) --- src/groq/_utils/_sync.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/groq/_utils/_sync.py b/src/groq/_utils/_sync.py index 8b3aaf2b..ad7ec71b 100644 --- a/src/groq/_utils/_sync.py +++ b/src/groq/_utils/_sync.py @@ -7,16 +7,20 @@ from typing import Any, TypeVar, Callable, Awaitable from typing_extensions import ParamSpec +import anyio +import sniffio +import anyio.to_thread + T_Retval = TypeVar("T_Retval") T_ParamSpec = ParamSpec("T_ParamSpec") if sys.version_info >= (3, 9): - to_thread = asyncio.to_thread + _asyncio_to_thread = asyncio.to_thread else: # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread # for Python 3.8 support - async def to_thread( + async def _asyncio_to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> Any: """Asynchronously run function *func* in a separate thread. @@ -34,6 +38,17 @@ async def to_thread( return await loop.run_in_executor(None, func_call) +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await _asyncio_to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + # inspired by `asyncer`, https://github.com/tiangolo/asyncer def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ From de2ac71d68109c3b29e6de1ba97f2c7092881c42 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 19 Feb 2025 04:01:55 +0000 Subject: [PATCH 07/23] chore(internal): codegen related update (#199) --- README.md | 18 ++++++++++++++++++ src/groq/_files.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fa683cb3..f5039b60 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,24 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## File uploads + +Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. + +```python +from pathlib import Path +from groq import Groq + +client = Groq() + +client.audio.transcriptions.create( + file=Path("/path/to/file"), + model="whisper-large-v3", +) +``` + +The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically. + ## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `groq.APIConnectionError` is raised. diff --git a/src/groq/_files.py b/src/groq/_files.py index 715cc207..68da8098 100644 --- a/src/groq/_files.py +++ b/src/groq/_files.py @@ -34,7 +34,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None: if not is_file_content(obj): prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead." + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/groq/groq-python/tree/main#file-uploads" ) from None From 43d3f66232cd5292ffd14616a72eef48fc75b4e2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 22:52:58 +0000 Subject: [PATCH 08/23] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 9ac361b3..6a902ec6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 15 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-e08e9ad549e042c93beeb744e18876d6738dd400889552b924e1242cdecf8205.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-f89535723507271523a0caf508b086a35c27864aad5e543b280e13d9c482c504.yml From 0fcd510d4171075ec554c305e048ff5a20ae115a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 22:55:01 +0000 Subject: [PATCH 09/23] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 6a902ec6..3913b8d8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 15 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-f89535723507271523a0caf508b086a35c27864aad5e543b280e13d9c482c504.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-21e2668d8b211239f9b1019b09a89fcbc00855284b2434a52d80abf32de2e8f7.yml From afa6c0fc0191cedbacf99a7c6ac662d888ba9ffd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 05:23:56 +0000 Subject: [PATCH 10/23] feat(client): allow passing `NotGiven` for body (#200) fix(client): mark some request bodies as optional --- src/groq/_base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groq/_base_client.py b/src/groq/_base_client.py index ed833036..e76c0a5c 100644 --- a/src/groq/_base_client.py +++ b/src/groq/_base_client.py @@ -518,7 +518,7 @@ def _build_request( # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, - json=json_data, + json=json_data if is_given(json_data) else None, files=files, **kwargs, ) From af101ee282a335d9f7970a92a99f6b63db9aebd8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 04:11:21 +0000 Subject: [PATCH 11/23] chore(internal): fix devcontainers setup (#201) --- .devcontainer/Dockerfile | 2 +- .devcontainer/devcontainer.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ac9a2e75..55d20255 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,4 +6,4 @@ USER vscode RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH -RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc +RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bbeb30b1..c17fdc16 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -24,6 +24,9 @@ } } } + }, + "features": { + "ghcr.io/devcontainers/features/node:1": {} } // Features to add to the dev container. More info: https://containers.dev/features. From 07ec0c885d7998717a2702de08dd5f1a4ee397ec Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2025 03:31:07 +0000 Subject: [PATCH 12/23] chore(internal): properly set __pydantic_private__ (#202) --- src/groq/_base_client.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/groq/_base_client.py b/src/groq/_base_client.py index e76c0a5c..53197f67 100644 --- a/src/groq/_base_client.py +++ b/src/groq/_base_client.py @@ -63,7 +63,7 @@ ModelBuilderProtocol, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import model_copy, model_dump +from ._compat import PYDANTIC_V2, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -207,6 +207,9 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options @@ -292,6 +295,9 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: + if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options From a6c6fde2ae256469481a28b59beaed1ab1f61c96 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 28 Feb 2025 03:07:59 +0000 Subject: [PATCH 13/23] docs: update URLs from stainlessapi.com to stainless.com (#203) More details at https://www.stainless.com/changelog/stainless-com --- README.md | 2 +- SECURITY.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f5039b60..f9c6eefd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The Groq Python library provides convenient access to the Groq REST API from any application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). -It is generated with [Stainless](https://www.stainlessapi.com/). +It is generated with [Stainless](https://www.stainless.com/). ## Documentation diff --git a/SECURITY.md b/SECURITY.md index 9550f353..c77277c1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting Security Issues -This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. -To report a security issue, please contact the Stainless team at security@stainlessapi.com. +To report a security issue, please contact the Stainless team at security@stainless.com. ## Responsible Disclosure From a0f45996ff149e0ed6f1fc262eb7041297b4bd68 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 28 Feb 2025 03:08:49 +0000 Subject: [PATCH 14/23] chore(docs): update client docstring (#204) --- src/groq/_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/groq/_client.py b/src/groq/_client.py index d77b872a..8c393bd3 100644 --- a/src/groq/_client.py +++ b/src/groq/_client.py @@ -74,7 +74,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new synchronous groq client instance. + """Construct a new synchronous Groq client instance. This automatically infers the `api_key` argument from the `GROQ_API_KEY` environment variable if it is not provided. """ @@ -252,7 +252,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new async groq client instance. + """Construct a new async AsyncGroq client instance. This automatically infers the `api_key` argument from the `GROQ_API_KEY` environment variable if it is not provided. """ From 12fdb59dbcaa121710f5c5ea710ff0f14c8c6dce Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 04:04:04 +0000 Subject: [PATCH 15/23] chore(internal): remove unused http client options forwarding (#205) --- src/groq/_base_client.py | 97 +--------------------------------------- 1 file changed, 1 insertion(+), 96 deletions(-) diff --git a/src/groq/_base_client.py b/src/groq/_base_client.py index 53197f67..531df7e6 100644 --- a/src/groq/_base_client.py +++ b/src/groq/_base_client.py @@ -9,7 +9,6 @@ import inspect import logging import platform -import warnings import email.utils from types import TracebackType from random import random @@ -36,7 +35,7 @@ import httpx import distro import pydantic -from httpx import URL, Limits +from httpx import URL from pydantic import PrivateAttr from . import _exceptions @@ -51,13 +50,10 @@ Timeout, NotGiven, ResponseT, - Transport, AnyMapping, PostParser, - ProxiesTypes, RequestFiles, HttpxSendArgs, - AsyncTransport, RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, @@ -337,9 +333,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): _base_url: URL max_retries: int timeout: Union[float, Timeout, None] - _limits: httpx.Limits - _proxies: ProxiesTypes | None - _transport: Transport | AsyncTransport | None _strict_response_validation: bool _idempotency_header: str | None _default_stream_cls: type[_DefaultStreamT] | None = None @@ -352,9 +345,6 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None = DEFAULT_TIMEOUT, - limits: httpx.Limits, - transport: Transport | AsyncTransport | None, - proxies: ProxiesTypes | None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: @@ -362,9 +352,6 @@ def __init__( self._base_url = self._enforce_trailing_slash(URL(base_url)) self.max_retries = max_retries self.timeout = timeout - self._limits = limits - self._proxies = proxies - self._transport = transport self._custom_headers = custom_headers or {} self._custom_query = custom_query or {} self._strict_response_validation = _strict_response_validation @@ -800,46 +787,11 @@ def __init__( base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: Transport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, _strict_response_validation: bool, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -860,12 +812,9 @@ def __init__( super().__init__( version=version, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, base_url=base_url, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -875,9 +824,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: @@ -1372,45 +1318,10 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: AsyncTransport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -1432,11 +1343,8 @@ def __init__( super().__init__( version=version, base_url=base_url, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -1446,9 +1354,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: From 7b04f472fa2a203cda9747308920974ed427f0ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 09:00:04 +0000 Subject: [PATCH 16/23] docs: revise readme docs about nested params (#206) --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index f9c6eefd..fda535e4 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,28 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: + +```python +from groq import Groq + +client = Groq() + +chat_completion = client.chat.completions.create( + messages=[ + { + "content": "content", + "role": "system", + } + ], + model="string", + response_format={"type": "text"}, +) +print(chat_completion.response_format) +``` + ## File uploads Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. From b0261ea62516b81ee14c445176962ca27bace728 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 09:28:50 +0000 Subject: [PATCH 17/23] test: add DEFER_PYDANTIC_BUILD=false flag to tests (#207) --- scripts/test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/test b/scripts/test index 4fa5698b..2b878456 100755 --- a/scripts/test +++ b/scripts/test @@ -52,6 +52,8 @@ else echo fi +export DEFER_PYDANTIC_BUILD=false + echo "==> Running tests" rye run pytest "$@" From 01fb0d14e438eeaef6ab1518a0c49e5b5b8e7197 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 22:02:10 +0000 Subject: [PATCH 18/23] chore(api): remove chat_completion_chunk to force a rebuild of it (#208) --- api.md | 1 - 1 file changed, 1 deletion(-) diff --git a/api.md b/api.md index b977024a..a0e27e25 100644 --- a/api.md +++ b/api.md @@ -22,7 +22,6 @@ Types: from groq.types.chat import ( ChatCompletion, ChatCompletionAssistantMessageParam, - ChatCompletionChunk, ChatCompletionContentPart, ChatCompletionContentPartImage, ChatCompletionContentPartText, From 91ec11b571630f021de3da04d9ec25f9e41d1a5d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 22:07:20 +0000 Subject: [PATCH 19/23] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 3913b8d8..5bbfc158 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 15 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-21e2668d8b211239f9b1019b09a89fcbc00855284b2434a52d80abf32de2e8f7.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-57d3a5849707a506928382cdfe5d611cf97b6ab39d5a72f354b68e497db69398.yml From aa1667f9a359a5eb9847fb72ede7cfb27b97baa8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 22:09:15 +0000 Subject: [PATCH 20/23] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 5bbfc158..3913b8d8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ configured_endpoints: 15 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-57d3a5849707a506928382cdfe5d611cf97b6ab39d5a72f354b68e497db69398.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-21e2668d8b211239f9b1019b09a89fcbc00855284b2434a52d80abf32de2e8f7.yml From 15e2dca833561464c7f56b3b5ce4de2bb4a90dfe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 22:12:35 +0000 Subject: [PATCH 21/23] feat(api): manual updates (#209) --- api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/api.md b/api.md index a0e27e25..b977024a 100644 --- a/api.md +++ b/api.md @@ -22,6 +22,7 @@ Types: from groq.types.chat import ( ChatCompletion, ChatCompletionAssistantMessageParam, + ChatCompletionChunk, ChatCompletionContentPart, ChatCompletionContentPartImage, ChatCompletionContentPartText, From edfee3b6c5976da372908fcb0cd02e91f5b0cea3 Mon Sep 17 00:00:00 2001 From: Graden Rea Date: Tue, 11 Mar 2025 15:22:06 -0700 Subject: [PATCH 22/23] fix: add reasoning field to ChoiceDelta class --- src/groq/types/chat/chat_completion_chunk.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/groq/types/chat/chat_completion_chunk.py b/src/groq/types/chat/chat_completion_chunk.py index 06d2a866..a84bfea8 100644 --- a/src/groq/types/chat/chat_completion_chunk.py +++ b/src/groq/types/chat/chat_completion_chunk.py @@ -68,6 +68,13 @@ class ChoiceDelta(BaseModel): model. """ + reasoning: Optional[str] = None + """The model's reasoning for a response. + + Only available for reasoning models when requests parameter reasoning_format has + value `parsed. + """ + role: Optional[Literal["system", "user", "assistant", "tool"]] = None """The role of the author of this message.""" From 37016fbb1a5054fc18942a4bd66c2213ed7aa4a4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 22:22:27 +0000 Subject: [PATCH 23/23] release: 0.19.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 38 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/groq/_version.py | 2 +- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4ad3fef3..e7562934 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.18.0" + ".": "0.19.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f3988a1..9bf35847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## 0.19.0 (2025-03-11) + +Full Changelog: [v0.18.0...v0.19.0](https://github.com/groq/groq-python/compare/v0.18.0...v0.19.0) + +### Features + +* **api:** manual updates ([#209](https://github.com/groq/groq-python/issues/209)) ([15e2dca](https://github.com/groq/groq-python/commit/15e2dca833561464c7f56b3b5ce4de2bb4a90dfe)) +* **client:** allow passing `NotGiven` for body ([#200](https://github.com/groq/groq-python/issues/200)) ([afa6c0f](https://github.com/groq/groq-python/commit/afa6c0fc0191cedbacf99a7c6ac662d888ba9ffd)) +* **client:** send `X-Stainless-Read-Timeout` header ([#193](https://github.com/groq/groq-python/issues/193)) ([e8911a4](https://github.com/groq/groq-python/commit/e8911a43d64861153306bd03cfdaa06670b335f5)) + + +### Bug Fixes + +* add reasoning field to ChoiceDelta class ([edfee3b](https://github.com/groq/groq-python/commit/edfee3b6c5976da372908fcb0cd02e91f5b0cea3)) +* asyncify on non-asyncio runtimes ([#198](https://github.com/groq/groq-python/issues/198)) ([49387fe](https://github.com/groq/groq-python/commit/49387fe83c2886f6c623f7718f041c0065829a2b)) +* **client:** mark some request bodies as optional ([afa6c0f](https://github.com/groq/groq-python/commit/afa6c0fc0191cedbacf99a7c6ac662d888ba9ffd)) +* GitHub Terraform: Create/Update .github/workflows/stale.yaml [skip ci] ([662763a](https://github.com/groq/groq-python/commit/662763a5eaf833226772dedb36f64cfd460901a1)) +* GitHub Terraform: Create/Update .github/workflows/stale.yaml [skip ci] ([5298ec1](https://github.com/groq/groq-python/commit/5298ec1a8c6f1b6958a217c95f6a8bb19e90bd28)) + + +### Chores + +* **api:** remove chat_completion_chunk to force a rebuild of it ([#208](https://github.com/groq/groq-python/issues/208)) ([01fb0d1](https://github.com/groq/groq-python/commit/01fb0d14e438eeaef6ab1518a0c49e5b5b8e7197)) +* **docs:** update client docstring ([#204](https://github.com/groq/groq-python/issues/204)) ([a0f4599](https://github.com/groq/groq-python/commit/a0f45996ff149e0ed6f1fc262eb7041297b4bd68)) +* **internal:** codegen related update ([#199](https://github.com/groq/groq-python/issues/199)) ([de2ac71](https://github.com/groq/groq-python/commit/de2ac71d68109c3b29e6de1ba97f2c7092881c42)) +* **internal:** fix devcontainers setup ([#201](https://github.com/groq/groq-python/issues/201)) ([af101ee](https://github.com/groq/groq-python/commit/af101ee282a335d9f7970a92a99f6b63db9aebd8)) +* **internal:** fix type traversing dictionary params ([#195](https://github.com/groq/groq-python/issues/195)) ([bcb0256](https://github.com/groq/groq-python/commit/bcb025668a8b7279b8dbfb79384b1a20b95cd57f)) +* **internal:** minor type handling changes ([#196](https://github.com/groq/groq-python/issues/196)) ([3ff53df](https://github.com/groq/groq-python/commit/3ff53df5cc754090a645babff1f3e5d636f9e71c)) +* **internal:** properly set __pydantic_private__ ([#202](https://github.com/groq/groq-python/issues/202)) ([07ec0c8](https://github.com/groq/groq-python/commit/07ec0c885d7998717a2702de08dd5f1a4ee397ec)) +* **internal:** remove unused http client options forwarding ([#205](https://github.com/groq/groq-python/issues/205)) ([12fdb59](https://github.com/groq/groq-python/commit/12fdb59dbcaa121710f5c5ea710ff0f14c8c6dce)) +* **internal:** update client tests ([#197](https://github.com/groq/groq-python/issues/197)) ([2f0d2c4](https://github.com/groq/groq-python/commit/2f0d2c475a11d7162dffbb31008f01b17b2c8ef4)) + + +### Documentation + +* revise readme docs about nested params ([#206](https://github.com/groq/groq-python/issues/206)) ([7b04f47](https://github.com/groq/groq-python/commit/7b04f472fa2a203cda9747308920974ed427f0ed)) +* update URLs from stainlessapi.com to stainless.com ([#203](https://github.com/groq/groq-python/issues/203)) ([a6c6fde](https://github.com/groq/groq-python/commit/a6c6fde2ae256469481a28b59beaed1ab1f61c96)) + ## 0.18.0 (2025-02-05) Full Changelog: [v0.17.0...v0.18.0](https://github.com/groq/groq-python/compare/v0.17.0...v0.18.0) diff --git a/pyproject.toml b/pyproject.toml index d2f35868..f5613cf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "groq" -version = "0.18.0" +version = "0.19.0" description = "The official Python library for the groq API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/groq/_version.py b/src/groq/_version.py index dbdf6580..f4939f22 100644 --- a/src/groq/_version.py +++ b/src/groq/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "groq" -__version__ = "0.18.0" # x-release-please-version +__version__ = "0.19.0" # x-release-please-version