use crate::backend::MemoryEntry;
pub struct HotCache {
inner: totalreclaw_core::hotcache::HotCache<Vec<MemoryEntry>>,
}
impl HotCache {
pub fn new() -> Self {
Self {
inner: totalreclaw_core::hotcache::HotCache::new(),
}
}
pub fn lookup(&self, query_embedding: &[f32]) -> Option<Vec<MemoryEntry>> {
self.inner.lookup(query_embedding).cloned()
}
pub fn insert(&mut self, query_embedding: Vec<f32>, results: Vec<MemoryEntry>) {
self.inner.insert(query_embedding, results);
}
pub fn clear(&mut self) {
self.inner.clear();
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl Default for HotCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::MemoryCategory;
fn make_entry(id: &str, content: &str) -> MemoryEntry {
MemoryEntry {
id: id.into(),
key: id.into(),
content: content.into(),
category: MemoryCategory::Core,
timestamp: String::new(),
session_id: None,
score: Some(0.9),
}
}
#[test]
fn test_hot_cache_miss_then_hit() {
let mut cache = HotCache::new();
let embedding = vec![1.0f32, 0.0, 0.0, 0.0];
assert!(cache.lookup(&embedding).is_none());
let results = vec![make_entry("1", "test fact")];
cache.insert(embedding.clone(), results.clone());
let hit = cache.lookup(&embedding);
assert!(hit.is_some());
assert_eq!(hit.unwrap().len(), 1);
}
#[test]
fn test_hot_cache_similar_query_hit() {
let mut cache = HotCache::new();
let emb1 = vec![1.0f32, 0.0, 0.0, 0.0];
let results = vec![make_entry("1", "test fact")];
cache.insert(emb1, results);
let emb2 = vec![0.99f32, 0.1, 0.0, 0.0];
assert!(cache.lookup(&emb2).is_some());
}
#[test]
fn test_hot_cache_dissimilar_query_miss() {
let mut cache = HotCache::new();
let emb1 = vec![1.0f32, 0.0, 0.0, 0.0];
let results = vec![make_entry("1", "test fact")];
cache.insert(emb1, results);
let emb2 = vec![0.0f32, 1.0, 0.0, 0.0];
assert!(cache.lookup(&emb2).is_none());
}
#[test]
fn test_hot_cache_eviction() {
let mut cache = HotCache::new();
for i in 0..35 {
let emb = vec![i as f32, 0.0, 0.0, 0.0];
cache.insert(emb, vec![make_entry(&i.to_string(), "fact")]);
}
assert_eq!(cache.len(), 30);
}
#[test]
fn test_hot_cache_clear() {
let mut cache = HotCache::new();
cache.insert(vec![1.0f32], vec![make_entry("1", "fact")]);
assert_eq!(cache.len(), 1);
cache.clear();
assert!(cache.is_empty());
}
}