|
|
""" |
|
|
API routes for EvoAgentX application. |
|
|
""" |
|
|
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks |
|
|
from fastapi.security import OAuth2PasswordRequestForm |
|
|
from typing import List, Dict, Any |
|
|
from fastapi import Response |
|
|
|
|
|
from datetime import timedelta |
|
|
|
|
|
from evoagentx.app.config import settings |
|
|
from evoagentx.app.schemas import ( |
|
|
AgentCreate, AgentUpdate, AgentResponse, |
|
|
WorkflowCreate, WorkflowUpdate, WorkflowResponse, |
|
|
ExecutionCreate, ExecutionResponse, |
|
|
PaginationParams, SearchParams, |
|
|
Token, UserCreate, UserResponse, |
|
|
) |
|
|
from evoagentx.app.services import AgentService, WorkflowService, WorkflowExecutionService |
|
|
from evoagentx.app.security import ( |
|
|
create_access_token, |
|
|
authenticate_user, |
|
|
create_user, |
|
|
get_current_active_user, |
|
|
get_current_admin_user |
|
|
) |
|
|
from evoagentx.app.db import Database, ExecutionStatus |
|
|
|
|
|
|
|
|
auth_router = APIRouter(prefix=settings.API_PREFIX) |
|
|
agents_router = APIRouter(prefix=settings.API_PREFIX) |
|
|
workflows_router = APIRouter(prefix=settings.API_PREFIX) |
|
|
executions_router = APIRouter(prefix=settings.API_PREFIX) |
|
|
system_router = APIRouter(prefix=settings.API_PREFIX) |
|
|
|
|
|
|
|
|
@auth_router.post("/auth/register", response_model=UserResponse, tags=["Authentication"]) |
|
|
async def register_user(user: UserCreate): |
|
|
"""Register a new user.""" |
|
|
return await create_user(user) |
|
|
|
|
|
@auth_router.post("/auth/login", response_model=Token, tags=["Authentication"]) |
|
|
async def login(form_data: OAuth2PasswordRequestForm = Depends()): |
|
|
"""Login and return access token.""" |
|
|
user = await authenticate_user(form_data.username, form_data.password) |
|
|
if not user: |
|
|
raise HTTPException( |
|
|
status_code=status.HTTP_401_UNAUTHORIZED, |
|
|
detail="Incorrect username or password", |
|
|
headers={"WWW-Authenticate": "Bearer"}, |
|
|
) |
|
|
|
|
|
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) |
|
|
access_token = create_access_token( |
|
|
subject=user['email'], |
|
|
expires_delta=access_token_expires |
|
|
) |
|
|
|
|
|
return { |
|
|
"access_token": access_token, |
|
|
"token_type": "bearer" |
|
|
} |
|
|
|
|
|
|
|
|
@agents_router.post("/agents", response_model=AgentResponse, tags=["Agents"]) |
|
|
async def create_agent( |
|
|
agent: AgentCreate, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Create a new agent.""" |
|
|
try: |
|
|
created_agent = await AgentService.create_agent( |
|
|
agent, |
|
|
user_id=str(current_user['_id']) |
|
|
) |
|
|
|
|
|
created_agent["_id"] = str(created_agent["_id"]) |
|
|
return AgentResponse(**created_agent) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
@agents_router.get("/agents/{agent_id}", response_model=AgentResponse, tags=["Agents"]) |
|
|
async def get_agent( |
|
|
agent_id: str, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Retrieve a specific agent by ID.""" |
|
|
agent = await AgentService.get_agent(agent_id) |
|
|
if not agent: |
|
|
raise HTTPException(status_code=404, detail="Agent not found") |
|
|
agent["_id"] = str(agent["_id"]) |
|
|
return AgentResponse(**agent) |
|
|
|
|
|
@agents_router.put("/agents/{agent_id}", response_model=AgentResponse, tags=["Agents"]) |
|
|
async def update_agent( |
|
|
agent_id: str, |
|
|
agent_update: AgentUpdate, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Update an existing agent.""" |
|
|
try: |
|
|
updated_agent = await AgentService.update_agent(agent_id, agent_update) |
|
|
if not updated_agent: |
|
|
raise HTTPException(status_code=404, detail="Agent not found") |
|
|
updated_agent["_id"] = str(updated_agent["_id"]) |
|
|
return AgentResponse(**updated_agent) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
@agents_router.get("/agents", response_model=List[AgentResponse], tags=["Agents"]) |
|
|
async def list_agents( |
|
|
pagination: PaginationParams = Depends(), |
|
|
search: SearchParams = Depends(), |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""List agents with optional pagination and search.""" |
|
|
agents, total = await AgentService.list_agents(pagination, search) |
|
|
|
|
|
for agent in agents: |
|
|
agent["_id"] = str(agent["_id"]) |
|
|
return [AgentResponse(**agent) for agent in agents] |
|
|
|
|
|
@agents_router.delete("/agents/{agent_id}", status_code=204, tags=["Agents"]) |
|
|
async def delete_agent( |
|
|
agent_id: str, |
|
|
current_user: Dict[str, Any] = Depends(get_current_admin_user) |
|
|
): |
|
|
"""Delete an agent (admin-only).""" |
|
|
try: |
|
|
success = await AgentService.delete_agent(agent_id) |
|
|
if not success: |
|
|
raise HTTPException(status_code=404, detail="Agent not found") |
|
|
return |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@workflows_router.post("/workflows", response_model=WorkflowResponse,status_code=201, tags=["Workflows"]) |
|
|
async def create_workflow( |
|
|
workflow: WorkflowCreate, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Create a new workflow.""" |
|
|
try: |
|
|
created_workflow = await WorkflowService.create_workflow( |
|
|
workflow, |
|
|
user_id=str(current_user['_id']) |
|
|
) |
|
|
|
|
|
created_workflow["_id"] = str(created_workflow["_id"]) |
|
|
return WorkflowResponse(**created_workflow) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
|
|
|
|
|
|
@workflows_router.get("/workflows/{workflow_id}", response_model=WorkflowResponse, tags=["Workflows"]) |
|
|
async def get_workflow( |
|
|
workflow_id: str, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Retrieve a specific workflow by ID.""" |
|
|
workflow = await WorkflowService.get_workflow(workflow_id) |
|
|
if not workflow: |
|
|
raise HTTPException(status_code=404, detail="Workflow not found") |
|
|
|
|
|
workflow["_id"] = str(workflow["_id"]) |
|
|
return WorkflowResponse(**workflow) |
|
|
|
|
|
@workflows_router.put("/workflows/{workflow_id}", response_model=WorkflowResponse, tags=["Workflows"]) |
|
|
async def update_workflow( |
|
|
workflow_id: str, |
|
|
workflow_update: WorkflowUpdate, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Update an existing workflow.""" |
|
|
try: |
|
|
updated_workflow = await WorkflowService.update_workflow(workflow_id, workflow_update) |
|
|
if not updated_workflow: |
|
|
raise HTTPException(status_code=404, detail="Workflow not found") |
|
|
|
|
|
updated_workflow["_id"] = str(updated_workflow["_id"]) |
|
|
return WorkflowResponse(**updated_workflow) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
@workflows_router.delete("/workflows/{workflow_id}", status_code=204, tags=["Workflows"]) |
|
|
async def delete_workflow( |
|
|
workflow_id: str, |
|
|
current_user: Dict[str, Any] = Depends(get_current_admin_user) |
|
|
): |
|
|
"""Delete a workflow (admin-only).""" |
|
|
try: |
|
|
success = await WorkflowService.delete_workflow(workflow_id) |
|
|
if not success: |
|
|
raise HTTPException(status_code=404, detail="Workflow not found") |
|
|
return Response(status_code=204) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
|
|
|
@workflows_router.get("/workflows", response_model=List[WorkflowResponse], tags=["Workflows"]) |
|
|
async def list_workflows( |
|
|
pagination: PaginationParams = Depends(), |
|
|
search: SearchParams = Depends(), |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""List workflows with optional pagination and search.""" |
|
|
workflows, total = await WorkflowService.list_workflows(pagination, search) |
|
|
|
|
|
|
|
|
converted_workflows = [ |
|
|
{**workflow, "_id": str(workflow["_id"])} |
|
|
for workflow in workflows |
|
|
] |
|
|
|
|
|
return [WorkflowResponse(**workflow) for workflow in converted_workflows] |
|
|
|
|
|
|
|
|
|
|
|
@executions_router.post("/executions", response_model=ExecutionResponse, status_code=202) |
|
|
async def create_execution( |
|
|
execution: ExecutionCreate, |
|
|
background_tasks: BackgroundTasks, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Create and start a workflow execution.""" |
|
|
try: |
|
|
execution_result = await WorkflowExecutionService.create_execution( |
|
|
execution_data=execution, |
|
|
user_id=str(current_user['_id']) |
|
|
) |
|
|
|
|
|
execution_result["_id"] = str(execution_result["_id"]) |
|
|
return ExecutionResponse(**execution_result) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
|
|
|
@executions_router.get("/executions/{execution_id}", response_model=ExecutionResponse) |
|
|
async def get_execution( |
|
|
execution_id: str, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Retrieve a specific workflow execution by ID.""" |
|
|
try: |
|
|
execution = await WorkflowExecutionService.get_execution(execution_id) |
|
|
if not execution: |
|
|
raise HTTPException(status_code=404, detail="Execution not found") |
|
|
execution["_id"] = str(execution["_id"]) |
|
|
return ExecutionResponse(**execution) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
|
|
|
@executions_router.post("/executions/{execution_id}/stop", response_model=ExecutionResponse) |
|
|
async def stop_execution( |
|
|
execution_id: str, |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Stop (cancel) a workflow execution.""" |
|
|
try: |
|
|
updated_execution = await WorkflowExecutionService.update_execution_status( |
|
|
execution_id=execution_id, |
|
|
status=ExecutionStatus.CANCELLED |
|
|
) |
|
|
if not updated_execution: |
|
|
raise HTTPException(status_code=404, detail="Execution not found") |
|
|
|
|
|
updated_execution["_id"] = str(updated_execution["_id"]) |
|
|
return ExecutionResponse(**updated_execution) |
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
|
|
|
|
|
|
@executions_router.get("/executions", response_model=List[ExecutionResponse]) |
|
|
async def list_executions( |
|
|
pagination: PaginationParams = Depends(), |
|
|
search: SearchParams = Depends(), |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""List workflow executions with optional pagination and search.""" |
|
|
executions, total = await WorkflowExecutionService.list_executions( |
|
|
params=pagination, |
|
|
search=search |
|
|
) |
|
|
|
|
|
for exec_item in executions: |
|
|
exec_item["_id"] = str(exec_item["_id"]) |
|
|
return [ExecutionResponse(**exec_item) for exec_item in executions] |
|
|
|
|
|
|
|
|
@executions_router.get("/executions/{execution_id}/logs", response_model=List[Dict[str, Any]]) |
|
|
async def get_execution_logs( |
|
|
execution_id: str, |
|
|
pagination: PaginationParams = Depends(), |
|
|
current_user: Dict[str, Any] = Depends(get_current_active_user) |
|
|
): |
|
|
"""Retrieve logs for a specific execution.""" |
|
|
logs, total = await WorkflowExecutionService.get_execution_logs(execution_id, params=pagination) |
|
|
|
|
|
for log in logs: |
|
|
log["_id"] = str(log["_id"]) |
|
|
return logs |
|
|
|
|
|
|
|
|
@system_router.get("/health", tags=["System"]) |
|
|
async def health_check(): |
|
|
"""Simple health check endpoint.""" |
|
|
try: |
|
|
|
|
|
await Database.db.command('ping') |
|
|
return { |
|
|
"status": "healthy", |
|
|
"version": "1.0.0" |
|
|
} |
|
|
except Exception as e: |
|
|
raise HTTPException(status_code=500, detail=f"Database connection error: {str(e)}") |
|
|
|
|
|
|
|
|
__all__ = [ |
|
|
'auth_router', |
|
|
'agents_router', |
|
|
'workflows_router', |
|
|
'executions_router', |
|
|
'system_router' |
|
|
] |