1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Fact {
6 pub subject: String,
7 pub relation: String,
8 pub object: String,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct Episode {
13 pub text: String,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub enum MemoryKind {
18 Fact(Fact),
19 Episode(Episode),
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct MemoryRecord {
24 pub id: i64,
25 pub kind: MemoryKind,
26 pub strength: f64,
27 pub embedding: Option<Vec<f32>>,
28 pub created_at: DateTime<Utc>,
29 pub last_accessed_at: DateTime<Utc>,
30 pub access_count: i64,
31 #[serde(default, skip_serializing_if = "Vec::is_empty")]
32 pub tags: Vec<String>,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub source: Option<String>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub session_id: Option<String>,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub channel: Option<String>,
39 #[serde(default = "default_importance")]
40 pub importance: f64,
41 #[serde(default = "default_namespace")]
42 pub namespace: String,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub checksum: Option<String>,
45}
46
47fn default_importance() -> f64 {
48 0.5
49}
50
51fn default_namespace() -> String {
52 "default".to_string()
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct MemoryStats {
57 pub total_memories: i64,
58 pub total_facts: i64,
59 pub total_episodes: i64,
60 pub avg_strength: f64,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ExportData {
65 pub memories: Vec<MemoryRecord>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct AuditEntry {
72 pub id: i64,
73 pub timestamp: DateTime<Utc>,
74 pub action: String,
75 pub memory_id: Option<i64>,
76 pub actor: String,
77 pub details_json: Option<String>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct VerifyResult {
84 pub total_checked: usize,
85 pub valid: usize,
86 pub corrupted: Vec<CorruptedMemory>,
87 pub missing_checksum: usize,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct CorruptedMemory {
92 pub id: i64,
93 pub expected: String,
94 pub actual: String,
95}
96
97#[derive(Debug, Clone, Serialize)]
100pub enum RememberResult {
101 Created(MemoryRecord),
103 Duplicate {
106 existing: MemoryRecord,
107 similarity: f32,
108 },
109 Updated(MemoryRecord),
111}
112
113impl RememberResult {
114 pub fn memory(&self) -> &MemoryRecord {
116 match self {
117 RememberResult::Created(m) => m,
118 RememberResult::Duplicate { existing, .. } => existing,
119 RememberResult::Updated(m) => m,
120 }
121 }
122
123 pub fn is_duplicate(&self) -> bool {
125 matches!(self, RememberResult::Duplicate { .. })
126 }
127
128 pub fn is_updated(&self) -> bool {
130 matches!(self, RememberResult::Updated(_))
131 }
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct GraphNode {
137 pub memory: MemoryRecord,
138 pub depth: usize,
140 pub connected_via: String,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct ProvenanceInfo {
147 pub memory: MemoryRecord,
148 pub created_at: String,
149 pub last_accessed_at: String,
150 pub access_count: i64,
151 pub strength: f64,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub source: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub session_id: Option<String>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub channel: Option<String>,
158 pub related: Vec<GraphNode>,
160}
161
162impl MemoryRecord {
163 pub fn text_for_embedding(&self) -> String {
164 match &self.kind {
165 MemoryKind::Fact(f) => format!("{} {} {}", f.subject, f.relation, f.object),
166 MemoryKind::Episode(e) => e.text.clone(),
167 }
168 }
169
170 pub fn subject(&self) -> Option<&str> {
171 match &self.kind {
172 MemoryKind::Fact(f) => Some(&f.subject),
173 MemoryKind::Episode(_) => None,
174 }
175 }
176
177 pub fn object(&self) -> Option<&str> {
179 match &self.kind {
180 MemoryKind::Fact(f) => Some(&f.object),
181 MemoryKind::Episode(_) => None,
182 }
183 }
184}