Skip to main content
Glama

CodeGraph CLI MCP Server

by Jakedismo
advanced_config.rsโ€ข17.4 kB
use anyhow::{Context, Result}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::env; use std::fs; use std::path::{Path, PathBuf}; use tracing::{debug, info, warn}; use crate::embedding_config::{EmbeddingModelConfig, EmbeddingPreset}; use crate::performance_config::{PerformanceMode, PerformanceModeConfig, PerformanceProfile}; #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct AdvancedConfig { #[serde(default)] pub embedding: EmbeddingModelConfig, #[serde(default)] pub performance: PerformanceModeConfig, #[serde(default)] pub runtime: RuntimeConfig, #[serde(default)] pub monitoring: MonitoringConfig, #[serde(default)] pub templates: ConfigTemplates, } impl Default for AdvancedConfig { fn default() -> Self { Self { embedding: EmbeddingModelConfig::default(), performance: PerformanceModeConfig::default(), runtime: RuntimeConfig::default(), monitoring: MonitoringConfig::default(), templates: ConfigTemplates::default(), } } } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct RuntimeConfig { #[serde(default = "RuntimeConfig::default_allow_runtime_switching")] pub allow_runtime_switching: bool, #[serde(default = "RuntimeConfig::default_hot_reload")] pub hot_reload: bool, #[serde(default = "RuntimeConfig::default_config_watch_interval_secs")] pub config_watch_interval_secs: u64, #[serde(default)] pub fallback_configs: Vec<String>, #[serde(default)] pub environment_overrides: HashMap<String, String>, } impl RuntimeConfig { fn default_allow_runtime_switching() -> bool { true } fn default_hot_reload() -> bool { false } fn default_config_watch_interval_secs() -> u64 { 30 } } impl Default for RuntimeConfig { fn default() -> Self { Self { allow_runtime_switching: Self::default_allow_runtime_switching(), hot_reload: Self::default_hot_reload(), config_watch_interval_secs: Self::default_config_watch_interval_secs(), fallback_configs: vec![], environment_overrides: HashMap::new(), } } } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct MonitoringConfig { #[serde(default = "MonitoringConfig::default_enabled")] pub enabled: bool, #[serde(default = "MonitoringConfig::default_metrics_enabled")] pub metrics_enabled: bool, #[serde(default = "MonitoringConfig::default_trace_enabled")] pub trace_enabled: bool, #[serde(default = "MonitoringConfig::default_profile_enabled")] pub profile_enabled: bool, #[serde(default = "MonitoringConfig::default_metrics_interval_secs")] pub metrics_interval_secs: u64, #[serde(default)] pub export_targets: Vec<String>, } impl MonitoringConfig { fn default_enabled() -> bool { true } fn default_metrics_enabled() -> bool { true } fn default_trace_enabled() -> bool { false } fn default_profile_enabled() -> bool { false } fn default_metrics_interval_secs() -> u64 { 60 } } impl Default for MonitoringConfig { fn default() -> Self { Self { enabled: Self::default_enabled(), metrics_enabled: Self::default_metrics_enabled(), trace_enabled: Self::default_trace_enabled(), profile_enabled: Self::default_profile_enabled(), metrics_interval_secs: Self::default_metrics_interval_secs(), export_targets: vec![], } } } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ConfigTemplates { #[serde(default)] pub embedding_presets: HashMap<String, EmbeddingModelConfig>, #[serde(default)] pub performance_profiles: HashMap<String, PerformanceModeConfig>, #[serde(default)] pub quick_configs: HashMap<String, QuickConfig>, } impl Default for ConfigTemplates { fn default() -> Self { let mut embedding_presets = HashMap::new(); for preset in EmbeddingPreset::all_presets() { embedding_presets.insert(preset.name, preset.config); } let mut performance_profiles = HashMap::new(); for profile in PerformanceProfile::all_profiles() { performance_profiles.insert(profile.name, profile.config); } Self { embedding_presets, performance_profiles, quick_configs: QuickConfig::all_configs(), } } } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct QuickConfig { pub name: String, pub description: String, pub embedding_preset: String, pub performance_profile: String, pub monitoring_enabled: bool, } impl QuickConfig { pub fn all_configs() -> HashMap<String, Self> { let mut configs = HashMap::new(); configs.insert( "development".to_string(), Self { name: "development".to_string(), description: "Development environment with fast iteration".to_string(), embedding_preset: "local-minilm".to_string(), performance_profile: "research".to_string(), monitoring_enabled: true, }, ); configs.insert( "staging".to_string(), Self { name: "staging".to_string(), description: "Staging environment with balanced performance".to_string(), embedding_preset: "openai-small".to_string(), performance_profile: "production".to_string(), monitoring_enabled: true, }, ); configs.insert( "production".to_string(), Self { name: "production".to_string(), description: "Production environment with high performance".to_string(), embedding_preset: "openai-large".to_string(), performance_profile: "production".to_string(), monitoring_enabled: true, }, ); configs.insert( "edge".to_string(), Self { name: "edge".to_string(), description: "Edge deployment with minimal resources".to_string(), embedding_preset: "local-minilm".to_string(), performance_profile: "edge".to_string(), monitoring_enabled: false, }, ); configs } pub fn apply(&self, config: &mut AdvancedConfig) -> Result<()> { // Apply embedding preset if let Some(embedding) = config .templates .embedding_presets .get(&self.embedding_preset) { config.embedding = embedding.clone(); } else { warn!("Embedding preset '{}' not found", self.embedding_preset); } // Apply performance profile if let Some(performance) = config .templates .performance_profiles .get(&self.performance_profile) { config.performance = performance.clone(); } else { warn!( "Performance profile '{}' not found", self.performance_profile ); } // Apply monitoring settings config.monitoring.enabled = self.monitoring_enabled; Ok(()) } } impl AdvancedConfig { pub fn validate(&self) -> Result<()> { self.embedding .validate() .context("Invalid embedding configuration")?; self.performance .validate() .context("Invalid performance configuration")?; Ok(()) } pub fn apply_template(&mut self, template_name: &str) -> Result<()> { let quick_config = self .templates .quick_configs .get(template_name) .ok_or_else(|| anyhow::anyhow!("Template '{}' not found", template_name))? .clone(); quick_config.apply(self)?; info!("Applied configuration template: {}", template_name); Ok(()) } pub fn apply_environment_overrides(&mut self) { // Override embedding provider from environment if let Ok(provider) = env::var("CODEGRAPH_EMBEDDING_PROVIDER") { match provider.to_lowercase().as_str() { "openai" => { self.embedding.provider = crate::embedding_config::EmbeddingProvider::OpenAI; } "local" => { self.embedding.provider = crate::embedding_config::EmbeddingProvider::Local; } _ => { warn!("Unknown embedding provider: {}", provider); } } } // Override performance mode from environment if let Ok(mode) = env::var("CODEGRAPH_PERFORMANCE_MODE") { match mode.to_lowercase().as_str() { "high_accuracy" => { self.performance = PerformanceModeConfig::high_accuracy(); } "balanced" => { self.performance = PerformanceModeConfig::balanced(); } "high_speed" => { self.performance = PerformanceModeConfig::high_speed(); } "ultra_fast" => { self.performance = PerformanceModeConfig::ultra_fast(); } _ => { warn!("Unknown performance mode: {}", mode); } } } // Apply custom environment overrides let overrides: Vec<(String, String, String)> = self .runtime .environment_overrides .iter() .filter_map(|(key, value)| { env::var(key).ok().map(|env_value| { debug!("Applying environment override: {} = {}", key, env_value); (key.clone(), value.clone(), env_value) }) }) .collect(); for (_, path, env_value) in overrides { self.apply_override(&path, &env_value); } } fn apply_override(&mut self, path: &str, value: &str) { // Simple path-based override system let parts: Vec<&str> = path.split('.').collect(); match parts.as_slice() { ["embedding", "dimension"] => { if let Ok(dim) = value.parse::<usize>() { self.embedding.dimension = dim; } } ["performance", "cache", "max_size_mb"] => { if let Ok(size) = value.parse::<usize>() { self.performance.cache.max_size_mb = size; } } ["performance", "processing", "batch_size"] => { if let Ok(size) = value.parse::<usize>() { self.performance.processing.batch_size = size; } } _ => { debug!("Unknown override path: {}", path); } } } pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> { let content = fs::read_to_string(&path) .with_context(|| format!("Failed to read config file: {:?}", path.as_ref()))?; let config: Self = toml::from_str(&content) .with_context(|| format!("Failed to parse config file: {:?}", path.as_ref()))?; Ok(config) } pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> { let content = toml::to_string_pretty(self).context("Failed to serialize configuration")?; fs::write(&path, content) .with_context(|| format!("Failed to write config file: {:?}", path.as_ref()))?; Ok(()) } pub fn to_json(&self) -> Result<String> { serde_json::to_string_pretty(self).context("Failed to serialize configuration to JSON") } pub fn from_json(json: &str) -> Result<Self> { serde_json::from_str(json).context("Failed to parse JSON configuration") } } #[derive(Debug)] pub struct ConfigurationManager { config: std::sync::Arc<tokio::sync::RwLock<AdvancedConfig>>, config_path: Option<PathBuf>, watch_handle: Option<tokio::task::JoinHandle<()>>, } impl ConfigurationManager { pub fn new(config: AdvancedConfig) -> Self { Self { config: std::sync::Arc::new(tokio::sync::RwLock::new(config)), config_path: None, watch_handle: None, } } pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> { let config = AdvancedConfig::from_file(&path)?; let mut manager = Self::new(config); manager.config_path = Some(path.as_ref().to_path_buf()); Ok(manager) } pub async fn get_config(&self) -> AdvancedConfig { self.config.read().await.clone() } pub async fn update_config(&self, config: AdvancedConfig) -> Result<()> { config.validate()?; *self.config.write().await = config; if let Some(ref path) = self.config_path { self.config.read().await.to_file(path)?; } Ok(()) } pub async fn switch_performance_mode(&self, mode: PerformanceMode) -> Result<()> { let mut config = self.config.write().await; if !config.runtime.allow_runtime_switching { anyhow::bail!("Runtime configuration switching is disabled"); } config.performance = PerformanceModeConfig::from_mode(mode); config.validate()?; info!("Switched performance mode to: {:?}", mode); Ok(()) } pub async fn switch_embedding_preset(&self, preset_name: &str) -> Result<()> { let mut config = self.config.write().await; if !config.runtime.allow_runtime_switching { anyhow::bail!("Runtime configuration switching is disabled"); } let preset = EmbeddingPreset::get_by_name(preset_name) .ok_or_else(|| anyhow::anyhow!("Embedding preset '{}' not found", preset_name))?; config.embedding = preset.config; config.validate()?; info!("Switched embedding preset to: {}", preset_name); Ok(()) } pub async fn start_hot_reload(&mut self) -> Result<()> { let config = self.config.read().await; if !config.runtime.hot_reload { return Ok(()); } let Some(ref path) = self.config_path else { return Ok(()); }; let config_arc = self.config.clone(); let path = path.clone(); let interval = config.runtime.config_watch_interval_secs; let handle = tokio::spawn(async move { let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(interval)); loop { interval.tick().await; match AdvancedConfig::from_file(&path) { Ok(new_config) => { if let Err(e) = new_config.validate() { warn!("Invalid configuration detected during hot reload: {}", e); continue; } *config_arc.write().await = new_config; info!("Configuration reloaded from: {:?}", path); } Err(e) => { warn!("Failed to reload configuration: {}", e); } } } }); self.watch_handle = Some(handle); info!("Started configuration hot reload"); Ok(()) } pub fn stop_hot_reload(&mut self) { if let Some(handle) = self.watch_handle.take() { handle.abort(); info!("Stopped configuration hot reload"); } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_default_config() { let config = AdvancedConfig::default(); assert!(config.validate().is_ok()); } #[test] fn test_apply_template() { let mut config = AdvancedConfig::default(); assert!(config.apply_template("development").is_ok()); assert!(config.validate().is_ok()); } #[test] fn test_serialization() { let config = AdvancedConfig::default(); let json = config.to_json().unwrap(); let from_json = AdvancedConfig::from_json(&json).unwrap(); assert_eq!(config.embedding.dimension, from_json.embedding.dimension); assert_eq!(config.performance.mode, from_json.performance.mode); } #[tokio::test] async fn test_configuration_manager() { let config = AdvancedConfig::default(); let manager = ConfigurationManager::new(config); let current = manager.get_config().await; assert_eq!(current.performance.mode, PerformanceMode::Balanced); assert!(manager .switch_performance_mode(PerformanceMode::HighSpeed) .await .is_ok()); let updated = manager.get_config().await; assert_eq!(updated.performance.mode, PerformanceMode::HighSpeed); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jakedismo/codegraph-rust'

If you have feedback or need assistance with the MCP directory API, please join our Discord server