codex_memory/memory/
reflection_engine.rs

1//! Reflection & Insight Generation Engine
2//!
3//! This module implements a cognitive architecture for generating higher-level insights
4//! from accumulated memories through reflection processes. The design is based on:
5//!
6//! ## Cognitive Science Foundation
7//!
8//! ### Core Research
9//! 1. **Metacognition Theory (Flavell, 1979)**: Thinking about thinking processes
10//! 2. **Elaborative Processing (Craik & Lockhart, 1972)**: Deeper processing creates stronger memories
11//! 3. **Schema Theory (Bartlett, 1932)**: Knowledge organized in interconnected structures
12//! 4. **Constructive Memory (Schacter, 1999)**: Memory reconstruction creates new insights
13//! 5. **Knowledge Graph Theory (Semantic Networks)**: Conceptual relationships enable inference
14//!
15//! ### Reflection Triggers
16//! - **Importance Accumulation**: Sum > 150 points triggers reflection
17//! - **Semantic Clustering**: Dense clusters of related memories
18//! - **Temporal Patterns**: Recurring themes over time
19//! - **Contradiction Detection**: Conflicting information requiring resolution
20//! - **Gap Identification**: Missing knowledge in established schemas
21//!
22//! ## Architecture Components
23//!
24//! ### 1. Reflection Trigger System
25//! Monitors memory accumulation and identifies when reflection should occur
26//!
27//! ### 2. Memory Clustering Engine
28//! Groups semantically related memories for insight generation
29//!
30//! ### 3. Insight Generation Pipeline
31//! - Pattern Detection: Identifies recurring themes and relationships
32//! - Gap Analysis: Finds missing connections in knowledge structures
33//! - Synthesis: Combines related concepts into higher-level insights
34//! - Validation: Ensures insights are novel and meaningful
35//!
36//! ### 4. Knowledge Graph Builder
37//! Creates and maintains bidirectional relationships between memories and insights
38//!
39//! ### 5. Meta-Memory Manager
40//! Handles insight storage, retrieval, and relationship tracking
41
42use 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/// Configuration for reflection and insight generation
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct ReflectionConfig {
56    /// Importance threshold that triggers reflection
57    pub importance_trigger_threshold: f64,
58
59    /// Maximum memories to analyze in one reflection session
60    pub max_memories_per_reflection: usize,
61
62    /// Target number of insights per reflection
63    pub target_insights_per_reflection: usize,
64
65    /// Minimum similarity for memory clustering
66    pub clustering_similarity_threshold: f64,
67
68    /// Importance multiplier for generated insights
69    pub insight_importance_multiplier: f64,
70
71    /// Maximum depth for knowledge graph traversal
72    pub max_graph_depth: usize,
73
74    /// Minimum cluster size for insight generation
75    pub min_cluster_size: usize,
76
77    /// Time window for temporal pattern analysis (days)
78    pub temporal_analysis_window_days: i64,
79
80    /// Cooldown period between reflections (hours)
81    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/// Types of insights that can be generated
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
102pub enum InsightType {
103    /// Pattern detected across multiple memories
104    Pattern,
105    /// Synthesis of related concepts
106    Synthesis,
107    /// Gap identified in knowledge structure
108    Gap,
109    /// Contradiction requiring resolution
110    Contradiction,
111    /// Temporal trend or evolution
112    Trend,
113    /// Causal relationship discovered
114    Causality,
115    /// Analogy between disparate concepts
116    Analogy,
117}
118
119/// Insight generation result
120#[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/// Metrics for validating insight quality
135#[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/// Knowledge graph node representing concepts and relationships
145#[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/// Types of knowledge nodes
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
159pub enum NodeType {
160    Concept,
161    Entity,
162    Relationship,
163    Insight,
164    Memory,
165}
166
167/// Edges in the knowledge graph
168#[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/// Types of relationships in the knowledge graph
177#[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/// Memory cluster for insight generation
192#[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/// Reflection session state
203#[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/// Status of reflection session
216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
217pub enum ReflectionStatus {
218    InProgress,
219    Completed,
220    Failed,
221    Cancelled,
222}
223
224/// Main reflection and insight generation engine
225pub 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    /// Check if reflection should be triggered based on accumulated importance
244    pub async fn should_trigger_reflection(&self) -> Result<Option<String>> {
245        // Check cooldown period
246        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        // Calculate total importance since last reflection
254        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        // Check for dense semantic clusters
269        if let Some(cluster_reason) = self.check_cluster_density(&recent_memories).await? {
270            return Ok(Some(cluster_reason));
271        }
272
273        // Check for temporal patterns
274        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    /// Execute a complete reflection session
282    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    /// Main reflection pipeline execution
328    async fn execute_reflection_pipeline(&self, session: &mut ReflectionSession) -> Result<()> {
329        // Step 1: Gather memories for analysis
330        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        // Step 2: Cluster memories by semantic similarity
339        session.generated_clusters = self.cluster_memories(&session.analyzed_memories).await?;
340
341        // Step 3: Generate insights from clusters
342        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        // Step 4: Cross-cluster insight generation
348        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        // Step 5: Update knowledge graph
354        session.knowledge_graph_updates = self
355            .update_knowledge_graph(&session.generated_insights)
356            .await?;
357
358        // Step 6: Store insights as meta-memories
359        self.store_insights_as_memories(&session.generated_insights)
360            .await?;
361
362        // Step 7: Validate and prune insights to prevent loops
363        self.validate_and_prune_insights(&mut session.generated_insights)
364            .await?;
365
366        Ok(())
367    }
368
369    /// Gather memories for reflection analysis
370    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        // Get recent high-importance memories
376        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    /// Cluster memories by semantic similarity using hierarchical clustering
400    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            // Find similar memories for this cluster
409            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            // Only create cluster if it meets minimum size requirement
431            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    /// Create a memory cluster with computed properties
441    async fn create_memory_cluster(&self, memories: Vec<Memory>) -> Result<MemoryCluster> {
442        let cluster_id = Uuid::new_v4();
443
444        // Calculate centroid embedding if available
445        let centroid_embedding = self.calculate_centroid_embedding(&memories)?;
446
447        // Calculate coherence score (average pairwise similarity)
448        let coherence_score = self.calculate_cluster_coherence(&memories)?;
449
450        // Extract dominant concepts (simplified - would use NER/topic modeling in production)
451        let dominant_concepts = self.extract_dominant_concepts(&memories).await?;
452
453        // Calculate temporal span
454        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    /// Generate insights from a single memory cluster
467    async fn generate_cluster_insights(&self, cluster: &MemoryCluster) -> Result<Vec<Insight>> {
468        let mut insights = Vec::new();
469
470        // Pattern detection insight
471        if let Some(pattern_insight) = self.detect_cluster_patterns(cluster).await? {
472            insights.push(pattern_insight);
473        }
474
475        // Synthesis insight
476        if let Some(synthesis_insight) = self.generate_synthesis_insight(cluster).await? {
477            insights.push(synthesis_insight);
478        }
479
480        // Temporal trend insight
481        if let Some(trend_insight) = self.detect_temporal_trends(cluster).await? {
482            insights.push(trend_insight);
483        }
484
485        // Gap analysis insight
486        if let Some(gap_insight) = self.identify_knowledge_gaps(cluster).await? {
487            insights.push(gap_insight);
488        }
489
490        Ok(insights)
491    }
492
493    /// Generate insights across multiple clusters
494    async fn generate_cross_cluster_insights(
495        &self,
496        clusters: &[MemoryCluster],
497    ) -> Result<Vec<Insight>> {
498        let mut insights = Vec::new();
499
500        // Analogy detection between clusters
501        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        // Causal relationship detection
513        let causal_insights = self.detect_causal_relationships(clusters).await?;
514        insights.extend(causal_insights);
515
516        Ok(insights)
517    }
518
519    /// Update the knowledge graph with new insights
520    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            // Create node for the insight itself
525            let insight_node = KnowledgeNode {
526                id: insight.id,
527                concept: insight.content.clone(),
528                node_type: NodeType::Insight,
529                embedding: None, // Would generate embedding in production
530                confidence: insight.confidence_score,
531                connections: Vec::new(),
532                created_at: insight.generated_at,
533            };
534
535            new_nodes.push(insight_node);
536
537            // Create nodes for related concepts
538            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    /// Store insights as high-importance meta-memories
562    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); // Cap at 1.0
567
568            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,                 // Would generate in production
581                tier: Some(MemoryTier::Working), // Start insights in working tier
582                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    /// Validate insights and prune duplicates/loops to prevent insight inflation
602    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            // Check for conceptual novelty
608            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            // Validate insight quality
615            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    // Helper methods for insight generation
628    async fn detect_cluster_patterns(&self, _cluster: &MemoryCluster) -> Result<Option<Insight>> {
629        // Implementation would analyze recurring patterns within cluster
630        // This is a simplified placeholder
631        Ok(None)
632    }
633
634    async fn generate_synthesis_insight(
635        &self,
636        _cluster: &MemoryCluster,
637    ) -> Result<Option<Insight>> {
638        // Implementation would synthesize cluster concepts into higher-level understanding
639        Ok(None)
640    }
641
642    async fn detect_temporal_trends(&self, _cluster: &MemoryCluster) -> Result<Option<Insight>> {
643        // Implementation would analyze temporal patterns within cluster
644        Ok(None)
645    }
646
647    async fn identify_knowledge_gaps(&self, _cluster: &MemoryCluster) -> Result<Option<Insight>> {
648        // Implementation would identify missing knowledge in cluster domain
649        Ok(None)
650    }
651
652    async fn detect_cross_cluster_analogies(
653        &self,
654        _cluster1: &MemoryCluster,
655        _cluster2: &MemoryCluster,
656    ) -> Result<Option<Insight>> {
657        // Implementation would find analogies between different concept clusters
658        Ok(None)
659    }
660
661    async fn detect_causal_relationships(
662        &self,
663        _clusters: &[MemoryCluster],
664    ) -> Result<Vec<Insight>> {
665        // Implementation would identify causal relationships across clusters
666        Ok(Vec::new())
667    }
668
669    // Utility methods
670    async fn get_recent_memories_since(&self, cutoff_time: DateTime<Utc>) -> Result<Vec<Memory>> {
671        let search_request = SearchRequest {
672            date_range: Some(DateRange {
673                start: Some(cutoff_time),
674                end: Some(Utc::now()),
675            }),
676            search_type: Some(SearchType::Temporal),
677            limit: Some(1000),
678            ..Default::default()
679        };
680
681        let response = self.repository.search_memories(search_request).await?;
682        Ok(response.results.into_iter().map(|r| r.memory).collect())
683    }
684
685    async fn check_cluster_density(&self, _memories: &[Memory]) -> Result<Option<String>> {
686        // Implementation would check for dense semantic clusters
687        Ok(None)
688    }
689
690    async fn check_temporal_patterns(&self, _memories: &[Memory]) -> Result<Option<String>> {
691        // Implementation would check for temporal patterns warranting reflection
692        Ok(None)
693    }
694
695    fn calculate_cosine_similarity(&self, vec1: &Vector, vec2: &Vector) -> Result<f64> {
696        let slice1 = vec1.as_slice();
697        let slice2 = vec2.as_slice();
698
699        if slice1.len() != slice2.len() {
700            return Ok(0.0);
701        }
702
703        let dot_product: f64 = slice1
704            .iter()
705            .zip(slice2.iter())
706            .map(|(a, b)| (*a as f64) * (*b as f64))
707            .sum();
708
709        let norm1: f64 = slice1
710            .iter()
711            .map(|x| (*x as f64).powi(2))
712            .sum::<f64>()
713            .sqrt();
714        let norm2: f64 = slice2
715            .iter()
716            .map(|x| (*x as f64).powi(2))
717            .sum::<f64>()
718            .sqrt();
719
720        if norm1 == 0.0 || norm2 == 0.0 {
721            return Ok(0.0);
722        }
723
724        Ok(dot_product / (norm1 * norm2))
725    }
726
727    fn calculate_centroid_embedding(&self, memories: &[Memory]) -> Result<Option<Vector>> {
728        let embeddings: Vec<_> = memories
729            .iter()
730            .filter_map(|m| m.embedding.as_ref())
731            .collect();
732
733        if embeddings.is_empty() {
734            return Ok(None);
735        }
736
737        let dim = embeddings[0].as_slice().len();
738        let mut centroid = vec![0.0f32; dim];
739
740        for embedding in &embeddings {
741            for (i, &val) in embedding.as_slice().iter().enumerate() {
742                centroid[i] += val;
743            }
744        }
745
746        for val in &mut centroid {
747            *val /= embeddings.len() as f32;
748        }
749
750        Ok(Some(Vector::from(centroid)))
751    }
752
753    fn calculate_cluster_coherence(&self, memories: &[Memory]) -> Result<f64> {
754        let embeddings: Vec<_> = memories
755            .iter()
756            .filter_map(|m| m.embedding.as_ref())
757            .collect();
758
759        if embeddings.len() < 2 {
760            return Ok(1.0);
761        }
762
763        let mut total_similarity = 0.0;
764        let mut pair_count = 0;
765
766        for i in 0..embeddings.len() {
767            for j in i + 1..embeddings.len() {
768                let similarity = self.calculate_cosine_similarity(embeddings[i], embeddings[j])?;
769                total_similarity += similarity;
770                pair_count += 1;
771            }
772        }
773
774        Ok(if pair_count > 0 {
775            total_similarity / pair_count as f64
776        } else {
777            0.0
778        })
779    }
780
781    async fn extract_dominant_concepts(&self, _memories: &[Memory]) -> Result<Vec<String>> {
782        // Simplified implementation - would use NLP/topic modeling in production
783        Ok(vec!["concept1".to_string(), "concept2".to_string()])
784    }
785
786    fn calculate_temporal_span(
787        &self,
788        memories: &[Memory],
789    ) -> Option<(DateTime<Utc>, DateTime<Utc>)> {
790        if memories.is_empty() {
791            return None;
792        }
793
794        let mut min_time = memories[0].created_at;
795        let mut max_time = memories[0].created_at;
796
797        for memory in memories {
798            if memory.created_at < min_time {
799                min_time = memory.created_at;
800            }
801            if memory.created_at > max_time {
802                max_time = memory.created_at;
803            }
804        }
805
806        Some((min_time, max_time))
807    }
808
809    fn hash_insight_concepts(&self, insight: &Insight) -> u64 {
810        use std::collections::hash_map::DefaultHasher;
811        use std::hash::{Hash, Hasher};
812
813        let mut hasher = DefaultHasher::new();
814        insight.related_concepts.hash(&mut hasher);
815        insight.insight_type.hash(&mut hasher);
816        hasher.finish()
817    }
818
819    async fn validate_insight_quality(&self, insight: &Insight) -> Result<bool> {
820        // Validate based on metrics
821        let metrics = &insight.validation_metrics;
822
823        // Minimum thresholds for quality
824        Ok(metrics.novelty_score > 0.3
825            && metrics.coherence_score > 0.5
826            && metrics.evidence_strength > 0.4
827            && insight.confidence_score > 0.6)
828    }
829}
830
831/// Knowledge graph management
832pub struct KnowledgeGraph {
833    nodes: HashMap<Uuid, KnowledgeNode>,
834    concept_index: HashMap<String, Vec<Uuid>>,
835}
836
837impl KnowledgeGraph {
838    pub fn new() -> Self {
839        Self {
840            nodes: HashMap::new(),
841            concept_index: HashMap::new(),
842        }
843    }
844
845    pub fn add_node(&mut self, node: KnowledgeNode) {
846        // Index by concept for fast lookup
847        self.concept_index
848            .entry(node.concept.clone())
849            .or_insert_with(Vec::new)
850            .push(node.id);
851
852        self.nodes.insert(node.id, node);
853    }
854
855    pub fn find_related_concepts(&self, concept: &str, max_depth: usize) -> Vec<Uuid> {
856        let mut related = Vec::new();
857        let mut visited = HashSet::new();
858        let mut queue = VecDeque::new();
859
860        // Start with direct matches
861        if let Some(direct_matches) = self.concept_index.get(concept) {
862            for &node_id in direct_matches {
863                queue.push_back((node_id, 0));
864            }
865        }
866
867        // Breadth-first traversal
868        while let Some((node_id, depth)) = queue.pop_front() {
869            if depth >= max_depth || visited.contains(&node_id) {
870                continue;
871            }
872
873            visited.insert(node_id);
874            related.push(node_id);
875
876            if let Some(node) = self.nodes.get(&node_id) {
877                for edge in &node.connections {
878                    if !visited.contains(&edge.target_node_id) {
879                        queue.push_back((edge.target_node_id, depth + 1));
880                    }
881                }
882            }
883        }
884
885        related
886    }
887}
888
889impl Default for KnowledgeGraph {
890    fn default() -> Self {
891        Self::new()
892    }
893}
894
895#[cfg(test)]
896mod tests {
897    use super::*;
898
899    #[test]
900    fn test_reflection_config_defaults() {
901        let config = ReflectionConfig::default();
902        assert_eq!(config.importance_trigger_threshold, 150.0);
903        assert_eq!(config.target_insights_per_reflection, 3);
904        assert_eq!(config.insight_importance_multiplier, 1.5);
905    }
906
907    #[test]
908    fn test_knowledge_graph_creation() {
909        let mut graph = KnowledgeGraph::new();
910
911        let node = KnowledgeNode {
912            id: Uuid::new_v4(),
913            concept: "test_concept".to_string(),
914            node_type: NodeType::Concept,
915            embedding: None,
916            confidence: 0.8,
917            connections: Vec::new(),
918            created_at: Utc::now(),
919        };
920
921        let node_id = node.id;
922        graph.add_node(node);
923
924        assert!(graph.nodes.contains_key(&node_id));
925        assert!(graph.concept_index.contains_key("test_concept"));
926    }
927
928    #[test]
929    fn test_insight_type_serialization() {
930        let insight_type = InsightType::Pattern;
931        let serialized = serde_json::to_string(&insight_type).unwrap();
932        let deserialized: InsightType = serde_json::from_str(&serialized).unwrap();
933        assert_eq!(insight_type, deserialized);
934    }
935}