alex-mindspace's picture
(hopefully) working swarm demo
b3509ba
import uuid
import pandas as pd
from datetime import datetime
from swarmai.utils.task_queue.TaskQueueBase import TaskQueueBase
from swarmai.utils.task_queue.Task import Task
from swarmai.agents.AgentBase import AgentBase
class PandasQueue(TaskQueueBase):
"""Super simple implementatin of the versatile task queue using pandas DataFrame.
Pretty slow, but allows for easy manipulation of tasks, filtering, etc.
Thread-safeness is handeled by the TaskQueueBase class.
In the current swarm architecture the taks should have following attributes:
- task_id: unique identifier of the task
- priority: priority of the task. Task queue will first return high priority tasks.
- task_type: type of the task, so that specific agents can filter tasks
- task_description: description of the task
- status: status of the task, e.g. "pending", "in progress", "completed", "failed", 'cancelled'
"""
def __init__(self, task_types: list, agent_types: list, task_association: dict):
"""
Task association is a dictionary that returns a list of task_types for a given agent_type.
Attributes:
- task_types (list[str]): list of task types that are supported by the task queue
- agent_types (list[str]): list of agent types that are supported by the task queue
- task_association (dict): dictionary that returns a list of task_types for a given agent_type
"""
super().__init__()
self.columns = ["task_id", "priority", "task_type", "task_description", "status", "add_time", "claim_time", "complete_time", "claim_agent_id"]
self.tasks = pd.DataFrame(columns=self.columns)
self.task_types = task_types
self.agent_types = agent_types
self.task_association = task_association
def add_task(self, task: Task) -> bool:
"""Adds a task to the queue.
Task attr = (task_id, priority, task_type, task_description, status)
"""
if task.task_type not in self.task_types:
raise ValueError(f"Task type {task.task_type} is not supported.")
if task.task_description is None:
raise ValueError(f"Task description {task.task_description} is not valid.")
if isinstance(task.task_description, str) == False:
raise ValueError(f"Task description {task.task_description} is not valid.")
if task.task_description == "":
raise ValueError(f"Task description {task.task_description} is not valid.")
priority = task.priority
task_type = task.task_type
task_description = task.task_description
status = "pending"
add_time = datetime.now()
task_i = pd.DataFrame([[uuid.uuid4(), priority, task_type, task_description, status, add_time, None, None, None]], columns=self.columns)
self.tasks = pd.concat([self.tasks, task_i], ignore_index=True)
def get_task(self, agent: AgentBase) -> Task:
"""Gets the next task from the queue, based on the agent type
"""
supported_tasks = self._get_supported_tasks(agent.agent_type)
df_clone = self.tasks.copy()
# get only pending tasks
df_clone = df_clone[df_clone["status"] == "pending"]
# get only supported tasks
df_clone = df_clone[df_clone["task_type"].isin(supported_tasks)]
if len(df_clone) == 0:
return None
# sort by priority
df_clone = df_clone.sort_values(by="priority", ascending=False)
# get the first task
task = df_clone.iloc[0]
# claim the task
status = "in progress"
claim_time = datetime.now()
claim_agent_id = agent.agent_id
task_obj = Task(task_id=task["task_id"], priority=task["priority"], task_type=task["task_type"], task_description=task["task_description"], status=status)
# update the task in the queue
df_i = pd.DataFrame([[task["task_id"], task["priority"], task["task_type"], task["task_description"], status, task["add_time"], claim_time, None, claim_agent_id]], columns=self.columns)
self.tasks = self.tasks[self.tasks["task_id"] != task["task_id"]]
self.tasks = pd.concat([self.tasks, df_i], ignore_index=True)
return task_obj
def complete_task(self, task_id):
"""Completes the task with the given task_id.
"""
task = self.tasks[self.tasks["task_id"] == task_id]
if len(task) == 0:
"""In case task was deleted from the queue"""
return False
task = task.iloc[0]
if task["status"] != "in progress":
return False
status = "completed"
complete_time = datetime.now()
df_i = pd.DataFrame([[task["task_id"], task["priority"], task["task_type"], task["task_description"], status, task["add_time"], task["claim_time"], complete_time, task["claim_agent_id"]]], columns=self.columns)
self.tasks = self.tasks[self.tasks["task_id"] != task["task_id"]]
self.tasks = pd.concat([self.tasks, df_i], ignore_index=True)
return True
def reset_task(self, task_id: str):
task = self.tasks[self.tasks["task_id"] == task_id]
if len(task) == 0:
"""In case task was deleted from the queue"""
return False
task = task.iloc[0]
status = "pending"
df_i = pd.DataFrame([[task["task_id"], task["priority"], task["task_type"], task["task_description"], status, task["add_time"], None, None, None]], columns=self.columns)
self.tasks = self.tasks[self.tasks["task_id"] != task["task_id"]]
self.tasks = pd.concat([self.tasks, df_i], ignore_index=True)
return True
def _get_supported_tasks(self, agent_type):
"""Returns a list of supported tasks for a given agent type.
"""
if agent_type not in self.agent_types:
raise ValueError(f"Agent type {agent_type} is not supported.")
if self.task_association is None:
# get all present task types
return self.task_types
return self.task_association[agent_type]
def get_all_tasks(self):
"""Returns all tasks in the queue.
Allows the manager model to bush up the tasks list to delete duplicates or unnecessary tasks.
"""
raise NotImplementedError