//! # Gemini SDK //! //! A simple Rust SDK for Google Gemini AI services. //! //! ## Features //! //! - **Simple Interface**: Send text + attachments to Gemini AI //! - **Type Safety**: Strong typing with compile-time error checking //! - **Easy Integration**: Minimal external dependencies //! //! ## Quick Start //! //! ```rust,no_run //! use gemini_sdk::{GeminiSDK, MessageRequest, Attachment, Agent}; //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { //! // Initialize SDK //! let mut sdk = GeminiSDK::new()?; //! //! // Create an agent (optional) //! let agent = Agent::new("assistant", "Assistant", "You are a helpful assistant"); //! sdk.add_agent(agent); //! //! // Send a message with attachment //! let request = MessageRequest { //! text: "Analyze this image".to_string(), //! attachments: vec![Attachment::new("./image.jpg")], //! agent_id: Some("assistant".to_string()), //! }; //! //! let response = sdk.send_message(request).await?; //! println!("Response: {}", response.content); //! //! Ok(()) //! } //! ``` pub mod sdk; pub mod agent; pub mod attachment; pub mod error; // Re-export main types for convenience pub use sdk::{GeminiSDK, MessageRequest, MessageResponse}; pub use agent::Agent; pub use attachment::Attachment; pub use error::{SDKError, Result}; /// SDK version pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// Default configuration for the SDK #[derive(Debug, Clone)] pub struct GeminiConfig { pub base_url: String, pub bearer_token: String, pub timeout: u64, pub model_name: String, pub max_retries: u32, pub retry_delay: u64, pub temperature: f32, pub max_tokens: u32, pub cloudflare_project_id: String, pub cloudflare_gateway_id: String, pub google_project_id: String, pub regions: Vec, } impl Default for GeminiConfig { fn default() -> Self { Self { base_url: "https://bowongai-dev--bowong-ai-video-gemini-fastapi-webapp.modal.run".to_string(), bearer_token: "bowong7777".to_string(), timeout: 120, model_name: "gemini-2.5-pro".to_string(), max_retries: 3, retry_delay: 2, temperature: 0.7, max_tokens: 64000, cloudflare_project_id: "67720b647ff2b55cf37ba3ef9e677083".to_string(), cloudflare_gateway_id: "bowong-dev".to_string(), google_project_id: "gen-lang-client-0413414134".to_string(), regions: vec![ "us-central1".to_string(), "us-east1".to_string(), "europe-west1".to_string(), ], } } } impl GeminiConfig { /// Create configuration from environment variables pub fn from_env() -> Self { Self { base_url: std::env::var("GEMINI_BASE_URL") .unwrap_or_else(|_| Self::default().base_url), bearer_token: std::env::var("GEMINI_BEARER_TOKEN") .unwrap_or_else(|_| Self::default().bearer_token), timeout: std::env::var("GEMINI_TIMEOUT") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(Self::default().timeout), model_name: std::env::var("GEMINI_MODEL_NAME") .unwrap_or_else(|_| Self::default().model_name), max_retries: std::env::var("GEMINI_MAX_RETRIES") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(Self::default().max_retries), retry_delay: std::env::var("GEMINI_RETRY_DELAY") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(Self::default().retry_delay), temperature: std::env::var("GEMINI_TEMPERATURE") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(Self::default().temperature), max_tokens: std::env::var("GEMINI_MAX_TOKENS") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(Self::default().max_tokens), cloudflare_project_id: std::env::var("GEMINI_CLOUDFLARE_PROJECT_ID") .unwrap_or_else(|_| Self::default().cloudflare_project_id), cloudflare_gateway_id: std::env::var("GEMINI_CLOUDFLARE_GATEWAY_ID") .unwrap_or_else(|_| Self::default().cloudflare_gateway_id), google_project_id: std::env::var("GEMINI_GOOGLE_PROJECT_ID") .unwrap_or_else(|_| Self::default().google_project_id), regions: std::env::var("GEMINI_REGIONS") .ok() .map(|s| s.split(',').map(|s| s.trim().to_string()).collect()) .unwrap_or_else(|| Self::default().regions), } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_config_default() { let config = GeminiConfig::default(); assert!(!config.base_url.is_empty()); assert!(!config.bearer_token.is_empty()); assert!(config.timeout > 0); assert!(!config.model_name.is_empty()); assert!(config.max_retries > 0); assert!(config.temperature >= 0.0 && config.temperature <= 2.0); assert!(config.max_tokens > 0); assert!(!config.regions.is_empty()); } #[test] fn test_config_from_env() { // Test that from_env doesn't panic and returns valid config let config = GeminiConfig::from_env(); assert!(!config.base_url.is_empty()); assert!(!config.bearer_token.is_empty()); } #[test] fn test_version() { assert!(!VERSION.is_empty()); } }