totalreclaw_memory/
hotcache.rs1use crate::backend::MemoryEntry;
13
14pub struct HotCache {
18 inner: totalreclaw_core::hotcache::HotCache<Vec<MemoryEntry>>,
19}
20
21impl HotCache {
22 pub fn new() -> Self {
24 Self {
25 inner: totalreclaw_core::hotcache::HotCache::new(),
26 }
27 }
28
29 pub fn lookup(&self, query_embedding: &[f32]) -> Option<Vec<MemoryEntry>> {
33 self.inner.lookup(query_embedding).cloned()
34 }
35
36 pub fn insert(&mut self, query_embedding: Vec<f32>, results: Vec<MemoryEntry>) {
40 self.inner.insert(query_embedding, results);
41 }
42
43 pub fn clear(&mut self) {
45 self.inner.clear();
46 }
47
48 pub fn len(&self) -> usize {
50 self.inner.len()
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.inner.is_empty()
56 }
57}
58
59impl Default for HotCache {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::backend::MemoryCategory;
69
70 fn make_entry(id: &str, content: &str) -> MemoryEntry {
71 MemoryEntry {
72 id: id.into(),
73 key: id.into(),
74 content: content.into(),
75 category: MemoryCategory::Core,
76 timestamp: String::new(),
77 session_id: None,
78 score: Some(0.9),
79 }
80 }
81
82 #[test]
83 fn test_hot_cache_miss_then_hit() {
84 let mut cache = HotCache::new();
85
86 let embedding = vec![1.0f32, 0.0, 0.0, 0.0];
87 assert!(cache.lookup(&embedding).is_none());
88
89 let results = vec![make_entry("1", "test fact")];
90 cache.insert(embedding.clone(), results.clone());
91
92 let hit = cache.lookup(&embedding);
94 assert!(hit.is_some());
95 assert_eq!(hit.unwrap().len(), 1);
96 }
97
98 #[test]
99 fn test_hot_cache_similar_query_hit() {
100 let mut cache = HotCache::new();
101
102 let emb1 = vec![1.0f32, 0.0, 0.0, 0.0];
103 let results = vec![make_entry("1", "test fact")];
104 cache.insert(emb1, results);
105
106 let emb2 = vec![0.99f32, 0.1, 0.0, 0.0];
108 assert!(cache.lookup(&emb2).is_some());
109 }
110
111 #[test]
112 fn test_hot_cache_dissimilar_query_miss() {
113 let mut cache = HotCache::new();
114
115 let emb1 = vec![1.0f32, 0.0, 0.0, 0.0];
116 let results = vec![make_entry("1", "test fact")];
117 cache.insert(emb1, results);
118
119 let emb2 = vec![0.0f32, 1.0, 0.0, 0.0];
121 assert!(cache.lookup(&emb2).is_none());
122 }
123
124 #[test]
125 fn test_hot_cache_eviction() {
126 let mut cache = HotCache::new();
127
128 for i in 0..35 {
130 let emb = vec![i as f32, 0.0, 0.0, 0.0];
131 cache.insert(emb, vec![make_entry(&i.to_string(), "fact")]);
132 }
133
134 assert_eq!(cache.len(), 30);
135 }
136
137 #[test]
138 fn test_hot_cache_clear() {
139 let mut cache = HotCache::new();
140 cache.insert(vec![1.0f32], vec![make_entry("1", "fact")]);
141 assert_eq!(cache.len(), 1);
142
143 cache.clear();
144 assert!(cache.is_empty());
145 }
146}