1use crate::agent::{Agent, AgentBuilder};
10use crate::config::Config;
11use crate::error::{HeliosError, Result};
12use crate::tools::{Tool, ToolParameter, ToolResult};
13use serde_json::Value;
14use std::collections::HashMap;
15use std::sync::Arc;
16use tokio::sync::RwLock;
17
18pub type AgentId = String;
20
21#[derive(Debug, Clone)]
23pub struct ForestMessage {
24 pub from: AgentId,
26 pub to: Option<AgentId>,
28 pub content: String,
30 pub metadata: HashMap<String, String>,
32 pub timestamp: chrono::DateTime<chrono::Utc>,
34}
35
36impl ForestMessage {
37 pub fn new(from: AgentId, to: Option<AgentId>, content: String) -> Self {
39 Self {
40 from,
41 to,
42 content,
43 metadata: HashMap::new(),
44 timestamp: chrono::Utc::now(),
45 }
46 }
47
48 pub fn broadcast(from: AgentId, content: String) -> Self {
50 Self::new(from, None, content)
51 }
52
53 pub fn with_metadata(mut self, key: String, value: String) -> Self {
55 self.metadata.insert(key, value);
56 self
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct SharedContext {
63 pub data: HashMap<String, Value>,
65 pub message_history: Vec<ForestMessage>,
67 pub metadata: HashMap<String, String>,
69}
70
71impl SharedContext {
72 pub fn new() -> Self {
74 Self {
75 data: HashMap::new(),
76 message_history: Vec::new(),
77 metadata: HashMap::new(),
78 }
79 }
80
81 pub fn set(&mut self, key: String, value: Value) {
83 self.data.insert(key, value);
84 }
85
86 pub fn get(&self, key: &str) -> Option<&Value> {
88 self.data.get(key)
89 }
90
91 pub fn remove(&mut self, key: &str) -> Option<Value> {
93 self.data.remove(key)
94 }
95
96 pub fn add_message(&mut self, message: ForestMessage) {
98 self.message_history.push(message);
99 }
100
101 pub fn get_recent_messages(&self, limit: usize) -> &[ForestMessage] {
103 let len = self.message_history.len();
104 let start = len.saturating_sub(limit);
105 &self.message_history[start..]
106 }
107}
108
109impl Default for SharedContext {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115pub struct ForestOfAgents {
117 agents: HashMap<AgentId, Agent>,
119 shared_context: Arc<RwLock<SharedContext>>,
121 message_queue: Arc<RwLock<Vec<ForestMessage>>>,
123 max_iterations: usize,
125}
126
127impl ForestOfAgents {
128 pub fn new() -> Self {
130 Self {
131 agents: HashMap::new(),
132 shared_context: Arc::new(RwLock::new(SharedContext::new())),
133 message_queue: Arc::new(RwLock::new(Vec::new())),
134 max_iterations: 10,
135 }
136 }
137
138 pub fn with_max_iterations(max_iterations: usize) -> Self {
140 Self {
141 max_iterations,
142 ..Self::new()
143 }
144 }
145
146 pub fn add_agent(&mut self, id: AgentId, mut agent: Agent) -> Result<()> {
157 if self.agents.contains_key(&id) {
158 return Err(HeliosError::AgentError(format!(
159 "Agent with ID '{}' already exists",
160 id
161 )));
162 }
163
164 let send_message_tool = Box::new(SendMessageTool::new(
166 id.clone(),
167 Arc::clone(&self.message_queue),
168 Arc::clone(&self.shared_context),
169 ));
170 agent.register_tool(send_message_tool);
171
172 let delegate_task_tool = Box::new(DelegateTaskTool::new(
173 id.clone(),
174 Arc::clone(&self.message_queue),
175 Arc::clone(&self.shared_context),
176 ));
177 agent.register_tool(delegate_task_tool);
178
179 let share_context_tool = Box::new(ShareContextTool::new(
180 id.clone(),
181 Arc::clone(&self.shared_context),
182 ));
183 agent.register_tool(share_context_tool);
184
185 self.agents.insert(id, agent);
186 Ok(())
187 }
188
189 pub fn remove_agent(&mut self, id: &AgentId) -> Option<Agent> {
199 self.agents.remove(id)
200 }
201
202 pub fn get_agent(&self, id: &AgentId) -> Option<&Agent> {
204 self.agents.get(id)
205 }
206
207 pub fn get_agent_mut(&mut self, id: &AgentId) -> Option<&mut Agent> {
209 self.agents.get_mut(id)
210 }
211
212 pub fn list_agents(&self) -> Vec<AgentId> {
214 self.agents.keys().cloned().collect()
215 }
216
217 pub async fn send_message(
229 &self,
230 from: &AgentId,
231 to: Option<&AgentId>,
232 content: String,
233 ) -> Result<()> {
234 if !self.agents.contains_key(from) {
235 return Err(HeliosError::AgentError(format!(
236 "Agent '{}' not found",
237 from
238 )));
239 }
240
241 let message = if let Some(to_id) = to {
242 ForestMessage::new(from.clone(), Some(to_id.clone()), content)
243 } else {
244 ForestMessage::broadcast(from.clone(), content)
245 };
246
247 let mut queue = self.message_queue.write().await;
248 queue.push(message.clone());
249
250 let mut context = self.shared_context.write().await;
252 context.add_message(message);
253
254 Ok(())
255 }
256
257 pub async fn process_messages(&mut self) -> Result<()> {
259 let messages: Vec<ForestMessage> = {
260 let mut queue = self.message_queue.write().await;
261 queue.drain(..).collect()
262 };
263
264 for message in messages {
265 if let Some(to_id) = &message.to {
266 if let Some(agent) = self.agents.get_mut(to_id) {
268 let formatted_message =
270 format!("Message from {}: {}", message.from, message.content);
271 agent.chat_session_mut().add_user_message(formatted_message);
272 }
273 } else {
274 for (agent_id, agent) in &mut self.agents {
276 if agent_id != &message.from {
277 let formatted_message =
278 format!("Broadcast from {}: {}", message.from, message.content);
279 agent.chat_session_mut().add_user_message(formatted_message);
280 }
281 }
282 }
283 }
284
285 Ok(())
286 }
287
288 pub async fn execute_collaborative_task(
300 &mut self,
301 initiator: &AgentId,
302 task_description: String,
303 involved_agents: Vec<AgentId>,
304 ) -> Result<String> {
305 for agent_id in &involved_agents {
307 if !self.agents.contains_key(agent_id) {
308 return Err(HeliosError::AgentError(format!(
309 "Agent '{}' not found",
310 agent_id
311 )));
312 }
313 }
314
315 if !self.agents.contains_key(initiator) {
316 return Err(HeliosError::AgentError(format!(
317 "Initiator agent '{}' not found",
318 initiator
319 )));
320 }
321
322 {
324 let mut context = self.shared_context.write().await;
325 context.set(
326 "current_task".to_string(),
327 Value::String(task_description.clone()),
328 );
329 context.set(
330 "involved_agents".to_string(),
331 Value::Array(
332 involved_agents
333 .iter()
334 .map(|id| Value::String(id.clone()))
335 .collect(),
336 ),
337 );
338 context.set(
339 "task_status".to_string(),
340 Value::String("in_progress".to_string()),
341 );
342 }
343
344 let initiator_agent = self.agents.get_mut(initiator).unwrap();
346 let breakdown_prompt = format!(
347 "You are working with other agents to complete this task: {}\n\
348 The other agents involved are: {}\n\
349 Please break down this task into subtasks that can be delegated to the other agents, \
350 or coordinate with them to work together. Use the available communication tools to \
351 delegate tasks, share information, and collaborate.",
352 task_description,
353 involved_agents.join(", ")
354 );
355
356 let mut result = initiator_agent.chat(breakdown_prompt).await?;
357
358 for _iteration in 0..self.max_iterations {
360 self.process_messages().await?;
362
363 let mut active_responses = Vec::new();
365
366 for agent_id in &involved_agents {
367 if let Some(agent) = self.agents.get_mut(agent_id) {
368 if !agent.chat_session().messages.is_empty() {
370 let last_message = &agent.chat_session().messages.last().unwrap();
371 if last_message.role == crate::chat::Role::User {
372 let response = agent
374 .chat("Continue collaborating on the current task.")
375 .await?;
376 active_responses.push((agent_id.clone(), response));
377 }
378 }
379 }
380 }
381
382 if active_responses.is_empty() {
383 break; }
385
386 for (agent_id, response) in active_responses {
388 result = format!("{}\n\nAgent {}: {}", result, agent_id, response);
389 }
390 }
391
392 {
394 let mut context = self.shared_context.write().await;
395 context.set(
396 "task_status".to_string(),
397 Value::String("completed".to_string()),
398 );
399 }
400
401 Ok(result)
402 }
403
404 pub async fn get_shared_context(&self) -> SharedContext {
406 self.shared_context.read().await.clone()
407 }
408
409 pub async fn set_shared_context(&self, key: String, value: Value) {
411 let mut context = self.shared_context.write().await;
412 context.set(key, value);
413 }
414}
415
416impl Default for ForestOfAgents {
417 fn default() -> Self {
418 Self::new()
419 }
420}
421
422pub struct SendMessageTool {
424 agent_id: AgentId,
425 message_queue: Arc<RwLock<Vec<ForestMessage>>>,
426 shared_context: Arc<RwLock<SharedContext>>,
427}
428
429impl SendMessageTool {
430 pub fn new(
432 agent_id: AgentId,
433 message_queue: Arc<RwLock<Vec<ForestMessage>>>,
434 shared_context: Arc<RwLock<SharedContext>>,
435 ) -> Self {
436 Self {
437 agent_id,
438 message_queue,
439 shared_context,
440 }
441 }
442}
443
444#[async_trait::async_trait]
445impl Tool for SendMessageTool {
446 fn name(&self) -> &str {
447 "send_message"
448 }
449
450 fn description(&self) -> &str {
451 "Send a message to another agent or broadcast to all agents in the forest."
452 }
453
454 fn parameters(&self) -> HashMap<String, ToolParameter> {
455 let mut params = HashMap::new();
456 params.insert(
457 "to".to_string(),
458 ToolParameter {
459 param_type: "string".to_string(),
460 description: "ID of the recipient agent (leave empty for broadcast)".to_string(),
461 required: Some(false),
462 },
463 );
464 params.insert(
465 "message".to_string(),
466 ToolParameter {
467 param_type: "string".to_string(),
468 description: "The message content to send".to_string(),
469 required: Some(true),
470 },
471 );
472 params
473 }
474
475 async fn execute(&self, args: Value) -> Result<ToolResult> {
476 let message = args
477 .get("message")
478 .and_then(|v| v.as_str())
479 .ok_or_else(|| HeliosError::ToolError("Missing 'message' parameter".to_string()))?
480 .to_string();
481
482 let to = args
483 .get("to")
484 .and_then(|v| v.as_str())
485 .map(|s| s.to_string());
486
487 let forest_message = if let Some(to_id) = &to {
488 ForestMessage::new(self.agent_id.clone(), Some(to_id.clone()), message)
489 } else {
490 ForestMessage::broadcast(self.agent_id.clone(), message)
491 };
492
493 {
494 let mut queue = self.message_queue.write().await;
495 queue.push(forest_message.clone());
496 }
497
498 {
499 let mut context = self.shared_context.write().await;
500 context.add_message(forest_message);
501 }
502
503 Ok(ToolResult::success("Message sent successfully"))
504 }
505}
506
507pub struct DelegateTaskTool {
509 agent_id: AgentId,
510 message_queue: Arc<RwLock<Vec<ForestMessage>>>,
511 shared_context: Arc<RwLock<SharedContext>>,
512}
513
514impl DelegateTaskTool {
515 pub fn new(
517 agent_id: AgentId,
518 message_queue: Arc<RwLock<Vec<ForestMessage>>>,
519 shared_context: Arc<RwLock<SharedContext>>,
520 ) -> Self {
521 Self {
522 agent_id,
523 message_queue,
524 shared_context,
525 }
526 }
527}
528
529#[async_trait::async_trait]
530impl Tool for DelegateTaskTool {
531 fn name(&self) -> &str {
532 "delegate_task"
533 }
534
535 fn description(&self) -> &str {
536 "Delegate a specific task to another agent for execution."
537 }
538
539 fn parameters(&self) -> HashMap<String, ToolParameter> {
540 let mut params = HashMap::new();
541 params.insert(
542 "to".to_string(),
543 ToolParameter {
544 param_type: "string".to_string(),
545 description: "ID of the agent to delegate the task to".to_string(),
546 required: Some(true),
547 },
548 );
549 params.insert(
550 "task".to_string(),
551 ToolParameter {
552 param_type: "string".to_string(),
553 description: "Description of the task to delegate".to_string(),
554 required: Some(true),
555 },
556 );
557 params.insert(
558 "context".to_string(),
559 ToolParameter {
560 param_type: "string".to_string(),
561 description: "Additional context or requirements for the task".to_string(),
562 required: Some(false),
563 },
564 );
565 params
566 }
567
568 async fn execute(&self, args: Value) -> Result<ToolResult> {
569 let to = args
570 .get("to")
571 .and_then(|v| v.as_str())
572 .ok_or_else(|| HeliosError::ToolError("Missing 'to' parameter".to_string()))?;
573
574 let task = args
575 .get("task")
576 .and_then(|v| v.as_str())
577 .ok_or_else(|| HeliosError::ToolError("Missing 'task' parameter".to_string()))?;
578
579 let context = args.get("context").and_then(|v| v.as_str()).unwrap_or("");
580
581 let message = if context.is_empty() {
582 format!("Task delegated: {}", task)
583 } else {
584 format!("Task delegated: {}\nContext: {}", task, context)
585 };
586
587 let forest_message =
588 ForestMessage::new(self.agent_id.clone(), Some(to.to_string()), message)
589 .with_metadata("type".to_string(), "task_delegation".to_string())
590 .with_metadata("task".to_string(), task.to_string());
591
592 {
593 let mut queue = self.message_queue.write().await;
594 queue.push(forest_message.clone());
595 }
596
597 {
598 let mut context_lock = self.shared_context.write().await;
599 context_lock.add_message(forest_message);
600 }
601
602 Ok(ToolResult::success(format!(
603 "Task delegated to agent '{}'",
604 to
605 )))
606 }
607}
608
609pub struct ShareContextTool {
611 agent_id: AgentId,
612 shared_context: Arc<RwLock<SharedContext>>,
613}
614
615impl ShareContextTool {
616 pub fn new(agent_id: AgentId, shared_context: Arc<RwLock<SharedContext>>) -> Self {
618 Self {
619 agent_id,
620 shared_context,
621 }
622 }
623}
624
625#[async_trait::async_trait]
626impl Tool for ShareContextTool {
627 fn name(&self) -> &str {
628 "share_context"
629 }
630
631 fn description(&self) -> &str {
632 "Share information in the shared context that all agents can access."
633 }
634
635 fn parameters(&self) -> HashMap<String, ToolParameter> {
636 let mut params = HashMap::new();
637 params.insert(
638 "key".to_string(),
639 ToolParameter {
640 param_type: "string".to_string(),
641 description: "Key for the shared information".to_string(),
642 required: Some(true),
643 },
644 );
645 params.insert(
646 "value".to_string(),
647 ToolParameter {
648 param_type: "string".to_string(),
649 description: "Value to share".to_string(),
650 required: Some(true),
651 },
652 );
653 params.insert(
654 "description".to_string(),
655 ToolParameter {
656 param_type: "string".to_string(),
657 description: "Description of what this information represents".to_string(),
658 required: Some(false),
659 },
660 );
661 params
662 }
663
664 async fn execute(&self, args: Value) -> Result<ToolResult> {
665 let key = args
666 .get("key")
667 .and_then(|v| v.as_str())
668 .ok_or_else(|| HeliosError::ToolError("Missing 'key' parameter".to_string()))?;
669
670 let value = args
671 .get("value")
672 .and_then(|v| v.as_str())
673 .ok_or_else(|| HeliosError::ToolError("Missing 'value' parameter".to_string()))?;
674
675 let description = args
676 .get("description")
677 .and_then(|v| v.as_str())
678 .unwrap_or("");
679
680 let mut context = self.shared_context.write().await;
681
682 let metadata = serde_json::json!({
684 "shared_by": self.agent_id,
685 "timestamp": chrono::Utc::now().to_rfc3339(),
686 "description": description
687 });
688
689 let value_with_meta = serde_json::json!({
690 "value": value,
691 "metadata": metadata
692 });
693
694 context.set(key.to_string(), value_with_meta);
695
696 Ok(ToolResult::success(format!(
697 "Information shared with key '{}'",
698 key
699 )))
700 }
701}
702
703pub struct ForestBuilder {
705 config: Option<Config>,
706 agents: Vec<(AgentId, AgentBuilder)>,
707 max_iterations: usize,
708}
709
710impl ForestBuilder {
711 pub fn new() -> Self {
713 Self {
714 config: None,
715 agents: Vec::new(),
716 max_iterations: 10,
717 }
718 }
719
720 pub fn config(mut self, config: Config) -> Self {
722 self.config = Some(config);
723 self
724 }
725
726 pub fn agent(mut self, id: AgentId, builder: AgentBuilder) -> Self {
728 self.agents.push((id, builder));
729 self
730 }
731
732 pub fn max_iterations(mut self, max: usize) -> Self {
734 self.max_iterations = max;
735 self
736 }
737
738 pub async fn build(self) -> Result<ForestOfAgents> {
740 let config = self
741 .config
742 .ok_or_else(|| HeliosError::AgentError("Config is required".to_string()))?;
743
744 let mut forest = ForestOfAgents::with_max_iterations(self.max_iterations);
745
746 for (id, builder) in self.agents {
747 let agent = builder.config(config.clone()).build().await?;
748 forest.add_agent(id, agent)?;
749 }
750
751 Ok(forest)
752 }
753}
754
755impl Default for ForestBuilder {
756 fn default() -> Self {
757 Self::new()
758 }
759}
760
761#[cfg(test)]
762mod tests {
763 use super::*;
764 use crate::config::Config;
765 use crate::tools::Tool;
766 use serde_json::Value;
767
768 #[tokio::test]
770 async fn test_forest_creation_and_agent_management() {
771 let mut forest = ForestOfAgents::new();
772 let config = Config::new_default();
773
774 let agent1 = Agent::builder("agent1")
776 .config(config.clone())
777 .system_prompt("You are agent 1")
778 .build()
779 .await
780 .unwrap();
781
782 let agent2 = Agent::builder("agent2")
783 .config(config)
784 .system_prompt("You are agent 2")
785 .build()
786 .await
787 .unwrap();
788
789 forest.add_agent("agent1".to_string(), agent1).unwrap();
791 forest.add_agent("agent2".to_string(), agent2).unwrap();
792
793 let agents = forest.list_agents();
795 assert_eq!(agents.len(), 2);
796 assert!(agents.contains(&"agent1".to_string()));
797 assert!(agents.contains(&"agent2".to_string()));
798
799 assert!(forest.get_agent(&"agent1".to_string()).is_some());
801 assert!(forest.get_agent(&"agent3".to_string()).is_none());
802
803 let agent3 = Agent::builder("agent3")
805 .config(Config::new_default())
806 .build()
807 .await
808 .unwrap();
809 let result = forest.add_agent("agent1".to_string(), agent3);
810 assert!(result.is_err());
811
812 let removed = forest.remove_agent(&"agent1".to_string());
814 assert!(removed.is_some());
815 assert_eq!(forest.list_agents().len(), 1);
816 assert!(forest.get_agent(&"agent1".to_string()).is_none());
817 }
818
819 #[tokio::test]
821 async fn test_message_sending() {
822 let mut forest = ForestOfAgents::new();
823 let config = Config::new_default();
824
825 let agent1 = Agent::builder("alice")
827 .config(config.clone())
828 .build()
829 .await
830 .unwrap();
831
832 let agent2 = Agent::builder("bob").config(config).build().await.unwrap();
833
834 forest.add_agent("alice".to_string(), agent1).unwrap();
835 forest.add_agent("bob".to_string(), agent2).unwrap();
836
837 forest
839 .send_message(
840 &"alice".to_string(),
841 Some(&"bob".to_string()),
842 "Hello Bob!".to_string(),
843 )
844 .await
845 .unwrap();
846
847 forest.process_messages().await.unwrap();
849
850 let bob = forest.get_agent(&"bob".to_string()).unwrap();
852 let messages = bob.chat_session().messages.clone();
853 assert!(messages.len() >= 1);
854 let last_message = messages.last().unwrap();
855 assert_eq!(last_message.role, crate::chat::Role::User);
856 assert!(last_message
857 .content
858 .contains("Message from alice: Hello Bob!"));
859
860 let alice_message_count_before = forest
862 .get_agent(&"alice".to_string())
863 .unwrap()
864 .chat_session()
865 .messages
866 .len();
867 forest
868 .send_message(&"alice".to_string(), None, "Hello everyone!".to_string())
869 .await
870 .unwrap();
871 forest.process_messages().await.unwrap();
872
873 let alice = forest.get_agent(&"alice".to_string()).unwrap();
875 assert_eq!(
876 alice.chat_session().messages.len(),
877 alice_message_count_before
878 );
879
880 let bob = forest.get_agent(&"bob".to_string()).unwrap();
881 let bob_messages = bob.chat_session().messages.clone();
882 let bob_last = bob_messages.last().unwrap();
883 assert!(bob_last
884 .content
885 .contains("Broadcast from alice: Hello everyone!"));
886 }
887
888 #[tokio::test]
890 async fn test_send_message_tool() {
891 let message_queue = Arc::new(RwLock::new(Vec::<ForestMessage>::new()));
892 let shared_context = Arc::new(RwLock::new(SharedContext::new()));
893
894 let tool = SendMessageTool::new(
895 "alice".to_string(),
896 message_queue.clone(),
897 shared_context.clone(),
898 );
899
900 let args = serde_json::json!({
902 "to": "bob",
903 "message": "Test message"
904 });
905
906 let result = tool.execute(args).await.unwrap();
907 assert!(result.success);
908 assert_eq!(result.output, "Message sent successfully");
909
910 let queue = message_queue.read().await;
912 assert_eq!(queue.len(), 1);
913 let message = &queue[0];
914 assert_eq!(message.from, "alice");
915 assert_eq!(message.to, Some("bob".to_string()));
916 assert_eq!(message.content, "Test message");
917
918 let context = shared_context.read().await;
920 let messages = context.get_recent_messages(10);
921 assert_eq!(messages.len(), 1);
922 assert_eq!(messages[0].from, "alice");
923
924 }
927
928 #[tokio::test]
930 async fn test_delegate_task_tool() {
931 let message_queue = Arc::new(RwLock::new(Vec::new()));
932 let shared_context = Arc::new(RwLock::new(SharedContext::new()));
933
934 let tool = DelegateTaskTool::new(
935 "manager".to_string(),
936 Arc::clone(&message_queue),
937 Arc::clone(&shared_context),
938 );
939
940 let args = serde_json::json!({
942 "to": "worker",
943 "task": "Analyze the data",
944 "context": "Use statistical methods"
945 });
946
947 let result = tool.execute(args).await.unwrap();
948 assert!(result.success);
949 assert_eq!(result.output, "Task delegated to agent 'worker'");
950
951 let queue = message_queue.read().await;
953 assert_eq!(queue.len(), 1);
954 let message = &queue[0];
955 assert_eq!(message.from, "manager");
956 assert_eq!(message.to, Some("worker".to_string()));
957 assert!(message.content.contains("Task delegated: Analyze the data"));
958 assert!(message.content.contains("Context: Use statistical methods"));
959
960 assert_eq!(
962 message.metadata.get("type"),
963 Some(&"task_delegation".to_string())
964 );
965 assert_eq!(
966 message.metadata.get("task"),
967 Some(&"Analyze the data".to_string())
968 );
969 }
970
971 #[tokio::test]
973 async fn test_share_context_tool() {
974 let shared_context = Arc::new(RwLock::new(SharedContext::new()));
975
976 let tool = ShareContextTool::new("researcher".to_string(), Arc::clone(&shared_context));
977
978 let args = serde_json::json!({
980 "key": "findings",
981 "value": "Temperature affects reaction rate",
982 "description": "Key experimental finding"
983 });
984
985 let result = tool.execute(args).await.unwrap();
986 assert!(result.success);
987 assert_eq!(result.output, "Information shared with key 'findings'");
988
989 let context = shared_context.read().await;
991 let findings_data = context.get("findings").unwrap();
992 let findings_obj = findings_data.as_object().unwrap();
993
994 assert_eq!(
996 findings_obj.get("value").unwrap(),
997 &Value::String("Temperature affects reaction rate".to_string())
998 );
999
1000 let metadata = findings_obj.get("metadata").unwrap();
1002 let metadata_obj = metadata.as_object().unwrap();
1003 assert_eq!(
1004 metadata_obj.get("shared_by").unwrap(),
1005 &Value::String("researcher".to_string())
1006 );
1007 assert_eq!(
1008 metadata_obj.get("description").unwrap(),
1009 &Value::String("Key experimental finding".to_string())
1010 );
1011 assert!(metadata_obj.contains_key("timestamp"));
1012 }
1013
1014 #[tokio::test]
1016 async fn test_shared_context() {
1017 let mut context = SharedContext::new();
1018
1019 context.set("key1".to_string(), Value::String("value1".to_string()));
1021 context.set("key2".to_string(), Value::Number(42.into()));
1022
1023 assert_eq!(
1024 context.get("key1"),
1025 Some(&Value::String("value1".to_string()))
1026 );
1027 assert_eq!(context.get("key2"), Some(&Value::Number(42.into())));
1028 assert_eq!(context.get("key3"), None);
1029
1030 let msg1 = ForestMessage::new(
1032 "alice".to_string(),
1033 Some("bob".to_string()),
1034 "Hello".to_string(),
1035 );
1036 let msg2 = ForestMessage::broadcast("bob".to_string(), "Hi everyone".to_string());
1037
1038 context.add_message(msg1);
1039 context.add_message(msg2);
1040
1041 let messages = context.get_recent_messages(10);
1042 assert_eq!(messages.len(), 2);
1043 assert_eq!(messages[0].from, "alice");
1044 assert_eq!(messages[1].from, "bob");
1045
1046 let removed = context.remove("key1");
1048 assert_eq!(removed, Some(Value::String("value1".to_string())));
1049 assert_eq!(context.get("key1"), None);
1050 }
1051
1052 #[tokio::test]
1054 async fn test_collaborative_task() {
1055 let mut forest = ForestOfAgents::new();
1056 let config = Config::new_default();
1057
1058 let coordinator = Agent::builder("coordinator")
1060 .config(config.clone())
1061 .system_prompt(
1062 "You are a task coordinator. Break down tasks and delegate to specialists.",
1063 )
1064 .build()
1065 .await
1066 .unwrap();
1067
1068 let researcher = Agent::builder("researcher")
1069 .config(config.clone())
1070 .system_prompt("You are a researcher. Gather and analyze information.")
1071 .build()
1072 .await
1073 .unwrap();
1074
1075 let writer = Agent::builder("writer")
1076 .config(config)
1077 .system_prompt("You are a writer. Create clear, well-structured content.")
1078 .build()
1079 .await
1080 .unwrap();
1081
1082 forest
1083 .add_agent("coordinator".to_string(), coordinator)
1084 .unwrap();
1085 forest
1086 .add_agent("researcher".to_string(), researcher)
1087 .unwrap();
1088 forest.add_agent("writer".to_string(), writer).unwrap();
1089
1090 assert_eq!(forest.list_agents().len(), 3);
1099 assert!(forest.get_agent(&"coordinator".to_string()).is_some());
1100 assert!(forest.get_agent(&"researcher".to_string()).is_some());
1101 assert!(forest.get_agent(&"writer".to_string()).is_some());
1102
1103 forest
1108 .set_shared_context(
1109 "current_task".to_string(),
1110 Value::String("Create a report on climate change impacts".to_string()),
1111 )
1112 .await;
1113 forest
1114 .set_shared_context(
1115 "involved_agents".to_string(),
1116 Value::Array(vec![
1117 Value::String("researcher".to_string()),
1118 Value::String("writer".to_string()),
1119 ]),
1120 )
1121 .await;
1122 forest
1123 .set_shared_context(
1124 "task_status".to_string(),
1125 Value::String("in_progress".to_string()),
1126 )
1127 .await;
1128
1129 let context = forest.get_shared_context().await;
1131 assert_eq!(
1132 context.get("task_status"),
1133 Some(&Value::String("in_progress".to_string()))
1134 );
1135 assert!(context.get("current_task").is_some());
1136 assert!(context.get("involved_agents").is_some());
1137 }
1138
1139 #[tokio::test]
1141 async fn test_forest_builder() {
1142 let config = Config::new_default();
1143
1144 let forest = ForestBuilder::new()
1145 .config(config)
1146 .agent(
1147 "agent1".to_string(),
1148 Agent::builder("agent1").system_prompt("Agent 1 prompt"),
1149 )
1150 .agent(
1151 "agent2".to_string(),
1152 Agent::builder("agent2").system_prompt("Agent 2 prompt"),
1153 )
1154 .max_iterations(5)
1155 .build()
1156 .await
1157 .unwrap();
1158
1159 assert_eq!(forest.list_agents().len(), 2);
1160 assert!(forest.get_agent(&"agent1".to_string()).is_some());
1161 assert!(forest.get_agent(&"agent2".to_string()).is_some());
1162 assert_eq!(forest.max_iterations, 5);
1163 }
1164
1165 #[tokio::test]
1167 async fn test_forest_error_handling() {
1168 let mut forest = ForestOfAgents::new();
1169
1170 let result = forest
1172 .send_message(
1173 &"nonexistent".to_string(),
1174 Some(&"target".to_string()),
1175 "test".to_string(),
1176 )
1177 .await;
1178 assert!(result.is_err());
1179
1180 let result = forest
1182 .execute_collaborative_task(&"nonexistent".to_string(), "test task".to_string(), vec![])
1183 .await;
1184 assert!(result.is_err());
1185
1186 let config = Config::new_default();
1188 let agent = Agent::builder("real_agent")
1189 .config(config)
1190 .build()
1191 .await
1192 .unwrap();
1193 forest.add_agent("real_agent".to_string(), agent).unwrap();
1194
1195 let result = forest
1196 .execute_collaborative_task(
1197 &"real_agent".to_string(),
1198 "test task".to_string(),
1199 vec!["nonexistent".to_string()],
1200 )
1201 .await;
1202 assert!(result.is_err());
1203 }
1204
1205 #[tokio::test]
1207 async fn test_forest_message() {
1208 let msg = ForestMessage::new(
1210 "alice".to_string(),
1211 Some("bob".to_string()),
1212 "Hello".to_string(),
1213 );
1214 assert_eq!(msg.from, "alice");
1215 assert_eq!(msg.to, Some("bob".to_string()));
1216 assert_eq!(msg.content, "Hello");
1217
1218 let broadcast = ForestMessage::broadcast("alice".to_string(), "Announcement".to_string());
1220 assert_eq!(broadcast.from, "alice");
1221 assert!(broadcast.to.is_none());
1222 assert_eq!(broadcast.content, "Announcement");
1223
1224 let msg_with_meta = msg.with_metadata("priority".to_string(), "high".to_string());
1226 assert_eq!(
1227 msg_with_meta.metadata.get("priority"),
1228 Some(&"high".to_string())
1229 );
1230 }
1231}