From aebec35d5442207d35d5cd204c999c3d9a978cdb Mon Sep 17 00:00:00 2001 From: Kye Date: Mon, 22 Jan 2024 17:50:25 -0500 Subject: [PATCH] [FEATS] [ broadcast, circular_swarm, exponential_swarm, fibonacci_swarm, geometric_swarm, grid_swarm, harmonic_swarm, linear_swarm, log_swarm, mesh_swarm, one_to_one, one_to_three, power_swarm, prime_swarm, pyramid_swarm, sigmoid_swarm, staircase_swarm, star_swarm,] --- mkdocs.yml | 2 +- swarms/structs/__init__.py | 53 ++++++-- swarms/structs/agent.py | 17 +++ swarms/structs/company.py | 156 +++++++++++++++++++++++ swarms/structs/swarming_architectures.py | 94 ++++++++++++++ 5 files changed, 313 insertions(+), 9 deletions(-) create mode 100644 swarms/structs/company.py diff --git a/mkdocs.yml b/mkdocs.yml index e7d76b06..184f059f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -111,7 +111,7 @@ nav: - sequential_workflow: "swarms/structs/sequential_workflow.md" - workflow: "swarms/structs/workflow.md" - baseworkflow: "swarms/structs/baseworkflow.md" - - Multi-Agent-Architectures: + - Multi Agent Architectures: - conversation: "swarms/structs/conversation.md" - groupchat: "swarms/structs/groupchat.md" - swarmnetwork: "swarms/structs/swarmnetwork.md" diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index 90b0b911..11e9e523 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -3,12 +3,16 @@ from swarms.structs.autoscaler import AutoScaler from swarms.structs.base import BaseStructure from swarms.structs.base_swarm import AbstractSwarm from swarms.structs.base_workflow import BaseWorkflow +from swarms.structs.block_wrapper import block from swarms.structs.concurrent_workflow import ConcurrentWorkflow from swarms.structs.conversation import Conversation +from swarms.structs.graph_workflow import GraphWorkflow from swarms.structs.groupchat import GroupChat, GroupChatManager +from swarms.structs.message import Message from swarms.structs.model_parallizer import ModelParallelizer from swarms.structs.multi_agent_collab import MultiAgentCollaboration from swarms.structs.nonlinear_workflow import NonlinearWorkflow +from swarms.structs.plan import Plan from swarms.structs.recursive_workflow import RecursiveWorkflow from swarms.structs.schemas import ( Artifact, @@ -17,23 +21,38 @@ from swarms.structs.schemas import ( TaskInput, ) from swarms.structs.sequential_workflow import SequentialWorkflow +from swarms.structs.step import Step from swarms.structs.swarm_net import SwarmNetwork +from swarms.structs.swarming_architectures import ( + broadcast, + circular_swarm, + exponential_swarm, + fibonacci_swarm, + geometric_swarm, + grid_swarm, + harmonic_swarm, + linear_swarm, + log_swarm, + mesh_swarm, + one_to_one, + one_to_three, + power_swarm, + prime_swarm, + pyramid_swarm, + sigmoid_swarm, + staircase_swarm, + star_swarm, +) +from swarms.structs.task import Task from swarms.structs.utils import ( + detect_markdown, distribute_tasks, extract_key_from_json, extract_tokens_from_text, find_agent_by_id, find_token_in_text, parse_tasks, - detect_markdown, ) -from swarms.structs.task import Task -from swarms.structs.block_wrapper import block -from swarms.structs.graph_workflow import GraphWorkflow -from swarms.structs.step import Step -from swarms.structs.plan import Plan -from swarms.structs.message import Message - __all__ = [ "Agent", @@ -68,4 +87,22 @@ __all__ = [ "Step", "Plan", "Message", + "broadcast", + "circular_swarm", + "exponential_swarm", + "fibonacci_swarm", + "geometric_swarm", + "grid_swarm", + "harmonic_swarm", + "linear_swarm", + "log_swarm", + "mesh_swarm", + "one_to_one", + "one_to_three", + "power_swarm", + "prime_swarm", + "pyramid_swarm", + "sigmoid_swarm", + "staircase_swarm", + "star_swarm", ] diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index 046844eb..dace1ff3 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -1,6 +1,7 @@ import asyncio import json import logging +import os import random import time import uuid @@ -173,6 +174,7 @@ class Agent: traceback_handlers: Any = None, streaming_on: Optional[bool] = False, docs: List[str] = None, + docs_folder: str = None, *args, **kwargs: Any, ): @@ -215,6 +217,7 @@ class Agent: self.traceback_handlers = traceback_handlers self.streaming_on = streaming_on self.docs = docs + self.docs_folder = docs_folder # The max_loops will be set dynamically if the dynamic_loop if self.dynamic_loops: @@ -245,6 +248,9 @@ class Agent: if self.docs: self.ingest_docs(self.docs) + if self.docs_folder: + self.get_docs_from_doc_folders() + def set_system_prompt(self, system_prompt: str): """Set the system prompt""" self.system_prompt = system_prompt @@ -1145,3 +1151,14 @@ class Agent: """Send a message to the agent""" message = f"{agent_name}: {message}" return self.run(message, *args, **kwargs) + + def get_docs_from_doc_folders(self): + """Get the docs from the files""" + # Get the list of files then extract them and add them to the memory + files = os.listdir(self.docs_folder) + + # Extract the text from the files + for file in files: + text = data_to_text(file) + + return self.short_memory.append(text) diff --git a/swarms/structs/company.py b/swarms/structs/company.py new file mode 100644 index 00000000..b01c0a00 --- /dev/null +++ b/swarms/structs/company.py @@ -0,0 +1,156 @@ +from typing import List, Optional, Union, Dict +from dataclasses import dataclass, field +from swarms.structs.agent import Agent +from swarms.utils.logger import logger + + +@dataclass +class Company: + """ + Represents a company with a hierarchical organizational structure. + """ + org_chart: Union[List[Agent], List[List[Agent]]] + shared_instructions: str = None + ceo: Optional[Agent] = None + agents: List[Agent] = field(default_factory=list) + agent_interactions: Dict[str, List[str]] = field( + default_factory=dict + ) + + def __post_init__(self): + self._parse_org_chart(self.org_chart) + + def add(self, agent: Agent) -> None: + """ + Adds an agent to the company. + + Args: + agent (Agent): The agent to be added. + + Raises: + ValueError: If an agent with the same ID already exists in the company. + """ + try: + if any( + existing_agent.id == agent.id + for existing_agent in self.agents + ): + raise ValueError( + f"Agent with id {agent.id} already exists in the" + " company." + ) + self.agents.append(agent) + + except Exception as error: + logger.error( + f"[ERROR][CLASS: Company][METHOD: add] {error}" + ) + raise error + + def get(self, agent_name: str) -> Agent: + """ + Retrieves an agent from the company by name. + + Args: + agent_name (str): The name of the agent to retrieve. + + Returns: + Agent: The retrieved agent. + + Raises: + ValueError: If an agent with the specified name does not exist in the company. + """ + try: + for agent in self.agents: + if agent.name == agent_name: + return agent + raise ValueError( + f"Agent with name {agent_name} does not exist in the" + " company." + ) + except Exception as error: + logger.error( + f"[ERROR][CLASS: Company][METHOD: get] {error}" + ) + raise error + + def remove(self, agent: Agent) -> None: + """ + Removes an agent from the company. + + Args: + agent (Agent): The agent to be removed. + """ + self.agents.remove(agent) + + def _parse_org_chart( + self, org_chart: Union[List[Agent], List[List[Agent]]] + ) -> None: + """ + Parses the organization chart and adds agents to the company. + + Args: + org_chart (Union[List[Agent], List[List[Agent]]]): The organization chart + representing the hierarchy of agents. + + Raises: + ValueError: If more than one CEO is found in the org chart or if an invalid + agent is encountered. + + """ + try: + for node in org_chart: + if isinstance(node, Agent): + if self.ceo: + raise ValueError("1 CEO is only allowed") + self.ceo = node + self.add(node) + + elif isinstance(node, list): + for agent in node: + if not isinstance(agent, Agent): + raise ValueError( + "Invalid agent in org chart" + ) + self.add(agent) + + for i, agent in enumerate(node): + if i == len(node) - 1: + continue + + for other_agent in node[i + 1]: + self.__init_task(agent, other_agent) + except Exception as error: + logger.error( + "[ERROR][CLASS: Company][METHOD: _parse_org_chart]" + f" {error}" + ) + raise error + + def _init_interaction( + self, + agent1: Agent, + agent2: Agent, + ) -> None: + if agent1.ai_name not in self.agents_interactions: + self.agents_interactions[agent1.ai_name] = [] + self.agents_interactions[agent1.ai_name].append( + agent2.ai_name + ) + + def run(self): + """ + Run the company + """ + for ( + agent_name, + interaction_agents, + ) in self.agents_interactions.items(): + agent = self.get(agent_name) + for interaction_agent in interaction_agents: + task_description = ( + f"Task for {agent_name} to interact with" + f" {interaction_agent}" + ) + print(f"{task_description} is being executed") + agent.run(task_description) diff --git a/swarms/structs/swarming_architectures.py b/swarms/structs/swarming_architectures.py index a6ccdf9b..ad3ad4ed 100644 --- a/swarms/structs/swarming_architectures.py +++ b/swarms/structs/swarming_architectures.py @@ -1,6 +1,8 @@ import math from typing import List from swarms.structs.agent import Agent +import asyncio +from swarms.utils.logger import logger def circular_swarm(agents: List[Agent], tasks: List[str]): @@ -159,3 +161,95 @@ def sinusoidal_swarm(agents: List[Agent], task: str): for i in range(len(agents)): index = int((math.sin(i) + 1) / 2 * len(agents)) agents[index].run(task) + + +async def one_to_three(sender: Agent, agents: List[Agent], task: str): + """ + Sends a message from the sender agent to three other agents. + + Args: + sender (Agent): The agent sending the message. + agents (List[Agent]): The list of agents to receive the message. + task (str): The message to be sent. + + Raises: + Exception: If there is an error while sending the message. + + Returns: + None + """ + try: + receive_tasks = [] + for agent in agents: + receive_tasks.append( + agent.receive_message(sender.ai_name, task) + ) + + await asyncio.gather(*receive_tasks) + except Exception as error: + logger.error( + f"[ERROR][CLASS: Agent][METHOD: one_to_three] {error}" + ) + raise error + + +async def broadcast( + sender: Agent, + agents: List[Agent], + task: str, +): + """ + Broadcasts a message from the sender agent to a list of agents. + + Args: + sender (Agent): The agent sending the message. + agents (List[Agent]): The list of agents to receive the message. + task (str): The message to be broadcasted. + + Raises: + Exception: If an error occurs during the broadcast. + + Returns: + None + """ + try: + receive_tasks = [] + for agent in agents: + receive_tasks.append( + agent.receive_message(sender.ai_name, task) + ) + + await asyncio.gather(*receive_tasks) + except Exception as error: + logger.error( + f"[ERROR][CLASS: Agent][METHOD: broadcast] {error}" + ) + raise error + + +async def one_to_one( + sender: Agent, + receiver: Agent, + task: str, +): + """ + Sends a message from the sender agent to the receiver agent. + + Args: + sender (Agent): The agent sending the message. + receiver (Agent): The agent to receive the message. + task (str): The message to be sent. + + Raises: + Exception: If an error occurs during the message sending. + + Returns: + None + """ + try: + await receiver.receive_message(sender.ai_name, task) + except Exception as error: + logger.error( + f"[ERROR][CLASS: Agent][METHOD: one_to_one] {error}" + ) + raise error