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

219 lines
13 KiB
Python

from datetime import datetime
import json
from sqlalchemy import asc
from sqlalchemy.sql.operators import and_
import logging
import superagi
from superagi.agent.agent_message_builder import AgentLlmMessageBuilder
from superagi.agent.agent_prompt_builder import AgentPromptBuilder
from superagi.agent.output_handler import ToolOutputHandler, get_output_handler
from superagi.agent.task_queue import TaskQueue
from superagi.agent.tool_builder import ToolBuilder
from superagi.apm.event_handler import EventHandler
from superagi.config.config import get_config
from superagi.helper.error_handler import ErrorHandler
from superagi.helper.token_counter import TokenCounter
from superagi.lib.logger import logger
from superagi.models.agent import Agent
from superagi.models.agent_config import AgentConfiguration
from superagi.models.agent_execution import AgentExecution
from superagi.models.agent_execution_config import AgentExecutionConfiguration
from superagi.models.agent_execution_feed import AgentExecutionFeed
from superagi.models.agent_execution_permission import AgentExecutionPermission
from superagi.models.organisation import Organisation
from superagi.models.tool import Tool
from superagi.models.workflows.agent_workflow import AgentWorkflow
from superagi.models.workflows.agent_workflow_step import AgentWorkflowStep
from superagi.models.workflows.iteration_workflow import IterationWorkflow
from superagi.models.workflows.iteration_workflow_step import IterationWorkflowStep
from superagi.resource_manager.resource_summary import ResourceSummarizer
from superagi.tools.resource.query_resource import QueryResourceTool
from superagi.tools.thinking.tools import ThinkingTool
from superagi.apm.call_log_helper import CallLogHelper
class AgentIterationStepHandler:
""" Handles iteration workflow steps in the agent workflow."""
def __init__(self, session, llm, agent_id: int, agent_execution_id: int, memory=None):
self.session = session
self.llm = llm
self.agent_execution_id = agent_execution_id
self.agent_id = agent_id
self.memory = memory
self.organisation = Agent.find_org_by_agent_id(self.session, agent_id=self.agent_id)
self.task_queue = TaskQueue(str(self.agent_execution_id))
def execute_step(self):
agent_config = Agent.fetch_configuration(self.session, self.agent_id)
execution = AgentExecution.get_agent_execution_from_id(self.session, self.agent_execution_id)
iteration_workflow_step = IterationWorkflowStep.find_by_id(self.session, execution.iteration_workflow_step_id)
agent_execution_config = AgentExecutionConfiguration.fetch_configuration(self.session, self.agent_execution_id)
if not self._handle_wait_for_permission(execution, agent_config, agent_execution_config,
iteration_workflow_step):
return
workflow_step = AgentWorkflowStep.find_by_id(self.session, execution.current_agent_step_id)
organisation = Agent.find_org_by_agent_id(self.session, agent_id=self.agent_id)
iteration_workflow = IterationWorkflow.find_by_id(self.session, workflow_step.action_reference_id)
agent_feeds = AgentExecutionFeed.fetch_agent_execution_feeds(self.session, self.agent_execution_id)
if not agent_feeds:
self.task_queue.clear_tasks()
agent_tools = self._build_tools(agent_config, agent_execution_config)
prompt = self._build_agent_prompt(iteration_workflow=iteration_workflow,
agent_config=agent_config,
agent_execution_config=agent_execution_config,
prompt=iteration_workflow_step.prompt,
agent_tools=agent_tools)
messages = AgentLlmMessageBuilder(self.session, self.llm, self.llm.get_model(), self.agent_id, self.agent_execution_id) \
.build_agent_messages(prompt, agent_feeds, history_enabled=iteration_workflow_step.history_enabled,
completion_prompt=iteration_workflow_step.completion_prompt)
logger.debug("Prompt messages:", messages)
current_tokens = TokenCounter.count_message_tokens(messages = messages, model = self.llm.get_model())
response = self.llm.chat_completion(messages, TokenCounter(session=self.session, organisation_id=organisation.id).token_limit(self.llm.get_model()) - current_tokens)
if 'error' in response and response['message'] is not None:
ErrorHandler.handle_openai_errors(self.session, self.agent_id, self.agent_execution_id, response['message'])
if 'content' not in response or response['content'] is None:
raise RuntimeError(f"Failed to get response from llm")
total_tokens = current_tokens + TokenCounter.count_message_tokens(response['content'], self.llm.get_model())
AgentExecution.update_tokens(self.session, self.agent_execution_id, total_tokens)
try:
content = json.loads(response['content'])
tool = content.get('tool', {})
tool_name = tool.get('name', '') if tool else ''
except json.JSONDecodeError:
print("Decoding JSON has failed")
tool_name = ''
CallLogHelper(session=self.session, organisation_id=organisation.id).create_call_log(execution.name,
agent_config['agent_id'], total_tokens, tool_name, agent_config['model'])
assistant_reply = response['content']
output_handler = get_output_handler(iteration_workflow_step.output_type,
agent_execution_id=self.agent_execution_id,
agent_config=agent_config,memory=self.memory, agent_tools=agent_tools)
response = output_handler.handle(self.session, assistant_reply)
if response.status == "COMPLETE":
execution.status = "COMPLETED"
self.session.commit()
self._update_agent_execution_next_step(execution, iteration_workflow_step.next_step_id, "COMPLETE")
EventHandler(session=self.session).create_event('run_completed',
{'agent_execution_id': execution.id,
'name': execution.name,
'tokens_consumed': execution.num_of_tokens,
"calls": execution.num_of_calls},
execution.agent_id, organisation.id)
elif response.status == "WAITING_FOR_PERMISSION":
execution.status = "WAITING_FOR_PERMISSION"
execution.permission_id = response.permission_id
self.session.commit()
else:
# moving to next step of iteration or workflow
self._update_agent_execution_next_step(execution, iteration_workflow_step.next_step_id)
logger.info(f"Starting next job for agent execution id: {self.agent_execution_id}")
self.session.flush()
def _update_agent_execution_next_step(self, execution, next_step_id, step_response: str = "default"):
if next_step_id == -1:
next_step = AgentWorkflowStep.fetch_next_step(self.session, execution.current_agent_step_id, step_response)
if str(next_step) == "COMPLETE":
execution.current_agent_step_id = -1
execution.status = "COMPLETED"
else:
AgentExecution.assign_next_step_id(self.session, self.agent_execution_id, next_step.id)
else:
execution.iteration_workflow_step_id = next_step_id
self.session.commit()
def _build_agent_prompt(self, iteration_workflow: IterationWorkflow, agent_config: dict,
agent_execution_config: dict,
prompt: str, agent_tools: list):
max_token_limit = int(get_config("MAX_TOOL_TOKEN_LIMIT", 600))
prompt = AgentPromptBuilder.replace_main_variables(prompt, agent_execution_config["goal"],
agent_execution_config["instruction"],
agent_config["constraints"], agent_tools,
(not iteration_workflow.has_task_queue))
if iteration_workflow.has_task_queue:
response = self.task_queue.get_last_task_details()
last_task, last_task_result = (response["task"], response["response"]) if response is not None else ("", "")
current_task = self.task_queue.get_first_task() or ""
token_limit = TokenCounter(session=self.session, organisation_id=self.organisation.id).token_limit() - max_token_limit
prompt = AgentPromptBuilder.replace_task_based_variables(prompt, current_task, last_task, last_task_result,
self.task_queue.get_tasks(),
self.task_queue.get_completed_tasks(), token_limit)
return prompt
def _build_tools(self, agent_config: dict, agent_execution_config: dict):
agent_tools = [ThinkingTool()]
config_data = AgentConfiguration.get_model_api_key(self.session, self.agent_id, agent_config["model"])
model_api_key = config_data['api_key']
tool_builder = ToolBuilder(self.session, self.agent_id, self.agent_execution_id)
resource_summary = ResourceSummarizer(session=self.session, agent_id=self.agent_id, model=agent_config['model']).fetch_or_create_agent_resource_summary(default_summary=agent_config.get("resource_summary"))
if resource_summary is not None:
agent_tools.append(QueryResourceTool())
user_tools = self.session.query(Tool).filter(
and_(Tool.id.in_(agent_execution_config["tools"]), Tool.file_name is not None)).all()
for tool in user_tools:
agent_tools.append(tool_builder.build_tool(tool))
agent_tools = [tool_builder.set_default_params_tool(tool, agent_config, agent_execution_config,
model_api_key, resource_summary,self.memory) for tool in agent_tools]
return agent_tools
def _handle_wait_for_permission(self, agent_execution, agent_config: dict, agent_execution_config: dict,
iteration_workflow_step: IterationWorkflowStep):
"""
Handles the wait for permission when the agent execution is waiting for permission.
Args:
agent_execution (AgentExecution): The agent execution.
agent_config (dict): The agent configuration.
agent_execution_config (dict): The agent execution configuration.
iteration_workflow_step (IterationWorkflowStep): The iteration workflow step.
Raises:
Returns permission success or failure
"""
if agent_execution.status != "WAITING_FOR_PERMISSION":
return True
agent_execution_permission = self.session.query(AgentExecutionPermission).filter(
AgentExecutionPermission.id == agent_execution.permission_id).first()
if agent_execution_permission.status == "PENDING":
logger.error("handle_wait_for_permission: Permission is still pending")
return False
if agent_execution_permission.status == "APPROVED":
agent_tools = self._build_tools(agent_config, agent_execution_config)
tool_output_handler = ToolOutputHandler(self.agent_execution_id, agent_config, agent_tools,self.memory)
tool_result = tool_output_handler.handle_tool_response(self.session,
agent_execution_permission.assistant_reply)
result = tool_result.result
else:
result = f"User denied the permission to run the tool {agent_execution_permission.tool_name}" \
f"{' and has given the following feedback : ' + agent_execution_permission.user_feedback if agent_execution_permission.user_feedback else ''}"
agent_execution_feed = AgentExecutionFeed(agent_execution_id=agent_execution_permission.agent_execution_id,
agent_id=agent_execution_permission.agent_id,
feed=agent_execution_permission.assistant_reply,
role="assistant",
feed_group_id=agent_execution.current_feed_group_id)
self.session.add(agent_execution_feed)
agent_execution_feed1 = AgentExecutionFeed(agent_execution_id=agent_execution_permission.agent_execution_id,
agent_id=agent_execution_permission.agent_id,
feed=result, role="user",
feed_group_id=agent_execution.current_feed_group_id)
self.session.add(agent_execution_feed1)
agent_execution.status = "RUNNING"
execution = AgentExecution.find_by_id(self.session, agent_execution_permission.agent_execution_id)
self._update_agent_execution_next_step(execution, iteration_workflow_step.next_step_id)
self.session.commit()
return True