1use base64::Engine;
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12
13fn deserialize_audio_bytes<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
16where
17 D: serde::Deserializer<'de>,
18{
19 let s = String::deserialize(deserializer)?;
20 base64::engine::general_purpose::STANDARD.decode(&s).map_err(serde::de::Error::custom)
21}
22
23fn serialize_audio_bytes<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
24where
25 S: serde::Serializer,
26{
27 let s = base64::engine::general_purpose::STANDARD.encode(bytes);
28 serializer.serialize_str(&s)
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(tag = "type")]
36#[non_exhaustive]
37pub enum ClientEvent {
38 #[serde(rename = "session.update")]
40 SessionUpdate {
41 session: Value,
43 },
44
45 #[serde(rename = "input_audio_buffer.append")]
47 AudioDelta {
48 #[serde(skip_serializing_if = "Option::is_none")]
50 event_id: Option<String>,
51 #[serde(
53 serialize_with = "serialize_audio_bytes",
54 deserialize_with = "deserialize_audio_bytes"
55 )]
56 audio: Vec<u8>,
57 #[serde(skip)]
60 format: Option<crate::audio::AudioFormat>,
61 },
62
63 #[serde(rename = "input_audio_buffer.commit")]
65 InputAudioBufferCommit,
66
67 #[serde(rename = "input_audio_buffer.clear")]
69 InputAudioBufferClear,
70
71 #[serde(rename = "conversation.item.create")]
73 ConversationItemCreate {
74 item: Value,
76 },
77
78 #[serde(rename = "response.create")]
80 ResponseCreate {
81 #[serde(skip_serializing_if = "Option::is_none")]
83 config: Option<Value>,
84 },
85
86 #[serde(rename = "response.cancel")]
88 ResponseCancel,
89
90 #[serde(rename = "message")]
92 Message {
93 role: String,
95 parts: Vec<adk_core::types::Part>,
97 },
98
99 #[serde(skip_serializing)]
106 UpdateSession {
107 #[serde(skip_serializing_if = "Option::is_none")]
109 instructions: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 tools: Option<Vec<crate::config::ToolDefinition>>,
113 },
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct ConversationItem {
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub id: Option<String>,
122 #[serde(rename = "type")]
124 pub item_type: String,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub role: Option<String>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub content: Option<Vec<ContentPart>>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub call_id: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub output: Option<String>,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ContentPart {
142 #[serde(rename = "type")]
144 pub content_type: String,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub text: Option<String>,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub audio: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub transcript: Option<String>,
154}
155
156impl ConversationItem {
157 pub fn user_text(text: impl Into<String>) -> Self {
159 Self {
160 id: None,
161 item_type: "message".to_string(),
162 role: Some("user".to_string()),
163 content: Some(vec![ContentPart {
164 content_type: "input_text".to_string(),
165 text: Some(text.into()),
166 audio: None,
167 transcript: None,
168 }]),
169 call_id: None,
170 output: None,
171 }
172 }
173
174 pub fn tool_response(call_id: impl Into<String>, output: impl Into<String>) -> Self {
176 Self {
177 id: None,
178 item_type: "function_call_output".to_string(),
179 role: None,
180 content: None,
181 call_id: Some(call_id.into()),
182 output: Some(output.into()),
183 }
184 }
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(tag = "type")]
196#[non_exhaustive]
197pub enum ServerEvent {
198 #[serde(rename = "session.created")]
200 SessionCreated {
201 event_id: String,
203 session: Value,
205 },
206
207 #[serde(rename = "session.updated")]
209 SessionUpdated {
210 event_id: String,
212 session: Value,
214 },
215
216 #[serde(rename = "error")]
218 Error {
219 event_id: String,
221 error: ErrorInfo,
223 },
224
225 #[serde(rename = "input_audio_buffer.speech_started")]
227 SpeechStarted {
228 event_id: String,
230 audio_start_ms: u64,
232 },
233
234 #[serde(rename = "input_audio_buffer.speech_stopped")]
236 SpeechStopped {
237 event_id: String,
239 audio_end_ms: u64,
241 },
242
243 #[serde(rename = "input_audio_buffer.committed")]
245 AudioCommitted {
246 event_id: String,
248 item_id: String,
250 },
251
252 #[serde(rename = "input_audio_buffer.cleared")]
254 AudioCleared {
255 event_id: String,
257 },
258
259 #[serde(rename = "conversation.item.created")]
261 ItemCreated {
262 event_id: String,
264 item: Value,
266 },
267
268 #[serde(rename = "response.created")]
270 ResponseCreated {
271 event_id: String,
273 response: Value,
275 },
276
277 #[serde(rename = "response.done")]
279 ResponseDone {
280 event_id: String,
282 response: Value,
284 },
285
286 #[serde(rename = "response.output_item.added")]
288 OutputItemAdded {
289 event_id: String,
291 response_id: String,
293 output_index: u32,
295 item: Value,
297 },
298
299 #[serde(rename = "response.output_item.done")]
301 OutputItemDone {
302 event_id: String,
304 response_id: String,
306 output_index: u32,
308 item: Value,
310 },
311
312 #[serde(alias = "response.audio.delta", rename = "response.output_audio.delta")]
314 AudioDelta {
315 event_id: String,
317 response_id: String,
319 item_id: String,
321 output_index: u32,
323 content_index: u32,
325 #[serde(
327 serialize_with = "serialize_audio_bytes",
328 deserialize_with = "deserialize_audio_bytes"
329 )]
330 delta: Vec<u8>,
331 },
332
333 #[serde(alias = "response.audio.done", rename = "response.output_audio.done")]
335 AudioDone {
336 event_id: String,
338 response_id: String,
340 item_id: String,
342 output_index: u32,
344 content_index: u32,
346 },
347
348 #[serde(alias = "response.text.delta", rename = "response.output_text.delta")]
350 TextDelta {
351 event_id: String,
353 response_id: String,
355 item_id: String,
357 output_index: u32,
359 content_index: u32,
361 delta: String,
363 },
364
365 #[serde(alias = "response.text.done", rename = "response.output_text.done")]
367 TextDone {
368 event_id: String,
370 response_id: String,
372 item_id: String,
374 output_index: u32,
376 content_index: u32,
378 text: String,
380 },
381
382 #[serde(
384 alias = "response.audio_transcript.delta",
385 rename = "response.output_audio_transcript.delta"
386 )]
387 TranscriptDelta {
388 event_id: String,
390 response_id: String,
392 item_id: String,
394 output_index: u32,
396 content_index: u32,
398 delta: String,
400 },
401
402 #[serde(
404 alias = "response.audio_transcript.done",
405 rename = "response.output_audio_transcript.done"
406 )]
407 TranscriptDone {
408 event_id: String,
410 response_id: String,
412 item_id: String,
414 output_index: u32,
416 content_index: u32,
418 transcript: String,
420 },
421
422 #[serde(rename = "response.function_call_arguments.delta")]
424 FunctionCallDelta {
425 event_id: String,
427 response_id: String,
429 item_id: String,
431 output_index: u32,
433 call_id: String,
435 delta: String,
437 },
438
439 #[serde(rename = "response.function_call_arguments.done")]
441 FunctionCallDone {
442 event_id: String,
444 response_id: String,
446 item_id: String,
448 output_index: u32,
450 call_id: String,
452 name: String,
454 arguments: String,
456 },
457
458 #[serde(rename = "rate_limits.updated")]
460 RateLimitsUpdated {
461 event_id: String,
463 rate_limits: Vec<RateLimit>,
465 },
466
467 #[serde(rename = "conversation.item.input_audio_transcription.delta")]
469 InputTranscriptDelta {
470 item_id: String,
472 content_index: u32,
474 delta: String,
476 },
477
478 #[serde(rename = "conversation.item.input_audio_transcription.completed")]
480 InputTranscriptCompleted {
481 item_id: String,
483 content_index: u32,
485 transcript: String,
487 },
488
489 #[serde(other)]
491 Unknown,
492}
493
494#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct ErrorInfo {
497 #[serde(rename = "type")]
499 pub error_type: String,
500 #[serde(skip_serializing_if = "Option::is_none")]
502 pub code: Option<String>,
503 pub message: String,
505 #[serde(skip_serializing_if = "Option::is_none")]
507 pub param: Option<String>,
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct RateLimit {
513 pub name: String,
515 pub limit: u64,
517 pub remaining: u64,
519 pub reset_seconds: f64,
521}
522
523#[derive(Debug, Clone, Serialize, Deserialize)]
525pub struct ToolCall {
526 pub call_id: String,
528 pub name: String,
530 pub arguments: Value,
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct ToolResponse {
537 pub call_id: String,
539 pub output: Value,
541}
542
543impl ToolResponse {
544 pub fn new(call_id: impl Into<String>, output: impl Serialize) -> Self {
546 Self {
547 call_id: call_id.into(),
548 output: serde_json::to_value(output).unwrap_or(Value::Null),
549 }
550 }
551
552 pub fn from_string(call_id: impl Into<String>, output: impl Into<String>) -> Self {
554 Self { call_id: call_id.into(), output: Value::String(output.into()) }
555 }
556}