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

420 lines
17 KiB
Python

from fastapi import APIRouter
from fastapi import HTTPException, Depends
from fastapi_jwt_auth import AuthJWT
from fastapi_sqlalchemy import db
from pydantic import BaseModel
from sqlalchemy import desc
import ast
from pytz import timezone
from sqlalchemy import func, or_
from superagi.models.agent import Agent
from superagi.models.agent_execution_config import AgentExecutionConfiguration
from superagi.models.agent_config import AgentConfiguration
from superagi.models.agent_schedule import AgentSchedule
from superagi.models.agent_template import AgentTemplate
from superagi.models.project import Project
from superagi.models.workflows.agent_workflow import AgentWorkflow
from superagi.models.agent_execution import AgentExecution
from superagi.models.tool import Tool
from superagi.controllers.types.agent_schedule import AgentScheduleInput
from superagi.controllers.types.agent_with_config import AgentConfigInput
from superagi.controllers.types.agent_with_config_schedule import AgentConfigSchedule
from jsonmerge import merge
from datetime import datetime
import json
from superagi.models.toolkit import Toolkit
from superagi.models.knowledges import Knowledges
from sqlalchemy import func
# from superagi.types.db import AgentOut, AgentIn
from superagi.helper.auth import check_auth
from superagi.apm.event_handler import EventHandler
from superagi.models.workflows.iteration_workflow import IterationWorkflow
router = APIRouter()
class AgentOut(BaseModel):
id: int
name: str
project_id: int
description: str
created_at: datetime
updated_at: datetime
class Config:
orm_mode = True
class AgentIn(BaseModel):
name: str
project_id: int
description: str
class Config:
orm_mode = True
@router.post("/create", status_code=201)
def create_agent_with_config(agent_with_config: AgentConfigInput,
Authorize: AuthJWT = Depends(check_auth)):
"""
Create a new agent with configurations.
Args:
agent_with_config (AgentConfigInput): Data for creating a new agent with configurations.
- name (str): Name of the agent.
- project_id (int): Identifier of the associated project.
- description (str): Description of the agent.
- goal (List[str]): List of goals for the agent.
- constraints (List[str]): List of constraints for the agent.
- tools (List[int]): List of tool identifiers associated with the agent.
- exit (str): Exit condition for the agent.
- iteration_interval (int): Interval between iterations for the agent.
- model (str): Model information for the agent.
- permission_type (str): Permission type for the agent.
- LTM_DB (str): LTM database for the agent.
- max_iterations (int): Maximum number of iterations for the agent.
- user_timezone (string): Timezone of the user
Returns:
dict: Dictionary containing the created agent's ID, execution ID, name, and content type.
Raises:
HTTPException (status_code=404): If the associated project or any of the tools is not found.
"""
project = db.session.query(Project).get(agent_with_config.project_id)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
invalid_tools = Tool.get_invalid_tools(agent_with_config.tools, db.session)
if len(invalid_tools) > 0: # If the returned value is not True (then it is an invalid tool_id)
raise HTTPException(status_code=404,
detail=f"Tool with IDs {str(invalid_tools)} does not exist. 404 Not Found.")
agent_toolkit_tools = Toolkit.fetch_tool_ids_from_toolkit(session=db.session,
toolkit_ids=agent_with_config.toolkits)
agent_with_config.tools.extend(agent_toolkit_tools)
db_agent = Agent.create_agent_with_config(db, agent_with_config)
start_step = AgentWorkflow.fetch_trigger_step_id(db.session, db_agent.agent_workflow_id)
iteration_step_id = IterationWorkflow.fetch_trigger_step_id(db.session,
start_step.action_reference_id).id if start_step.action_type == "ITERATION_WORKFLOW" else -1
# Creating an execution with RUNNING status
execution = AgentExecution(status='CREATED', last_execution_time=datetime.now(), agent_id=db_agent.id,
name="New Run", current_agent_step_id=start_step.id, iteration_workflow_step_id=iteration_step_id)
agent_execution_configs = {
"goal": agent_with_config.goal,
"instruction": agent_with_config.instruction,
"constraints": agent_with_config.constraints,
"toolkits": agent_with_config.toolkits,
"exit": agent_with_config.exit,
"tools": agent_with_config.tools,
"iteration_interval": agent_with_config.iteration_interval,
"model": agent_with_config.model,
"permission_type": agent_with_config.permission_type,
"LTM_DB": agent_with_config.LTM_DB,
"max_iterations": agent_with_config.max_iterations,
"user_timezone": agent_with_config.user_timezone,
"knowledge": agent_with_config.knowledge
}
db.session.add(execution)
db.session.commit()
db.session.flush()
AgentExecutionConfiguration.add_or_update_agent_execution_config(session=db.session, execution=execution,
agent_execution_configs=agent_execution_configs)
agent = db.session.query(Agent).filter(Agent.id == db_agent.id, ).first()
organisation = agent.get_agent_organisation(db.session)
EventHandler(session=db.session).create_event('run_created',
{'agent_execution_id': execution.id,
'agent_execution_name': execution.name},
db_agent.id,
organisation.id if organisation else 0),
if agent_with_config.knowledge:
knowledge_name = db.session.query(Knowledges.name).filter(Knowledges.id == agent_with_config.knowledge).first()[0]
EventHandler(session=db.session).create_event('knowledge_picked',
{'knowledge_name': knowledge_name,
'agent_execution_id': execution.id},
db_agent.id,
organisation.id if organisation else 0)
EventHandler(session=db.session).create_event('agent_created',
{'agent_name': agent_with_config.name,
'model': agent_with_config.model},
db_agent.id,
organisation.id if organisation else 0)
db.session.commit()
return {
"id": db_agent.id,
"execution_id": execution.id,
"name": db_agent.name,
"contentType": "Agents"
}
@router.post("/schedule", status_code=201)
def create_and_schedule_agent(agent_config_schedule: AgentConfigSchedule,
Authorize: AuthJWT = Depends(check_auth)):
"""
Create a new agent with configurations and scheduling.
Args:
agent_with_config_schedule (AgentConfigSchedule): Data for creating a new agent with configurations and scheduling.
Returns:
dict: Dictionary containing the created agent's ID, name, content type and schedule ID of the agent.
Raises:
HTTPException (status_code=500): If the associated agent fails to get scheduled.
"""
project = db.session.query(Project).get(agent_config_schedule.agent_config.project_id)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
agent_config = agent_config_schedule.agent_config
invalid_tools = Tool.get_invalid_tools(agent_config.tools, db.session)
if len(invalid_tools) > 0: # If the returned value is not True (then it is an invalid tool_id)
raise HTTPException(status_code=404,
detail=f"Tool with IDs {str(invalid_tools)} does not exist. 404 Not Found.")
agent_toolkit_tools = Toolkit.fetch_tool_ids_from_toolkit(session=db.session,
toolkit_ids=agent_config.toolkits)
agent_config.tools.extend(agent_toolkit_tools)
db_agent = Agent.create_agent_with_config(db, agent_config)
# Update the agent_id of schedule before scheduling the agent
agent_schedule = agent_config_schedule.schedule
# Create a new agent schedule
agent_schedule = AgentSchedule(
agent_id=db_agent.id,
start_time=agent_schedule.start_time,
next_scheduled_time=agent_schedule.start_time,
recurrence_interval=agent_schedule.recurrence_interval,
expiry_date=agent_schedule.expiry_date,
expiry_runs=agent_schedule.expiry_runs,
current_runs=0,
status="SCHEDULED"
)
agent_schedule.agent_id = db_agent.id
db.session.add(agent_schedule)
db.session.commit()
if agent_schedule.id is None:
raise HTTPException(status_code=500, detail="Failed to schedule agent")
agent = db.session.query(Agent).filter(Agent.id == db_agent.id, ).first()
organisation = agent.get_agent_organisation(db.session)
EventHandler(session=db.session).create_event('agent_created', {'agent_name': agent_config.name,
'model': agent_config.model}, db_agent.id,
organisation.id if organisation else 0)
db.session.commit()
return {
"id": db_agent.id,
"name": db_agent.name,
"contentType": "Agents",
"schedule_id": agent_schedule.id
}
@router.post("/stop/schedule", status_code=200)
def stop_schedule(agent_id: int, Authorize: AuthJWT = Depends(check_auth)):
"""
Stopping the scheduling for a given agent.
Args:
agent_id (int): Identifier of the Agent
Authorize (AuthJWT, optional): Authorization dependency. Defaults to Depends(check_auth).
Raises:
HTTPException (status_code=404): If the agent schedule is not found.
"""
agent_to_delete = db.session.query(AgentSchedule).filter(AgentSchedule.agent_id == agent_id,
AgentSchedule.status == "SCHEDULED").first()
if not agent_to_delete:
raise HTTPException(status_code=404, detail="Schedule not found")
agent_to_delete.status = "STOPPED"
db.session.commit()
@router.put("/edit/schedule", status_code=200)
def edit_schedule(schedule: AgentScheduleInput,
Authorize: AuthJWT = Depends(check_auth)):
"""
Edit the scheduling for a given agent.
Args:
agent_id (int): Identifier of the Agent
schedule (AgentSchedule): New schedule data
Authorize (AuthJWT, optional): Authorization dependency. Defaults to Depends(check_auth).
Raises:
HTTPException (status_code=404): If the agent schedule is not found.
"""
agent_to_edit = db.session.query(AgentSchedule).filter(AgentSchedule.agent_id == schedule.agent_id, AgentSchedule.status == "SCHEDULED").first()
if not agent_to_edit:
raise HTTPException(status_code=404, detail="Schedule not found")
# Update agent schedule with new data
agent_to_edit.start_time = schedule.start_time
agent_to_edit.next_scheduled_time = schedule.start_time
agent_to_edit.recurrence_interval = schedule.recurrence_interval
agent_to_edit.expiry_date = schedule.expiry_date
agent_to_edit.expiry_runs = schedule.expiry_runs
db.session.commit()
@router.get("/get/schedule_data/{agent_id}")
def get_schedule_data(agent_id: int, Authorize: AuthJWT = Depends(check_auth)):
"""
Get the scheduling data for a given agent.
Args:
agent_id (int): Identifier of the Agent
Raises:
HTTPException (status_code=404): If the agent schedule is not found.
Returns:
current_datetime (DateTime): Current Date and Time.
recurrence_interval (String): Time interval for recurring schedule run.
expiry_date (DateTime): The date and time when the agent is scheduled to stop runs.
expiry_runs (Integer): The number of runs before the agent expires.
"""
agent = db.session.query(AgentSchedule).filter(AgentSchedule.agent_id == agent_id,
AgentSchedule.status == "SCHEDULED").first()
if not agent:
raise HTTPException(status_code=404, detail="Agent Schedule not found")
user_timezone = db.session.query(AgentConfiguration).filter(AgentConfiguration.key == "user_timezone",
AgentConfiguration.agent_id == agent_id).first()
if user_timezone and user_timezone.value != "None":
tzone = timezone(user_timezone.value)
else:
tzone = timezone('GMT')
current_datetime = datetime.now(tzone).strftime("%d/%m/%Y %I:%M %p")
return {
"current_datetime": current_datetime,
"start_date": agent.start_time.astimezone(tzone).strftime("%d %b %Y"),
"start_time": agent.start_time.astimezone(tzone).strftime("%I:%M %p"),
"recurrence_interval": agent.recurrence_interval if agent.recurrence_interval else None,
"expiry_date": agent.expiry_date.astimezone(tzone).strftime("%d/%m/%Y") if agent.expiry_date else None,
"expiry_runs": agent.expiry_runs if agent.expiry_runs != -1 else None
}
@router.get("/get/project/{project_id}")
def get_agents_by_project_id(project_id: int,
Authorize: AuthJWT = Depends(check_auth)):
"""
Get all agents by project ID.
Args:
project_id (int): Identifier of the project.
Authorize (AuthJWT, optional): Authorization dependency. Defaults to Depends(check_auth).
Returns:
list: List of agents associated with the project, including their status and scheduling information.
Raises:
HTTPException (status_code=404): If the project is not found.
"""
# Checking for project
project = db.session.query(Project).get(project_id)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
agents = db.session.query(Agent).filter(Agent.project_id == project_id, or_(or_(Agent.is_deleted == False, Agent.is_deleted is None), Agent.is_deleted is None)).all()
new_agents, new_agents_sorted = [], []
for agent in agents:
agent_dict = vars(agent)
agent_id = agent.id
# Query the AgentExecution table using the agent ID
executions = db.session.query(AgentExecution).filter_by(agent_id=agent_id).all()
is_running = False
for execution in executions:
if execution.status == "RUNNING":
is_running = True
break
# Check if the agent is scheduled
is_scheduled = db.session.query(AgentSchedule).filter_by(agent_id=agent_id, status="SCHEDULED").first() is not None
new_agent = {
**agent_dict,
'is_running': is_running,
'is_scheduled': is_scheduled
}
new_agents.append(new_agent)
new_agents_sorted = sorted(new_agents, key=lambda agent: agent['is_running'] == True, reverse=True)
return new_agents_sorted
@router.put("/delete/{agent_id}", status_code=200)
def delete_agent(agent_id: int, Authorize: AuthJWT = Depends(check_auth)):
"""
Delete an existing Agent
- Updates the is_deleted flag: Executes a soft delete
- AgentExecutions are updated to: "TERMINATED" if agentexecution is created, All the agent executions are updated
- AgentExecutionPermission is set to: "REJECTED" if agentexecutionpersmision is created
Args:
agent_id (int): Identifier of the Agent to delete
Returns:
A dictionary containing a "success" key with the value True to indicate a successful delete.
Raises:
HTTPException (Status Code=404): If the Agent or associated Project is not found or deleted already.
"""
db_agent = db.session.query(Agent).filter(Agent.id == agent_id).first()
db_agent_executions = db.session.query(AgentExecution).filter(AgentExecution.agent_id == agent_id).all()
db_agent_schedule = db.session.query(AgentSchedule).filter(AgentSchedule.agent_id == agent_id, AgentSchedule.status == "SCHEDULED").first()
if not db_agent or db_agent.is_deleted:
raise HTTPException(status_code=404, detail="agent not found")
# Deletion Procedure
db_agent.is_deleted = True
if db_agent_executions:
# Updating all the RUNNING executions to TERMINATED
for db_agent_execution in db_agent_executions:
db_agent_execution.status = "TERMINATED"
if db_agent_schedule:
# Updating the schedule status to STOPPED
db_agent_schedule.status = "STOPPED"
db.session.commit()