use anyhow::Result;
use clawgarden_proto::{FileMemory, Memory, MemoryCategory, MemoryEntry};
use std::path::PathBuf;
pub fn create_memory(agent_name: &str) -> Result<Box<dyn Memory>> {
let config = clawgarden_proto::AppConfig::load();
let path = PathBuf::from(config.agents_dir())
.join(agent_name)
.join("memory.md");
Ok(Box::new(FileMemory::new(path)))
}
pub async fn load_memory(agent_name: &str) -> Result<String> {
let config = clawgarden_proto::AppConfig::load();
let path = PathBuf::from(config.agents_dir())
.join(agent_name)
.join("memory.md");
match tokio::fs::read_to_string(&path).await {
Ok(content) => {
log::info!(
"Loaded memory for agent '{}' from {}",
agent_name,
path.display()
);
Ok(content)
}
Err(e) => {
log::debug!(
"Memory file not found for agent '{}' at {} (optional): {}",
agent_name,
path.display(),
e
);
Ok(String::new())
}
}
}
pub fn recall_for_prompt(agent_name: &str, query: &str, limit: usize) -> String {
let mem = match create_memory(agent_name) {
Ok(m) => m,
Err(e) => {
log::debug!("Failed to create memory for recall: {}", e);
return String::new();
}
};
match mem.recall(query, limit) {
Ok(entries) if entries.is_empty() => String::new(),
Ok(entries) => {
let mut out = String::from("[Things you remember]\n");
for entry in &entries {
out.push_str(&format!("- {}: {}\n", entry.key, entry.content));
}
out
}
Err(e) => {
log::debug!("Memory recall failed: {}", e);
String::new()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_load_memory_missing_file() {
let result = load_memory("nonexistent_agent").await;
assert!(result.is_ok());
assert!(result.unwrap().is_empty());
}
#[test]
fn test_create_memory() {
let result = create_memory("test_agent");
assert!(result.is_ok());
}
#[test]
fn test_recall_for_prompt_no_file() {
let result = recall_for_prompt("nonexistent_agent_12345", "Rust", 5);
assert!(result.is_empty());
}
}