use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Fact {
pub subject: String,
pub relation: String,
pub object: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Episode {
pub text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MemoryKind {
Fact(Fact),
Episode(Episode),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryRecord {
pub id: i64,
pub kind: MemoryKind,
pub strength: f64,
pub embedding: Option<Vec<f32>>,
pub created_at: DateTime<Utc>,
pub last_accessed_at: DateTime<Utc>,
pub access_count: i64,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub channel: Option<String>,
#[serde(default = "default_importance")]
pub importance: f64,
#[serde(default = "default_namespace")]
pub namespace: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub checksum: Option<String>,
}
fn default_importance() -> f64 {
0.5
}
fn default_namespace() -> String {
"default".to_string()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryStats {
pub total_memories: i64,
pub total_facts: i64,
pub total_episodes: i64,
pub avg_strength: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportData {
pub memories: Vec<MemoryRecord>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEntry {
pub id: i64,
pub timestamp: DateTime<Utc>,
pub action: String,
pub memory_id: Option<i64>,
pub actor: String,
pub details_json: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifyResult {
pub total_checked: usize,
pub valid: usize,
pub corrupted: Vec<CorruptedMemory>,
pub missing_checksum: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorruptedMemory {
pub id: i64,
pub expected: String,
pub actual: String,
}
#[derive(Debug, Clone, Serialize)]
pub enum RememberResult {
Created(MemoryRecord),
Duplicate {
existing: MemoryRecord,
similarity: f32,
},
Updated(MemoryRecord),
}
impl RememberResult {
pub fn memory(&self) -> &MemoryRecord {
match self {
RememberResult::Created(m) => m,
RememberResult::Duplicate { existing, .. } => existing,
RememberResult::Updated(m) => m,
}
}
pub fn is_duplicate(&self) -> bool {
matches!(self, RememberResult::Duplicate { .. })
}
pub fn is_updated(&self) -> bool {
matches!(self, RememberResult::Updated(_))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphNode {
pub memory: MemoryRecord,
pub depth: usize,
pub connected_via: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProvenanceInfo {
pub memory: MemoryRecord,
pub created_at: String,
pub last_accessed_at: String,
pub access_count: i64,
pub strength: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub channel: Option<String>,
pub related: Vec<GraphNode>,
}
impl MemoryRecord {
pub fn text_for_embedding(&self) -> String {
match &self.kind {
MemoryKind::Fact(f) => format!("{} {} {}", f.subject, f.relation, f.object),
MemoryKind::Episode(e) => e.text.clone(),
}
}
pub fn subject(&self) -> Option<&str> {
match &self.kind {
MemoryKind::Fact(f) => Some(&f.subject),
MemoryKind::Episode(_) => None,
}
}
pub fn object(&self) -> Option<&str> {
match &self.kind {
MemoryKind::Fact(f) => Some(&f.object),
MemoryKind::Episode(_) => None,
}
}
}