diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0e5b256..c658eef 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.3.1" + ".": "1.3.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eb7dfd..060c866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.3.2 (2024-09-25) + +Full Changelog: [v1.3.1...v1.3.2](https://github.com/julep-ai/python-sdk/compare/v1.3.1...v1.3.2) + +### Chores + +* **internal:** codegen related update ([#27](https://github.com/julep-ai/python-sdk/issues/27)) ([cb98d42](https://github.com/julep-ai/python-sdk/commit/cb98d423b87043de48ca3e845f107b797a69824b)) +* **internal:** use `typing_extensions.overload` instead of `typing` ([#25](https://github.com/julep-ai/python-sdk/issues/25)) ([3c50f29](https://github.com/julep-ai/python-sdk/commit/3c50f297ee48e3a7bc284551a1cd12905bc7ae29)) + ## 1.3.1 (2024-09-24) Full Changelog: [v1.3.0...v1.3.1](https://github.com/julep-ai/python-sdk/compare/v1.3.0...v1.3.1) diff --git a/pyproject.toml b/pyproject.toml index 267927b..010258c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "julep" -version = "1.3.1" +version = "1.3.2" description = "The official Python library for the julep API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/julep/_base_client.py b/src/julep/_base_client.py index 77e56ea..8239530 100644 --- a/src/julep/_base_client.py +++ b/src/julep/_base_client.py @@ -412,7 +412,10 @@ 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() - headers.setdefault("x-stainless-retry-count", str(retries_taken)) + # Don't set the retry count header if it was 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): + headers["x-stainless-retry-count"] = str(retries_taken) return headers diff --git a/src/julep/_version.py b/src/julep/_version.py index 80f4fce..951fd08 100644 --- a/src/julep/_version.py +++ b/src/julep/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "julep" -__version__ = "1.3.1" # x-release-please-version +__version__ = "1.3.2" # x-release-please-version diff --git a/src/julep/resources/agents/docs.py b/src/julep/resources/agents/docs.py index bdc58c5..131db18 100644 --- a/src/julep/resources/agents/docs.py +++ b/src/julep/resources/agents/docs.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional, overload -from typing_extensions import Literal +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, overload import httpx diff --git a/src/julep/resources/executions/executions.py b/src/julep/resources/executions/executions.py index bada6ca..0cb5cc5 100644 --- a/src/julep/resources/executions/executions.py +++ b/src/julep/resources/executions/executions.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import Optional, overload -from typing_extensions import Literal +from typing import Optional +from typing_extensions import Literal, overload import httpx diff --git a/src/julep/resources/users/docs.py b/src/julep/resources/users/docs.py index cb90bf9..ecc7997 100644 --- a/src/julep/resources/users/docs.py +++ b/src/julep/resources/users/docs.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional, overload -from typing_extensions import Literal +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, overload import httpx diff --git a/tests/test_client.py b/tests/test_client.py index 0d0c061..01ef01f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -758,6 +758,54 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("julep._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_omit_retry_count_header(self, client: Julep, failures_before_success: int, respx_mock: MockRouter) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/agents/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").mock(side_effect=retry_handler) + + response = client.agents.with_raw_response.create_or_update( + agent_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", extra_headers={"x-stainless-retry-count": Omit()} + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("julep._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_overwrite_retry_count_header( + self, client: Julep, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/agents/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").mock(side_effect=retry_handler) + + response = client.agents.with_raw_response.create_or_update( + agent_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", extra_headers={"x-stainless-retry-count": "42"} + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + class TestAsyncJulep: client = AsyncJulep(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -1492,3 +1540,55 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("julep._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_omit_retry_count_header( + self, async_client: AsyncJulep, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/agents/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").mock(side_effect=retry_handler) + + response = await client.agents.with_raw_response.create_or_update( + agent_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", extra_headers={"x-stainless-retry-count": Omit()} + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("julep._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_overwrite_retry_count_header( + self, async_client: AsyncJulep, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/agents/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").mock(side_effect=retry_handler) + + response = await client.agents.with_raw_response.create_or_update( + agent_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", extra_headers={"x-stainless-retry-count": "42"} + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42"