1use crate::types::*;
6use crate::Result;
7use async_trait::async_trait;
8use std::collections::HashMap;
9use std::sync::{Arc, RwLock};
10use tracing::{debug, info};
11
12#[derive(Debug, Clone)]
14pub struct CoordinationMessage {
15 pub id: uuid::Uuid,
17
18 pub from_agent_id: uuid::Uuid,
20
21 pub to_agent_id: uuid::Uuid,
23
24 pub message_type: CoordinationMessageType,
26
27 pub content: serde_json::Value,
29
30 pub priority: i32,
32
33 pub timestamp: i64,
35
36 pub requires_response: bool,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum CoordinationMessageType {
43 HelpRequest,
45
46 HelpOffer,
48
49 InformationShare,
51
52 TaskDelegation,
54
55 CapabilityQuery,
57
58 StatusUpdate,
60
61 Generic,
63}
64
65#[derive(Debug, Clone)]
67pub struct AgentCapability {
68 pub agent_id: uuid::Uuid,
70
71 pub name: String,
73
74 pub description: String,
76
77 pub proficiency: f32,
79
80 pub availability: f32,
82}
83
84pub struct MultiAgentCoordinator {
86 agents: Arc<RwLock<HashMap<uuid::Uuid, AgentInfo>>>,
88
89 messages: Arc<RwLock<Vec<CoordinationMessage>>>,
91
92 capabilities: Arc<RwLock<HashMap<uuid::Uuid, Vec<AgentCapability>>>>,
94}
95
96#[derive(Debug, Clone)]
98pub struct AgentInfo {
99 pub id: uuid::Uuid,
101
102 pub name: String,
104
105 pub status: AgentStatus,
107
108 pub load: f32,
110
111 pub last_heartbeat: i64,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub enum AgentStatus {
118 Online,
120
121 Busy,
123
124 Idle,
126
127 Offline,
129}
130
131impl MultiAgentCoordinator {
132 pub fn new() -> Self {
134 Self {
135 agents: Arc::new(RwLock::new(HashMap::new())),
136 messages: Arc::new(RwLock::new(Vec::new())),
137 capabilities: Arc::new(RwLock::new(HashMap::new())),
138 }
139 }
140
141 pub fn register_agent(&self, agent_id: uuid::Uuid, name: String) -> Result<()> {
143 info!("Registering agent {} ({})", name, agent_id);
144
145 let info = AgentInfo {
146 id: agent_id,
147 name,
148 status: AgentStatus::Online,
149 load: 0.0,
150 last_heartbeat: chrono::Utc::now().timestamp(),
151 };
152
153 self.agents.write().unwrap().insert(agent_id, info);
154
155 Ok(())
156 }
157
158 pub fn unregister_agent(&self, agent_id: uuid::Uuid) -> Result<()> {
160 info!("Unregistering agent {}", agent_id);
161 self.agents.write().unwrap().remove(&agent_id);
162 Ok(())
163 }
164
165 pub fn send_message(&self, message: CoordinationMessage) -> Result<()> {
167 debug!(
168 "Sending coordination message: {} -> {}",
169 message.from_agent_id, message.to_agent_id
170 );
171
172 self.messages.write().unwrap().push(message);
173
174 Ok(())
175 }
176
177 pub fn get_messages(&self, agent_id: uuid::Uuid) -> Vec<CoordinationMessage> {
179 let mut messages = self.messages.write().unwrap();
180
181 let agent_messages: Vec<_> = messages
183 .iter()
184 .filter(|m| m.to_agent_id == agent_id)
185 .cloned()
186 .collect();
187
188 messages.retain(|m| m.to_agent_id != agent_id);
190
191 agent_messages
192 }
193
194 pub fn register_capability(&self, capability: AgentCapability) -> Result<()> {
196 debug!(
197 "Registering capability {} for agent {}",
198 capability.name, capability.agent_id
199 );
200
201 self.capabilities
202 .write()
203 .unwrap()
204 .entry(capability.agent_id)
205 .or_insert_with(Vec::new)
206 .push(capability);
207
208 Ok(())
209 }
210
211 pub fn find_agents_with_capability(&self, capability_name: &str) -> Vec<(uuid::Uuid, f32)> {
213 let capabilities = self.capabilities.read().unwrap();
214 let agents = self.agents.read().unwrap();
215
216 let mut matches = Vec::new();
217
218 for (agent_id, caps) in capabilities.iter() {
219 if let Some(agent_info) = agents.get(agent_id) {
220 if agent_info.status == AgentStatus::Online
222 || agent_info.status == AgentStatus::Idle
223 {
224 for cap in caps {
225 if cap.name == capability_name {
226 let score =
227 cap.proficiency * cap.availability * (1.0 - agent_info.load);
228 matches.push((*agent_id, score));
229 }
230 }
231 }
232 }
233 }
234
235 matches.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
237
238 matches
239 }
240
241 pub async fn request_help(
243 &self,
244 from_agent_id: uuid::Uuid,
245 capability_needed: &str,
246 request_data: serde_json::Value,
247 ) -> Result<Option<uuid::Uuid>> {
248 let candidates = self.find_agents_with_capability(capability_needed);
250
251 if let Some((best_agent_id, score)) = candidates.first() {
252 info!(
253 "Found agent {} for capability {} (score: {})",
254 best_agent_id, capability_needed, score
255 );
256
257 let message = CoordinationMessage {
259 id: uuid::Uuid::new_v4(),
260 from_agent_id,
261 to_agent_id: *best_agent_id,
262 message_type: CoordinationMessageType::HelpRequest,
263 content: request_data,
264 priority: 5,
265 timestamp: chrono::Utc::now().timestamp(),
266 requires_response: true,
267 };
268
269 self.send_message(message)?;
270
271 Ok(Some(*best_agent_id))
272 } else {
273 debug!("No agents found with capability {}", capability_needed);
274 Ok(None)
275 }
276 }
277
278 pub fn update_agent_status(
280 &self,
281 agent_id: uuid::Uuid,
282 status: AgentStatus,
283 load: f32,
284 ) -> Result<()> {
285 if let Some(agent) = self.agents.write().unwrap().get_mut(&agent_id) {
286 agent.status = status;
287 agent.load = load;
288 agent.last_heartbeat = chrono::Utc::now().timestamp();
289 }
290 Ok(())
291 }
292
293 pub fn get_active_agents(&self) -> Vec<AgentInfo> {
295 self.agents
296 .read()
297 .unwrap()
298 .values()
299 .filter(|a| a.status != AgentStatus::Offline)
300 .cloned()
301 .collect()
302 }
303
304 pub fn broadcast(
306 &self,
307 from_agent_id: uuid::Uuid,
308 content: serde_json::Value,
309 ) -> Result<usize> {
310 let agents = self.get_active_agents();
311 let mut sent = 0;
312
313 for agent in agents {
314 if agent.id != from_agent_id {
315 let message = CoordinationMessage {
316 id: uuid::Uuid::new_v4(),
317 from_agent_id,
318 to_agent_id: agent.id,
319 message_type: CoordinationMessageType::InformationShare,
320 content: content.clone(),
321 priority: 3,
322 timestamp: chrono::Utc::now().timestamp(),
323 requires_response: false,
324 };
325
326 self.send_message(message)?;
327 sent += 1;
328 }
329 }
330
331 Ok(sent)
332 }
333}
334
335impl Default for MultiAgentCoordinator {
336 fn default() -> Self {
337 Self::new()
338 }
339}
340
341pub struct MultiAgentService {
343 coordinator: Arc<MultiAgentCoordinator>,
344 agent_id: uuid::Uuid,
345}
346
347impl MultiAgentService {
348 pub fn new(coordinator: Arc<MultiAgentCoordinator>, agent_id: uuid::Uuid) -> Self {
350 Self {
351 coordinator,
352 agent_id,
353 }
354 }
355
356 pub async fn request_help(
358 &self,
359 capability: &str,
360 data: serde_json::Value,
361 ) -> Result<Option<uuid::Uuid>> {
362 self.coordinator
363 .request_help(self.agent_id, capability, data)
364 .await
365 }
366
367 pub fn offer_capability(
369 &self,
370 name: String,
371 description: String,
372 proficiency: f32,
373 ) -> Result<()> {
374 let capability = AgentCapability {
375 agent_id: self.agent_id,
376 name,
377 description,
378 proficiency,
379 availability: 1.0,
380 };
381
382 self.coordinator.register_capability(capability)
383 }
384
385 pub fn get_messages(&self) -> Vec<CoordinationMessage> {
387 self.coordinator.get_messages(self.agent_id)
388 }
389
390 pub fn update_status(&self, status: AgentStatus, load: f32) -> Result<()> {
392 self.coordinator
393 .update_agent_status(self.agent_id, status, load)
394 }
395
396 pub fn find_agents(&self, capability: &str) -> Vec<(uuid::Uuid, f32)> {
398 self.coordinator.find_agents_with_capability(capability)
399 }
400}
401
402#[async_trait]
403impl Service for MultiAgentService {
404 fn service_type(&self) -> &str {
405 "multi-agent-coordination"
406 }
407
408 async fn initialize(&mut self, _runtime: Arc<dyn std::any::Any + Send + Sync>) -> Result<()> {
409 info!("Multi-agent coordination service initialized");
410 Ok(())
411 }
412
413 async fn start(&mut self) -> Result<()> {
414 info!("Multi-agent coordination service started");
415 Ok(())
416 }
417
418 fn query_agents(&self, capability: &str) -> Option<Vec<(uuid::Uuid, f32)>> {
419 Some(self.coordinator.find_agents_with_capability(capability))
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 #[test]
428 fn test_coordinator_creation() {
429 let coordinator = MultiAgentCoordinator::new();
430 assert_eq!(coordinator.get_active_agents().len(), 0);
431 }
432
433 #[test]
434 fn test_agent_registration() {
435 let coordinator = MultiAgentCoordinator::new();
436
437 let agent_id = uuid::Uuid::new_v4();
438 coordinator
439 .register_agent(agent_id, "TestAgent".to_string())
440 .unwrap();
441
442 assert_eq!(coordinator.get_active_agents().len(), 1);
443 }
444
445 #[test]
446 fn test_capability_registration() {
447 let coordinator = MultiAgentCoordinator::new();
448
449 let agent_id = uuid::Uuid::new_v4();
450 coordinator
451 .register_agent(agent_id, "Agent1".to_string())
452 .unwrap();
453
454 let capability = AgentCapability {
455 agent_id,
456 name: "code_generation".to_string(),
457 description: "Can generate code".to_string(),
458 proficiency: 0.9,
459 availability: 1.0,
460 };
461
462 coordinator.register_capability(capability).unwrap();
463
464 let matches = coordinator.find_agents_with_capability("code_generation");
465 assert_eq!(matches.len(), 1);
466 assert_eq!(matches[0].0, agent_id);
467 }
468
469 #[tokio::test]
470 async fn test_help_request() {
471 let coordinator = MultiAgentCoordinator::new();
472
473 let agent1 = uuid::Uuid::new_v4();
474 let agent2 = uuid::Uuid::new_v4();
475
476 coordinator
477 .register_agent(agent1, "Agent1".to_string())
478 .unwrap();
479 coordinator
480 .register_agent(agent2, "Agent2".to_string())
481 .unwrap();
482
483 let capability = AgentCapability {
485 agent_id: agent2,
486 name: "translation".to_string(),
487 description: "Can translate text".to_string(),
488 proficiency: 0.95,
489 availability: 1.0,
490 };
491 coordinator.register_capability(capability).unwrap();
492
493 let result = coordinator
495 .request_help(
496 agent1,
497 "translation",
498 serde_json::json!({"text": "Hello", "to_lang": "Spanish"}),
499 )
500 .await
501 .unwrap();
502
503 assert!(result.is_some());
504 assert_eq!(result.unwrap(), agent2);
505
506 let messages = coordinator.get_messages(agent2);
508 assert_eq!(messages.len(), 1);
509 assert_eq!(
510 messages[0].message_type,
511 CoordinationMessageType::HelpRequest
512 );
513 }
514
515 #[test]
516 fn test_broadcast() {
517 let coordinator = MultiAgentCoordinator::new();
518
519 let agent1 = uuid::Uuid::new_v4();
520 let agent2 = uuid::Uuid::new_v4();
521 let agent3 = uuid::Uuid::new_v4();
522
523 coordinator
524 .register_agent(agent1, "Agent1".to_string())
525 .unwrap();
526 coordinator
527 .register_agent(agent2, "Agent2".to_string())
528 .unwrap();
529 coordinator
530 .register_agent(agent3, "Agent3".to_string())
531 .unwrap();
532
533 let sent = coordinator
534 .broadcast(agent1, serde_json::json!({"info": "test"}))
535 .unwrap();
536
537 assert_eq!(sent, 2); assert_eq!(coordinator.get_messages(agent2).len(), 1);
540 assert_eq!(coordinator.get_messages(agent3).len(), 1);
541 assert_eq!(coordinator.get_messages(agent1).len(), 0);
542 }
543}