Skip to content

Commit 4b680b4

Browse files
sdk(python): add support for client_request_id -> X-Client-Request-Id
1 parent 6574bcd commit 4b680b4

File tree

7 files changed

+54
-2
lines changed

7 files changed

+54
-2
lines changed

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,12 @@ print(completion)
678678

679679
These methods return a [`LegacyAPIResponse`](https://github.com/openai/openai-python/tree/main/src/openai/_legacy_response.py) object. This is a legacy class as we're changing it slightly in the next major version.
680680

681+
Tag your own requests for easier support follow-up:
682+
683+
```py
684+
client.responses.create(..., extra_headers={"X-Client-Request-Id": "123e4567-e89b-12d3-a456-426614174000"})
685+
```
686+
681687
For the sync client this will mostly be the same with the exception
682688
of `content` & `text` will be methods instead of properties. In the
683689
async client, all methods will be async.

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "openai"
3-
version = "2.7.1"
3+
version = "2.7.2"
44
description = "The official Python library for the openai API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

‎src/openai/_base_client.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,22 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0
438438
# headers are case-insensitive while dictionaries are not.
439439
headers=httpx.Headers(headers_dict)
440440

441+
option_client_request_id=options.client_request_id
442+
ifoption_client_request_id:
443+
headers["X-Client-Request-Id"] =option_client_request_id
444+
else:
445+
client_request_id_header=None
446+
forkey, valueincustom_headers.items():
447+
ifkey.lower() !="x-client-request-id":
448+
continue
449+
ifisinstance(value, Omit) orvaluein (None, ""):
450+
break
451+
client_request_id_header=str(value)
452+
break
453+
454+
ifclient_request_id_header:
455+
headers["X-Client-Request-Id"] =client_request_id_header
456+
441457
idempotency_header=self._idempotency_header
442458
ifidempotency_headerandoptions.idempotency_keyandidempotency_headernotinheaders:
443459
headers[idempotency_header] =options.idempotency_key
@@ -1850,6 +1866,7 @@ def make_request_options(
18501866
extra_query: Query|None=None,
18511867
extra_body: Body|None=None,
18521868
idempotency_key: str|None=None,
1869+
client_request_id: str|None=None,
18531870
timeout: float|httpx.Timeout|None|NotGiven=not_given,
18541871
post_parser: PostParser|NotGiven=not_given,
18551872
) ->RequestOptions:
@@ -1873,6 +1890,9 @@ def make_request_options(
18731890
ifidempotency_keyisnotNone:
18741891
options["idempotency_key"] =idempotency_key
18751892

1893+
ifclient_request_id:
1894+
options["client_request_id"] =client_request_id
1895+
18761896
ifis_given(post_parser):
18771897
# internal
18781898
options["post_parser"] =post_parser# type: ignore

‎src/openai/_models.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
805805
timeout: float|Timeout|None
806806
files: HttpxRequestFiles|None
807807
idempotency_key: str
808+
client_request_id: str|None
808809
json_data: Body
809810
extra_json: AnyMapping
810811
follow_redirects: bool
@@ -820,6 +821,7 @@ class FinalRequestOptions(pydantic.BaseModel):
820821
timeout: Union[float, Timeout, None, NotGiven] =NotGiven()
821822
files: Union[HttpxRequestFiles, None] =None
822823
idempotency_key: Union[str, None] =None
824+
client_request_id: Union[str, None] =None
823825
post_parser: Union[Callable[[Any], Any], NotGiven] =NotGiven()
824826
follow_redirects: Union[bool, None] =None
825827

‎src/openai/_types.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class RequestOptions(TypedDict, total=False):
112112
params: Query
113113
extra_json: AnyMapping
114114
idempotency_key: str
115+
client_request_id: str|None
115116
follow_redirects: bool
116117

117118

‎src/openai/_version.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__="openai"
4-
__version__="2.7.1"# x-release-please-version
4+
__version__="2.7.2"# x-release-please-version

‎tests/test_client.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,29 @@ def test_default_headers_option(self) -> None:
349349
test_client.close()
350350
test_client2.close()
351351

352+
deftest_client_request_id_header_from_option(self, client: OpenAI) ->None:
353+
request=client._build_request(
354+
FinalRequestOptions(method="get", url="/foo", client_request_id="custom-option-id")
355+
)
356+
357+
assertrequest.headers.get("X-Client-Request-Id") =="custom-option-id"
358+
359+
deftest_client_request_id_header_from_custom_headers(self, client: OpenAI) ->None:
360+
request=client._build_request(
361+
FinalRequestOptions(
362+
method="get",
363+
url="/foo",
364+
headers={"x-client-request-id": "custom-header-id"},
365+
)
366+
)
367+
368+
assertrequest.headers.get("X-Client-Request-Id") =="custom-header-id"
369+
370+
deftest_client_request_id_header_absent_when_unspecified(self, client: OpenAI) ->None:
371+
request=client._build_request(FinalRequestOptions(method="get", url="/foo"))
372+
373+
assertrequest.headers.get("X-Client-Request-Id") isNone
374+
352375
deftest_validate_headers(self) ->None:
353376
client=OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
354377
options=client._prepare_options(FinalRequestOptions(method="get", url="/foo"))

0 commit comments

Comments
(0)