dynamo_protocols/types/realtime.rs
1// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Re-exports upstream async-openai realtime event types. Per the ownership
5// rubric in `lib/protocols/CLAUDE.md`, no Dynamo-side overrides are needed
6// today: upstream covers the full event surface (`RealtimeClientEvent`,
7// `RealtimeServerEvent`, and their per-variant payloads) and no real client
8// has been observed to send a shape upstream rejects.
9
10pub use async_openai::types::realtime::*;
11
12/// Returns the `type` wire-tag string for a realtime event variant — useful
13/// for logging, error messages, and metric labels that need a stable name
14/// without reserializing the value.
15///
16/// `async-openai` ships an equivalent `crate::traits::EventType` trait, but it
17/// is gated on the `_api` feature, which pulls reqwest / tokio / secrecy /
18/// eventsource-stream into the build. `dynamo-protocols` is types-only by
19/// design (see the Cargo.toml banner), so we mirror the trait shape locally.
20/// If `_api` ever becomes affordable for this crate, swap `pub use
21/// async_openai::traits::EventType;` in here and remove the impls below; call
22/// sites need no changes.
23///
24/// Implemented today only for `RealtimeClientEvent`. The `RealtimeServerEvent`
25/// impl can be added when a consumer needs it.
26///
27/// [NOTE] Could be replaced with a serde-introspection helper (e.g. the
28/// `serde_variant` crate) that reads the wire tag from `#[serde(rename)]`
29/// at runtime; deferred until as clean up work.
30pub trait EventType {
31 fn event_type(&self) -> &'static str;
32}
33
34impl EventType for RealtimeClientEvent {
35 fn event_type(&self) -> &'static str {
36 // `RealtimeClientEvent` is not `#[non_exhaustive]`, so a future upstream
37 // variant breaks this match at compile time rather than silently
38 // returning a stale label.
39 match self {
40 RealtimeClientEvent::SessionUpdate(_) => "session.update",
41 RealtimeClientEvent::InputAudioBufferAppend(_) => "input_audio_buffer.append",
42 RealtimeClientEvent::InputAudioBufferCommit(_) => "input_audio_buffer.commit",
43 RealtimeClientEvent::InputAudioBufferClear(_) => "input_audio_buffer.clear",
44 RealtimeClientEvent::ConversationItemCreate(_) => "conversation.item.create",
45 RealtimeClientEvent::ConversationItemRetrieve(_) => "conversation.item.retrieve",
46 RealtimeClientEvent::ConversationItemTruncate(_) => "conversation.item.truncate",
47 RealtimeClientEvent::ConversationItemDelete(_) => "conversation.item.delete",
48 RealtimeClientEvent::ResponseCreate(_) => "response.create",
49 RealtimeClientEvent::ResponseCancel(_) => "response.cancel",
50 RealtimeClientEvent::OutputAudioBufferClear(_) => "output_audio_buffer.clear",
51 }
52 }
53}