1use crate::server::protocol::RemoteRole;
4use serde::{Deserialize, Serialize};
5
6const PROBATION_DURATION_MS: i64 = 7 * 24 * 60 * 60 * 1_000;
7
8#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
10pub struct MemoryKey {
11 pub id: i64,
13 pub version: u64,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub namespace: Option<String>,
18}
19
20impl MemoryKey {
21 pub const fn local(id: i64, version: u64) -> Self {
23 Self {
24 id,
25 version,
26 namespace: None,
27 }
28 }
29
30 pub fn remote(namespace: String, id: i64, version: u64) -> Self {
32 Self {
33 id,
34 version,
35 namespace: Some(namespace),
36 }
37 }
38
39 pub const fn is_local(&self) -> bool {
41 self.namespace.is_none()
42 }
43}
44
45#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
47pub struct MemoryRecord {
48 pub key: MemoryKey,
50 pub content: String,
52 pub created_at_ms: i64,
54 pub updated_at_ms: i64,
56 pub last_scanned_at_ms: Option<i64>,
58 pub scan_count: u64,
60 pub last_used_at_ms: Option<i64>,
62 pub use_count: u64,
64 pub probation_until_ms: Option<i64>,
66}
67
68#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
70pub struct MemoryCandidate {
71 pub key: MemoryKey,
73 pub preview: String,
75 pub score: f64,
77}
78
79#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
81pub struct MemoryScan {
82 pub abstained: bool,
84 pub candidates: Vec<MemoryCandidate>,
86}
87
88impl MemoryScan {
89 pub fn rank(query: &str, memories: &[MemoryRecord], limit: usize) -> Self {
94 let candidates = crate::retrieval::rank(query, memories, limit);
95 Self {
96 abstained: candidates.is_empty(),
97 candidates,
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, Eq, PartialEq)]
104pub struct MemoryLimits {
105 pub content_bytes: usize,
107 pub records: usize,
109 pub total_content_bytes: usize,
111 pub database_bytes: usize,
113 pub scan_results: usize,
115 pub query_bytes: usize,
117 pub probation_duration_ms: i64,
119}
120
121impl MemoryLimits {
122 pub const PRODUCTION: Self = Self {
124 content_bytes: 1_024,
125 records: 512,
126 total_content_bytes: 256 * 1_024,
127 database_bytes: 4 * 1_024 * 1_024,
128 scan_results: 5,
129 query_bytes: 512,
130 probation_duration_ms: PROBATION_DURATION_MS,
131 };
132}
133
134#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
136#[serde(rename_all = "snake_case")]
137pub enum MemorySource {
138 Local,
140 Remote,
142}
143
144#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
146pub struct MemoryAccess {
147 pub source: MemorySource,
149 pub namespace: Option<String>,
151 pub role: Option<RemoteRole>,
153}
154
155#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
157pub struct MemoryImportReport {
158 pub inserted: usize,
160 pub skipped: usize,
162}
163
164#[derive(Clone, Debug)]
165pub(crate) struct StoredMemory {
166 pub(crate) namespace: Option<String>,
167 pub(crate) id: i64,
168 pub(crate) content: String,
169 pub(crate) created_at_ms: i64,
170 pub(crate) updated_at_ms: i64,
171 pub(crate) last_scanned_at_ms: Option<i64>,
172 pub(crate) scan_count: u64,
173 pub(crate) last_used_at_ms: Option<i64>,
174 pub(crate) use_count: u64,
175 pub(crate) probation_until_ms: Option<i64>,
176 pub(crate) version: u64,
177}
178
179impl StoredMemory {
180 pub(crate) fn key(&self) -> MemoryKey {
181 match &self.namespace {
182 Some(namespace) => MemoryKey::remote(namespace.clone(), self.id, self.version),
183 None => MemoryKey::local(self.id, self.version),
184 }
185 }
186}
187
188impl From<StoredMemory> for MemoryRecord {
189 fn from(memory: StoredMemory) -> Self {
190 Self {
191 key: memory.key(),
192 content: memory.content,
193 created_at_ms: memory.created_at_ms,
194 updated_at_ms: memory.updated_at_ms,
195 last_scanned_at_ms: memory.last_scanned_at_ms,
196 scan_count: memory.scan_count,
197 last_used_at_ms: memory.last_used_at_ms,
198 use_count: memory.use_count,
199 probation_until_ms: memory.probation_until_ms,
200 }
201 }
202}
203
204pub fn normalize_identity(content: &str) -> String {
206 content
207 .split_whitespace()
208 .map(str::to_lowercase)
209 .collect::<Vec<_>>()
210 .join(" ")
211}