Skip to main content

matrixcode_core/memory/
unified_registry.rs

1//! Unified registry for learning from extraction results.
2//!
3//! This module provides a unified interface to learn from extraction
4//! results, updating pattern registry for persistent learning.
5
6use anyhow::Result;
7
8use super::unified_extraction::UnifiedExtractionResult;
9use super::pattern_registry::PatternRegistry;
10
11/// Unified registry that manages pattern registry.
12///
13/// Provides a single interface to learn from unified extraction results,
14/// updating pattern registry and saving them atomically.
15/// Note: Keywords are now handled in real-time via FocusTrackerConfig,
16/// not persisted in the registry.
17pub struct UnifiedRegistry {
18    /// Pattern registry for conversation patterns.
19    pattern_registry: PatternRegistry,
20}
21
22impl Default for UnifiedRegistry {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl UnifiedRegistry {
29    /// Create a new unified registry with default registries.
30    pub fn new() -> Self {
31        Self {
32            pattern_registry: PatternRegistry::new(),
33        }
34    }
35
36    /// Create a unified registry from existing pattern registry.
37    pub fn from_pattern_registry(pattern_registry: PatternRegistry) -> Self {
38        Self {
39            pattern_registry,
40        }
41    }
42
43    /// Load unified registry from default file paths.
44    ///
45    /// If files don't exist, creates registries with presets loaded.
46    pub fn load_or_default() -> Result<Self> {
47        let pattern_registry = PatternRegistry::from_default_file()?;
48        Ok(Self {
49            pattern_registry,
50        })
51    }
52
53    /// Learn from a unified extraction result.
54    ///
55    /// Updates pattern registry with extracted data.
56    /// Note: Keywords are no longer persisted; they are used in real-time.
57    pub fn learn_from_extraction(&mut self, result: &UnifiedExtractionResult, _session_id: &str) {
58        // Learn conversation patterns
59        if !result.conversation_patterns.is_empty() {
60            self.pattern_registry.learn_patterns(&result.conversation_patterns);
61        }
62
63        // Keywords are now handled in real-time via FocusTrackerConfig,
64        // not persisted in the registry
65    }
66
67    /// Save all registries to their default file paths.
68    pub fn save_all(&self) -> Result<()> {
69        self.pattern_registry.save_to_default_file()?;
70        Ok(())
71    }
72
73    /// Get reference to pattern registry.
74    pub fn pattern_registry(&self) -> &PatternRegistry {
75        &self.pattern_registry
76    }
77
78    /// Get mutable reference to pattern registry.
79    pub fn pattern_registry_mut(&mut self) -> &mut PatternRegistry {
80        &mut self.pattern_registry
81    }
82
83    /// Prune pattern registry (remove inactive/old entries).
84    pub fn prune(&mut self) {
85        self.pattern_registry.prune();
86    }
87
88    /// Get combined statistics.
89    pub fn stats(&self) -> UnifiedRegistryStats {
90        let pattern_stats = self.pattern_registry.stats();
91
92        UnifiedRegistryStats {
93            total_patterns: pattern_stats.total,
94            active_patterns: pattern_stats.active,
95        }
96    }
97}
98
99/// Combined statistics for unified registry.
100#[derive(Debug, Clone)]
101pub struct UnifiedRegistryStats {
102    /// Total pattern count.
103    pub total_patterns: usize,
104    /// Active pattern count.
105    pub active_patterns: usize,
106}
107
108impl std::fmt::Display for UnifiedRegistryStats {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        writeln!(
111            f,
112            "Unified Registry Stats:"
113        )?;
114        writeln!(
115            f,
116            "  Patterns: {} (active: {})",
117            self.total_patterns, self.active_patterns
118        )
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_unified_registry_new() {
128        let registry = UnifiedRegistry::new();
129        // PatternRegistry is now empty - no presets loaded
130        assert!(registry.pattern_registry().is_empty());
131    }
132
133    #[test]
134    fn test_unified_registry_default() {
135        let registry = UnifiedRegistry::default();
136        // PatternRegistry is now empty - no presets loaded
137        assert!(registry.pattern_registry().is_empty());
138    }
139
140    #[test]
141    fn test_unified_registry_stats() {
142        let registry = UnifiedRegistry::new();
143        let stats = registry.stats();
144
145        // PatternRegistry is empty, so total_patterns should be 0
146        assert_eq!(stats.total_patterns, 0);
147    }
148
149    #[test]
150    fn test_unified_registry_stats_display() {
151        let registry = UnifiedRegistry::new();
152        let stats = registry.stats();
153        let display = format!("{}", stats);
154
155        assert!(display.contains("Unified Registry Stats"));
156        assert!(display.contains("Patterns:"));
157    }
158
159    #[test]
160    fn test_unified_registry_prune() {
161        let mut registry = UnifiedRegistry::new();
162        // Add a pattern first
163        registry.pattern_registry_mut().add_pattern(
164            crate::memory::ConversationPattern::manual(
165                crate::memory::PatternType::Code,
166                "test-pattern",
167            ),
168        );
169
170        registry.prune();
171
172        // After prune, manually added patterns should still be present
173        assert!(!registry.pattern_registry().is_empty());
174    }
175
176    #[test]
177    fn test_unified_registry_mut_accessors() {
178        let mut registry = UnifiedRegistry::new();
179
180        // PatternRegistry is now empty - no presets loaded
181        let patterns = registry.pattern_registry_mut();
182        assert!(patterns.is_empty());
183
184        // Test mutable accessor works - add a pattern
185        registry.pattern_registry_mut().add_pattern(
186            crate::memory::ConversationPattern::manual(
187                crate::memory::PatternType::Code,
188                "test-pattern",
189            ),
190        );
191        assert!(!registry.pattern_registry().is_empty());
192    }
193}