1mod file_meta;
2
3pub use file_meta::{normalize_path, normalize_path_str, FileMetaStore};
4
5use moka::sync::Cache;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8
9pub struct EmbeddingCache {
11 cache: Cache<String, Arc<Vec<f32>>>,
12 hits: AtomicU64,
13 misses: AtomicU64,
14 max_memory_mb: usize,
15}
16
17impl EmbeddingCache {
18 #[allow(dead_code)] pub fn new(max_memory_mb: usize) -> Self {
20 let avg_embedding_size = 384 * std::mem::size_of::<f32>(); let max_entries = (max_memory_mb * 1024 * 1024) / avg_embedding_size;
23
24 let cache = Cache::builder()
25 .max_capacity(max_entries as u64)
26 .weigher(|_key: &String, value: &Arc<Vec<f32>>| {
27 (value.len() * std::mem::size_of::<f32>()) as u32
28 })
29 .build();
30
31 Self {
32 cache,
33 hits: AtomicU64::new(0),
34 misses: AtomicU64::new(0),
35 max_memory_mb,
36 }
37 }
38
39 #[allow(dead_code)] pub fn get_or_compute<F>(&self, key: &str, compute: F) -> Arc<Vec<f32>>
42 where
43 F: FnOnce() -> Vec<f32>,
44 {
45 if let Some(value) = self.cache.get(key) {
46 self.hits.fetch_add(1, Ordering::Relaxed);
47 value
48 } else {
49 self.misses.fetch_add(1, Ordering::Relaxed);
50 let value = Arc::new(compute());
51 self.cache.insert(key.to_string(), value.clone());
52 value
53 }
54 }
55
56 #[allow(dead_code)] pub fn hit_rate(&self) -> f64 {
59 let hits = self.hits.load(Ordering::Relaxed);
60 let misses = self.misses.load(Ordering::Relaxed);
61 let total = hits + misses;
62
63 if total == 0 {
64 0.0
65 } else {
66 hits as f64 / total as f64
67 }
68 }
69
70 #[allow(dead_code)] pub fn stats(&self) -> CacheStats {
73 CacheStats {
74 hits: self.hits.load(Ordering::Relaxed),
75 misses: self.misses.load(Ordering::Relaxed),
76 size: self.cache.entry_count(),
77 max_memory_mb: self.max_memory_mb,
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
83#[allow(dead_code)] pub struct CacheStats {
85 pub hits: u64,
86 pub misses: u64,
87 pub size: u64,
88 pub max_memory_mb: usize,
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_cache() {
97 let cache = EmbeddingCache::new(100);
98
99 let result = cache.get_or_compute("test", || vec![1.0, 2.0, 3.0]);
100 assert_eq!(*result, vec![1.0, 2.0, 3.0]);
101
102 let stats = cache.stats();
103 assert_eq!(stats.misses, 1);
104 assert_eq!(stats.hits, 0);
105 }
106}