1use serde::Deserialize;
2use std::path::Path;
3
4#[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 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_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}