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 pub candidates: Vec<Candidate>,
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub prompt_feedback: Option<PromptFeedback>,
137 #[serde(skip_serializing_if = "Option::is_none")]
139 pub usage_metadata: Option<UsageMetadata>,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub model_version: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub response_id: Option<String>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
150#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
151pub enum BlockReason {
152 BlockReasonUnspecified,
154 Safety,
156 Other,
158 Blocklist,
160 ProhibitedContent,
162 ImageSafety,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
168#[serde(rename_all = "camelCase")]
169pub struct PromptFeedback {
170 pub safety_ratings: Vec<SafetyRating>,
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub block_reason: Option<BlockReason>,
175}
176
177impl GenerationResponse {
178 pub fn text(&self) -> String {
180 self.candidates
181 .first()
182 .and_then(|c| {
183 c.content.parts.as_ref().and_then(|parts| {
184 parts.first().and_then(|p| match p {
185 Part::Text {
186 text,
187 thought: _,
188 thought_signature: _,
189 } => Some(text.clone()),
190 _ => None,
191 })
192 })
193 })
194 .unwrap_or_default()
195 }
196
197 pub fn function_calls(&self) -> Vec<&crate::tools::FunctionCall> {
199 self.candidates
200 .iter()
201 .flat_map(|c| {
202 c.content
203 .parts
204 .as_ref()
205 .map(|parts| {
206 parts
207 .iter()
208 .filter_map(|p| match p {
209 Part::FunctionCall {
210 function_call,
211 thought_signature: _,
212 } => Some(function_call),
213 _ => None,
214 })
215 .collect::<Vec<_>>()
216 })
217 .unwrap_or_default()
218 })
219 .collect()
220 }
221
222 pub fn function_calls_with_thoughts(
224 &self,
225 ) -> Vec<(&crate::tools::FunctionCall, Option<&String>)> {
226 self.candidates
227 .iter()
228 .flat_map(|c| {
229 c.content
230 .parts
231 .as_ref()
232 .map(|parts| {
233 parts
234 .iter()
235 .filter_map(|p| match p {
236 Part::FunctionCall {
237 function_call,
238 thought_signature,
239 } => Some((function_call, thought_signature.as_ref())),
240 _ => None,
241 })
242 .collect::<Vec<_>>()
243 })
244 .unwrap_or_default()
245 })
246 .collect()
247 }
248
249 pub fn thoughts(&self) -> Vec<String> {
251 self.candidates
252 .iter()
253 .flat_map(|c| {
254 c.content
255 .parts
256 .as_ref()
257 .map(|parts| {
258 parts
259 .iter()
260 .filter_map(|p| match p {
261 Part::Text {
262 text,
263 thought: Some(true),
264 thought_signature: _,
265 } => Some(text.clone()),
266 _ => None,
267 })
268 .collect::<Vec<_>>()
269 })
270 .unwrap_or_default()
271 })
272 .collect()
273 }
274
275 pub fn all_text(&self) -> Vec<(String, bool)> {
277 self.candidates
278 .iter()
279 .flat_map(|c| {
280 c.content
281 .parts
282 .as_ref()
283 .map(|parts| {
284 parts
285 .iter()
286 .filter_map(|p| match p {
287 Part::Text {
288 text,
289 thought,
290 thought_signature: _,
291 } => Some((text.clone(), thought.unwrap_or(false))),
292 _ => None,
293 })
294 .collect::<Vec<_>>()
295 })
296 .unwrap_or_default()
297 })
298 .collect()
299 }
300
301 pub fn text_with_thoughts(&self) -> Vec<(String, bool, Option<&String>)> {
303 self.candidates
304 .iter()
305 .flat_map(|c| {
306 c.content
307 .parts
308 .as_ref()
309 .map(|parts| {
310 parts
311 .iter()
312 .filter_map(|p| match p {
313 Part::Text {
314 text,
315 thought,
316 thought_signature,
317 } => Some((
318 text.clone(),
319 thought.unwrap_or(false),
320 thought_signature.as_ref(),
321 )),
322 _ => None,
323 })
324 .collect::<Vec<_>>()
325 })
326 .unwrap_or_default()
327 })
328 .collect()
329 }
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize)]
334#[serde(rename_all = "camelCase")]
335pub struct GenerateContentRequest {
336 pub contents: Vec<Content>,
338 #[serde(skip_serializing_if = "Option::is_none")]
340 pub generation_config: Option<GenerationConfig>,
341 #[serde(skip_serializing_if = "Option::is_none")]
343 pub safety_settings: Option<Vec<SafetySetting>>,
344 #[serde(skip_serializing_if = "Option::is_none")]
346 pub tools: Option<Vec<crate::tools::Tool>>,
347 #[serde(skip_serializing_if = "Option::is_none")]
349 pub tool_config: Option<crate::tools::ToolConfig>,
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub system_instruction: Option<Content>,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub cached_content: Option<String>,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
360#[serde(rename_all = "camelCase")]
361pub struct ThinkingConfig {
362 #[serde(skip_serializing_if = "Option::is_none")]
373 pub thinking_budget: Option<i32>,
374
375 #[serde(skip_serializing_if = "Option::is_none")]
380 pub include_thoughts: Option<bool>,
381}
382
383impl ThinkingConfig {
384 pub fn new() -> Self {
390 Self {
391 thinking_budget: None,
392 include_thoughts: None,
393 }
394 }
395
396 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
398 self.thinking_budget = Some(budget);
399 self
400 }
401
402 pub fn with_dynamic_thinking(mut self) -> Self {
404 self.thinking_budget = Some(-1);
405 self
406 }
407
408 pub fn with_thoughts_included(mut self, include: bool) -> Self {
410 self.include_thoughts = Some(include);
411 self
412 }
413}
414
415impl Default for ThinkingConfig {
416 fn default() -> Self {
417 Self::new()
418 }
419}
420
421#[derive(Debug, Default, Clone, Serialize, Deserialize)]
423#[serde(rename_all = "camelCase")]
424pub struct GenerationConfig {
425 #[serde(skip_serializing_if = "Option::is_none")]
430 pub temperature: Option<f32>,
431
432 #[serde(skip_serializing_if = "Option::is_none")]
438 pub top_p: Option<f32>,
439
440 #[serde(skip_serializing_if = "Option::is_none")]
445 pub top_k: Option<i32>,
446
447 #[serde(skip_serializing_if = "Option::is_none")]
451 pub max_output_tokens: Option<i32>,
452
453 #[serde(skip_serializing_if = "Option::is_none")]
457 pub candidate_count: Option<i32>,
458
459 #[serde(skip_serializing_if = "Option::is_none")]
463 pub stop_sequences: Option<Vec<String>>,
464
465 #[serde(skip_serializing_if = "Option::is_none")]
469 pub response_mime_type: Option<String>,
470 #[serde(skip_serializing_if = "Option::is_none")]
474 pub response_schema: Option<serde_json::Value>,
475
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub response_modalities: Option<Vec<String>>,
479
480 #[serde(skip_serializing_if = "Option::is_none")]
482 pub speech_config: Option<SpeechConfig>,
483
484 #[serde(skip_serializing_if = "Option::is_none")]
488 pub thinking_config: Option<ThinkingConfig>,
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
493#[serde(rename_all = "camelCase")]
494pub struct SpeechConfig {
495 #[serde(skip_serializing_if = "Option::is_none")]
497 pub voice_config: Option<VoiceConfig>,
498 #[serde(skip_serializing_if = "Option::is_none")]
500 pub multi_speaker_voice_config: Option<MultiSpeakerVoiceConfig>,
501}
502
503#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
505#[serde(rename_all = "camelCase")]
506pub struct VoiceConfig {
507 #[serde(skip_serializing_if = "Option::is_none")]
509 pub prebuilt_voice_config: Option<PrebuiltVoiceConfig>,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
514#[serde(rename_all = "camelCase")]
515pub struct PrebuiltVoiceConfig {
516 pub voice_name: String,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
522#[serde(rename_all = "camelCase")]
523pub struct MultiSpeakerVoiceConfig {
524 pub speaker_voice_configs: Vec<SpeakerVoiceConfig>,
526}
527
528#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
530#[serde(rename_all = "camelCase")]
531pub struct SpeakerVoiceConfig {
532 pub speaker: String,
534 pub voice_config: VoiceConfig,
536}
537
538impl SpeechConfig {
539 pub fn single_voice(voice_name: impl Into<String>) -> Self {
541 Self {
542 voice_config: Some(VoiceConfig {
543 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
544 voice_name: voice_name.into(),
545 }),
546 }),
547 multi_speaker_voice_config: None,
548 }
549 }
550
551 pub fn multi_speaker(speakers: Vec<SpeakerVoiceConfig>) -> Self {
553 Self {
554 voice_config: None,
555 multi_speaker_voice_config: Some(MultiSpeakerVoiceConfig {
556 speaker_voice_configs: speakers,
557 }),
558 }
559 }
560}
561
562impl SpeakerVoiceConfig {
563 pub fn new(speaker: impl Into<String>, voice_name: impl Into<String>) -> Self {
565 Self {
566 speaker: speaker.into(),
567 voice_config: VoiceConfig {
568 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
569 voice_name: voice_name.into(),
570 }),
571 },
572 }
573 }
574}