gproxy_protocol/wire/openai/realtime/
rtc.rs1use super::session::RealtimeSessionCreateRequest;
4use crate::{
5 WireRequest, WireResponse,
6 connection::{HttpBody, MultipartPart},
7};
8
9#[derive(Debug, gproxy_protocol_macros::WireBuilder)]
10#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
11#[derive(gproxy_protocol_macros::DeclaredFields)]
12pub struct CreateRealtimeCallMultipartForm {
13 pub sdp: MultipartPart,
14 pub session: Option<RealtimeSessionCreateRequest>,
15}
16pub type CreateRealtimeCallRequest = WireRequest<CreateRealtimeCallMultipartForm>;
17pub type CreateRealtimeCallResponse = WireResponse<HttpBody>;
18pub type RealtimeCallSessionJson = RealtimeSessionCreateRequest;
20
21#[derive(
25 Debug,
26 Clone,
27 PartialEq,
28 serde::Serialize,
29 serde::Deserialize,
30 gproxy_protocol_macros::WireBuilder,
31 gproxy_protocol_macros::DeclaredFields,
32)]
33#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
34pub struct LiveSessionCreateRequest {
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub model: Option<String>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub instructions: Option<String>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub audio: Option<super::RealtimeAudioConfig>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub delegation: Option<LiveDelegation>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub initial_items: Option<Vec<LiveInitialMessage>>,
45 #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
46 pub rest: crate::Rest,
47}
48
49#[derive(
50 Debug,
51 Clone,
52 PartialEq,
53 serde::Serialize,
54 serde::Deserialize,
55 gproxy_protocol_macros::WireBuilder,
56 gproxy_protocol_macros::DeclaredFields,
57)]
58#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
59pub struct LiveDelegation {
60 #[serde(rename = "type")]
61 pub type_: String,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub ack_filler: Option<bool>,
64 #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
65 pub rest: crate::Rest,
66}
67
68#[derive(
69 Debug,
70 Clone,
71 PartialEq,
72 serde::Serialize,
73 serde::Deserialize,
74 gproxy_protocol_macros::WireBuilder,
75 gproxy_protocol_macros::DeclaredFields,
76)]
77#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
78pub struct LiveInitialMessage {
79 #[serde(rename = "type")]
80 pub type_: String,
81 pub role: String,
82 pub content: Vec<LiveInitialText>,
83 #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
84 pub rest: crate::Rest,
85}
86
87#[derive(
88 Debug,
89 Clone,
90 PartialEq,
91 serde::Serialize,
92 serde::Deserialize,
93 gproxy_protocol_macros::WireBuilder,
94 gproxy_protocol_macros::DeclaredFields,
95)]
96#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
97pub struct LiveInitialText {
98 #[serde(rename = "type")]
99 pub type_: String,
100 pub text: String,
101 #[serde(default, flatten, skip_serializing_if = "serde_json::Map::is_empty")]
102 pub rest: crate::Rest,
103}
104
105#[derive(Debug, gproxy_protocol_macros::WireBuilder, gproxy_protocol_macros::DeclaredFields)]
106#[cfg_attr(not(feature = "exhaustive"), non_exhaustive)]
107pub struct CreateLiveCallMultipartForm {
108 pub sdp: MultipartPart,
109 pub session: LiveSessionCreateRequest,
110}
111pub type CreateLiveCallRequest = WireRequest<CreateLiveCallMultipartForm>;
112pub type CreateLiveCallResponse = WireResponse<HttpBody>;
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub enum RealtimeRoute<'a> {
118 CreateCall,
119 CreateLiveCall,
120 Connect,
121 Live { call_id: Option<&'a str> },
122}
123impl<'a> RealtimeRoute<'a> {
124 pub fn from_request(method: &http::Method, path: &'a str) -> Option<Self> {
126 let route = Self::from_path(path)?;
127 match (method, route) {
128 (&http::Method::POST, Self::Live { call_id: None }) => Some(Self::CreateLiveCall),
129 (&http::Method::POST, Self::CreateCall) => Some(route),
130 (&http::Method::GET, Self::Connect | Self::Live { .. }) => Some(route),
131 _ => None,
132 }
133 }
134 pub fn from_path(path: &'a str) -> Option<Self> {
137 let path = path.strip_prefix("/v1/").unwrap_or(path);
138 let path = path.trim_start_matches('/').trim_end_matches('/');
139 match path {
140 "realtime/calls" => Some(Self::CreateCall),
141 "realtime" => Some(Self::Connect),
142 "live" => Some(Self::Live { call_id: None }),
143 _ => path
144 .strip_prefix("live/")
145 .filter(|id| !id.is_empty() && !id.contains('/') && !matches!(*id, "." | ".."))
146 .map(|id| Self::Live { call_id: Some(id) }),
147 }
148 }
149 pub fn operation(self) -> crate::OperationKey {
150 crate::OperationKey {
151 operation: match self {
152 Self::CreateCall | Self::CreateLiveCall => crate::Operation::CreateRealtimeCall,
153 _ => crate::Operation::ConnectRealtime,
154 },
155 dialect: crate::Dialect::OpenAi,
156 }
157 }
158}