use crate::{
error::KowalskiError,
memory::{MemoryProvider, MemoryQuery, MemoryUnit},
};
use async_trait::async_trait;
use log::{debug, info};
pub struct WorkingMemory {
store: Vec<MemoryUnit>,
capacity: usize,
}
impl WorkingMemory {
pub fn new(capacity: usize) -> Self {
info!("Initializing working memory with capacity: {}", capacity);
Self {
store: Vec::with_capacity(capacity),
capacity,
}
}
pub fn len(&self) -> usize {
self.store.len()
}
pub fn is_empty(&self) -> bool {
self.store.is_empty()
}
}
#[async_trait]
impl MemoryProvider for WorkingMemory {
async fn add(&mut self, memory: MemoryUnit) -> Result<(), KowalskiError> {
info!("[WorkingMemory] Adding memory unit: {}", memory.id);
debug!("Adding memory unit to working memory: {}", memory.id);
if self.store.len() == self.capacity {
let removed = self.store.remove(0);
debug!(
"Working memory at capacity. Removed oldest unit: {}",
removed.id
);
}
self.store.push(memory);
Ok(())
}
async fn retrieve(
&self,
query: &str,
retrieval_limit: usize,
) -> Result<Vec<MemoryUnit>, KowalskiError> {
info!("[WorkingMemory][RETRIEVE] Query: '{}'", query);
for unit in &self.store {
info!("[WorkingMemory][RETRIEVE] Stored: '{}'", unit.content);
}
let lower_query = query.to_lowercase().trim().to_string();
let query_words: Vec<&str> = lower_query.split_whitespace().collect();
let mut results = Vec::new();
for unit in &self.store {
let content = unit.content.to_lowercase();
if query_words.iter().any(|w| content.contains(w)) {
results.push(unit.clone());
}
}
let results = if results.len() > retrieval_limit {
results[results.len() - retrieval_limit..].to_vec()
} else {
results
};
Ok(results)
}
async fn search(&self, query: MemoryQuery) -> Result<Vec<MemoryUnit>, KowalskiError> {
debug!("Searching working memory with query: {:?}", query);
self.retrieve(&query.text_query, query.top_k).await
}
}