Files
2026-07-13 12:43:34 +08:00

278 lines
12 KiB
Python

import json
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import JSONB
from superagi.lib.logger import logger
from superagi.models.base_model import DBBaseModel
from superagi.models.workflows.agent_workflow_step_tool import AgentWorkflowStepTool
from superagi.models.workflows.agent_workflow_step_wait import AgentWorkflowStepWait
from superagi.models.workflows.iteration_workflow import IterationWorkflow
class AgentWorkflowStep(DBBaseModel):
"""
Step of an agent workflow
Attributes:
id (int): The unique identifier of the agent workflow step.
agent_workflow_id (int): The ID of the agent workflow to which this step belongs.
unique_id (str): The unique identifier of the step.
step_type (str): The type of the step (TRIGGER, NORMAL).
action_type (str): The type of the action (TOOL, ITERATION_WORKFLOW, LLM).
action_reference_id: Reference id of the tool/iteration workflow/llm
next_steps: Next steps output and step id.
"""
__tablename__ = 'agent_workflow_steps'
id = Column(Integer, primary_key=True)
agent_workflow_id = Column(Integer)
unique_id = Column(String)
step_type = Column(String) # TRIGGER, NORMAL
action_type = Column(String) # TOOL, ITERATION_WORKFLOW, LLM, WAIT_STEP
action_reference_id = Column(Integer) # id of the action
next_steps = Column(JSONB) # edge_ref_id, response, step_id
def __repr__(self):
"""
Returns a string representation of the AgentWorkflowStep object.
Returns:
str: String representation of the AgentWorkflowStep.
"""
return f"AgentWorkflowStep(id={self.id}, status='{self.agent_workflow_id}', " \
f"prompt='{self.unique_id}', agent_id={self.step_type}, action_type={self.action_type}, " \
f"action_reference_id={self.action_reference_id}, next_steps={self.next_steps})"
def to_dict(self):
"""
Converts the AgentWorkflowStep object to a dictionary.
Returns:
dict: Dictionary representation of the AgentWorkflowStep.
"""
return {
'id': self.id,
'agent_workflow_id': self.agent_workflow_id,
'unique_id': self.unique_id,
'step_type': self.step_type,
'next_steps': self.next_steps,
'action_type': self.action_type,
'action_reference_id': self.action_reference_id
}
def to_json(self):
"""
Converts the AgentWorkflowStep object to a JSON string.
Returns:
str: JSON string representation of the AgentWorkflowStep.
"""
return json.dumps(self.to_dict())
@classmethod
def from_json(cls, json_data):
"""
Creates an AgentWorkflowStep object from a JSON string.
Args:
json_data (str): JSON string representing the AgentWorkflowStep.
Returns:
AgentWorkflowStep: AgentWorkflowStep object created from the JSON string.
"""
data = json.loads(json_data)
return cls(
id=data['id'],
agent_workflow_id=data['agent_workflow_id'],
unique_id=data['unique_id'],
step_type=data['step_type'],
action_type=data['action_type'],
action_reference_id=data['action_reference_id'],
next_steps=data['next_steps'],
)
@classmethod
def find_by_unique_id(cls, session, unique_id: str):
""" Adds a workflows step in the next_steps column"""
return session.query(AgentWorkflowStep).filter(AgentWorkflowStep.unique_id == unique_id).first()
@classmethod
def find_by_id(cls, session, step_id: int):
""" Find the workflow step by id"""
return session.query(AgentWorkflowStep).filter(AgentWorkflowStep.id == step_id).first()
@classmethod
def find_or_create_tool_workflow_step(cls, session, agent_workflow_id: int, unique_id: str,
tool_name: str, input_instruction: str,
output_instruction: str = "", step_type="NORMAL",
history_enabled: bool = True, completion_prompt: str = None):
""" Find or create a tool workflow step
Args:
session: db session
agent_workflow_id: id of the agent workflow
unique_id: unique id of the step
tool_name: name of the tool
input_instruction: input instruction of the tool
output_instruction: output instruction of the tool
step_type: type of the step
history_enabled: whether to enable history for the step
completion_prompt: completion prompt in the llm
Returns:
AgentWorkflowStep.
"""
workflow_step = session.query(AgentWorkflowStep).filter(
AgentWorkflowStep.agent_workflow_id == agent_workflow_id, AgentWorkflowStep.unique_id == unique_id).first()
if completion_prompt is None:
completion_prompt = f"Respond with only valid JSON conforming to the given json schema. Response should contain tool name and tool arguments to achieve the given instruction."
step_tool = AgentWorkflowStepTool.find_or_create_tool(session, unique_id, tool_name,
input_instruction, output_instruction,
history_enabled, completion_prompt)
if workflow_step is None:
workflow_step = AgentWorkflowStep(unique_id=unique_id, step_type=step_type,
agent_workflow_id=agent_workflow_id)
session.add(workflow_step)
session.commit()
workflow_step.step_type = step_type
workflow_step.agent_workflow_id = agent_workflow_id
workflow_step.action_reference_id = step_tool.id
workflow_step.action_type = "TOOL"
workflow_step.next_steps = []
workflow_step.completion_prompt = completion_prompt
session.commit()
return workflow_step
@classmethod
def find_or_create_wait_workflow_step(cls, session, agent_workflow_id: int, unique_id: str,
wait_description: str, delay: int, step_type="NORMAL"):
""" Find or create a wait workflow step"""
logger.info("Finding or creating wait step")
workflow_step = session.query(AgentWorkflowStep).filter(
AgentWorkflowStep.agent_workflow_id == agent_workflow_id, AgentWorkflowStep.unique_id == unique_id).first()
step_wait = AgentWorkflowStepWait.find_or_create_wait(session=session, step_unique_id=unique_id,
description=wait_description, delay=delay)
if workflow_step is None:
workflow_step = AgentWorkflowStep(unique_id=unique_id, step_type=step_type,
agent_workflow_id=agent_workflow_id)
session.add(workflow_step)
session.commit()
workflow_step.step_type = step_type
workflow_step.agent_workflow_id = agent_workflow_id
workflow_step.action_reference_id = step_wait.id
workflow_step.action_type = "WAIT_STEP"
workflow_step.next_steps = []
session.commit()
return workflow_step
@classmethod
def find_or_create_iteration_workflow_step(cls, session, agent_workflow_id: int, unique_id: str,
iteration_workflow_name: str, step_type="NORMAL"):
""" Find or create a iteration workflow step
Args:
session: db session
agent_workflow_id: id of the agent workflow
unique_id: unique id of the step
iteration_workflow_name: name of the iteration workflow
step_type: type of the step
Returns:
AgentWorkflowStep.
"""
workflow_step = session.query(AgentWorkflowStep).filter(
AgentWorkflowStep.agent_workflow_id == agent_workflow_id, AgentWorkflowStep.unique_id == unique_id).first()
iteration_workflow = IterationWorkflow.find_workflow_by_name(session, iteration_workflow_name)
if workflow_step is None:
workflow_step = AgentWorkflowStep(unique_id=unique_id, step_type=step_type,
agent_workflow_id=agent_workflow_id)
session.add(workflow_step)
session.commit()
workflow_step.step_type = step_type
workflow_step.agent_workflow_id = agent_workflow_id
workflow_step.action_reference_id = iteration_workflow.id
workflow_step.action_type = "ITERATION_WORKFLOW"
workflow_step.next_steps = []
session.commit()
return workflow_step
@classmethod
def add_next_workflow_step(cls, session, current_agent_step_id: int, next_step_id: int,
step_response: str = "default"):
""" Add Next workflow steps in the next_steps column
Args:
session: db session
current_agent_step_id: id of the current agent step
next_step_id: id of the next agent step
step_response: response of the current step
"""
next_unique_id = "-1"
if next_step_id != -1:
next_workflow_step = AgentWorkflowStep.find_by_id(session, next_step_id)
next_unique_id = next_workflow_step.unique_id
current_step = session.query(AgentWorkflowStep).filter(AgentWorkflowStep.id == current_agent_step_id).first()
next_steps = json.loads(json.dumps(current_step.next_steps))
existing_steps = [step for step in next_steps if step["step_id"] == next_unique_id]
if existing_steps:
existing_steps[0]["step_response"] = step_response
current_step.next_steps = next_steps
else:
next_steps.append({"step_response": str(step_response), "step_id": str(next_unique_id)})
current_step.next_steps = next_steps
session.commit()
return current_step
@classmethod
def fetch_default_next_step(cls, session, current_agent_step_id: int):
""" Fetches the default next step
Args:
session: db session
current_agent_step_id: id of the current agent step
"""
current_step = AgentWorkflowStep.find_by_id(session, current_agent_step_id)
next_steps = current_step.next_steps
default_steps = [step for step in next_steps if step["step_response"] == "default"]
if default_steps:
return AgentWorkflowStep.find_by_unique_id(session, default_steps[0]["step_id"])
return None
@classmethod
def fetch_next_step(cls, session, current_agent_step_id: int, step_response: str):
""" Fetch the next step based on the step response
Args:
session: db session
current_agent_step_id: id of the current agent step
step_response: response of the current step
"""
current_step = AgentWorkflowStep.find_by_id(session, current_agent_step_id)
next_steps = current_step.next_steps
matching_steps = [step for step in next_steps if str(step["step_response"]).lower() == step_response.lower()]
if matching_steps:
if str(matching_steps[0]["step_id"]) == "-1":
return "COMPLETE"
return AgentWorkflowStep.find_by_unique_id(session, matching_steps[0]["step_id"])
logger.info(f"Could not find next step for step_id: {current_agent_step_id} and step_response: {step_response}")
default_steps = [step for step in next_steps if str(step["step_response"]).lower() == "default"]
if default_steps:
if str(default_steps[0]["step_id"]) == "-1":
return "COMPLETE"
return AgentWorkflowStep.find_by_unique_id(session, default_steps[0]["step_id"])
return None