1use crate::error::{MemoryError, Result};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::time::Duration;
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
16pub struct Config {
17 pub database: DatabaseConfig,
19 pub cache: CacheConfig,
21 pub vector: VectorConfig,
23 pub graph: GraphConfig,
25 pub performance: PerformanceConfig,
27 pub learning: LearningConfig,
29 pub retrieval: RetrievalConfig,
31 pub security: SecurityConfig,
33 pub logging: LoggingConfig,
35 pub features: FeatureFlags,
37 pub custom: HashMap<String, serde_json::Value>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
43pub struct DatabaseConfig {
44 pub database_type: DatabaseType,
46 pub url: String,
48 pub max_connections: u32,
50 pub min_connections: u32,
52 pub connect_timeout: u64,
54 pub idle_timeout: u64,
56 pub max_lifetime: u64,
58 pub enable_ssl: bool,
60 pub ssl_cert_path: Option<PathBuf>,
62 pub database_name: String,
64 pub table_prefix: String,
66 pub auto_migrate: bool,
68 pub backup: BackupConfig,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
74pub enum DatabaseType {
75 SQLite,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
80pub struct BackupConfig {
81 pub enabled: bool,
83 pub interval_hours: u64,
85 pub retention_days: u64,
87 pub backup_path: PathBuf,
89 pub compress: bool,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95pub struct CacheConfig {
96 pub enabled: bool,
98 pub cache_type: CacheType,
100 pub max_size_mb: u64,
102 pub ttl_seconds: u64,
104 pub lru_capacity: usize,
106 pub redis: Option<RedisConfig>,
108 pub warmup: WarmupConfig,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
114pub enum CacheType {
115 Memory,
116 Redis,
117 Hybrid,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
122pub struct RedisConfig {
123 pub url: String,
125 pub pool_size: u32,
127 pub connect_timeout_ms: u64,
129 pub command_timeout_ms: u64,
131 pub password: Option<String>,
133 pub database: u8,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
139pub struct WarmupConfig {
140 pub enabled: bool,
142 pub memory_count: usize,
144 pub strategy: WarmupStrategy,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
150pub enum WarmupStrategy {
151 RecentlyAccessed,
153 HighImportance,
155 HighFrequency,
157 Mixed,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
163pub struct VectorConfig {
164 pub dimension: usize,
166 pub similarity_threshold: f32,
168 pub index_type: VectorIndexType,
170 pub distance_metric: DistanceMetric,
172 pub index_params: IndexParams,
174 pub search_params: SearchParams,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
180pub enum VectorIndexType {
181 Flat,
183 IVF,
185 HNSW,
187 LSH,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
193pub enum DistanceMetric {
194 Cosine,
196 Euclidean,
198 Manhattan,
200 DotProduct,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
206pub struct IndexParams {
207 pub nlist: Option<usize>,
209 pub m: Option<usize>,
211 pub ef_construction: Option<usize>,
213 pub num_tables: Option<usize>,
215 pub num_hash_funcs: Option<usize>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
221pub struct SearchParams {
222 pub nprobe: Option<usize>,
224 pub ef_search: Option<usize>,
226 pub max_results: usize,
228 pub timeout_ms: u64,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
234pub struct GraphConfig {
235 pub max_connections_per_node: usize,
237 pub connection_threshold: f32,
239 pub pagerank: PageRankConfig,
241 pub traversal: TraversalConfig,
243 pub compression: CompressionConfig,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
249pub struct PageRankConfig {
250 pub damping_factor: f32,
252 pub max_iterations: usize,
254 pub convergence_threshold: f32,
256 pub personalization_weight: f32,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
262pub struct TraversalConfig {
263 pub max_depth: usize,
265 pub max_visited_nodes: usize,
267 pub timeout_ms: u64,
269 pub cycle_detection: bool,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
275pub struct CompressionConfig {
276 pub enabled: bool,
278 pub weak_connection_threshold: f32,
280 pub compression_interval_hours: u64,
282 pub min_connections_to_keep: usize,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
288pub struct PerformanceConfig {
289 pub worker_threads: usize,
291 pub batch_size: usize,
293 pub query_timeout_ms: u64,
295 pub creation_timeout_ms: u64,
297 pub concurrency_limit: usize,
299 pub memory_limit_mb: u64,
301 pub enable_metrics: bool,
303 pub metrics_interval_seconds: u64,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
309pub struct RetrievalConfig {
310 pub fusion_weights: FusionWeights,
312 pub max_results: usize,
314 pub timeout_ms: u64,
316 pub enable_cache: bool,
318 pub cache_ttl_seconds: u64,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
324pub struct FusionWeights {
325 pub semantic_weight: f32,
327 pub temporal_weight: f32,
329 pub structural_weight: f32,
331 pub importance_weight: f32,
333 pub personalization_weight: f32,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
339pub struct LearningConfig {
340 pub enabled: bool,
342 pub learning_rate: f32,
344 pub importance_decay_factor: f32,
346 pub connection_decay_factor: f32,
348 pub learning_interval_hours: u64,
350 pub min_learning_samples: usize,
352 pub max_interaction_history: usize,
354 pub personalization: PersonalizationConfig,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
360pub struct PersonalizationConfig {
361 pub enabled: bool,
363 pub user_behavior_weight: f32,
365 pub temporal_decay_weight: f32,
367 pub feedback_weight: f32,
369 pub update_interval_hours: u64,
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
375pub struct SecurityConfig {
376 pub enable_auth: bool,
378 pub jwt_secret: Option<String>,
380 pub token_expiry_hours: u64,
382 pub enable_encryption: bool,
384 pub encryption_key: Option<String>,
386 pub access_control: AccessControlConfig,
388 pub audit: AuditConfig,
390}
391
392#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
394pub struct AccessControlConfig {
395 pub enabled: bool,
397 pub default_permissions: Vec<String>,
399 pub admin_users: Vec<String>,
401 pub readonly_users: Vec<String>,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
407pub struct AuditConfig {
408 pub enabled: bool,
410 pub log_path: PathBuf,
412 pub event_types: Vec<String>,
414 pub retention_days: u64,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
420pub struct LoggingConfig {
421 pub level: LogLevel,
423 pub format: LogFormat,
425 pub targets: Vec<LogTarget>,
427 pub file_path: Option<PathBuf>,
429 pub max_file_size_mb: u64,
431 pub max_files: u32,
433 pub structured: bool,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
439pub enum LogLevel {
440 Error,
441 Warn,
442 Info,
443 Debug,
444 Trace,
445}
446
447#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
449pub enum LogFormat {
450 Plain,
451 Json,
452 Compact,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
457pub enum LogTarget {
458 Console,
459 File,
460 Syslog,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
465pub struct FeatureFlags {
466 pub experimental_features: bool,
468 pub debug_mode: bool,
470 pub profiling: bool,
472 pub ab_testing: bool,
474 pub custom_flags: HashMap<String, bool>,
476}
477
478#[derive(Debug, Default)]
480pub struct ConfigBuilder {
481 config: Config,
482}
483
484impl ConfigBuilder {
485 pub fn new() -> Self {
487 Self {
488 config: Config::default(),
489 }
490 }
491
492 pub fn database(mut self, database: DatabaseConfig) -> Self {
494 self.config.database = database;
495 self
496 }
497
498 pub fn cache(mut self, cache: CacheConfig) -> Self {
500 self.config.cache = cache;
501 self
502 }
503
504 pub fn vector(mut self, vector: VectorConfig) -> Self {
506 self.config.vector = vector;
507 self
508 }
509
510 pub fn graph(mut self, graph: GraphConfig) -> Self {
512 self.config.graph = graph;
513 self
514 }
515
516 pub fn performance(mut self, performance: PerformanceConfig) -> Self {
518 self.config.performance = performance;
519 self
520 }
521
522 pub fn learning(mut self, learning: LearningConfig) -> Self {
524 self.config.learning = learning;
525 self
526 }
527
528 pub fn security(mut self, security: SecurityConfig) -> Self {
530 self.config.security = security;
531 self
532 }
533
534 pub fn logging(mut self, logging: LoggingConfig) -> Self {
536 self.config.logging = logging;
537 self
538 }
539
540 pub fn features(mut self, features: FeatureFlags) -> Self {
542 self.config.features = features;
543 self
544 }
545
546 pub fn build(self) -> Config {
548 self.config
549 }
550}
551
552impl Config {
553 pub fn from_env() -> Result<Self> {
555 let mut config = Self::default();
556
557 if let Ok(url) = std::env::var("AI00_MEM_DATABASE_URL") {
559 config.database.url = url;
560 }
561
562 if let Ok(db_type) = std::env::var("AI00_MEM_DATABASE_TYPE") {
564 config.database.database_type = match db_type.to_lowercase().as_str() {
565 "sqlite" => DatabaseType::SQLite,
566 _ => {
567 return Err(MemoryError::validation_error(format!(
568 "Unsupported database type: {}",
569 db_type
570 )))
571 }
572 };
573 }
574
575 if let Ok(cache_enabled) = std::env::var("AI00_MEM_CACHE_ENABLED") {
577 config.cache.enabled = cache_enabled.parse().unwrap_or(true);
578 }
579
580 if let Ok(cache_size) = std::env::var("AI00_MEM_CACHE_SIZE_MB") {
581 config.cache.max_size_mb = cache_size.parse().unwrap_or(256);
582 }
583
584 if let Ok(dimension) = std::env::var("AI00_MEM_VECTOR_DIMENSION") {
586 config.vector.dimension = dimension.parse().unwrap_or(256);
587 }
588
589 if let Ok(workers) = std::env::var("AI00_MEM_WORKER_THREADS") {
591 config.performance.worker_threads = workers.parse().unwrap_or(4);
592 }
593
594 if let Ok(log_level) = std::env::var("AI00_MEM_LOG_LEVEL") {
596 config.logging.level = match log_level.to_lowercase().as_str() {
597 "error" => LogLevel::Error,
598 "warn" => LogLevel::Warn,
599 "info" => LogLevel::Info,
600 "debug" => LogLevel::Debug,
601 "trace" => LogLevel::Trace,
602 _ => LogLevel::Info,
603 };
604 }
605
606 Ok(config)
607 }
608
609 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
611 let content = std::fs::read_to_string(path)
612 .map_err(|e| MemoryError::validation_error(e.to_string()))?;
613
614 let config: Config = toml::from_str(&content)
615 .or_else(|_| serde_json::from_str(&content))
616 .or_else(|_| serde_yaml::from_str(&content))
617 .map_err(|e| {
618 MemoryError::validation_error(format!("Failed to parse configuration file: {}", e))
619 })?;
620
621 Ok(config)
622 }
623
624 pub fn validate(&self) -> Result<()> {
626 if self.database.url.is_empty() {
628 return Err(MemoryError::validation_error(
629 "Database URL cannot be empty".to_string(),
630 ));
631 }
632
633 if self.database.max_connections == 0 {
634 return Err(MemoryError::validation_error(
635 "Max connections must be greater than 0".to_string(),
636 ));
637 }
638
639 if self.database.min_connections > self.database.max_connections {
640 return Err(MemoryError::validation_error(
641 "Min connections cannot be greater than max connections".to_string(),
642 ));
643 }
644
645 if self.vector.dimension == 0 {
647 return Err(MemoryError::validation_error(
648 "Vector dimension must be greater than 0".to_string(),
649 ));
650 }
651
652 if !(0.0..=1.0).contains(&self.vector.similarity_threshold) {
653 return Err(MemoryError::validation_error(
654 "Similarity threshold must be between 0.0 and 1.0".to_string(),
655 ));
656 }
657
658 if self.graph.max_connections_per_node == 0 {
660 return Err(MemoryError::validation_error(
661 "Max connections per node must be greater than 0".to_string(),
662 ));
663 }
664
665 if !(0.0..=1.0).contains(&self.graph.connection_threshold) {
666 return Err(MemoryError::validation_error(
667 "Connection threshold must be between 0.0 and 1.0".to_string(),
668 ));
669 }
670
671 if !(0.0..=1.0).contains(&self.graph.pagerank.damping_factor) {
673 return Err(MemoryError::validation_error(
674 "PageRank damping factor must be between 0.0 and 1.0".to_string(),
675 ));
676 }
677
678 if self.performance.worker_threads == 0 {
680 return Err(MemoryError::validation_error(
681 "Worker threads must be greater than 0".to_string(),
682 ));
683 }
684
685 if self.performance.batch_size == 0 {
686 return Err(MemoryError::validation_error(
687 "Batch size must be greater than 0".to_string(),
688 ));
689 }
690
691 Ok(())
692 }
693
694 pub fn database_connect_timeout(&self) -> Duration {
696 Duration::from_secs(self.database.connect_timeout)
697 }
698
699 pub fn query_timeout(&self) -> Duration {
701 Duration::from_millis(self.performance.query_timeout_ms)
702 }
703
704 pub fn cache_ttl(&self) -> Duration {
706 Duration::from_secs(self.cache.ttl_seconds)
707 }
708}
709
710impl Default for DatabaseConfig {
713 fn default() -> Self {
714 Self {
715 database_type: DatabaseType::SQLite,
716 url: "sqlite://ai00_mem.db".to_string(),
717 max_connections: 10,
718 min_connections: 1,
719 connect_timeout: 30,
720 idle_timeout: 600,
721 max_lifetime: 3600,
722 enable_ssl: false,
723 ssl_cert_path: None,
724 database_name: "ai00_mem".to_string(),
725 table_prefix: "ai00_".to_string(),
726 auto_migrate: true,
727 backup: BackupConfig::default(),
728 }
729 }
730}
731
732impl Default for BackupConfig {
733 fn default() -> Self {
734 Self {
735 enabled: false,
736 interval_hours: 24,
737 retention_days: 30,
738 backup_path: PathBuf::from("./backups"),
739 compress: true,
740 }
741 }
742}
743
744impl Default for CacheConfig {
745 fn default() -> Self {
746 Self {
747 enabled: true,
748 cache_type: CacheType::Memory,
749 max_size_mb: 256,
750 ttl_seconds: 3600,
751 lru_capacity: 10000,
752 redis: None,
753 warmup: WarmupConfig::default(),
754 }
755 }
756}
757
758impl Default for WarmupConfig {
759 fn default() -> Self {
760 Self {
761 enabled: true,
762 memory_count: 1000,
763 strategy: WarmupStrategy::Mixed,
764 }
765 }
766}
767
768impl Default for VectorConfig {
769 fn default() -> Self {
770 Self {
771 dimension: 256, similarity_threshold: 0.7,
773 index_type: VectorIndexType::Flat,
774 distance_metric: DistanceMetric::Cosine,
775 index_params: IndexParams::default(),
776 search_params: SearchParams::default(),
777 }
778 }
779}
780
781impl Default for IndexParams {
782 fn default() -> Self {
783 Self {
784 nlist: Some(100),
785 m: Some(16),
786 ef_construction: Some(200),
787 num_tables: Some(10),
788 num_hash_funcs: Some(4),
789 }
790 }
791}
792
793impl Default for SearchParams {
794 fn default() -> Self {
795 Self {
796 nprobe: Some(10),
797 ef_search: Some(50),
798 max_results: 100,
799 timeout_ms: 5000,
800 }
801 }
802}
803
804impl Default for GraphConfig {
805 fn default() -> Self {
806 Self {
807 max_connections_per_node: 50,
808 connection_threshold: 0.5,
809 pagerank: PageRankConfig::default(),
810 traversal: TraversalConfig::default(),
811 compression: CompressionConfig::default(),
812 }
813 }
814}
815
816impl Default for PageRankConfig {
817 fn default() -> Self {
818 Self {
819 damping_factor: 0.85,
820 max_iterations: 100,
821 convergence_threshold: 1e-6,
822 personalization_weight: 0.15,
823 }
824 }
825}
826
827impl Default for TraversalConfig {
828 fn default() -> Self {
829 Self {
830 max_depth: 5,
831 max_visited_nodes: 1000,
832 timeout_ms: 10000,
833 cycle_detection: true,
834 }
835 }
836}
837
838impl Default for CompressionConfig {
839 fn default() -> Self {
840 Self {
841 enabled: true,
842 weak_connection_threshold: 0.1,
843 compression_interval_hours: 24,
844 min_connections_to_keep: 5,
845 }
846 }
847}
848
849impl Default for PerformanceConfig {
850 fn default() -> Self {
851 Self {
852 worker_threads: num_cpus::get(),
853 batch_size: 100,
854 query_timeout_ms: 5000,
855 creation_timeout_ms: 10000,
856 concurrency_limit: 100,
857 memory_limit_mb: 1024,
858 enable_metrics: true,
859 metrics_interval_seconds: 60,
860 }
861 }
862}
863
864impl Default for LearningConfig {
865 fn default() -> Self {
866 Self {
867 enabled: true,
868 learning_rate: 0.01,
869 importance_decay_factor: 0.95,
870 connection_decay_factor: 0.98,
871 learning_interval_hours: 1,
872 min_learning_samples: 10,
873 max_interaction_history: 1000,
874 personalization: PersonalizationConfig::default(),
875 }
876 }
877}
878
879impl Default for RetrievalConfig {
880 fn default() -> Self {
881 Self {
882 fusion_weights: FusionWeights::default(),
883 max_results: 100,
884 timeout_ms: 5000,
885 enable_cache: true,
886 cache_ttl_seconds: 3600,
887 }
888 }
889}
890
891impl Default for FusionWeights {
892 fn default() -> Self {
893 Self {
894 semantic_weight: 0.4,
895 temporal_weight: 0.2,
896 structural_weight: 0.2,
897 importance_weight: 0.1,
898 personalization_weight: 0.1,
899 }
900 }
901}
902
903impl Default for PersonalizationConfig {
904 fn default() -> Self {
905 Self {
906 enabled: true,
907 user_behavior_weight: 0.4,
908 temporal_decay_weight: 0.3,
909 feedback_weight: 0.3,
910 update_interval_hours: 6,
911 }
912 }
913}
914
915impl Default for SecurityConfig {
916 fn default() -> Self {
917 Self {
918 enable_auth: false,
919 jwt_secret: None,
920 token_expiry_hours: 24,
921 enable_encryption: false,
922 encryption_key: None,
923 access_control: AccessControlConfig::default(),
924 audit: AuditConfig::default(),
925 }
926 }
927}
928
929impl Default for AccessControlConfig {
930 fn default() -> Self {
931 Self {
932 enabled: false,
933 default_permissions: vec!["read".to_string(), "write".to_string()],
934 admin_users: Vec::new(),
935 readonly_users: Vec::new(),
936 }
937 }
938}
939
940impl Default for AuditConfig {
941 fn default() -> Self {
942 Self {
943 enabled: false,
944 log_path: PathBuf::from("./logs/audit.log"),
945 event_types: vec![
946 "create".to_string(),
947 "update".to_string(),
948 "delete".to_string(),
949 "query".to_string(),
950 ],
951 retention_days: 90,
952 }
953 }
954}
955
956impl Default for LoggingConfig {
957 fn default() -> Self {
958 Self {
959 level: LogLevel::Info,
960 format: LogFormat::Plain,
961 targets: vec![LogTarget::Console],
962 file_path: None,
963 max_file_size_mb: 100,
964 max_files: 10,
965 structured: false,
966 }
967 }
968}
969
970#[cfg(test)]
971mod tests {
972 use super::*;
973 use std::io::Write;
974 use tempfile::NamedTempFile;
975
976 #[test]
977 fn test_default_config() {
978 let config = Config::default();
979 assert!(config.validate().is_ok());
980 assert_eq!(config.database.database_type, DatabaseType::SQLite);
981 assert_eq!(config.vector.dimension, 256);
982 assert!(config.cache.enabled);
983 }
984
985 #[test]
986 fn test_config_builder() {
987 let config = ConfigBuilder::new()
988 .database(DatabaseConfig {
989 database_type: DatabaseType::SQLite,
990 url: "sqlite://test.db".to_string(),
991 ..Default::default()
992 })
993 .vector(VectorConfig {
994 dimension: 1024,
995 ..Default::default()
996 })
997 .build();
998
999 assert_eq!(config.database.database_type, DatabaseType::SQLite);
1000 assert_eq!(config.vector.dimension, 1024);
1001 }
1002
1003 #[test]
1004 fn test_config_validation() {
1005 let mut config = Config::default();
1006
1007 config.database.url = String::new();
1009 assert!(config.validate().is_err());
1010
1011 config.database.url = "sqlite://test.db".to_string();
1013 config.vector.dimension = 0;
1014 assert!(config.validate().is_err());
1015
1016 config.vector.dimension = 256;
1018 assert!(config.validate().is_ok());
1019 }
1020
1021 #[test]
1022 fn test_config_from_env() {
1023 std::env::set_var("AI00_MEM_DATABASE_URL", "postgresql://localhost/test");
1024 std::env::set_var("AI00_MEM_DATABASE_TYPE", "sqlite");
1025 std::env::set_var("AI00_MEM_VECTOR_DIMENSION", "1024");
1026
1027 let config = Config::from_env().unwrap();
1028 assert_eq!(config.database.url, "postgresql://localhost/test");
1029 assert_eq!(config.database.database_type, DatabaseType::SQLite);
1030 assert_eq!(config.vector.dimension, 1024);
1031
1032 std::env::remove_var("AI00_MEM_DATABASE_URL");
1034 std::env::remove_var("AI00_MEM_DATABASE_TYPE");
1035 std::env::remove_var("AI00_MEM_VECTOR_DIMENSION");
1036 }
1037
1038 #[test]
1039 fn test_config_from_file() {
1040 let config_content = r#"
1041[database]
1042database_type = "SQLite"
1043url = "sqlite://test.db"
1044max_connections = 20
1045min_connections = 1
1046connect_timeout = 30
1047idle_timeout = 600
1048max_lifetime = 3600
1049enable_ssl = false
1050database_name = "test_db"
1051table_prefix = "test_"
1052auto_migrate = true
1053
1054[database.backup]
1055enabled = false
1056interval_hours = 24
1057retention_days = 30
1058backup_path = "./backups"
1059compress = true
1060
1061[vector]
1062dimension = 1024
1063similarity_threshold = 0.8
1064index_type = "Flat"
1065distance_metric = "Cosine"
1066
1067[vector.index_params]
1068
1069[vector.search_params]
1070ef_search = 50
1071max_results = 100
1072timeout_ms = 5000
1073
1074[cache]
1075enabled = true
1076cache_type = "Memory"
1077max_size_mb = 512
1078ttl_seconds = 3600
1079lru_capacity = 10000
1080
1081[cache.warmup]
1082enabled = true
1083memory_count = 1000
1084strategy = "Mixed"
1085
1086[graph]
1087max_connections_per_node = 50
1088connection_threshold = 0.5
1089
1090[graph.pagerank]
1091damping_factor = 0.85
1092max_iterations = 100
1093convergence_threshold = 0.0001
1094personalization_weight = 0.1
1095
1096[graph.traversal]
1097max_depth = 5
1098max_visited_nodes = 1000
1099timeout_ms = 5000
1100cycle_detection = true
1101
1102[graph.compression]
1103enabled = true
1104weak_connection_threshold = 0.1
1105compression_interval_hours = 24
1106min_connections_to_keep = 5
1107
1108[performance]
1109worker_threads = 4
1110batch_size = 100
1111query_timeout_ms = 5000
1112creation_timeout_ms = 10000
1113concurrency_limit = 100
1114memory_limit_mb = 1024
1115enable_metrics = true
1116metrics_interval_seconds = 60
1117
1118[learning]
1119enabled = true
1120learning_rate = 0.01
1121importance_decay_factor = 0.95
1122connection_decay_factor = 0.9
1123learning_interval_hours = 6
1124min_learning_samples = 10
1125max_interaction_history = 1000
1126
1127[learning.personalization]
1128enabled = true
1129user_behavior_weight = 0.4
1130temporal_decay_weight = 0.3
1131feedback_weight = 0.3
1132update_interval_hours = 6
1133
1134[retrieval]
1135max_results = 100
1136timeout_ms = 5000
1137enable_cache = true
1138cache_ttl_seconds = 3600
1139
1140[retrieval.fusion_weights]
1141semantic_weight = 0.4
1142temporal_weight = 0.2
1143structural_weight = 0.2
1144importance_weight = 0.1
1145personalization_weight = 0.1
1146
1147[security]
1148enable_auth = false
1149token_expiry_hours = 24
1150enable_encryption = false
1151
1152[security.access_control]
1153enabled = false
1154default_permissions = ["read", "write"]
1155admin_users = []
1156readonly_users = []
1157
1158[security.audit]
1159enabled = false
1160log_path = "./logs/audit.log"
1161event_types = ["create", "update", "delete", "query"]
1162retention_days = 90
1163
1164[logging]
1165level = "Info"
1166format = "Plain"
1167targets = ["Console"]
1168max_file_size_mb = 100
1169max_files = 10
1170structured = false
1171
1172[features]
1173experimental_features = false
1174debug_mode = false
1175profiling = false
1176ab_testing = false
1177
1178[features.custom_flags]
1179
1180[custom]
1181"#;
1182
1183 let mut temp_file = NamedTempFile::new().unwrap();
1184 temp_file.write_all(config_content.as_bytes()).unwrap();
1185
1186 let config = Config::from_file(temp_file.path()).unwrap();
1187 assert_eq!(config.database.database_type, DatabaseType::SQLite);
1188 assert_eq!(config.database.max_connections, 20);
1189 assert_eq!(config.vector.dimension, 1024);
1190 assert_eq!(config.cache.max_size_mb, 512);
1191 }
1192
1193 #[test]
1194 fn test_timeout_conversions() {
1195 let config = Config::default();
1196
1197 let db_timeout = config.database_connect_timeout();
1198 assert_eq!(db_timeout, Duration::from_secs(30));
1199
1200 let query_timeout = config.query_timeout();
1201 assert_eq!(query_timeout, Duration::from_millis(5000));
1202
1203 let cache_ttl = config.cache_ttl();
1204 assert_eq!(cache_ttl, Duration::from_secs(3600));
1205 }
1206}