use std::env; use std::path::Path; use platform_config::ConfigLoader; /// AI Service configuration use serde::{Deserialize, Serialize}; /// Main AI Service configuration #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct AiServiceConfig { /// Server configuration #[serde(default)] pub server: ServerConfig, /// RAG integration configuration #[serde(default)] pub rag: RagIntegrationConfig, /// MCP integration configuration #[serde(default)] pub mcp: McpIntegrationConfig, /// DAG execution configuration #[serde(default)] pub dag: DagConfig, } /// Server configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerConfig { /// Server bind address #[serde(default = "default_host")] pub host: String, /// Server port #[serde(default = "default_server_port")] pub port: u16, /// Number of worker threads #[serde(default = "default_workers")] pub workers: usize, /// TCP keep-alive timeout (seconds) #[serde(default = "default_keep_alive")] pub keep_alive: u64, /// Request timeout (milliseconds) #[serde(default = "default_request_timeout")] pub request_timeout: u64, } /// RAG integration configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RagIntegrationConfig { /// Enable RAG integration #[serde(default)] pub enabled: bool, /// RAG service URL #[serde(default = "default_rag_url")] pub rag_service_url: String, /// Request timeout (milliseconds) #[serde(default = "default_rag_timeout")] pub timeout: u64, /// Max retries for failed requests #[serde(default = "default_max_retries")] pub max_retries: u32, /// Enable response caching #[serde(default = "default_true")] pub cache_enabled: bool, } /// MCP integration configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpIntegrationConfig { /// Enable MCP integration #[serde(default)] pub enabled: bool, /// MCP service URL #[serde(default = "default_mcp_url")] pub mcp_service_url: String, /// Request timeout (milliseconds) #[serde(default = "default_mcp_timeout")] pub timeout: u64, /// Max retries for failed requests #[serde(default = "default_max_retries")] pub max_retries: u32, /// MCP protocol version #[serde(default = "default_protocol_version")] pub protocol_version: String, } /// DAG execution configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DagConfig { /// Maximum concurrent tasks #[serde(default = "default_max_concurrent_tasks")] pub max_concurrent_tasks: usize, /// Task timeout (milliseconds) #[serde(default = "default_task_timeout")] pub task_timeout: u64, /// Number of retry attempts #[serde(default = "default_dag_retry_attempts")] pub retry_attempts: u32, /// Delay between retries (milliseconds) #[serde(default = "default_retry_delay")] pub retry_delay: u64, /// Task queue size #[serde(default = "default_queue_size")] pub queue_size: usize, } // Default value functions fn default_host() -> String { "127.0.0.1".to_string() } fn default_server_port() -> u16 { 8082 } fn default_workers() -> usize { 4 } fn default_keep_alive() -> u64 { 75 } fn default_request_timeout() -> u64 { 30000 } fn default_rag_url() -> String { "http://localhost:8083".to_string() } fn default_rag_timeout() -> u64 { 30000 } fn default_mcp_url() -> String { "http://localhost:8084".to_string() } fn default_mcp_timeout() -> u64 { 30000 } fn default_max_retries() -> u32 { 3 } fn default_true() -> bool { true } fn default_protocol_version() -> String { "1.0".to_string() } fn default_max_concurrent_tasks() -> usize { 10 } fn default_task_timeout() -> u64 { 600000 } fn default_dag_retry_attempts() -> u32 { 3 } fn default_retry_delay() -> u64 { 1000 } fn default_queue_size() -> usize { 1000 } impl Default for ServerConfig { fn default() -> Self { Self { host: default_host(), port: default_server_port(), workers: default_workers(), keep_alive: default_keep_alive(), request_timeout: default_request_timeout(), } } } impl Default for RagIntegrationConfig { fn default() -> Self { Self { enabled: false, rag_service_url: default_rag_url(), timeout: default_rag_timeout(), max_retries: default_max_retries(), cache_enabled: default_true(), } } } impl Default for McpIntegrationConfig { fn default() -> Self { Self { enabled: false, mcp_service_url: default_mcp_url(), timeout: default_mcp_timeout(), max_retries: default_max_retries(), protocol_version: default_protocol_version(), } } } impl Default for DagConfig { fn default() -> Self { Self { max_concurrent_tasks: default_max_concurrent_tasks(), task_timeout: default_task_timeout(), retry_attempts: default_dag_retry_attempts(), retry_delay: default_retry_delay(), queue_size: default_queue_size(), } } } impl ConfigLoader for AiServiceConfig { fn service_name() -> &'static str { "ai-service" } fn load_from_hierarchy() -> std::result::Result> { let service = Self::service_name(); if let Some(path) = platform_config::resolve_config_path(service) { return Self::from_path(&path); } // Fallback to defaults Ok(Self::default()) } fn apply_env_overrides( &mut self, ) -> std::result::Result<(), Box> { Self::apply_env_overrides_internal(self); Ok(()) } fn from_path>( path: P, ) -> std::result::Result> { let path = path.as_ref(); let json_value = platform_config::format::load_config(path).map_err(|e| { let err: Box = Box::new(e); err })?; serde_json::from_value(json_value).map_err(|e| { let err_msg = format!( "Failed to deserialize AI service config from {:?}: {}", path, e ); Box::new(std::io::Error::new( std::io::ErrorKind::InvalidData, err_msg, )) as Box }) } } impl AiServiceConfig { /// Load configuration from hierarchical sources with mode support /// /// Priority order: /// 1. AI_SERVICE_CONFIG environment variable (explicit path) /// 2. AI_SERVICE_MODE environment variable (mode-specific file) /// 3. Default configuration /// /// After loading, applies environment variable overrides. pub fn load_from_hierarchy() -> Result> { ::load_from_hierarchy().map_err(|_e| { Box::new(std::io::Error::other("Failed to load AI service config")) as Box }) } /// Internal: Apply environment variable overrides (mutable reference) /// /// Overrides take precedence over loaded config values. /// Pattern: AI_SERVICE_{SECTION}_{KEY} fn apply_env_overrides_internal(config: &mut Self) { // Server overrides if let Ok(val) = env::var("AI_SERVICE_SERVER_HOST") { config.server.host = val; } if let Ok(val) = env::var("AI_SERVICE_SERVER_PORT") { if let Ok(port) = val.parse() { config.server.port = port; } } if let Ok(val) = env::var("AI_SERVICE_SERVER_WORKERS") { if let Ok(workers) = val.parse() { config.server.workers = workers; } } // RAG integration overrides if let Ok(val) = env::var("AI_SERVICE_RAG_ENABLED") { config.rag.enabled = val.parse().unwrap_or(config.rag.enabled); } if let Ok(val) = env::var("AI_SERVICE_RAG_URL") { config.rag.rag_service_url = val; } if let Ok(val) = env::var("AI_SERVICE_RAG_TIMEOUT") { if let Ok(timeout) = val.parse() { config.rag.timeout = timeout; } } // MCP integration overrides if let Ok(val) = env::var("AI_SERVICE_MCP_ENABLED") { config.mcp.enabled = val.parse().unwrap_or(config.mcp.enabled); } if let Ok(val) = env::var("AI_SERVICE_MCP_URL") { config.mcp.mcp_service_url = val; } if let Ok(val) = env::var("AI_SERVICE_MCP_TIMEOUT") { if let Ok(timeout) = val.parse() { config.mcp.timeout = timeout; } } // DAG overrides if let Ok(val) = env::var("AI_SERVICE_DAG_MAX_CONCURRENT_TASKS") { if let Ok(tasks) = val.parse() { config.dag.max_concurrent_tasks = tasks; } } if let Ok(val) = env::var("AI_SERVICE_DAG_TASK_TIMEOUT") { if let Ok(timeout) = val.parse() { config.dag.task_timeout = timeout; } } if let Ok(val) = env::var("AI_SERVICE_DAG_RETRY_ATTEMPTS") { if let Ok(retries) = val.parse() { config.dag.retry_attempts = retries; } } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_default_config() { let config = AiServiceConfig::default(); assert_eq!(config.server.port, 8082); assert_eq!(config.server.workers, 4); assert!(!config.rag.enabled); assert!(!config.mcp.enabled); assert_eq!(config.dag.max_concurrent_tasks, 10); } #[test] fn test_server_config_defaults() { let server = ServerConfig::default(); assert_eq!(server.host, "127.0.0.1"); assert_eq!(server.port, 8082); assert_eq!(server.workers, 4); } #[test] fn test_dag_config_defaults() { let dag = DagConfig::default(); assert_eq!(dag.max_concurrent_tasks, 10); assert_eq!(dag.task_timeout, 600000); assert_eq!(dag.retry_attempts, 3); } }