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, PartialEq)]
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, PartialEq)]
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, PartialEq)]
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, PartialEq)]
186pub struct SafetyRating {
187 pub category: String,
189 pub probability: String,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
195#[serde(rename_all = "camelCase")]
196pub struct CitationMetadata {
197 pub citation_sources: Vec<CitationSource>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
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, PartialEq)]
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, PartialEq)]
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, PartialEq)]
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, PartialEq)]
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, PartialEq)]
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
342 .parts
343 .as_ref()
344 .map(|parts| {
345 parts
346 .iter()
347 .filter_map(|p| match p {
348 Part::FunctionCall { function_call } => Some(function_call),
349 _ => None,
350 })
351 .collect::<Vec<_>>()
352 })
353 .unwrap_or_default()
354 })
355 .collect()
356 }
357
358 pub fn thoughts(&self) -> Vec<String> {
360 self.candidates
361 .iter()
362 .flat_map(|c| {
363 c.content
364 .parts
365 .as_ref()
366 .map(|parts| {
367 parts
368 .iter()
369 .filter_map(|p| match p {
370 Part::Text {
371 text,
372 thought: Some(true),
373 } => Some(text.clone()),
374 _ => None,
375 })
376 .collect::<Vec<_>>()
377 })
378 .unwrap_or_default()
379 })
380 .collect()
381 }
382
383 pub fn all_text(&self) -> Vec<(String, bool)> {
385 self.candidates
386 .iter()
387 .flat_map(|c| {
388 c.content
389 .parts
390 .as_ref()
391 .map(|parts| {
392 parts
393 .iter()
394 .filter_map(|p| match p {
395 Part::Text { text, thought } => {
396 Some((text.clone(), thought.unwrap_or(false)))
397 }
398 _ => None,
399 })
400 .collect::<Vec<_>>()
401 })
402 .unwrap_or_default()
403 })
404 .collect()
405 }
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410#[serde(rename_all = "camelCase")]
411pub struct GenerateContentRequest {
412 pub contents: Vec<Content>,
414 #[serde(skip_serializing_if = "Option::is_none")]
416 pub generation_config: Option<GenerationConfig>,
417 #[serde(skip_serializing_if = "Option::is_none")]
419 pub safety_settings: Option<Vec<SafetySetting>>,
420 #[serde(skip_serializing_if = "Option::is_none")]
422 pub tools: Option<Vec<super::tools::Tool>>,
423 #[serde(skip_serializing_if = "Option::is_none")]
425 pub tool_config: Option<ToolConfig>,
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub system_instruction: Option<Content>,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct EmbedContentRequest {
434 pub model: String,
436 pub content: Content,
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub task_type: Option<TaskType>,
441 #[serde(skip_serializing_if = "Option::is_none")]
443 pub title: Option<String>,
444 #[serde(skip_serializing_if = "Option::is_none")]
446 pub output_dimensionality: Option<i32>,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct BatchEmbedContentsRequest {
452 pub requests: Vec<EmbedContentRequest>,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458#[serde(rename_all = "camelCase")]
459pub struct BatchGenerateContentRequest {
460 pub batch: BatchConfig,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct BatchConfig {
468 pub display_name: String,
470 pub input_config: InputConfig,
472}
473
474#[derive(Debug, Clone, Serialize, Deserialize)]
476#[serde(rename_all = "camelCase")]
477pub struct InputConfig {
478 pub requests: RequestsContainer,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
484#[serde(rename_all = "camelCase")]
485pub struct RequestsContainer {
486 pub requests: Vec<BatchRequestItem>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492#[serde(rename_all = "camelCase")]
493pub struct BatchRequestItem {
494 pub request: GenerateContentRequest,
496 pub metadata: Option<RequestMetadata>,
498}
499
500#[derive(Debug, Clone, Serialize, Deserialize)]
502#[serde(rename_all = "camelCase")]
503pub struct RequestMetadata {
504 pub key: String,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
510#[serde(rename_all = "camelCase")]
511pub struct BatchGenerateContentResponse {
512 pub name: String,
514 pub metadata: BatchMetadata,
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize)]
520#[serde(rename_all = "camelCase")]
521pub struct BatchMetadata {
522 #[serde(rename = "@type")]
524 pub type_annotation: String,
525 pub model: String,
527 pub display_name: String,
529 pub create_time: String,
531 pub update_time: String,
533 pub batch_stats: BatchStats,
535 pub state: BatchState,
537 pub name: String,
539 pub output: Option<OutputConfig>,
541}
542
543#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
545#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
546pub enum BatchState {
547 BatchStateUnspecified,
548 BatchStatePending,
549 BatchStateRunning,
550 BatchStateSucceeded,
551 BatchStateFailed,
552 BatchStateCancelled,
553 BatchStateExpired,
554}
555
556#[derive(Debug, Clone, Serialize, Deserialize)]
558#[serde(rename_all = "camelCase")]
559pub struct BatchStats {
560 #[serde(deserialize_with = "from_str_to_i64")]
562 pub request_count: i64,
563 #[serde(default, deserialize_with = "from_str_to_i64_optional")]
565 pub pending_request_count: Option<i64>,
566 #[serde(default, deserialize_with = "from_str_to_i64_optional")]
568 pub completed_request_count: Option<i64>,
569 #[serde(default, deserialize_with = "from_str_to_i64_optional")]
571 pub failed_request_count: Option<i64>,
572 #[serde(default, deserialize_with = "from_str_to_i64_optional")]
574 pub successful_request_count: Option<i64>,
575}
576
577fn from_str_to_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
578where
579 D: serde::Deserializer<'de>,
580{
581 let s: String = serde::Deserialize::deserialize(deserializer)?;
582 s.parse::<i64>().map_err(serde::de::Error::custom)
583}
584
585fn from_str_to_i64_optional<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
586where
587 D: serde::Deserializer<'de>,
588{
589 match Option::<String>::deserialize(deserializer)? {
590 Some(s) => s.parse::<i64>().map(Some).map_err(serde::de::Error::custom),
591 None => Ok(None),
592 }
593}
594
595#[derive(Debug, Clone, Serialize, Deserialize)]
597#[serde(rename_all = "camelCase")]
598pub struct ThinkingConfig {
599 #[serde(skip_serializing_if = "Option::is_none")]
610 pub thinking_budget: Option<i32>,
611
612 #[serde(skip_serializing_if = "Option::is_none")]
617 pub include_thoughts: Option<bool>,
618}
619
620impl ThinkingConfig {
621 pub fn new() -> Self {
623 Self {
624 thinking_budget: None,
625 include_thoughts: None,
626 }
627 }
628
629 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
631 self.thinking_budget = Some(budget);
632 self
633 }
634
635 pub fn with_dynamic_thinking(mut self) -> Self {
637 self.thinking_budget = Some(-1);
638 self
639 }
640
641 pub fn with_thoughts_included(mut self, include: bool) -> Self {
643 self.include_thoughts = Some(include);
644 self
645 }
646}
647
648impl Default for ThinkingConfig {
649 fn default() -> Self {
650 Self::new()
651 }
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
656#[serde(rename_all = "camelCase")]
657pub struct GenerationConfig {
658 #[serde(skip_serializing_if = "Option::is_none")]
663 pub temperature: Option<f32>,
664
665 #[serde(skip_serializing_if = "Option::is_none")]
671 pub top_p: Option<f32>,
672
673 #[serde(skip_serializing_if = "Option::is_none")]
678 pub top_k: Option<i32>,
679
680 #[serde(skip_serializing_if = "Option::is_none")]
684 pub max_output_tokens: Option<i32>,
685
686 #[serde(skip_serializing_if = "Option::is_none")]
690 pub candidate_count: Option<i32>,
691
692 #[serde(skip_serializing_if = "Option::is_none")]
696 pub stop_sequences: Option<Vec<String>>,
697
698 #[serde(skip_serializing_if = "Option::is_none")]
702 pub response_mime_type: Option<String>,
703 #[serde(skip_serializing_if = "Option::is_none")]
707 pub response_schema: Option<serde_json::Value>,
708
709 #[serde(skip_serializing_if = "Option::is_none")]
711 pub response_modalities: Option<Vec<String>>,
712
713 #[serde(skip_serializing_if = "Option::is_none")]
715 pub speech_config: Option<SpeechConfig>,
716
717 #[serde(skip_serializing_if = "Option::is_none")]
721 pub thinking_config: Option<ThinkingConfig>,
722}
723
724impl Default for GenerationConfig {
725 fn default() -> Self {
726 Self {
727 temperature: Some(0.7),
728 top_p: Some(0.95),
729 top_k: Some(40),
730 max_output_tokens: Some(1024),
731 candidate_count: Some(1),
732 stop_sequences: None,
733 response_mime_type: None,
734 response_schema: None,
735 response_modalities: None,
736 speech_config: None,
737 thinking_config: None,
738 }
739 }
740}
741
742#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
744#[serde(rename_all = "camelCase")]
745pub struct SpeechConfig {
746 #[serde(skip_serializing_if = "Option::is_none")]
748 pub voice_config: Option<VoiceConfig>,
749 #[serde(skip_serializing_if = "Option::is_none")]
751 pub multi_speaker_voice_config: Option<MultiSpeakerVoiceConfig>,
752}
753
754#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
756#[serde(rename_all = "camelCase")]
757pub struct VoiceConfig {
758 #[serde(skip_serializing_if = "Option::is_none")]
760 pub prebuilt_voice_config: Option<PrebuiltVoiceConfig>,
761}
762
763#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
765#[serde(rename_all = "camelCase")]
766pub struct PrebuiltVoiceConfig {
767 pub voice_name: String,
769}
770
771#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
773#[serde(rename_all = "camelCase")]
774pub struct MultiSpeakerVoiceConfig {
775 pub speaker_voice_configs: Vec<SpeakerVoiceConfig>,
777}
778
779#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
781#[serde(rename_all = "camelCase")]
782pub struct SpeakerVoiceConfig {
783 pub speaker: String,
785 pub voice_config: VoiceConfig,
787}
788
789impl SpeechConfig {
790 pub fn single_voice(voice_name: impl Into<String>) -> Self {
792 Self {
793 voice_config: Some(VoiceConfig {
794 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
795 voice_name: voice_name.into(),
796 }),
797 }),
798 multi_speaker_voice_config: None,
799 }
800 }
801
802 pub fn multi_speaker(speakers: Vec<SpeakerVoiceConfig>) -> Self {
804 Self {
805 voice_config: None,
806 multi_speaker_voice_config: Some(MultiSpeakerVoiceConfig {
807 speaker_voice_configs: speakers,
808 }),
809 }
810 }
811}
812
813impl SpeakerVoiceConfig {
814 pub fn new(speaker: impl Into<String>, voice_name: impl Into<String>) -> Self {
816 Self {
817 speaker: speaker.into(),
818 voice_config: VoiceConfig {
819 prebuilt_voice_config: Some(PrebuiltVoiceConfig {
820 voice_name: voice_name.into(),
821 }),
822 },
823 }
824 }
825}
826
827#[derive(Debug, Clone, Serialize, Deserialize)]
829pub struct ToolConfig {
830 #[serde(skip_serializing_if = "Option::is_none")]
832 pub function_calling_config: Option<FunctionCallingConfig>,
833}
834
835#[derive(Debug, Clone, Serialize, Deserialize)]
837pub struct FunctionCallingConfig {
838 pub mode: FunctionCallingMode,
840}
841
842#[derive(Debug, Clone, Serialize, Deserialize)]
844#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
845pub enum FunctionCallingMode {
846 Auto,
848 Any,
850 None,
852}
853
854#[derive(Debug, Clone, Serialize, Deserialize)]
856pub struct SafetySetting {
857 pub category: HarmCategory,
859 pub threshold: HarmBlockThreshold,
861}
862
863#[derive(Debug, Clone, Serialize, Deserialize)]
865#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
866pub enum HarmCategory {
867 Dangerous,
869 Harassment,
871 HateSpeech,
873 SexuallyExplicit,
875}
876
877#[allow(clippy::enum_variant_names)]
879#[derive(Debug, Clone, Serialize, Deserialize)]
880#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
881pub enum HarmBlockThreshold {
882 BlockLowAndAbove,
884 BlockMediumAndAbove,
886 BlockHighAndAbove,
888 BlockOnlyHigh,
890 BlockNone,
892}
893
894#[derive(Debug, Clone, Serialize, Deserialize)]
896#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
897pub enum TaskType {
898 SemanticSimilarity,
900 Classification,
902 Clustering,
904
905 RetrievalDocument,
907 RetrievalQuery,
908 QuestionAnswering,
909 FactVerification,
910
911 CodeRetrievalQuery,
914}
915
916#[derive(Debug, Clone, PartialEq)]
918pub enum BatchStatus {
919 Pending,
921 Running {
923 pending_count: i64,
924 completed_count: i64,
925 failed_count: i64,
926 total_count: i64,
927 },
928 Succeeded { results: Vec<BatchResultItem> },
930 Cancelled,
932 Expired,
934}
935
936impl BatchStatus {
937 pub(crate) fn from_operation(operation: BatchOperation) -> crate::Result<Self> {
939 if operation.done {
940 match operation.result {
942 Some(OperationResult::Failure { error }) => {
943 Err(crate::Error::BatchFailed {
944 name: operation.name,
945 error,
946 })
947 }
948 Some(OperationResult::Success { response }) => {
949 let mut results: Vec<BatchResultItem> = response
950 .inlined_responses
951 .inlined_responses
952 .into_iter()
953 .map(|item| match item {
954 BatchGenerateContentResponseItem::Success { response, metadata } => {
955 BatchResultItem::Success {
956 key: metadata.key,
957 response,
958 }
959 }
960 BatchGenerateContentResponseItem::Error { error, metadata } => {
961 BatchResultItem::Error {
962 key: metadata.key,
963 error,
964 }
965 }
966 })
967 .collect();
968
969 results.sort_by_key(|item| {
971 let key_str = match item {
972 BatchResultItem::Success { key, .. } => key,
973 BatchResultItem::Error { key, .. } => key,
974 };
975 key_str.parse::<usize>().unwrap_or(usize::MAX)
976 });
977
978 Ok(BatchStatus::Succeeded { results })
979 }
980 None => match operation.metadata.state {
983 BatchState::BatchStateCancelled => Ok(BatchStatus::Cancelled),
984 BatchState::BatchStateExpired => Ok(BatchStatus::Expired),
985 BatchState::BatchStateSucceeded => Ok(BatchStatus::Succeeded { results: vec![] }), _ => Err(crate::Error::InconsistentBatchState {
987 description: format!(
988 "Operation is done but has no response or error. Final state is ambiguous: {:?}.",
989 operation.metadata.state
990 ),
991 }),
992 },
993 }
994 } else {
995 match operation.metadata.state {
997 BatchState::BatchStatePending => Ok(BatchStatus::Pending),
998 BatchState::BatchStateRunning => {
999 let total_count = operation.metadata.batch_stats.request_count;
1000 let pending_count = operation
1001 .metadata
1002 .batch_stats
1003 .pending_request_count
1004 .unwrap_or(total_count); let completed_count = operation
1006 .metadata
1007 .batch_stats
1008 .completed_request_count
1009 .unwrap_or(0);
1010 let failed_count = operation
1011 .metadata
1012 .batch_stats
1013 .failed_request_count
1014 .unwrap_or(0);
1015 Ok(BatchStatus::Running {
1016 pending_count,
1017 completed_count,
1018 failed_count,
1019 total_count,
1020 })
1021 }
1022 terminal_state => Err(crate::Error::InconsistentBatchState {
1024 description: format!(
1025 "Operation is not done, but API reported a terminal state: {:?}.",
1026 terminal_state
1027 ),
1028 }),
1029 }
1030 }
1031 }
1032}
1033
1034#[derive(Debug, Serialize, Deserialize)]
1036pub struct BatchOperation {
1037 pub name: String,
1038 pub metadata: BatchMetadata,
1039 #[serde(default)]
1040 pub done: bool,
1041 #[serde(flatten)]
1042 pub result: Option<OperationResult>,
1043}
1044
1045#[derive(Debug, Serialize, Deserialize)]
1047#[serde(untagged)]
1048pub enum OperationResult {
1049 Success { response: BatchOperationResponse },
1050 Failure { error: crate::error::OperationError },
1051}
1052
1053#[derive(Debug, Clone, Serialize, Deserialize)]
1055#[serde(rename_all = "camelCase")]
1056pub struct BatchOperationResponse {
1057 #[serde(rename = "@type")]
1058 pub type_annotation: String,
1059 pub inlined_responses: InlinedResponses,
1060}
1061
1062#[derive(Debug, Clone, Serialize, Deserialize)]
1064#[serde(rename_all = "camelCase")]
1065pub struct OutputConfig {
1066 pub inlined_responses: InlinedResponses,
1067}
1068
1069#[derive(Debug, Clone, Serialize, Deserialize)]
1071#[serde(rename_all = "camelCase")]
1072pub struct InlinedResponses {
1073 pub inlined_responses: Vec<BatchGenerateContentResponseItem>,
1074}
1075
1076#[derive(Debug, Clone, Serialize, Deserialize)]
1078#[serde(rename_all = "camelCase")]
1079#[serde(untagged)]
1080pub enum BatchGenerateContentResponseItem {
1081 Success {
1082 response: GenerationResponse,
1083 metadata: RequestMetadata,
1084 },
1085 Error {
1086 error: IndividualRequestError,
1087 metadata: RequestMetadata,
1088 },
1089}
1090
1091#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1093pub struct IndividualRequestError {
1094 pub code: i32,
1095 pub message: String,
1096 #[serde(skip_serializing_if = "Option::is_none")]
1098 pub details: Option<serde_json::Value>,
1099}
1100
1101#[derive(Debug, Clone, PartialEq)]
1103pub enum BatchResultItem {
1104 Success {
1105 key: String,
1106 response: GenerationResponse,
1107 },
1108 Error {
1109 key: String,
1110 error: IndividualRequestError,
1111 },
1112}
1113
1114#[derive(Debug, serde::Deserialize)]
1116#[serde(rename_all = "camelCase")]
1117pub struct ListBatchesResponse {
1118 pub operations: Vec<BatchOperation>,
1120 pub next_page_token: Option<String>,
1122}