-
Notifications
You must be signed in to change notification settings - Fork 4.2k
feat(realtime): add typed create_response helper to RealtimeSession #3722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,11 +107,23 @@ class RealtimeModelSendSessionUpdate: | |
| """The updated session settings to send.""" | ||
|
|
||
|
|
||
| @dataclass | ||
| class RealtimeModelSendResponseCreate: | ||
| """Trigger a new model response via a `response.create` event.""" | ||
|
|
||
| instructions: str | None = None | ||
| """Optional instructions that override the session instructions for this response only.""" | ||
|
|
||
| metadata: dict[str, Any] | None = None | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The Realtime response-create payload this is converted into expects platform metadata as Useful? React with 👍 / 👎. |
||
| """Optional metadata to attach to this response.""" | ||
|
|
||
|
|
||
| RealtimeModelSendEvent: TypeAlias = ( | ||
| RealtimeModelSendRawMessage | ||
| | RealtimeModelSendUserInput | ||
| | RealtimeModelSendAudio | ||
| | RealtimeModelSendToolOutput | ||
| | RealtimeModelSendInterrupt | ||
| | RealtimeModelSendSessionUpdate | ||
| | RealtimeModelSendResponseCreate | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,6 +57,9 @@ | |
| from openai.types.realtime.realtime_function_tool import ( | ||
| RealtimeFunctionTool as OpenAISessionFunction, | ||
| ) | ||
| from openai.types.realtime.realtime_response_create_params import ( | ||
| RealtimeResponseCreateParams as OpenAIRealtimeResponseCreateParams, | ||
| ) | ||
| from openai.types.realtime.realtime_server_event import ( | ||
| RealtimeServerEvent as OpenAIRealtimeServerEvent, | ||
| ) | ||
|
|
@@ -140,6 +143,7 @@ | |
| RealtimeModelSendEvent, | ||
| RealtimeModelSendInterrupt, | ||
| RealtimeModelSendRawMessage, | ||
| RealtimeModelSendResponseCreate, | ||
| RealtimeModelSendSessionUpdate, | ||
| RealtimeModelSendToolOutput, | ||
| RealtimeModelSendUserInput, | ||
|
|
@@ -726,6 +730,8 @@ async def send_event(self, event: RealtimeModelSendEvent) -> None: | |
| await self._send_interrupt(event) | ||
| elif isinstance(event, RealtimeModelSendSessionUpdate): | ||
| await self._send_session_update(event) | ||
| elif isinstance(event, RealtimeModelSendResponseCreate): | ||
| await self._send_response_create(event) | ||
| else: | ||
| assert_never(event) | ||
| raise ValueError(f"Unknown event type: {type(event)}") | ||
|
|
@@ -977,6 +983,16 @@ async def _send_session_update(self, event: RealtimeModelSendSessionUpdate) -> N | |
| """Send a session update to the model.""" | ||
| await self._update_session_config(event.session_settings) | ||
|
|
||
| async def _send_response_create(self, event: RealtimeModelSendResponseCreate) -> None: | ||
| """Send a response.create through the sequencer so it cannot collide with an active one.""" | ||
| converted = _ConversionHelper.convert_response_create(event) | ||
| request_version = await self._reserve_response_create_request(manual=True) | ||
| self._start_response_create( | ||
| request_version, | ||
| response_create=converted, | ||
| manual=True, | ||
| ) | ||
|
|
||
| async def _handle_audio_delta(self, parsed: ResponseAudioDeltaEvent) -> None: | ||
| """Handle audio delta events and update audio tracking state.""" | ||
| self._current_item_id = parsed.item_id | ||
|
|
@@ -1705,6 +1721,24 @@ def try_convert_raw_message( | |
| except Exception: | ||
| return None | ||
|
|
||
| @classmethod | ||
| def convert_response_create( | ||
| cls, event: RealtimeModelSendResponseCreate | ||
| ) -> OpenAIResponseCreateEvent: | ||
| response_params: dict[str, Any] = {} | ||
| if event.instructions is not None: | ||
| response_params["instructions"] = event.instructions | ||
| if event.metadata is not None: | ||
| response_params["metadata"] = event.metadata | ||
| # Omit the response field entirely when no params are provided. Passing response=None | ||
| # explicitly marks the field as set, so model_dump_json(exclude_unset=True) would emit | ||
| # {"response": null} instead of a bare {"type": "response.create"}, which the Realtime | ||
| # schema can reject. | ||
| if not response_params: | ||
| return OpenAIResponseCreateEvent(type="response.create") | ||
| response = OpenAIRealtimeResponseCreateParams(**response_params) | ||
| return OpenAIResponseCreateEvent(type="response.create", response=response) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When Useful? React with 👍 / 👎. |
||
|
|
||
| @classmethod | ||
| def convert_tracing_config( | ||
| cls, tracing_config: RealtimeModelTracingConfig | Literal["auto"] | None | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a caller passes
create_response(metadata={"turn": 1})or any other non-string value, this new typed event says the value is valid and then forwards it unchanged intoRealtimeResponseCreateParams; the Realtime API/OpenAI SDK schema defines response metadata as string key/value pairs, so typed callers do not get checker feedback and instead hit validation or API errors at runtime. Please match the API surface withdict[str, str]here and on the publicRealtimeSession.create_responseparameter.Useful? React with 👍 / 👎.