diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0389323..d66d8f94 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,30 @@ jobs: - name: Run lints run: ./scripts/lint + upload: + if: github.repository == 'stainless-sdks/groqcloud-python' + timeout-minutes: 10 + name: upload + permissions: + contents: read + id-token: write + runs-on: depot-ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Get GitHub OIDC Token + id: github-oidc + uses: actions/github-script@v6 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + test: timeout-minutes: 10 name: test diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d2d60a3d..a36746b8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.24.0" + ".": "0.25.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 7a7d45c6..3a552aa1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 17 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-e4cd6fe4e6ac62707635fac8fb7d966a0360868e467b578ddd7cc04a9459ff26.yml -openapi_spec_hash: e618e809624bb2f3b36995638c3ba791 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/groqcloud%2Fgroqcloud-66cde2321f98e4359dd3df749ddaf02abf371b19b126f1defa314d18f178c88d.yml +openapi_spec_hash: aa85981d19627dff8b175a7fd60d575d config_hash: 6b1c374dcc1ffa3165dd22f52a77ff89 diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e39459..ba6789cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 0.25.0 (2025-05-16) + +Full Changelog: [v0.24.0...v0.25.0](https://github.com/groq/groq-python/compare/v0.24.0...v0.25.0) + +### Features + +* **api:** api update ([e3acb61](https://github.com/groq/groq-python/commit/e3acb618e09864829089575c51ec617b2f4b6975)) +* **api:** api update ([4bb39e5](https://github.com/groq/groq-python/commit/4bb39e59bbd317de95b3e1d896ab83ee41bc7b3e)) +* **api:** api update ([ce3c251](https://github.com/groq/groq-python/commit/ce3c2514e2e66557de3e583532743cb3806032a2)) + + +### Bug Fixes + +* GitHub Terraform: Create/Update .github/workflows/stale.yaml [skip ci] ([a365e26](https://github.com/groq/groq-python/commit/a365e262f988103f5757ffd9054b822a72868586)) +* **package:** support direct resource imports ([3ee8779](https://github.com/groq/groq-python/commit/3ee87792c92196abba0a1c7d9400a34d95c58895)) + + +### Chores + +* **ci:** fix installation instructions ([454ff60](https://github.com/groq/groq-python/commit/454ff60a12d7e16127d87ef9c152b9e4a394246d)) +* **ci:** upload sdks to package manager ([1f464f8](https://github.com/groq/groq-python/commit/1f464f84bee361e07214e6b65a6727a1b5e0bc7b)) +* **internal:** avoid errors for isinstance checks on proxies ([7a04964](https://github.com/groq/groq-python/commit/7a04964d964feaac9dfcf1f46dad1f63a5acf714)) +* use lazy imports for resources ([8c6351a](https://github.com/groq/groq-python/commit/8c6351a0215827dea34174978a78d06a528e6eef)) + + +### Documentation + +* remove or fix invalid readme examples ([4dc027a](https://github.com/groq/groq-python/commit/4dc027a35054db11eec6c8bfe1fd463b0c49db6e)) + ## 0.24.0 (2025-05-02) Full Changelog: [v0.23.1...v0.24.0](https://github.com/groq/groq-python/compare/v0.23.1...v0.24.0) diff --git a/README.md b/README.md index 4321c9c8..09384309 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ chat_completion = client.chat.completions.create( ], model="llama3-8b-8192", ) -print(chat_completion.choices[0].message.content) +print(chat_completion.id) ``` While you can provide an `api_key` keyword argument, @@ -72,7 +72,7 @@ async def main() -> None: ], model="llama3-8b-8192", ) - print(chat_completion.choices[0].message.content) + print(chat_completion.id) asyncio.run(main()) @@ -106,9 +106,13 @@ chat_completion = client.chat.completions.create( } ], model="string", - response_format={"type": "json_object"}, + search_settings={ + "exclude_domains": ["string"], + "include_domains": ["string"], + "include_images": True, + }, ) -print(chat_completion.response_format) +print(chat_completion.search_settings) ``` ## File uploads diff --git a/pyproject.toml b/pyproject.toml index 8d1ca7b5..45df707f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "groq" -version = "0.24.0" +version = "0.25.0" description = "The official Python library for the groq API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 00000000..7da1e1b7 --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -exuo pipefail + +RESPONSE=$(curl -X POST "$URL" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + +SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') + +if [[ "$SIGNED_URL" == "null" ]]; then + echo -e "\033[31mFailed to get signed URL.\033[0m" + exit 1 +fi + +UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \ + -H "Content-Type: application/gzip" \ + --data-binary @- "$SIGNED_URL" 2>&1) + +if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then + echo -e "\033[32mUploaded build to Stainless storage.\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/groqcloud-python/$SHA'\033[0m" +else + echo -e "\033[31mFailed to upload artifact.\033[0m" + exit 1 +fi diff --git a/src/groq/__init__.py b/src/groq/__init__.py index 07f77a63..9ad2b0e2 100644 --- a/src/groq/__init__.py +++ b/src/groq/__init__.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import typing as _t + from . import types from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes from ._utils import file_from_path @@ -68,6 +70,9 @@ "DefaultAsyncHttpxClient", ] +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + _setup_logging() # Update the __module__ attribute for exported symbols so that diff --git a/src/groq/_client.py b/src/groq/_client.py index 6c7f997b..ee203f04 100644 --- a/src/groq/_client.py +++ b/src/groq/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Union, Mapping +from typing import TYPE_CHECKING, Any, Union, Mapping from typing_extensions import Self, override import httpx @@ -20,8 +20,8 @@ RequestOptions, ) from ._utils import is_given, get_async_library +from ._compat import cached_property from ._version import __version__ -from .resources import files, models, batches, embeddings from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import GroqError, APIStatusError from ._base_client import ( @@ -29,22 +29,20 @@ SyncAPIClient, AsyncAPIClient, ) -from .resources.chat import chat -from .resources.audio import audio + +if TYPE_CHECKING: + from .resources import chat, audio, files, models, batches, embeddings + from .resources.files import Files, AsyncFiles + from .resources.models import Models, AsyncModels + from .resources.batches import Batches, AsyncBatches + from .resources.chat.chat import Chat, AsyncChat + from .resources.embeddings import Embeddings, AsyncEmbeddings + from .resources.audio.audio import Audio, AsyncAudio __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Groq", "AsyncGroq", "Client", "AsyncClient"] class Groq(SyncAPIClient): - chat: chat.Chat - embeddings: embeddings.Embeddings - audio: audio.Audio - models: models.Models - batches: batches.Batches - files: files.Files - with_raw_response: GroqWithRawResponse - with_streaming_response: GroqWithStreamedResponse - # client options api_key: str @@ -99,14 +97,49 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.chat = chat.Chat(self) - self.embeddings = embeddings.Embeddings(self) - self.audio = audio.Audio(self) - self.models = models.Models(self) - self.batches = batches.Batches(self) - self.files = files.Files(self) - self.with_raw_response = GroqWithRawResponse(self) - self.with_streaming_response = GroqWithStreamedResponse(self) + @cached_property + def chat(self) -> Chat: + from .resources.chat import Chat + + return Chat(self) + + @cached_property + def embeddings(self) -> Embeddings: + from .resources.embeddings import Embeddings + + return Embeddings(self) + + @cached_property + def audio(self) -> Audio: + from .resources.audio import Audio + + return Audio(self) + + @cached_property + def models(self) -> Models: + from .resources.models import Models + + return Models(self) + + @cached_property + def batches(self) -> Batches: + from .resources.batches import Batches + + return Batches(self) + + @cached_property + def files(self) -> Files: + from .resources.files import Files + + return Files(self) + + @cached_property + def with_raw_response(self) -> GroqWithRawResponse: + return GroqWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GroqWithStreamedResponse: + return GroqWithStreamedResponse(self) @property @override @@ -214,15 +247,6 @@ def _make_status_error( class AsyncGroq(AsyncAPIClient): - chat: chat.AsyncChat - embeddings: embeddings.AsyncEmbeddings - audio: audio.AsyncAudio - models: models.AsyncModels - batches: batches.AsyncBatches - files: files.AsyncFiles - with_raw_response: AsyncGroqWithRawResponse - with_streaming_response: AsyncGroqWithStreamedResponse - # client options api_key: str @@ -277,14 +301,49 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.chat = chat.AsyncChat(self) - self.embeddings = embeddings.AsyncEmbeddings(self) - self.audio = audio.AsyncAudio(self) - self.models = models.AsyncModels(self) - self.batches = batches.AsyncBatches(self) - self.files = files.AsyncFiles(self) - self.with_raw_response = AsyncGroqWithRawResponse(self) - self.with_streaming_response = AsyncGroqWithStreamedResponse(self) + @cached_property + def chat(self) -> AsyncChat: + from .resources.chat import AsyncChat + + return AsyncChat(self) + + @cached_property + def embeddings(self) -> AsyncEmbeddings: + from .resources.embeddings import AsyncEmbeddings + + return AsyncEmbeddings(self) + + @cached_property + def audio(self) -> AsyncAudio: + from .resources.audio import AsyncAudio + + return AsyncAudio(self) + + @cached_property + def models(self) -> AsyncModels: + from .resources.models import AsyncModels + + return AsyncModels(self) + + @cached_property + def batches(self) -> AsyncBatches: + from .resources.batches import AsyncBatches + + return AsyncBatches(self) + + @cached_property + def files(self) -> AsyncFiles: + from .resources.files import AsyncFiles + + return AsyncFiles(self) + + @cached_property + def with_raw_response(self) -> AsyncGroqWithRawResponse: + return AsyncGroqWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGroqWithStreamedResponse: + return AsyncGroqWithStreamedResponse(self) @property @override @@ -392,43 +451,175 @@ def _make_status_error( class GroqWithRawResponse: + _client: Groq + def __init__(self, client: Groq) -> None: - self.chat = chat.ChatWithRawResponse(client.chat) - self.embeddings = embeddings.EmbeddingsWithRawResponse(client.embeddings) - self.audio = audio.AudioWithRawResponse(client.audio) - self.models = models.ModelsWithRawResponse(client.models) - self.batches = batches.BatchesWithRawResponse(client.batches) - self.files = files.FilesWithRawResponse(client.files) + self._client = client + + @cached_property + def chat(self) -> chat.ChatWithRawResponse: + from .resources.chat import ChatWithRawResponse + + return ChatWithRawResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.EmbeddingsWithRawResponse: + from .resources.embeddings import EmbeddingsWithRawResponse + + return EmbeddingsWithRawResponse(self._client.embeddings) + + @cached_property + def audio(self) -> audio.AudioWithRawResponse: + from .resources.audio import AudioWithRawResponse + + return AudioWithRawResponse(self._client.audio) + + @cached_property + def models(self) -> models.ModelsWithRawResponse: + from .resources.models import ModelsWithRawResponse + + return ModelsWithRawResponse(self._client.models) + + @cached_property + def batches(self) -> batches.BatchesWithRawResponse: + from .resources.batches import BatchesWithRawResponse + + return BatchesWithRawResponse(self._client.batches) + + @cached_property + def files(self) -> files.FilesWithRawResponse: + from .resources.files import FilesWithRawResponse + + return FilesWithRawResponse(self._client.files) class AsyncGroqWithRawResponse: + _client: AsyncGroq + def __init__(self, client: AsyncGroq) -> None: - self.chat = chat.AsyncChatWithRawResponse(client.chat) - self.embeddings = embeddings.AsyncEmbeddingsWithRawResponse(client.embeddings) - self.audio = audio.AsyncAudioWithRawResponse(client.audio) - self.models = models.AsyncModelsWithRawResponse(client.models) - self.batches = batches.AsyncBatchesWithRawResponse(client.batches) - self.files = files.AsyncFilesWithRawResponse(client.files) + self._client = client + + @cached_property + def chat(self) -> chat.AsyncChatWithRawResponse: + from .resources.chat import AsyncChatWithRawResponse + + return AsyncChatWithRawResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.AsyncEmbeddingsWithRawResponse: + from .resources.embeddings import AsyncEmbeddingsWithRawResponse + + return AsyncEmbeddingsWithRawResponse(self._client.embeddings) + + @cached_property + def audio(self) -> audio.AsyncAudioWithRawResponse: + from .resources.audio import AsyncAudioWithRawResponse + + return AsyncAudioWithRawResponse(self._client.audio) + + @cached_property + def models(self) -> models.AsyncModelsWithRawResponse: + from .resources.models import AsyncModelsWithRawResponse + + return AsyncModelsWithRawResponse(self._client.models) + + @cached_property + def batches(self) -> batches.AsyncBatchesWithRawResponse: + from .resources.batches import AsyncBatchesWithRawResponse + + return AsyncBatchesWithRawResponse(self._client.batches) + + @cached_property + def files(self) -> files.AsyncFilesWithRawResponse: + from .resources.files import AsyncFilesWithRawResponse + + return AsyncFilesWithRawResponse(self._client.files) class GroqWithStreamedResponse: + _client: Groq + def __init__(self, client: Groq) -> None: - self.chat = chat.ChatWithStreamingResponse(client.chat) - self.embeddings = embeddings.EmbeddingsWithStreamingResponse(client.embeddings) - self.audio = audio.AudioWithStreamingResponse(client.audio) - self.models = models.ModelsWithStreamingResponse(client.models) - self.batches = batches.BatchesWithStreamingResponse(client.batches) - self.files = files.FilesWithStreamingResponse(client.files) + self._client = client + + @cached_property + def chat(self) -> chat.ChatWithStreamingResponse: + from .resources.chat import ChatWithStreamingResponse + + return ChatWithStreamingResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.EmbeddingsWithStreamingResponse: + from .resources.embeddings import EmbeddingsWithStreamingResponse + + return EmbeddingsWithStreamingResponse(self._client.embeddings) + + @cached_property + def audio(self) -> audio.AudioWithStreamingResponse: + from .resources.audio import AudioWithStreamingResponse + + return AudioWithStreamingResponse(self._client.audio) + + @cached_property + def models(self) -> models.ModelsWithStreamingResponse: + from .resources.models import ModelsWithStreamingResponse + + return ModelsWithStreamingResponse(self._client.models) + + @cached_property + def batches(self) -> batches.BatchesWithStreamingResponse: + from .resources.batches import BatchesWithStreamingResponse + + return BatchesWithStreamingResponse(self._client.batches) + + @cached_property + def files(self) -> files.FilesWithStreamingResponse: + from .resources.files import FilesWithStreamingResponse + + return FilesWithStreamingResponse(self._client.files) class AsyncGroqWithStreamedResponse: + _client: AsyncGroq + def __init__(self, client: AsyncGroq) -> None: - self.chat = chat.AsyncChatWithStreamingResponse(client.chat) - self.embeddings = embeddings.AsyncEmbeddingsWithStreamingResponse(client.embeddings) - self.audio = audio.AsyncAudioWithStreamingResponse(client.audio) - self.models = models.AsyncModelsWithStreamingResponse(client.models) - self.batches = batches.AsyncBatchesWithStreamingResponse(client.batches) - self.files = files.AsyncFilesWithStreamingResponse(client.files) + self._client = client + + @cached_property + def chat(self) -> chat.AsyncChatWithStreamingResponse: + from .resources.chat import AsyncChatWithStreamingResponse + + return AsyncChatWithStreamingResponse(self._client.chat) + + @cached_property + def embeddings(self) -> embeddings.AsyncEmbeddingsWithStreamingResponse: + from .resources.embeddings import AsyncEmbeddingsWithStreamingResponse + + return AsyncEmbeddingsWithStreamingResponse(self._client.embeddings) + + @cached_property + def audio(self) -> audio.AsyncAudioWithStreamingResponse: + from .resources.audio import AsyncAudioWithStreamingResponse + + return AsyncAudioWithStreamingResponse(self._client.audio) + + @cached_property + def models(self) -> models.AsyncModelsWithStreamingResponse: + from .resources.models import AsyncModelsWithStreamingResponse + + return AsyncModelsWithStreamingResponse(self._client.models) + + @cached_property + def batches(self) -> batches.AsyncBatchesWithStreamingResponse: + from .resources.batches import AsyncBatchesWithStreamingResponse + + return AsyncBatchesWithStreamingResponse(self._client.batches) + + @cached_property + def files(self) -> files.AsyncFilesWithStreamingResponse: + from .resources.files import AsyncFilesWithStreamingResponse + + return AsyncFilesWithStreamingResponse(self._client.files) Client = Groq diff --git a/src/groq/_utils/_proxy.py b/src/groq/_utils/_proxy.py index ffd883e9..0f239a33 100644 --- a/src/groq/_utils/_proxy.py +++ b/src/groq/_utils/_proxy.py @@ -46,7 +46,10 @@ def __dir__(self) -> Iterable[str]: @property # type: ignore @override def __class__(self) -> type: # pyright: ignore - proxied = self.__get_proxied__() + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) if issubclass(type(proxied), LazyProxy): return type(proxied) return proxied.__class__ diff --git a/src/groq/_utils/_resources_proxy.py b/src/groq/_utils/_resources_proxy.py new file mode 100644 index 00000000..4c4c8766 --- /dev/null +++ b/src/groq/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `groq.resources` module. + + This is used so that we can lazily import `groq.resources` only when + needed *and* so that users can just import `groq` and reference `groq.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("groq.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() diff --git a/src/groq/_version.py b/src/groq/_version.py index 28652de7..a9df67c7 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.24.0" # x-release-please-version +__version__ = "0.25.0" # x-release-please-version diff --git a/src/groq/resources/chat/completions.py b/src/groq/resources/chat/completions.py index 42576ea0..f6f69d67 100644 --- a/src/groq/resources/chat/completions.py +++ b/src/groq/resources/chat/completions.py @@ -202,6 +202,7 @@ def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, reasoning_format: Optional[Literal["hidden", "raw", "parsed"]] | NotGiven = NOT_GIVEN, response_format: Optional[completion_create_params.ResponseFormat] | NotGiven = NOT_GIVEN, + search_settings: Optional[completion_create_params.SearchSettings] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "on_demand", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -229,8 +230,8 @@ def create( model: ID of the model to use. For details on which models are compatible with the Chat API, see available [models](https://console.groq.com/docs/models) - exclude_domains: A list of domains to exclude from the search results when the model uses a web - search tool. + exclude_domains: Deprecated: Use search_settings.exclude_domains instead. A list of domains to + exclude from the search results when the model uses a web search tool. frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to @@ -251,8 +252,8 @@ def create( A list of functions the model may generate JSON inputs for. - include_domains: A list of domains to include in the search results when the model uses a web - search tool. + include_domains: Deprecated: Use search_settings.include_domains instead. A list of domains to + include in the search results when the model uses a web search tool. logit_bias: This is not yet supported by any of our models. Modify the likelihood of specified tokens appearing in the completion. @@ -283,13 +284,15 @@ def create( reasoning_format: Specifies how to output reasoning tokens - response_format: An object specifying the format that the model must output. + response_format: An object specifying the format that the model must output. Setting to + `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs + which ensures the model will match your supplied JSON schema. json_schema + response format is only supported on llama 4 models. Setting to + `{ "type": "json_object" }` enables the older JSON mode, which ensures the + message the model generates is valid JSON. Using `json_schema` is preferred for + models that support it. - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. + search_settings: Settings for web search functionality when the model uses a web search tool. seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -373,6 +376,7 @@ def create( "presence_penalty": presence_penalty, "reasoning_format": reasoning_format, "response_format": response_format, + "search_settings": search_settings, "seed": seed, "service_tier": service_tier, "stop": stop, @@ -569,6 +573,7 @@ async def create( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, reasoning_format: Optional[Literal["hidden", "raw", "parsed"]] | NotGiven = NOT_GIVEN, response_format: Optional[completion_create_params.ResponseFormat] | NotGiven = NOT_GIVEN, + search_settings: Optional[completion_create_params.SearchSettings] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "on_demand", "flex"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -596,8 +601,8 @@ async def create( model: ID of the model to use. For details on which models are compatible with the Chat API, see available [models](https://console.groq.com/docs/models) - exclude_domains: A list of domains to exclude from the search results when the model uses a web - search tool. + exclude_domains: Deprecated: Use search_settings.exclude_domains instead. A list of domains to + exclude from the search results when the model uses a web search tool. frequency_penalty: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to @@ -618,8 +623,8 @@ async def create( A list of functions the model may generate JSON inputs for. - include_domains: A list of domains to include in the search results when the model uses a web - search tool. + include_domains: Deprecated: Use search_settings.include_domains instead. A list of domains to + include in the search results when the model uses a web search tool. logit_bias: This is not yet supported by any of our models. Modify the likelihood of specified tokens appearing in the completion. @@ -650,13 +655,15 @@ async def create( reasoning_format: Specifies how to output reasoning tokens - response_format: An object specifying the format that the model must output. - - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the - message the model generates is valid JSON. + response_format: An object specifying the format that the model must output. Setting to + `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs + which ensures the model will match your supplied JSON schema. json_schema + response format is only supported on llama 4 models. Setting to + `{ "type": "json_object" }` enables the older JSON mode, which ensures the + message the model generates is valid JSON. Using `json_schema` is preferred for + models that support it. - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. + search_settings: Settings for web search functionality when the model uses a web search tool. seed: If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return @@ -740,6 +747,7 @@ async def create( "presence_penalty": presence_penalty, "reasoning_format": reasoning_format, "response_format": response_format, + "search_settings": search_settings, "seed": seed, "service_tier": service_tier, "stop": stop, diff --git a/src/groq/types/chat/chat_completion_message.py b/src/groq/types/chat/chat_completion_message.py index 4212e1d5..fa1c55fb 100644 --- a/src/groq/types/chat/chat_completion_message.py +++ b/src/groq/types/chat/chat_completion_message.py @@ -6,7 +6,35 @@ from ..._models import BaseModel from .chat_completion_message_tool_call import ChatCompletionMessageToolCall -__all__ = ["ChatCompletionMessage", "ExecutedTool", "FunctionCall"] +__all__ = [ + "ChatCompletionMessage", + "ExecutedTool", + "ExecutedToolSearchResults", + "ExecutedToolSearchResultsResult", + "FunctionCall", +] + + +class ExecutedToolSearchResultsResult(BaseModel): + content: Optional[str] = None + """The content of the search result""" + + score: Optional[float] = None + """The relevance score of the search result""" + + title: Optional[str] = None + """The title of the search result""" + + url: Optional[str] = None + """The URL of the search result""" + + +class ExecutedToolSearchResults(BaseModel): + images: Optional[List[str]] = None + """List of image URLs returned by the search""" + + results: Optional[List[ExecutedToolSearchResultsResult]] = None + """List of search results""" class ExecutedTool(BaseModel): @@ -22,6 +50,9 @@ class ExecutedTool(BaseModel): output: Optional[str] = None """The output returned by the tool.""" + search_results: Optional[ExecutedToolSearchResults] = None + """The search results returned by the tool, if applicable.""" + class FunctionCall(BaseModel): arguments: str diff --git a/src/groq/types/chat/completion_create_params.py b/src/groq/types/chat/completion_create_params.py index 428fb5c4..c5638589 100644 --- a/src/groq/types/chat/completion_create_params.py +++ b/src/groq/types/chat/completion_create_params.py @@ -11,7 +11,17 @@ from .chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam from .chat_completion_function_call_option_param import ChatCompletionFunctionCallOptionParam -__all__ = ["CompletionCreateParams", "FunctionCall", "Function", "ResponseFormat"] +__all__ = [ + "CompletionCreateParams", + "FunctionCall", + "Function", + "ResponseFormat", + "ResponseFormatResponseFormatText", + "ResponseFormatResponseFormatJsonSchema", + "ResponseFormatResponseFormatJsonSchemaJsonSchema", + "ResponseFormatResponseFormatJsonObject", + "SearchSettings", +] class CompletionCreateParams(TypedDict, total=False): @@ -39,8 +49,8 @@ class CompletionCreateParams(TypedDict, total=False): exclude_domains: Optional[List[str]] """ - A list of domains to exclude from the search results when the model uses a web - search tool. + Deprecated: Use search_settings.exclude_domains instead. A list of domains to + exclude from the search results when the model uses a web search tool. """ frequency_penalty: Optional[float] @@ -71,8 +81,8 @@ class CompletionCreateParams(TypedDict, total=False): include_domains: Optional[List[str]] """ - A list of domains to include in the search results when the model uses a web - search tool. + Deprecated: Use search_settings.include_domains instead. A list of domains to + include in the search results when the model uses a web search tool. """ logit_bias: Optional[Dict[str, int]] @@ -128,13 +138,17 @@ class CompletionCreateParams(TypedDict, total=False): response_format: Optional[ResponseFormat] """An object specifying the format that the model must output. - Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the - message the model generates is valid JSON. - - **Important:** when using JSON mode, you **must** also instruct the model to - produce JSON yourself via a system or user message. + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured + Outputs which ensures the model will match your supplied JSON schema. + json_schema response format is only supported on llama 4 models. Setting to + `{ "type": "json_object" }` enables the older JSON mode, which ensures the + message the model generates is valid JSON. Using `json_schema` is preferred for + models that support it. """ + search_settings: Optional[SearchSettings] + """Settings for web search functionality when the model uses a web search tool.""" + seed: Optional[int] """ If specified, our system will make a best effort to sample deterministically, @@ -249,6 +263,65 @@ class Function(TypedDict, total=False): """ -class ResponseFormat(TypedDict, total=False): - type: Literal["text", "json_object"] - """Must be one of `text` or `json_object`.""" +class ResponseFormatResponseFormatText(TypedDict, total=False): + type: Required[Literal["text"]] + """The type of response format being defined. Always `text`.""" + + +class ResponseFormatResponseFormatJsonSchemaJsonSchema(TypedDict, total=False): + name: Required[str] + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + description: str + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + schema: Dict[str, object] + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + strict: Optional[bool] + """Whether to enable strict schema adherence when generating the output. + + If set to true, the model will always follow the exact schema defined in the + `schema` field. Only a subset of JSON Schema is supported when `strict` is + `true`. To learn more, read the + [Structured Outputs guide](/docs/guides/structured-outputs). + """ + + +class ResponseFormatResponseFormatJsonSchema(TypedDict, total=False): + json_schema: Required[ResponseFormatResponseFormatJsonSchemaJsonSchema] + """Structured Outputs configuration options, including a JSON Schema.""" + + type: Required[Literal["json_schema"]] + """The type of response format being defined. Always `json_schema`.""" + + +class ResponseFormatResponseFormatJsonObject(TypedDict, total=False): + type: Required[Literal["json_object"]] + """The type of response format being defined. Always `json_object`.""" + + +ResponseFormat: TypeAlias = Union[ + ResponseFormatResponseFormatText, ResponseFormatResponseFormatJsonSchema, ResponseFormatResponseFormatJsonObject +] + + +class SearchSettings(TypedDict, total=False): + exclude_domains: Optional[List[str]] + """A list of domains to exclude from the search results.""" + + include_domains: Optional[List[str]] + """A list of domains to include in the search results.""" + + include_images: Optional[bool] + """Whether to include images in the search results.""" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 0f65ce0b..5b3b03d0 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -61,7 +61,12 @@ def test_method_create_with_all_params(self, client: Groq) -> None: parallel_tool_calls=True, presence_penalty=-2, reasoning_format="hidden", - response_format={"type": "json_object"}, + response_format={"type": "text"}, + search_settings={ + "exclude_domains": ["string"], + "include_domains": ["string"], + "include_images": True, + }, seed=0, service_tier="auto", stop="\n", @@ -169,7 +174,12 @@ async def test_method_create_with_all_params(self, async_client: AsyncGroq) -> N parallel_tool_calls=True, presence_penalty=-2, reasoning_format="hidden", - response_format={"type": "json_object"}, + response_format={"type": "text"}, + search_settings={ + "exclude_domains": ["string"], + "include_domains": ["string"], + "include_images": True, + }, seed=0, service_tier="auto", stop="\n", diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index 2fa9c4a9..4c48c14b 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -21,3 +21,14 @@ def test_recursive_proxy() -> None: assert dir(proxy) == [] assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" + + +def test_isinstance_does_not_error() -> None: + class AlwaysErrorProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + raise RuntimeError("Mocking missing dependency") + + proxy = AlwaysErrorProxy() + assert not isinstance(proxy, dict) + assert isinstance(proxy, LazyProxy)