Clawith/backend/app/api/triggers.py

132 lines
3.9 KiB
Python

"""Triggers REST API — CRUD endpoints for the Aware page frontend."""
import uuid
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from app.api.auth import get_current_user
from app.database import async_session
from app.models.trigger import AgentTrigger
router = APIRouter(prefix="/api/agents", tags=["triggers"])
class TriggerResponse(BaseModel):
id: str
name: str
type: str
config: dict
reason: str
focus_ref: str | None = None
is_enabled: bool
fire_count: int
max_fires: int | None = None
cooldown_seconds: int
last_fired_at: str | None = None
created_at: str | None = None
expires_at: str | None = None
class TriggerUpdate(BaseModel):
config: dict | None = None
reason: str | None = None
is_enabled: bool | None = None
max_fires: int | None = None
cooldown_seconds: int | None = None
expires_at: str | None = None
@router.get("/{agent_id}/triggers", response_model=list[TriggerResponse])
async def list_agent_triggers(agent_id: uuid.UUID, user=Depends(get_current_user)):
"""List all triggers for an agent."""
async with async_session() as db:
result = await db.execute(
select(AgentTrigger)
.where(AgentTrigger.agent_id == agent_id)
.order_by(AgentTrigger.created_at.desc())
)
triggers = result.scalars().all()
return [
TriggerResponse(
id=str(t.id),
name=t.name,
type=t.type,
config=t.config or {},
reason=t.reason or "",
focus_ref=t.focus_ref,
is_enabled=t.is_enabled,
fire_count=t.fire_count,
max_fires=t.max_fires,
cooldown_seconds=t.cooldown_seconds,
last_fired_at=t.last_fired_at.isoformat() if t.last_fired_at else None,
created_at=t.created_at.isoformat() if t.created_at else None,
expires_at=t.expires_at.isoformat() if t.expires_at else None,
)
for t in triggers
]
@router.patch("/{agent_id}/triggers/{trigger_id}")
async def update_trigger(
agent_id: uuid.UUID,
trigger_id: uuid.UUID,
body: TriggerUpdate,
user=Depends(get_current_user),
):
"""Update a trigger (from frontend management UI)."""
async with async_session() as db:
result = await db.execute(
select(AgentTrigger).where(
AgentTrigger.id == trigger_id,
AgentTrigger.agent_id == agent_id,
)
)
trigger = result.scalar_one_or_none()
if not trigger:
raise HTTPException(404, "Trigger not found")
if body.config is not None:
trigger.config = body.config
if body.reason is not None:
trigger.reason = body.reason
if body.is_enabled is not None:
trigger.is_enabled = body.is_enabled
if body.max_fires is not None:
trigger.max_fires = body.max_fires
if body.cooldown_seconds is not None:
trigger.cooldown_seconds = body.cooldown_seconds
if body.expires_at is not None:
from datetime import datetime
trigger.expires_at = datetime.fromisoformat(body.expires_at)
await db.commit()
return {"ok": True}
@router.delete("/{agent_id}/triggers/{trigger_id}")
async def delete_trigger(
agent_id: uuid.UUID,
trigger_id: uuid.UUID,
user=Depends(get_current_user),
):
"""Delete a trigger entirely."""
async with async_session() as db:
result = await db.execute(
select(AgentTrigger).where(
AgentTrigger.id == trigger_id,
AgentTrigger.agent_id == agent_id,
)
)
trigger = result.scalar_one_or_none()
if not trigger:
raise HTTPException(404, "Trigger not found")
await db.delete(trigger)
await db.commit()
return {"ok": True}