Skip to content

Commit 989c8cd

Browse files
feat(api): support Responses WebSocket warmup and local parsing
Castiron-Internal-PR: openai/openai-python-internal#387 Castiron-Source-SHA: 011966e4e1c8ef5879fda79b8bbde5d79794b760 Castiron-Public-Base-SHA: 68b962c
1 parent 68b962c commit 989c8cd

19 files changed

Lines changed: 503 additions & 41 deletions

‎.castiron.stats.yml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
schema_version: 1
2-
generation_id: e511c488-31b9-4833-baf5-cfb49bbba7ac
3-
openapi_spec_hash: d57efec7c76a156447c9ab739b446dc1
4-
openapi_transformed_spec_hash: b34c897c8cec867ec4ac8e22727c5975
2+
generation_id: 66f75554-d35a-43fb-a349-c03a542189d2
3+
openapi_spec_hash: b006a5de3d55cecef746265fa2d4d7c9
4+
openapi_transformed_spec_hash: d5d0c68e913851f8de7b3e6d2cd9a03b
55
config_hash: 8da38e8d8f706786c2d8586691168746
6-
codegen_sha: e46410d5a9751f068b9afce57ecf25abda75de09
7-
codegen_hash: 33536449fa4898b45941d5fa4b5a958f88e2701a4b85fae21c372b75614198cd
6+
codegen_sha: 36e52c095cd7ec03ea1e65043d0fdd2ec06adca3
7+
codegen_hash: 18c5a7e1999ee5ccbb262fe37132309a27d93ca84f08b834602e5e58f7345e67

‎api_reference/openapi.transformed.yml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68571,6 +68571,12 @@ components:
6857168571
`stream_id` controls routing; `previous_response_id` controls
6857268572
conversation lineage, so a new lane can fork from a response created
6857368573
on another lane.
68574+
generate:
68575+
type: boolean
68576+
description: |
68577+
Whether to generate output for this response. Defaults to true when omitted.
68578+
Set to false to prepare request state without model output; the response
68579+
ID can be used as previous_response_id on a later turn.
6857468580
required:
6857568581
- type
6857668582
- $ref: '#/components/schemas/CreateResponse'
@@ -111494,6 +111500,12 @@ components:
111494111500
`stream_id` controls routing; `previous_response_id` controls
111495111501
conversation lineage, so a new lane can fork from a response created
111496111502
on another lane.
111503+
generate:
111504+
type: boolean
111505+
description: |
111506+
Whether to generate output for this response. Defaults to true when omitted.
111507+
Set to false to prepare request state without model output; the response
111508+
ID can be used as previous_response_id on a later turn.
111497111509
required:
111498111510
- type
111499111511
- $ref: '#/components/schemas/BetaCreateResponse'

‎scripts/castiron/generated-files.json‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@
379379
},
380380
"src/openai/resources/beta/responses/responses.py": {
381381
"mode": "100644",
382-
"sha256": "043ff25a6a7f078bb0b11f6368ee136d7f3bb371c65360c8eb608afaf86de023"
382+
"sha256": "505f230e5616690d534818b4b23db7fea448fa7c9ae2901f8b46bfcc1283b629"
383383
},
384384
"src/openai/resources/beta/threads/__init__.py": {
385385
"mode": "100644",
@@ -559,19 +559,19 @@
559559
},
560560
"src/openai/resources/live/forks.py": {
561561
"mode": "100644",
562-
"sha256": "eda42fc4ed92675d23eb62905f154a5b115567c158dd4b0fe5cf3d0824e2bd9d"
562+
"sha256": "7fe78c144680df54628773683f9dd5b06c5241d0218b4f4c745890ddae225c92"
563563
},
564564
"src/openai/resources/live/live.py": {
565565
"mode": "100644",
566-
"sha256": "a42b9133f64a4421eafbb1d0d10a9cddcefdc872270518f7bdf61ce192265985"
566+
"sha256": "86be3e074d011a750fa52a4d216a7e87ee5c8e82f6ccec00102c03a539199882"
567567
},
568568
"src/openai/resources/live/sessions.py": {
569569
"mode": "100644",
570570
"sha256": "9ba2d59d9c7498c96ac2e7e48660c91468e0eeb5df881e77f123ee37985c3dfd"
571571
},
572572
"src/openai/resources/live/sideband.py": {
573573
"mode": "100644",
574-
"sha256": "7ed377e577ce3b229692bd112059914700741cbff042a7a92cc2a581ec2ac1f8"
574+
"sha256": "d19149c042afcd8c8efca3d7794092acc1c2f76a4bcb4f6f620547314a78e59b"
575575
},
576576
"src/openai/resources/models.py": {
577577
"mode": "100644",
@@ -599,7 +599,7 @@
599599
},
600600
"src/openai/resources/realtime/realtime.py": {
601601
"mode": "100644",
602-
"sha256": "563d955afa8c0b023f4fcb353565db0b4348394f36e6a02659db6c2885286149"
602+
"sha256": "d45ef5f2d1e25e6f8761592a9fb5739d7a344c6e64a4e7a4006418c16f05a540"
603603
},
604604
"src/openai/resources/realtime/translations/__init__.py": {
605605
"mode": "100644",
@@ -611,15 +611,15 @@
611611
},
612612
"src/openai/resources/realtime/translations/translations.py": {
613613
"mode": "100644",
614-
"sha256": "8f45807c74c477a1d13df9979c6274a089c380004b943fcb6d9342b1a7ac3ef1"
614+
"sha256": "4a6f072cea78d5afab3fe1c995b939ff8c79f449665346c729abb5c516c53f21"
615615
},
616616
"src/openai/resources/responses/__init__.py": {
617617
"mode": "100644",
618618
"sha256": "70034ef169af2cd1991aaaed26fb565d2a2d5a5b6b14309a72ab1f483f0aba15"
619619
},
620620
"src/openai/resources/responses/api.md": {
621621
"mode": "100644",
622-
"sha256": "e7815b1278aa2780bb885ac5a249fe00859dff67d3899b708d25d2968e734785"
622+
"sha256": "b635f37005fb7126cc69a7b69e3f14a9df135d9523271cc4407b1ffcb9bd5c8c"
623623
},
624624
"src/openai/resources/responses/input_items.py": {
625625
"mode": "100644",
@@ -631,7 +631,7 @@
631631
},
632632
"src/openai/resources/responses/responses.py": {
633633
"mode": "100644",
634-
"sha256": "6e536e48d77d9ed583bd0c86a4f189b664e8636ae46a52a1c80fac28ffc3b4ec"
634+
"sha256": "5fa683c532adcec0b0ecfc9e0108075923e3243eab319a79325a880b707acaee"
635635
},
636636
"src/openai/resources/safety/__init__.py": {
637637
"mode": "100644",
@@ -3087,11 +3087,11 @@
30873087
},
30883088
"src/openai/types/beta/beta_responses_client_event.py": {
30893089
"mode": "100644",
3090-
"sha256": "939052a456df5a661ed334033a9151c18ed63dfba6c520fd92a7572d50efc091"
3090+
"sha256": "977d2d22d8468005d9dcf27c5e9c7e5c6430bb9dacd5258369022f1513431d3d"
30913091
},
30923092
"src/openai/types/beta/beta_responses_client_event_param.py": {
30933093
"mode": "100644",
3094-
"sha256": "ba2e2d06061761345308086b4b1d2ce39f86db631c46db825c5bbc7f62f63ccb"
3094+
"sha256": "0ad4be42df7202197e66ed97ae6fbfbdcad2aeb4e92328064630497d18fb457c"
30953095
},
30963096
"src/openai/types/beta/beta_responses_server_event.py": {
30973097
"mode": "100644",
@@ -6735,11 +6735,11 @@
67356735
},
67366736
"src/openai/types/responses/responses_client_event.py": {
67376737
"mode": "100644",
6738-
"sha256": "77d64183c9c69abfd4300c8094fbd3bc9c3a89743b9efa83a5264bd3dc165961"
6738+
"sha256": "3993eec234392a96646408fb959860a86b2711ac53dc7e8ae1338c43ca6383b4"
67396739
},
67406740
"src/openai/types/responses/responses_client_event_param.py": {
67416741
"mode": "100644",
6742-
"sha256": "95444697e02d702e9f568da381903d251b3d5c649de9c5d26662a1782431647b"
6742+
"sha256": "20cbab38d02cfd2506ad2104d09e2b3bfb0d2b0d8a55adec13882f3a15c9db2d"
67436743
},
67446744
"src/openai/types/responses/responses_server_event.py": {
67456745
"mode": "100644",

‎src/openai/lib/_response_resources.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def parse(
4040
resource: Responses,
4141
*,
4242
text_format: type[TextFormatT] | Omit = omit,
43+
parse_as: type[TextFormatT] | None | Omit = omit,
4344
access_programs: response_create_params.AccessPrograms | Omit = omit,
4445
background: Optional[bool] | Omit = omit,
4546
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
@@ -94,7 +95,7 @@ def parse(
9495
def parser(raw_response: Response) -> ParsedResponse[TextFormatT]:
9596
return parse_response(
9697
input_tools=tools,
97-
text_format=text_format,
98+
text_format=text_format if not is_given(parse_as) else omit if parse_as is None else parse_as,
9899
response=raw_response,
99100
)
100101

@@ -156,6 +157,7 @@ async def async_parse(
156157
resource: AsyncResponses,
157158
*,
158159
text_format: type[TextFormatT] | Omit = omit,
160+
parse_as: type[TextFormatT] | None | Omit = omit,
159161
access_programs: response_create_params.AccessPrograms | Omit = omit,
160162
background: Optional[bool] | Omit = omit,
161163
context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit,
@@ -210,7 +212,7 @@ async def async_parse(
210212
def parser(raw_response: Response) -> ParsedResponse[TextFormatT]:
211213
return parse_response(
212214
input_tools=tools,
213-
text_format=text_format,
215+
text_format=text_format if not is_given(parse_as) else omit if parse_as is None else parse_as,
214216
response=raw_response,
215217
)
216218

‎src/openai/lib/responses_tool_runner.py‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import hashlib
99
from copy import copy, deepcopy
1010
from time import monotonic
11-
from typing import TypeVar, Callable, Awaitable, cast
11+
from typing import Any, List, Union, TypeVar, Callable, Awaitable, cast, get_args
1212
from inspect import isawaitable, iscoroutine
1313
from functools import partial
1414
from contextvars import copy_context
@@ -24,7 +24,7 @@
2424
from .._types import Headers, omit
2525
from .._utils import is_given, transform
2626
from .browser import BrowserToolset, AsyncBrowserToolset, _legacy_state_url
27-
from .._compat import model_copy
27+
from .._compat import PYDANTIC_V1, model_copy
2828
from .._models import validate_type, construct_type_unchecked
2929
from .computer import (
3030
ComputerToolset,
@@ -55,6 +55,7 @@
5555
from ..types.responses.response_output_message import ResponseOutputMessage
5656
from ..types.responses.response_input_item_param import McpApprovalResponse
5757
from ..types.responses.response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam
58+
from ..types.responses.response_configuration_update_item_param_param import ResponseConfigurationUpdateItemParamParam
5859

5960
__all__ = [
6061
"RegisteredFunction",
@@ -78,6 +79,21 @@
7879
ToolOutput: TypeAlias = FunctionCallOutput | ComputerCallOutput | McpApprovalResponse
7980
_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel)
8081

82+
# The runner rejects configuration_update histories. Pydantic v1 cannot even
83+
# construct that item's TypedDict validator because "schema" shadows BaseModel.
84+
# Omit only this unsupported branch from replay validation, not public inputs.
85+
_history_input_type: Any = ResponseInputParam
86+
if PYDANTIC_V1:
87+
_history_input_type = List[
88+
Union[
89+
tuple(
90+
item
91+
for item in get_args(get_args(ResponseInputParam)[0])
92+
if item is not ResponseConfigurationUpdateItemParamParam
93+
)
94+
]
95+
]
96+
8197

8298
def _ledger_tools(tools: list[ToolParam]) -> list[ToolParam]:
8399
result: list[ToolParam] = []
@@ -507,7 +523,7 @@ def _history_calls(history: ResponseInputParam, tools: list[ToolParam], *, budge
507523
# the same action parser as dispatch, without changing the saved input.
508524
try:
509525
validate_type(
510-
type_=ResponseInputParam,
526+
type_=_history_input_type,
511527
value=[item for item in history if item.get("type") not in ("computer_call", "computer_call_output")],
512528
)
513529
except pydantic.ValidationError:

‎src/openai/resources/beta/responses/responses.py‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5276,7 +5276,11 @@ async def __aexit__(
52765276
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
52775277
) -> None:
52785278
if self.__connection is not None:
5279-
await self.__connection.close()
5279+
try:
5280+
await self.__connection.close()
5281+
except BaseException:
5282+
if exc is None:
5283+
raise
52805284

52815285

52825286
class ResponsesConnection(WebSocketConnectionBase):
@@ -5690,7 +5694,11 @@ def __exit__(
56905694
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
56915695
) -> None:
56925696
if self.__connection is not None:
5693-
self.__connection.close()
5697+
try:
5698+
self.__connection.close()
5699+
except BaseException:
5700+
if exc is None:
5701+
raise
56945702

56955703

56965704
class BaseResponsesConnectionResource:
@@ -5707,6 +5715,7 @@ def create(
57075715
context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]]
57085716
| Omit = omit,
57095717
conversation: Optional[beta_responses_client_event_param.ResponseCreateConversation] | Omit = omit,
5718+
generate: bool | Omit = omit,
57105719
include: Optional[List[BetaResponseIncludable]] | Omit = omit,
57115720
input: Union[str, BetaResponseInputParam] | Omit = omit,
57125721
instructions: Optional[str] | Omit = omit,
@@ -5873,6 +5882,7 @@ def create(
58735882
"background": background,
58745883
"context_management": context_management,
58755884
"conversation": conversation,
5885+
"generate": generate,
58765886
"include": include,
58775887
"input": input,
58785888
"instructions": instructions,
@@ -5965,6 +5975,7 @@ async def create(
59655975
context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]]
59665976
| Omit = omit,
59675977
conversation: Optional[beta_responses_client_event_param.ResponseCreateConversation] | Omit = omit,
5978+
generate: bool | Omit = omit,
59685979
include: Optional[List[BetaResponseIncludable]] | Omit = omit,
59695980
input: Union[str, BetaResponseInputParam] | Omit = omit,
59705981
instructions: Optional[str] | Omit = omit,
@@ -6131,6 +6142,7 @@ async def create(
61316142
"background": background,
61326143
"context_management": context_management,
61336144
"conversation": conversation,
6145+
"generate": generate,
61346146
"include": include,
61356147
"input": input,
61366148
"instructions": instructions,

‎src/openai/resources/live/forks.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,11 @@ async def __aexit__(
531531
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
532532
) -> None:
533533
if self.__connection is not None:
534-
await self.__connection.close()
534+
try:
535+
await self.__connection.close()
536+
except BaseException:
537+
if exc is None:
538+
raise
535539

536540

537541
class ForksConnection(WebSocketConnectionBase):
@@ -953,7 +957,11 @@ def __exit__(
953957
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
954958
) -> None:
955959
if self.__connection is not None:
956-
self.__connection.close()
960+
try:
961+
self.__connection.close()
962+
except BaseException:
963+
if exc is None:
964+
raise
957965

958966

959967
class BaseForksConnectionResource:

‎src/openai/resources/live/live.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,11 @@ async def __aexit__(
744744
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
745745
) -> None:
746746
if self.__connection is not None:
747-
await self.__connection.close()
747+
try:
748+
await self.__connection.close()
749+
except BaseException:
750+
if exc is None:
751+
raise
748752

749753

750754
class LiveConnection(WebSocketConnectionBase):
@@ -1158,7 +1162,11 @@ def __exit__(
11581162
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
11591163
) -> None:
11601164
if self.__connection is not None:
1161-
self.__connection.close()
1165+
try:
1166+
self.__connection.close()
1167+
except BaseException:
1168+
if exc is None:
1169+
raise
11621170

11631171

11641172
class BaseLiveConnectionResource:

‎src/openai/resources/live/sideband.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,11 @@ async def __aexit__(
543543
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
544544
) -> None:
545545
if self.__connection is not None:
546-
await self.__connection.close()
546+
try:
547+
await self.__connection.close()
548+
except BaseException:
549+
if exc is None:
550+
raise
547551

548552

549553
class SidebandConnection(WebSocketConnectionBase):
@@ -974,7 +978,11 @@ def __exit__(
974978
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
975979
) -> None:
976980
if self.__connection is not None:
977-
self.__connection.close()
981+
try:
982+
self.__connection.close()
983+
except BaseException:
984+
if exc is None:
985+
raise
978986

979987

980988
class BaseSidebandConnectionResource:

‎src/openai/resources/realtime/realtime.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,11 @@ async def __aexit__(
750750
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
751751
) -> None:
752752
if self.__connection is not None:
753-
await self.__connection.close()
753+
try:
754+
await self.__connection.close()
755+
except BaseException:
756+
if exc is None:
757+
raise
754758

755759

756760
class RealtimeConnection(WebSocketConnectionBase):
@@ -1199,7 +1203,11 @@ def __exit__(
11991203
self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None
12001204
) -> None:
12011205
if self.__connection is not None:
1202-
self.__connection.close()
1206+
try:
1207+
self.__connection.close()
1208+
except BaseException:
1209+
if exc is None:
1210+
raise
12031211

12041212

12051213
class BaseRealtimeConnectionResource:

0 commit comments

Comments
 (0)