code_mesh_core/
config.rs

1//! Configuration management for Code Mesh Core
2//!
3//! This module provides a comprehensive configuration system that supports:
4//! - Multiple configuration sources (files, environment, programmatic)
5//! - Provider-specific configurations
6//! - Tool configurations and permissions
7//! - Runtime feature detection
8//! - Configuration validation and defaults
9
10use crate::{Error, Result};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::path::PathBuf;
14
15/// Main configuration structure
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Config {
18    /// General application settings
19    pub app: AppConfig,
20    
21    /// Provider configurations
22    pub providers: HashMap<String, ProviderConfig>,
23    
24    /// Tool configurations
25    pub tools: ToolConfig,
26    
27    /// Storage configuration
28    pub storage: StorageConfig,
29    
30    /// Authentication configuration
31    pub auth: AuthConfig,
32    
33    /// Session configuration
34    pub session: SessionConfig,
35    
36    /// Memory configuration
37    pub memory: MemoryConfig,
38    
39    /// Agent configuration
40    pub agent: AgentConfig,
41}
42
43impl Default for Config {
44    fn default() -> Self {
45        Self {
46            app: AppConfig::default(),
47            providers: HashMap::new(),
48            tools: ToolConfig::default(),
49            storage: StorageConfig::default(),
50            auth: AuthConfig::default(),
51            session: SessionConfig::default(),
52            memory: MemoryConfig::default(),
53            agent: AgentConfig::default(),
54        }
55    }
56}
57
58/// Application-level configuration
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct AppConfig {
61    /// Application name
62    pub name: String,
63    
64    /// Version information
65    pub version: String,
66    
67    /// Data directory path
68    pub data_dir: Option<PathBuf>,
69    
70    /// Configuration directory path
71    pub config_dir: Option<PathBuf>,
72    
73    /// Log level
74    pub log_level: String,
75    
76    /// Feature flags
77    pub features: FeatureConfig,
78}
79
80impl Default for AppConfig {
81    fn default() -> Self {
82        Self {
83            name: "code-mesh".to_string(),
84            version: crate::VERSION.to_string(),
85            data_dir: None,
86            config_dir: None,
87            log_level: "info".to_string(),
88            features: FeatureConfig::default(),
89        }
90    }
91}
92
93/// Feature configuration flags
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct FeatureConfig {
96    /// Enable compression for storage
97    pub compression: bool,
98    
99    /// Enable file watching
100    pub file_watching: bool,
101    
102    /// Enable advanced cryptography
103    pub advanced_crypto: bool,
104    
105    /// Enable telemetry
106    pub telemetry: bool,
107    
108    /// Enable experimental features
109    pub experimental: bool,
110}
111
112impl Default for FeatureConfig {
113    fn default() -> Self {
114        Self {
115            compression: crate::features::HAS_COMPRESSION,
116            file_watching: crate::features::HAS_FILE_WATCHING,
117            advanced_crypto: crate::features::HAS_ADVANCED_CRYPTO,
118            telemetry: false,
119            experimental: false,
120        }
121    }
122}
123
124/// Provider configuration
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ProviderConfig {
127    /// Provider ID
128    pub id: String,
129    
130    /// Provider name
131    pub name: String,
132    
133    /// Base URL for API
134    pub base_url: Option<String>,
135    
136    /// API version
137    pub api_version: Option<String>,
138    
139    /// Default model to use
140    pub default_model: Option<String>,
141    
142    /// Model configurations
143    pub models: HashMap<String, ModelConfig>,
144    
145    /// Provider-specific options
146    pub options: HashMap<String, serde_json::Value>,
147    
148    /// Rate limiting configuration
149    pub rate_limit: RateLimitConfig,
150    
151    /// Timeout configuration
152    pub timeout: TimeoutConfig,
153    
154    /// Retry configuration
155    pub retry: RetryConfig,
156    
157    /// Whether this provider is enabled
158    pub enabled: bool,
159}
160
161impl Default for ProviderConfig {
162    fn default() -> Self {
163        Self {
164            id: String::new(),
165            name: String::new(),
166            base_url: None,
167            api_version: None,
168            default_model: None,
169            models: HashMap::new(),
170            options: HashMap::new(),
171            rate_limit: RateLimitConfig::default(),
172            timeout: TimeoutConfig::default(),
173            retry: RetryConfig::default(),
174            enabled: true,
175        }
176    }
177}
178
179/// Model configuration
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ModelConfig {
182    /// Model ID
183    pub id: String,
184    
185    /// Model name
186    pub name: String,
187    
188    /// Maximum context length
189    pub max_context: Option<u32>,
190    
191    /// Maximum output tokens
192    pub max_output: Option<u32>,
193    
194    /// Default temperature
195    pub temperature: Option<f32>,
196    
197    /// Supports tool calling
198    pub supports_tools: bool,
199    
200    /// Supports vision/images
201    pub supports_vision: bool,
202    
203    /// Supports streaming
204    pub supports_streaming: bool,
205    
206    /// Supports caching
207    pub supports_caching: bool,
208    
209    /// Cost information
210    pub cost: CostConfig,
211    
212    /// Model-specific options
213    pub options: HashMap<String, serde_json::Value>,
214}
215
216impl Default for ModelConfig {
217    fn default() -> Self {
218        Self {
219            id: String::new(),
220            name: String::new(),
221            max_context: None,
222            max_output: None,
223            temperature: None,
224            supports_tools: true,
225            supports_vision: false,
226            supports_streaming: true,
227            supports_caching: false,
228            cost: CostConfig::default(),
229            options: HashMap::new(),
230        }
231    }
232}
233
234/// Cost configuration for models
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct CostConfig {
237    /// Input tokens cost per 1K tokens
238    pub input_cost_per_1k: f64,
239    
240    /// Output tokens cost per 1K tokens
241    pub output_cost_per_1k: f64,
242    
243    /// Cache read cost per 1K tokens
244    pub cache_read_cost_per_1k: Option<f64>,
245    
246    /// Cache write cost per 1K tokens
247    pub cache_write_cost_per_1k: Option<f64>,
248    
249    /// Currency for costs
250    pub currency: String,
251}
252
253impl Default for CostConfig {
254    fn default() -> Self {
255        Self {
256            input_cost_per_1k: 0.0,
257            output_cost_per_1k: 0.0,
258            cache_read_cost_per_1k: None,
259            cache_write_cost_per_1k: None,
260            currency: "USD".to_string(),
261        }
262    }
263}
264
265/// Rate limiting configuration
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct RateLimitConfig {
268    /// Requests per minute
269    pub requests_per_minute: Option<u32>,
270    
271    /// Tokens per minute
272    pub tokens_per_minute: Option<u32>,
273    
274    /// Concurrent requests
275    pub concurrent_requests: Option<u32>,
276}
277
278impl Default for RateLimitConfig {
279    fn default() -> Self {
280        Self {
281            requests_per_minute: None,
282            tokens_per_minute: None,
283            concurrent_requests: Some(4),
284        }
285    }
286}
287
288/// Timeout configuration
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct TimeoutConfig {
291    /// Connect timeout in seconds
292    pub connect: u64,
293    
294    /// Request timeout in seconds
295    pub request: u64,
296    
297    /// Stream timeout in seconds
298    pub stream: u64,
299}
300
301impl Default for TimeoutConfig {
302    fn default() -> Self {
303        Self {
304            connect: 10,
305            request: 300,
306            stream: 600,
307        }
308    }
309}
310
311/// Retry configuration
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct RetryConfig {
314    /// Maximum number of retries
315    pub max_retries: u32,
316    
317    /// Base delay in milliseconds
318    pub base_delay: u64,
319    
320    /// Maximum delay in milliseconds
321    pub max_delay: u64,
322    
323    /// Exponential backoff multiplier
324    pub backoff_multiplier: f64,
325}
326
327impl Default for RetryConfig {
328    fn default() -> Self {
329        Self {
330            max_retries: 3,
331            base_delay: 1000,
332            max_delay: 60000,
333            backoff_multiplier: 2.0,
334        }
335    }
336}
337
338/// Tool system configuration
339#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct ToolConfig {
341    /// Enabled tools
342    pub enabled: Vec<String>,
343    
344    /// Disabled tools
345    pub disabled: Vec<String>,
346    
347    /// Tool-specific configurations
348    pub tool_configs: HashMap<String, ToolSpecificConfig>,
349    
350    /// Default permission level
351    pub default_permission: String,
352    
353    /// Sandbox configuration
354    pub sandbox: SandboxConfig,
355}
356
357impl Default for ToolConfig {
358    fn default() -> Self {
359        Self {
360            enabled: vec!["read".to_string(), "write".to_string(), "edit".to_string()],
361            disabled: Vec::new(),
362            tool_configs: HashMap::new(),
363            default_permission: "restricted".to_string(),
364            sandbox: SandboxConfig::default(),
365        }
366    }
367}
368
369/// Tool-specific configuration
370#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct ToolSpecificConfig {
372    /// Permission level for this tool
373    pub permission: String,
374    
375    /// Allowed file patterns
376    pub allowed_patterns: Vec<String>,
377    
378    /// Denied file patterns
379    pub denied_patterns: Vec<String>,
380    
381    /// Maximum file size in bytes
382    pub max_file_size: Option<u64>,
383    
384    /// Tool-specific options
385    pub options: HashMap<String, serde_json::Value>,
386}
387
388impl Default for ToolSpecificConfig {
389    fn default() -> Self {
390        Self {
391            permission: "restricted".to_string(),
392            allowed_patterns: Vec::new(),
393            denied_patterns: Vec::new(),
394            max_file_size: None,
395            options: HashMap::new(),
396        }
397    }
398}
399
400/// Sandbox configuration for tool execution
401#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct SandboxConfig {
403    /// Enable sandboxing
404    pub enabled: bool,
405    
406    /// Allowed directories
407    pub allowed_dirs: Vec<PathBuf>,
408    
409    /// Denied directories
410    pub denied_dirs: Vec<PathBuf>,
411    
412    /// Network access allowed
413    pub network_access: bool,
414    
415    /// Maximum execution time in seconds
416    pub max_execution_time: u64,
417    
418    /// Maximum memory usage in MB
419    pub max_memory_mb: Option<u64>,
420}
421
422impl Default for SandboxConfig {
423    fn default() -> Self {
424        Self {
425            enabled: true,
426            allowed_dirs: Vec::new(),
427            denied_dirs: Vec::new(),
428            network_access: false,
429            max_execution_time: 30,
430            max_memory_mb: Some(512),
431        }
432    }
433}
434
435/// Storage configuration
436#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct StorageConfig {
438    /// Storage backend type
439    pub backend: String,
440    
441    /// Connection string or path
442    pub connection: String,
443    
444    /// Enable compression
445    pub compression: bool,
446    
447    /// Encryption configuration
448    pub encryption: EncryptionConfig,
449    
450    /// Backup configuration
451    pub backup: BackupConfig,
452}
453
454impl Default for StorageConfig {
455    fn default() -> Self {
456        Self {
457            backend: "file".to_string(),
458            connection: "data/storage".to_string(),
459            compression: crate::features::HAS_COMPRESSION,
460            encryption: EncryptionConfig::default(),
461            backup: BackupConfig::default(),
462        }
463    }
464}
465
466/// Encryption configuration
467#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct EncryptionConfig {
469    /// Enable encryption
470    pub enabled: bool,
471    
472    /// Encryption algorithm
473    pub algorithm: String,
474    
475    /// Key derivation method
476    pub key_derivation: String,
477}
478
479impl Default for EncryptionConfig {
480    fn default() -> Self {
481        Self {
482            enabled: false,
483            algorithm: "aes-256-gcm".to_string(),
484            key_derivation: "pbkdf2".to_string(),
485        }
486    }
487}
488
489/// Backup configuration
490#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct BackupConfig {
492    /// Enable automatic backups
493    pub enabled: bool,
494    
495    /// Backup interval in hours
496    pub interval_hours: u64,
497    
498    /// Maximum number of backups to keep
499    pub max_backups: u32,
500    
501    /// Backup directory
502    pub backup_dir: Option<PathBuf>,
503}
504
505impl Default for BackupConfig {
506    fn default() -> Self {
507        Self {
508            enabled: true,
509            interval_hours: 24,
510            max_backups: 7,
511            backup_dir: None,
512        }
513    }
514}
515
516/// Authentication configuration
517#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct AuthConfig {
519    /// Storage backend for auth data
520    pub storage_backend: String,
521    
522    /// Token refresh interval in minutes
523    pub refresh_interval: u64,
524    
525    /// Security configuration
526    pub security: SecurityConfig,
527}
528
529impl Default for AuthConfig {
530    fn default() -> Self {
531        Self {
532            storage_backend: "encrypted_file".to_string(),
533            refresh_interval: 60,
534            security: SecurityConfig::default(),
535        }
536    }
537}
538
539/// Security configuration
540#[derive(Debug, Clone, Serialize, Deserialize)]
541pub struct SecurityConfig {
542    /// Enable key rotation
543    pub key_rotation: bool,
544    
545    /// Key rotation interval in days
546    pub rotation_interval_days: u64,
547    
548    /// Enable audit logging
549    pub audit_logging: bool,
550}
551
552impl Default for SecurityConfig {
553    fn default() -> Self {
554        Self {
555            key_rotation: true,
556            rotation_interval_days: 30,
557            audit_logging: true,
558        }
559    }
560}
561
562/// Session configuration
563#[derive(Debug, Clone, Serialize, Deserialize)]
564pub struct SessionConfig {
565    /// Maximum number of messages per session
566    pub max_messages: u32,
567    
568    /// Maximum session age in hours
569    pub max_age_hours: u64,
570    
571    /// Auto-save interval in seconds
572    pub auto_save_interval: u64,
573    
574    /// Context window configuration
575    pub context_window: ContextWindowConfig,
576}
577
578impl Default for SessionConfig {
579    fn default() -> Self {
580        Self {
581            max_messages: 1000,
582            max_age_hours: 24 * 7, // 1 week
583            auto_save_interval: 30,
584            context_window: ContextWindowConfig::default(),
585        }
586    }
587}
588
589/// Context window configuration
590#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct ContextWindowConfig {
592    /// Maximum tokens in context
593    pub max_tokens: u32,
594    
595    /// Context trimming strategy
596    pub trim_strategy: String,
597    
598    /// Preserve system messages
599    pub preserve_system: bool,
600    
601    /// Preserve recent messages count
602    pub preserve_recent: u32,
603}
604
605impl Default for ContextWindowConfig {
606    fn default() -> Self {
607        Self {
608            max_tokens: 100000,
609            trim_strategy: "preserve_recent".to_string(),
610            preserve_system: true,
611            preserve_recent: 10,
612        }
613    }
614}
615
616/// Memory configuration
617#[derive(Debug, Clone, Serialize, Deserialize)]
618pub struct MemoryConfig {
619    /// Memory backend type
620    pub backend: String,
621    
622    /// Maximum memory entries
623    pub max_entries: u32,
624    
625    /// Memory TTL in hours
626    pub ttl_hours: u64,
627    
628    /// Enable memory compression
629    pub compression: bool,
630    
631    /// Memory indexing configuration
632    pub indexing: IndexingConfig,
633}
634
635impl Default for MemoryConfig {
636    fn default() -> Self {
637        Self {
638            backend: "hybrid".to_string(),
639            max_entries: 10000,
640            ttl_hours: 24 * 30, // 30 days
641            compression: crate::features::HAS_COMPRESSION,
642            indexing: IndexingConfig::default(),
643        }
644    }
645}
646
647/// Memory indexing configuration
648#[derive(Debug, Clone, Serialize, Deserialize)]
649pub struct IndexingConfig {
650    /// Enable semantic search
651    pub semantic_search: bool,
652    
653    /// Enable full-text search
654    pub full_text_search: bool,
655    
656    /// Embedding model for semantic search
657    pub embedding_model: Option<String>,
658}
659
660impl Default for IndexingConfig {
661    fn default() -> Self {
662        Self {
663            semantic_search: false,
664            full_text_search: true,
665            embedding_model: None,
666        }
667    }
668}
669
670/// Agent configuration
671#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct AgentConfig {
673    /// Maximum concurrent agents
674    pub max_concurrent: u32,
675    
676    /// Agent timeout in seconds
677    pub timeout_seconds: u64,
678    
679    /// Enable agent collaboration
680    pub collaboration: bool,
681    
682    /// Default agent capabilities
683    pub default_capabilities: Vec<String>,
684}
685
686impl Default for AgentConfig {
687    fn default() -> Self {
688        Self {
689            max_concurrent: 4,
690            timeout_seconds: 300,
691            collaboration: true,
692            default_capabilities: vec![
693                "read".to_string(),
694                "write".to_string(),
695                "execute".to_string(),
696            ],
697        }
698    }
699}
700
701/// Configuration manager for loading and managing configurations
702pub struct ConfigManager {
703    config: Config,
704    config_path: Option<PathBuf>,
705}
706
707impl ConfigManager {
708    /// Create a new configuration manager
709    pub fn new() -> Self {
710        Self {
711            config: Config::default(),
712            config_path: None,
713        }
714    }
715
716    /// Load configuration from file
717    pub async fn load_from_file<P: Into<PathBuf>>(&mut self, path: P) -> Result<()> {
718        let path = path.into();
719        let content = std::fs::read_to_string(&path)
720            .map_err(|e| Error::Io(e))?;
721        
722        let config: Config = match path.extension().and_then(|s| s.to_str()) {
723            Some("json") => serde_json::from_str(&content)?,
724            Some("toml") => toml::from_str(&content)
725                .map_err(|e| Error::Other(anyhow::anyhow!("TOML parse error: {}", e)))?,
726            Some("yaml") | Some("yml") => {
727                return Err(Error::Other(anyhow::anyhow!("YAML support not implemented")));
728            }
729            _ => return Err(Error::Other(anyhow::anyhow!("Unsupported config file format"))),
730        };
731
732        self.config = config;
733        self.config_path = Some(path);
734        Ok(())
735    }
736
737    /// Load configuration from environment variables
738    pub fn load_from_env(&mut self) -> Result<()> {
739        // Load environment variables with CODE_MESH_ prefix
740        let mut config = self.config.clone();
741        
742        if let Ok(log_level) = std::env::var("CODE_MESH_LOG_LEVEL") {
743            config.app.log_level = log_level;
744        }
745        
746        if let Ok(data_dir) = std::env::var("CODE_MESH_DATA_DIR") {
747            config.app.data_dir = Some(PathBuf::from(data_dir));
748        }
749        
750        // Add more environment variable mappings as needed
751        
752        self.config = config;
753        Ok(())
754    }
755
756    /// Save configuration to file
757    pub async fn save_to_file<P: Into<PathBuf>>(&self, path: P) -> Result<()> {
758        let path = path.into();
759        
760        let content = match path.extension().and_then(|s| s.to_str()) {
761            Some("json") => serde_json::to_string_pretty(&self.config)?,
762            Some("toml") => toml::to_string_pretty(&self.config)
763                .map_err(|e| Error::Other(anyhow::anyhow!("TOML serialize error: {}", e)))?,
764            _ => return Err(Error::Other(anyhow::anyhow!("Unsupported config file format"))),
765        };
766        
767        std::fs::write(path, content)?;
768        Ok(())
769    }
770
771    /// Get the current configuration
772    pub fn config(&self) -> &Config {
773        &self.config
774    }
775
776    /// Get mutable access to the configuration
777    pub fn config_mut(&mut self) -> &mut Config {
778        &mut self.config
779    }
780
781    /// Validate the current configuration
782    pub fn validate(&self) -> Result<()> {
783        // Validate provider configurations
784        for (id, provider) in &self.config.providers {
785            if provider.id != *id {
786                return Err(Error::Other(anyhow::anyhow!(
787                    "Provider ID mismatch: {} != {}",
788                    provider.id,
789                    id
790                )));
791            }
792        }
793
794        // Validate tool configurations
795        if self.config.tools.default_permission.is_empty() {
796            return Err(Error::Other(anyhow::anyhow!(
797                "Default permission cannot be empty"
798            )));
799        }
800
801        // Add more validation rules as needed
802
803        Ok(())
804    }
805
806    /// Get configuration for a specific provider
807    pub fn get_provider_config(&self, provider_id: &str) -> Option<&ProviderConfig> {
808        self.config.providers.get(provider_id)
809    }
810
811    /// Get configuration for a specific tool
812    pub fn get_tool_config(&self, tool_id: &str) -> Option<&ToolSpecificConfig> {
813        self.config.tools.tool_configs.get(tool_id)
814    }
815
816    /// Merge another configuration into the current one
817    pub fn merge(&mut self, other: Config) -> Result<()> {
818        // Deep merge configurations
819        // This is a simplified version - in practice, you'd want more sophisticated merging
820        for (id, provider) in other.providers {
821            self.config.providers.insert(id, provider);
822        }
823
824        for (id, tool_config) in other.tools.tool_configs {
825            self.config.tools.tool_configs.insert(id, tool_config);
826        }
827
828        Ok(())
829    }
830}
831
832impl Default for ConfigManager {
833    fn default() -> Self {
834        Self::new()
835    }
836}