Skip to main content

graphrag_core/config/
enhancements.rs

1//! Configuration for latest enhancements and atomic component control
2
3use serde::{Deserialize, Serialize};
4
5/// Configuration for latest enhancements with atomic control
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct EnhancementsConfig {
8    /// Master switch for all enhancements
9    pub enabled: bool,
10    /// Query analysis configuration
11    pub query_analysis: QueryAnalysisConfig,
12    /// Adaptive retrieval configuration
13    pub adaptive_retrieval: AdaptiveRetrievalConfig,
14    /// Performance benchmarking configuration
15    pub performance_benchmarking: BenchmarkingConfig,
16    /// Enhanced function registry configuration
17    pub enhanced_function_registry: FunctionRegistryConfig,
18    /// LightRAG dual-level retrieval configuration
19    #[cfg(feature = "lightrag")]
20    pub lightrag: LightRAGConfig,
21    /// Leiden community detection configuration
22    #[cfg(feature = "leiden")]
23    pub leiden: LeidenCommunitiesConfig,
24    /// Cross-encoder reranking configuration
25    #[cfg(feature = "cross-encoder")]
26    pub cross_encoder: CrossEncoderConfig,
27}
28
29impl Default for EnhancementsConfig {
30    fn default() -> Self {
31        Self {
32            enabled: true,
33            query_analysis: QueryAnalysisConfig::default(),
34            adaptive_retrieval: AdaptiveRetrievalConfig::default(),
35            performance_benchmarking: BenchmarkingConfig::default(),
36            enhanced_function_registry: FunctionRegistryConfig::default(),
37            #[cfg(feature = "lightrag")]
38            lightrag: LightRAGConfig::default(),
39            #[cfg(feature = "leiden")]
40            leiden: LeidenCommunitiesConfig::default(),
41            #[cfg(feature = "cross-encoder")]
42            cross_encoder: CrossEncoderConfig::default(),
43        }
44    }
45}
46
47/// Query analysis enhancement configuration
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct QueryAnalysisConfig {
50    /// Enable query type analysis
51    pub enabled: bool,
52    /// Minimum confidence for type classification
53    pub min_confidence: f32,
54    /// Enable automatic strategy suggestion
55    pub enable_strategy_suggestion: bool,
56    /// Enable keyword-based analysis
57    pub enable_keyword_analysis: bool,
58    /// Enable complexity scoring
59    pub enable_complexity_scoring: bool,
60}
61
62impl Default for QueryAnalysisConfig {
63    fn default() -> Self {
64        Self {
65            enabled: true,
66            min_confidence: 0.6,
67            enable_strategy_suggestion: true,
68            enable_keyword_analysis: true,
69            enable_complexity_scoring: true,
70        }
71    }
72}
73
74/// Adaptive retrieval enhancement configuration
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct AdaptiveRetrievalConfig {
77    /// Enable adaptive strategy selection
78    pub enabled: bool,
79    /// Use query analysis for strategy selection
80    pub use_query_analysis: bool,
81    /// Enable cross-strategy result fusion
82    pub enable_cross_strategy_fusion: bool,
83    /// Diversity threshold for result selection
84    pub diversity_threshold: f32,
85    /// Enable diversity-aware selection
86    pub enable_diversity_selection: bool,
87    /// Enable confidence-based weighting
88    pub enable_confidence_weighting: bool,
89}
90
91impl Default for AdaptiveRetrievalConfig {
92    fn default() -> Self {
93        Self {
94            enabled: true,
95            use_query_analysis: true,
96            enable_cross_strategy_fusion: true,
97            diversity_threshold: 0.8,
98            enable_diversity_selection: true,
99            enable_confidence_weighting: true,
100        }
101    }
102}
103
104/// Performance benchmarking configuration
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct BenchmarkingConfig {
107    /// Enable performance benchmarking
108    pub enabled: bool,
109    /// Generate automatic recommendations
110    pub auto_recommendations: bool,
111    /// Run comprehensive testing suite
112    pub comprehensive_testing: bool,
113    /// Number of benchmark iterations
114    pub iterations: usize,
115    /// Include parallel performance testing
116    pub include_parallel: bool,
117    /// Enable memory profiling
118    pub enable_memory_profiling: bool,
119}
120
121impl Default for BenchmarkingConfig {
122    fn default() -> Self {
123        Self {
124            enabled: false, // Disabled by default (dev/test only)
125            auto_recommendations: true,
126            comprehensive_testing: false,
127            iterations: 3,
128            include_parallel: true,
129            enable_memory_profiling: false,
130        }
131    }
132}
133
134/// Enhanced function registry configuration
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct FunctionRegistryConfig {
137    /// Enable enhanced function registry
138    pub enabled: bool,
139    /// Enable function categorization
140    pub categorization: bool,
141    /// Track function usage statistics
142    pub usage_statistics: bool,
143    /// Allow runtime function registration
144    pub dynamic_registration: bool,
145    /// Enable function performance monitoring
146    pub performance_monitoring: bool,
147    /// Enable function recommendation system
148    pub recommendation_system: bool,
149}
150
151impl Default for FunctionRegistryConfig {
152    fn default() -> Self {
153        Self {
154            enabled: true,
155            categorization: true,
156            usage_statistics: true,
157            dynamic_registration: true,
158            performance_monitoring: false,
159            recommendation_system: true,
160        }
161    }
162}
163
164/// LightRAG dual-level retrieval configuration
165#[cfg(feature = "lightrag")]
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct LightRAGConfig {
168    /// Enable LightRAG dual-level retrieval
169    pub enabled: bool,
170    /// Maximum keywords for query extraction (LightRAG: <20)
171    pub max_keywords: usize,
172    /// Weight for high-level (topic) results
173    pub high_level_weight: f32,
174    /// Weight for low-level (entity) results
175    pub low_level_weight: f32,
176    /// Merge strategy: "interleave", "high_first", "low_first", "weighted"
177    pub merge_strategy: String,
178    /// Language for keyword extraction
179    pub language: String,
180    /// Enable caching for keyword extraction
181    pub enable_cache: bool,
182}
183
184#[cfg(feature = "lightrag")]
185impl Default for LightRAGConfig {
186    fn default() -> Self {
187        Self {
188            enabled: true,
189            max_keywords: 20,  // LightRAG optimization: <20 keywords
190            high_level_weight: 0.6,
191            low_level_weight: 0.4,
192            merge_strategy: "weighted".to_string(),
193            language: "English".to_string(),
194            enable_cache: true,
195        }
196    }
197}
198
199/// Leiden community detection configuration
200#[cfg(feature = "leiden")]
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct LeidenCommunitiesConfig {
203    /// Enable Leiden community detection
204    pub enabled: bool,
205    /// Maximum community size
206    pub max_cluster_size: usize,
207    /// Use only largest connected component
208    pub use_lcc: bool,
209    /// Random seed for reproducibility (None = random)
210    pub seed: Option<u64>,
211    /// Modularity resolution (lower = larger communities)
212    pub resolution: f32,
213    /// Maximum hierarchical depth
214    pub max_levels: usize,
215    /// Minimum improvement threshold
216    pub min_improvement: f32,
217    /// Enable hierarchical clustering on entity graph
218    pub enable_hierarchical: bool,
219    /// Auto-generate summaries for each community
220    pub generate_summaries: bool,
221    /// Maximum summary length (number of entities/sentences)
222    pub max_summary_length: usize,
223    /// Use extractive summarization (vs LLM-based)
224    pub use_extractive_summary: bool,
225    /// Adaptive query routing configuration
226    pub adaptive_routing: AdaptiveRoutingConfig,
227}
228
229/// Adaptive query routing configuration
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct AdaptiveRoutingConfig {
232    /// Enable adaptive query routing
233    pub enabled: bool,
234    /// Default level when complexity is unclear
235    pub default_level: usize,
236    /// Weight for keyword-based selection (0.0-1.0)
237    pub keyword_weight: f32,
238    /// Weight for query length-based selection (0.0-1.0)
239    pub length_weight: f32,
240    /// Weight for entity mention-based selection (0.0-1.0)
241    pub entity_weight: f32,
242}
243
244impl Default for AdaptiveRoutingConfig {
245    fn default() -> Self {
246        Self {
247            enabled: true,
248            default_level: 1,
249            keyword_weight: 0.5,
250            length_weight: 0.3,
251            entity_weight: 0.2,
252        }
253    }
254}
255
256#[cfg(feature = "leiden")]
257impl Default for LeidenCommunitiesConfig {
258    fn default() -> Self {
259        Self {
260            enabled: true,
261            max_cluster_size: 10,
262            use_lcc: true,
263            seed: None,
264            resolution: 1.0,
265            max_levels: 5,
266            min_improvement: 0.001,
267            enable_hierarchical: true,
268            generate_summaries: true,
269            max_summary_length: 5,
270            use_extractive_summary: true,
271            adaptive_routing: AdaptiveRoutingConfig::default(),
272        }
273    }
274}
275
276/// Cross-encoder reranking configuration
277#[cfg(feature = "cross-encoder")]
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct CrossEncoderConfig {
280    /// Enable cross-encoder reranking
281    pub enabled: bool,
282    /// Model name/path for cross-encoder
283    pub model_name: String,
284    /// Maximum sequence length
285    pub max_length: usize,
286    /// Batch size for inference
287    pub batch_size: usize,
288    /// Top-k results to return after reranking
289    pub top_k: usize,
290    /// Minimum confidence threshold (0.0-1.0)
291    pub min_confidence: f32,
292    /// Enable score normalization
293    pub normalize_scores: bool,
294}
295
296#[cfg(feature = "cross-encoder")]
297impl Default for CrossEncoderConfig {
298    fn default() -> Self {
299        Self {
300            enabled: true,
301            model_name: "cross-encoder/ms-marco-MiniLM-L-6-v2".to_string(),
302            max_length: 512,
303            batch_size: 32,
304            top_k: 10,
305            min_confidence: 0.0,
306            normalize_scores: true,
307        }
308    }
309}
310
311impl EnhancementsConfig {
312    /// Check if any enhancement is enabled
313    pub fn has_any_enabled(&self) -> bool {
314        self.enabled
315            && (self.query_analysis.enabled
316                || self.adaptive_retrieval.enabled
317                || self.performance_benchmarking.enabled
318                || self.enhanced_function_registry.enabled
319                || {
320                    #[cfg(feature = "lightrag")]
321                    {
322                        self.lightrag.enabled
323                    }
324                    #[cfg(not(feature = "lightrag"))]
325                    {
326                        false
327                    }
328                }
329                || {
330                    #[cfg(feature = "leiden")]
331                    {
332                        self.leiden.enabled
333                    }
334                    #[cfg(not(feature = "leiden"))]
335                    {
336                        false
337                    }
338                }
339                || {
340                    #[cfg(feature = "cross-encoder")]
341                    {
342                        self.cross_encoder.enabled
343                    }
344                    #[cfg(not(feature = "cross-encoder"))]
345                    {
346                        false
347                    }
348                })
349    }
350
351    /// Get enabled enhancements as a list
352    pub fn get_enabled_enhancements(&self) -> Vec<String> {
353        let mut enabled = Vec::new();
354
355        if !self.enabled {
356            return enabled;
357        }
358
359        if self.query_analysis.enabled {
360            enabled.push("Query Analysis".to_string());
361        }
362        if self.adaptive_retrieval.enabled {
363            enabled.push("Adaptive Retrieval".to_string());
364        }
365        if self.performance_benchmarking.enabled {
366            enabled.push("Performance Benchmarking".to_string());
367        }
368        if self.enhanced_function_registry.enabled {
369            enabled.push("Enhanced Function Registry".to_string());
370        }
371        #[cfg(feature = "lightrag")]
372        if self.lightrag.enabled {
373            enabled.push("LightRAG Dual-Level Retrieval".to_string());
374        }
375        #[cfg(feature = "leiden")]
376        if self.leiden.enabled {
377            enabled.push("Leiden Community Detection".to_string());
378        }
379        #[cfg(feature = "cross-encoder")]
380        if self.cross_encoder.enabled {
381            enabled.push("Cross-Encoder Reranking".to_string());
382        }
383
384        enabled
385    }
386
387    /// Disable all enhancements
388    pub fn disable_all(&mut self) {
389        self.enabled = false;
390    }
391
392    /// Enable only specific enhancements
393    pub fn enable_only(&mut self, components: &[&str]) {
394        // First disable all
395        self.query_analysis.enabled = false;
396        self.adaptive_retrieval.enabled = false;
397        self.performance_benchmarking.enabled = false;
398        self.enhanced_function_registry.enabled = false;
399        #[cfg(feature = "lightrag")]
400        {
401            self.lightrag.enabled = false;
402        }
403        #[cfg(feature = "leiden")]
404        {
405            self.leiden.enabled = false;
406        }
407        #[cfg(feature = "cross-encoder")]
408        {
409            self.cross_encoder.enabled = false;
410        }
411
412        // Then enable specified ones
413        for component in components {
414            match component.to_lowercase().as_str() {
415                "query_analysis" | "query" => self.query_analysis.enabled = true,
416                "adaptive_retrieval" | "adaptive" => self.adaptive_retrieval.enabled = true,
417                "performance_benchmarking" | "benchmark" => {
418                    self.performance_benchmarking.enabled = true
419                }
420                "enhanced_function_registry" | "registry" => {
421                    self.enhanced_function_registry.enabled = true
422                }
423                #[cfg(feature = "lightrag")]
424                "lightrag" | "dual_level" => self.lightrag.enabled = true,
425                #[cfg(feature = "leiden")]
426                "leiden" | "communities" => self.leiden.enabled = true,
427                #[cfg(feature = "cross-encoder")]
428                "cross_encoder" | "reranking" => self.cross_encoder.enabled = true,
429                _ => eprintln!("Unknown enhancement component: {component}"),
430            }
431        }
432
433        self.enabled = true;
434    }
435
436    /// Get configuration summary
437    pub fn get_summary(&self) -> EnhancementsSummary {
438        let total = {
439            #[allow(unused_mut)] // mut is needed for cfg blocks
440            let mut count = 4; // Base components
441            #[cfg(feature = "lightrag")]
442            {
443                count += 1;
444            }
445            #[cfg(feature = "leiden")]
446            {
447                count += 1;
448            }
449            #[cfg(feature = "cross-encoder")]
450            {
451                count += 1;
452            }
453            count
454        };
455
456        EnhancementsSummary {
457            master_enabled: self.enabled,
458            total_components: total,
459            enabled_components: self.get_enabled_enhancements().len(),
460            components: vec![
461                ComponentStatus {
462                    name: "Query Analysis".to_string(),
463                    enabled: self.query_analysis.enabled,
464                    features: vec![
465                        (
466                            "Strategy Suggestion".to_string(),
467                            self.query_analysis.enable_strategy_suggestion,
468                        ),
469                        (
470                            "Keyword Analysis".to_string(),
471                            self.query_analysis.enable_keyword_analysis,
472                        ),
473                        (
474                            "Complexity Scoring".to_string(),
475                            self.query_analysis.enable_complexity_scoring,
476                        ),
477                    ],
478                },
479                ComponentStatus {
480                    name: "Adaptive Retrieval".to_string(),
481                    enabled: self.adaptive_retrieval.enabled,
482                    features: vec![
483                        (
484                            "Cross-Strategy Fusion".to_string(),
485                            self.adaptive_retrieval.enable_cross_strategy_fusion,
486                        ),
487                        (
488                            "Diversity Selection".to_string(),
489                            self.adaptive_retrieval.enable_diversity_selection,
490                        ),
491                        (
492                            "Confidence Weighting".to_string(),
493                            self.adaptive_retrieval.enable_confidence_weighting,
494                        ),
495                    ],
496                },
497                ComponentStatus {
498                    name: "Performance Benchmarking".to_string(),
499                    enabled: self.performance_benchmarking.enabled,
500                    features: vec![
501                        (
502                            "Auto Recommendations".to_string(),
503                            self.performance_benchmarking.auto_recommendations,
504                        ),
505                        (
506                            "Comprehensive Testing".to_string(),
507                            self.performance_benchmarking.comprehensive_testing,
508                        ),
509                        (
510                            "Memory Profiling".to_string(),
511                            self.performance_benchmarking.enable_memory_profiling,
512                        ),
513                    ],
514                },
515                ComponentStatus {
516                    name: "Enhanced Function Registry".to_string(),
517                    enabled: self.enhanced_function_registry.enabled,
518                    features: vec![
519                        (
520                            "Categorization".to_string(),
521                            self.enhanced_function_registry.categorization,
522                        ),
523                        (
524                            "Usage Statistics".to_string(),
525                            self.enhanced_function_registry.usage_statistics,
526                        ),
527                        (
528                            "Dynamic Registration".to_string(),
529                            self.enhanced_function_registry.dynamic_registration,
530                        ),
531                    ],
532                },
533                #[cfg(feature = "lightrag")]
534                ComponentStatus {
535                    name: "LightRAG Dual-Level Retrieval".to_string(),
536                    enabled: self.lightrag.enabled,
537                    features: vec![
538                        (
539                            "Dual-Level Keywords".to_string(),
540                            true, // Always enabled when LightRAG is enabled
541                        ),
542                        (
543                            format!("Max Keywords: {}", self.lightrag.max_keywords),
544                            true,
545                        ),
546                        (
547                            format!("Merge: {}", self.lightrag.merge_strategy),
548                            true,
549                        ),
550                    ],
551                },
552                #[cfg(feature = "leiden")]
553                ComponentStatus {
554                    name: "Leiden Community Detection".to_string(),
555                    enabled: self.leiden.enabled,
556                    features: vec![
557                        (
558                            format!("Max Cluster Size: {}", self.leiden.max_cluster_size),
559                            true,
560                        ),
561                        (
562                            format!("Resolution: {:.2}", self.leiden.resolution),
563                            true,
564                        ),
565                        (
566                            "Refinement Phase".to_string(),
567                            true, // Always enabled (KEY difference from Louvain)
568                        ),
569                        (
570                            "Hierarchical Clustering".to_string(),
571                            self.leiden.enable_hierarchical,
572                        ),
573                        (
574                            "Auto-Generate Summaries".to_string(),
575                            self.leiden.generate_summaries,
576                        ),
577                        (
578                            format!("Max Levels: {}", self.leiden.max_levels),
579                            self.leiden.enable_hierarchical,
580                        ),
581                        (
582                            "Adaptive Query Routing".to_string(),
583                            self.leiden.adaptive_routing.enabled,
584                        ),
585                    ],
586                },
587                #[cfg(feature = "cross-encoder")]
588                ComponentStatus {
589                    name: "Cross-Encoder Reranking".to_string(),
590                    enabled: self.cross_encoder.enabled,
591                    features: vec![
592                        (
593                            format!("Model: {}", self.cross_encoder.model_name),
594                            true,
595                        ),
596                        (
597                            format!("Top-K: {}", self.cross_encoder.top_k),
598                            true,
599                        ),
600                        (
601                            "Score Normalization".to_string(),
602                            self.cross_encoder.normalize_scores,
603                        ),
604                    ],
605                },
606            ],
607        }
608    }
609}
610
611/// Summary of enhancements configuration
612#[derive(Debug)]
613pub struct EnhancementsSummary {
614    /// Whether enhancements are enabled at master level
615    pub master_enabled: bool,
616    /// Total number of enhancement components
617    pub total_components: usize,
618    /// Number of enabled enhancement components
619    pub enabled_components: usize,
620    /// Detailed status of each enhancement component
621    pub components: Vec<ComponentStatus>,
622}
623
624/// Status of individual enhancement component
625#[derive(Debug)]
626pub struct ComponentStatus {
627    /// Name of the component
628    pub name: String,
629    /// Whether the component is enabled
630    pub enabled: bool,
631    /// List of features and their enabled status
632    pub features: Vec<(String, bool)>,
633}
634
635impl EnhancementsSummary {
636    /// Print configuration summary
637    pub fn print(&self) {
638        println!("🚀 GraphRAG Enhancements Configuration");
639        println!("{}", "=".repeat(50));
640        println!(
641            "Master Switch: {}",
642            if self.master_enabled {
643                "✅ Enabled"
644            } else {
645                "❌ Disabled"
646            }
647        );
648        println!(
649            "Components: {}/{} enabled",
650            self.enabled_components, self.total_components
651        );
652
653        for component in &self.components {
654            let status = if component.enabled && self.master_enabled {
655                "✅"
656            } else {
657                "❌"
658            };
659            println!("\n{} {}", status, component.name);
660
661            if component.enabled && self.master_enabled {
662                for (feature, enabled) in &component.features {
663                    let feature_status = if *enabled { "  ✓" } else { "  ✗" };
664                    println!("  {feature_status} {feature}");
665                }
666            }
667        }
668    }
669
670    /// Get enabled percentage
671    pub fn get_enabled_percentage(&self) -> f32 {
672        if !self.master_enabled {
673            return 0.0;
674        }
675        (self.enabled_components as f32 / self.total_components as f32) * 100.0
676    }
677}
678
679#[cfg(test)]
680mod tests {
681    use super::*;
682
683    #[test]
684    fn test_default_config() {
685        let config = EnhancementsConfig::default();
686        assert!(config.enabled);
687        assert!(config.query_analysis.enabled);
688        assert!(config.adaptive_retrieval.enabled);
689        assert!(!config.performance_benchmarking.enabled); // Disabled by default
690        assert!(config.enhanced_function_registry.enabled);
691    }
692
693    #[test]
694    fn test_enable_only() {
695        let mut config = EnhancementsConfig::default();
696        config.enable_only(&["query_analysis", "adaptive_retrieval"]);
697
698        assert!(config.query_analysis.enabled);
699        assert!(config.adaptive_retrieval.enabled);
700        assert!(!config.performance_benchmarking.enabled);
701        assert!(!config.enhanced_function_registry.enabled);
702    }
703
704    #[test]
705    fn test_disable_all() {
706        let mut config = EnhancementsConfig::default();
707        config.disable_all();
708
709        assert!(!config.enabled);
710        assert!(!config.has_any_enabled());
711    }
712
713    #[test]
714    fn test_summary() {
715        let config = EnhancementsConfig::default();
716        let summary = config.get_summary();
717
718        let expected = {
719            let mut count = 4;
720            #[cfg(feature = "lightrag")]
721            {
722                count += 1;
723            }
724            #[cfg(feature = "leiden")]
725            {
726                count += 1;
727            }
728            #[cfg(feature = "cross-encoder")]
729            {
730                count += 1;
731            }
732            count
733        };
734        assert_eq!(summary.total_components, expected);
735        assert!(summary.get_enabled_percentage() > 0.0);
736    }
737}