Skip to main content

dhive_core/
memory.rs

1//! Memory 数据结构 — D-HIVE 记忆内核
2//!
3//! Mem 结构体、Zone 语义分区、supersedes 超越链、KernelState 持久化格式。
4
5use serde::{Deserialize, Serialize};
6use crate::trust::TrustData;
7
8/// 置信度阶梯 (重新导出以保持模块独立)
9pub use crate::trust::Confidence;
10
11/// 记忆级别:activity(临时活动) < fact(持久事实) < cornerstone(基石原则)
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13#[serde(rename_all = "camelCase")]
14pub enum MemLevel {
15    Activity,
16    Fact,
17    Cornerstone,
18}
19
20impl MemLevel {
21    pub fn as_str(&self) -> &'static str {
22        match self {
23            MemLevel::Activity => "activity",
24            MemLevel::Fact => "fact",
25            MemLevel::Cornerstone => "cornerstone",
26        }
27    }
28
29    pub fn from_str(s: &str) -> Option<Self> {
30        match s {
31            "activity" => Some(MemLevel::Activity),
32            "fact" => Some(MemLevel::Fact),
33            "cornerstone" => Some(MemLevel::Cornerstone),
34            _ => None,
35        }
36    }
37}
38
39/// Zone 语义分区
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
41#[serde(rename_all = "camelCase")]
42pub enum MemoryZone {
43    /// 用户身份、偏好、核心原则(几乎不变)
44    Core,
45    /// 当前焦点、近期决策(中频更新)
46    Work,
47    /// 项目级约定和上下文(按项目隔离)
48    Project,
49    /// 会话摘要(高频写入)
50    Episode,
51    /// 未分类(兜底)
52    General,
53}
54
55impl MemoryZone {
56    pub fn as_str(&self) -> &'static str {
57        match self {
58            MemoryZone::Core => "core",
59            MemoryZone::Work => "work",
60            MemoryZone::Project => "project",
61            MemoryZone::Episode => "episode",
62            MemoryZone::General => "general",
63        }
64    }
65
66    pub fn from_str(s: &str) -> Option<Self> {
67        match s {
68            "core" => Some(MemoryZone::Core),
69            "work" => Some(MemoryZone::Work),
70            "project" => Some(MemoryZone::Project),
71            "episode" => Some(MemoryZone::Episode),
72            "general" => Some(MemoryZone::General),
73            _ => None,
74        }
75    }
76}
77
78impl Default for MemoryZone {
79    fn default() -> Self {
80        MemoryZone::General
81    }
82}
83
84/// 记忆条目 — D-HIVE 知识的基本单元
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct Mem {
87    /// 唯一标识符 (timestamp-nanoid 格式)
88    pub id: String,
89    /// 记忆级别
90    pub level: MemLevel,
91    /// Zone 语义分区 (默认 general)
92    #[serde(default)]
93    pub zone: MemoryZone,
94    /// 记忆内容 (最长 5000 字符)
95    pub content: String,
96    /// 记忆权重 0.0–1.0 (策展评分算法自动维护)
97    #[serde(default = "default_weight")]
98    pub weight: f64,
99    /// 标签列表
100    #[serde(default)]
101    pub tags: Vec<String>,
102    /// 创建来源
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub source: Option<String>,
105    /// 扩展元数据
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub metadata: Option<serde_json::Value>,
108    /// 上次搜索命中时间 (ISO 8601)
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub last_matched: Option<String>,
111    /// 搜索命中次数
112    #[serde(default)]
113    pub match_count: u32,
114    /// 被加载进 context 的次数
115    #[serde(default)]
116    pub loaded_count: u32,
117    /// 被引用的次数
118    #[serde(default)]
119    pub referenced_count: u32,
120    /// 上次加载时间
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub last_loaded: Option<String>,
123    /// 上次引用时间
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub last_referenced: Option<String>,
126    /// 被此记忆替代的旧记忆 ID 列表
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub supersedes: Option<Vec<String>>,
129    /// 发生替代的时间
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub superseded_at: Option<String>,
132    /// 替代此记忆的新记忆 ID (单链)
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub superseded_by: Option<String>,
135    /// 信任数据
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub trust: Option<TrustData>,
138    /// 创建时间 (ISO 8601)
139    pub created_at: String,
140    /// 最后更新时间 (ISO 8601)
141    pub updated_at: String,
142}
143
144fn default_weight() -> f64 { 0.5 }
145
146impl Mem {
147    /// 返回短 ID (后 8 位)
148    pub fn short_id(&self) -> &str {
149        if self.id.len() > 8 {
150            &self.id[self.id.len() - 8..]
151        } else {
152            &self.id
153        }
154    }
155
156    /// 此记忆是否被超越
157    pub fn is_superseded(&self) -> bool {
158        self.superseded_by.is_some()
159    }
160}
161
162/// 告警
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct Warning {
165    #[serde(rename = "memoryId")]
166    pub memory_id: String,
167    #[serde(rename = "cornerstoneContent")]
168    pub cornerstone_content: String,
169    pub violation: String,
170    pub severity: String,
171    pub timestamp: String,
172}
173
174/// 会话信息
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct SessionInfo {
177    pub id: String,
178    #[serde(rename = "startedAt")]
179    pub started_at: String,
180    #[serde(skip_serializing_if = "Option::is_none")]
181    #[serde(rename = "endedAt")]
182    pub ended_at: Option<String>,
183    #[serde(rename = "activityCount", default)]
184    pub activity_count: u32,
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub summary: Option<String>,
187}
188
189/// D-HIVE 内核状态 — kernel-state.json 的完整模型
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct KernelState {
192    #[serde(default)]
193    pub memories: Vec<Mem>,
194    #[serde(default, rename = "reflectedIds")]
195    pub reflected_ids: Vec<String>,
196    #[serde(skip_serializing_if = "Option::is_none", rename = "lastReflectionAt")]
197    pub last_reflection_at: Option<String>,
198    #[serde(skip_serializing_if = "Option::is_none", rename = "lastCuratedAt")]
199    pub last_curated_at: Option<String>,
200    #[serde(default)]
201    pub warnings: Vec<Warning>,
202    #[serde(default)]
203    pub sessions: Vec<SessionInfo>,
204    #[serde(default, rename = "pendingFacts")]
205    pub pending_facts: Vec<serde_json::Value>,
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub skills: Option<Vec<serde_json::Value>>,
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub goals: Option<Vec<serde_json::Value>>,
210}
211
212impl KernelState {
213    /// 加载 kernel-state.json 文件
214    pub fn load(path: &str) -> Result<Self, String> {
215        let content = std::fs::read_to_string(path)
216            .map_err(|e| format!("Failed to read {}: {}", path, e))?;
217        serde_json::from_str(&content)
218            .map_err(|e| format!("Failed to parse {}: {}", path, e))
219    }
220
221    /// 保存 kernel-state.json 文件
222    pub fn save(&self, path: &str) -> Result<(), String> {
223        let json = serde_json::to_string_pretty(self)
224            .map_err(|e| format!("Failed to serialize: {}", e))?;
225        std::fs::write(path, json)
226            .map_err(|e| format!("Failed to write {}: {}", path, e))
227    }
228
229    /// 返回活跃记忆列表 (未被超越的记忆)
230    pub fn list_active(&self) -> Vec<&Mem> {
231        self.memories.iter().filter(|m| !m.is_superseded()).collect()
232    }
233
234    /// 通过 ID (精确或后 8 位) 查找记忆
235    pub fn find_mem(&self, id: &str) -> Option<&Mem> {
236        self.memories.iter().find(|m| m.id == id || m.id.ends_with(id))
237    }
238
239    /// 通过 ID (精确或后 8 位) 查找可变引用
240    pub fn find_mem_mut(&mut self, id: &str) -> Option<&mut Mem> {
241        self.memories.iter_mut().find(|m| m.id == id || m.id.ends_with(id))
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248
249    #[test]
250    fn test_mem_level_roundtrip() {
251        assert_eq!(MemLevel::from_str("activity"), Some(MemLevel::Activity));
252        assert_eq!(MemLevel::from_str("fact"), Some(MemLevel::Fact));
253        assert_eq!(MemLevel::from_str("cornerstone"), Some(MemLevel::Cornerstone));
254        assert_eq!(MemLevel::from_str("invalid"), None);
255        assert_eq!(MemLevel::Activity.as_str(), "activity");
256    }
257
258    #[test]
259    fn test_memory_zone_roundtrip() {
260        assert_eq!(MemoryZone::from_str("core"), Some(MemoryZone::Core));
261        assert_eq!(MemoryZone::from_str("general"), Some(MemoryZone::General));
262        assert_eq!(MemoryZone::from_str("unknown"), None);
263    }
264
265    #[test]
266    fn test_short_id() {
267        let mem = Mem {
268            id: "1779000000001-abcdefgh".into(),
269            level: MemLevel::Fact,
270            zone: MemoryZone::General,
271            content: "test".into(),
272            weight: 0.5,
273            tags: vec![],
274            source: None,
275            metadata: None,
276            last_matched: None,
277            match_count: 0,
278            loaded_count: 0,
279            referenced_count: 0,
280            last_loaded: None,
281            last_referenced: None,
282            supersedes: None,
283            superseded_at: None,
284            superseded_by: None,
285            trust: None,
286            created_at: "2026-01-01T00:00:00Z".into(),
287            updated_at: "2026-01-01T00:00:00Z".into(),
288        };
289        assert_eq!(mem.short_id(), "abcdefgh");
290    }
291
292    #[test]
293    fn test_list_active_filters_superseded() {
294        let mut state = KernelState {
295            memories: vec![],
296            reflected_ids: vec![],
297            last_reflection_at: None,
298            last_curated_at: None,
299            warnings: vec![],
300            sessions: vec![],
301            pending_facts: vec![],
302            skills: None,
303            goals: None,
304        };
305
306        let m1 = Mem {
307            id: "m1".into(),
308            level: MemLevel::Fact,
309            zone: MemoryZone::General,
310            content: "active".into(),
311            weight: 0.5,
312            tags: vec![],
313            source: None, metadata: None,
314            last_matched: None, match_count: 0, loaded_count: 0, referenced_count: 0,
315            last_loaded: None, last_referenced: None,
316            supersedes: None, superseded_at: None, superseded_by: None,
317            trust: None,
318            created_at: "2026-01-01T00:00:00Z".into(),
319            updated_at: "2026-01-01T00:00:00Z".into(),
320        };
321        let m2 = Mem {
322            id: "m2".into(),
323            level: MemLevel::Fact,
324            zone: MemoryZone::General,
325            content: "superseded".into(),
326            weight: 0.5,
327            tags: vec![],
328            source: None, metadata: None,
329            last_matched: None, match_count: 0, loaded_count: 0, referenced_count: 0,
330            last_loaded: None, last_referenced: None,
331            supersedes: None, superseded_at: None,
332            superseded_by: Some("m3".into()),
333            trust: None,
334            created_at: "2026-01-01T00:00:00Z".into(),
335            updated_at: "2026-01-01T00:00:00Z".into(),
336        };
337        state.memories = vec![m1, m2];
338        let active = state.list_active();
339        assert_eq!(active.len(), 1);
340        assert_eq!(active[0].id, "m1");
341    }
342}