use serde::{ Serialize, Deserialize };
use std::collections::HashMap;
use std::time::{ Duration, Instant };
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct CachedContentRequest
{
pub content_id : String,
pub model : String,
pub content : String,
pub content_type : ContentType,
pub ttl : Option< Duration >,
pub priority : CachePriority,
pub metadata : Option< serde_json::Value >,
}
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct CachedContentResponse
{
pub content_id : String,
pub content : String,
pub model : String,
pub content_type : ContentType,
pub cached_at : u64, pub expires_at : Option< u64 >,
pub access_count : u64,
pub performance_metrics : Option< CachePerformanceMetrics >,
pub metadata : Option< serde_json::Value >,
}
#[ derive( Debug, Clone, PartialEq, Serialize, Deserialize ) ]
pub enum ContentType
{
ChatConversation,
ModelResponse,
SystemInstruction,
UserPrompt,
FunctionCallResult,
EmbeddingsData,
GeneratedCode,
DocumentContent,
Custom( String ),
}
#[ derive( Debug, Clone, PartialEq, Serialize, Deserialize ) ]
pub enum CachePriority
{
Low,
Normal,
High,
Critical,
}
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct ContentCacheConfig
{
max_content_items : usize,
default_ttl : Duration,
max_content_size : usize,
memory_limit : usize,
intelligent_management : bool,
cleanup_interval : Duration,
performance_tracking : bool,
}
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct CacheInvalidationRequest
{
pub content_ids : Option< Vec< String > >,
pub model : Option< String >,
pub content_type : Option< ContentType >,
pub older_than : Option< u64 >,
pub priorities : Option< Vec< CachePriority > >,
pub force : bool,
}
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct CacheInvalidationResponse
{
pub invalidated_count : u64,
pub invalidated_ids : Vec< String >,
pub memory_freed : usize,
pub duration_ms : u64,
pub success : bool,
pub errors : Vec< String >,
}
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct CachePerformanceMetrics
{
pub hit_ratio : f64,
pub avg_retrieval_time_us : u64,
pub avg_storage_time_us : u64,
pub memory_efficiency : f64,
pub cache_pressure : f64,
pub evictions : u64,
}
#[ derive( Debug, Clone ) ]
pub struct IntelligentCacheManager
{
config : ContentCacheConfig,
content_store : HashMap< String, CachedContentResponse >,
access_patterns : HashMap< String, AccessPattern >,
performance_metrics : CachePerformanceMetrics,
#[ allow(dead_code) ]
last_cleanup : Instant,
}
#[ derive( Debug, Clone ) ]
struct AccessPattern
{
access_count : u64,
last_accessed : Instant,
access_frequency : f64, #[ allow(dead_code) ]
content_type : ContentType,
priority : CachePriority,
}
impl CachedContentRequest
{
#[ inline ]
#[ must_use ]
pub fn new( content_id : String, model : String, content : String, content_type : ContentType ) -> Self
{
Self
{
content_id,
model,
content,
content_type,
ttl : None,
priority : CachePriority::Normal,
metadata : None,
}
}
#[ inline ]
#[ must_use ]
pub fn with_ttl( mut self, ttl : Duration ) -> Self
{
self.ttl = Some( ttl );
self
}
#[ inline ]
#[ must_use ]
pub fn with_priority( mut self, priority : CachePriority ) -> Self
{
self.priority = priority;
self
}
#[ inline ]
#[ must_use ]
pub fn with_metadata( mut self, metadata : serde_json::Value ) -> Self
{
self.metadata = Some( metadata );
self
}
#[ inline ]
#[ must_use ]
pub fn estimate_size( &self ) -> usize
{
self.content_id.len() +
self.model.len() +
self.content.len() +
std ::mem::size_of::< ContentType >() +
std ::mem::size_of::< CachePriority >() +
self.metadata.as_ref().map_or( 0, | m | m.to_string().len() )
}
}
impl ContentType
{
#[ inline ]
#[ must_use ]
pub fn default_ttl( &self ) -> Duration
{
match self
{
ContentType::ChatConversation => Duration::from_secs( 3600 ), ContentType::ModelResponse => Duration::from_secs( 1800 ), ContentType::SystemInstruction => Duration::from_secs( 7200 ), ContentType::UserPrompt => Duration::from_secs( 1200 ), ContentType::FunctionCallResult => Duration::from_secs( 900 ), ContentType::EmbeddingsData => Duration::from_secs( 14400 ), ContentType::GeneratedCode => Duration::from_secs( 2400 ), ContentType::DocumentContent => Duration::from_secs( 10800 ), ContentType::Custom( _ ) => Duration::from_secs( 1800 ), }
}
#[ inline ]
#[ must_use ]
pub fn default_priority( &self ) -> CachePriority
{
match self
{
ContentType::SystemInstruction => CachePriority::High,
ContentType::EmbeddingsData => CachePriority::High,
ContentType::DocumentContent => CachePriority::Normal,
ContentType::GeneratedCode => CachePriority::Normal,
ContentType::ChatConversation => CachePriority::Normal,
ContentType::ModelResponse => CachePriority::Low,
ContentType::UserPrompt => CachePriority::Low,
ContentType::FunctionCallResult => CachePriority::Normal,
ContentType::Custom( _ ) => CachePriority::Normal,
}
}
}
impl CachePriority
{
#[ inline ]
#[ must_use ]
pub fn eviction_weight( &self ) -> u32
{
match self
{
CachePriority::Low => 1,
CachePriority::Normal => 10,
CachePriority::High => 100,
CachePriority::Critical => 1000,
}
}
}
impl ContentCacheConfig
{
#[ inline ]
#[ must_use ]
pub fn new() -> Self
{
Self
{
max_content_items : 1000,
default_ttl : Duration::from_secs( 1800 ), max_content_size : 1024 * 1024, memory_limit : 100 * 1024 * 1024, intelligent_management : true,
cleanup_interval : Duration::from_secs( 300 ), performance_tracking : true,
}
}
#[ inline ]
#[ must_use ]
pub fn with_max_items( mut self, max_items : usize ) -> Self
{
self.max_content_items = max_items;
self
}
#[ inline ]
#[ must_use ]
pub fn with_default_ttl( mut self, ttl : Duration ) -> Self
{
self.default_ttl = ttl;
self
}
#[ inline ]
#[ must_use ]
pub fn with_memory_limit( mut self, limit : usize ) -> Self
{
self.memory_limit = limit;
self
}
#[ inline ]
#[ must_use ]
pub fn with_intelligent_management( mut self, enabled : bool ) -> Self
{
self.intelligent_management = enabled;
self
}
#[ inline ]
#[ must_use ]
pub fn with_cleanup_interval( mut self, interval : Duration ) -> Self
{
self.cleanup_interval = interval;
self
}
#[ inline ]
#[ must_use ]
pub fn max_content_items( &self ) -> usize { self.max_content_items }
#[ inline ]
#[ must_use ]
pub fn default_ttl( &self ) -> Duration { self.default_ttl }
#[ inline ]
#[ must_use ]
pub fn max_content_size( &self ) -> usize { self.max_content_size }
#[ inline ]
#[ must_use ]
pub fn memory_limit( &self ) -> usize { self.memory_limit }
#[ inline ]
#[ must_use ]
pub fn intelligent_management( &self ) -> bool { self.intelligent_management }
#[ inline ]
#[ must_use ]
pub fn cleanup_interval( &self ) -> Duration { self.cleanup_interval }
#[ inline ]
#[ must_use ]
pub fn performance_tracking( &self ) -> bool { self.performance_tracking }
}
impl Default for ContentCacheConfig
{
fn default() -> Self
{
Self::new()
}
}
impl IntelligentCacheManager
{
#[ inline ]
#[ must_use ]
pub fn new( config : ContentCacheConfig ) -> Self
{
Self
{
config,
content_store : HashMap::new(),
access_patterns : HashMap::new(),
performance_metrics : CachePerformanceMetrics
{
hit_ratio : 0.0,
avg_retrieval_time_us : 0,
avg_storage_time_us : 0,
memory_efficiency : 1.0,
cache_pressure : 0.0,
evictions : 0,
},
last_cleanup : Instant::now(),
}
}
#[ inline ]
pub fn store_content( &mut self, request : CachedContentRequest ) -> Result< (), String >
{
let start_time = Instant::now();
let content_size = request.estimate_size();
if content_size > self.config.max_content_size
{
return Err( format!( "Content size {} exceeds limit {}", content_size, self.config.max_content_size ) );
}
if self.needs_eviction( content_size )
{
self.intelligent_eviction( content_size )?;
}
let cached_content = CachedContentResponse
{
content_id : request.content_id.clone(),
content : request.content,
model : request.model,
content_type : request.content_type.clone(),
cached_at : std::time::SystemTime::now().duration_since( std::time::UNIX_EPOCH ).unwrap().as_secs(),
expires_at : request.ttl.map( | ttl |
std ::time::SystemTime::now().duration_since( std::time::UNIX_EPOCH ).unwrap().as_secs() + ttl.as_secs()
),
access_count : 0,
performance_metrics : None,
metadata : request.metadata,
};
self.content_store.insert( request.content_id.clone(), cached_content );
self.access_patterns.insert( request.content_id, AccessPattern
{
access_count : 0,
last_accessed : Instant::now(),
access_frequency : 0.0,
content_type : request.content_type,
priority : request.priority,
} );
self.performance_metrics.avg_storage_time_us = start_time.elapsed().as_micros() as u64;
Ok( () )
}
#[ inline ]
pub fn retrieve_content( &mut self, content_id : &str ) -> Option< CachedContentResponse >
{
let start_time = Instant::now();
if let Some( mut content ) = self.content_store.get( content_id ).cloned()
{
if let Some( expires_at ) = content.expires_at
{
let now = std::time::SystemTime::now().duration_since( std::time::UNIX_EPOCH ).unwrap().as_secs();
if now > expires_at
{
self.content_store.remove( content_id );
self.access_patterns.remove( content_id );
return None;
}
}
if let Some( pattern ) = self.access_patterns.get_mut( content_id )
{
pattern.access_count += 1;
pattern.last_accessed = Instant::now();
let hours_since_first_access = pattern.last_accessed.duration_since( Instant::now() - Duration::from_secs( 3600 ) ).as_secs_f64() / 3600.0;
if hours_since_first_access > 0.0
{
pattern.access_frequency = pattern.access_count as f64 / hours_since_first_access;
}
}
content.access_count += 1;
self.content_store.insert( content_id.to_string(), content.clone() );
self.performance_metrics.avg_retrieval_time_us = start_time.elapsed().as_micros() as u64;
Some( content )
}
else
{
None
}
}
#[ inline ]
pub fn invalidate_content( &mut self, request : CacheInvalidationRequest ) -> CacheInvalidationResponse
{
let start_time = Instant::now();
let mut invalidated_ids = Vec::new();
let mut memory_freed = 0usize;
let mut errors = Vec::new();
let mut ids_to_remove = Vec::new();
for ( content_id, content ) in &self.content_store
{
let mut should_invalidate = false;
if let Some( ref target_ids ) = request.content_ids
{
if target_ids.contains( content_id )
{
should_invalidate = true;
}
}
if let Some( ref target_model ) = request.model
{
if content.model == *target_model
{
should_invalidate = true;
}
}
if let Some( ref target_type ) = request.content_type
{
if content.content_type == *target_type
{
should_invalidate = true;
}
}
if let Some( older_than ) = request.older_than
{
if content.cached_at < older_than
{
should_invalidate = true;
}
}
if let Some( ref target_priorities ) = request.priorities
{
if let Some( pattern ) = self.access_patterns.get( content_id )
{
if target_priorities.contains( &pattern.priority )
{
should_invalidate = true;
}
}
}
if should_invalidate
{
if let Some( pattern ) = self.access_patterns.get( content_id )
{
if pattern.priority == CachePriority::Critical && !request.force
{
should_invalidate = false;
errors.push( format!( "Cannot invalidate critical content '{}' without force flag", content_id ) );
}
}
}
if should_invalidate
{
ids_to_remove.push( content_id.clone() );
memory_freed += content.content.len() + content_id.len() + content.model.len();
}
}
for content_id in &ids_to_remove
{
self.content_store.remove( content_id );
self.access_patterns.remove( content_id );
invalidated_ids.push( content_id.clone() );
}
CacheInvalidationResponse
{
invalidated_count : invalidated_ids.len() as u64,
invalidated_ids,
memory_freed,
duration_ms : start_time.elapsed().as_millis() as u64,
success : errors.is_empty(),
errors,
}
}
#[ inline ]
#[ must_use ]
pub fn performance_metrics( &self ) -> &CachePerformanceMetrics
{
&self.performance_metrics
}
#[ inline ]
#[ must_use ]
pub fn cache_utilization( &self ) -> ( usize, usize, f64 )
{
let current_items = self.content_store.len();
let max_items = self.config.max_content_items;
let utilization = current_items as f64 / max_items as f64;
( current_items, max_items, utilization )
}
#[ inline ]
#[ must_use ]
fn needs_eviction( &self, additional_size : usize ) -> bool
{
let current_items = self.content_store.len();
let current_memory = self.estimate_memory_usage();
current_items >= self.config.max_content_items ||
current_memory + additional_size > self.config.memory_limit
}
#[ inline ]
fn intelligent_eviction( &mut self, space_needed : usize ) -> Result< (), String >
{
let mut eviction_candidates = Vec::new();
for ( content_id, _content ) in &self.content_store
{
if let Some( pattern ) = self.access_patterns.get( content_id )
{
let age_factor = pattern.last_accessed.elapsed().as_secs() as f64 / 3600.0; let frequency_factor = 1.0 / ( pattern.access_frequency + 1.0 ); let priority_factor = 1.0 / pattern.priority.eviction_weight() as f64;
let eviction_score = age_factor * frequency_factor * priority_factor;
eviction_candidates.push( ( content_id.clone(), eviction_score ) );
}
}
eviction_candidates.sort_by( | a, b | b.1.partial_cmp( &a.1 ).unwrap() );
let mut freed_space = 0usize;
let mut evicted_count = 0usize;
for ( content_id, _score ) in eviction_candidates
{
if let Some( content ) = self.content_store.get( &content_id )
{
freed_space += content.content.len() + content_id.len() + content.model.len();
self.content_store.remove( &content_id );
self.access_patterns.remove( &content_id );
evicted_count += 1;
if freed_space >= space_needed
{
break;
}
}
}
self.performance_metrics.evictions += evicted_count as u64;
if freed_space >= space_needed
{
Ok( () )
}
else
{
Err( format!( "Could not free enough space : needed {}, freed {}", space_needed, freed_space ) )
}
}
#[ inline ]
#[ must_use ]
fn estimate_memory_usage( &self ) -> usize
{
self.content_store.iter().map( | ( id, content ) |
id.len() + content.content.len() + content.model.len() +
std ::mem::size_of::< CachedContentResponse >()
).sum()
}
}