llm_optimizer_config/
lib.rs1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct OptimizerConfig {
23 pub service: ServiceConfig,
25
26 pub database: DatabaseConfig,
28
29 pub integrations: IntegrationConfig,
31
32 pub strategies: StrategyConfig,
34
35 pub observability: ObservabilityConfig,
37}
38
39impl OptimizerConfig {
40 pub fn load(config_path: Option<PathBuf>) -> Result<Self> {
42 let mut figment = Figment::new();
43
44 if let Some(path) = config_path {
46 figment = figment.merge(Yaml::file(path));
47 }
48
49 figment = figment.merge(Env::prefixed("OPTIMIZER_").split("__"));
51
52 figment.extract().map_err(|e| ConfigError::LoadError(e.to_string()))
53 }
54
55 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#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ServiceConfig {
84 pub name: String,
86
87 pub host: String,
89
90 pub port: u16,
92
93 pub mode: DeploymentMode,
95
96 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, }
109 }
110}
111
112#[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#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct DatabaseConfig {
124 pub connection_string: String,
126
127 pub max_connections: u32,
129
130 pub timeout_secs: u64,
132
133 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#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct IntegrationConfig {
151 pub observatory_url: Option<String>,
153
154 pub orchestrator_url: Option<String>,
156
157 pub sentinel_kafka_brokers: Option<Vec<String>>,
159
160 pub governance_url: Option<String>,
162
163 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#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ObservabilityConfig {
182 pub log_level: String,
184
185 pub json_logging: bool,
187
188 pub metrics_endpoint: Option<String>,
190
191 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}