1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5#[serde(rename_all = "lowercase")]
6pub enum Role {
7 User,
9 Model,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(untagged)]
16pub enum Part {
17 Text {
19 text: String,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 thought: Option<bool>,
24 },
25 InlineData {
26 #[serde(rename = "inlineData")]
28 inline_data: Blob,
29 },
30 FunctionCall {
32 #[serde(rename = "functionCall")]
34 function_call: super::tools::FunctionCall,
35 },
36 FunctionResponse {
38 #[serde(rename = "functionResponse")]
40 function_response: super::tools::FunctionResponse,
41 },
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46#[serde(rename_all = "camelCase")]
47pub struct Blob {
48 pub mime_type: String,
49 pub data: String, }
51
52impl Blob {
53 pub fn new(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
55 Self {
56 mime_type: mime_type.into(),
57 data: data.into(),
58 }
59 }
60}
61
62#[derive(Debug, Default, Clone, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct Content {
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub parts: Option<Vec<Part>>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub role: Option<Role>,
72}
73
74impl Content {
75 pub fn text(text: impl Into<String>) -> Self {
77 Self {
78 parts: Some(vec![Part::Text {
79 text: text.into(),
80 thought: None,
81 }]),
82 role: None,
83 }
84 }
85
86 pub fn function_call(function_call: super::tools::FunctionCall) -> Self {
88 Self {
89 parts: Some(vec![Part::FunctionCall { function_call }]),
90 role: None,
91 }
92 }
93
94 pub fn function_response(function_response: super::tools::FunctionResponse) -> Self {
96 Self {
97 parts: Some(vec![Part::FunctionResponse { function_response }]),
98 role: None,
99 }
100 }
101
102 pub fn function_response_json(name: impl Into<String>, response: serde_json::Value) -> Self {
104 Self {
105 parts: Some(vec![Part::FunctionResponse {
106 function_response: super::tools::FunctionResponse::new(name, response),
107 }]),
108 role: None,
109 }
110 }
111
112 pub fn inline_data(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
114 Self {
115 parts: Some(vec![Part::InlineData {
116 inline_data: Blob::new(mime_type, data),
117 }]),
118 role: None,
119 }
120 }
121
122 pub fn with_role(mut self, role: Role) -> Self {
124 self.role = Some(role);
125 self
126 }
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct Message {
132 pub content: Content,
134 pub role: Role,
136}
137
138impl Message {
139 pub fn user(text: impl Into<String>) -> Self {
141 Self {
142 content: Content::text(text).with_role(Role::User),
143 role: Role::User,
144 }
145 }
146
147 pub fn model(text: impl Into<String>) -> Self {
149 Self {
150 content: Content::text(text).with_role(Role::Model),
151 role: Role::Model,
152 }
153 }
154
155 pub fn embed(text: impl Into<String>) -> Self {
156 Self {
157 content: Content::text(text),
158 role: Role::Model,
159 }
160 }
161
162 pub fn function(name: impl Into<String>, response: serde_json::Value) -> Self {
164 Self {
165 content: Content::function_response_json(name, response).with_role(Role::Model),
166 role: Role::Model,
167 }
168 }
169
170 pub fn function_str(
172 name: impl Into<String>,
173 response: impl Into<String>,
174 ) -> Result<Self, serde_json::Error> {
175 let response_str = response.into();
176 let json = serde_json::from_str(&response_str)?;
177 Ok(Self {
178 content: Content::function_response_json(name, json).with_role(Role::Model),
179 role: Role::Model,
180 })
181 }
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct SafetyRating {
187 pub category: String,
189 pub probability: String,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase")]
196pub struct CitationMetadata {
197 pub citation_sources: Vec<CitationSource>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct CitationSource {
205 pub uri: Option<String>,
207 pub title: Option<String>,
209 pub start_index: Option<i32>,
211 pub end_index: Option<i32>,
213 pub license: Option<String>,
215 pub publication_date: Option<String>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct Candidate {
223 pub content: Content,
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub safety_ratings: Option<Vec<SafetyRating>>,
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub citation_metadata: Option<CitationMetadata>,
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub finish_reason: Option<String>,
234 #[serde(skip_serializing_if = "Option::is_none")]
236 pub index: Option<i32>,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
241#[serde(rename_all = "camelCase")]
242pub struct UsageMetadata {
243 pub prompt_token_count: i32,
245 #[serde(skip_serializing_if = "Option::is_none")]
247 pub candidates_token_count: Option<i32>,
248 pub total_token_count: i32,
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub thoughts_token_count: Option<i32>,
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub prompt_tokens_details: Option<Vec<PromptTokenDetails>>,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct PromptTokenDetails {
262 pub modality: String,
264 pub token_count: i32,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
270#[serde(rename_all = "camelCase")]
271pub struct GenerationResponse {
272 pub candidates: Vec<Candidate>,
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub prompt_feedback: Option<PromptFeedback>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub usage_metadata: Option<UsageMetadata>,
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub model_version: Option<String>,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub response_id: Option<String>,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct ContentEmbedding {
291 pub values: Vec<f32>, }
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct ContentEmbeddingResponse {
298 pub embedding: ContentEmbedding,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
304pub struct BatchContentEmbeddingResponse {
305 pub embeddings: Vec<ContentEmbedding>,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
311#[serde(rename_all = "camelCase")]
312pub struct PromptFeedback {
313 pub safety_ratings: Vec<SafetyRating>,
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub block_reason: Option<String>,
318}
319
320impl GenerationResponse {
321 pub fn text(&self) -> String {
323 self.candidates
324 .first()
325 .and_then(|c| {
326 c.content.parts.as_ref().and_then(|parts| {
327 parts.first().and_then(|p| match p {
328 Part::Text { text, thought: _ } => Some(text.clone()),
329 _ => None,
330 })
331 })
332 })
333 .unwrap_or_default()
334 }
335
336 pub fn function_calls(&self) -> Vec<&super::tools::FunctionCall> {
338 self.candidates
339 .iter()
340 .flat_map(|c| {
341 c.content.parts.as_ref().map(|parts| {
342 parts.iter().filter_map(|p| match p {
343 Part::FunctionCall { function_call } => Some(function_call),
344 _ => None,
345 }).collect::<Vec<_>>()
346 }).unwrap_or_default()
347 })
348 .collect()
349 }
350
351 pub fn thoughts(&self) -> Vec<String> {
353 self.candidates
354 .iter()
355 .flat_map(|c| {
356 c.content.parts.as_ref().map(|parts| {
357 parts.iter().filter_map(|p| match p {
358 Part::Text {
359 text,
360 thought: Some(true),
361 } => Some(text.clone()),
362 _ => None,
363 }).collect::<Vec<_>>()
364 }).unwrap_or_default()
365 })
366 .collect()
367 }
368
369 pub fn all_text(&self) -> Vec<(String, bool)> {
371 self.candidates
372 .iter()
373 .flat_map(|c| {
374 c.content.parts.as_ref().map(|parts| {
375 parts.iter().filter_map(|p| match p {
376 Part::Text { text, thought } => Some((text.clone(), thought.unwrap_or(false))),
377 _ => None,
378 }).collect::<Vec<_>>()
379 }).unwrap_or_default()
380 })
381 .collect()
382 }
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
387#[serde(rename_all = "camelCase")]
388pub struct GenerateContentRequest {
389 pub contents: Vec<Content>,
391 #[serde(skip_serializing_if = "Option::is_none")]
393 pub generation_config: Option<GenerationConfig>,
394 #[serde(skip_serializing_if = "Option::is_none")]
396 pub safety_settings: Option<Vec<SafetySetting>>,
397 #[serde(skip_serializing_if = "Option::is_none")]
399 pub tools: Option<Vec<super::tools::Tool>>,
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub tool_config: Option<ToolConfig>,
403 #[serde(skip_serializing_if = "Option::is_none")]
405 pub system_instruction: Option<Content>,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct EmbedContentRequest {
411 pub model: String,
413 pub content: Content,
415 #[serde(skip_serializing_if = "Option::is_none")]
417 pub task_type: Option<TaskType>,
418 #[serde(skip_serializing_if = "Option::is_none")]
420 pub title: Option<String>,
421 #[serde(skip_serializing_if = "Option::is_none")]
423 pub output_dimensionality: Option<i32>,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct BatchEmbedContentsRequest {
429 pub requests: Vec<EmbedContentRequest>,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
435#[serde(rename_all = "camelCase")]
436pub struct BatchGenerateContentRequest {
437 pub batch: BatchConfig,
439}
440
441#[derive(Debug, Clone, Serialize, Deserialize)]
443#[serde(rename_all = "camelCase")]
444pub struct BatchConfig {
445 pub display_name: String,
447 pub input_config: InputConfig,
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
453#[serde(rename_all = "camelCase")]
454pub struct InputConfig {
455 pub requests: RequestsContainer,
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct RequestsContainer {
463 pub requests: Vec<BatchRequestItem>,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
469#[serde(rename_all = "camelCase")]
470pub struct BatchRequestItem {
471 pub request: GenerateContentRequest,
473 pub metadata: Option<RequestMetadata>,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
479#[serde(rename_all = "camelCase")]
480pub struct RequestMetadata {
481 pub key: String,
483}
484
485#[derive(Debug, Clone, Serialize, Deserialize)]
487#[serde(rename_all = "camelCase")]
488pub struct BatchGenerateContentResponse {
489 pub name: String,
491 pub metadata: BatchMetadata,
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize)]
497#[serde(rename_all = "camelCase")]
498pub struct BatchMetadata {
499 #[serde(rename = "@type")]
501 pub type_annotation: String,
502 pub model: String,
504 pub display_name: String,
506 pub create_time: String,
508 pub update_time: String,
510 pub batch_stats: BatchStats,
512 pub state: String,
514 pub name: String,
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize)]
520#[serde(rename_all = "camelCase")]
521pub struct BatchStats {
522 pub request_count: String,
524 pub pending_request_count: Option<String>,
526 pub completed_request_count: Option<String>,
528 pub failed_request_count: Option<String>,
530}
531
532#[derive(Debug, Clone, Serialize, Deserialize)]
534#[serde(rename_all = "camelCase")]
535pub struct ThinkingConfig {
536 #[serde(skip_serializing_if = "Option::is_none")]
547 pub thinking_budget: Option<i32>,
548
549 #[serde(skip_serializing_if = "Option::is_none")]
554 pub include_thoughts: Option<bool>,
555}
556
557impl ThinkingConfig {
558 pub fn new() -> Self {
560 Self {
561 thinking_budget: None,
562 include_thoughts: None,
563 }
564 }
565
566 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
568 self.thinking_budget = Some(budget);
569 self
570 }
571
572 pub fn with_dynamic_thinking(mut self) -> Self {
574 self.thinking_budget = Some(-1);
575 self
576 }
577
578 pub fn with_thoughts_included(mut self, include: bool) -> Self {
580 self.include_thoughts = Some(include);
581 self
582 }
583}
584
585impl Default for ThinkingConfig {
586 fn default() -> Self {
587 Self::new()
588 }
589}
590
591#[derive(Debug, Clone, Serialize, Deserialize)]
593#[serde(rename_all = "camelCase")]
594pub struct GenerationConfig {
595 #[serde(skip_serializing_if = "Option::is_none")]
600 pub temperature: Option<f32>,
601
602 #[serde(skip_serializing_if = "Option::is_none")]
608 pub top_p: Option<f32>,
609
610 #[serde(skip_serializing_if = "Option::is_none")]
615 pub top_k: Option<i32>,
616
617 #[serde(skip_serializing_if = "Option::is_none")]
621 pub max_output_tokens: Option<i32>,
622
623 #[serde(skip_serializing_if = "Option::is_none")]
627 pub candidate_count: Option<i32>,
628
629 #[serde(skip_serializing_if = "Option::is_none")]
633 pub stop_sequences: Option<Vec<String>>,
634
635 #[serde(skip_serializing_if = "Option::is_none")]
639 pub response_mime_type: Option<String>,
640
641 #[serde(skip_serializing_if = "Option::is_none")]
645 pub response_schema: Option<serde_json::Value>,
646
647 #[serde(skip_serializing_if = "Option::is_none")]
651 pub thinking_config: Option<ThinkingConfig>,
652}
653
654impl Default for GenerationConfig {
655 fn default() -> Self {
656 Self {
657 temperature: Some(0.7),
658 top_p: Some(0.95),
659 top_k: Some(40),
660 max_output_tokens: Some(1024),
661 candidate_count: Some(1),
662 stop_sequences: None,
663 response_mime_type: None,
664 response_schema: None,
665 thinking_config: None,
666 }
667 }
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct ToolConfig {
673 #[serde(skip_serializing_if = "Option::is_none")]
675 pub function_calling_config: Option<FunctionCallingConfig>,
676}
677
678#[derive(Debug, Clone, Serialize, Deserialize)]
680pub struct FunctionCallingConfig {
681 pub mode: FunctionCallingMode,
683}
684
685#[derive(Debug, Clone, Serialize, Deserialize)]
687#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
688pub enum FunctionCallingMode {
689 Auto,
691 Any,
693 None,
695}
696
697#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct SafetySetting {
700 pub category: HarmCategory,
702 pub threshold: HarmBlockThreshold,
704}
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
708#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
709pub enum HarmCategory {
710 Dangerous,
712 Harassment,
714 HateSpeech,
716 SexuallyExplicit,
718}
719
720#[allow(clippy::enum_variant_names)]
722#[derive(Debug, Clone, Serialize, Deserialize)]
723#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
724pub enum HarmBlockThreshold {
725 BlockLowAndAbove,
727 BlockMediumAndAbove,
729 BlockHighAndAbove,
731 BlockOnlyHigh,
733 BlockNone,
735}
736
737#[derive(Debug, Clone, Serialize, Deserialize)]
739#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
740pub enum TaskType {
741 SemanticSimilarity,
743 Classification,
745 Clustering,
747
748 RetrievalDocument,
750 RetrievalQuery,
751 QuestionAnswering,
752 FactVerification,
753
754 CodeRetrievalQuery,
757}