pub mod azure_blob;
pub mod gcs;
pub mod s3;
#[cfg(feature = "s3")]
pub use s3::{S3Cache, S3CacheConfig, S3StorageClass};
#[cfg(feature = "s3")]
pub use gcs::{GcsCache, GcsCacheConfig};
#[cfg(feature = "s3")]
pub use azure_blob::{AzureBlobCache, AzureBlobCacheConfig};
use async_trait::async_trait;
use serde::{Serialize, de::DeserializeOwned};
use std::time::Duration;
use super::types::CacheKey;
use crate::utils::error::gateway_error::Result;
#[async_trait]
pub trait CloudCache: Send + Sync {
async fn get<T: DeserializeOwned + Send>(&self, key: &CacheKey) -> Result<Option<T>>;
async fn set<T: Serialize + Send + Sync>(
&self,
key: &CacheKey,
value: &T,
ttl: Duration,
) -> Result<()>;
async fn delete(&self, key: &CacheKey) -> Result<bool>;
async fn exists(&self, key: &CacheKey) -> Result<bool>;
async fn list_keys(&self, prefix: &str) -> Result<Vec<String>>;
async fn clear(&self) -> Result<()>;
fn name(&self) -> &'static str;
}
#[derive(Debug, Clone)]
pub struct CloudCacheConfig {
pub bucket: String,
pub prefix: String,
pub default_ttl: Duration,
pub enable_compression: bool,
pub compression_threshold: usize,
}
impl Default for CloudCacheConfig {
fn default() -> Self {
Self {
bucket: String::new(),
prefix: "litellm-cache/".to_string(),
default_ttl: Duration::from_secs(3600),
enable_compression: true,
compression_threshold: 1024,
}
}
}
impl CloudCacheConfig {
pub fn new(bucket: impl Into<String>) -> Self {
Self {
bucket: bucket.into(),
..Default::default()
}
}
pub fn prefix(mut self, prefix: impl Into<String>) -> Self {
self.prefix = prefix.into();
self
}
pub fn default_ttl(mut self, ttl: Duration) -> Self {
self.default_ttl = ttl;
self
}
pub fn compression(mut self, enabled: bool) -> Self {
self.enable_compression = enabled;
self
}
pub fn compression_threshold(mut self, threshold: usize) -> Self {
self.compression_threshold = threshold;
self
}
}
#[derive(Debug, Clone, Serialize, serde::Deserialize)]
pub struct CacheMetadata {
pub expires_at: u64,
pub original_size: usize,
pub compressed: bool,
pub content_type: String,
}
impl CacheMetadata {
pub fn new(ttl: Duration, original_size: usize, compressed: bool) -> Self {
let expires_at = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
+ ttl.as_secs();
Self {
expires_at,
original_size,
compressed,
content_type: "application/json".to_string(),
}
}
pub fn is_expired(&self) -> bool {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
now >= self.expires_at
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cloud_cache_config_default() {
let config = CloudCacheConfig::default();
assert!(config.bucket.is_empty());
assert_eq!(config.prefix, "litellm-cache/");
assert_eq!(config.default_ttl, Duration::from_secs(3600));
assert!(config.enable_compression);
}
#[test]
fn test_cloud_cache_config_builder() {
let config = CloudCacheConfig::new("my-bucket")
.prefix("cache/")
.default_ttl(Duration::from_secs(7200))
.compression(false);
assert_eq!(config.bucket, "my-bucket");
assert_eq!(config.prefix, "cache/");
assert_eq!(config.default_ttl, Duration::from_secs(7200));
assert!(!config.enable_compression);
}
#[test]
fn test_cache_metadata_not_expired() {
let metadata = CacheMetadata::new(Duration::from_secs(3600), 100, false);
assert!(!metadata.is_expired());
}
#[test]
fn test_cache_metadata_expired() {
let mut metadata = CacheMetadata::new(Duration::from_secs(0), 100, false);
metadata.expires_at = 0; assert!(metadata.is_expired());
}
}