Skip to main content

cascade_agent/
config.rs

1use serde::Deserialize;
2use std::path::Path;
3
4/// Top-level agent configuration loaded from TOML.
5#[derive(Debug, Deserialize, Clone)]
6pub struct AgentConfig {
7    pub agent: AgentSettings,
8    pub memory: MemorySettings,
9    pub knowledge: KnowledgeSettings,
10    pub orchestrator: OrchestratorSettings,
11    pub search: SearchSettings,
12    pub paths: PathSettings,
13}
14
15#[derive(Debug, Deserialize, Clone)]
16pub struct AgentSettings {
17    pub cascade_name: String,
18    pub cascade_config_path: String,
19    #[serde(default = "default_max_tool_rounds")]
20    pub max_tool_rounds: usize,
21    pub soul_md_path: String,
22}
23
24#[derive(Debug, Deserialize, Clone)]
25pub struct MemorySettings {
26    pub context_token_limit: usize,
27    #[serde(default = "default_compaction_ratio")]
28    pub compaction_target_ratio: f64,
29    pub summarization_cascade: String,
30    pub tokenizer_model: String,
31}
32
33#[derive(Debug, Deserialize, Clone)]
34pub struct KnowledgeSettings {
35    pub db_path: String,
36    #[serde(default = "default_embedding_model")]
37    pub embedding_model: String,
38    #[serde(default = "default_collection")]
39    pub default_collection: String,
40    #[serde(default = "default_similarity_threshold")]
41    pub similarity_threshold: f32,
42    #[serde(default = "default_max_results")]
43    pub max_results: usize,
44}
45
46#[derive(Debug, Deserialize, Clone)]
47pub struct OrchestratorSettings {
48    #[serde(default)]
49    pub enabled: bool,
50    #[serde(default = "default_transport")]
51    pub transport: String,
52    #[serde(default = "default_bind_address")]
53    pub bind_address: String,
54    pub connect_url: Option<String>,
55}
56
57#[derive(Debug, Deserialize, Clone)]
58pub struct SearchSettings {
59    #[serde(default = "default_tavily_env")]
60    pub tavily_api_key_env: String,
61    #[serde(default = "default_brave_env")]
62    pub brave_api_key_env: String,
63    #[serde(default = "default_max_results")]
64    pub max_results: usize,
65}
66
67#[derive(Debug, Deserialize, Clone)]
68pub struct PathSettings {
69    pub skills_dir: String,
70    pub plans_dir: String,
71    pub outputs_dir: String,
72}
73
74fn default_max_tool_rounds() -> usize {
75    25
76}
77fn default_compaction_ratio() -> f64 {
78    0.6
79}
80fn default_embedding_model() -> String {
81    "multilingual-e5-base".into()
82}
83fn default_collection() -> String {
84    "general".into()
85}
86fn default_similarity_threshold() -> f32 {
87    0.65
88}
89fn default_max_results() -> usize {
90    5
91}
92fn default_transport() -> String {
93    "websocket".into()
94}
95fn default_bind_address() -> String {
96    "127.0.0.1:9876".into()
97}
98fn default_tavily_env() -> String {
99    "TAVILY_API_KEY".into()
100}
101fn default_brave_env() -> String {
102    "BRAVE_API_KEY".into()
103}
104
105impl AgentConfig {
106    /// Load configuration from a TOML file path.
107    /// Expands `~` in paths.
108    pub fn load(path: &Path) -> crate::error::Result<Self> {
109        let content = std::fs::read_to_string(path).map_err(|e| {
110            crate::error::AgentError::ConfigError(format!(
111                "Cannot read config file {}: {}",
112                path.display(),
113                e
114            ))
115        })?;
116
117        let mut config: AgentConfig = toml::from_str(&content).map_err(|e| {
118            crate::error::AgentError::ConfigError(format!("Failed to parse config: {}", e))
119        })?;
120
121        // Expand ~ in paths
122        expand_home(&mut config.agent.cascade_config_path);
123        expand_home(&mut config.agent.soul_md_path);
124        expand_home(&mut config.knowledge.db_path);
125        expand_home(&mut config.paths.skills_dir);
126        expand_home(&mut config.paths.plans_dir);
127        expand_home(&mut config.paths.outputs_dir);
128
129        Ok(config)
130    }
131}
132
133fn expand_home(s: &mut String) {
134    if let Some(rest) = s.strip_prefix("~/") {
135        if let Some(home) = dirs::home_dir() {
136            *s = format!("{}/{}", home.display(), rest);
137        }
138    }
139}