gemini_genai_rs/protocol/messages/
server.rs1use serde::{Deserialize, Serialize};
4
5use crate::protocol::types::*;
6
7#[derive(Debug, Clone, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct SetupCompleteMessage {
11 pub setup_complete: SetupCompletePayload,
13}
14
15#[derive(Debug, Clone, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct SetupCompletePayload {
19 #[serde(default)]
21 pub session_resumption: Option<SessionResumptionResult>,
22}
23
24#[derive(Debug, Clone, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct SessionResumptionResult {
28 #[serde(default)]
30 pub handle: Option<String>,
31 #[serde(default)]
33 pub resumed: Option<bool>,
34}
35
36#[derive(Debug, Clone, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub struct ServerContentMessage {
40 pub server_content: ServerContentPayload,
42 #[serde(default)]
44 pub usage_metadata: Option<UsageMetadata>,
45}
46
47#[derive(Debug, Clone, Deserialize)]
49#[serde(rename_all = "camelCase")]
50pub struct ServerContentPayload {
51 #[serde(default)]
53 pub model_turn: Option<Content>,
54 #[serde(default)]
56 pub turn_complete: Option<bool>,
57 #[serde(default)]
59 pub generation_complete: Option<bool>,
60 #[serde(default)]
62 pub interrupted: Option<bool>,
63 #[serde(default)]
65 pub input_transcription: Option<TranscriptionPayload>,
66 #[serde(default)]
68 pub output_transcription: Option<TranscriptionPayload>,
69 #[serde(default)]
71 pub grounding_metadata: Option<GroundingMetadata>,
72 #[serde(default)]
74 pub url_context_metadata: Option<UrlContextMetadata>,
75 #[serde(default)]
77 pub turn_complete_reason: Option<String>,
78 #[serde(default)]
80 pub waiting_for_input: Option<bool>,
81 #[serde(default)]
85 pub interaction_status: Option<String>,
86}
87
88#[derive(Debug, Clone, Deserialize)]
90pub struct TranscriptionPayload {
91 #[serde(default)]
93 pub text: Option<String>,
94}
95
96#[derive(Debug, Clone, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub struct ToolCallMessage {
100 pub tool_call: ToolCallPayload,
102}
103
104#[derive(Debug, Clone, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct ToolCallPayload {
108 pub function_calls: Vec<FunctionCall>,
110}
111
112#[derive(Debug, Clone, Deserialize)]
114#[serde(rename_all = "camelCase")]
115pub struct ToolCallCancellationMessage {
116 pub tool_call_cancellation: ToolCallCancellationPayload,
118}
119
120#[derive(Debug, Clone, Deserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct ToolCallCancellationPayload {
124 pub ids: Vec<String>,
126}
127
128#[derive(Debug, Clone, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct GoAwayMessage {
132 pub go_away: GoAwayPayload,
134}
135
136#[derive(Debug, Clone, Deserialize)]
138#[serde(rename_all = "camelCase")]
139pub struct GoAwayPayload {
140 #[serde(default, deserialize_with = "proto_duration::deserialize_opt")]
144 pub time_left: Option<std::time::Duration>,
145}
146
147mod proto_duration {
149 use serde::{Deserialize, Deserializer};
150 use std::time::Duration;
151
152 pub(super) fn parse(s: &str) -> Option<Duration> {
153 let secs: f64 = s.trim().strip_suffix('s')?.parse().ok()?;
154 (secs.is_finite() && secs >= 0.0).then(|| Duration::from_secs_f64(secs))
155 }
156
157 pub(super) fn deserialize_opt<'de, D: Deserializer<'de>>(
158 d: D,
159 ) -> Result<Option<Duration>, D::Error> {
160 Ok(Option::<String>::deserialize(d)?.as_deref().and_then(parse))
161 }
162
163 #[cfg(test)]
164 mod tests {
165 use super::*;
166
167 #[test]
168 fn parses_proto_duration_strings() {
169 assert_eq!(parse("30s"), Some(Duration::from_secs(30)));
170 assert_eq!(parse("2.5s"), Some(Duration::from_millis(2500)));
171 assert_eq!(parse("0s"), Some(Duration::ZERO));
172 assert_eq!(parse("30"), None);
173 assert_eq!(parse("-1s"), None);
174 assert_eq!(parse("soon"), None);
175 }
176 }
177}
178
179#[derive(Debug, Clone, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct SessionResumptionUpdateMessage {
183 pub session_resumption_update: SessionResumptionUpdatePayload,
185}
186
187#[derive(Debug, Clone, Deserialize)]
189#[serde(rename_all = "camelCase")]
190pub struct SessionResumptionUpdatePayload {
191 #[serde(default)]
193 pub new_handle: Option<String>,
194 #[serde(default)]
196 pub resumable: Option<bool>,
197 #[serde(default, deserialize_with = "string_or_number")]
201 pub last_consumed_client_message_index: Option<String>,
202}
203
204fn string_or_number<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Option<String>, D::Error> {
206 Ok(match Option::<serde_json::Value>::deserialize(d)? {
207 Some(serde_json::Value::String(s)) => Some(s),
208 Some(serde_json::Value::Number(n)) => Some(n.to_string()),
209 _ => None,
210 })
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
215#[serde(rename_all = "camelCase")]
216pub struct VoiceActivityMessage {
217 pub voice_activity: VoiceActivityPayload,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223#[serde(rename_all = "camelCase")]
224pub struct VoiceActivityPayload {
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub voice_activity_type: Option<VoiceActivityType>,
228 #[serde(default, skip_serializing_if = "Option::is_none")]
231 pub audio_offset: Option<String>,
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
241#[non_exhaustive]
242pub enum VoiceActivityType {
243 #[serde(rename = "ACTIVITY_START", alias = "VOICE_ACTIVITY_START")]
245 VoiceActivityStart,
246 #[serde(rename = "ACTIVITY_END", alias = "VOICE_ACTIVITY_END")]
248 VoiceActivityEnd,
249 #[serde(rename = "TYPE_UNSPECIFIED", other)]
251 Unspecified,
252}
253
254#[derive(Debug, Clone, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct ServerMessageWrapper {
258 #[serde(default)]
260 pub usage_metadata: Option<UsageMetadata>,
261}
262
263#[derive(Debug, Clone)]
269pub enum ServerMessage {
270 SetupComplete(SetupCompleteMessage),
272 ServerContent(Box<ServerContentMessage>),
274 ToolCall(ToolCallMessage),
276 ToolCallCancellation(ToolCallCancellationMessage),
278 GoAway(GoAwayMessage),
280 SessionResumptionUpdate(SessionResumptionUpdateMessage),
282 VoiceActivity(VoiceActivityMessage),
284 Unknown(serde_json::Value),
286}
287
288#[derive(Deserialize)]
293#[serde(rename_all = "camelCase")]
294struct RawServerMessage {
295 setup_complete: Option<SetupCompletePayload>,
296 server_content: Option<ServerContentPayload>,
297 usage_metadata: Option<UsageMetadata>,
298 tool_call: Option<ToolCallPayload>,
299 tool_call_cancellation: Option<ToolCallCancellationPayload>,
300 go_away: Option<GoAwayPayload>,
301 session_resumption_update: Option<SessionResumptionUpdatePayload>,
302 voice_activity: Option<VoiceActivityPayload>,
303}
304
305impl ServerMessage {
306 pub fn parse(text: &str) -> Result<Self, serde_json::Error> {
312 let raw: RawServerMessage = match serde_json::from_str(text) {
313 Ok(raw) => raw,
314 Err(e) => {
318 return if text.trim_start().starts_with('{') {
319 Err(e)
320 } else {
321 serde_json::from_str::<serde_json::Value>(text).map(ServerMessage::Unknown)
322 };
323 }
324 };
325
326 if let Some(setup_complete) = raw.setup_complete {
327 Ok(ServerMessage::SetupComplete(SetupCompleteMessage {
328 setup_complete,
329 }))
330 } else if let Some(tool_call_cancellation) = raw.tool_call_cancellation {
331 Ok(ServerMessage::ToolCallCancellation(
332 ToolCallCancellationMessage {
333 tool_call_cancellation,
334 },
335 ))
336 } else if let Some(tool_call) = raw.tool_call {
337 Ok(ServerMessage::ToolCall(ToolCallMessage { tool_call }))
338 } else if let Some(server_content) = raw.server_content {
339 Ok(ServerMessage::ServerContent(Box::new(
340 ServerContentMessage {
341 server_content,
342 usage_metadata: raw.usage_metadata,
343 },
344 )))
345 } else if let Some(go_away) = raw.go_away {
346 Ok(ServerMessage::GoAway(GoAwayMessage { go_away }))
347 } else if let Some(session_resumption_update) = raw.session_resumption_update {
348 Ok(ServerMessage::SessionResumptionUpdate(
349 SessionResumptionUpdateMessage {
350 session_resumption_update,
351 },
352 ))
353 } else if let Some(voice_activity) = raw.voice_activity {
354 Ok(ServerMessage::VoiceActivity(VoiceActivityMessage {
355 voice_activity,
356 }))
357 } else {
358 serde_json::from_str::<serde_json::Value>(text).map(ServerMessage::Unknown)
360 }
361 }
362}