use crate::error::RuntimeError;
use iron_providers::{GenerationConfig, ToolPolicy};
pub use crate::context::config::ContextManagementConfig;
pub use crate::prompt::config::PromptCompositionConfig;
pub trait ConfigSource {
fn to_config(&self) -> Result<Config, RuntimeError>;
}
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
pub max_iterations: u32,
pub default_approval_strategy: ApprovalStrategy,
pub context_window_policy: ContextWindowPolicy,
pub model: String,
pub provider_name: Option<String>,
pub default_generation: GenerationConfig,
pub default_tool_policy: ToolPolicy,
pub context_management: ContextManagementConfig,
pub embedded_python: EmbeddedPythonConfig,
pub prompt_composition: PromptCompositionConfig,
pub mcp: McpConfig,
pub plugins: PluginConfig,
pub skills: SkillConfig,
pub workspace_roots: Vec<std::path::PathBuf>,
}
impl Default for Config {
fn default() -> Self {
Self {
max_iterations: 10,
default_approval_strategy: ApprovalStrategy::PerTool,
context_window_policy: ContextWindowPolicy::default(),
model: "gpt-4o".to_string(),
provider_name: None,
default_generation: GenerationConfig::default(),
default_tool_policy: ToolPolicy::Auto,
context_management: ContextManagementConfig::default(),
embedded_python: EmbeddedPythonConfig::default(),
prompt_composition: PromptCompositionConfig::default(),
mcp: McpConfig::default(),
plugins: PluginConfig::default(),
skills: SkillConfig::default(),
workspace_roots: Vec::new(),
}
}
}
impl Config {
pub fn new() -> Self {
Self::default()
}
pub fn with_max_iterations(mut self, max: u32) -> Self {
self.max_iterations = max;
self
}
pub fn with_approval_strategy(mut self, strategy: ApprovalStrategy) -> Self {
self.default_approval_strategy = strategy;
self
}
pub fn with_context_window_policy(mut self, policy: ContextWindowPolicy) -> Self {
self.context_window_policy = policy;
self
}
pub fn with_model<S: Into<String>>(mut self, model: S) -> Self {
self.model = model.into();
self
}
pub fn with_provider_name<S: Into<String>>(mut self, name: S) -> Self {
self.provider_name = Some(name.into());
self
}
pub fn with_default_generation(mut self, generation: GenerationConfig) -> Self {
self.default_generation = generation;
self
}
pub fn with_default_tool_policy(mut self, policy: ToolPolicy) -> Self {
self.default_tool_policy = policy;
self
}
pub fn with_context_management(mut self, config: ContextManagementConfig) -> Self {
self.context_management = config;
self
}
pub fn with_embedded_python(mut self, config: EmbeddedPythonConfig) -> Self {
self.embedded_python = config;
self
}
pub fn with_prompt_composition(mut self, config: PromptCompositionConfig) -> Self {
self.prompt_composition = config;
self
}
pub fn with_embedded_python_enabled(mut self) -> Self {
self.embedded_python.enabled = true;
self
}
pub fn with_mcp(mut self, mcp: McpConfig) -> Self {
self.mcp = mcp;
self
}
pub fn with_plugins(mut self, plugins: PluginConfig) -> Self {
self.plugins = plugins;
self
}
pub fn with_skills(mut self, skills: SkillConfig) -> Self {
self.skills = skills;
self
}
pub fn with_workspace_roots(mut self, roots: Vec<std::path::PathBuf>) -> Self {
self.workspace_roots = roots;
self
}
pub fn validate(&self) -> Result<(), RuntimeError> {
if self.model.trim().is_empty() {
return Err(RuntimeError::invalid_config(
"Config model is required but was empty",
));
}
if self.max_iterations == 0 {
return Err(RuntimeError::invalid_config(
"Config max_iterations must be greater than 0",
));
}
if let Some(temp) = self.default_generation.temperature {
if !(0.0..=2.0).contains(&temp) {
return Err(RuntimeError::invalid_config(format!(
"Config default temperature must be between 0.0 and 2.0, got {}",
temp
)));
}
}
self.context_management
.validate()
.map_err(RuntimeError::invalid_config)?;
if self.embedded_python.enabled {
self.embedded_python.validate()?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ApprovalStrategy {
Always,
Never,
#[default]
PerTool,
AutoApprove,
}
impl ApprovalStrategy {
pub fn is_approval_required(self, tool_requires_approval: bool) -> bool {
match self {
ApprovalStrategy::Always => true,
ApprovalStrategy::Never => false,
ApprovalStrategy::PerTool => tool_requires_approval,
ApprovalStrategy::AutoApprove => tool_requires_approval,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ContextWindowPolicy {
#[default]
KeepAll,
KeepRecent(usize),
}
impl ContextWindowPolicy {
pub fn apply<T>(&self, messages: &mut Vec<T>, _summarize_fn: impl FnOnce(&[T]) -> T) {
match self {
ContextWindowPolicy::KeepAll => {
}
ContextWindowPolicy::KeepRecent(n) => {
if messages.len() > *n {
let start = messages.len() - *n;
*messages = messages.split_off(start);
}
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct EmbeddedPythonConfig {
pub enabled: bool,
pub max_script_timeout_secs: u64,
pub max_source_bytes: usize,
pub max_result_bytes: usize,
pub max_child_calls: usize,
pub max_child_outcome_items: usize,
}
impl Default for EmbeddedPythonConfig {
fn default() -> Self {
Self {
enabled: false,
max_script_timeout_secs: 30,
max_source_bytes: 32 * 1024,
max_result_bytes: 64 * 1024,
max_child_calls: 20,
max_child_outcome_items: 20,
}
}
}
impl EmbeddedPythonConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_timeout(mut self, secs: u64) -> Self {
self.max_script_timeout_secs = secs;
self
}
pub fn with_max_source_bytes(mut self, bytes: usize) -> Self {
self.max_source_bytes = bytes;
self
}
pub fn with_max_result_bytes(mut self, bytes: usize) -> Self {
self.max_result_bytes = bytes;
self
}
pub fn with_max_child_calls(mut self, n: usize) -> Self {
self.max_child_calls = n;
self
}
pub fn validate(&self) -> Result<(), RuntimeError> {
if self.max_script_timeout_secs == 0 {
return Err(RuntimeError::invalid_config(
"EmbeddedPythonConfig max_script_timeout_secs must be > 0",
));
}
if self.max_source_bytes == 0 {
return Err(RuntimeError::invalid_config(
"EmbeddedPythonConfig max_source_bytes must be > 0",
));
}
if self.max_result_bytes == 0 {
return Err(RuntimeError::invalid_config(
"EmbeddedPythonConfig max_result_bytes must be > 0",
));
}
if self.max_child_calls == 0 {
return Err(RuntimeError::invalid_config(
"EmbeddedPythonConfig max_child_calls must be > 0",
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct McpConfig {
pub enabled: bool,
pub enabled_by_default: bool,
}
impl Default for McpConfig {
fn default() -> Self {
Self {
enabled: false,
enabled_by_default: true,
}
}
}
impl McpConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_enabled_by_default(mut self, enabled_by_default: bool) -> Self {
self.enabled_by_default = enabled_by_default;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PluginConfig {
pub enabled: bool,
pub enabled_by_default: bool,
pub artifact_cache_dir: std::path::PathBuf,
pub max_memory_bytes: u64,
}
impl Default for PluginConfig {
fn default() -> Self {
Self {
enabled: false,
enabled_by_default: true,
artifact_cache_dir: std::path::PathBuf::from("./plugin_cache"),
max_memory_bytes: crate::plugin::wasm_host::DEFAULT_PLUGIN_MAX_MEMORY_BYTES,
}
}
}
impl PluginConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_enabled_by_default(mut self, enabled_by_default: bool) -> Self {
self.enabled_by_default = enabled_by_default;
self
}
pub fn with_artifact_cache_dir(mut self, dir: std::path::PathBuf) -> Self {
self.artifact_cache_dir = dir;
self
}
pub fn with_max_memory_bytes(mut self, bytes: u64) -> Self {
self.max_memory_bytes = bytes;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SkillConfig {
pub enabled: bool,
pub trust_project_skills: bool,
pub additional_skill_dirs: Vec<std::path::PathBuf>,
}
impl Default for SkillConfig {
fn default() -> Self {
Self {
enabled: true,
trust_project_skills: false,
additional_skill_dirs: Vec::new(),
}
}
}
impl SkillConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_trust_project_skills(mut self, trust: bool) -> Self {
self.trust_project_skills = trust;
self
}
pub fn with_additional_skill_dir(mut self, dir: std::path::PathBuf) -> Self {
self.additional_skill_dirs.push(dir);
self
}
}
pub mod builtin_models;
pub mod effective_catalog;
pub mod error;
pub mod records;
pub mod store;
pub mod crypto;
mod db;
mod key_source;
pub mod migrations;
#[cfg(test)]
mod automation_task_tests;
#[cfg(test)]
mod scheduled_task_tests;
pub use builtin_models::{builtin_model_catalog, BuiltinModelEntry};
pub use effective_catalog::{
build_effective_catalog, CatalogError, EffectiveModelCatalog, EffectiveModelEntry,
};
pub use error::ConfigError;
pub use records::{
BootstrapMetadataInput, BootstrapMetadataRecord, CredentialRecord, CustomModelInput,
CustomModelRecord, DefaultModelInput, DefaultModelRecord, McpServerConfigInput,
McpServerConfigRecord, ProfileInput, ProfileRecord, PromptInput, PromptRecord,
ProviderConfigInput, ProviderConfigRecord, ProviderProfileInput, ProviderProfileRecord,
RuntimeSettingsSnapshot, SavedHandoffInput, SavedHandoffMetadata, SavedHandoffRecord,
ScheduleInput, ScheduleRecord, SkillSettingsInput, SkillSettingsRecord,
};
pub use store::{default_config_path, ConfigStore, OpenOptions};