api_gemini 0.6.1

Gemini's API for accessing large language models (LLMs).
Documentation
//! Batch Mode API for async job-based processing with 50% cost discount.
//!
//! Provides methods for creating batch jobs, polling status, and retrieving results.
//! Batch Mode offers 50% cost discount with 24-hour SLO.
//!
//! **NOTE:** Batch Mode API endpoints are not yet available from Gemini. All methods
//! return `Error::NotImplemented`. Replace with real HTTP calls when endpoints ship.

use crate::
{
  client ::Client,
  error ::Error,
  models ::
  {
    GenerateContentRequest,
    batch ::*,
  },
};
use std::time::{ Duration, SystemTime };

/// API for managing batch jobs with async processing.
#[ derive( Debug ) ]
pub struct BatchApi
{
}

impl BatchApi
{
  /// Create a new BatchApi instance.
  #[ inline ]
  pub fn new( _client : &Client ) -> Self
  {
    Self {}
  }

  /// Create a batch job with inline content generation requests.
  ///
  /// # Arguments
  ///
  /// * `model` - Model name (e.g., "gemini-2.5-flash")
  /// * `requests` - Vec of GenerateContentRequest to process
  ///
  /// # Returns
  ///
  /// Returns a BatchJob with job_id for status polling.
  ///
  /// # Errors
  ///
  /// Returns error if job creation fails or request is invalid.
  pub async fn create_inline(
    &self,
    model : &str,
    requests : Vec< GenerateContentRequest >
  ) -> Result< BatchJob, Error >
  {
    let _ = ( model, requests );
    Err( Error::NotImplemented( "Batch Mode API endpoints not yet available from Gemini".to_string() ) )
  }

  /// Get status of a batch job.
  ///
  /// # Arguments
  ///
  /// * `job_id` - The batch job identifier
  ///
  /// # Returns
  ///
  /// Returns BatchJobStatus with current state and progress.
  ///
  /// # Errors
  ///
  /// Returns error if job not found or API call fails.
  pub async fn get_status( &self, job_id : &str ) -> Result< BatchJobStatus, Error >
  {
    let _ = job_id;
    Err( Error::NotImplemented( "Batch Mode API endpoints not yet available from Gemini".to_string() ) )
  }

  /// Wait for batch job completion and retrieve results.
  ///
  /// Polls job status until completion or timeout.
  ///
  /// # Arguments
  ///
  /// * `job_id` - The batch job identifier
  /// * `timeout` - Maximum time to wait
  ///
  /// # Returns
  ///
  /// Returns BatchJobResults with all responses.
  ///
  /// # Errors
  ///
  /// Returns error if timeout reached or job fails.
  pub async fn wait_and_retrieve(
    &self,
    job_id : &str,
    timeout : Duration
  ) -> Result< BatchJobResults, Error >
  {
    let start = SystemTime::now();
    let poll_interval = Duration::from_secs( 5 );

    loop
    {
      let status = self.get_status( job_id ).await?;

      match status.state
      {
        BatchJobState::Succeeded | BatchJobState::PartiallyCompleted =>
        {
          return self.retrieve_results( job_id ).await;
        }
        BatchJobState::Failed =>
        {
          return Err( Error::ApiError(
            status.error.unwrap_or_else( || "Batch job failed".to_string() )
          ) );
        }
        BatchJobState::Cancelled =>
        {
          return Err( Error::ApiError( "Batch job was cancelled".to_string() ) );
        }
        BatchJobState::Pending | BatchJobState::Running =>
        {
          // Check timeout
          if start.elapsed().unwrap_or( Duration::ZERO ) > timeout
          {
            return Err( Error::ApiError( "Batch job timeout".to_string() ) );
          }

          // Wait before next poll
          tokio ::time::sleep( poll_interval ).await;
        }
      }
    }
  }

  /// Retrieve results from a completed batch job.
  ///
  /// # Arguments
  ///
  /// * `job_id` - The batch job identifier
  ///
  /// # Returns
  ///
  /// Returns BatchJobResults with responses and billing info.
  ///
  /// # Errors
  ///
  /// Returns error if results not available or expired.
  async fn retrieve_results( &self, job_id : &str ) -> Result< BatchJobResults, Error >
  {
    let _ = job_id;
    Err( Error::NotImplemented( "Batch Mode API endpoints not yet available from Gemini".to_string() ) )
  }

  /// Cancel a running batch job.
  ///
  /// # Arguments
  ///
  /// * `job_id` - The batch job identifier
  ///
  /// # Errors
  ///
  /// Returns error if job cannot be cancelled or not found.
  pub async fn cancel( &self, job_id : &str ) -> Result< (), Error >
  {
    let _ = job_id;
    Err( Error::NotImplemented( "Batch Mode API endpoints not yet available from Gemini".to_string() ) )
  }

  /// List all batch jobs.
  ///
  /// # Returns
  ///
  /// Returns BatchJobList with jobs and optional next page token.
  ///
  /// # Errors
  ///
  /// Returns error if list operation fails.
  pub async fn list( &self ) -> Result< BatchJobList, Error >
  {
    self.list_with_page_size( None, None ).await
  }

  /// List batch jobs with pagination token.
  ///
  /// # Arguments
  ///
  /// * `page_token` - Token from previous list response
  ///
  /// # Returns
  ///
  /// Returns next page of batch jobs.
  ///
  /// # Errors
  ///
  /// Returns error if token invalid or list fails.
  pub async fn list_with_token( &self, page_token : &str ) -> Result< BatchJobList, Error >
  {
    self.list_with_page_size( None, Some( page_token.to_string() ) ).await
  }

  /// Internal list implementation with page size and token.
  async fn list_with_page_size(
    &self,
    _page_size : Option< i32 >,
    _page_token : Option< String >
  ) -> Result< BatchJobList, Error >
  {
    Err( Error::NotImplemented( "Batch Mode API endpoints not yet available from Gemini".to_string() ) )
  }

  /// Create a batch job for embedding generation.
  ///
  /// # Arguments
  ///
  /// * `model` - Embedding model name (e.g., "gemini-embedding-001")
  /// * `texts` - Vec of text strings to embed
  ///
  /// # Returns
  ///
  /// Returns BatchJob for polling and result retrieval.
  ///
  /// # Errors
  ///
  /// Returns error if job creation fails.
  pub async fn create_embedding_batch(
    &self,
    model : &str,
    texts : Vec< String >
  ) -> Result< BatchJob, Error >
  {
    let _ = ( model, texts );
    Err( Error::NotImplemented( "Batch Mode API endpoints not yet available from Gemini".to_string() ) )
  }

  /// Wait for embedding batch completion and retrieve results.
  ///
  /// # Arguments
  ///
  /// * `job_id` - The batch job identifier
  /// * `timeout` - Maximum time to wait
  ///
  /// # Returns
  ///
  /// Returns BatchEmbeddingResults with all embeddings.
  ///
  /// # Errors
  ///
  /// Returns error if timeout or job fails.
  pub async fn wait_and_retrieve_embeddings(
    &self,
    job_id : &str,
    timeout : Duration
  ) -> Result< BatchEmbeddingResults, Error >
  {
    let start = SystemTime::now();
    let poll_interval = Duration::from_secs( 5 );

    loop
    {
      let status = self.get_status( job_id ).await?;

      match status.state
      {
        BatchJobState::Succeeded | BatchJobState::PartiallyCompleted =>
        {
          return self.retrieve_embedding_results( job_id ).await;
        }
        BatchJobState::Failed =>
        {
          return Err( Error::ApiError(
            status.error.unwrap_or_else( || "Batch job failed".to_string() )
          ) );
        }
        BatchJobState::Cancelled =>
        {
          return Err( Error::ApiError( "Batch job was cancelled".to_string() ) );
        }
        BatchJobState::Pending | BatchJobState::Running =>
        {
          if start.elapsed().unwrap_or( Duration::ZERO ) > timeout
          {
            return Err( Error::ApiError( "Batch job timeout".to_string() ) );
          }

          tokio ::time::sleep( poll_interval ).await;
        }
      }
    }
  }

  /// Retrieve embedding results from completed job.
  async fn retrieve_embedding_results( &self, job_id : &str ) -> Result< BatchEmbeddingResults, Error >
  {
    let _ = job_id;
    Err( Error::NotImplemented( "Batch Mode API endpoints not yet available from Gemini".to_string() ) )
  }
}