Skip to main content

aster/memory/
memory_manager.rs

1//! 简单记忆管理器
2//!
3//! 持久化存储用户偏好和项目上下文
4
5use std::collections::HashMap;
6use std::fs;
7use std::path::{Path, PathBuf};
8
9use chrono::Utc;
10
11use super::types::{MemoryEntry, MemoryScope, SimpleMemoryStore, Timestamp};
12
13const MEMORY_VERSION: &str = "1.0.0";
14
15/// 获取当前时间戳
16fn now() -> Timestamp {
17    Utc::now().to_rfc3339()
18}
19
20/// 记忆管理器
21pub struct MemoryManager {
22    global_store_path: PathBuf,
23    project_store_path: PathBuf,
24    global_store: SimpleMemoryStore,
25    project_store: SimpleMemoryStore,
26}
27
28impl MemoryManager {
29    /// 创建新的记忆管理器
30    pub fn new(project_dir: Option<&Path>) -> Self {
31        let global_dir = dirs::home_dir()
32            .unwrap_or_default()
33            .join(".aster")
34            .join("memory");
35
36        let project_dir_path = project_dir
37            .map(|p| p.join(".aster").join("memory"))
38            .unwrap_or_else(|| {
39                std::env::current_dir()
40                    .unwrap_or_default()
41                    .join(".aster")
42                    .join("memory")
43            });
44
45        let global_store_path = global_dir.join("memory.json");
46        let project_store_path = project_dir_path.join("memory.json");
47
48        let global_store = Self::load_store(&global_store_path);
49        let project_store = Self::load_store(&project_store_path);
50
51        Self {
52            global_store_path,
53            project_store_path,
54            global_store,
55            project_store,
56        }
57    }
58
59    /// 设置记忆值
60    pub fn set(&mut self, key: &str, value: &str, scope: MemoryScope) {
61        let (store, store_path) = match scope {
62            MemoryScope::Global => (&mut self.global_store, &self.global_store_path),
63            MemoryScope::Project => (&mut self.project_store, &self.project_store_path),
64        };
65
66        let current_time = now();
67        let existing = store.entries.get(key);
68
69        let entry = MemoryEntry {
70            key: key.to_string(),
71            value: value.to_string(),
72            scope,
73            created_at: existing
74                .map(|e| e.created_at.clone())
75                .unwrap_or_else(|| current_time.clone()),
76            updated_at: current_time,
77        };
78
79        store.entries.insert(key.to_string(), entry);
80        Self::save_store(store_path, store);
81    }
82
83    /// 获取记忆值
84    pub fn get(&self, key: &str, scope: Option<MemoryScope>) -> Option<&str> {
85        match scope {
86            Some(MemoryScope::Global) => {
87                self.global_store.entries.get(key).map(|e| e.value.as_str())
88            }
89            Some(MemoryScope::Project) => self
90                .project_store
91                .entries
92                .get(key)
93                .map(|e| e.value.as_str()),
94            None => {
95                // 先查项目,再查全局
96                self.project_store
97                    .entries
98                    .get(key)
99                    .or_else(|| self.global_store.entries.get(key))
100                    .map(|e| e.value.as_str())
101            }
102        }
103    }
104
105    /// 删除记忆值
106    pub fn delete(&mut self, key: &str, scope: MemoryScope) -> bool {
107        let (store, store_path) = match scope {
108            MemoryScope::Global => (&mut self.global_store, &self.global_store_path),
109            MemoryScope::Project => (&mut self.project_store, &self.project_store_path),
110        };
111
112        if store.entries.remove(key).is_some() {
113            Self::save_store(store_path, store);
114            true
115        } else {
116            false
117        }
118    }
119
120    /// 列出所有记忆条目
121    pub fn list(&self, scope: Option<MemoryScope>) -> Vec<&MemoryEntry> {
122        let mut entries: Vec<&MemoryEntry> = Vec::new();
123
124        if scope != Some(MemoryScope::Project) {
125            entries.extend(self.global_store.entries.values());
126        }
127        if scope != Some(MemoryScope::Global) {
128            entries.extend(self.project_store.entries.values());
129        }
130
131        entries.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
132        entries
133    }
134
135    /// 清空记忆
136    pub fn clear(&mut self, scope: MemoryScope) {
137        let (store, store_path) = match scope {
138            MemoryScope::Global => (&mut self.global_store, &self.global_store_path),
139            MemoryScope::Project => (&mut self.project_store, &self.project_store_path),
140        };
141
142        store.entries.clear();
143        Self::save_store(store_path, store);
144    }
145
146    /// 获取记忆摘要(用于 system prompt)
147    pub fn get_summary(&self) -> String {
148        let entries = self.list(None);
149        if entries.is_empty() {
150            return String::new();
151        }
152
153        let lines: Vec<String> = entries
154            .iter()
155            .take(20)
156            .map(|e| format!("- {}: {}", e.key, e.value))
157            .collect();
158
159        format!("User Memory:\n{}", lines.join("\n"))
160    }
161
162    /// 搜索记忆
163    pub fn search(&self, query: &str) -> Vec<&MemoryEntry> {
164        let entries = self.list(None);
165        let lower_query = query.to_lowercase();
166
167        entries
168            .into_iter()
169            .filter(|e| {
170                e.key.to_lowercase().contains(&lower_query)
171                    || e.value.to_lowercase().contains(&lower_query)
172            })
173            .collect()
174    }
175
176    // === 私有方法 ===
177
178    fn load_store(path: &Path) -> SimpleMemoryStore {
179        if path.exists() {
180            if let Ok(content) = fs::read_to_string(path) {
181                if let Ok(store) = serde_json::from_str(&content) {
182                    return store;
183                }
184            }
185        }
186        SimpleMemoryStore {
187            entries: HashMap::new(),
188            version: MEMORY_VERSION.to_string(),
189        }
190    }
191
192    fn save_store(path: &Path, store: &SimpleMemoryStore) {
193        if let Some(parent) = path.parent() {
194            let _ = fs::create_dir_all(parent);
195        }
196        if let Ok(content) = serde_json::to_string_pretty(store) {
197            let _ = fs::write(path, content);
198        }
199    }
200}
201
202impl Default for MemoryManager {
203    fn default() -> Self {
204        Self::new(None)
205    }
206}