diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5d5a838..fb80559e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,14 +61,18 @@ jobs: run: rye build - name: Get GitHub OIDC Token - if: github.repository == 'stainless-sdks/groqcloud-python' + if: |- + github.repository == 'stainless-sdks/groqcloud-python' && + !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball - if: github.repository == 'stainless-sdks/groqcloud-python' + if: |- + github.repository == 'stainless-sdks/groqcloud-python' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2601677b..b55c11f0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.1.0" + ".": "1.1.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ae19afde..51437fd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.1.1 (2026-03-11) + +Full Changelog: [v1.1.0...v1.1.1](https://github.com/groq/groq-python/compare/v1.1.0...v1.1.1) + +### Chores + +* **ci:** skip uploading artifacts on stainless-internal branches ([1995ebc](https://github.com/groq/groq-python/commit/1995ebc477fe434a449cbcef01264a80f020238c)) +* **docs:** remove www prefix ([2f429b1](https://github.com/groq/groq-python/commit/2f429b1a1a1041eba1ffafc3e80178f4480a358b)) +* format all `api.md` files ([b7a0488](https://github.com/groq/groq-python/commit/b7a0488d9825f071b86bf64be0d25bc997044c77)) +* **internal:** add request options to SSE classes ([7ffeb7c](https://github.com/groq/groq-python/commit/7ffeb7c99593bf014142d8b265771000535ba265)) +* **internal:** bump dependencies ([862b60e](https://github.com/groq/groq-python/commit/862b60e54157dc874dae9ee8356531a38bca4054)) +* **internal:** codegen related update ([f65774d](https://github.com/groq/groq-python/commit/f65774da349ab03b0fbb96391ac11860142d9ba0)) +* **internal:** fix lint error on Python 3.14 ([759089a](https://github.com/groq/groq-python/commit/759089a9c9fc1e7a6f087e350eec082ac98c5e7a)) +* **internal:** make `test_proxy_environment_variables` more resilient ([2421ef6](https://github.com/groq/groq-python/commit/2421ef6992b546d8c9afe1d43e71f882c7c8bcbc)) +* **internal:** make `test_proxy_environment_variables` more resilient to env ([fd9609d](https://github.com/groq/groq-python/commit/fd9609dfaf976665b07513bbbd22c6aa7f561d85)) +* **test:** do not count install time for mock server timeout ([c242fb0](https://github.com/groq/groq-python/commit/c242fb04f36f70d690760e4fb1e77ac451833d94)) +* update mock server docs ([6c8a563](https://github.com/groq/groq-python/commit/6c8a563e1babd4dbc92a2328eeda825ce8b09e51)) +* update placeholder string ([fb77938](https://github.com/groq/groq-python/commit/fb779388836e873cd3dba70ab12a2d42afbcade7)) + ## 1.1.0 (2026-03-08) Full Changelog: [v1.0.0...v1.1.0](https://github.com/groq/groq-python/compare/v1.0.0...v1.1.0) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 80751fa5..03129292 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,7 +62,7 @@ If you’d like to use the repository from source, you can either install from g To install via git: ```sh -$ pip install git+ssh://git@github.com/groq/groq-python#main.git +$ pip install git+ssh://git@github.com/groq/groq-python.git ``` Alternatively, you can build from source and install the wheel file: @@ -88,8 +88,7 @@ $ pip install ./path-to-wheel-file.whl Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. ```sh -# you will need npm installed -$ npx prism mock path/to/your/openapi.yml +$ ./scripts/mock ``` ```sh diff --git a/pyproject.toml b/pyproject.toml index dbcf6a9d..16f04750 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "groq" -version = "1.1.0" +version = "1.1.1" description = "The official Python library for the groq API" dynamic = ["readme"] license = "Apache-2.0" @@ -68,7 +68,7 @@ format = { chain = [ # run formatting again to fix any inconsistencies when imports are stripped "format:ruff", ]} -"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" +"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'" "format:ruff" = "ruff format" "lint" = { chain = [ diff --git a/requirements-dev.lock b/requirements-dev.lock index a09a93cc..19201a09 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,14 +12,14 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.2 +aiohttp==3.13.3 # via groq # via httpx-aiohttp aiosignal==1.4.0 # via aiohttp annotated-types==0.7.0 # via pydantic -anyio==4.12.0 +anyio==4.12.1 # via groq # via httpx argcomplete==3.6.3 @@ -31,7 +31,7 @@ attrs==25.4.0 # via nox backports-asyncio-runner==1.2.0 # via pytest-asyncio -certifi==2025.11.12 +certifi==2026.1.4 # via httpcore # via httpx colorlog==6.10.1 @@ -61,7 +61,7 @@ httpx==0.28.1 # via groq # via httpx-aiohttp # via respx -httpx-aiohttp==0.1.9 +httpx-aiohttp==0.1.12 # via groq humanize==4.13.0 # via nox @@ -69,7 +69,7 @@ idna==3.11 # via anyio # via httpx # via yarl -importlib-metadata==8.7.0 +importlib-metadata==8.7.1 iniconfig==2.1.0 # via pytest markdown-it-py==3.0.0 @@ -82,14 +82,14 @@ multidict==6.7.0 mypy==1.17.0 mypy-extensions==1.1.0 # via mypy -nodeenv==1.9.1 +nodeenv==1.10.0 # via pyright nox==2025.11.12 packaging==25.0 # via dependency-groups # via nox # via pytest -pathspec==0.12.1 +pathspec==1.0.3 # via mypy platformdirs==4.4.0 # via virtualenv @@ -115,13 +115,13 @@ python-dateutil==2.9.0.post0 # via time-machine respx==0.22.0 rich==14.2.0 -ruff==0.14.7 +ruff==0.14.13 six==1.17.0 # via python-dateutil sniffio==1.3.1 # via groq time-machine==2.19.0 -tomli==2.3.0 +tomli==2.4.0 # via dependency-groups # via mypy # via nox @@ -141,7 +141,7 @@ typing-extensions==4.15.0 # via virtualenv typing-inspection==0.4.2 # via pydantic -virtualenv==20.35.4 +virtualenv==20.36.1 # via nox yarl==1.22.0 # via aiohttp diff --git a/requirements.lock b/requirements.lock index 9c630f3c..04d57c29 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,21 +12,21 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.2 +aiohttp==3.13.3 # via groq # via httpx-aiohttp aiosignal==1.4.0 # via aiohttp annotated-types==0.7.0 # via pydantic -anyio==4.12.0 +anyio==4.12.1 # via groq # via httpx async-timeout==5.0.1 # via aiohttp attrs==25.4.0 # via aiohttp -certifi==2025.11.12 +certifi==2026.1.4 # via httpcore # via httpx distro==1.9.0 @@ -43,7 +43,7 @@ httpcore==1.0.9 httpx==0.28.1 # via groq # via httpx-aiohttp -httpx-aiohttp==0.1.9 +httpx-aiohttp==0.1.12 # via groq idna==3.11 # via anyio diff --git a/scripts/mock b/scripts/mock index 0b28f6ea..bcf3b392 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,11 +21,22 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then + # Pre-install the package so the download doesn't eat into the startup timeout + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & - # Wait for server to come online + # Wait for server to come online (max 30s) echo -n "Waiting for server" + attempts=0 while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + attempts=$((attempts + 1)) + if [ "$attempts" -ge 300 ]; then + echo + echo "Timed out waiting for Prism server to start" + cat .prism.log + exit 1 + fi echo -n "." sleep 0.1 done diff --git a/src/groq/_response.py b/src/groq/_response.py index 15e874e6..af34c9f4 100644 --- a/src/groq/_response.py +++ b/src/groq/_response.py @@ -152,6 +152,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -162,6 +163,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -175,6 +177,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/groq/_streaming.py b/src/groq/_streaming.py index 3d4e27af..44c671e3 100644 --- a/src/groq/_streaming.py +++ b/src/groq/_streaming.py @@ -4,7 +4,7 @@ import json import inspect from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx @@ -14,6 +14,7 @@ if TYPE_CHECKING: from ._client import Groq, AsyncGroq + from ._models import FinalRequestOptions _T = TypeVar("_T") @@ -23,7 +24,7 @@ class Stream(Generic[_T]): """Provides the core interface to iterate over a synchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEBytesDecoder def __init__( @@ -32,10 +33,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: Groq, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() @@ -123,7 +126,7 @@ class AsyncStream(Generic[_T]): """Provides the core interface to iterate over an asynchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEDecoder | SSEBytesDecoder def __init__( @@ -132,10 +135,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: AsyncGroq, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() diff --git a/src/groq/_version.py b/src/groq/_version.py index 5392b7b2..8e584166 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__ = "1.1.0" # x-release-please-version +__version__ = "1.1.1" # x-release-please-version diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index ec565f88..1aa62ce3 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -30,7 +30,7 @@ def test_method_create(self, client: Groq) -> None: def test_method_create_with_all_params(self, client: Groq) -> None: transcription = client.audio.transcriptions.create( model="whisper-large-v3-turbo", - file=b"raw file contents", + file=b"Example data", language="string", prompt="prompt", response_format="json", @@ -85,7 +85,7 @@ async def test_method_create(self, async_client: AsyncGroq) -> None: async def test_method_create_with_all_params(self, async_client: AsyncGroq) -> None: transcription = await async_client.audio.transcriptions.create( model="whisper-large-v3-turbo", - file=b"raw file contents", + file=b"Example data", language="string", prompt="prompt", response_format="json", diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index 8faf4bb2..707dcbf4 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -30,7 +30,7 @@ def test_method_create(self, client: Groq) -> None: def test_method_create_with_all_params(self, client: Groq) -> None: translation = client.audio.translations.create( model="whisper-large-v3-turbo", - file=b"raw file contents", + file=b"Example data", prompt="prompt", response_format="json", temperature=0, @@ -83,7 +83,7 @@ async def test_method_create(self, async_client: AsyncGroq) -> None: async def test_method_create_with_all_params(self, async_client: AsyncGroq) -> None: translation = await async_client.audio.translations.create( model="whisper-large-v3-turbo", - file=b"raw file contents", + file=b"Example data", prompt="prompt", response_format="json", temperature=0, diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 342677e2..922eb115 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -28,7 +28,7 @@ class TestFiles: @parametrize def test_method_create(self, client: Groq) -> None: file = client.files.create( - file=b"raw file contents", + file=b"Example data", purpose="batch", ) assert_matches_type(FileCreateResponse, file, path=["response"]) @@ -36,7 +36,7 @@ def test_method_create(self, client: Groq) -> None: @parametrize def test_raw_response_create(self, client: Groq) -> None: response = client.files.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", purpose="batch", ) @@ -48,7 +48,7 @@ def test_raw_response_create(self, client: Groq) -> None: @parametrize def test_streaming_response_create(self, client: Groq) -> None: with client.files.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", purpose="batch", ) as response: assert not response.is_closed @@ -219,7 +219,7 @@ class TestAsyncFiles: @parametrize async def test_method_create(self, async_client: AsyncGroq) -> None: file = await async_client.files.create( - file=b"raw file contents", + file=b"Example data", purpose="batch", ) assert_matches_type(FileCreateResponse, file, path=["response"]) @@ -227,7 +227,7 @@ async def test_method_create(self, async_client: AsyncGroq) -> None: @parametrize async def test_raw_response_create(self, async_client: AsyncGroq) -> None: response = await async_client.files.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", purpose="batch", ) @@ -239,7 +239,7 @@ async def test_raw_response_create(self, async_client: AsyncGroq) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncGroq) -> None: async with async_client.files.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", purpose="batch", ) as response: assert not response.is_closed diff --git a/tests/test_client.py b/tests/test_client.py index 619db892..f4122a75 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -979,6 +979,14 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has any proxy env vars set + monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultHttpxClient() @@ -1913,6 +1921,14 @@ async def test_get_platform(self) -> None: async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has any proxy env vars set + monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultAsyncHttpxClient()