1use crate::{Error, Result};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::path::PathBuf;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Config {
18 pub app: AppConfig,
20
21 pub providers: HashMap<String, ProviderConfig>,
23
24 pub tools: ToolConfig,
26
27 pub storage: StorageConfig,
29
30 pub auth: AuthConfig,
32
33 pub session: SessionConfig,
35
36 pub memory: MemoryConfig,
38
39 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#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct AppConfig {
61 pub name: String,
63
64 pub version: String,
66
67 pub data_dir: Option<PathBuf>,
69
70 pub config_dir: Option<PathBuf>,
72
73 pub log_level: String,
75
76 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#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct FeatureConfig {
96 pub compression: bool,
98
99 pub file_watching: bool,
101
102 pub advanced_crypto: bool,
104
105 pub telemetry: bool,
107
108 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#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ProviderConfig {
127 pub id: String,
129
130 pub name: String,
132
133 pub base_url: Option<String>,
135
136 pub api_version: Option<String>,
138
139 pub default_model: Option<String>,
141
142 pub models: HashMap<String, ModelConfig>,
144
145 pub options: HashMap<String, serde_json::Value>,
147
148 pub rate_limit: RateLimitConfig,
150
151 pub timeout: TimeoutConfig,
153
154 pub retry: RetryConfig,
156
157 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#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ModelConfig {
182 pub id: String,
184
185 pub name: String,
187
188 pub max_context: Option<u32>,
190
191 pub max_output: Option<u32>,
193
194 pub temperature: Option<f32>,
196
197 pub supports_tools: bool,
199
200 pub supports_vision: bool,
202
203 pub supports_streaming: bool,
205
206 pub supports_caching: bool,
208
209 pub cost: CostConfig,
211
212 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#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct CostConfig {
237 pub input_cost_per_1k: f64,
239
240 pub output_cost_per_1k: f64,
242
243 pub cache_read_cost_per_1k: Option<f64>,
245
246 pub cache_write_cost_per_1k: Option<f64>,
248
249 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#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct RateLimitConfig {
268 pub requests_per_minute: Option<u32>,
270
271 pub tokens_per_minute: Option<u32>,
273
274 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#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct TimeoutConfig {
291 pub connect: u64,
293
294 pub request: u64,
296
297 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#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct RetryConfig {
314 pub max_retries: u32,
316
317 pub base_delay: u64,
319
320 pub max_delay: u64,
322
323 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#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct ToolConfig {
341 pub enabled: Vec<String>,
343
344 pub disabled: Vec<String>,
346
347 pub tool_configs: HashMap<String, ToolSpecificConfig>,
349
350 pub default_permission: String,
352
353 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#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct ToolSpecificConfig {
372 pub permission: String,
374
375 pub allowed_patterns: Vec<String>,
377
378 pub denied_patterns: Vec<String>,
380
381 pub max_file_size: Option<u64>,
383
384 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#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct SandboxConfig {
403 pub enabled: bool,
405
406 pub allowed_dirs: Vec<PathBuf>,
408
409 pub denied_dirs: Vec<PathBuf>,
411
412 pub network_access: bool,
414
415 pub max_execution_time: u64,
417
418 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#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct StorageConfig {
438 pub backend: String,
440
441 pub connection: String,
443
444 pub compression: bool,
446
447 pub encryption: EncryptionConfig,
449
450 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#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct EncryptionConfig {
469 pub enabled: bool,
471
472 pub algorithm: String,
474
475 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#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct BackupConfig {
492 pub enabled: bool,
494
495 pub interval_hours: u64,
497
498 pub max_backups: u32,
500
501 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#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct AuthConfig {
519 pub storage_backend: String,
521
522 pub refresh_interval: u64,
524
525 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#[derive(Debug, Clone, Serialize, Deserialize)]
541pub struct SecurityConfig {
542 pub key_rotation: bool,
544
545 pub rotation_interval_days: u64,
547
548 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#[derive(Debug, Clone, Serialize, Deserialize)]
564pub struct SessionConfig {
565 pub max_messages: u32,
567
568 pub max_age_hours: u64,
570
571 pub auto_save_interval: u64,
573
574 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, auto_save_interval: 30,
584 context_window: ContextWindowConfig::default(),
585 }
586 }
587}
588
589#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct ContextWindowConfig {
592 pub max_tokens: u32,
594
595 pub trim_strategy: String,
597
598 pub preserve_system: bool,
600
601 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#[derive(Debug, Clone, Serialize, Deserialize)]
618pub struct MemoryConfig {
619 pub backend: String,
621
622 pub max_entries: u32,
624
625 pub ttl_hours: u64,
627
628 pub compression: bool,
630
631 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, compression: crate::features::HAS_COMPRESSION,
642 indexing: IndexingConfig::default(),
643 }
644 }
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
649pub struct IndexingConfig {
650 pub semantic_search: bool,
652
653 pub full_text_search: bool,
655
656 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#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct AgentConfig {
673 pub max_concurrent: u32,
675
676 pub timeout_seconds: u64,
678
679 pub collaboration: bool,
681
682 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
701pub struct ConfigManager {
703 config: Config,
704 config_path: Option<PathBuf>,
705}
706
707impl ConfigManager {
708 pub fn new() -> Self {
710 Self {
711 config: Config::default(),
712 config_path: None,
713 }
714 }
715
716 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 pub fn load_from_env(&mut self) -> Result<()> {
739 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 self.config = config;
753 Ok(())
754 }
755
756 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 pub fn config(&self) -> &Config {
773 &self.config
774 }
775
776 pub fn config_mut(&mut self) -> &mut Config {
778 &mut self.config
779 }
780
781 pub fn validate(&self) -> Result<()> {
783 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 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 Ok(())
804 }
805
806 pub fn get_provider_config(&self, provider_id: &str) -> Option<&ProviderConfig> {
808 self.config.providers.get(provider_id)
809 }
810
811 pub fn get_tool_config(&self, tool_id: &str) -> Option<&ToolSpecificConfig> {
813 self.config.tools.tool_configs.get(tool_id)
814 }
815
816 pub fn merge(&mut self, other: Config) -> Result<()> {
818 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}