Skip to main content

gproxy_protocol/wire/openai/
live.rs

1//! GPT-Live public session protocol, OpenAI reference verified 2026-10-20.
2//! Legacy Codex quicksilver/AVAS types remain in realtime::rtc.
3use crate::{Rest, WireRequest, WireResponse};
4use serde::{Deserialize, Serialize};
5#[derive(
6    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
7)]
8#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
9#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
10#[serde(untagged)]
11pub enum LiveVoice {
12    Builtin(String),
13    Custom(CustomVoice),
14}
15
16#[derive(
17    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
18)]
19#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
20#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
21#[derive(gproxy_protocol_macros::WireBuilder)]
22pub struct CustomVoice {
23    pub id: String,
24    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
25    pub rest: Rest,
26}
27
28#[derive(
29    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
30)]
31#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
32#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
33#[derive(gproxy_protocol_macros::WireBuilder)]
34pub struct LiveAudioFormat {
35    #[serde(rename = "type")]
36    pub type_: String,
37    #[serde(default, skip_serializing_if = "Option::is_none")]
38    pub rate: Option<u32>,
39    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
40    pub rest: Rest,
41}
42
43#[derive(
44    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
45)]
46#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
47#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
48#[derive(gproxy_protocol_macros::WireBuilder)]
49pub struct LiveAudioInput {
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub format: Option<LiveAudioFormat>,
52    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
53    pub rest: Rest,
54}
55
56#[derive(
57    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
58)]
59#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
60#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
61#[derive(gproxy_protocol_macros::WireBuilder)]
62pub struct LiveAudioOutput {
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub voice: Option<LiveVoice>,
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub format: Option<LiveAudioFormat>,
67    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
68    pub rest: Rest,
69}
70
71#[derive(
72    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
73)]
74#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
75#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
76#[derive(gproxy_protocol_macros::WireBuilder)]
77pub struct LiveAudio {
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub input: Option<LiveAudioInput>,
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub output: Option<LiveAudioOutput>,
82    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
83    pub rest: Rest,
84}
85
86#[derive(
87    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
88)]
89#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
90#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
91#[derive(gproxy_protocol_macros::WireBuilder)]
92pub struct LiveDecisionsConfig {
93    pub model: String,
94    pub questions: Vec<super::decisions::DecisionQuestion>,
95    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
96    pub rest: Rest,
97}
98
99#[derive(
100    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
101)]
102#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
103#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
104#[derive(gproxy_protocol_macros::WireBuilder)]
105pub struct LiveResponsesConfig {
106    pub model: String,
107    #[serde(
108        default,
109        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
110        skip_serializing_if = "Option::is_none"
111    )]
112    pub instructions: Option<Option<String>>,
113    #[serde(
114        default,
115        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
116        skip_serializing_if = "Option::is_none"
117    )]
118    pub max_output_tokens: Option<Option<u64>>,
119    #[serde(
120        default,
121        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
122        skip_serializing_if = "Option::is_none"
123    )]
124    pub parallel_tool_calls: Option<Option<bool>>,
125    #[serde(
126        default,
127        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
128        skip_serializing_if = "Option::is_none"
129    )]
130    pub reasoning: Option<Option<super::responses::ReasoningConfig>>,
131    #[serde(
132        default,
133        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
134        skip_serializing_if = "Option::is_none"
135    )]
136    pub service_tier: Option<Option<String>>,
137    #[serde(
138        default,
139        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
140        skip_serializing_if = "Option::is_none"
141    )]
142    pub text: Option<Option<serde_json::Value>>,
143    #[serde(default, skip_serializing_if = "Option::is_none")]
144    pub tools: Option<Vec<super::responses::Tool>>,
145    #[serde(default, skip_serializing_if = "Option::is_none")]
146    pub tool_choice: Option<serde_json::Value>,
147    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
148    pub rest: Rest,
149}
150
151#[derive(
152    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
153)]
154#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
155#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
156#[serde(tag = "type", rename_all = "snake_case")]
157pub enum LiveDelegation {
158    Client {
159        #[serde(default, flatten)]
160        rest: Rest,
161    },
162    Responses {
163        responses: Box<LiveResponsesConfig>,
164        #[serde(default, flatten)]
165        rest: Rest,
166    },
167    Decisions {
168        decisions: LiveDecisionsConfig,
169        #[serde(default, flatten)]
170        rest: Rest,
171    },
172}
173
174#[derive(
175    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
176)]
177#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
178#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
179#[derive(gproxy_protocol_macros::WireBuilder)]
180pub struct LiveTriggerQuestions {
181    pub questions: Vec<super::decisions::DecisionQuestion>,
182    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
183    pub rest: Rest,
184}
185
186#[derive(
187    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
188)]
189#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
190#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
191#[serde(rename_all = "snake_case")]
192pub enum LiveTriggerEvent {
193    #[serde(rename = "session.input_transcript.delta")]
194    InputTranscriptDelta,
195    #[serde(rename = "session.output_transcript.delta")]
196    OutputTranscriptDelta,
197}
198
199#[derive(
200    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
201)]
202#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
203#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
204#[serde(rename_all = "snake_case")]
205pub enum LiveTriggerType {
206    Decisions,
207}
208
209#[derive(
210    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
211)]
212#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
213#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
214#[derive(gproxy_protocol_macros::WireBuilder)]
215pub struct LiveDecisionTrigger {
216    #[serde(rename = "type")]
217    pub type_: LiveTriggerType,
218    pub event_type: LiveTriggerEvent,
219    pub decisions: LiveTriggerQuestions,
220    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
221    pub rest: Rest,
222}
223
224#[derive(
225    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
226)]
227#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
228#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
229#[derive(gproxy_protocol_macros::WireBuilder)]
230pub struct LiveServerEventSelector {
231    #[serde(rename = "type")]
232    pub type_: String,
233    #[serde(default, skip_serializing_if = "Option::is_none")]
234    pub response_event: Option<serde_json::Value>,
235    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
236    pub rest: Rest,
237}
238
239#[derive(
240    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
241)]
242#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
243#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
244#[serde(untagged)]
245pub enum LiveAllowedClientEvents {
246    All(String),
247    Events(Vec<String>),
248}
249
250#[derive(
251    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
252)]
253#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
254#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
255#[serde(untagged)]
256pub enum LiveAllowedServerEvents {
257    All(String),
258    Events(Vec<LiveServerEventSelector>),
259}
260
261#[derive(
262    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
263)]
264#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
265#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
266#[derive(gproxy_protocol_macros::WireBuilder)]
267pub struct LiveDataChannelConfig {
268    #[serde(default, skip_serializing_if = "Option::is_none")]
269    pub allowed_client_events: Option<LiveAllowedClientEvents>,
270    #[serde(default, skip_serializing_if = "Option::is_none")]
271    pub allowed_server_events: Option<LiveAllowedServerEvents>,
272    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
273    pub rest: Rest,
274}
275
276#[derive(
277    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
278)]
279#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
280#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
281#[derive(gproxy_protocol_macros::WireBuilder)]
282pub struct LiveClientConfig {
283    pub data_channel: LiveDataChannelConfig,
284    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
285    pub rest: Rest,
286}
287
288#[derive(
289    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
290)]
291#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
292#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
293#[derive(gproxy_protocol_macros::WireBuilder)]
294pub struct LiveSessionConfig {
295    pub model: String,
296    #[serde(default, skip_serializing_if = "Option::is_none")]
297    pub instructions: Option<String>,
298    #[serde(default, skip_serializing_if = "Option::is_none")]
299    pub audio: Option<LiveAudio>,
300    #[serde(default, skip_serializing_if = "Option::is_none")]
301    pub client: Option<LiveClientConfig>,
302    #[serde(
303        default,
304        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
305        skip_serializing_if = "Option::is_none"
306    )]
307    pub delegation: Option<Option<LiveDelegation>>,
308    #[serde(default, skip_serializing_if = "Option::is_none")]
309    pub initial_items: Option<Vec<serde_json::Value>>,
310    #[serde(default, skip_serializing_if = "Option::is_none")]
311    pub triggers: Option<Vec<LiveDecisionTrigger>>,
312    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
313    pub rest: Rest,
314}
315
316#[derive(
317    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
318)]
319#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
320#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
321#[derive(gproxy_protocol_macros::WireBuilder)]
322pub struct LiveSipDigest {
323    #[serde(rename = "type")]
324    pub type_: String,
325    pub username: String,
326    pub password: String,
327    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
328    pub rest: Rest,
329}
330
331#[derive(
332    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
333)]
334#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
335#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
336#[derive(gproxy_protocol_macros::WireBuilder)]
337pub struct LiveSipTrunk {
338    pub provider_url: String,
339    pub caller_number: String,
340    pub auth: LiveSipDigest,
341    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
342    pub rest: Rest,
343}
344
345#[derive(
346    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
347)]
348#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
349#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
350#[serde(tag = "type", rename_all = "snake_case")]
351pub enum LiveCreateTransport {
352    Webrtc {
353        sdp: String,
354        #[serde(default, flatten)]
355        rest: Rest,
356    },
357    Sip {
358        destination: String,
359        trunk: LiveSipTrunk,
360        #[serde(default, flatten)]
361        rest: Rest,
362    },
363}
364
365#[derive(
366    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
367)]
368#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
369#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
370#[serde(tag = "type", rename_all = "snake_case")]
371pub enum LiveResponseTransport {
372    Webrtc {
373        sdp: String,
374        #[serde(default, flatten)]
375        rest: Rest,
376    },
377    Sip {
378        #[serde(default, flatten)]
379        rest: Rest,
380    },
381}
382
383#[derive(
384    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
385)]
386#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
387#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
388#[derive(gproxy_protocol_macros::WireBuilder)]
389pub struct CreateLiveSessionRequestBody {
390    pub session: LiveSessionConfig,
391    pub transport: LiveCreateTransport,
392    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
393    pub rest: Rest,
394}
395
396#[derive(
397    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
398)]
399#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
400#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
401#[derive(gproxy_protocol_macros::WireBuilder)]
402pub struct LiveSessionId {
403    pub id: String,
404    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
405    pub rest: Rest,
406}
407
408#[derive(
409    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
410)]
411#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
412#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
413#[derive(gproxy_protocol_macros::WireBuilder)]
414pub struct CreateLiveSessionResponseBody {
415    pub session: LiveSessionId,
416    pub transport: LiveResponseTransport,
417    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
418    pub rest: Rest,
419}
420
421#[derive(
422    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
423)]
424#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
425#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
426#[derive(gproxy_protocol_macros::WireBuilder)]
427pub struct LiveSessionUsage {
428    pub seconds: f64,
429    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
430    pub rest: Rest,
431}
432
433#[derive(
434    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
435)]
436#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
437#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
438#[derive(gproxy_protocol_macros::WireBuilder)]
439pub struct LiveDecisionResult {
440    pub model: String,
441    pub answers: Vec<super::decisions::DecisionAnswer>,
442    #[serde(default, skip_serializing_if = "Option::is_none")]
443    pub id: Option<String>,
444    #[serde(default, skip_serializing_if = "Option::is_none")]
445    pub usage: Option<super::responses::ResponseUsage>,
446    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
447    pub rest: Rest,
448}
449
450#[derive(
451    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
452)]
453#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
454#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
455#[derive(gproxy_protocol_macros::WireBuilder)]
456pub struct LiveDecisionStreamEvent {
457    #[serde(rename = "type")]
458    pub type_: String,
459    pub decision: LiveDecisionResult,
460    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
461    pub rest: Rest,
462}
463
464#[derive(
465    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
466)]
467#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
468#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
469#[serde(untagged)]
470pub enum LiveDelegatedEvent {
471    Decision(Box<LiveDecisionStreamEvent>),
472    Response(Box<super::responses::stream::StreamEvent>),
473    Other(serde_json::Value),
474}
475
476#[derive(
477    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
478)]
479#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
480#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
481#[derive(gproxy_protocol_macros::WireBuilder)]
482pub struct LiveServerEvent {
483    #[serde(rename = "type")]
484    pub type_: String,
485    #[serde(default, skip_serializing_if = "Option::is_none")]
486    pub event_id: Option<String>,
487    #[serde(default, skip_serializing_if = "Option::is_none")]
488    pub session: Option<LiveSessionConfig>,
489    #[serde(default, skip_serializing_if = "Option::is_none")]
490    pub session_id: Option<String>,
491    #[serde(
492        default,
493        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
494        skip_serializing_if = "Option::is_none"
495    )]
496    pub delegation_id: Option<Option<String>>,
497    #[serde(
498        default,
499        deserialize_with = "crate::wire::openai::responses::input::present_nullable",
500        skip_serializing_if = "Option::is_none"
501    )]
502    pub trigger_id: Option<Option<String>>,
503    #[serde(default, skip_serializing_if = "Option::is_none")]
504    pub source_event_type: Option<String>,
505    #[serde(default, skip_serializing_if = "Option::is_none")]
506    pub source_event_id: Option<String>,
507    #[serde(default, skip_serializing_if = "Option::is_none")]
508    pub event: Option<LiveDelegatedEvent>,
509    #[serde(default, skip_serializing_if = "Option::is_none")]
510    pub usage: Option<LiveSessionUsage>,
511    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
512    pub rest: Rest,
513}
514
515#[derive(
516    Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::DeclaredFields,
517)]
518#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
519#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
520#[derive(gproxy_protocol_macros::WireBuilder)]
521pub struct LiveClientEvent {
522    #[serde(rename = "type")]
523    pub type_: String,
524    #[serde(default, skip_serializing_if = "Option::is_none")]
525    pub event_id: Option<String>,
526    #[serde(default, skip_serializing_if = "Option::is_none")]
527    pub session: Option<serde_json::Value>,
528    #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
529    pub rest: Rest,
530}
531
532pub type CreateLiveSessionRequest = WireRequest<CreateLiveSessionRequestBody>;
533pub type CreateLiveSessionResponse = WireResponse<CreateLiveSessionResponseBody>;