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 9f07d4d

Browse files
Stainless BotRobertCraigie
Stainless Bot
authored andcommittedSep 25, 2024·
feat(client): allow overriding retry count header (#1745)
1 parent b912108 commit 9f07d4d

File tree

2 files changed

+138
-18
lines changed

2 files changed

+138
-18
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

+134-16
Original file line numberDiff line numberDiff line change
@@ -788,10 +788,71 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
788788
)
789789

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

796857
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
797858
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@@ -822,10 +883,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
822883
model="gpt-4o",
823884
) as response:
824885
assert response.retries_taken == failures_before_success
825-
if failures_before_success == 0:
826-
assert "x-stainless-retry-count" not in response.http_request.headers
827-
else:
828-
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
886+
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
829887

830888

831889
class TestAsyncOpenAI:
@@ -1590,10 +1648,73 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
15901648
)
15911649

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

15981719
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
15991720
@mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@@ -1625,7 +1746,4 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
16251746
model="gpt-4o",
16261747
) as response:
16271748
assert response.retries_taken == failures_before_success
1628-
if failures_before_success == 0:
1629-
assert "x-stainless-retry-count" not in response.http_request.headers
1630-
else:
1631-
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
1749+
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success

0 commit comments

Comments
 (0)
Please sign in to comment.