gemini_rust/batch.rs
1//! The Batch module for managing batch operations.
2//!
3//! For more information, see the official Google AI documentation:
4//! - [Batch Mode Guide](https://ai.google.dev/gemini-api/docs/batch-mode)
5//! - [Batch API Reference](https://ai.google.dev/api/batch-mode)
6//!
7//! # Design Note: Resource Management in Batch Operations
8//!
9//! The Batch API methods that consume the [`Batch`] struct (`cancel`, `delete`, `wait_for_completion`)
10//! return `std::result::Result<T, (Self, crate::Error)>` instead of the crate's `Result<T>`.
11//! This design follows patterns used in channel libraries (e.g., `std::sync::mpsc::Receiver`)
12//! and provides two key benefits:
13//!
14//! 1. **Resource Safety**: Once a [`Batch`] is consumed by an operation, it cannot be used again,
15//! preventing invalid operations on deleted or canceled batches.
16//!
17//! 2. **Error Recovery**: If an operation fails due to transient network issues, both the
18//! [`Batch`] and error information are returned, allowing callers to retry the operation.
19//!
20//! ## Example usage:
21//! ```rust,ignore
22//! use gemini_rust::{Gemini, Message};
23//!
24//! #[tokio::main]
25//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
26//! let client = Gemini::new(std::env::var("GEMINI_API_KEY")?);
27//! let request = client.generate_content().with_user_message("Why is the sky blue?").build();
28//! let batch = client.batch_generate_content_sync().with_request(request).execute().await?;
29//!
30//! match batch.delete().await {
31//! Ok(()) => println!("Batch deleted successfully!"),
32//! Err((batch, error)) => {
33//! println!("Failed to delete batch: {}", error);
34//! // Can retry: batch.delete().await
35//! }
36//! }
37//! Ok(())
38//! }
39//! ```
40
41use std::{sync::Arc, time::Duration};
42use tokio::time::sleep;
43
44use crate::{
45 client::GeminiClient,
46 models::{BatchOperation, BatchStatus},
47 Error, Result,
48};
49
50/// Represents a long-running batch operation, providing methods to manage its lifecycle.
51///
52/// A `Batch` object is a handle to a batch operation on the Gemini API. It allows you to
53/// check the status, cancel the operation, or delete it once it's no longer needed.
54pub struct Batch {
55 /// The unique resource name of the batch operation, e.g., `operations/batch-xxxxxxxx`.
56 pub name: String,
57 client: Arc<GeminiClient>,
58}
59
60impl Batch {
61 /// Creates a new Batch instance.
62 pub(crate) fn new(name: String, client: Arc<GeminiClient>) -> Self {
63 Self { name, client }
64 }
65
66 /// Returns the unique resource name of the batch operation.
67 pub fn name(&self) -> &str {
68 &self.name
69 }
70
71 /// Retrieves the current status of the batch operation by making an API call.
72 ///
73 /// This method provides a snapshot of the batch's state at a single point in time.
74 pub async fn status(&self) -> Result<BatchStatus> {
75 let operation: BatchOperation = self.client.get_batch_operation(&self.name).await?;
76 BatchStatus::from_operation(operation)
77 }
78
79 /// Sends a request to the API to cancel the batch operation.
80 ///
81 /// Cancellation is not guaranteed to be instantaneous. The operation may continue to run for
82 /// some time after the cancellation request is made.
83 ///
84 /// Consumes the batch. If cancellation fails, returns the batch and error information
85 /// so it can be retried.
86 pub async fn cancel(self) -> std::result::Result<(), (Self, crate::Error)> {
87 match self.client.cancel_batch_operation(&self.name).await {
88 Ok(()) => Ok(()),
89 Err(e) => Err((self, e)),
90 }
91 }
92
93 /// Deletes the batch operation resource from the server.
94 ///
95 /// Note: This method indicates that the client is no longer interested in the operation result.
96 /// It does not cancel a running operation. To stop a running batch, use the `cancel` method.
97 /// This method should typically be used after the batch has completed.
98 ///
99 /// Consumes the batch. If deletion fails, returns the batch and error information
100 /// so it can be retried.
101 pub async fn delete(self) -> std::result::Result<(), (Self, crate::Error)> {
102 match self.client.delete_batch_operation(&self.name).await {
103 Ok(()) => Ok(()),
104 Err(e) => Err((self, e)),
105 }
106 }
107
108 /// Waits for the batch operation to complete by periodically polling its status.
109 ///
110 /// This method polls the batch status with a specified delay until the operation
111 /// reaches a terminal state (Succeeded, Failed, Cancelled, or Expired).
112 ///
113 /// Consumes the batch and returns the final status. If there's an error during polling,
114 /// the batch is returned in the error variant so it can be retried.
115 pub async fn wait_for_completion(
116 self,
117 delay: Duration,
118 ) -> std::result::Result<BatchStatus, (Self, crate::Error)> {
119 let batch_name = self.name.clone();
120 loop {
121 match self.status().await {
122 Ok(status) => match status {
123 BatchStatus::Succeeded { .. } | BatchStatus::Cancelled => return Ok(status),
124 BatchStatus::Expired => {
125 return Err((self, Error::BatchExpired { name: batch_name }))
126 }
127 _ => sleep(delay).await,
128 },
129 Err(e) => match e {
130 Error::BatchFailed { .. } => return Err((self, e)),
131 _ => return Err((self, e)), // Return the batch and error for retry
132 },
133 }
134 }
135 }
136}