1use serde::{Deserialize, Serialize};
4
5use crate::types::*;
6use crate::types::{AgentId, MemoryId, SpaceId, UserId};
7
8fn default_user_id() -> UserId {
14 UserId::nil()
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum MemoryType {
20 Episodic,
22 Semantic,
24 Procedural,
26 AntiPattern,
28 Reasoning,
30 Correction,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct MemoryNode {
40 pub id: MemoryId,
42 pub agent_id: AgentId,
44 #[serde(default = "default_user_id")]
50 pub user_id: UserId,
51 pub memory_type: MemoryType,
53 pub embedding: Embedding,
55 pub content: String,
57 pub created_at: Timestamp,
59 pub accessed_at: Timestamp,
61 pub access_count: u32,
63 pub salience: Salience,
65 pub confidence: Confidence,
67 pub space_id: SpaceId,
69 pub attributes: std::collections::HashMap<String, AttributeValue>,
71 pub tags: Vec<String>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub valid_from: Option<Timestamp>,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub valid_until: Option<Timestamp>,
81}
82
83impl MemoryNode {
84 pub fn new(
86 agent_id: AgentId,
87 memory_type: MemoryType,
88 content: String,
89 embedding: Embedding,
90 ) -> Self {
91 let now = std::time::SystemTime::now()
92 .duration_since(std::time::UNIX_EPOCH)
93 .unwrap_or_default()
94 .as_micros() as u64;
95
96 Self {
97 id: MemoryId::new(),
98 agent_id,
99 user_id: UserId::nil(),
100 memory_type,
101 embedding,
102 content,
103 created_at: now,
104 accessed_at: now,
105 access_count: 0,
106 salience: 1.0,
107 confidence: 1.0,
108 space_id: SpaceId::nil(),
109 attributes: std::collections::HashMap::new(),
110 tags: Vec::new(),
111 valid_from: None,
112 valid_until: None,
113 }
114 }
115
116 pub fn with_user_id(mut self, user_id: UserId) -> Self {
121 self.user_id = user_id;
122 self
123 }
124}
125
126impl MemoryNode {
127 pub fn is_valid_at(&self, at: Timestamp) -> bool {
129 let from = self.valid_from.unwrap_or(0);
130 match self.valid_until {
131 Some(until) => at >= from && at < until,
132 None => at >= from,
133 }
134 }
135
136 pub fn invalidate(&mut self, at: Timestamp) {
138 self.valid_until = Some(at);
139 }
140
141 pub fn is_invalidated(&self) -> bool {
143 self.valid_until.is_some()
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::types::{AgentId, UserId};
151
152 #[test]
153 fn new_memory_has_no_temporal_bounds() {
154 let node = MemoryNode::new(
155 AgentId::new(),
156 MemoryType::Semantic,
157 "test".to_string(),
158 vec![1.0],
159 );
160 assert_eq!(node.valid_from, None);
161 assert_eq!(node.valid_until, None);
162 assert!(!node.is_invalidated());
163 }
164
165 #[test]
166 fn invalidate_memory() {
167 let mut node = MemoryNode::new(
168 AgentId::new(),
169 MemoryType::Semantic,
170 "Alice works at Acme".to_string(),
171 vec![1.0],
172 );
173 assert!(node.is_valid_at(node.created_at));
174
175 node.invalidate(node.created_at + 1_000_000);
176 assert!(node.is_invalidated());
177 assert!(node.is_valid_at(node.created_at));
178 assert!(!node.is_valid_at(node.created_at + 1_000_000));
179 }
180
181 #[test]
182 fn serde_backward_compatible() {
183 let json = r#"{"id":"00000000-0000-0000-0000-000000000001","agent_id":"00000000-0000-0000-0000-000000000002","memory_type":"Semantic","embedding":[1.0],"content":"test","created_at":1000,"accessed_at":1000,"access_count":0,"salience":1.0,"confidence":1.0,"space_id":"00000000-0000-0000-0000-000000000000","attributes":{},"tags":[]}"#;
184 let node: MemoryNode = serde_json::from_str(json).unwrap();
185 assert_eq!(node.valid_from, None);
186 assert_eq!(node.valid_until, None);
187 assert!(node.is_valid_at(5000));
188 assert!(
193 node.user_id.is_nil(),
194 "legacy memory (no user_id) must default to the nil user"
195 );
196 }
197
198 #[test]
199 fn with_user_id_sets_owner() {
200 let user = UserId::new();
201 let node = MemoryNode::new(
202 AgentId::new(),
203 MemoryType::Semantic,
204 "test".to_string(),
205 vec![1.0],
206 )
207 .with_user_id(user);
208 assert_eq!(node.user_id, user);
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub enum AttributeValue {
215 String(String),
217 Integer(i64),
219 Float(f64),
221 Boolean(bool),
223 Bytes(Vec<u8>),
225}