1use serde::{Deserialize, Serialize};
8
9use super::audio::{InputAudioFormat, OutputAudioFormat};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum TurnDetectionType {
17 #[default]
19 ClientVad,
20 ServerVad,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct TurnDetection {
28 #[serde(rename = "type")]
30 pub type_: TurnDetectionType,
31}
32
33impl TurnDetection {
34 pub fn new(type_: TurnDetectionType) -> Self {
35 Self { type_ }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
41#[serde(rename_all = "snake_case")]
42pub enum ChatMode {
43 VideoPassive,
45 #[default]
47 Audio,
48}
49
50#[derive(Debug, Clone, Default, Serialize, Deserialize)]
52pub struct BetaFields {
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub chat_mode: Option<ChatMode>,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub tts_source: Option<String>,
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub auto_search: Option<bool>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct RealtimeTool {
67 #[serde(rename = "type")]
69 pub type_: String,
70 pub name: String,
72 pub description: String,
74 pub parameters: serde_json::Value,
76}
77
78impl RealtimeTool {
79 pub fn function(
82 name: impl Into<String>,
83 description: impl Into<String>,
84 parameters: serde_json::Value,
85 ) -> Self {
86 Self {
87 type_: "function".to_string(),
88 name: name.into(),
89 description: description.into(),
90 parameters,
91 }
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct SessionConfig {
98 pub input_audio_format: InputAudioFormat,
100 pub output_audio_format: OutputAudioFormat,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub instructions: Option<String>,
105 pub turn_detection: TurnDetection,
107 pub beta_fields: BetaFields,
109 #[serde(default, skip_serializing_if = "Vec::is_empty")]
111 pub tools: Vec<RealtimeTool>,
112}
113
114impl Default for SessionConfig {
115 fn default() -> Self {
116 Self {
117 input_audio_format: InputAudioFormat::default(),
118 output_audio_format: OutputAudioFormat::default(),
119 instructions: None,
120 turn_detection: TurnDetection::new(TurnDetectionType::default()),
121 beta_fields: BetaFields::default(),
122 tools: Vec::new(),
123 }
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
129#[serde(rename_all = "snake_case")]
130pub enum ItemType {
131 Message,
132 FunctionCall,
133 FunctionCallOutput,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ItemContent {
139 #[serde(rename = "type")]
141 pub type_: String,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub text: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub audio: Option<String>,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub transcript: Option<String>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct RealtimeConversationItem {
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub id: Option<String>,
160 #[serde(rename = "type")]
162 pub type_: ItemType,
163 pub object: String,
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub status: Option<String>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub role: Option<String>,
171 #[serde(default, skip_serializing_if = "Vec::is_empty")]
173 pub content: Vec<ItemContent>,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub name: Option<String>,
177 #[serde(skip_serializing_if = "Option::is_none")]
179 pub arguments: Option<String>,
180 #[serde(skip_serializing_if = "Option::is_none")]
182 pub output: Option<String>,
183}
184
185impl RealtimeConversationItem {
186 pub fn user_text(text: impl Into<String>) -> Self {
188 Self {
189 id: None,
190 type_: ItemType::Message,
191 object: "realtime.item".to_string(),
192 status: Some("completed".to_string()),
193 role: Some("user".to_string()),
194 content: vec![ItemContent {
195 type_: "input_text".to_string(),
196 text: Some(text.into()),
197 audio: None,
198 transcript: None,
199 }],
200 name: None,
201 arguments: None,
202 output: None,
203 }
204 }
205
206 pub fn function_output(call_name: impl Into<String>, output: impl Into<String>) -> Self {
208 Self {
209 id: None,
210 type_: ItemType::FunctionCallOutput,
211 object: "realtime.item".to_string(),
212 status: None,
213 role: None,
214 content: Vec::new(),
215 name: Some(call_name.into()),
216 arguments: None,
217 output: Some(output.into()),
218 }
219 }
220}
221
222#[derive(Debug, Clone, Default, Serialize, Deserialize)]
224pub struct TokenDetails {
225 #[serde(skip_serializing_if = "Option::is_none")]
226 pub text_tokens: Option<u64>,
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub audio_tokens: Option<u64>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub cached_tokens: Option<u64>,
231}
232
233#[derive(Debug, Clone, Default, Serialize, Deserialize)]
235pub struct RealtimeUsage {
236 #[serde(default)]
237 pub total_tokens: u64,
238 #[serde(default)]
239 pub input_tokens: u64,
240 #[serde(default)]
241 pub output_tokens: u64,
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub input_token_details: Option<TokenDetails>,
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub output_token_details: Option<TokenDetails>,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct RealtimeResponse {
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub id: Option<String>,
253 pub object: String,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub status: Option<String>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub usage: Option<RealtimeUsage>,
259}