1use serde::{Deserialize, Serialize};
2use time::OffsetDateTime;
3
4use crate::{
5 safety::{SafetyRating, SafetySetting},
6 Content, Modality, Part,
7};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
12pub enum FinishReason {
13 FinishReasonUnspecified,
15 Stop,
17 MaxTokens,
19 Safety,
21 Recitation,
23 Language,
25 Other,
27 Blocklist,
29 ProhibitedContent,
31 Spii,
33 MalformedFunctionCall,
35 ImageSafety,
37 UnexpectedToolCall,
39 TooManyToolCalls,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
45#[serde(rename_all = "camelCase")]
46pub struct CitationMetadata {
47 pub citation_sources: Vec<CitationSource>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53#[serde(rename_all = "camelCase")]
54pub struct CitationSource {
55 pub uri: Option<String>,
57 pub title: Option<String>,
59 pub start_index: Option<i32>,
61 pub end_index: Option<i32>,
63 pub license: Option<String>,
65 #[serde(default, with = "time::serde::rfc3339::option")]
67 pub publication_date: Option<OffsetDateTime>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
72#[serde(rename_all = "camelCase")]
73pub struct Candidate {
74 #[serde(default)]
76 pub content: Content,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub safety_ratings: Option<Vec<SafetyRating>>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub citation_metadata: Option<CitationMetadata>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub finish_reason: Option<FinishReason>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub index: Option<i32>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
93#[serde(rename_all = "camelCase")]
94pub struct UsageMetadata {
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub prompt_token_count: Option<i32>,
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub candidates_token_count: Option<i32>,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub total_token_count: Option<i32>,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub thoughts_token_count: Option<i32>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub prompt_tokens_details: Option<Vec<PromptTokenDetails>>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub cached_content_token_count: Option<i32>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub cache_tokens_details: Option<Vec<PromptTokenDetails>>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
120#[serde(rename_all = "camelCase")]
121pub struct PromptTokenDetails {
122 pub modality: Modality,
124 pub token_count: i32,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
130#[serde(rename_all = "camelCase")]
131pub struct GenerationResponse {
132 #[serde(default, skip_serializing_if = "Vec::is_empty")]
134 pub candidates: Vec<Candidate>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub prompt_feedback: Option<PromptFeedback>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub usage_metadata: Option<UsageMetadata>,
141 #[serde(skip_serializing_if = "Option::is_none")]
143 pub model_version: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub response_id: Option<String>,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
151#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
152pub enum BlockReason {
153 BlockReasonUnspecified,
155 Safety,
157 Other,
159 Blocklist,
161 ProhibitedContent,
163 ImageSafety,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
169#[serde(rename_all = "camelCase")]
170pub struct PromptFeedback {
171 #[serde(default, skip_serializing_if = "Vec::is_empty")]
173 pub safety_ratings: Vec<SafetyRating>,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub block_reason: Option<BlockReason>,
177}
178
179impl GenerationResponse {
180 pub fn text(&self) -> String {
182 self.candidates
183 .first()
184 .and_then(|c| {
185 c.content.parts.as_ref().and_then(|parts| {
186 parts.first().and_then(|p| match p {
187 Part::Text {
188 text,
189 thought: _,
190 thought_signature: _,
191 } => Some(text.clone()),
192 _ => None,
193 })
194 })
195 })
196 .unwrap_or_default()
197 }
198
199 pub fn function_calls(&self) -> Vec<&crate::tools::FunctionCall> {
201 self.candidates
202 .iter()
203 .flat_map(|c| {
204 c.content
205 .parts
206 .as_ref()
207 .map(|parts| {
208 parts
209 .iter()
210 .filter_map(|p| match p {
211 Part::FunctionCall {
212 function_call,
213 thought_signature: _,
214 } => Some(function_call),
215 _ => None,
216 })
217 .collect::<Vec<_>>()
218 })
219 .unwrap_or_default()
220 })
221 .collect()
222 }
223
224 pub fn function_calls_with_thoughts(
226 &self,
227 ) -> Vec<(&crate::tools::FunctionCall, Option<&String>)> {
228 self.candidates
229 .iter()
230 .flat_map(|c| {
231 c.content
232 .parts
233 .as_ref()
234 .map(|parts| {
235 parts
236 .iter()
237 .filter_map(|p| match p {
238 Part::FunctionCall {
239 function_call,
240 thought_signature,
241 } => Some((function_call, thought_signature.as_ref())),
242 _ => None,
243 })
244 .collect::<Vec<_>>()
245 })
246 .unwrap_or_default()
247 })
248 .collect()
249 }
250
251 pub fn thoughts(&self) -> Vec<String> {
253 self.candidates
254 .iter()
255 .flat_map(|c| {
256 c.content
257 .parts
258 .as_ref()
259 .map(|parts| {
260 parts
261 .iter()
262 .filter_map(|p| match p {
263 Part::Text {
264 text,
265 thought: Some(true),
266 thought_signature: _,
267 } => Some(text.clone()),
268 _ => None,
269 })
270 .collect::<Vec<_>>()
271 })
272 .unwrap_or_default()
273 })
274 .collect()
275 }
276
277 pub fn all_text(&self) -> Vec<(String, bool)> {
279 self.candidates
280 .iter()
281 .flat_map(|c| {
282 c.content
283 .parts
284 .as_ref()
285 .map(|parts| {
286 parts
287 .iter()
288 .filter_map(|p| match p {
289 Part::Text {
290 text,
291 thought,
292 thought_signature: _,
293 } => Some((text.clone(), thought.unwrap_or(false))),
294 _ => None,
295 })
296 .collect::<Vec<_>>()
297 })
298 .unwrap_or_default()
299 })
300 .collect()
301 }
302
303 pub fn text_with_thoughts(&self) -> Vec<(String, bool, Option<&String>)> {
305 self.candidates
306 .iter()
307 .flat_map(|c| {
308 c.content
309 .parts
310 .as_ref()
311 .map(|parts| {
312 parts
313 .iter()
314 .filter_map(|p| match p {
315 Part::Text {
316 text,
317 thought,
318 thought_signature,
319 } => Some((
320 text.clone(),
321 thought.unwrap_or(false),
322 thought_signature.as_ref(),
323 )),
324 _ => None,
325 })
326 .collect::<Vec<_>>()
327 })
328 .unwrap_or_default()
329 })
330 .collect()
331 }
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
336#[serde(rename_all = "camelCase")]
337pub struct GenerateContentRequest {
338 pub contents: Vec<Content>,
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub generation_config: Option<GenerationConfig>,
343 #[serde(skip_serializing_if = "Option::is_none")]
345 pub safety_settings: Option<Vec<SafetySetting>>,
346 #[serde(skip_serializing_if = "Option::is_none")]
348 pub tools: Option<Vec<crate::tools::Tool>>,
349 #[serde(skip_serializing_if = "Option::is_none")]
351 pub tool_config: Option<crate::tools::ToolConfig>,
352 #[serde(skip_serializing_if = "Option::is_none")]
354 pub system_instruction: Option<Content>,
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub cached_content: Option<String>,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize)]
362#[serde(rename_all = "camelCase")]
363pub struct ThinkingConfig {
364 #[serde(skip_serializing_if = "Option::is_none")]
375 pub thinking_budget: Option<i32>,
376
377 #[serde(skip_serializing_if = "Option::is_none")]
382 pub include_thoughts: Option<bool>,
383}
384
385impl ThinkingConfig {
386 pub fn new() -> Self {
392 Self {
393 thinking_budget: None,
394 include_thoughts: None,
395 }
396 }
397
398 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
400 self.thinking_budget = Some(budget);
401 self
402 }
403
404 pub fn with_dynamic_thinking(mut self) -> Self {
406 self.thinking_budget = Some(-1);
407 self
408 }
409
410 pub fn with_thoughts_included(mut self, include: bool) -> Self {
412 self.include_thoughts = Some(include);
413 self
414 }
415
416 pub fn dynamic_thinking() -> Self {
418 Self {
419 thinking_budget: Some(-1),
420 include_thoughts: Some(true),
421 }
422 }
423}
424
425impl Default for ThinkingConfig {
426 fn default() -> Self {
427 Self::new()
428 }
429}
430
431#[derive(Debug, Default, Clone, Serialize, Deserialize)]
433#[serde(rename_all = "camelCase")]
434pub struct GenerationConfig {
435 #[serde(skip_serializing_if = "Option::is_none")]
440 pub temperature: Option<f32>,
441
442 #[serde(skip_serializing_if = "Option::is_none")]
448 pub top_p: Option<f32>,
449
450 #[serde(skip_serializing_if = "Option::is_none")]
455 pub top_k: Option<i32>,
456
457 #[serde(skip_serializing_if = "Option::is_none")]
461 pub max_output_tokens: Option<i32>,
462
463 #[serde(skip_serializing_if = "Option::is_none")]
467 pub candidate_count: Option<i32>,
468
469 #[serde(skip_serializing_if = "Option::is_none")]
473 pub stop_sequences: Option<Vec<String>>,
474
475 #[serde(skip_serializing_if = "Option::is_none")]
479 pub response_mime_type: Option<String>,
480 #[serde(skip_serializing_if = "Option::is_none")]
484 pub response_schema: Option<serde_json::Value>,
485
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub response_modalities: Option<Vec<String>>,
489
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub speech_config: Option<SpeechConfig>,
493
494 #[serde(skip_serializing_if = "Option::is_none")]
498 pub thinking_config: Option<ThinkingConfig>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
503#[serde(rename_all = "camelCase")]
504pub struct SpeechConfig {
505 #[serde(skip_serializing_if = "Option::is_none")]
507 pub voice_config: Option<VoiceConfig>,
508 #[serde(skip_serializing_if = "Option::is_none")]
510 pub multi_speaker_voice_config: Option<MultiSpeakerVoiceConfig>,
511}
512
513#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
515#[serde(rename_all = "camelCase")]
516pub struct VoiceConfig {
517 #[serde(skip_serializing_if = "Option::is_none")]
519 pub prebuilt_voice_config: Option<PrebuiltVoiceConfig>,
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
524#[serde(rename_all = "camelCase")]
525pub struct PrebuiltVoiceConfig {
526 pub voice_name: String,
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
532#[serde(rename_all = "camelCase")]
533pub struct MultiSpeakerVoiceConfig {
534 pub speaker_voice_configs: Vec<SpeakerVoiceConfig>,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
540#[serde(rename_all = "camelCase")]
541pub struct SpeakerVoiceConfig {
542 pub speaker: String,
544 pub voice_config: VoiceConfig,
546}
547
548impl SpeechConfig {
549 pub fn single_voice(voice_name: impl Into<String>) -> Self {
551 Self {
552 voice_config: Some(VoiceConfig {
553 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
554 voice_name: voice_name.into(),
555 }),
556 }),
557 multi_speaker_voice_config: None,
558 }
559 }
560
561 pub fn multi_speaker(speakers: Vec<SpeakerVoiceConfig>) -> Self {
563 Self {
564 voice_config: None,
565 multi_speaker_voice_config: Some(MultiSpeakerVoiceConfig {
566 speaker_voice_configs: speakers,
567 }),
568 }
569 }
570}
571
572impl SpeakerVoiceConfig {
573 pub fn new(speaker: impl Into<String>, voice_name: impl Into<String>) -> Self {
575 Self {
576 speaker: speaker.into(),
577 voice_config: VoiceConfig {
578 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
579 voice_name: voice_name.into(),
580 }),
581 },
582 }
583 }
584}