llm_optimizer_config/
lib.rs

1//! Configuration management for LLM Auto-Optimizer
2
3use figment::{Figment, providers::{Format, Yaml, Env}};
4use llm_optimizer_types::strategies::StrategyConfig;
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7use thiserror::Error;
8
9#[derive(Error, Debug)]
10pub enum ConfigError {
11    #[error("Failed to load configuration: {0}")]
12    LoadError(String),
13
14    #[error("Invalid configuration: {0}")]
15    ValidationError(String),
16}
17
18pub type Result<T> = std::result::Result<T, ConfigError>;
19
20/// Main optimizer configuration
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct OptimizerConfig {
23    /// Service configuration
24    pub service: ServiceConfig,
25
26    /// Database configuration
27    pub database: DatabaseConfig,
28
29    /// Integration endpoints
30    pub integrations: IntegrationConfig,
31
32    /// Optimization strategies
33    pub strategies: StrategyConfig,
34
35    /// Observability settings
36    pub observability: ObservabilityConfig,
37}
38
39impl OptimizerConfig {
40    /// Load configuration from file and environment
41    pub fn load(config_path: Option<PathBuf>) -> Result<Self> {
42        let mut figment = Figment::new();
43
44        // Load from file if provided
45        if let Some(path) = config_path {
46            figment = figment.merge(Yaml::file(path));
47        }
48
49        // Override with environment variables (prefixed with OPTIMIZER_)
50        figment = figment.merge(Env::prefixed("OPTIMIZER_").split("__"));
51
52        figment.extract().map_err(|e| ConfigError::LoadError(e.to_string()))
53    }
54
55    /// Validate configuration
56    pub fn validate(&self) -> Result<()> {
57        if self.service.port == 0 || self.service.port > 65535 {
58            return Err(ConfigError::ValidationError("Invalid service port".to_string()));
59        }
60
61        if self.database.connection_string.is_empty() {
62            return Err(ConfigError::ValidationError("Database connection string required".to_string()));
63        }
64
65        Ok(())
66    }
67}
68
69impl Default for OptimizerConfig {
70    fn default() -> Self {
71        Self {
72            service: ServiceConfig::default(),
73            database: DatabaseConfig::default(),
74            integrations: IntegrationConfig::default(),
75            strategies: StrategyConfig::default(),
76            observability: ObservabilityConfig::default(),
77        }
78    }
79}
80
81/// Service configuration
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ServiceConfig {
84    /// Service name
85    pub name: String,
86
87    /// Service host
88    pub host: String,
89
90    /// Service port
91    pub port: u16,
92
93    /// Deployment mode
94    pub mode: DeploymentMode,
95
96    /// Optimization loop interval in seconds
97    pub optimization_interval_secs: u64,
98}
99
100impl Default for ServiceConfig {
101    fn default() -> Self {
102        Self {
103            name: "llm-auto-optimizer".to_string(),
104            host: "0.0.0.0".to_string(),
105            port: 8080,
106            mode: DeploymentMode::Standalone,
107            optimization_interval_secs: 900, // 15 minutes
108        }
109    }
110}
111
112/// Deployment mode
113#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
114#[serde(rename_all = "snake_case")]
115pub enum DeploymentMode {
116    Sidecar,
117    Standalone,
118    Daemon,
119}
120
121/// Database configuration
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct DatabaseConfig {
124    /// Database connection string
125    pub connection_string: String,
126
127    /// Maximum number of connections
128    pub max_connections: u32,
129
130    /// Connection timeout in seconds
131    pub timeout_secs: u64,
132
133    /// Enable migrations on startup
134    pub auto_migrate: bool,
135}
136
137impl Default for DatabaseConfig {
138    fn default() -> Self {
139        Self {
140            connection_string: "sqlite://optimizer.db".to_string(),
141            max_connections: 10,
142            timeout_secs: 30,
143            auto_migrate: true,
144        }
145    }
146}
147
148/// Integration endpoints configuration
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct IntegrationConfig {
151    /// LLM Observatory endpoint
152    pub observatory_url: Option<String>,
153
154    /// LLM Orchestrator endpoint
155    pub orchestrator_url: Option<String>,
156
157    /// LLM Sentinel Kafka brokers
158    pub sentinel_kafka_brokers: Option<Vec<String>>,
159
160    /// LLM Governance endpoint
161    pub governance_url: Option<String>,
162
163    /// LLM Registry endpoint
164    pub registry_url: Option<String>,
165}
166
167impl Default for IntegrationConfig {
168    fn default() -> Self {
169        Self {
170            observatory_url: None,
171            orchestrator_url: None,
172            sentinel_kafka_brokers: None,
173            governance_url: None,
174            registry_url: None,
175        }
176    }
177}
178
179/// Observability configuration
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ObservabilityConfig {
182    /// Log level
183    pub log_level: String,
184
185    /// Enable structured JSON logging
186    pub json_logging: bool,
187
188    /// Metrics export endpoint
189    pub metrics_endpoint: Option<String>,
190
191    /// Traces export endpoint
192    pub traces_endpoint: Option<String>,
193}
194
195impl Default for ObservabilityConfig {
196    fn default() -> Self {
197        Self {
198            log_level: "info".to_string(),
199            json_logging: false,
200            metrics_endpoint: None,
201            traces_endpoint: None,
202        }
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209
210    #[test]
211    fn test_default_config() {
212        let config = OptimizerConfig::default();
213        assert_eq!(config.service.port, 8080);
214        assert!(config.validate().is_ok());
215    }
216
217    #[test]
218    fn test_config_validation() {
219        let mut config = OptimizerConfig::default();
220        config.service.port = 0;
221        assert!(config.validate().is_err());
222
223        config.service.port = 8080;
224        config.database.connection_string = String::new();
225        assert!(config.validate().is_err());
226    }
227}