You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
12 KiB
435 lines
12 KiB
import asyncio
|
|
from abc import ABC
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
from typing import Any, Callable, Dict, List, Optional
|
|
|
|
|
|
from swarms.structs.agent import Agent
|
|
|
|
|
|
class AbstractSwarm(ABC):
|
|
"""
|
|
Abstract Swarm Class for multi-agent systems
|
|
|
|
Attributes:
|
|
agents (List[Agent]): A list of agents
|
|
max_loops (int): The maximum number of loops to run
|
|
|
|
|
|
Methods:
|
|
communicate: Communicate with the swarm through the orchestrator, protocols, and the universal communication layer
|
|
run: Run the swarm
|
|
step: Step the swarm
|
|
add_agent: Add a agent to the swarm
|
|
remove_agent: Remove a agent from the swarm
|
|
broadcast: Broadcast a message to all agents
|
|
reset: Reset the swarm
|
|
plan: agents must individually plan using a workflow or pipeline
|
|
direct_message: Send a direct message to a agent
|
|
autoscaler: Autoscaler that acts like kubernetes for autonomous agents
|
|
get_agent_by_id: Locate a agent by id
|
|
get_agent_by_name: Locate a agent by name
|
|
assign_task: Assign a task to a agent
|
|
get_all_tasks: Get all tasks
|
|
get_finished_tasks: Get all finished tasks
|
|
get_pending_tasks: Get all pending tasks
|
|
pause_agent: Pause a agent
|
|
resume_agent: Resume a agent
|
|
stop_agent: Stop a agent
|
|
restart_agent: Restart agent
|
|
scale_up: Scale up the number of agents
|
|
scale_down: Scale down the number of agents
|
|
scale_to: Scale to a specific number of agents
|
|
get_all_agents: Get all agents
|
|
get_swarm_size: Get the size of the swarm
|
|
get_swarm_status: Get the status of the swarm
|
|
save_swarm_state: Save the swarm state
|
|
loop: Loop through the swarm
|
|
run_async: Run the swarm asynchronously
|
|
run_batch_async: Run the swarm asynchronously
|
|
run_batch: Run the swarm asynchronously
|
|
batched_run: Run the swarm asynchronously
|
|
abatch_run: Asynchronous batch run with language model
|
|
arun: Asynchronous run
|
|
|
|
"""
|
|
|
|
# @abstractmethod
|
|
def __init__(self, agents: List[Agent], max_loops: int = 200):
|
|
"""Initialize the swarm with agents"""
|
|
self.agents = agents
|
|
self.max_loops = max_loops
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def communicate(self):
|
|
"""Communicate with the swarm through the orchestrator, protocols, and the universal communication layer"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def run(self):
|
|
"""Run the swarm"""
|
|
pass
|
|
|
|
def __call__(
|
|
self,
|
|
task,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
"""Call self as a function
|
|
|
|
Args:
|
|
task (_type_): _description_
|
|
|
|
Returns:
|
|
_type_: _description_
|
|
"""
|
|
return self.run(task, *args, **kwargs)
|
|
|
|
def step(self):
|
|
"""Step the swarm"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def add_agent(self, agent: "Agent"):
|
|
"""Add a agent to the swarm"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def remove_agent(self, agent: "Agent"):
|
|
"""Remove a agent from the swarm"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def broadcast(
|
|
self, message: str, sender: Optional["Agent"] = None
|
|
):
|
|
"""Broadcast a message to all agents"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def reset(self):
|
|
"""Reset the swarm"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def plan(self, task: str):
|
|
"""agents must individually plan using a workflow or pipeline"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def direct_message(
|
|
self,
|
|
message: str,
|
|
sender: "Agent",
|
|
recipient: "Agent",
|
|
):
|
|
"""Send a direct message to a agent"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def autoscaler(self, num_agents: int, agent: ["Agent"]):
|
|
"""Autoscaler that acts like kubernetes for autonomous agents"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def get_agent_by_id(self, id: str) -> "Agent":
|
|
"""Locate a agent by id"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def get_agent_by_name(self, name: str) -> "Agent":
|
|
"""Locate a agent by name"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def assign_task(self, agent: "Agent", task: Any) -> Dict:
|
|
"""Assign a task to a agent"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def get_all_tasks(self, agent: "Agent", task: Any):
|
|
"""Get all tasks"""
|
|
|
|
# @abstractmethod
|
|
def get_finished_tasks(self) -> List[Dict]:
|
|
"""Get all finished tasks"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def get_pending_tasks(self) -> List[Dict]:
|
|
"""Get all pending tasks"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def pause_agent(self, agent: "Agent", agent_id: str):
|
|
"""Pause a agent"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def resume_agent(self, agent: "Agent", agent_id: str):
|
|
"""Resume a agent"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def stop_agent(self, agent: "Agent", agent_id: str):
|
|
"""Stop a agent"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def restart_agent(self, agent: "Agent"):
|
|
"""Restart agent"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def scale_up(self, num_agent: int):
|
|
"""Scale up the number of agents"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def scale_down(self, num_agent: int):
|
|
"""Scale down the number of agents"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def scale_to(self, num_agent: int):
|
|
"""Scale to a specific number of agents"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def get_all_agents(self) -> List["Agent"]:
|
|
"""Get all agents"""
|
|
pass
|
|
|
|
# @abstractmethod
|
|
def get_swarm_size(self) -> int:
|
|
"""Get the size of the swarm"""
|
|
pass
|
|
|
|
# #@abstractmethod
|
|
def get_swarm_status(self) -> Dict:
|
|
"""Get the status of the swarm"""
|
|
pass
|
|
|
|
# #@abstractmethod
|
|
def save_swarm_state(self):
|
|
"""Save the swarm state"""
|
|
pass
|
|
|
|
def batched_run(self, tasks: List[Any], *args, **kwargs):
|
|
"""_summary_
|
|
|
|
Args:
|
|
tasks (List[Any]): _description_
|
|
"""
|
|
# Implement batched run
|
|
return [self.run(task, *args, **kwargs) for task in tasks]
|
|
|
|
async def abatch_run(self, tasks: List[str], *args, **kwargs):
|
|
"""Asynchronous batch run with language model
|
|
|
|
Args:
|
|
tasks (List[str]): _description_
|
|
|
|
Returns:
|
|
_type_: _description_
|
|
"""
|
|
return await asyncio.gather(
|
|
*(self.arun(task, *args, **kwargs) for task in tasks)
|
|
)
|
|
|
|
async def arun(self, task: Optional[str] = None, *args, **kwargs):
|
|
"""Asynchronous run
|
|
|
|
Args:
|
|
task (Optional[str], optional): _description_. Defaults to None.
|
|
"""
|
|
loop = asyncio.get_event_loop()
|
|
result = await loop.run_in_executor(
|
|
None, self.run, task, *args, **kwargs
|
|
)
|
|
return result
|
|
|
|
def loop(
|
|
self,
|
|
task: Optional[str] = None,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
"""Loop through the swarm
|
|
|
|
Args:
|
|
task (Optional[str], optional): _description_. Defaults to None.
|
|
"""
|
|
# Loop through the self.max_loops
|
|
for i in range(self.max_loops):
|
|
self.run(task, *args, **kwargs)
|
|
|
|
async def aloop(
|
|
self,
|
|
task: Optional[str] = None,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
"""Asynchronous loop through the swarm
|
|
|
|
Args:
|
|
task (Optional[str], optional): _description_. Defaults to None.
|
|
"""
|
|
# Async Loop through the self.max_loops
|
|
loop = asyncio.get_event_loop()
|
|
result = await loop.run_in_executor(
|
|
None, self.loop, task, *args, **kwargs
|
|
)
|
|
return result
|
|
|
|
def run_async(self, task: Optional[str] = None, *args, **kwargs):
|
|
"""Run the swarm asynchronously
|
|
|
|
Args:
|
|
task (Optional[str], optional): _description_. Defaults to None.
|
|
"""
|
|
loop = asyncio.get_event_loop()
|
|
result = loop.run_until_complete(
|
|
self.arun(task, *args, **kwargs)
|
|
)
|
|
return result
|
|
|
|
def run_batch_async(self, tasks: List[str], *args, **kwargs):
|
|
"""Run the swarm asynchronously
|
|
|
|
Args:
|
|
task (Optional[str], optional): _description_. Defaults to None.
|
|
"""
|
|
loop = asyncio.get_event_loop()
|
|
result = loop.run_until_complete(
|
|
self.abatch_run(tasks, *args, **kwargs)
|
|
)
|
|
return result
|
|
|
|
def run_batch(self, tasks: List[str], *args, **kwargs):
|
|
"""Run the swarm asynchronously
|
|
|
|
Args:
|
|
task (Optional[str], optional): _description_. Defaults to None.
|
|
"""
|
|
return self.batched_run(tasks, *args, **kwargs)
|
|
|
|
def reset_all_agents(self):
|
|
"""Reset all agents
|
|
|
|
Returns:
|
|
|
|
"""
|
|
for agent in self.agents:
|
|
agent.reset()
|
|
|
|
def select_agent(self, agent_id: str):
|
|
"""
|
|
Select an agent through their id
|
|
"""
|
|
# Find agent with id
|
|
for agent in self.agents:
|
|
if agent.id == agent_id:
|
|
return agent
|
|
|
|
def select_agent_by_name(self, agent_name: str):
|
|
"""
|
|
Select an agent through their name
|
|
"""
|
|
# Find agent with id
|
|
for agent in self.agents:
|
|
if agent.name == agent_name:
|
|
return agent
|
|
|
|
def task_assignment_by_id(
|
|
self, task: str, agent_id: str, *args, **kwargs
|
|
):
|
|
"""
|
|
Assign a task to an agent
|
|
"""
|
|
# Assign task to agent by their agent id
|
|
agent = self.select_agent(agent_id)
|
|
return agent.run(task, *args, **kwargs)
|
|
|
|
def task_assignment_by_name(
|
|
self, task: str, agent_name: str, *args, **kwargs
|
|
):
|
|
"""
|
|
Assign a task to an agent
|
|
"""
|
|
# Assign task to agent by their agent id
|
|
agent = self.select_agent_by_name(agent_name)
|
|
return agent.run(task, *args, **kwargs)
|
|
|
|
def concurrent_run(self, task: str) -> List[str]:
|
|
"""Synchronously run the task on all llms and collect responses"""
|
|
with ThreadPoolExecutor() as executor:
|
|
future_to_llm = {
|
|
executor.submit(agent, task): agent
|
|
for agent in self.agents
|
|
}
|
|
responses = []
|
|
for future in as_completed(future_to_llm):
|
|
try:
|
|
responses.append(future.result())
|
|
except Exception as error:
|
|
print(
|
|
f"{future_to_llm[future]} generated an"
|
|
f" exception: {error}"
|
|
)
|
|
self.last_responses = responses
|
|
self.task_history.append(task)
|
|
return responses
|
|
|
|
def add_llm(self, agent: Callable):
|
|
"""Add an llm to the god mode"""
|
|
self.agents.append(agent)
|
|
|
|
def remove_llm(self, agent: Callable):
|
|
"""Remove an llm from the god mode"""
|
|
self.agents.remove(agent)
|
|
|
|
def add_agent(self, agent: Agent = None, *args, **kwargs):
|
|
"""Add an agent to the swarm
|
|
|
|
Args:
|
|
agent (Agent, optional): _description_. Defaults to None.
|
|
|
|
Returns:
|
|
_type_: _description_
|
|
"""
|
|
self.agents.append(agent)
|
|
return agent
|
|
|
|
def run_all(self, task: str = None, *args, **kwargs):
|
|
"""Run all agents
|
|
|
|
Args:
|
|
task (str, optional): _description_. Defaults to None.
|
|
|
|
Returns:
|
|
_type_: _description_
|
|
"""
|
|
responses = []
|
|
for agent in self.agents:
|
|
responses.append(agent(task, *args, **kwargs))
|
|
return responses
|
|
|
|
def run_on_all_agents(self, task: str = None, *args, **kwargs):
|
|
"""Run on all agents
|
|
|
|
Args:
|
|
task (str, optional): _description_. Defaults to None.
|
|
|
|
Returns:
|
|
_type_: _description_
|
|
"""
|
|
with ThreadPoolExecutor() as executor:
|
|
responses = executor.map(
|
|
lambda agent: agent(task, *args, **kwargs),
|
|
self.agents,
|
|
)
|
|
return list(responses)
|