ricecoder_storage/markdown_config/integration/
agents.rs1use crate::markdown_config::error::MarkdownConfigResult;
4use crate::markdown_config::loader::{ConfigFile, ConfigFileType, ConfigurationLoader};
5use crate::markdown_config::types::AgentConfig;
6use std::path::PathBuf;
7use std::sync::Arc;
8use tracing::{debug, info, warn};
9
10pub type RegistrationResult = (usize, usize, Vec<(String, String)>);
12
13pub trait AgentRegistrar: Send + Sync {
18 fn register_agent(&mut self, agent: AgentConfig) -> Result<(), String>;
20}
21
22pub struct AgentConfigIntegration {
27 loader: Arc<ConfigurationLoader>,
28}
29
30impl AgentConfigIntegration {
31 pub fn new(loader: Arc<ConfigurationLoader>) -> Self {
33 Self { loader }
34 }
35
36 pub fn discover_agent_configs(&self, paths: &[PathBuf]) -> MarkdownConfigResult<Vec<ConfigFile>> {
44 let all_files = self.loader.discover(paths)?;
45
46 let agent_files: Vec<ConfigFile> = all_files
48 .into_iter()
49 .filter(|f| f.config_type == ConfigFileType::Agent)
50 .collect();
51
52 debug!("Discovered {} agent configuration files", agent_files.len());
53 Ok(agent_files)
54 }
55
56 pub async fn load_agent_configs(
64 &self,
65 paths: &[PathBuf],
66 ) -> MarkdownConfigResult<(Vec<AgentConfig>, Vec<(PathBuf, String)>)> {
67 let files = self.discover_agent_configs(paths)?;
68
69 let mut agents = Vec::new();
70 let mut errors = Vec::new();
71
72 for file in files {
73 match self.loader.load(&file).await {
74 Ok(config) => {
75 match config {
76 crate::markdown_config::loader::LoadedConfig::Agent(agent) => {
77 debug!("Loaded agent configuration: {}", agent.name);
78 agents.push(agent);
79 }
80 _ => {
81 warn!("Expected agent configuration but got different type from {}", file.path.display());
82 errors.push((
83 file.path,
84 "Expected agent configuration but got different type".to_string(),
85 ));
86 }
87 }
88 }
89 Err(e) => {
90 let error_msg = e.to_string();
91 warn!("Failed to load agent configuration from {}: {}", file.path.display(), error_msg);
92 errors.push((file.path, error_msg));
93 }
94 }
95 }
96
97 info!("Loaded {} agent configurations", agents.len());
98 Ok((agents, errors))
99 }
100
101 pub fn register_agents(
113 &self,
114 agents: Vec<AgentConfig>,
115 registrar: &mut dyn AgentRegistrar,
116 ) -> MarkdownConfigResult<RegistrationResult> {
117 let mut success_count = 0;
118 let mut error_count = 0;
119 let mut errors = Vec::new();
120
121 for agent in agents {
122 if let Err(e) = agent.validate() {
124 error_count += 1;
125 let error_msg = format!("Invalid agent configuration: {}", e);
126 warn!("Failed to register agent '{}': {}", agent.name, error_msg);
127 errors.push((agent.name.clone(), error_msg));
128 continue;
129 }
130
131 debug!("Registering agent: {}", agent.name);
132
133 match registrar.register_agent(agent.clone()) {
135 Ok(_) => {
136 success_count += 1;
137 info!("Registered agent: {}", agent.name);
138 }
139 Err(e) => {
140 error_count += 1;
141 warn!("Failed to register agent '{}': {}", agent.name, e);
142 errors.push((agent.name.clone(), e));
143 }
144 }
145 }
146
147 debug!(
148 "Agent registration complete: {} successful, {} failed",
149 success_count, error_count
150 );
151
152 Ok((success_count, error_count, errors))
153 }
154
155 pub async fn load_and_register_agents(
164 &self,
165 paths: &[PathBuf],
166 registrar: &mut dyn AgentRegistrar,
167 ) -> MarkdownConfigResult<(usize, usize, Vec<(String, String)>)> {
168 let (agents, load_errors) = self.load_agent_configs(paths).await?;
169
170 let (success, errors, mut reg_errors) = self.register_agents(agents, registrar)?;
171
172 for (path, msg) in load_errors {
174 reg_errors.push((path.display().to_string(), msg));
175 }
176
177 Ok((success, errors, reg_errors))
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use crate::markdown_config::registry::ConfigRegistry;
185 use std::fs;
186 use tempfile::TempDir;
187
188 fn create_test_agent_file(dir: &PathBuf, name: &str, content: &str) -> PathBuf {
189 let path = dir.join(format!("{}.agent.md", name));
190 fs::write(&path, content).unwrap();
191 path
192 }
193
194 #[test]
195 fn test_discover_agent_configs() {
196 let temp_dir = TempDir::new().unwrap();
197 let dir_path = temp_dir.path().to_path_buf();
198
199 create_test_agent_file(&dir_path, "agent1", "---\nname: agent1\n---\nTest");
201 create_test_agent_file(&dir_path, "agent2", "---\nname: agent2\n---\nTest");
202
203 fs::write(dir_path.join("mode1.mode.md"), "---\nname: mode1\n---\nTest").unwrap();
205
206 let registry = Arc::new(ConfigRegistry::new());
207 let loader = Arc::new(ConfigurationLoader::new(registry));
208 let integration = AgentConfigIntegration::new(loader);
209
210 let discovered = integration.discover_agent_configs(&[dir_path]).unwrap();
211
212 assert_eq!(discovered.len(), 2);
213 assert!(discovered.iter().all(|f| f.config_type == ConfigFileType::Agent));
214 }
215
216 #[tokio::test]
217 async fn test_load_agent_configs() {
218 let temp_dir = TempDir::new().unwrap();
219 let dir_path = temp_dir.path().to_path_buf();
220
221 let agent_content = r#"---
222name: test-agent
223description: A test agent
224model: gpt-4
225temperature: 0.7
226max_tokens: 2000
227---
228You are a helpful assistant"#;
229
230 create_test_agent_file(&dir_path, "test-agent", agent_content);
231
232 let registry = Arc::new(ConfigRegistry::new());
233 let loader = Arc::new(ConfigurationLoader::new(registry));
234 let integration = AgentConfigIntegration::new(loader);
235
236 let (agents, errors) = integration.load_agent_configs(&[dir_path]).await.unwrap();
237
238 assert_eq!(agents.len(), 1);
239 assert_eq!(errors.len(), 0);
240 assert_eq!(agents[0].name, "test-agent");
241 assert_eq!(agents[0].model, Some("gpt-4".to_string()));
242 }
243
244 #[tokio::test]
245 async fn test_load_agent_configs_with_errors() {
246 let temp_dir = TempDir::new().unwrap();
247 let dir_path = temp_dir.path().to_path_buf();
248
249 let valid_content = r#"---
251name: valid-agent
252---
253Valid agent"#;
254 create_test_agent_file(&dir_path, "valid-agent", valid_content);
255
256 fs::write(dir_path.join("invalid.agent.md"), "# No frontmatter\nJust markdown").unwrap();
258
259 let registry = Arc::new(ConfigRegistry::new());
260 let loader = Arc::new(ConfigurationLoader::new(registry));
261 let integration = AgentConfigIntegration::new(loader);
262
263 let (agents, errors) = integration.load_agent_configs(&[dir_path]).await.unwrap();
264
265 assert_eq!(agents.len(), 1);
266 assert_eq!(errors.len(), 1);
267 assert_eq!(agents[0].name, "valid-agent");
268 }
269
270 #[test]
271 fn test_register_agents() {
272 let registry = Arc::new(ConfigRegistry::new());
273 let loader = Arc::new(ConfigurationLoader::new(registry));
274 let integration = AgentConfigIntegration::new(loader);
275
276 let agents = vec![
277 AgentConfig {
278 name: "agent1".to_string(),
279 description: Some("Test agent 1".to_string()),
280 prompt: "You are agent 1".to_string(),
281 model: Some("gpt-4".to_string()),
282 temperature: Some(0.7),
283 max_tokens: Some(2000),
284 tools: vec![],
285 },
286 AgentConfig {
287 name: "agent2".to_string(),
288 description: Some("Test agent 2".to_string()),
289 prompt: "You are agent 2".to_string(),
290 model: None,
291 temperature: None,
292 max_tokens: None,
293 tools: vec![],
294 },
295 ];
296
297 struct MockRegistrar;
298 impl AgentRegistrar for MockRegistrar {
299 fn register_agent(&mut self, _agent: AgentConfig) -> Result<(), String> {
300 Ok(())
301 }
302 }
303
304 let mut registrar = MockRegistrar;
305 let (success, errors, error_list) = integration
306 .register_agents(agents, &mut registrar)
307 .unwrap();
308
309 assert_eq!(success, 2);
310 assert_eq!(errors, 0);
311 assert_eq!(error_list.len(), 0);
312 }
313
314 #[test]
315 fn test_register_invalid_agent() {
316 let registry = Arc::new(ConfigRegistry::new());
317 let loader = Arc::new(ConfigurationLoader::new(registry));
318 let integration = AgentConfigIntegration::new(loader);
319
320 let agents = vec![
321 AgentConfig {
322 name: String::new(), description: None,
324 prompt: "Test".to_string(),
325 model: None,
326 temperature: None,
327 max_tokens: None,
328 tools: vec![],
329 },
330 ];
331
332 struct MockRegistrar;
333 impl AgentRegistrar for MockRegistrar {
334 fn register_agent(&mut self, _agent: AgentConfig) -> Result<(), String> {
335 Ok(())
336 }
337 }
338
339 let mut registrar = MockRegistrar;
340 let (success, errors, error_list) = integration
341 .register_agents(agents, &mut registrar)
342 .unwrap();
343
344 assert_eq!(success, 0);
345 assert_eq!(errors, 1);
346 assert_eq!(error_list.len(), 1);
347 }
348}