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