Skip to main content

garudust_memory/
file_store.rs

1use std::path::{Path, PathBuf};
2
3use async_trait::async_trait;
4use garudust_core::{
5    config::MemoryExpiryConfig,
6    error::AgentError,
7    memory::{MemoryContent, MemoryStore},
8};
9
10pub struct FileMemoryStore {
11    memory_path: PathBuf,
12    profile_path: PathBuf,
13}
14
15impl FileMemoryStore {
16    pub fn new(home_dir: &Path) -> Self {
17        let memories = home_dir.join("memories");
18        Self {
19            memory_path: memories.join("MEMORY.md"),
20            profile_path: memories.join("USER.md"),
21        }
22    }
23
24    async fn read_file(&self, path: &PathBuf) -> Result<String, AgentError> {
25        match tokio::fs::read_to_string(path).await {
26            Ok(s) => Ok(s),
27            Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(String::new()),
28            Err(e) => Err(AgentError::Other(anyhow::anyhow!("{e}"))),
29        }
30    }
31
32    async fn write_file(&self, path: &PathBuf, content: &str) -> Result<(), AgentError> {
33        if let Some(parent) = path.parent() {
34            tokio::fs::create_dir_all(parent)
35                .await
36                .map_err(|e| AgentError::Other(anyhow::anyhow!("{e}")))?;
37        }
38        tokio::fs::write(path, content)
39            .await
40            .map_err(|e| AgentError::Other(anyhow::anyhow!("{e}")))
41    }
42
43    /// Expire old entries from MEMORY.md according to `config`.
44    /// Returns the number of entries removed (0 means no file write occurred).
45    pub async fn expire_entries(&self, config: &MemoryExpiryConfig) -> Result<usize, AgentError> {
46        let mut mem = self.read_memory().await?;
47        let removed = mem.expire(config);
48        if removed > 0 {
49            self.write_memory(&mem).await?;
50        }
51        Ok(removed)
52    }
53}
54
55#[async_trait]
56impl MemoryStore for FileMemoryStore {
57    async fn read_memory(&self) -> Result<MemoryContent, AgentError> {
58        let raw = self.read_file(&self.memory_path).await?;
59        Ok(MemoryContent::parse(&raw))
60    }
61
62    async fn write_memory(&self, content: &MemoryContent) -> Result<(), AgentError> {
63        self.write_file(&self.memory_path, &content.serialize())
64            .await
65    }
66
67    async fn read_user_profile(&self) -> Result<String, AgentError> {
68        self.read_file(&self.profile_path).await
69    }
70
71    async fn write_user_profile(&self, content: &str) -> Result<(), AgentError> {
72        self.write_file(&self.profile_path, content).await
73    }
74}