1use reqwest::Url;
2use serde::{Deserialize, Serialize};
3use time::OffsetDateTime;
4
5use crate::{
6 safety::{SafetyRating, SafetySetting},
7 Content, Modality, Part,
8};
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
13pub enum FinishReason {
14 FinishReasonUnspecified,
16 Stop,
18 MaxTokens,
20 Safety,
22 Recitation,
24 Language,
26 Other,
28 Blocklist,
30 ProhibitedContent,
32 Spii,
34 MalformedFunctionCall,
36 ImageSafety,
38 UnexpectedToolCall,
40 TooManyToolCalls,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
46#[serde(rename_all = "camelCase")]
47pub struct CitationMetadata {
48 pub citation_sources: Vec<CitationSource>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
54#[serde(rename_all = "camelCase")]
55pub struct CitationSource {
56 pub uri: Option<String>,
58 pub title: Option<String>,
60 pub start_index: Option<i32>,
62 pub end_index: Option<i32>,
64 pub license: Option<String>,
66 #[serde(default, with = "time::serde::rfc3339::option")]
68 pub publication_date: Option<OffsetDateTime>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
73#[serde(rename_all = "camelCase")]
74pub struct Candidate {
75 #[serde(default)]
77 pub content: Content,
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub safety_ratings: Option<Vec<SafetyRating>>,
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub citation_metadata: Option<CitationMetadata>,
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub grounding_metadata: Option<GroundingMetadata>,
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub finish_reason: Option<FinishReason>,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub index: Option<i32>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
97#[serde(rename_all = "camelCase")]
98pub struct UsageMetadata {
99 #[serde(skip_serializing_if = "Option::is_none")]
101 pub prompt_token_count: Option<i32>,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub candidates_token_count: Option<i32>,
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub total_token_count: Option<i32>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub thoughts_token_count: Option<i32>,
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub prompt_tokens_details: Option<Vec<PromptTokenDetails>>,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub cached_content_token_count: Option<i32>,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub cache_tokens_details: Option<Vec<PromptTokenDetails>>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124#[serde(rename_all = "camelCase")]
125pub struct PromptTokenDetails {
126 pub modality: Modality,
128 pub token_count: i32,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
134#[serde(rename_all = "camelCase")]
135pub struct GroundingMetadata {
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub grounding_chunks: Option<Vec<GroundingChunk>>,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub grounding_supports: Option<Vec<GroundingSupport>>,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub web_search_queries: Option<Vec<String>>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub google_maps_widget_context_token: Option<String>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
152#[serde(rename_all = "camelCase")]
153pub struct GroundingChunk {
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub maps: Option<MapsGroundingChunk>,
157 #[serde(skip_serializing_if = "Option::is_none")]
159 pub web: Option<WebGroundingChunk>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
164#[serde(rename_all = "camelCase")]
165pub struct MapsGroundingChunk {
166 pub uri: Url,
168 pub title: String,
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub place_id: Option<String>,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
177#[serde(rename_all = "camelCase")]
178pub struct WebGroundingChunk {
179 pub uri: Url,
181 pub title: String,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
187#[serde(rename_all = "camelCase")]
188pub struct GroundingSupport {
189 pub segment: GroundingSegment,
191 pub grounding_chunk_indices: Vec<u32>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
197#[serde(rename_all = "camelCase")]
198pub struct GroundingSegment {
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub start_index: Option<u32>,
202 #[serde(skip_serializing_if = "Option::is_none")]
204 pub end_index: Option<u32>,
205 #[serde(skip_serializing_if = "Option::is_none")]
207 pub text: Option<String>,
208}
209
210#[deprecated(
212 since = "1.8.0",
213 note = "Use crate::interactions::Interaction instead. See migration guide: interactions-api/migration-plan.md"
214)]
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
216#[serde(rename_all = "camelCase")]
217pub struct GenerationResponse {
218 #[serde(default, skip_serializing_if = "Vec::is_empty")]
220 pub candidates: Vec<Candidate>,
221 #[serde(skip_serializing_if = "Option::is_none")]
223 pub prompt_feedback: Option<PromptFeedback>,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub usage_metadata: Option<UsageMetadata>,
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub model_version: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub response_id: Option<String>,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
237#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
238pub enum BlockReason {
239 BlockReasonUnspecified,
241 Safety,
243 Other,
245 Blocklist,
247 ProhibitedContent,
249 ImageSafety,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
255#[serde(rename_all = "camelCase")]
256pub struct PromptFeedback {
257 #[serde(default, skip_serializing_if = "Vec::is_empty")]
259 pub safety_ratings: Vec<SafetyRating>,
260 #[serde(skip_serializing_if = "Option::is_none")]
262 pub block_reason: Option<BlockReason>,
263}
264
265impl GenerationResponse {
266 pub fn text(&self) -> String {
268 self.candidates
269 .first()
270 .and_then(|c| {
271 c.content.parts.as_ref().and_then(|parts| {
272 parts.first().and_then(|p| match p {
273 Part::Text {
274 text,
275 thought: _,
276 thought_signature: _,
277 } => Some(text.clone()),
278 _ => None,
279 })
280 })
281 })
282 .unwrap_or_default()
283 }
284
285 pub fn finish_reason(&self) -> Option<FinishReason> {
287 self.candidates
288 .first()
289 .and_then(|c| c.finish_reason.clone())
290 }
291
292 pub fn function_calls(&self) -> Vec<&crate::tools::FunctionCall> {
294 self.candidates
295 .iter()
296 .flat_map(|c| {
297 c.content
298 .parts
299 .as_ref()
300 .map(|parts| {
301 parts
302 .iter()
303 .filter_map(|p| match p {
304 Part::FunctionCall {
305 function_call,
306 thought_signature: _,
307 } => Some(function_call),
308 _ => None,
309 })
310 .collect::<Vec<_>>()
311 })
312 .unwrap_or_default()
313 })
314 .collect()
315 }
316
317 pub fn function_calls_with_thoughts(
319 &self,
320 ) -> Vec<(&crate::tools::FunctionCall, Option<&String>)> {
321 self.candidates
322 .iter()
323 .flat_map(|c| {
324 c.content
325 .parts
326 .as_ref()
327 .map(|parts| {
328 parts
329 .iter()
330 .filter_map(|p| match p {
331 Part::FunctionCall {
332 function_call,
333 thought_signature,
334 } => Some((function_call, thought_signature.as_ref())),
335 _ => None,
336 })
337 .collect::<Vec<_>>()
338 })
339 .unwrap_or_default()
340 })
341 .collect()
342 }
343
344 pub fn thoughts(&self) -> Vec<String> {
346 self.candidates
347 .iter()
348 .flat_map(|c| {
349 c.content
350 .parts
351 .as_ref()
352 .map(|parts| {
353 parts
354 .iter()
355 .filter_map(|p| match p {
356 Part::Text {
357 text,
358 thought: Some(true),
359 thought_signature: _,
360 } => Some(text.clone()),
361 _ => None,
362 })
363 .collect::<Vec<_>>()
364 })
365 .unwrap_or_default()
366 })
367 .collect()
368 }
369
370 pub fn all_text(&self) -> Vec<(String, bool)> {
372 self.candidates
373 .iter()
374 .flat_map(|c| {
375 c.content
376 .parts
377 .as_ref()
378 .map(|parts| {
379 parts
380 .iter()
381 .filter_map(|p| match p {
382 Part::Text {
383 text,
384 thought,
385 thought_signature: _,
386 } => Some((text.clone(), thought.unwrap_or(false))),
387 _ => None,
388 })
389 .collect::<Vec<_>>()
390 })
391 .unwrap_or_default()
392 })
393 .collect()
394 }
395
396 pub fn text_with_thoughts(&self) -> Vec<(String, bool, Option<&String>)> {
398 self.candidates
399 .iter()
400 .flat_map(|c| {
401 c.content
402 .parts
403 .as_ref()
404 .map(|parts| {
405 parts
406 .iter()
407 .filter_map(|p| match p {
408 Part::Text {
409 text,
410 thought,
411 thought_signature,
412 } => Some((
413 text.clone(),
414 thought.unwrap_or(false),
415 thought_signature.as_ref(),
416 )),
417 _ => None,
418 })
419 .collect::<Vec<_>>()
420 })
421 .unwrap_or_default()
422 })
423 .collect()
424 }
425}
426
427#[deprecated(
429 since = "1.8.0",
430 note = "Use crate::interactions::CreateInteractionRequest instead. See migration guide: interactions-api/migration-plan.md"
431)]
432#[derive(Debug, Clone, Serialize, Deserialize)]
433#[serde(rename_all = "camelCase")]
434pub struct GenerateContentRequest {
435 pub contents: Vec<Content>,
437 #[serde(skip_serializing_if = "Option::is_none")]
439 pub generation_config: Option<GenerationConfig>,
440 #[serde(skip_serializing_if = "Option::is_none")]
442 pub safety_settings: Option<Vec<SafetySetting>>,
443 #[serde(skip_serializing_if = "Option::is_none")]
445 pub tools: Option<Vec<crate::tools::Tool>>,
446 #[serde(skip_serializing_if = "Option::is_none")]
448 pub tool_config: Option<crate::tools::ToolConfig>,
449 #[serde(skip_serializing_if = "Option::is_none")]
451 pub system_instruction: Option<Content>,
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub cached_content: Option<String>,
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
461#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
462pub enum ThinkingLevel {
463 ThinkingLevelUnspecified,
465 Minimal,
467 Low,
469 Medium,
471 High,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
480#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
481pub enum MediaResolutionLevel {
482 MediaResolutionUnspecified,
484 MediaResolutionLow,
486 MediaResolutionMedium,
488 MediaResolutionHigh,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
495pub struct MediaResolution {
496 pub level: MediaResolutionLevel,
498}
499
500#[derive(Debug, Clone, Serialize, Deserialize)]
505#[serde(rename_all = "camelCase")]
506pub struct ThinkingConfig {
507 #[serde(skip_serializing_if = "Option::is_none")]
518 pub thinking_budget: Option<i32>,
519
520 #[serde(skip_serializing_if = "Option::is_none")]
525 pub include_thoughts: Option<bool>,
526
527 #[serde(skip_serializing_if = "Option::is_none")]
531 pub thinking_level: Option<ThinkingLevel>,
532}
533
534impl ThinkingConfig {
535 pub fn new() -> Self {
537 Self {
538 thinking_budget: None,
539 include_thoughts: None,
540 thinking_level: None,
541 }
542 }
543
544 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
546 self.thinking_budget = Some(budget);
547 self
548 }
549
550 pub fn with_dynamic_thinking(mut self) -> Self {
552 self.thinking_budget = Some(-1);
553 self
554 }
555
556 pub fn with_thoughts_included(mut self, include: bool) -> Self {
558 self.include_thoughts = Some(include);
559 self
560 }
561
562 pub fn with_thinking_level(mut self, level: ThinkingLevel) -> Self {
564 self.thinking_level = Some(level);
565 self
566 }
567
568 pub fn dynamic_thinking() -> Self {
570 Self {
571 thinking_budget: Some(-1),
572 include_thoughts: Some(true),
573 thinking_level: None,
574 }
575 }
576}
577
578impl Default for ThinkingConfig {
579 fn default() -> Self {
580 Self::new()
581 }
582}
583
584#[deprecated(
586 since = "1.8.0",
587 note = "Use crate::interactions::InteractionGenerationConfig instead"
588)]
589#[derive(Debug, Default, Clone, Serialize, Deserialize)]
590#[serde(rename_all = "camelCase")]
591pub struct GenerationConfig {
592 #[serde(skip_serializing_if = "Option::is_none")]
597 pub temperature: Option<f32>,
598
599 #[serde(skip_serializing_if = "Option::is_none")]
605 pub top_p: Option<f32>,
606
607 #[serde(skip_serializing_if = "Option::is_none")]
612 pub top_k: Option<i32>,
613
614 #[serde(skip_serializing_if = "Option::is_none")]
620 pub seed: Option<i32>,
621
622 #[serde(skip_serializing_if = "Option::is_none")]
626 pub max_output_tokens: Option<i32>,
627
628 #[serde(skip_serializing_if = "Option::is_none")]
632 pub candidate_count: Option<i32>,
633
634 #[serde(skip_serializing_if = "Option::is_none")]
638 pub stop_sequences: Option<Vec<String>>,
639
640 #[serde(skip_serializing_if = "Option::is_none")]
644 pub response_mime_type: Option<String>,
645 #[serde(skip_serializing_if = "Option::is_none")]
649 pub response_schema: Option<serde_json::Value>,
650
651 #[serde(skip_serializing_if = "Option::is_none")]
655 pub response_json_schema: Option<serde_json::Value>,
656
657 #[serde(skip_serializing_if = "Option::is_none")]
659 pub response_modalities: Option<Vec<String>>,
660
661 #[serde(skip_serializing_if = "Option::is_none")]
664 pub image_config: Option<ImageConfig>,
665
666 #[serde(skip_serializing_if = "Option::is_none")]
668 pub speech_config: Option<SpeechConfig>,
669
670 #[serde(skip_serializing_if = "Option::is_none")]
674 pub thinking_config: Option<ThinkingConfig>,
675
676 #[serde(skip_serializing_if = "Option::is_none", rename = "media_resolution")]
680 pub media_resolution: Option<MediaResolutionLevel>,
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
685#[serde(rename_all = "camelCase")]
686pub struct CountTokensResponse {
687 pub total_tokens: u32,
689 #[serde(skip_serializing_if = "Option::is_none")]
691 pub cached_content_token_count: Option<u32>,
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
696#[serde(rename_all = "camelCase")]
697pub struct ImageConfig {
698 #[serde(skip_serializing_if = "Option::is_none")]
704 pub aspect_ratio: Option<String>,
705 #[serde(skip_serializing_if = "Option::is_none")]
708 pub image_size: Option<String>,
709}
710
711#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
713#[serde(rename_all = "camelCase")]
714pub struct SpeechConfig {
715 #[serde(skip_serializing_if = "Option::is_none")]
717 pub voice_config: Option<VoiceConfig>,
718 #[serde(skip_serializing_if = "Option::is_none")]
720 pub multi_speaker_voice_config: Option<MultiSpeakerVoiceConfig>,
721}
722
723#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
725#[serde(rename_all = "camelCase")]
726pub struct VoiceConfig {
727 #[serde(skip_serializing_if = "Option::is_none")]
729 pub prebuilt_voice_config: Option<PrebuiltVoiceConfig>,
730}
731
732#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
734#[serde(rename_all = "camelCase")]
735pub struct PrebuiltVoiceConfig {
736 pub voice_name: String,
738}
739
740#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
742#[serde(rename_all = "camelCase")]
743pub struct MultiSpeakerVoiceConfig {
744 pub speaker_voice_configs: Vec<SpeakerVoiceConfig>,
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
750#[serde(rename_all = "camelCase")]
751pub struct SpeakerVoiceConfig {
752 pub speaker: String,
754 pub voice_config: VoiceConfig,
756}
757
758impl SpeechConfig {
759 pub fn single_voice(voice_name: impl Into<String>) -> Self {
761 Self {
762 voice_config: Some(VoiceConfig {
763 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
764 voice_name: voice_name.into(),
765 }),
766 }),
767 multi_speaker_voice_config: None,
768 }
769 }
770
771 pub fn multi_speaker(speakers: Vec<SpeakerVoiceConfig>) -> Self {
773 Self {
774 voice_config: None,
775 multi_speaker_voice_config: Some(MultiSpeakerVoiceConfig {
776 speaker_voice_configs: speakers,
777 }),
778 }
779 }
780}
781
782impl SpeakerVoiceConfig {
783 pub fn new(speaker: impl Into<String>, voice_name: impl Into<String>) -> Self {
785 Self {
786 speaker: speaker.into(),
787 voice_config: VoiceConfig {
788 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
789 voice_name: voice_name.into(),
790 }),
791 },
792 }
793 }
794}