Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 66f0cfc

Browse files
author
Stainless Bot
committedSep 25, 2024
feat(client): allow overriding retry count header (#1745)
1 parent b912108 commit 66f0cfc

File tree

2 files changed

+134
-2
lines changed

2 files changed

+134
-2
lines changed
 

‎src/openai/_base_client.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,10 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0
413413
if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
414414
headers[idempotency_header] = options.idempotency_key or self._idempotency_key()
415415

416-
if retries_taken > 0:
417-
headers.setdefault("x-stainless-retry-count", str(retries_taken))
416+
# Don't set the retry count header if it was already set or removed by the caller. We check
417+
# `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case.
418+
if "x-stainless-retry-count" not in (header.lower() for header in custom_headers):
419+
headers["x-stainless-retry-count"] = str(retries_taken)
418420

419421
return headers
420422

‎tests/test_client.py

+130
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,70 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
793793
else:
794794
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
795795

796+
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
797+
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
798+
@pytest.mark.respx(base_url=base_url)
799+
def test_omit_retry_count_header(
800+
self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter
801+
) -> None:
802+
client = client.with_options(max_retries=4)
803+
804+
nb_retries = 0
805+
806+
def retry_handler(_request: httpx.Request) -> httpx.Response:
807+
nonlocal nb_retries
808+
if nb_retries < failures_before_success:
809+
nb_retries += 1
810+
return httpx.Response(500)
811+
return httpx.Response(200)
812+
813+
respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
814+
815+
response = client.chat.completions.with_raw_response.create(
816+
messages=[
817+
{
818+
"content": "string",
819+
"role": "system",
820+
}
821+
],
822+
model="gpt-4o",
823+
extra_headers={"x-stainless-retry-count": Omit()},
824+
)
825+
826+
assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0
827+
828+
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
829+
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
830+
@pytest.mark.respx(base_url=base_url)
831+
def test_overwrite_retry_count_header(
832+
self, client: OpenAI, failures_before_success: int, respx_mock: MockRouter
833+
) -> None:
834+
client = client.with_options(max_retries=4)
835+
836+
nb_retries = 0
837+
838+
def retry_handler(_request: httpx.Request) -> httpx.Response:
839+
nonlocal nb_retries
840+
if nb_retries < failures_before_success:
841+
nb_retries += 1
842+
return httpx.Response(500)
843+
return httpx.Response(200)
844+
845+
respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
846+
847+
response = client.chat.completions.with_raw_response.create(
848+
messages=[
849+
{
850+
"content": "string",
851+
"role": "system",
852+
}
853+
],
854+
model="gpt-4o",
855+
extra_headers={"x-stainless-retry-count": "42"},
856+
)
857+
858+
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
859+
796860
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
797861
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
798862
@pytest.mark.respx(base_url=base_url)
@@ -1595,6 +1659,72 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
15951659
else:
15961660
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
15971661

1662+
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
1663+
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
1664+
@pytest.mark.respx(base_url=base_url)
1665+
@pytest.mark.asyncio
1666+
async def test_omit_retry_count_header(
1667+
self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter
1668+
) -> None:
1669+
client = async_client.with_options(max_retries=4)
1670+
1671+
nb_retries = 0
1672+
1673+
def retry_handler(_request: httpx.Request) -> httpx.Response:
1674+
nonlocal nb_retries
1675+
if nb_retries < failures_before_success:
1676+
nb_retries += 1
1677+
return httpx.Response(500)
1678+
return httpx.Response(200)
1679+
1680+
respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
1681+
1682+
response = await client.chat.completions.with_raw_response.create(
1683+
messages=[
1684+
{
1685+
"content": "string",
1686+
"role": "system",
1687+
}
1688+
],
1689+
model="gpt-4o",
1690+
extra_headers={"x-stainless-retry-count": Omit()},
1691+
)
1692+
1693+
assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0
1694+
1695+
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
1696+
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
1697+
@pytest.mark.respx(base_url=base_url)
1698+
@pytest.mark.asyncio
1699+
async def test_overwrite_retry_count_header(
1700+
self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter
1701+
) -> None:
1702+
client = async_client.with_options(max_retries=4)
1703+
1704+
nb_retries = 0
1705+
1706+
def retry_handler(_request: httpx.Request) -> httpx.Response:
1707+
nonlocal nb_retries
1708+
if nb_retries < failures_before_success:
1709+
nb_retries += 1
1710+
return httpx.Response(500)
1711+
return httpx.Response(200)
1712+
1713+
respx_mock.post("/chat/completions").mock(side_effect=retry_handler)
1714+
1715+
response = await client.chat.completions.with_raw_response.create(
1716+
messages=[
1717+
{
1718+
"content": "string",
1719+
"role": "system",
1720+
}
1721+
],
1722+
model="gpt-4o",
1723+
extra_headers={"x-stainless-retry-count": "42"},
1724+
)
1725+
1726+
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
1727+
15981728
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
15991729
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
16001730
@pytest.mark.respx(base_url=base_url)

0 commit comments

Comments
 (0)
Please sign in to comment.