use super::store::StoreItem;
use crate::utils::time;
pub const DEFAULT_DECAY_LAMBDA: f64 = 0.05;
pub const PRUNE_THRESHOLD: f32 = 1.0;
pub fn decay_score(item: &StoreItem) -> f32 {
decay_score_with_lambda(item, DEFAULT_DECAY_LAMBDA)
}
pub fn decay_score_with_lambda(item: &StoreItem, lambda: f64) -> f32 {
let Some(last_access) = item.last_accessed else {
return item.importance;
};
let now = time::now_secs();
let days_since = (now.saturating_sub(last_access)) as f64 / 86400.0;
let decay = (-lambda * days_since).exp();
(item.importance as f64 * decay) as f32
}
pub fn should_prune(item: &StoreItem) -> bool {
decay_score(item) < PRUNE_THRESHOLD
}
pub fn sort_by_decayed_score(items: &mut Vec<StoreItem>, limit: usize) {
items.sort_by(|a, b| {
decay_score(b)
.partial_cmp(&decay_score(a))
.unwrap_or(std::cmp::Ordering::Equal)
});
items.truncate(limit);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::store::StoreItem;
use serde_json::json;
#[test]
fn test_no_access_no_decay() {
let item = StoreItem::new(vec!["test".into()], "k1".into(), json!({"content": "test"}));
assert_eq!(decay_score(&item), 5.0); }
#[test]
fn test_recent_no_decay() {
let now = time::now_secs();
let item = StoreItem {
last_accessed: Some(now), importance: 10.0,
..StoreItem::new(vec!["t".into()], "k".into(), json!({}))
};
let score = decay_score(&item);
assert!(score > 9.9); }
#[test]
fn test_old_access_decays() {
let now = time::now_secs();
let thirty_days = now - 30 * 86400;
let item = StoreItem {
last_accessed: Some(thirty_days),
importance: 10.0,
..StoreItem::new(vec!["t".into()], "k".into(), json!({}))
};
let score = decay_score(&item);
assert!(score < 3.0 && score > 1.5);
}
#[test]
fn test_should_prune() {
let now = time::now_secs();
let old = now - 90 * 86400; let item = StoreItem {
last_accessed: Some(old),
importance: 5.0,
..StoreItem::new(vec!["t".into()], "k".into(), json!({}))
};
assert!(should_prune(&item));
}
}