use serde::{Deserialize, Serialize};
use crate::types::*;
use crate::types::{AgentId, MemoryId, SpaceId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MemoryType {
Episodic,
Semantic,
Procedural,
AntiPattern,
Reasoning,
Correction,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryNode {
pub id: MemoryId,
pub agent_id: AgentId,
pub memory_type: MemoryType,
pub embedding: Embedding,
pub content: String,
pub created_at: Timestamp,
pub accessed_at: Timestamp,
pub access_count: u32,
pub salience: Salience,
pub confidence: Confidence,
pub space_id: SpaceId,
pub attributes: std::collections::HashMap<String, AttributeValue>,
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub valid_from: Option<Timestamp>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub valid_until: Option<Timestamp>,
}
impl MemoryNode {
pub fn new(
agent_id: AgentId,
memory_type: MemoryType,
content: String,
embedding: Embedding,
) -> Self {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_micros() as u64;
Self {
id: MemoryId::new(),
agent_id,
memory_type,
embedding,
content,
created_at: now,
accessed_at: now,
access_count: 0,
salience: 1.0,
confidence: 1.0,
space_id: SpaceId::nil(),
attributes: std::collections::HashMap::new(),
tags: Vec::new(),
valid_from: None,
valid_until: None,
}
}
}
impl MemoryNode {
pub fn is_valid_at(&self, at: Timestamp) -> bool {
let from = self.valid_from.unwrap_or(0);
match self.valid_until {
Some(until) => at >= from && at < until,
None => at >= from,
}
}
pub fn invalidate(&mut self, at: Timestamp) {
self.valid_until = Some(at);
}
pub fn is_invalidated(&self) -> bool {
self.valid_until.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::AgentId;
#[test]
fn new_memory_has_no_temporal_bounds() {
let node = MemoryNode::new(
AgentId::new(),
MemoryType::Semantic,
"test".to_string(),
vec![1.0],
);
assert_eq!(node.valid_from, None);
assert_eq!(node.valid_until, None);
assert!(!node.is_invalidated());
}
#[test]
fn invalidate_memory() {
let mut node = MemoryNode::new(
AgentId::new(),
MemoryType::Semantic,
"Alice works at Acme".to_string(),
vec![1.0],
);
assert!(node.is_valid_at(node.created_at));
node.invalidate(node.created_at + 1_000_000);
assert!(node.is_invalidated());
assert!(node.is_valid_at(node.created_at));
assert!(!node.is_valid_at(node.created_at + 1_000_000));
}
#[test]
fn serde_backward_compatible() {
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":[]}"#;
let node: MemoryNode = serde_json::from_str(json).unwrap();
assert_eq!(node.valid_from, None);
assert_eq!(node.valid_until, None);
assert!(node.is_valid_at(5000));
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AttributeValue {
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Bytes(Vec<u8>),
}