use anyhow::{anyhow, Result};
use super::Config;
impl Config {
pub fn validate(&self) -> Result<()> {
self.validate_thresholds()?;
self.validate_mcp_config()?;
if let Some(layers) = &self.layers {
self.validate_layers(layers)?;
}
self.validate_required_fields()?;
Ok(())
}
fn validate_required_fields(&self) -> Result<()> {
if self.model.is_empty() {
return Err(anyhow!("Model field cannot be empty"));
}
if self.markdown_theme.is_empty() {
return Err(anyhow!("Markdown theme field cannot be empty"));
}
if self.ask.system.is_empty() {
return Err(anyhow!("Ask configuration 'system' field cannot be empty"));
}
if self.shell.system.is_empty() {
return Err(anyhow!(
"Shell configuration 'system' field cannot be empty"
));
}
if self.ask.temperature < 0.0 || self.ask.temperature > 2.0 {
return Err(anyhow!(
"Ask configuration temperature must be between 0.0 and 2.0, got: {}",
self.ask.temperature
));
}
if self.ask.top_p < 0.0 || self.ask.top_p > 1.0 {
return Err(anyhow!(
"Ask configuration top_p must be between 0.0 and 1.0, got: {}",
self.ask.top_p
));
}
if self.ask.top_k < 1 || self.ask.top_k > 1000 {
return Err(anyhow!(
"Ask configuration top_k must be between 1 and 1000, got: {}",
self.ask.top_k
));
}
if self.shell.temperature < 0.0 || self.shell.temperature > 2.0 {
return Err(anyhow!(
"Shell configuration temperature must be between 0.0 and 2.0, got: {}",
self.shell.temperature
));
}
if self.shell.top_p < 0.0 || self.shell.top_p > 1.0 {
return Err(anyhow!(
"Shell configuration top_p must be between 0.0 and 1.0, got: {}",
self.shell.top_p
));
}
if self.shell.top_k < 1 || self.shell.top_k > 1000 {
return Err(anyhow!(
"Shell configuration top_k must be between 1 and 1000, got: {}",
self.shell.top_k
));
}
for role in &self.roles {
if role.config.temperature < 0.0 || role.config.temperature > 2.0 {
return Err(anyhow!(
"Role '{}' temperature must be between 0.0 and 2.0, got: {}",
role.name,
role.config.temperature
));
}
if role.config.top_p < 0.0 || role.config.top_p > 1.0 {
return Err(anyhow!(
"Role '{}' top_p must be between 0.0 and 1.0, got: {}",
role.name,
role.config.top_p
));
}
if role.config.top_k < 1 || role.config.top_k > 1000 {
return Err(anyhow!(
"Role '{}' top_k must be between 1 and 1000, got: {}",
role.name,
role.config.top_k
));
}
}
if let Some(layers) = &self.layers {
for layer in layers {
if layer.temperature < 0.0 || layer.temperature > 2.0 {
return Err(anyhow!(
"Layer '{}' temperature must be between 0.0 and 2.0, got: {}",
layer.name,
layer.temperature
));
}
if layer.top_p < 0.0 || layer.top_p > 1.0 {
return Err(anyhow!(
"Layer '{}' top_p must be between 0.0 and 1.0, got: {}",
layer.name,
layer.top_p
));
}
if layer.top_k < 1 || layer.top_k > 1000 {
return Err(anyhow!(
"Layer '{}' top_k must be between 1 and 1000, got: {}",
layer.name,
layer.top_k
));
}
}
}
Ok(())
}
pub fn validate_thresholds(&self) -> Result<()> {
if self.cache_tokens_threshold > 1_000_000 {
return Err(anyhow!(
"Cache tokens threshold too high: {}. Maximum allowed: 1,000,000",
self.cache_tokens_threshold
));
}
if self.mcp_response_warning_threshold > 1_000_000 {
return Err(anyhow!(
"MCP response warning threshold too high: {}. Maximum allowed: 1,000,000",
self.mcp_response_warning_threshold
));
}
if self.max_session_tokens_threshold > 2_000_000 {
return Err(anyhow!(
"Max session tokens threshold too high: {}. Maximum allowed: 2,000,000",
self.max_session_tokens_threshold
));
}
if self.cache_timeout_seconds > 86400 {
return Err(anyhow!(
"Cache timeout too high: {} seconds. Maximum allowed: 86400 (24 hours)",
self.cache_timeout_seconds
));
}
Ok(())
}
fn validate_mcp_config(&self) -> Result<()> {
for server_config in &self.mcp.servers {
let server_name = &server_config.name();
if server_config.timeout_seconds() == 0 {
return Err(anyhow!(
"Server '{}' has invalid timeout: 0. Must be greater than 0",
server_name
));
}
if server_config.timeout_seconds() > 3600 {
return Err(anyhow!(
"Server '{}' timeout too high: {} seconds. Maximum allowed: 3600 (1 hour)",
server_name,
server_config.timeout_seconds()
));
}
if matches!(
server_config.connection_type(),
crate::config::McpConnectionType::Http
) {
if server_config.url().is_none() && server_config.command().is_none() {
return Err(anyhow!(
"External server '{}' must have either 'url' or 'command' specified",
server_name
));
}
if server_config.url().is_some() && server_config.command().is_some() {
return Err(anyhow!(
"External server '{}' cannot have both 'url' and 'command' specified",
server_name
));
}
}
}
Ok(())
}
fn validate_layers(&self, layers: &[crate::session::layers::LayerConfig]) -> Result<()> {
for (index, layer) in layers.iter().enumerate() {
if layer.name.is_empty() {
return Err(anyhow!("Layer at index {} has empty name", index));
}
if layer.description.is_empty() {
return Err(anyhow!(
"Layer '{}' at index {} has empty description",
layer.name,
index
));
}
}
Ok(())
}
}