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#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
212#[serde(rename_all = "camelCase")]
213pub struct GenerationResponse {
214 #[serde(default, skip_serializing_if = "Vec::is_empty")]
216 pub candidates: Vec<Candidate>,
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub prompt_feedback: Option<PromptFeedback>,
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub usage_metadata: Option<UsageMetadata>,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub model_version: Option<String>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub response_id: Option<String>,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
233#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
234pub enum BlockReason {
235 BlockReasonUnspecified,
237 Safety,
239 Other,
241 Blocklist,
243 ProhibitedContent,
245 ImageSafety,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
251#[serde(rename_all = "camelCase")]
252pub struct PromptFeedback {
253 #[serde(default, skip_serializing_if = "Vec::is_empty")]
255 pub safety_ratings: Vec<SafetyRating>,
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub block_reason: Option<BlockReason>,
259}
260
261impl GenerationResponse {
262 pub fn text(&self) -> String {
264 self.candidates
265 .first()
266 .and_then(|c| {
267 c.content.parts.as_ref().and_then(|parts| {
268 parts.first().and_then(|p| match p {
269 Part::Text {
270 text,
271 thought: _,
272 thought_signature: _,
273 } => Some(text.clone()),
274 _ => None,
275 })
276 })
277 })
278 .unwrap_or_default()
279 }
280
281 pub fn function_calls(&self) -> Vec<&crate::tools::FunctionCall> {
283 self.candidates
284 .iter()
285 .flat_map(|c| {
286 c.content
287 .parts
288 .as_ref()
289 .map(|parts| {
290 parts
291 .iter()
292 .filter_map(|p| match p {
293 Part::FunctionCall {
294 function_call,
295 thought_signature: _,
296 } => Some(function_call),
297 _ => None,
298 })
299 .collect::<Vec<_>>()
300 })
301 .unwrap_or_default()
302 })
303 .collect()
304 }
305
306 pub fn function_calls_with_thoughts(
308 &self,
309 ) -> Vec<(&crate::tools::FunctionCall, Option<&String>)> {
310 self.candidates
311 .iter()
312 .flat_map(|c| {
313 c.content
314 .parts
315 .as_ref()
316 .map(|parts| {
317 parts
318 .iter()
319 .filter_map(|p| match p {
320 Part::FunctionCall {
321 function_call,
322 thought_signature,
323 } => Some((function_call, thought_signature.as_ref())),
324 _ => None,
325 })
326 .collect::<Vec<_>>()
327 })
328 .unwrap_or_default()
329 })
330 .collect()
331 }
332
333 pub fn thoughts(&self) -> Vec<String> {
335 self.candidates
336 .iter()
337 .flat_map(|c| {
338 c.content
339 .parts
340 .as_ref()
341 .map(|parts| {
342 parts
343 .iter()
344 .filter_map(|p| match p {
345 Part::Text {
346 text,
347 thought: Some(true),
348 thought_signature: _,
349 } => Some(text.clone()),
350 _ => None,
351 })
352 .collect::<Vec<_>>()
353 })
354 .unwrap_or_default()
355 })
356 .collect()
357 }
358
359 pub fn all_text(&self) -> Vec<(String, bool)> {
361 self.candidates
362 .iter()
363 .flat_map(|c| {
364 c.content
365 .parts
366 .as_ref()
367 .map(|parts| {
368 parts
369 .iter()
370 .filter_map(|p| match p {
371 Part::Text {
372 text,
373 thought,
374 thought_signature: _,
375 } => Some((text.clone(), thought.unwrap_or(false))),
376 _ => None,
377 })
378 .collect::<Vec<_>>()
379 })
380 .unwrap_or_default()
381 })
382 .collect()
383 }
384
385 pub fn text_with_thoughts(&self) -> Vec<(String, bool, Option<&String>)> {
387 self.candidates
388 .iter()
389 .flat_map(|c| {
390 c.content
391 .parts
392 .as_ref()
393 .map(|parts| {
394 parts
395 .iter()
396 .filter_map(|p| match p {
397 Part::Text {
398 text,
399 thought,
400 thought_signature,
401 } => Some((
402 text.clone(),
403 thought.unwrap_or(false),
404 thought_signature.as_ref(),
405 )),
406 _ => None,
407 })
408 .collect::<Vec<_>>()
409 })
410 .unwrap_or_default()
411 })
412 .collect()
413 }
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418#[serde(rename_all = "camelCase")]
419pub struct GenerateContentRequest {
420 pub contents: Vec<Content>,
422 #[serde(skip_serializing_if = "Option::is_none")]
424 pub generation_config: Option<GenerationConfig>,
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub safety_settings: Option<Vec<SafetySetting>>,
428 #[serde(skip_serializing_if = "Option::is_none")]
430 pub tools: Option<Vec<crate::tools::Tool>>,
431 #[serde(skip_serializing_if = "Option::is_none")]
433 pub tool_config: Option<crate::tools::ToolConfig>,
434 #[serde(skip_serializing_if = "Option::is_none")]
436 pub system_instruction: Option<Content>,
437 #[serde(skip_serializing_if = "Option::is_none")]
439 pub cached_content: Option<String>,
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
446#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
447pub enum ThinkingLevel {
448 ThinkingLevelUnspecified,
450 Low,
452 High,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
461#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
462pub enum MediaResolutionLevel {
463 MediaResolutionUnspecified,
465 MediaResolutionLow,
467 MediaResolutionMedium,
469 MediaResolutionHigh,
471}
472
473#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
476pub struct MediaResolution {
477 pub level: MediaResolutionLevel,
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize)]
486#[serde(rename_all = "camelCase")]
487pub struct ThinkingConfig {
488 #[serde(skip_serializing_if = "Option::is_none")]
499 pub thinking_budget: Option<i32>,
500
501 #[serde(skip_serializing_if = "Option::is_none")]
506 pub include_thoughts: Option<bool>,
507
508 #[serde(skip_serializing_if = "Option::is_none")]
512 pub thinking_level: Option<ThinkingLevel>,
513}
514
515impl ThinkingConfig {
516 pub fn new() -> Self {
518 Self {
519 thinking_budget: None,
520 include_thoughts: None,
521 thinking_level: None,
522 }
523 }
524
525 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
527 self.thinking_budget = Some(budget);
528 self
529 }
530
531 pub fn with_dynamic_thinking(mut self) -> Self {
533 self.thinking_budget = Some(-1);
534 self
535 }
536
537 pub fn with_thoughts_included(mut self, include: bool) -> Self {
539 self.include_thoughts = Some(include);
540 self
541 }
542
543 pub fn with_thinking_level(mut self, level: ThinkingLevel) -> Self {
545 self.thinking_level = Some(level);
546 self
547 }
548
549 pub fn dynamic_thinking() -> Self {
551 Self {
552 thinking_budget: Some(-1),
553 include_thoughts: Some(true),
554 thinking_level: None,
555 }
556 }
557}
558
559impl Default for ThinkingConfig {
560 fn default() -> Self {
561 Self::new()
562 }
563}
564
565#[derive(Debug, Default, Clone, Serialize, Deserialize)]
567#[serde(rename_all = "camelCase")]
568pub struct GenerationConfig {
569 #[serde(skip_serializing_if = "Option::is_none")]
574 pub temperature: Option<f32>,
575
576 #[serde(skip_serializing_if = "Option::is_none")]
582 pub top_p: Option<f32>,
583
584 #[serde(skip_serializing_if = "Option::is_none")]
589 pub top_k: Option<i32>,
590
591 #[serde(skip_serializing_if = "Option::is_none")]
597 pub seed: Option<i32>,
598
599 #[serde(skip_serializing_if = "Option::is_none")]
603 pub max_output_tokens: Option<i32>,
604
605 #[serde(skip_serializing_if = "Option::is_none")]
609 pub candidate_count: Option<i32>,
610
611 #[serde(skip_serializing_if = "Option::is_none")]
615 pub stop_sequences: Option<Vec<String>>,
616
617 #[serde(skip_serializing_if = "Option::is_none")]
621 pub response_mime_type: Option<String>,
622 #[serde(skip_serializing_if = "Option::is_none")]
626 pub response_schema: Option<serde_json::Value>,
627
628 #[serde(skip_serializing_if = "Option::is_none")]
630 pub response_modalities: Option<Vec<String>>,
631
632 #[serde(skip_serializing_if = "Option::is_none")]
635 pub image_config: Option<ImageConfig>,
636
637 #[serde(skip_serializing_if = "Option::is_none")]
639 pub speech_config: Option<SpeechConfig>,
640
641 #[serde(skip_serializing_if = "Option::is_none")]
645 pub thinking_config: Option<ThinkingConfig>,
646
647 #[serde(skip_serializing_if = "Option::is_none", rename = "media_resolution")]
651 pub media_resolution: Option<MediaResolutionLevel>,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
656#[serde(rename_all = "camelCase")]
657pub struct CountTokensResponse {
658 pub total_tokens: u32,
660 #[serde(skip_serializing_if = "Option::is_none")]
662 pub cached_content_token_count: Option<u32>,
663}
664
665#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
667#[serde(rename_all = "camelCase")]
668pub struct ImageConfig {
669 #[serde(skip_serializing_if = "Option::is_none")]
675 pub aspect_ratio: Option<String>,
676 #[serde(skip_serializing_if = "Option::is_none")]
679 pub image_size: Option<String>,
680}
681
682#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
684#[serde(rename_all = "camelCase")]
685pub struct SpeechConfig {
686 #[serde(skip_serializing_if = "Option::is_none")]
688 pub voice_config: Option<VoiceConfig>,
689 #[serde(skip_serializing_if = "Option::is_none")]
691 pub multi_speaker_voice_config: Option<MultiSpeakerVoiceConfig>,
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
696#[serde(rename_all = "camelCase")]
697pub struct VoiceConfig {
698 #[serde(skip_serializing_if = "Option::is_none")]
700 pub prebuilt_voice_config: Option<PrebuiltVoiceConfig>,
701}
702
703#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
705#[serde(rename_all = "camelCase")]
706pub struct PrebuiltVoiceConfig {
707 pub voice_name: String,
709}
710
711#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
713#[serde(rename_all = "camelCase")]
714pub struct MultiSpeakerVoiceConfig {
715 pub speaker_voice_configs: Vec<SpeakerVoiceConfig>,
717}
718
719#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
721#[serde(rename_all = "camelCase")]
722pub struct SpeakerVoiceConfig {
723 pub speaker: String,
725 pub voice_config: VoiceConfig,
727}
728
729impl SpeechConfig {
730 pub fn single_voice(voice_name: impl Into<String>) -> Self {
732 Self {
733 voice_config: Some(VoiceConfig {
734 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
735 voice_name: voice_name.into(),
736 }),
737 }),
738 multi_speaker_voice_config: None,
739 }
740 }
741
742 pub fn multi_speaker(speakers: Vec<SpeakerVoiceConfig>) -> Self {
744 Self {
745 voice_config: None,
746 multi_speaker_voice_config: Some(MultiSpeakerVoiceConfig {
747 speaker_voice_configs: speakers,
748 }),
749 }
750 }
751}
752
753impl SpeakerVoiceConfig {
754 pub fn new(speaker: impl Into<String>, voice_name: impl Into<String>) -> Self {
756 Self {
757 speaker: speaker.into(),
758 voice_config: VoiceConfig {
759 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
760 voice_name: voice_name.into(),
761 }),
762 },
763 }
764 }
765}