1use chrono::{DateTime, Utc};
2use pgvector::Vector;
3use serde::{Deserialize, Serialize};
4use sqlx::postgres::types::PgInterval;
5use sqlx::FromRow;
6use std::str::FromStr;
7use uuid::Uuid;
8
9#[derive(Debug, Clone)]
10pub struct SerializableVector(pub Option<Vector>);
11
12impl Serialize for SerializableVector {
13 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
14 where
15 S: serde::Serializer,
16 {
17 match &self.0 {
18 Some(v) => v.as_slice().serialize(serializer),
19 None => serializer.serialize_none(),
20 }
21 }
22}
23
24impl<'de> Deserialize<'de> for SerializableVector {
25 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
26 where
27 D: serde::Deserializer<'de>,
28 {
29 let opt_vec: Option<Vec<f32>> = Option::deserialize(deserializer)?;
30 Ok(SerializableVector(opt_vec.map(Vector::from)))
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
35#[sqlx(type_name = "varchar", rename_all = "lowercase")]
36pub enum MemoryTier {
37 Working,
38 Warm,
39 Cold,
40 Frozen,
41}
42
43impl FromStr for MemoryTier {
44 type Err = String;
45
46 fn from_str(s: &str) -> Result<Self, Self::Err> {
47 match s.to_lowercase().as_str() {
48 "working" => Ok(MemoryTier::Working),
49 "warm" => Ok(MemoryTier::Warm),
50 "cold" => Ok(MemoryTier::Cold),
51 "frozen" => Ok(MemoryTier::Frozen),
52 _ => Err(format!("Invalid memory tier: {s}")),
53 }
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
58#[sqlx(type_name = "varchar", rename_all = "lowercase")]
59pub enum MemoryStatus {
60 Active,
61 Migrating,
62 Archived,
63 Deleted,
64}
65
66#[derive(Debug, Clone, FromRow)]
67pub struct Memory {
68 pub id: Uuid,
69 pub content: String,
70 pub content_hash: String,
71 pub embedding: Option<Vector>,
72 pub tier: MemoryTier,
73 pub status: MemoryStatus,
74 pub importance_score: f64,
75 pub access_count: i32,
76 pub last_accessed_at: Option<DateTime<Utc>>,
77 pub metadata: serde_json::Value,
78 pub parent_id: Option<Uuid>,
79 pub created_at: DateTime<Utc>,
80 pub updated_at: DateTime<Utc>,
81 pub expires_at: Option<DateTime<Utc>>,
82 pub consolidation_strength: f64,
84 pub decay_rate: f64,
85 pub recall_probability: Option<f64>,
86 pub last_recall_interval: Option<PgInterval>,
87 pub recency_score: f64,
89 pub relevance_score: f64,
90}
91
92impl Serialize for Memory {
93 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
94 where
95 S: serde::Serializer,
96 {
97 use serde::ser::SerializeStruct;
98 let mut state = serializer.serialize_struct("Memory", 21)?;
99 state.serialize_field("id", &self.id)?;
100 state.serialize_field("content", &self.content)?;
101 state.serialize_field("content_hash", &self.content_hash)?;
102 state.serialize_field("embedding", &self.embedding.as_ref().map(|v| v.as_slice()))?;
103 state.serialize_field("tier", &self.tier)?;
104 state.serialize_field("status", &self.status)?;
105 state.serialize_field("importance_score", &self.importance_score)?;
106 state.serialize_field("access_count", &self.access_count)?;
107 state.serialize_field("last_accessed_at", &self.last_accessed_at)?;
108 state.serialize_field("metadata", &self.metadata)?;
109 state.serialize_field("parent_id", &self.parent_id)?;
110 state.serialize_field("created_at", &self.created_at)?;
111 state.serialize_field("updated_at", &self.updated_at)?;
112 state.serialize_field("expires_at", &self.expires_at)?;
113 state.serialize_field("consolidation_strength", &self.consolidation_strength)?;
114 state.serialize_field("decay_rate", &self.decay_rate)?;
115 state.serialize_field("recall_probability", &self.recall_probability)?;
116 state.serialize_field(
117 "last_recall_interval",
118 &self.last_recall_interval.as_ref().map(|i| i.microseconds),
119 )?;
120 state.serialize_field("recency_score", &self.recency_score)?;
121 state.serialize_field("relevance_score", &self.relevance_score)?;
122 state.end()
123 }
124}
125
126impl<'de> Deserialize<'de> for Memory {
127 fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
128 where
129 D: serde::Deserializer<'de>,
130 {
131 Ok(Memory::default())
133 }
134}
135
136#[derive(Debug, Clone, FromRow)]
137pub struct MemorySummary {
138 pub id: Uuid,
139 pub summary_level: String,
140 pub summary_content: String,
141 pub summary_embedding: Option<Vector>,
142 pub start_time: DateTime<Utc>,
143 pub end_time: DateTime<Utc>,
144 pub memory_count: i32,
145 pub metadata: serde_json::Value,
146 pub created_at: DateTime<Utc>,
147 pub updated_at: DateTime<Utc>,
148}
149
150impl Serialize for MemorySummary {
151 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
152 where
153 S: serde::Serializer,
154 {
155 use serde::ser::SerializeStruct;
156 let mut state = serializer.serialize_struct("MemorySummary", 10)?;
157 state.serialize_field("id", &self.id)?;
158 state.serialize_field("summary_level", &self.summary_level)?;
159 state.serialize_field("summary_content", &self.summary_content)?;
160 state.serialize_field(
161 "summary_embedding",
162 &self.summary_embedding.as_ref().map(|v| v.as_slice()),
163 )?;
164 state.serialize_field("start_time", &self.start_time)?;
165 state.serialize_field("end_time", &self.end_time)?;
166 state.serialize_field("memory_count", &self.memory_count)?;
167 state.serialize_field("metadata", &self.metadata)?;
168 state.serialize_field("created_at", &self.created_at)?;
169 state.serialize_field("updated_at", &self.updated_at)?;
170 state.end()
171 }
172}
173
174impl<'de> Deserialize<'de> for MemorySummary {
175 fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
176 where
177 D: serde::Deserializer<'de>,
178 {
179 unimplemented!("MemorySummary deserialization not needed")
180 }
181}
182
183#[derive(Debug, Clone, FromRow)]
184pub struct MemoryCluster {
185 pub id: Uuid,
186 pub cluster_name: String,
187 pub centroid_embedding: Vector,
188 pub concept_tags: Vec<String>,
189 pub member_count: i32,
190 pub tier: MemoryTier,
191 pub metadata: serde_json::Value,
192 pub created_at: DateTime<Utc>,
193 pub updated_at: DateTime<Utc>,
194}
195
196impl Serialize for MemoryCluster {
197 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
198 where
199 S: serde::Serializer,
200 {
201 use serde::ser::SerializeStruct;
202 let mut state = serializer.serialize_struct("MemoryCluster", 9)?;
203 state.serialize_field("id", &self.id)?;
204 state.serialize_field("cluster_name", &self.cluster_name)?;
205 state.serialize_field("centroid_embedding", &self.centroid_embedding.as_slice())?;
206 state.serialize_field("concept_tags", &self.concept_tags)?;
207 state.serialize_field("member_count", &self.member_count)?;
208 state.serialize_field("tier", &self.tier)?;
209 state.serialize_field("metadata", &self.metadata)?;
210 state.serialize_field("created_at", &self.created_at)?;
211 state.serialize_field("updated_at", &self.updated_at)?;
212 state.end()
213 }
214}
215
216impl<'de> Deserialize<'de> for MemoryCluster {
217 fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
218 where
219 D: serde::Deserializer<'de>,
220 {
221 unimplemented!("MemoryCluster deserialization not needed")
222 }
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
226pub struct MigrationHistoryEntry {
227 pub id: Uuid,
228 pub memory_id: Uuid,
229 pub from_tier: MemoryTier,
230 pub to_tier: MemoryTier,
231 pub migration_reason: Option<String>,
232 pub migrated_at: DateTime<Utc>,
233 pub migration_duration_ms: Option<i32>,
234 pub success: bool,
235 pub error_message: Option<String>,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct CreateMemoryRequest {
240 pub content: String,
241 pub embedding: Option<Vec<f32>>,
242 pub tier: Option<MemoryTier>,
243 pub importance_score: Option<f64>,
244 pub metadata: Option<serde_json::Value>,
245 pub parent_id: Option<Uuid>,
246 pub expires_at: Option<DateTime<Utc>>,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct UpdateMemoryRequest {
251 pub content: Option<String>,
252 pub embedding: Option<Vec<f32>>,
253 pub tier: Option<MemoryTier>,
254 pub importance_score: Option<f64>,
255 pub metadata: Option<serde_json::Value>,
256 pub expires_at: Option<DateTime<Utc>>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, Default)]
260pub struct SearchRequest {
261 pub query_text: Option<String>,
263 pub query_embedding: Option<Vec<f32>>,
264
265 pub search_type: Option<SearchType>,
267 pub hybrid_weights: Option<HybridWeights>,
268
269 pub tier: Option<MemoryTier>,
271 pub date_range: Option<DateRange>,
272 pub importance_range: Option<RangeFilter<f32>>,
273 pub metadata_filters: Option<serde_json::Value>,
274 pub tags: Option<Vec<String>>,
275
276 pub limit: Option<i32>,
278 pub offset: Option<i64>, pub cursor: Option<String>, pub similarity_threshold: Option<f32>,
281 pub include_metadata: Option<bool>,
282 pub include_facets: Option<bool>,
283
284 pub ranking_boost: Option<RankingBoost>,
286 pub explain_score: Option<bool>,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub enum SearchType {
291 Semantic,
292 Temporal,
293 Hybrid,
294 FullText,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct HybridWeights {
299 pub semantic_weight: f32,
300 pub temporal_weight: f32,
301 pub importance_weight: f32,
302 pub access_frequency_weight: f32,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct DateRange {
307 pub start: Option<DateTime<Utc>>,
308 pub end: Option<DateTime<Utc>>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct RangeFilter<T> {
313 pub min: Option<T>,
314 pub max: Option<T>,
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct RankingBoost {
319 pub recency_boost: Option<f32>,
320 pub importance_boost: Option<f32>,
321 pub access_frequency_boost: Option<f32>,
322 pub tier_boost: Option<std::collections::HashMap<MemoryTier, f32>>,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct SearchResult {
327 pub memory: Memory,
328 pub similarity_score: f32,
329 pub temporal_score: Option<f32>,
330 pub importance_score: f64,
331 pub access_frequency_score: Option<f32>,
332 pub combined_score: f32,
333 pub score_explanation: Option<ScoreExplanation>,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct ScoreExplanation {
338 pub semantic_contribution: f32,
339 pub temporal_contribution: f32,
340 pub importance_contribution: f32,
341 pub access_frequency_contribution: f32,
342 pub total_score: f32,
343 pub factors: Vec<String>,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct SearchResponse {
348 pub results: Vec<SearchResult>,
349 pub total_count: Option<i64>,
350 pub facets: Option<SearchFacets>,
351 pub suggestions: Option<Vec<String>>,
352 pub next_cursor: Option<String>,
353 pub execution_time_ms: u64,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct SearchFacets {
358 pub tiers: std::collections::HashMap<MemoryTier, i64>,
359 pub date_histogram: Vec<DateBucket>,
360 pub importance_ranges: Vec<ImportanceRange>,
361 pub tags: std::collections::HashMap<String, i64>,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct DateBucket {
366 pub date: DateTime<Utc>,
367 pub count: i64,
368 pub interval: String, }
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
372pub struct ImportanceRange {
373 pub min: f32,
374 pub max: f32,
375 pub count: i64,
376 pub label: String,
377}
378
379impl Default for Memory {
380 fn default() -> Self {
381 Self {
382 id: Uuid::new_v4(),
383 content: String::new(),
384 content_hash: String::new(),
385 embedding: None,
386 tier: MemoryTier::Working,
387 status: MemoryStatus::Active,
388 importance_score: 0.5,
389 access_count: 0,
390 last_accessed_at: None,
391 metadata: serde_json::json!({}),
392 parent_id: None,
393 created_at: Utc::now(),
394 updated_at: Utc::now(),
395 expires_at: None,
396 consolidation_strength: 1.0,
397 decay_rate: 1.0,
398 recall_probability: None,
399 last_recall_interval: None,
400 recency_score: 0.0,
401 relevance_score: 0.0,
402 }
403 }
404}
405
406impl Memory {
407 pub fn calculate_content_hash(content: &str) -> String {
408 use sha2::{Digest, Sha256};
409 let mut hasher = Sha256::new();
410 hasher.update(content.as_bytes());
411 hex::encode(hasher.finalize())
412 }
413
414 pub fn recall_count(&self) -> i32 {
416 self.access_count
417 }
418
419 pub fn should_migrate(&self) -> bool {
420 use crate::memory::simple_consolidation::{
421 SimpleConsolidationConfig, SimpleConsolidationEngine,
422 };
423
424 if matches!(self.tier, MemoryTier::Frozen) {
426 return false;
427 }
428
429 let config = SimpleConsolidationConfig::default();
430 let engine = SimpleConsolidationEngine::new(config);
431
432 match engine.calculate_recall_probability(self, None) {
434 Ok(recall_prob) => recall_prob < 0.86, Err(_) => {
436 match self.tier {
438 MemoryTier::Working => {
439 self.importance_score < 0.3
440 || (self.last_accessed_at.is_some()
441 && Utc::now()
442 .signed_duration_since(self.last_accessed_at.unwrap())
443 .num_hours()
444 > 24)
445 }
446 MemoryTier::Warm => {
447 self.importance_score < 0.1
448 && Utc::now().signed_duration_since(self.updated_at).num_days() > 7
449 }
450 MemoryTier::Cold => {
451 Utc::now().signed_duration_since(self.updated_at).num_days() > 30
452 }
453 MemoryTier::Frozen => false,
454 }
455 }
456 }
457 }
458
459 pub fn next_tier(&self) -> Option<MemoryTier> {
460 match self.tier {
461 MemoryTier::Working => Some(MemoryTier::Warm),
462 MemoryTier::Warm => Some(MemoryTier::Cold),
463 MemoryTier::Cold => Some(MemoryTier::Frozen),
464 MemoryTier::Frozen => None,
465 }
466 }
467
468 pub fn calculate_recall_probability(&self) -> Option<f64> {
472 use crate::memory::math_engine::{MathEngine, MemoryParameters};
473
474 let engine = MathEngine::new();
475 let params = MemoryParameters {
476 consolidation_strength: self.consolidation_strength,
477 decay_rate: self.decay_rate,
478 last_accessed_at: self.last_accessed_at,
479 created_at: self.created_at,
480 access_count: self.access_count,
481 importance_score: self.importance_score,
482 };
483
484 match engine.calculate_recall_probability(¶ms) {
485 Ok(result) => Some(result.recall_probability),
486 Err(e) => {
487 tracing::warn!(
488 "Recall probability calculation failed for memory {}: {}. Using fallback.",
489 self.id,
490 e
491 );
492 let fallback = (self.importance_score * self.consolidation_strength / 10.0)
494 .min(1.0)
495 .max(0.0);
496 Some(fallback)
497 }
498 }
499 }
500
501 pub fn update_consolidation_strength(&mut self, recall_interval: PgInterval) {
505 use crate::memory::math_engine::MathEngine;
506
507 let engine = MathEngine::new();
508
509 match engine.update_consolidation_strength(self.consolidation_strength, recall_interval) {
510 Ok(result) => {
511 self.consolidation_strength = result.new_consolidation_strength;
512 }
513 Err(_) => {
514 let time_hours = recall_interval.microseconds as f64 / 3_600_000_000.0;
516 let increment = time_hours.min(1.0) * 0.1; self.consolidation_strength = (self.consolidation_strength + increment).min(10.0);
518 }
519 }
520 }
521}
522
523#[derive(Debug, Clone, FromRow)]
526pub struct MemoryConsolidationLog {
527 pub id: Uuid,
528 pub memory_id: Uuid,
529 pub event_type: String,
530 pub previous_consolidation_strength: f64,
531 pub new_consolidation_strength: f64,
532 pub previous_recall_probability: Option<f64>,
533 pub new_recall_probability: Option<f64>,
534 pub recall_interval: Option<PgInterval>,
535 pub access_context: serde_json::Value,
536 pub created_at: DateTime<Utc>,
537}
538
539impl Serialize for MemoryConsolidationLog {
540 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
541 where
542 S: serde::Serializer,
543 {
544 use serde::ser::SerializeStruct;
545 let mut state = serializer.serialize_struct("MemoryConsolidationLog", 10)?;
546 state.serialize_field("id", &self.id)?;
547 state.serialize_field("memory_id", &self.memory_id)?;
548 state.serialize_field("event_type", &self.event_type)?;
549 state.serialize_field(
550 "previous_consolidation_strength",
551 &self.previous_consolidation_strength,
552 )?;
553 state.serialize_field(
554 "new_consolidation_strength",
555 &self.new_consolidation_strength,
556 )?;
557 state.serialize_field(
558 "previous_recall_probability",
559 &self.previous_recall_probability,
560 )?;
561 state.serialize_field("new_recall_probability", &self.new_recall_probability)?;
562 state.serialize_field(
563 "recall_interval_microseconds",
564 &self.recall_interval.as_ref().map(|i| i.microseconds),
565 )?;
566 state.serialize_field("access_context", &self.access_context)?;
567 state.serialize_field("created_at", &self.created_at)?;
568 state.end()
569 }
570}
571
572#[derive(Debug, Clone, FromRow)]
573pub struct FrozenMemory {
574 pub id: Uuid,
575 pub original_memory_id: Uuid,
576 pub compressed_content: serde_json::Value, pub original_metadata: Option<serde_json::Value>, pub freeze_reason: Option<String>,
579 pub frozen_at: DateTime<Utc>,
580 pub unfreeze_count: Option<i32>, pub last_unfrozen_at: Option<DateTime<Utc>>, pub compression_ratio: Option<f64>,
583}
584
585impl Serialize for FrozenMemory {
586 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
587 where
588 S: serde::Serializer,
589 {
590 use serde::ser::SerializeStruct;
591 let mut state = serializer.serialize_struct("FrozenMemory", 9)?;
592 state.serialize_field("id", &self.id)?;
593 state.serialize_field("original_memory_id", &self.original_memory_id)?;
594 state.serialize_field("compressed_content", &self.compressed_content)?;
595 state.serialize_field("original_metadata", &self.original_metadata)?;
596 state.serialize_field("freeze_reason", &self.freeze_reason)?;
597 state.serialize_field("frozen_at", &self.frozen_at)?;
598 state.serialize_field("unfreeze_count", &self.unfreeze_count)?;
599 state.serialize_field("last_unfrozen_at", &self.last_unfrozen_at)?;
600 state.serialize_field("compression_ratio", &self.compression_ratio)?;
601 state.end()
602 }
603}
604
605#[derive(Debug, Clone, FromRow, Serialize, Deserialize)]
606pub struct MemoryTierStatistics {
607 pub id: Uuid,
608 pub tier: MemoryTier,
609 pub total_memories: i64,
610 pub average_consolidation_strength: Option<f64>,
611 pub average_recall_probability: Option<f64>,
612 pub average_age_days: Option<f64>,
613 pub total_storage_bytes: i64,
614 pub snapshot_timestamp: DateTime<Utc>,
615}
616
617#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
618pub struct ConsolidationAnalytics {
619 pub tier: MemoryTier,
620 pub total_memories: i64,
621 pub avg_consolidation_strength: Option<f64>,
622 pub avg_recall_probability: Option<f64>,
623 pub avg_decay_rate: Option<f64>,
624 pub avg_age_days: Option<f64>,
625 pub migration_candidates: i64,
626 pub never_accessed: i64,
627 pub accessed_recently: i64,
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
631pub struct ConsolidationEventSummary {
632 pub event_type: String,
633 pub event_count: i64,
634 pub avg_strength_change: Option<f64>,
635 pub avg_probability_change: Option<f64>,
636 pub avg_recall_interval_hours: Option<f64>,
637}
638
639#[derive(Debug, Clone, Serialize, Deserialize)]
642pub struct FreezeMemoryRequest {
643 pub memory_id: Uuid,
644 pub reason: Option<String>,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
648pub struct FreezeMemoryResponse {
649 pub frozen_id: Uuid,
650 pub compression_ratio: Option<f64>,
651 pub original_tier: MemoryTier,
652 pub frozen_at: DateTime<Utc>,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct UnfreezeMemoryRequest {
657 pub frozen_id: Uuid,
658 pub target_tier: Option<MemoryTier>,
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct UnfreezeMemoryResponse {
663 pub memory_id: Uuid,
664 pub retrieval_delay_seconds: i32,
665 pub restoration_tier: MemoryTier,
666 pub unfrozen_at: DateTime<Utc>,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct ConsolidationSearchRequest {
673 pub min_consolidation_strength: Option<f64>,
674 pub max_consolidation_strength: Option<f64>,
675 pub min_recall_probability: Option<f64>,
676 pub max_recall_probability: Option<f64>,
677 pub include_frozen: Option<bool>,
678 pub tier: Option<MemoryTier>,
679 pub limit: Option<i32>,
680 pub offset: Option<i64>,
681}