rust-genai-types 0.3.0

Shared types for the Rust Gemini SDK
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
use serde::{Deserialize, Serialize};

use crate::config::{GenerationConfig, SpeechConfig, ThinkingConfig};
use crate::content::{Blob, Content, FunctionCall, FunctionResponse};
use crate::enums::{
    ActivityHandling, EndSensitivity, MediaResolution, Modality, StartSensitivity,
    TurnCompleteReason, TurnCoverage, VadSignalType,
};
use crate::grounding::GroundingMetadata;
use crate::http::HttpOptions;
use crate::response::{UrlContextMetadata, UsageMetadata};
use crate::tool::Tool;

/// Live 客户端初始化设置。
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveClientSetup {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generation_config: Option<GenerationConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system_instruction: Option<Content>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<Vec<Tool>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub realtime_input_config: Option<RealtimeInputConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_resumption: Option<SessionResumptionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub context_window_compression: Option<ContextWindowCompressionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub input_audio_transcription: Option<AudioTranscriptionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_audio_transcription: Option<AudioTranscriptionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub proactivity: Option<ProactivityConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub explicit_vad_signal: Option<bool>,
}

/// 兼容旧命名的 Live 会话设置。
pub type LiveSetup = LiveClientSetup;

/// Sent in response to a `LiveGenerateContentSetup` message from the client.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveServerSetupComplete {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_id: Option<String>,
}

/// Audio transcription in server content.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transcription {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub finished: Option<bool>,
}

/// Incremental server update generated by the model in response to client messages.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveServerContent {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model_turn: Option<Content>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub turn_complete: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub interrupted: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub grounding_metadata: Option<GroundingMetadata>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generation_complete: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub input_transcription: Option<Transcription>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_transcription: Option<Transcription>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url_context_metadata: Option<UrlContextMetadata>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub turn_complete_reason: Option<TurnCompleteReason>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub waiting_for_input: Option<bool>,
}

/// Request for the client to execute function calls.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveServerToolCall {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub function_calls: Option<Vec<FunctionCall>>,
}

/// Notification that previously issued tool calls should be cancelled.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveServerToolCallCancellation {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ids: Option<Vec<String>>,
}

/// Server will not be able to service client soon.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveServerGoAway {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub time_left: Option<String>,
}

/// Update of the session resumption state.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveServerSessionResumptionUpdate {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub new_handle: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resumable: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub last_consumed_client_message_index: Option<String>,
}

/// Voice activity detection signal.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VoiceActivityDetectionSignal {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vad_signal_type: Option<VadSignalType>,
}

/// Response message for API call.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveServerMessage {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub setup_complete: Option<LiveServerSetupComplete>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub server_content: Option<LiveServerContent>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_call: Option<LiveServerToolCall>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_call_cancellation: Option<LiveServerToolCallCancellation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub usage_metadata: Option<UsageMetadata>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub go_away: Option<LiveServerGoAway>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_resumption_update: Option<LiveServerSessionResumptionUpdate>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub voice_activity_detection_signal: Option<VoiceActivityDetectionSignal>,
}

/// Configures automatic detection of activity.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AutomaticActivityDetection {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub disabled: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub start_of_speech_sensitivity: Option<StartSensitivity>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub end_of_speech_sensitivity: Option<EndSensitivity>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prefix_padding_ms: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub silence_duration_ms: Option<i32>,
}

/// Realtime input config.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RealtimeInputConfig {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub automatic_activity_detection: Option<AutomaticActivityDetection>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub activity_handling: Option<ActivityHandling>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub turn_coverage: Option<TurnCoverage>,
}

/// Configuration of session resumption mechanism.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionResumptionConfig {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub handle: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transparent: Option<bool>,
}

/// Context window compression config.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContextWindowCompressionConfig {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trigger_tokens: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sliding_window: Option<SlidingWindow>,
}

/// Sliding window config.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SlidingWindow {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub target_tokens: Option<String>,
}

/// The audio transcription configuration in setup.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct AudioTranscriptionConfig {}

/// Proactivity config.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProactivityConfig {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub proactive_audio: Option<bool>,
}

/// Incremental update of the current conversation delivered from the client.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveClientContent {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub turns: Option<Vec<Content>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub turn_complete: Option<bool>,
}

/// Marks the start of user activity.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ActivityStart {}

/// Marks the end of user activity.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ActivityEnd {}

/// User input that is sent in real time.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveClientRealtimeInput {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub media_chunks: Option<Vec<Blob>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio: Option<Blob>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio_stream_end: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub video: Option<Blob>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub activity_start: Option<ActivityStart>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub activity_end: Option<ActivityEnd>,
}

/// Client generated response to a `ToolCall` received from the server.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveClientToolResponse {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub function_responses: Option<Vec<FunctionResponse>>,
}

/// Parameters for sending realtime input to the live API.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveSendRealtimeInputParameters {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub media: Option<Blob>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio: Option<Blob>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub audio_stream_end: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub video: Option<Blob>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub activity_start: Option<ActivityStart>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub activity_end: Option<ActivityEnd>,
}

/// Messages sent by the client in the API call.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveClientMessage {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub setup: Option<LiveClientSetup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_content: Option<LiveClientContent>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub realtime_input: Option<LiveClientRealtimeInput>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_response: Option<LiveClientToolResponse>,
}

/// Session config for the API connection.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct LiveConnectConfig {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub http_options: Option<HttpOptions>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub generation_config: Option<GenerationConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub response_modalities: Option<Vec<Modality>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub temperature: Option<f32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub top_p: Option<f32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub top_k: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_output_tokens: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub media_resolution: Option<MediaResolution>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub seed: Option<i32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub speech_config: Option<SpeechConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thinking_config: Option<ThinkingConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub enable_affective_dialog: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system_instruction: Option<Content>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tools: Option<Vec<Tool>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_resumption: Option<SessionResumptionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub input_audio_transcription: Option<AudioTranscriptionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_audio_transcription: Option<AudioTranscriptionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub realtime_input_config: Option<RealtimeInputConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub context_window_compression: Option<ContextWindowCompressionConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub proactivity: Option<ProactivityConfig>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub explicit_vad_signal: Option<bool>,
}

/// Parameters for sending client content to the live API.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveSendClientContentParameters {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub turns: Option<Vec<Content>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub turn_complete: Option<bool>,
}

/// Parameters for sending tool responses to the live API.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LiveSendToolResponseParameters {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub function_responses: Option<Vec<FunctionResponse>>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::content::{Content, Part, Role};
    use crate::enums::{ActivityHandling, StartSensitivity, VadSignalType};

    #[test]
    fn live_server_message_roundtrip() {
        let message = LiveServerMessage {
            setup_complete: Some(LiveServerSetupComplete {
                session_id: Some("session-123".to_string()),
            }),
            server_content: Some(LiveServerContent {
                model_turn: Some(Content::from_parts(vec![Part::text("hi")], Role::Model)),
                turn_complete: Some(true),
                interrupted: None,
                grounding_metadata: None,
                generation_complete: Some(true),
                input_transcription: Some(Transcription {
                    text: Some("hello".to_string()),
                    finished: Some(true),
                }),
                output_transcription: None,
                url_context_metadata: None,
                turn_complete_reason: Some(TurnCompleteReason::NeedMoreInput),
                waiting_for_input: Some(true),
            }),
            tool_call: None,
            tool_call_cancellation: None,
            usage_metadata: None,
            go_away: Some(LiveServerGoAway {
                time_left: Some("5s".to_string()),
            }),
            session_resumption_update: Some(LiveServerSessionResumptionUpdate {
                new_handle: Some("handle-1".to_string()),
                resumable: Some(true),
                last_consumed_client_message_index: Some("42".to_string()),
            }),
            voice_activity_detection_signal: Some(VoiceActivityDetectionSignal {
                vad_signal_type: Some(VadSignalType::VadSignalTypeSos),
            }),
        };

        let json = serde_json::to_string(&message).unwrap();
        let decoded: LiveServerMessage = serde_json::from_str(&json).unwrap();
        let text = decoded
            .server_content
            .as_ref()
            .and_then(|content| content.model_turn.as_ref())
            .and_then(|content| content.first_text());
        assert_eq!(text, Some("hi"));
    }

    #[test]
    fn realtime_input_config_serializes() {
        let config = RealtimeInputConfig {
            automatic_activity_detection: Some(AutomaticActivityDetection {
                disabled: Some(false),
                start_of_speech_sensitivity: Some(StartSensitivity::StartSensitivityHigh),
                end_of_speech_sensitivity: None,
                prefix_padding_ms: Some(120),
                silence_duration_ms: Some(300),
            }),
            activity_handling: Some(ActivityHandling::StartOfActivityInterrupts),
            turn_coverage: None,
        };

        let json = serde_json::to_string(&config).unwrap();
        assert!(json.contains("automaticActivityDetection"));
        assert!(json.contains("startOfSpeechSensitivity"));
    }
}