1use super::error::{MemoryError, Result};
43use super::models::*;
44use super::repository::MemoryRepository;
45use chrono::{DateTime, Duration, Utc};
46use pgvector::Vector;
47use serde::{Deserialize, Serialize};
48use std::collections::{HashMap, HashSet, VecDeque};
49use std::sync::Arc;
50use tracing::{debug, info, warn};
51use uuid::Uuid;
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct ReflectionConfig {
56 pub importance_trigger_threshold: f64,
58
59 pub max_memories_per_reflection: usize,
61
62 pub target_insights_per_reflection: usize,
64
65 pub clustering_similarity_threshold: f64,
67
68 pub insight_importance_multiplier: f64,
70
71 pub max_graph_depth: usize,
73
74 pub min_cluster_size: usize,
76
77 pub temporal_analysis_window_days: i64,
79
80 pub reflection_cooldown_hours: i64,
82}
83
84impl Default for ReflectionConfig {
85 fn default() -> Self {
86 Self {
87 importance_trigger_threshold: 150.0,
88 max_memories_per_reflection: 100,
89 target_insights_per_reflection: 3,
90 clustering_similarity_threshold: 0.75,
91 insight_importance_multiplier: 1.5,
92 max_graph_depth: 3,
93 min_cluster_size: 3,
94 temporal_analysis_window_days: 30,
95 reflection_cooldown_hours: 6,
96 }
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
102pub enum InsightType {
103 Pattern,
105 Synthesis,
107 Gap,
109 Contradiction,
111 Trend,
113 Causality,
115 Analogy,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct Insight {
122 pub id: Uuid,
123 pub insight_type: InsightType,
124 pub content: String,
125 pub confidence_score: f64,
126 pub source_memory_ids: Vec<Uuid>,
127 pub related_concepts: Vec<String>,
128 pub knowledge_graph_nodes: Vec<KnowledgeNode>,
129 pub importance_score: f64,
130 pub generated_at: DateTime<Utc>,
131 pub validation_metrics: ValidationMetrics,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct ValidationMetrics {
137 pub novelty_score: f64,
138 pub coherence_score: f64,
139 pub evidence_strength: f64,
140 pub semantic_richness: f64,
141 pub predictive_power: f64,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct KnowledgeNode {
147 pub id: Uuid,
148 pub concept: String,
149 pub node_type: NodeType,
150 #[serde(skip)]
151 pub embedding: Option<Vector>,
152 pub confidence: f64,
153 pub connections: Vec<KnowledgeEdge>,
154 pub created_at: DateTime<Utc>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
159pub enum NodeType {
160 Concept,
161 Entity,
162 Relationship,
163 Insight,
164 Memory,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct KnowledgeEdge {
170 pub target_node_id: Uuid,
171 pub relationship_type: RelationshipType,
172 pub strength: f64,
173 pub evidence_memories: Vec<Uuid>,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
178pub enum RelationshipType {
179 IsA,
180 PartOf,
181 CausedBy,
182 SimilarTo,
183 ConflictsWith,
184 Enables,
185 Requires,
186 Exemplifies,
187 GeneralizedBy,
188 TemporallyPrecedes,
189}
190
191#[derive(Debug, Clone)]
193pub struct MemoryCluster {
194 pub id: Uuid,
195 pub memories: Vec<Memory>,
196 pub centroid_embedding: Option<Vector>,
197 pub coherence_score: f64,
198 pub dominant_concepts: Vec<String>,
199 pub temporal_span: Option<(DateTime<Utc>, DateTime<Utc>)>,
200}
201
202#[derive(Debug, Clone)]
204pub struct ReflectionSession {
205 pub id: Uuid,
206 pub started_at: DateTime<Utc>,
207 pub trigger_reason: String,
208 pub analyzed_memories: Vec<Memory>,
209 pub generated_clusters: Vec<MemoryCluster>,
210 pub generated_insights: Vec<Insight>,
211 pub knowledge_graph_updates: Vec<KnowledgeNode>,
212 pub completion_status: ReflectionStatus,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
217pub enum ReflectionStatus {
218 InProgress,
219 Completed,
220 Failed,
221 Cancelled,
222}
223
224pub struct ReflectionEngine {
226 config: ReflectionConfig,
227 repository: Arc<MemoryRepository>,
228 #[allow(dead_code)]
229 knowledge_graph: KnowledgeGraph,
230 last_reflection_time: Option<DateTime<Utc>>,
231}
232
233impl ReflectionEngine {
234 pub fn new(config: ReflectionConfig, repository: Arc<MemoryRepository>) -> Self {
235 Self {
236 config,
237 repository,
238 knowledge_graph: KnowledgeGraph::new(),
239 last_reflection_time: None,
240 }
241 }
242
243 pub async fn should_trigger_reflection(&self) -> Result<Option<String>> {
245 if let Some(last_time) = self.last_reflection_time {
247 let hours_since_last = Utc::now().signed_duration_since(last_time).num_hours();
248 if hours_since_last < self.config.reflection_cooldown_hours {
249 return Ok(None);
250 }
251 }
252
253 let cutoff_time = self
255 .last_reflection_time
256 .unwrap_or(Utc::now() - Duration::days(1));
257
258 let recent_memories = self.get_recent_memories_since(cutoff_time).await?;
259 let total_importance: f64 = recent_memories.iter().map(|m| m.importance_score).sum();
260
261 if total_importance >= self.config.importance_trigger_threshold {
262 return Ok(Some(format!(
263 "Importance threshold reached: {:.1} >= {:.1}",
264 total_importance, self.config.importance_trigger_threshold
265 )));
266 }
267
268 if let Some(cluster_reason) = self.check_cluster_density(&recent_memories).await? {
270 return Ok(Some(cluster_reason));
271 }
272
273 if let Some(pattern_reason) = self.check_temporal_patterns(&recent_memories).await? {
275 return Ok(Some(pattern_reason));
276 }
277
278 Ok(None)
279 }
280
281 pub async fn execute_reflection(
283 &mut self,
284 trigger_reason: String,
285 ) -> Result<ReflectionSession> {
286 let session_id = Uuid::new_v4();
287 let start_time = Utc::now();
288
289 info!(
290 "Starting reflection session {}: {}",
291 session_id, trigger_reason
292 );
293
294 let mut session = ReflectionSession {
295 id: session_id,
296 started_at: start_time,
297 trigger_reason: trigger_reason.clone(),
298 analyzed_memories: Vec::new(),
299 generated_clusters: Vec::new(),
300 generated_insights: Vec::new(),
301 knowledge_graph_updates: Vec::new(),
302 completion_status: ReflectionStatus::InProgress,
303 };
304
305 match self.execute_reflection_pipeline(&mut session).await {
306 Ok(_) => {
307 session.completion_status = ReflectionStatus::Completed;
308 self.last_reflection_time = Some(Utc::now());
309
310 info!(
311 "Reflection session {} completed: {} insights generated from {} memories",
312 session_id,
313 session.generated_insights.len(),
314 session.analyzed_memories.len()
315 );
316 }
317 Err(e) => {
318 session.completion_status = ReflectionStatus::Failed;
319 warn!("Reflection session {} failed: {}", session_id, e);
320 return Err(e);
321 }
322 }
323
324 Ok(session)
325 }
326
327 async fn execute_reflection_pipeline(&self, session: &mut ReflectionSession) -> Result<()> {
329 session.analyzed_memories = self.gather_reflection_memories().await?;
331
332 if session.analyzed_memories.is_empty() {
333 return Err(MemoryError::InvalidRequest {
334 message: "No memories available for reflection".to_string(),
335 });
336 }
337
338 session.generated_clusters = self.cluster_memories(&session.analyzed_memories).await?;
340
341 for cluster in &session.generated_clusters {
343 let cluster_insights = self.generate_cluster_insights(cluster).await?;
344 session.generated_insights.extend(cluster_insights);
345 }
346
347 let cross_cluster_insights = self
349 .generate_cross_cluster_insights(&session.generated_clusters)
350 .await?;
351 session.generated_insights.extend(cross_cluster_insights);
352
353 session.knowledge_graph_updates = self
355 .update_knowledge_graph(&session.generated_insights)
356 .await?;
357
358 self.store_insights_as_memories(&session.generated_insights)
360 .await?;
361
362 self.validate_and_prune_insights(&mut session.generated_insights)
364 .await?;
365
366 Ok(())
367 }
368
369 async fn gather_reflection_memories(&self) -> Result<Vec<Memory>> {
371 let cutoff_time = self
372 .last_reflection_time
373 .unwrap_or(Utc::now() - Duration::days(self.config.temporal_analysis_window_days));
374
375 let search_request = SearchRequest {
377 date_range: Some(DateRange {
378 start: Some(cutoff_time),
379 end: Some(Utc::now()),
380 }),
381 importance_range: Some(RangeFilter {
382 min: Some(0.3),
383 max: None,
384 }),
385 limit: Some(self.config.max_memories_per_reflection as i32),
386 search_type: Some(SearchType::Temporal),
387 ..Default::default()
388 };
389
390 let search_response = self.repository.search_memories(search_request).await?;
391
392 Ok(search_response
393 .results
394 .into_iter()
395 .map(|result| result.memory)
396 .collect())
397 }
398
399 async fn cluster_memories(&self, memories: &[Memory]) -> Result<Vec<MemoryCluster>> {
401 let mut clusters = Vec::new();
402 let mut unassigned_memories: Vec<_> = memories.iter().collect();
403
404 while !unassigned_memories.is_empty() {
405 let seed_memory = unassigned_memories.remove(0);
406 let mut cluster_memories = vec![seed_memory.clone()];
407
408 let mut i = 0;
410 while i < unassigned_memories.len() {
411 let memory = unassigned_memories[i];
412
413 if let (Some(seed_embedding), Some(memory_embedding)) =
414 (&seed_memory.embedding, &memory.embedding)
415 {
416 let similarity =
417 self.calculate_cosine_similarity(seed_embedding, memory_embedding)?;
418
419 if similarity >= self.config.clustering_similarity_threshold {
420 cluster_memories.push(memory.clone());
421 unassigned_memories.remove(i);
422 } else {
423 i += 1;
424 }
425 } else {
426 i += 1;
427 }
428 }
429
430 if cluster_memories.len() >= self.config.min_cluster_size {
432 let cluster = self.create_memory_cluster(cluster_memories).await?;
433 clusters.push(cluster);
434 }
435 }
436
437 Ok(clusters)
438 }
439
440 async fn create_memory_cluster(&self, memories: Vec<Memory>) -> Result<MemoryCluster> {
442 let cluster_id = Uuid::new_v4();
443
444 let centroid_embedding = self.calculate_centroid_embedding(&memories)?;
446
447 let coherence_score = self.calculate_cluster_coherence(&memories)?;
449
450 let dominant_concepts = self.extract_dominant_concepts(&memories).await?;
452
453 let temporal_span = self.calculate_temporal_span(&memories);
455
456 Ok(MemoryCluster {
457 id: cluster_id,
458 memories,
459 centroid_embedding,
460 coherence_score,
461 dominant_concepts,
462 temporal_span,
463 })
464 }
465
466 async fn generate_cluster_insights(&self, cluster: &MemoryCluster) -> Result<Vec<Insight>> {
468 let mut insights = Vec::new();
469
470 if let Some(pattern_insight) = self.detect_cluster_patterns(cluster).await? {
472 insights.push(pattern_insight);
473 }
474
475 if let Some(synthesis_insight) = self.generate_synthesis_insight(cluster).await? {
477 insights.push(synthesis_insight);
478 }
479
480 if let Some(trend_insight) = self.detect_temporal_trends(cluster).await? {
482 insights.push(trend_insight);
483 }
484
485 if let Some(gap_insight) = self.identify_knowledge_gaps(cluster).await? {
487 insights.push(gap_insight);
488 }
489
490 Ok(insights)
491 }
492
493 async fn generate_cross_cluster_insights(
495 &self,
496 clusters: &[MemoryCluster],
497 ) -> Result<Vec<Insight>> {
498 let mut insights = Vec::new();
499
500 for i in 0..clusters.len() {
502 for j in i + 1..clusters.len() {
503 if let Some(analogy_insight) = self
504 .detect_cross_cluster_analogies(&clusters[i], &clusters[j])
505 .await?
506 {
507 insights.push(analogy_insight);
508 }
509 }
510 }
511
512 let causal_insights = self.detect_causal_relationships(clusters).await?;
514 insights.extend(causal_insights);
515
516 Ok(insights)
517 }
518
519 async fn update_knowledge_graph(&self, insights: &[Insight]) -> Result<Vec<KnowledgeNode>> {
521 let mut new_nodes = Vec::new();
522
523 for insight in insights {
524 let insight_node = KnowledgeNode {
526 id: insight.id,
527 concept: insight.content.clone(),
528 node_type: NodeType::Insight,
529 embedding: None, confidence: insight.confidence_score,
531 connections: Vec::new(),
532 created_at: insight.generated_at,
533 };
534
535 new_nodes.push(insight_node);
536
537 for concept in &insight.related_concepts {
539 let concept_node = KnowledgeNode {
540 id: Uuid::new_v4(),
541 concept: concept.clone(),
542 node_type: NodeType::Concept,
543 embedding: None,
544 confidence: 0.8,
545 connections: vec![KnowledgeEdge {
546 target_node_id: insight.id,
547 relationship_type: RelationshipType::Exemplifies,
548 strength: 0.9,
549 evidence_memories: insight.source_memory_ids.clone(),
550 }],
551 created_at: Utc::now(),
552 };
553
554 new_nodes.push(concept_node);
555 }
556 }
557
558 Ok(new_nodes)
559 }
560
561 async fn store_insights_as_memories(&self, insights: &[Insight]) -> Result<()> {
563 for insight in insights {
564 let importance_score =
565 insight.importance_score * self.config.insight_importance_multiplier;
566 let importance_score = importance_score.min(1.0); let metadata = serde_json::json!({
569 "insight_type": insight.insight_type,
570 "confidence_score": insight.confidence_score,
571 "source_memory_ids": insight.source_memory_ids,
572 "related_concepts": insight.related_concepts,
573 "validation_metrics": insight.validation_metrics,
574 "is_meta_memory": true,
575 "generated_by": "reflection_engine"
576 });
577
578 let create_request = CreateMemoryRequest {
579 content: insight.content.clone(),
580 embedding: None, tier: Some(MemoryTier::Working), importance_score: Some(importance_score),
583 metadata: Some(metadata),
584 parent_id: None,
585 expires_at: None,
586 };
587
588 match self.repository.create_memory(create_request).await {
589 Ok(memory) => {
590 debug!("Stored insight {} as memory {}", insight.id, memory.id);
591 }
592 Err(e) => {
593 warn!("Failed to store insight {} as memory: {}", insight.id, e);
594 }
595 }
596 }
597
598 Ok(())
599 }
600
601 async fn validate_and_prune_insights(&self, insights: &mut Vec<Insight>) -> Result<()> {
603 let mut validated_insights = Vec::new();
604 let mut seen_concepts = HashSet::new();
605
606 for insight in insights.iter() {
607 let concept_hash = self.hash_insight_concepts(insight);
609 if seen_concepts.contains(&concept_hash) {
610 debug!("Pruning duplicate insight: {}", insight.content);
611 continue;
612 }
613
614 if self.validate_insight_quality(insight).await? {
616 seen_concepts.insert(concept_hash);
617 validated_insights.push(insight.clone());
618 } else {
619 debug!("Pruning low-quality insight: {}", insight.content);
620 }
621 }
622
623 *insights = validated_insights;
624 Ok(())
625 }
626
627 async fn detect_cluster_patterns(&self, cluster: &MemoryCluster) -> Result<Option<Insight>> {
629 if cluster.memories.len() < self.config.min_cluster_size {
630 return Ok(None);
631 }
632
633 let pattern_frequency = self.analyze_content_patterns(&cluster.memories).await?;
635
636 if let Some((dominant_pattern, frequency)) =
637 pattern_frequency.iter().max_by_key(|(_, freq)| *freq)
638 {
639 if *frequency >= 3 {
640 let confidence_score =
641 ((*frequency as f64) / cluster.memories.len() as f64).min(1.0);
642
643 if confidence_score >= 0.6 {
644 let insight = Insight {
645 id: Uuid::new_v4(),
646 insight_type: InsightType::Pattern,
647 content: format!(
648 "Detected recurring pattern '{}' across {} memories in cluster with {:.1}% frequency",
649 dominant_pattern,
650 cluster.memories.len(),
651 confidence_score * 100.0
652 ),
653 confidence_score,
654 source_memory_ids: cluster.memories.iter().map(|m| m.id).collect(),
655 related_concepts: vec![dominant_pattern.clone()],
656 knowledge_graph_nodes: Vec::new(),
657 importance_score: confidence_score * 0.8,
658 generated_at: Utc::now(),
659 validation_metrics: ValidationMetrics {
660 novelty_score: 0.7,
661 coherence_score: confidence_score,
662 evidence_strength: confidence_score,
663 semantic_richness: 0.6,
664 predictive_power: 0.5,
665 },
666 };
667
668 return Ok(Some(insight));
669 }
670 }
671 }
672
673 Ok(None)
674 }
675
676 async fn generate_synthesis_insight(&self, cluster: &MemoryCluster) -> Result<Option<Insight>> {
677 if cluster.dominant_concepts.len() < 2 {
678 return Ok(None);
679 }
680
681 let synthesis_content = format!(
683 "Synthesis of {} related memories reveals connections between concepts: {}. This cluster shows coherence of {:.2} and spans memories from {} sources.",
684 cluster.memories.len(),
685 cluster.dominant_concepts.join(", "),
686 cluster.coherence_score,
687 cluster.memories.len()
688 );
689
690 let importance_score = cluster.coherence_score * 0.9;
691 let confidence_score = cluster.coherence_score;
692
693 let insight = Insight {
694 id: Uuid::new_v4(),
695 insight_type: InsightType::Synthesis,
696 content: synthesis_content,
697 confidence_score,
698 source_memory_ids: cluster.memories.iter().map(|m| m.id).collect(),
699 related_concepts: cluster.dominant_concepts.clone(),
700 knowledge_graph_nodes: Vec::new(),
701 importance_score,
702 generated_at: Utc::now(),
703 validation_metrics: ValidationMetrics {
704 novelty_score: 0.6,
705 coherence_score: cluster.coherence_score,
706 evidence_strength: (cluster.memories.len() as f64 / 10.0).min(1.0),
707 semantic_richness: (cluster.dominant_concepts.len() as f64 / 5.0).min(1.0),
708 predictive_power: 0.6,
709 },
710 };
711
712 Ok(Some(insight))
713 }
714
715 async fn detect_temporal_trends(&self, cluster: &MemoryCluster) -> Result<Option<Insight>> {
716 if let Some((start_time, end_time)) = cluster.temporal_span {
717 let duration = end_time.signed_duration_since(start_time);
718
719 if duration.num_hours() > 24 {
720 let trend_content = format!(
721 "Temporal trend detected: {} related memories occurred over {} days, suggesting sustained engagement with topic involving: {}",
722 cluster.memories.len(),
723 duration.num_days(),
724 cluster.dominant_concepts.join(", ")
725 );
726
727 let temporal_density =
728 cluster.memories.len() as f64 / duration.num_days().max(1) as f64;
729 let confidence_score = (temporal_density / 5.0).min(1.0);
730
731 if confidence_score >= 0.3 {
732 let insight = Insight {
733 id: Uuid::new_v4(),
734 insight_type: InsightType::Trend,
735 content: trend_content,
736 confidence_score,
737 source_memory_ids: cluster.memories.iter().map(|m| m.id).collect(),
738 related_concepts: cluster.dominant_concepts.clone(),
739 knowledge_graph_nodes: Vec::new(),
740 importance_score: confidence_score * 0.7,
741 generated_at: Utc::now(),
742 validation_metrics: ValidationMetrics {
743 novelty_score: 0.5,
744 coherence_score: cluster.coherence_score,
745 evidence_strength: confidence_score,
746 semantic_richness: 0.4,
747 predictive_power: 0.8,
748 },
749 };
750
751 return Ok(Some(insight));
752 }
753 }
754 }
755
756 Ok(None)
757 }
758
759 async fn identify_knowledge_gaps(&self, cluster: &MemoryCluster) -> Result<Option<Insight>> {
760 if cluster.memories.len() >= 5 && cluster.coherence_score < 0.6 {
764 let gap_content = format!(
766 "Potential knowledge gap identified: {} memories about {} show low coherence ({:.2}), suggesting missing connections or intermediate concepts",
767 cluster.memories.len(),
768 cluster.dominant_concepts.join(", "),
769 cluster.coherence_score
770 );
771
772 let confidence_score = 1.0 - cluster.coherence_score;
773
774 if confidence_score >= 0.4 {
775 let insight = Insight {
776 id: Uuid::new_v4(),
777 insight_type: InsightType::Gap,
778 content: gap_content,
779 confidence_score,
780 source_memory_ids: cluster.memories.iter().map(|m| m.id).collect(),
781 related_concepts: cluster.dominant_concepts.clone(),
782 knowledge_graph_nodes: Vec::new(),
783 importance_score: confidence_score * 0.6,
784 generated_at: Utc::now(),
785 validation_metrics: ValidationMetrics {
786 novelty_score: 0.8,
787 coherence_score: 0.5,
788 evidence_strength: confidence_score,
789 semantic_richness: 0.7,
790 predictive_power: 0.9,
791 },
792 };
793
794 return Ok(Some(insight));
795 }
796 }
797
798 Ok(None)
799 }
800
801 async fn analyze_content_patterns(
803 &self,
804 memories: &[Memory],
805 ) -> Result<HashMap<String, usize>> {
806 let mut pattern_counts = HashMap::new();
807
808 for memory in memories {
809 let content_lower = memory.content.to_lowercase();
812 let words: Vec<&str> = content_lower
813 .split_whitespace()
814 .filter(|word| word.len() > 4) .collect();
816
817 for word in words {
818 *pattern_counts.entry(word.to_string()).or_insert(0) += 1;
819 }
820 }
821
822 pattern_counts.retain(|_pattern, count| *count >= 2);
824
825 Ok(pattern_counts)
826 }
827
828 async fn detect_cross_cluster_analogies(
829 &self,
830 _cluster1: &MemoryCluster,
831 _cluster2: &MemoryCluster,
832 ) -> Result<Option<Insight>> {
833 Ok(None)
835 }
836
837 async fn detect_causal_relationships(
838 &self,
839 _clusters: &[MemoryCluster],
840 ) -> Result<Vec<Insight>> {
841 Ok(Vec::new())
843 }
844
845 async fn get_recent_memories_since(&self, cutoff_time: DateTime<Utc>) -> Result<Vec<Memory>> {
847 let search_request = SearchRequest {
848 date_range: Some(DateRange {
849 start: Some(cutoff_time),
850 end: Some(Utc::now()),
851 }),
852 search_type: Some(SearchType::Temporal),
853 limit: Some(1000),
854 ..Default::default()
855 };
856
857 let response = self.repository.search_memories(search_request).await?;
858 Ok(response.results.into_iter().map(|r| r.memory).collect())
859 }
860
861 async fn check_cluster_density(&self, _memories: &[Memory]) -> Result<Option<String>> {
862 Ok(None)
864 }
865
866 async fn check_temporal_patterns(&self, _memories: &[Memory]) -> Result<Option<String>> {
867 Ok(None)
869 }
870
871 fn calculate_cosine_similarity(&self, vec1: &Vector, vec2: &Vector) -> Result<f64> {
872 let slice1 = vec1.as_slice();
873 let slice2 = vec2.as_slice();
874
875 if slice1.len() != slice2.len() {
876 return Ok(0.0);
877 }
878
879 let dot_product: f64 = slice1
880 .iter()
881 .zip(slice2.iter())
882 .map(|(a, b)| (*a as f64) * (*b as f64))
883 .sum();
884
885 let norm1: f64 = slice1
886 .iter()
887 .map(|x| (*x as f64).powi(2))
888 .sum::<f64>()
889 .sqrt();
890 let norm2: f64 = slice2
891 .iter()
892 .map(|x| (*x as f64).powi(2))
893 .sum::<f64>()
894 .sqrt();
895
896 if norm1 == 0.0 || norm2 == 0.0 {
897 return Ok(0.0);
898 }
899
900 Ok(dot_product / (norm1 * norm2))
901 }
902
903 fn calculate_centroid_embedding(&self, memories: &[Memory]) -> Result<Option<Vector>> {
904 let embeddings: Vec<_> = memories
905 .iter()
906 .filter_map(|m| m.embedding.as_ref())
907 .collect();
908
909 if embeddings.is_empty() {
910 return Ok(None);
911 }
912
913 let dim = embeddings[0].as_slice().len();
914 let mut centroid = vec![0.0f32; dim];
915
916 for embedding in &embeddings {
917 for (i, &val) in embedding.as_slice().iter().enumerate() {
918 centroid[i] += val;
919 }
920 }
921
922 for val in &mut centroid {
923 *val /= embeddings.len() as f32;
924 }
925
926 Ok(Some(Vector::from(centroid)))
927 }
928
929 fn calculate_cluster_coherence(&self, memories: &[Memory]) -> Result<f64> {
930 let embeddings: Vec<_> = memories
931 .iter()
932 .filter_map(|m| m.embedding.as_ref())
933 .collect();
934
935 if embeddings.len() < 2 {
936 return Ok(1.0);
937 }
938
939 let mut total_similarity = 0.0;
940 let mut pair_count = 0;
941
942 for i in 0..embeddings.len() {
943 for j in i + 1..embeddings.len() {
944 let similarity = self.calculate_cosine_similarity(embeddings[i], embeddings[j])?;
945 total_similarity += similarity;
946 pair_count += 1;
947 }
948 }
949
950 Ok(if pair_count > 0 {
951 total_similarity / pair_count as f64
952 } else {
953 0.0
954 })
955 }
956
957 async fn extract_dominant_concepts(&self, _memories: &[Memory]) -> Result<Vec<String>> {
958 Ok(vec!["concept1".to_string(), "concept2".to_string()])
960 }
961
962 fn calculate_temporal_span(
963 &self,
964 memories: &[Memory],
965 ) -> Option<(DateTime<Utc>, DateTime<Utc>)> {
966 if memories.is_empty() {
967 return None;
968 }
969
970 let mut min_time = memories[0].created_at;
971 let mut max_time = memories[0].created_at;
972
973 for memory in memories {
974 if memory.created_at < min_time {
975 min_time = memory.created_at;
976 }
977 if memory.created_at > max_time {
978 max_time = memory.created_at;
979 }
980 }
981
982 Some((min_time, max_time))
983 }
984
985 fn hash_insight_concepts(&self, insight: &Insight) -> u64 {
986 use std::collections::hash_map::DefaultHasher;
987 use std::hash::{Hash, Hasher};
988
989 let mut hasher = DefaultHasher::new();
990 insight.related_concepts.hash(&mut hasher);
991 insight.insight_type.hash(&mut hasher);
992 hasher.finish()
993 }
994
995 async fn validate_insight_quality(&self, insight: &Insight) -> Result<bool> {
996 let metrics = &insight.validation_metrics;
998
999 Ok(metrics.novelty_score > 0.3
1001 && metrics.coherence_score > 0.5
1002 && metrics.evidence_strength > 0.4
1003 && insight.confidence_score > 0.6)
1004 }
1005}
1006
1007pub struct KnowledgeGraph {
1009 nodes: HashMap<Uuid, KnowledgeNode>,
1010 concept_index: HashMap<String, Vec<Uuid>>,
1011}
1012
1013impl KnowledgeGraph {
1014 pub fn new() -> Self {
1015 Self {
1016 nodes: HashMap::new(),
1017 concept_index: HashMap::new(),
1018 }
1019 }
1020
1021 pub fn add_node(&mut self, node: KnowledgeNode) {
1022 self.concept_index
1024 .entry(node.concept.clone())
1025 .or_default()
1026 .push(node.id);
1027
1028 self.nodes.insert(node.id, node);
1029 }
1030
1031 pub fn find_related_concepts(&self, concept: &str, max_depth: usize) -> Vec<Uuid> {
1032 let mut related = Vec::new();
1033 let mut visited = HashSet::new();
1034 let mut queue = VecDeque::new();
1035
1036 if let Some(direct_matches) = self.concept_index.get(concept) {
1038 for &node_id in direct_matches {
1039 queue.push_back((node_id, 0));
1040 }
1041 }
1042
1043 while let Some((node_id, depth)) = queue.pop_front() {
1045 if depth >= max_depth || visited.contains(&node_id) {
1046 continue;
1047 }
1048
1049 visited.insert(node_id);
1050 related.push(node_id);
1051
1052 if let Some(node) = self.nodes.get(&node_id) {
1053 for edge in &node.connections {
1054 if !visited.contains(&edge.target_node_id) {
1055 queue.push_back((edge.target_node_id, depth + 1));
1056 }
1057 }
1058 }
1059 }
1060
1061 related
1062 }
1063}
1064
1065impl Default for KnowledgeGraph {
1066 fn default() -> Self {
1067 Self::new()
1068 }
1069}
1070
1071#[cfg(test)]
1072mod tests {
1073 use super::*;
1074
1075 #[test]
1076 fn test_reflection_config_defaults() {
1077 let config = ReflectionConfig::default();
1078 assert_eq!(config.importance_trigger_threshold, 150.0);
1079 assert_eq!(config.target_insights_per_reflection, 3);
1080 assert_eq!(config.insight_importance_multiplier, 1.5);
1081 }
1082
1083 #[test]
1084 fn test_knowledge_graph_creation() {
1085 let mut graph = KnowledgeGraph::new();
1086
1087 let node = KnowledgeNode {
1088 id: Uuid::new_v4(),
1089 concept: "test_concept".to_string(),
1090 node_type: NodeType::Concept,
1091 embedding: None,
1092 confidence: 0.8,
1093 connections: Vec::new(),
1094 created_at: Utc::now(),
1095 };
1096
1097 let node_id = node.id;
1098 graph.add_node(node);
1099
1100 assert!(graph.nodes.contains_key(&node_id));
1101 assert!(graph.concept_index.contains_key("test_concept"));
1102 }
1103
1104 #[test]
1105 fn test_insight_type_serialization() {
1106 let insight_type = InsightType::Pattern;
1107 let serialized = serde_json::to_string(&insight_type).unwrap();
1108 let deserialized: InsightType = serde_json::from_str(&serialized).unwrap();
1109 assert_eq!(insight_type, deserialized);
1110 }
1111}