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}
82
83#[derive(Debug, Clone, Deserialize)]
85pub struct TranscriptionPayload {
86 #[serde(default)]
88 pub text: Option<String>,
89}
90
91#[derive(Debug, Clone, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct ToolCallMessage {
95 pub tool_call: ToolCallPayload,
97}
98
99#[derive(Debug, Clone, Deserialize)]
101#[serde(rename_all = "camelCase")]
102pub struct ToolCallPayload {
103 pub function_calls: Vec<FunctionCall>,
105}
106
107#[derive(Debug, Clone, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct ToolCallCancellationMessage {
111 pub tool_call_cancellation: ToolCallCancellationPayload,
113}
114
115#[derive(Debug, Clone, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct ToolCallCancellationPayload {
119 pub ids: Vec<String>,
121}
122
123#[derive(Debug, Clone, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct GoAwayMessage {
127 pub go_away: GoAwayPayload,
129}
130
131#[derive(Debug, Clone, Deserialize)]
133#[serde(rename_all = "camelCase")]
134pub struct GoAwayPayload {
135 #[serde(default, deserialize_with = "proto_duration::deserialize_opt")]
139 pub time_left: Option<std::time::Duration>,
140}
141
142mod proto_duration {
144 use serde::{Deserialize, Deserializer};
145 use std::time::Duration;
146
147 pub(super) fn parse(s: &str) -> Option<Duration> {
148 let secs: f64 = s.trim().strip_suffix('s')?.parse().ok()?;
149 (secs.is_finite() && secs >= 0.0).then(|| Duration::from_secs_f64(secs))
150 }
151
152 pub(super) fn deserialize_opt<'de, D: Deserializer<'de>>(
153 d: D,
154 ) -> Result<Option<Duration>, D::Error> {
155 Ok(Option::<String>::deserialize(d)?.as_deref().and_then(parse))
156 }
157
158 #[cfg(test)]
159 mod tests {
160 use super::*;
161
162 #[test]
163 fn parses_proto_duration_strings() {
164 assert_eq!(parse("30s"), Some(Duration::from_secs(30)));
165 assert_eq!(parse("2.5s"), Some(Duration::from_millis(2500)));
166 assert_eq!(parse("0s"), Some(Duration::ZERO));
167 assert_eq!(parse("30"), None);
168 assert_eq!(parse("-1s"), None);
169 assert_eq!(parse("soon"), None);
170 }
171 }
172}
173
174#[derive(Debug, Clone, Deserialize)]
176#[serde(rename_all = "camelCase")]
177pub struct SessionResumptionUpdateMessage {
178 pub session_resumption_update: SessionResumptionUpdatePayload,
180}
181
182#[derive(Debug, Clone, Deserialize)]
184#[serde(rename_all = "camelCase")]
185pub struct SessionResumptionUpdatePayload {
186 #[serde(default)]
188 pub new_handle: Option<String>,
189 #[serde(default)]
191 pub resumable: Option<bool>,
192 #[serde(default)]
194 pub last_consumed_client_message_index: Option<String>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct VoiceActivityMessage {
201 pub voice_activity: VoiceActivityPayload,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct VoiceActivityPayload {
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub voice_activity_type: Option<VoiceActivityType>,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
216pub enum VoiceActivityType {
217 #[serde(rename = "VOICE_ACTIVITY_START")]
219 VoiceActivityStart,
220 #[serde(rename = "VOICE_ACTIVITY_END")]
222 VoiceActivityEnd,
223}
224
225#[derive(Debug, Clone, Deserialize)]
227#[serde(rename_all = "camelCase")]
228pub struct ServerMessageWrapper {
229 #[serde(default)]
231 pub usage_metadata: Option<UsageMetadata>,
232}
233
234#[derive(Debug, Clone)]
240pub enum ServerMessage {
241 SetupComplete(SetupCompleteMessage),
243 ServerContent(Box<ServerContentMessage>),
245 ToolCall(ToolCallMessage),
247 ToolCallCancellation(ToolCallCancellationMessage),
249 GoAway(GoAwayMessage),
251 SessionResumptionUpdate(SessionResumptionUpdateMessage),
253 VoiceActivity(VoiceActivityMessage),
255 Unknown(serde_json::Value),
257}
258
259#[derive(Deserialize)]
264#[serde(rename_all = "camelCase")]
265struct RawServerMessage {
266 setup_complete: Option<SetupCompletePayload>,
267 server_content: Option<ServerContentPayload>,
268 usage_metadata: Option<UsageMetadata>,
269 tool_call: Option<ToolCallPayload>,
270 tool_call_cancellation: Option<ToolCallCancellationPayload>,
271 go_away: Option<GoAwayPayload>,
272 session_resumption_update: Option<SessionResumptionUpdatePayload>,
273 voice_activity: Option<VoiceActivityPayload>,
274}
275
276impl ServerMessage {
277 pub fn parse(text: &str) -> Result<Self, serde_json::Error> {
283 let raw: RawServerMessage = match serde_json::from_str(text) {
284 Ok(raw) => raw,
285 Err(e) => {
289 return if text.trim_start().starts_with('{') {
290 Err(e)
291 } else {
292 serde_json::from_str::<serde_json::Value>(text).map(ServerMessage::Unknown)
293 };
294 }
295 };
296
297 if let Some(setup_complete) = raw.setup_complete {
298 Ok(ServerMessage::SetupComplete(SetupCompleteMessage {
299 setup_complete,
300 }))
301 } else if let Some(tool_call_cancellation) = raw.tool_call_cancellation {
302 Ok(ServerMessage::ToolCallCancellation(
303 ToolCallCancellationMessage {
304 tool_call_cancellation,
305 },
306 ))
307 } else if let Some(tool_call) = raw.tool_call {
308 Ok(ServerMessage::ToolCall(ToolCallMessage { tool_call }))
309 } else if let Some(server_content) = raw.server_content {
310 Ok(ServerMessage::ServerContent(Box::new(
311 ServerContentMessage {
312 server_content,
313 usage_metadata: raw.usage_metadata,
314 },
315 )))
316 } else if let Some(go_away) = raw.go_away {
317 Ok(ServerMessage::GoAway(GoAwayMessage { go_away }))
318 } else if let Some(session_resumption_update) = raw.session_resumption_update {
319 Ok(ServerMessage::SessionResumptionUpdate(
320 SessionResumptionUpdateMessage {
321 session_resumption_update,
322 },
323 ))
324 } else if let Some(voice_activity) = raw.voice_activity {
325 Ok(ServerMessage::VoiceActivity(VoiceActivityMessage {
326 voice_activity,
327 }))
328 } else {
329 serde_json::from_str::<serde_json::Value>(text).map(ServerMessage::Unknown)
331 }
332 }
333}