m2m/config/
mod.rs

1//! Configuration management.
2//!
3//! Supports configuration from:
4//! - TOML config files
5//! - Environment variables
6
7use std::path::PathBuf;
8
9use serde::{Deserialize, Serialize};
10
11use crate::error::{M2MError, Result};
12
13/// Main configuration struct
14#[derive(Debug, Clone, Default, Serialize, Deserialize)]
15pub struct Config {
16    /// Compression configuration
17    #[serde(default)]
18    pub compression: CompressionConfig,
19
20    /// Model registry configuration
21    #[serde(default)]
22    pub models: ModelConfig,
23}
24
25impl Config {
26    /// Load configuration from a TOML file
27    pub fn from_file(path: impl Into<PathBuf>) -> Result<Self> {
28        let path = path.into();
29        let content = std::fs::read_to_string(&path)
30            .map_err(|e| M2MError::Config(format!("Failed to read config file: {e}")))?;
31
32        toml::from_str(&content)
33            .map_err(|e| M2MError::Config(format!("Failed to parse config: {e}")))
34    }
35
36    /// Load configuration from environment variables
37    pub fn from_env() -> Self {
38        let mut config = Self::default();
39
40        // Compression settings
41        if let Ok(val) = std::env::var("M2M_COMPRESS_MIN_TOKENS") {
42            if let Ok(val) = val.parse() {
43                config.compression.min_tokens = val;
44            }
45        }
46
47        config
48    }
49
50    /// Merge with another config (other takes precedence)
51    pub fn merge(self, other: Self) -> Self {
52        Self {
53            compression: other.compression,
54            models: other.models,
55        }
56    }
57}
58
59/// Compression configuration
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct CompressionConfig {
62    /// Enable compression (false = passthrough mode)
63    pub enabled: bool,
64
65    /// Minimum tokens to consider compression
66    pub min_tokens: usize,
67
68    /// Threshold for full compression
69    pub full_compression_threshold: usize,
70
71    /// Enable key abbreviation
72    pub abbreviate_keys: bool,
73
74    /// Enable role abbreviation
75    pub abbreviate_roles: bool,
76
77    /// Enable model abbreviation
78    pub abbreviate_models: bool,
79
80    /// Remove default values
81    pub remove_defaults: bool,
82}
83
84impl Default for CompressionConfig {
85    fn default() -> Self {
86        Self {
87            enabled: true,
88            min_tokens: 25,
89            full_compression_threshold: 50,
90            abbreviate_keys: true,
91            abbreviate_roles: true,
92            abbreviate_models: true,
93            remove_defaults: true,
94        }
95    }
96}
97
98/// Model registry configuration
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct ModelConfig {
101    /// Enable fetching models from OpenRouter
102    pub fetch_openrouter: bool,
103
104    /// Cache directory for dynamic models
105    pub cache_dir: Option<PathBuf>,
106
107    /// Cache TTL in seconds
108    pub cache_ttl_secs: u64,
109}
110
111impl Default for ModelConfig {
112    fn default() -> Self {
113        Self {
114            fetch_openrouter: false,
115            cache_dir: dirs::cache_dir().map(|p| p.join("m2m")),
116            cache_ttl_secs: 3600, // 1 hour
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_default_config() {
127        let config = Config::default();
128        assert!(config.compression.abbreviate_keys);
129    }
130
131    #[test]
132    fn test_config_from_toml() {
133        let toml = r#"
134            [compression]
135            enabled = true
136            min_tokens = 50
137            full_compression_threshold = 50
138            abbreviate_keys = true
139            abbreviate_roles = true
140            abbreviate_models = true
141            remove_defaults = true
142        "#;
143
144        let config: Config = toml::from_str(toml).unwrap();
145        assert_eq!(config.compression.min_tokens, 50);
146        assert!(config.compression.enabled);
147    }
148}