Newer
Older
cortex-hub / ai-hub / app / api / routes / agents.py
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