1use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12use std::collections::VecDeque;
13
14const MAX_EVENT_BUFFER_SIZE: usize = 1000;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(tag = "type", rename_all = "snake_case")]
20pub enum ConsolidationEvent {
21 MemoryStrengthened {
23 memory_id: String,
24 content_preview: String,
25 activation_before: f32,
26 activation_after: f32,
27 reason: StrengtheningReason,
28 timestamp: DateTime<Utc>,
29 },
30
31 MemoryDecayed {
33 memory_id: String,
34 content_preview: String,
35 activation_before: f32,
36 activation_after: f32,
37 at_risk: bool, timestamp: DateTime<Utc>,
39 },
40
41 EdgeFormed {
43 from_memory_id: String,
44 to_memory_id: String,
45 initial_strength: f32,
46 reason: EdgeFormationReason,
47 timestamp: DateTime<Utc>,
48 },
49
50 EdgeStrengthened {
52 from_memory_id: String,
53 to_memory_id: String,
54 strength_before: f32,
55 strength_after: f32,
56 co_activations: u32,
57 timestamp: DateTime<Utc>,
58 },
59
60 EdgePotentiated {
62 from_memory_id: String,
63 to_memory_id: String,
64 final_strength: f32,
65 total_co_activations: u32,
66 timestamp: DateTime<Utc>,
67 },
68
69 EdgePruned {
71 from_memory_id: String,
72 to_memory_id: String,
73 final_strength: f32,
74 reason: PruningReason,
75 timestamp: DateTime<Utc>,
76 },
77
78 FactExtracted {
80 fact_id: String,
81 fact_content: String,
82 confidence: f32,
83 source_memory_count: usize,
84 fact_type: String,
85 timestamp: DateTime<Utc>,
86 },
87
88 FactReinforced {
90 fact_id: String,
91 fact_content: String,
92 confidence_before: f32,
93 confidence_after: f32,
94 new_support_count: usize,
95 timestamp: DateTime<Utc>,
96 },
97
98 FactDecayed {
100 fact_id: String,
101 fact_content: String,
102 confidence_before: f32,
103 confidence_after: f32,
104 days_since_reinforcement: i64,
105 timestamp: DateTime<Utc>,
106 },
107
108 FactDeleted {
110 fact_id: String,
111 fact_content: String,
112 final_confidence: f32,
113 support_count: usize,
114 reason: String,
115 timestamp: DateTime<Utc>,
116 },
117
118 MemoryPromoted {
120 memory_id: String,
121 content_preview: String,
122 from_tier: String,
123 to_tier: String,
124 timestamp: DateTime<Utc>,
125 },
126
127 MaintenanceCycleCompleted {
129 memories_processed: usize,
130 memories_decayed: usize,
131 edges_pruned: usize,
132 duration_ms: u64,
133 timestamp: DateTime<Utc>,
134 },
135
136 MemoryReplayed {
140 memory_id: String,
141 content_preview: String,
142 activation_before: f32,
143 activation_after: f32,
144 replay_priority: f32,
145 connected_memories_replayed: usize,
146 timestamp: DateTime<Utc>,
147 },
148
149 ReplayCycleCompleted {
151 memories_replayed: usize,
152 edges_strengthened: usize,
153 total_priority_score: f32,
154 duration_ms: u64,
155 timestamp: DateTime<Utc>,
156 },
157
158 InterferenceDetected {
162 new_memory_id: String,
163 old_memory_id: String,
164 similarity: f32,
165 interference_type: InterferenceType,
166 timestamp: DateTime<Utc>,
167 },
168
169 MemoryWeakened {
171 memory_id: String,
172 content_preview: String,
173 activation_before: f32,
174 activation_after: f32,
175 interfering_memory_id: String,
176 interference_type: InterferenceType,
177 timestamp: DateTime<Utc>,
178 },
179
180 RetrievalCompetition {
182 query_preview: String,
183 winner_memory_id: String,
184 suppressed_memory_ids: Vec<String>,
185 competition_factor: f32,
186 timestamp: DateTime<Utc>,
187 },
188
189 PatternTriggeredReplay {
193 trigger_type: String,
194 memory_ids: Vec<String>,
195 pattern_confidence: f32,
196 trigger_description: String,
197 timestamp: DateTime<Utc>,
198 },
199
200 EntityPatternDetected {
202 entity_group: Vec<String>,
203 memory_ids: Vec<String>,
204 overlap_score: f32,
205 confidence: f32,
206 timestamp: DateTime<Utc>,
207 },
208
209 SemanticClusterFormed {
211 memory_ids: Vec<String>,
212 cluster_size: usize,
213 avg_similarity: f32,
214 centroid_id: String,
215 timestamp: DateTime<Utc>,
216 },
217
218 TemporalClusterFormed {
220 memory_ids: Vec<String>,
221 session_duration_secs: i64,
222 session_id: Option<String>,
223 timestamp: DateTime<Utc>,
224 },
225
226 SalienceSpikeDetected {
228 memory_id: String,
229 content_preview: String,
230 importance: f32,
231 arousal: f32,
232 surprise_factor: f32,
233 timestamp: DateTime<Utc>,
234 },
235
236 BehavioralChangeDetected {
238 change_type: String,
239 affected_memory_ids: Vec<String>,
240 context: String,
241 timestamp: DateTime<Utc>,
242 },
243
244 PatternDetected {
246 trigger_type: String,
247 description: String,
248 memory_ids: Vec<String>,
249 timestamp: DateTime<Utc>,
250 },
251
252 EdgePromotionBoostApplied {
256 memory_id: String,
257 entity_name: String,
258 old_tier: String,
259 new_tier: String,
260 importance_boost: f64,
261 new_importance: f64,
262 timestamp: DateTime<Utc>,
263 },
264
265 GraphOrphanDetected {
268 memory_id: String,
269 entity_count: usize,
270 compensatory_boost: f64,
271 timestamp: DateTime<Utc>,
272 },
273
274 GraphAdjustedPromotion {
277 memory_id: String,
278 base_threshold: f64,
279 adjusted_threshold: f64,
280 l2_plus_edge_count: usize,
281 promoted: bool,
282 timestamp: DateTime<Utc>,
283 },
284
285 GraphDecayConsolidated {
287 pruned_count: usize,
288 orphaned_entities: usize,
289 timestamp: DateTime<Utc>,
290 },
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
295#[serde(rename_all = "snake_case")]
296pub enum InterferenceType {
297 Retroactive,
299 Proactive,
301 RetrievalCompetition,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
307#[serde(rename_all = "snake_case")]
308pub enum StrengtheningReason {
309 Recalled,
311 SpreadingActivation,
313 ExplicitBoost,
315 CoRetrieval,
317 MaintenancePotentiation,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
323#[serde(rename_all = "snake_case")]
324pub enum EdgeFormationReason {
325 CoRetrieval,
327 SharedEntities,
329 SemanticSimilarity,
331 TemporalProximity,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
337#[serde(rename_all = "snake_case")]
338pub enum PruningReason {
339 DecayedBelowThreshold,
341 Inactivity,
343 Invalidated,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct ConsolidationReport {
350 pub period: ReportPeriod,
352
353 pub strengthened_memories: Vec<MemoryChange>,
355
356 pub decayed_memories: Vec<MemoryChange>,
358
359 pub formed_associations: Vec<AssociationChange>,
361
362 pub strengthened_associations: Vec<AssociationChange>,
364
365 pub potentiated_associations: Vec<AssociationChange>,
367
368 pub pruned_associations: Vec<AssociationChange>,
370
371 pub extracted_facts: Vec<FactChange>,
373
374 pub reinforced_facts: Vec<FactChange>,
376
377 pub decayed_facts: Vec<FactChange>,
379
380 pub deleted_facts: Vec<FactChange>,
382
383 pub replayed_memories: Vec<ReplayEvent>,
386
387 pub interference_events: Vec<InterferenceEvent>,
390
391 pub weakened_memories: Vec<MemoryChange>,
393
394 pub statistics: ConsolidationStats,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct ReplayEvent {
401 pub memory_id: String,
402 pub content_preview: String,
403 pub activation_before: f32,
404 pub activation_after: f32,
405 pub replay_priority: f32,
406 pub connected_memories: usize,
407 pub timestamp: DateTime<Utc>,
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize)]
412pub struct InterferenceEvent {
413 pub new_memory_id: String,
414 pub old_memory_id: String,
415 pub similarity: f32,
416 pub interference_type: InterferenceType,
417 pub timestamp: DateTime<Utc>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct ReportPeriod {
423 pub start: DateTime<Utc>,
424 pub end: DateTime<Utc>,
425}
426
427#[derive(Debug, Clone, Serialize, Deserialize)]
429pub struct MemoryChange {
430 pub memory_id: String,
431 pub content_preview: String,
432 pub activation_before: f32,
433 pub activation_after: f32,
434 pub change_reason: String,
435 pub at_risk: bool,
436 pub timestamp: DateTime<Utc>,
437}
438
439#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct AssociationChange {
442 pub from_memory_id: String,
443 pub to_memory_id: String,
444 pub strength_before: Option<f32>,
445 pub strength_after: f32,
446 pub co_activations: Option<u32>,
447 pub reason: String,
448 pub timestamp: DateTime<Utc>,
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
453pub struct FactChange {
454 pub fact_id: String,
455 pub fact_content: String,
456 pub confidence: f32,
457 pub support_count: usize,
458 pub fact_type: String,
459 pub timestamp: DateTime<Utc>,
460}
461
462#[derive(Debug, Clone, Default, Serialize, Deserialize)]
464pub struct ConsolidationStats {
465 pub total_memories: usize,
466 pub memories_strengthened: usize,
467 pub memories_decayed: usize,
468 pub memories_at_risk: usize,
469 pub edges_formed: usize,
470 pub edges_strengthened: usize,
471 pub edges_potentiated: usize,
472 pub edges_pruned: usize,
473 pub facts_extracted: usize,
474 pub facts_reinforced: usize,
475 pub facts_decayed: usize,
476 pub facts_deleted: usize,
477 pub maintenance_cycles: usize,
478 pub total_maintenance_duration_ms: u64,
479 pub memories_replayed: usize,
481 pub replay_cycles: usize,
482 pub total_replay_priority: f32,
483 pub interference_events: usize,
485 pub memories_weakened: usize,
486 pub retrieval_competitions: usize,
487}
488
489#[derive(Debug, Default)]
491pub struct ConsolidationEventBuffer {
492 events: VecDeque<ConsolidationEvent>,
493 max_size: usize,
494}
495
496impl ConsolidationEventBuffer {
497 pub fn new() -> Self {
498 Self {
499 events: VecDeque::new(),
500 max_size: MAX_EVENT_BUFFER_SIZE,
501 }
502 }
503
504 pub fn with_capacity(max_size: usize) -> Self {
505 Self {
506 events: VecDeque::with_capacity(max_size),
507 max_size,
508 }
509 }
510
511 pub fn push(&mut self, event: ConsolidationEvent) {
513 if self.events.len() >= self.max_size {
514 self.events.pop_front();
515 }
516 self.events.push_back(event);
517 }
518
519 pub fn events_since(&self, since: DateTime<Utc>) -> Vec<ConsolidationEvent> {
521 self.events
522 .iter()
523 .filter(|e| e.timestamp() >= since)
524 .cloned()
525 .collect()
526 }
527
528 pub fn all_events(&self) -> Vec<ConsolidationEvent> {
530 self.events.iter().cloned().collect()
531 }
532
533 pub fn clear(&mut self) {
535 self.events.clear();
536 }
537
538 pub fn len(&self) -> usize {
540 self.events.len()
541 }
542
543 pub fn is_empty(&self) -> bool {
545 self.events.is_empty()
546 }
547
548 pub fn generate_report(
550 &self,
551 since: DateTime<Utc>,
552 until: DateTime<Utc>,
553 ) -> ConsolidationReport {
554 let events: Vec<_> = self
555 .events
556 .iter()
557 .filter(|e| {
558 let ts = e.timestamp();
559 ts >= since && ts <= until
560 })
561 .collect();
562
563 let mut report = ConsolidationReport {
564 period: ReportPeriod {
565 start: since,
566 end: until,
567 },
568 strengthened_memories: Vec::new(),
569 decayed_memories: Vec::new(),
570 formed_associations: Vec::new(),
571 strengthened_associations: Vec::new(),
572 potentiated_associations: Vec::new(),
573 pruned_associations: Vec::new(),
574 extracted_facts: Vec::new(),
575 reinforced_facts: Vec::new(),
576 decayed_facts: Vec::new(),
577 deleted_facts: Vec::new(),
578 replayed_memories: Vec::new(),
580 interference_events: Vec::new(),
582 weakened_memories: Vec::new(),
583 statistics: ConsolidationStats::default(),
584 };
585
586 for event in events {
587 match event {
588 ConsolidationEvent::MemoryStrengthened {
589 memory_id,
590 content_preview,
591 activation_before,
592 activation_after,
593 reason,
594 timestamp,
595 } => {
596 report.strengthened_memories.push(MemoryChange {
597 memory_id: memory_id.clone(),
598 content_preview: content_preview.clone(),
599 activation_before: *activation_before,
600 activation_after: *activation_after,
601 change_reason: format!("{:?}", reason),
602 at_risk: false,
603 timestamp: *timestamp,
604 });
605 report.statistics.memories_strengthened += 1;
606 }
607
608 ConsolidationEvent::MemoryDecayed {
609 memory_id,
610 content_preview,
611 activation_before,
612 activation_after,
613 at_risk,
614 timestamp,
615 } => {
616 report.decayed_memories.push(MemoryChange {
617 memory_id: memory_id.clone(),
618 content_preview: content_preview.clone(),
619 activation_before: *activation_before,
620 activation_after: *activation_after,
621 change_reason: "decay".to_string(),
622 at_risk: *at_risk,
623 timestamp: *timestamp,
624 });
625 report.statistics.memories_decayed += 1;
626 if *at_risk {
627 report.statistics.memories_at_risk += 1;
628 }
629 }
630
631 ConsolidationEvent::EdgeFormed {
632 from_memory_id,
633 to_memory_id,
634 initial_strength,
635 reason,
636 timestamp,
637 } => {
638 report.formed_associations.push(AssociationChange {
639 from_memory_id: from_memory_id.clone(),
640 to_memory_id: to_memory_id.clone(),
641 strength_before: None,
642 strength_after: *initial_strength,
643 co_activations: Some(1),
644 reason: format!("{:?}", reason),
645 timestamp: *timestamp,
646 });
647 report.statistics.edges_formed += 1;
648 }
649
650 ConsolidationEvent::EdgeStrengthened {
651 from_memory_id,
652 to_memory_id,
653 strength_before,
654 strength_after,
655 co_activations,
656 timestamp,
657 } => {
658 report.strengthened_associations.push(AssociationChange {
659 from_memory_id: from_memory_id.clone(),
660 to_memory_id: to_memory_id.clone(),
661 strength_before: Some(*strength_before),
662 strength_after: *strength_after,
663 co_activations: Some(*co_activations),
664 reason: "co_activation".to_string(),
665 timestamp: *timestamp,
666 });
667 report.statistics.edges_strengthened += 1;
668 }
669
670 ConsolidationEvent::EdgePotentiated {
671 from_memory_id,
672 to_memory_id,
673 final_strength,
674 total_co_activations,
675 timestamp,
676 } => {
677 report.potentiated_associations.push(AssociationChange {
678 from_memory_id: from_memory_id.clone(),
679 to_memory_id: to_memory_id.clone(),
680 strength_before: None,
681 strength_after: *final_strength,
682 co_activations: Some(*total_co_activations),
683 reason: "long_term_potentiation".to_string(),
684 timestamp: *timestamp,
685 });
686 report.statistics.edges_potentiated += 1;
687 }
688
689 ConsolidationEvent::EdgePruned {
690 from_memory_id,
691 to_memory_id,
692 final_strength,
693 reason,
694 timestamp,
695 } => {
696 report.pruned_associations.push(AssociationChange {
697 from_memory_id: from_memory_id.clone(),
698 to_memory_id: to_memory_id.clone(),
699 strength_before: Some(*final_strength),
700 strength_after: 0.0,
701 co_activations: None,
702 reason: format!("{:?}", reason),
703 timestamp: *timestamp,
704 });
705 report.statistics.edges_pruned += 1;
706 }
707
708 ConsolidationEvent::FactExtracted {
709 fact_id,
710 fact_content,
711 confidence,
712 source_memory_count,
713 fact_type,
714 timestamp,
715 } => {
716 report.extracted_facts.push(FactChange {
717 fact_id: fact_id.clone(),
718 fact_content: fact_content.clone(),
719 confidence: *confidence,
720 support_count: *source_memory_count,
721 fact_type: fact_type.clone(),
722 timestamp: *timestamp,
723 });
724 report.statistics.facts_extracted += 1;
725 }
726
727 ConsolidationEvent::FactReinforced {
728 fact_id,
729 fact_content,
730 confidence_after,
731 new_support_count,
732 timestamp,
733 ..
734 } => {
735 report.reinforced_facts.push(FactChange {
736 fact_id: fact_id.clone(),
737 fact_content: fact_content.clone(),
738 confidence: *confidence_after,
739 support_count: *new_support_count,
740 fact_type: "reinforced".to_string(),
741 timestamp: *timestamp,
742 });
743 report.statistics.facts_reinforced += 1;
744 }
745
746 ConsolidationEvent::FactDecayed {
747 fact_id,
748 fact_content,
749 confidence_after,
750 days_since_reinforcement,
751 timestamp,
752 ..
753 } => {
754 report.decayed_facts.push(FactChange {
755 fact_id: fact_id.clone(),
756 fact_content: fact_content.clone(),
757 confidence: *confidence_after,
758 support_count: *days_since_reinforcement as usize,
759 fact_type: "decayed".to_string(),
760 timestamp: *timestamp,
761 });
762 report.statistics.facts_decayed += 1;
763 }
764
765 ConsolidationEvent::FactDeleted {
766 fact_id,
767 fact_content,
768 final_confidence,
769 support_count,
770 reason,
771 timestamp,
772 } => {
773 report.deleted_facts.push(FactChange {
774 fact_id: fact_id.clone(),
775 fact_content: fact_content.clone(),
776 confidence: *final_confidence,
777 support_count: *support_count,
778 fact_type: reason.clone(),
779 timestamp: *timestamp,
780 });
781 report.statistics.facts_deleted += 1;
782 }
783
784 ConsolidationEvent::MemoryPromoted { .. } => {
785 }
787
788 ConsolidationEvent::MaintenanceCycleCompleted { duration_ms, .. } => {
789 report.statistics.maintenance_cycles += 1;
790 report.statistics.total_maintenance_duration_ms += duration_ms;
791 }
792
793 ConsolidationEvent::MemoryReplayed {
795 memory_id,
796 content_preview,
797 activation_before,
798 activation_after,
799 replay_priority,
800 connected_memories_replayed,
801 timestamp,
802 } => {
803 report.replayed_memories.push(ReplayEvent {
804 memory_id: memory_id.clone(),
805 content_preview: content_preview.clone(),
806 activation_before: *activation_before,
807 activation_after: *activation_after,
808 replay_priority: *replay_priority,
809 connected_memories: *connected_memories_replayed,
810 timestamp: *timestamp,
811 });
812 report.statistics.memories_replayed += 1;
813 report.statistics.total_replay_priority += replay_priority;
814 }
815
816 ConsolidationEvent::ReplayCycleCompleted {
817 memories_replayed,
818 total_priority_score,
819 ..
820 } => {
821 report.statistics.replay_cycles += 1;
822 report.statistics.memories_replayed += memories_replayed;
823 report.statistics.total_replay_priority += total_priority_score;
824 }
825
826 ConsolidationEvent::InterferenceDetected {
828 new_memory_id,
829 old_memory_id,
830 similarity,
831 interference_type,
832 timestamp,
833 } => {
834 report.interference_events.push(InterferenceEvent {
835 new_memory_id: new_memory_id.clone(),
836 old_memory_id: old_memory_id.clone(),
837 similarity: *similarity,
838 interference_type: interference_type.clone(),
839 timestamp: *timestamp,
840 });
841 report.statistics.interference_events += 1;
842 }
843
844 ConsolidationEvent::MemoryWeakened {
845 memory_id,
846 content_preview,
847 activation_before,
848 activation_after,
849 interfering_memory_id,
850 interference_type,
851 timestamp,
852 } => {
853 report.weakened_memories.push(MemoryChange {
854 memory_id: memory_id.clone(),
855 content_preview: content_preview.clone(),
856 activation_before: *activation_before,
857 activation_after: *activation_after,
858 change_reason: format!(
859 "{:?} interference from {}",
860 interference_type, interfering_memory_id
861 ),
862 at_risk: *activation_after < 0.1,
863 timestamp: *timestamp,
864 });
865 report.statistics.memories_weakened += 1;
866 }
867
868 ConsolidationEvent::RetrievalCompetition { .. } => {
869 report.statistics.retrieval_competitions += 1;
870 }
871
872 ConsolidationEvent::PatternTriggeredReplay { .. } => {
875 }
877 ConsolidationEvent::EntityPatternDetected { .. } => {
878 }
880 ConsolidationEvent::SemanticClusterFormed { .. } => {
881 }
883 ConsolidationEvent::TemporalClusterFormed { .. } => {
884 }
886 ConsolidationEvent::SalienceSpikeDetected { .. } => {
887 }
889 ConsolidationEvent::BehavioralChangeDetected { .. } => {
890 }
892 ConsolidationEvent::PatternDetected { .. } => {
893 }
895
896 ConsolidationEvent::EdgePromotionBoostApplied { .. } => {}
898 ConsolidationEvent::GraphOrphanDetected { .. } => {}
899 ConsolidationEvent::GraphAdjustedPromotion { .. } => {}
900 ConsolidationEvent::GraphDecayConsolidated { .. } => {}
901 }
902 }
903
904 report
905 }
906
907 pub fn generate_report_from_events(
912 events: &[ConsolidationEvent],
913 since: DateTime<Utc>,
914 until: DateTime<Utc>,
915 ) -> ConsolidationReport {
916 let mut report = ConsolidationReport {
917 period: ReportPeriod {
918 start: since,
919 end: until,
920 },
921 strengthened_memories: Vec::new(),
922 decayed_memories: Vec::new(),
923 formed_associations: Vec::new(),
924 strengthened_associations: Vec::new(),
925 potentiated_associations: Vec::new(),
926 pruned_associations: Vec::new(),
927 extracted_facts: Vec::new(),
928 reinforced_facts: Vec::new(),
929 decayed_facts: Vec::new(),
930 deleted_facts: Vec::new(),
931 replayed_memories: Vec::new(),
932 interference_events: Vec::new(),
933 weakened_memories: Vec::new(),
934 statistics: ConsolidationStats::default(),
935 };
936
937 for event in events {
938 match event {
939 ConsolidationEvent::MemoryStrengthened {
940 memory_id,
941 content_preview,
942 activation_before,
943 activation_after,
944 reason,
945 timestamp,
946 } => {
947 report.strengthened_memories.push(MemoryChange {
948 memory_id: memory_id.clone(),
949 content_preview: content_preview.clone(),
950 activation_before: *activation_before,
951 activation_after: *activation_after,
952 change_reason: format!("{:?}", reason),
953 at_risk: false,
954 timestamp: *timestamp,
955 });
956 report.statistics.memories_strengthened += 1;
957 }
958
959 ConsolidationEvent::MemoryDecayed {
960 memory_id,
961 content_preview,
962 activation_before,
963 activation_after,
964 at_risk,
965 timestamp,
966 } => {
967 report.decayed_memories.push(MemoryChange {
968 memory_id: memory_id.clone(),
969 content_preview: content_preview.clone(),
970 activation_before: *activation_before,
971 activation_after: *activation_after,
972 change_reason: "decay".to_string(),
973 at_risk: *at_risk,
974 timestamp: *timestamp,
975 });
976 report.statistics.memories_decayed += 1;
977 if *at_risk {
978 report.statistics.memories_at_risk += 1;
979 }
980 }
981
982 ConsolidationEvent::EdgeFormed {
983 from_memory_id,
984 to_memory_id,
985 initial_strength,
986 reason,
987 timestamp,
988 } => {
989 report.formed_associations.push(AssociationChange {
990 from_memory_id: from_memory_id.clone(),
991 to_memory_id: to_memory_id.clone(),
992 strength_before: None,
993 strength_after: *initial_strength,
994 co_activations: Some(1),
995 reason: format!("{:?}", reason),
996 timestamp: *timestamp,
997 });
998 report.statistics.edges_formed += 1;
999 }
1000
1001 ConsolidationEvent::EdgeStrengthened {
1002 from_memory_id,
1003 to_memory_id,
1004 strength_before,
1005 strength_after,
1006 co_activations,
1007 timestamp,
1008 } => {
1009 report.strengthened_associations.push(AssociationChange {
1010 from_memory_id: from_memory_id.clone(),
1011 to_memory_id: to_memory_id.clone(),
1012 strength_before: Some(*strength_before),
1013 strength_after: *strength_after,
1014 co_activations: Some(*co_activations),
1015 reason: "co_activation".to_string(),
1016 timestamp: *timestamp,
1017 });
1018 report.statistics.edges_strengthened += 1;
1019 }
1020
1021 ConsolidationEvent::EdgePotentiated {
1022 from_memory_id,
1023 to_memory_id,
1024 final_strength,
1025 total_co_activations,
1026 timestamp,
1027 } => {
1028 report.potentiated_associations.push(AssociationChange {
1029 from_memory_id: from_memory_id.clone(),
1030 to_memory_id: to_memory_id.clone(),
1031 strength_before: None,
1032 strength_after: *final_strength,
1033 co_activations: Some(*total_co_activations),
1034 reason: "long_term_potentiation".to_string(),
1035 timestamp: *timestamp,
1036 });
1037 report.statistics.edges_potentiated += 1;
1038 }
1039
1040 ConsolidationEvent::EdgePruned {
1041 from_memory_id,
1042 to_memory_id,
1043 final_strength,
1044 reason,
1045 timestamp,
1046 } => {
1047 report.pruned_associations.push(AssociationChange {
1048 from_memory_id: from_memory_id.clone(),
1049 to_memory_id: to_memory_id.clone(),
1050 strength_before: Some(*final_strength),
1051 strength_after: 0.0,
1052 co_activations: None,
1053 reason: format!("{:?}", reason),
1054 timestamp: *timestamp,
1055 });
1056 report.statistics.edges_pruned += 1;
1057 }
1058
1059 ConsolidationEvent::FactExtracted {
1060 fact_id,
1061 fact_content,
1062 confidence,
1063 source_memory_count,
1064 fact_type,
1065 timestamp,
1066 } => {
1067 report.extracted_facts.push(FactChange {
1068 fact_id: fact_id.clone(),
1069 fact_content: fact_content.clone(),
1070 confidence: *confidence,
1071 support_count: *source_memory_count,
1072 fact_type: fact_type.clone(),
1073 timestamp: *timestamp,
1074 });
1075 report.statistics.facts_extracted += 1;
1076 }
1077
1078 ConsolidationEvent::FactReinforced {
1079 fact_id,
1080 fact_content,
1081 confidence_after,
1082 new_support_count,
1083 timestamp,
1084 ..
1085 } => {
1086 report.reinforced_facts.push(FactChange {
1087 fact_id: fact_id.clone(),
1088 fact_content: fact_content.clone(),
1089 confidence: *confidence_after,
1090 support_count: *new_support_count,
1091 fact_type: "reinforced".to_string(),
1092 timestamp: *timestamp,
1093 });
1094 report.statistics.facts_reinforced += 1;
1095 }
1096
1097 ConsolidationEvent::FactDecayed {
1098 fact_id,
1099 fact_content,
1100 confidence_after,
1101 days_since_reinforcement,
1102 timestamp,
1103 ..
1104 } => {
1105 report.decayed_facts.push(FactChange {
1106 fact_id: fact_id.clone(),
1107 fact_content: fact_content.clone(),
1108 confidence: *confidence_after,
1109 support_count: *days_since_reinforcement as usize,
1110 fact_type: "decayed".to_string(),
1111 timestamp: *timestamp,
1112 });
1113 report.statistics.facts_decayed += 1;
1114 }
1115
1116 ConsolidationEvent::FactDeleted {
1117 fact_id,
1118 fact_content,
1119 final_confidence,
1120 support_count,
1121 reason,
1122 timestamp,
1123 } => {
1124 report.deleted_facts.push(FactChange {
1125 fact_id: fact_id.clone(),
1126 fact_content: fact_content.clone(),
1127 confidence: *final_confidence,
1128 support_count: *support_count,
1129 fact_type: reason.clone(),
1130 timestamp: *timestamp,
1131 });
1132 report.statistics.facts_deleted += 1;
1133 }
1134
1135 ConsolidationEvent::MemoryPromoted { .. } => {
1136 }
1138
1139 ConsolidationEvent::MaintenanceCycleCompleted { duration_ms, .. } => {
1140 report.statistics.maintenance_cycles += 1;
1141 report.statistics.total_maintenance_duration_ms += duration_ms;
1142 }
1143
1144 ConsolidationEvent::MemoryReplayed {
1145 memory_id,
1146 content_preview,
1147 activation_before,
1148 activation_after,
1149 replay_priority,
1150 connected_memories_replayed,
1151 timestamp,
1152 } => {
1153 report.replayed_memories.push(ReplayEvent {
1154 memory_id: memory_id.clone(),
1155 content_preview: content_preview.clone(),
1156 activation_before: *activation_before,
1157 activation_after: *activation_after,
1158 replay_priority: *replay_priority,
1159 connected_memories: *connected_memories_replayed,
1160 timestamp: *timestamp,
1161 });
1162 report.statistics.memories_replayed += 1;
1163 report.statistics.total_replay_priority += replay_priority;
1164 }
1165
1166 ConsolidationEvent::ReplayCycleCompleted {
1167 memories_replayed,
1168 total_priority_score,
1169 ..
1170 } => {
1171 report.statistics.replay_cycles += 1;
1172 report.statistics.memories_replayed += memories_replayed;
1173 report.statistics.total_replay_priority += total_priority_score;
1174 }
1175
1176 ConsolidationEvent::InterferenceDetected {
1177 new_memory_id,
1178 old_memory_id,
1179 similarity,
1180 interference_type,
1181 timestamp,
1182 } => {
1183 report.interference_events.push(InterferenceEvent {
1184 new_memory_id: new_memory_id.clone(),
1185 old_memory_id: old_memory_id.clone(),
1186 similarity: *similarity,
1187 interference_type: interference_type.clone(),
1188 timestamp: *timestamp,
1189 });
1190 report.statistics.interference_events += 1;
1191 }
1192
1193 ConsolidationEvent::MemoryWeakened {
1194 memory_id,
1195 content_preview,
1196 activation_before,
1197 activation_after,
1198 interfering_memory_id,
1199 interference_type,
1200 timestamp,
1201 } => {
1202 report.weakened_memories.push(MemoryChange {
1203 memory_id: memory_id.clone(),
1204 content_preview: content_preview.clone(),
1205 activation_before: *activation_before,
1206 activation_after: *activation_after,
1207 change_reason: format!(
1208 "{:?} interference from {}",
1209 interference_type, interfering_memory_id
1210 ),
1211 at_risk: *activation_after < 0.1,
1212 timestamp: *timestamp,
1213 });
1214 report.statistics.memories_weakened += 1;
1215 }
1216
1217 ConsolidationEvent::RetrievalCompetition { .. } => {
1218 report.statistics.retrieval_competitions += 1;
1219 }
1220
1221 ConsolidationEvent::PatternTriggeredReplay { .. } => {}
1223 ConsolidationEvent::EntityPatternDetected { .. } => {}
1224 ConsolidationEvent::SemanticClusterFormed { .. } => {}
1225 ConsolidationEvent::TemporalClusterFormed { .. } => {}
1226 ConsolidationEvent::SalienceSpikeDetected { .. } => {}
1227 ConsolidationEvent::BehavioralChangeDetected { .. } => {}
1228 ConsolidationEvent::PatternDetected { .. } => {}
1229
1230 ConsolidationEvent::EdgePromotionBoostApplied { .. } => {}
1232 ConsolidationEvent::GraphOrphanDetected { .. } => {}
1233 ConsolidationEvent::GraphAdjustedPromotion { .. } => {}
1234 ConsolidationEvent::GraphDecayConsolidated { .. } => {}
1235 }
1236 }
1237
1238 report
1239 }
1240}
1241
1242impl ConsolidationEvent {
1243 pub fn timestamp(&self) -> DateTime<Utc> {
1245 match self {
1246 ConsolidationEvent::MemoryStrengthened { timestamp, .. } => *timestamp,
1247 ConsolidationEvent::MemoryDecayed { timestamp, .. } => *timestamp,
1248 ConsolidationEvent::EdgeFormed { timestamp, .. } => *timestamp,
1249 ConsolidationEvent::EdgeStrengthened { timestamp, .. } => *timestamp,
1250 ConsolidationEvent::EdgePotentiated { timestamp, .. } => *timestamp,
1251 ConsolidationEvent::EdgePruned { timestamp, .. } => *timestamp,
1252 ConsolidationEvent::FactExtracted { timestamp, .. } => *timestamp,
1253 ConsolidationEvent::FactReinforced { timestamp, .. } => *timestamp,
1254 ConsolidationEvent::FactDecayed { timestamp, .. } => *timestamp,
1255 ConsolidationEvent::FactDeleted { timestamp, .. } => *timestamp,
1256 ConsolidationEvent::MemoryPromoted { timestamp, .. } => *timestamp,
1257 ConsolidationEvent::MaintenanceCycleCompleted { timestamp, .. } => *timestamp,
1258 ConsolidationEvent::MemoryReplayed { timestamp, .. } => *timestamp,
1260 ConsolidationEvent::ReplayCycleCompleted { timestamp, .. } => *timestamp,
1261 ConsolidationEvent::InterferenceDetected { timestamp, .. } => *timestamp,
1263 ConsolidationEvent::MemoryWeakened { timestamp, .. } => *timestamp,
1264 ConsolidationEvent::RetrievalCompetition { timestamp, .. } => *timestamp,
1265 ConsolidationEvent::PatternTriggeredReplay { timestamp, .. } => *timestamp,
1267 ConsolidationEvent::EntityPatternDetected { timestamp, .. } => *timestamp,
1268 ConsolidationEvent::SemanticClusterFormed { timestamp, .. } => *timestamp,
1269 ConsolidationEvent::TemporalClusterFormed { timestamp, .. } => *timestamp,
1270 ConsolidationEvent::SalienceSpikeDetected { timestamp, .. } => *timestamp,
1271 ConsolidationEvent::BehavioralChangeDetected { timestamp, .. } => *timestamp,
1272 ConsolidationEvent::PatternDetected { timestamp, .. } => *timestamp,
1273 ConsolidationEvent::EdgePromotionBoostApplied { timestamp, .. } => *timestamp,
1275 ConsolidationEvent::GraphOrphanDetected { timestamp, .. } => *timestamp,
1276 ConsolidationEvent::GraphAdjustedPromotion { timestamp, .. } => *timestamp,
1277 ConsolidationEvent::GraphDecayConsolidated { timestamp, .. } => *timestamp,
1278 }
1279 }
1280
1281 pub fn is_significant(&self) -> bool {
1304 matches!(
1305 self,
1306 ConsolidationEvent::EdgePotentiated { .. }
1307 | ConsolidationEvent::FactExtracted { .. }
1308 | ConsolidationEvent::FactDeleted { .. }
1309 | ConsolidationEvent::FactReinforced { .. }
1310 | ConsolidationEvent::InterferenceDetected { .. }
1311 | ConsolidationEvent::MemoryReplayed { .. }
1312 | ConsolidationEvent::MemoryPromoted { .. }
1313 | ConsolidationEvent::ReplayCycleCompleted { .. }
1314 | ConsolidationEvent::MaintenanceCycleCompleted { .. }
1315 | ConsolidationEvent::PatternTriggeredReplay { .. }
1317 | ConsolidationEvent::EntityPatternDetected { .. }
1318 | ConsolidationEvent::SemanticClusterFormed { .. }
1319 | ConsolidationEvent::SalienceSpikeDetected { .. }
1320 | ConsolidationEvent::PatternDetected { .. }
1321 | ConsolidationEvent::EdgePromotionBoostApplied { .. }
1323 | ConsolidationEvent::GraphOrphanDetected { .. }
1324 | ConsolidationEvent::GraphAdjustedPromotion { .. }
1325 | ConsolidationEvent::GraphDecayConsolidated { .. }
1326 )
1327 }
1328}
1329
1330impl Default for ConsolidationReport {
1331 fn default() -> Self {
1332 Self {
1333 period: ReportPeriod {
1334 start: Utc::now(),
1335 end: Utc::now(),
1336 },
1337 strengthened_memories: Vec::new(),
1338 decayed_memories: Vec::new(),
1339 formed_associations: Vec::new(),
1340 strengthened_associations: Vec::new(),
1341 potentiated_associations: Vec::new(),
1342 pruned_associations: Vec::new(),
1343 extracted_facts: Vec::new(),
1344 reinforced_facts: Vec::new(),
1345 decayed_facts: Vec::new(),
1346 deleted_facts: Vec::new(),
1347 replayed_memories: Vec::new(),
1349 interference_events: Vec::new(),
1351 weakened_memories: Vec::new(),
1352 statistics: ConsolidationStats::default(),
1353 }
1354 }
1355}
1356
1357#[cfg(test)]
1358mod tests {
1359 use super::*;
1360
1361 #[test]
1362 fn test_event_buffer_push() {
1363 let mut buffer = ConsolidationEventBuffer::with_capacity(3);
1364
1365 for i in 0..5 {
1366 buffer.push(ConsolidationEvent::MemoryDecayed {
1367 memory_id: format!("mem-{}", i),
1368 content_preview: format!("Memory {}", i),
1369 activation_before: 0.5,
1370 activation_after: 0.4,
1371 at_risk: false,
1372 timestamp: Utc::now(),
1373 });
1374 }
1375
1376 assert_eq!(buffer.len(), 3);
1378 }
1379
1380 #[test]
1381 fn test_generate_report() {
1382 let mut buffer = ConsolidationEventBuffer::new();
1383 let now = Utc::now();
1384
1385 buffer.push(ConsolidationEvent::MemoryStrengthened {
1386 memory_id: "mem-1".to_string(),
1387 content_preview: "Test memory".to_string(),
1388 activation_before: 0.5,
1389 activation_after: 0.7,
1390 reason: StrengtheningReason::Recalled,
1391 timestamp: now,
1392 });
1393
1394 buffer.push(ConsolidationEvent::EdgeFormed {
1395 from_memory_id: "mem-1".to_string(),
1396 to_memory_id: "mem-2".to_string(),
1397 initial_strength: 0.5,
1398 reason: EdgeFormationReason::CoRetrieval,
1399 timestamp: now,
1400 });
1401
1402 let report = buffer.generate_report(
1403 now - chrono::Duration::hours(1),
1404 now + chrono::Duration::hours(1),
1405 );
1406
1407 assert_eq!(report.statistics.memories_strengthened, 1);
1408 assert_eq!(report.statistics.edges_formed, 1);
1409 }
1410}