from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, Response, status
from typing import List
from sqlalchemy.orm import Session
from app.api.dependencies import ServiceContainer, get_db, get_current_user
from app.api import schemas
from app.db import models
from app.db.models.agent import AgentTemplate, AgentInstance, AgentTrigger
from app.db.models import Message
from app.api.schemas import (
AgentTemplateCreate, AgentTemplateResponse,
AgentInstanceCreate, AgentInstanceResponse, AgentInstanceStatusUpdate
)
import uuid
import json
import os
import logging
from app.core.orchestration.agent_loop import AgentExecutor
from sqlalchemy.orm import joinedload
def create_agents_router(services: ServiceContainer) -> APIRouter:
router = APIRouter()
@router.get("", response_model=List[AgentInstanceResponse])
def get_agents(current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.list_user_agents(db, current_user.id)
@router.get("/{id}", response_model=AgentInstanceResponse)
def get_agent(id: str, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.get_agent_instance(db, id, current_user.id)
@router.post("/templates", response_model=AgentTemplateResponse)
def create_template(request: AgentTemplateCreate, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.create_template(db, current_user.id, request)
@router.post("/instances", response_model=AgentInstanceResponse)
def create_instance(request: AgentInstanceCreate, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.create_instance(db, current_user.id, request)
@router.patch("/{id}/status", response_model=AgentInstanceResponse)
def update_status(id: str, request: AgentInstanceStatusUpdate, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.update_status(db, id, current_user.id, request.status)
@router.patch("/{id}/config", response_model=AgentInstanceResponse)
def update_config(id: str, request: schemas.AgentConfigUpdate, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.update_config(db, id, current_user.id, request)
@router.post("/{id}/webhook")
async def webhook_receiver(id: str, payload: dict, background_tasks: BackgroundTasks, response: Response, token: str = None, sync: bool = False, skip_coworker: bool = False, db: Session = Depends(get_db)):
instance = services.agent_service.validate_webhook_trigger(db, id, token)
# Extract prompt from payload (supports 'prompt' or legacy 'override_prompt')
prompt = payload.get("prompt") or payload.get("override_prompt") or f"Webhook Event: {json.dumps(payload)}"
should_skip = skip_coworker or payload.get("skip_coworker") or False
if sync:
try:
result = await AgentExecutor.run(instance.id, prompt, services, services.user_service, skip_coworker=should_skip)
return {"status": "success", **result}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Agent execution failed: {str(e)}")
else:
background_tasks.add_task(AgentExecutor.run, instance.id, prompt, services, services.user_service, should_skip)
response.status_code = status.HTTP_202_ACCEPTED
return {"status": "accepted", "message": "Background task initiated"}
@router.post("/{id}/run", status_code=202)
def manual_trigger(id: str, payload: dict, background_tasks: BackgroundTasks, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
instance = db.query(AgentInstance).filter(
AgentInstance.id == id,
AgentInstance.user_id == current_user.id
).first()
if not instance:
raise HTTPException(status_code=404, detail="Instance not found")
prompt = payload.get("prompt") or f"Manual triggered execution for agent {id}."
should_skip = payload.get("skip_coworker", False)
background_tasks.add_task(AgentExecutor.run, instance.id, prompt, services, services.user_service, should_skip)
return {"message": "Accepted"}
@router.get("/{id}/triggers", response_model=List[schemas.AgentTriggerResponse])
def get_agent_triggers(id: str, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.list_agent_triggers(db, id, current_user.id)
@router.post("/{id}/triggers", response_model=schemas.AgentTriggerResponse)
def create_agent_trigger(id: str, request: schemas.AgentTriggerCreate, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
return services.agent_service.create_agent_trigger(db, id, current_user.id, request)
@router.delete("/triggers/{trigger_id}")
def delete_agent_trigger(trigger_id: str, db: Session = Depends(get_db)):
services.agent_service.delete_agent_trigger(db, trigger_id)
return {"message": "Trigger deleted successfully"}
@router.post("/{id}/metrics/reset")
def reset_agent_metrics(id: str, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
services.agent_service.reset_agent_metrics(db, id, current_user.id)
return {"message": "Metrics reset successfully"}
@router.get("/{id}/telemetry")
def get_telemetry(id: str, db: Session = Depends(get_db)):
instance = db.query(AgentInstance).filter(AgentInstance.id == id).first()
if not instance:
raise HTTPException(status_code=404, detail="Instance not found")
# For MVP/Area 3, return mock telemetry data (e.g. baseline or from cgroup)
# Real cgroup-based metrics will come in Phase 2
return {
"cpu_usage": 2.5,
"memory_usage": 512,
"network_tx": 120,
"network_rx": 450
}
@router.get("/{id}/dependencies")
def get_dependencies(id: str, db: Session = Depends(get_db)):
instance = db.query(AgentInstance).filter(AgentInstance.id == id).first()
if not instance:
raise HTTPException(status_code=404, detail="Instance not found")
return {
"dependencies": [],
"edges": []
}
@router.post("/deploy")
def deploy_agent(request: schemas.DeployAgentRequest, background_tasks: BackgroundTasks, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
result = services.agent_service.deploy_agent(db, current_user.id, request)
if request.initial_prompt:
background_tasks.add_task(AgentExecutor.run, result["instance_id"], request.initial_prompt, services, services.user_service)
return result
@router.delete("/{id}")
def delete_agent(id: str, current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
services.agent_service.delete_agent(db, id, current_user.id)
return {"message": "Agent deleted successfully"}
return router