Initial commit: Agent Command Center dashboard + weather briefing agent
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
import json
|
||||
|
||||
from database import get_db, init_db
|
||||
from models import Agent, Run
|
||||
|
||||
app = FastAPI(title="Agent Command Center", version="1.0.0")
|
||||
|
||||
|
||||
# --- Pydantic schemas ---
|
||||
|
||||
class AgentCreate(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
description: str = ""
|
||||
schedule: str = "manual"
|
||||
|
||||
class AgentUpdate(BaseModel):
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
schedule: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
|
||||
class RunCreate(BaseModel):
|
||||
status: str = "running"
|
||||
output: str = ""
|
||||
error: str = ""
|
||||
metadata: dict = {}
|
||||
|
||||
class RunUpdate(BaseModel):
|
||||
status: Optional[str] = None
|
||||
output: Optional[str] = None
|
||||
error: Optional[str] = None
|
||||
finished_at: Optional[str] = None
|
||||
metadata: Optional[dict] = None
|
||||
|
||||
|
||||
# --- API routes ---
|
||||
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
return {"status": "ok", "service": "agent-command-center"}
|
||||
|
||||
|
||||
@app.get("/api/agents")
|
||||
def list_agents(db: Session = Depends(get_db)):
|
||||
agents = db.query(Agent).all()
|
||||
result = []
|
||||
for a in agents:
|
||||
last_run = db.query(Run).filter(Run.agent_id == a.id).order_by(Run.started_at.desc()).first()
|
||||
recent_runs = db.query(Run).filter(Run.agent_id == a.id).order_by(Run.started_at.desc()).limit(10).all()
|
||||
success_streak = 0
|
||||
for r in recent_runs:
|
||||
if r.status == "success":
|
||||
success_streak += 1
|
||||
else:
|
||||
break
|
||||
result.append({
|
||||
"id": a.id,
|
||||
"name": a.name,
|
||||
"description": a.description,
|
||||
"schedule": a.schedule,
|
||||
"status": a.status,
|
||||
"created_at": a.created_at.isoformat() if a.created_at else None,
|
||||
"last_run": {
|
||||
"status": last_run.status,
|
||||
"started_at": last_run.started_at.isoformat() if last_run.started_at else None,
|
||||
"finished_at": last_run.finished_at.isoformat() if last_run.finished_at else None,
|
||||
} if last_run else None,
|
||||
"success_streak": success_streak,
|
||||
"total_runs": db.query(Run).filter(Run.agent_id == a.id).count(),
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
@app.post("/api/agents")
|
||||
def create_agent(agent: AgentCreate, db: Session = Depends(get_db)):
|
||||
existing = db.query(Agent).filter(Agent.id == agent.id).first()
|
||||
if existing:
|
||||
raise HTTPException(status_code=409, detail="Agent already exists")
|
||||
new_agent = Agent(
|
||||
id=agent.id,
|
||||
name=agent.name,
|
||||
description=agent.description,
|
||||
schedule=agent.schedule,
|
||||
)
|
||||
db.add(new_agent)
|
||||
db.commit()
|
||||
return {"id": new_agent.id, "status": "created"}
|
||||
|
||||
|
||||
@app.get("/api/agents/{agent_id}")
|
||||
def get_agent(agent_id: str, db: Session = Depends(get_db)):
|
||||
agent = db.query(Agent).filter(Agent.id == agent_id).first()
|
||||
if not agent:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
runs = db.query(Run).filter(Run.agent_id == agent_id).order_by(Run.started_at.desc()).limit(50).all()
|
||||
return {
|
||||
"id": agent.id,
|
||||
"name": agent.name,
|
||||
"description": agent.description,
|
||||
"schedule": agent.schedule,
|
||||
"status": agent.status,
|
||||
"created_at": agent.created_at.isoformat() if agent.created_at else None,
|
||||
"runs": [{
|
||||
"id": r.id,
|
||||
"started_at": r.started_at.isoformat() if r.started_at else None,
|
||||
"finished_at": r.finished_at.isoformat() if r.finished_at else None,
|
||||
"status": r.status,
|
||||
"output": r.output,
|
||||
"error": r.error,
|
||||
"metadata": r.metadata_,
|
||||
} for r in runs],
|
||||
}
|
||||
|
||||
|
||||
@app.put("/api/agents/{agent_id}")
|
||||
def update_agent(agent_id: str, update: AgentUpdate, db: Session = Depends(get_db)):
|
||||
agent = db.query(Agent).filter(Agent.id == agent_id).first()
|
||||
if not agent:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
for field, value in update.model_dump(exclude_none=True).items():
|
||||
setattr(agent, field, value)
|
||||
db.commit()
|
||||
return {"id": agent.id, "status": "updated"}
|
||||
|
||||
|
||||
@app.post("/api/agents/{agent_id}/runs")
|
||||
def create_run(agent_id: str, run: RunCreate, db: Session = Depends(get_db)):
|
||||
agent = db.query(Agent).filter(Agent.id == agent_id).first()
|
||||
if not agent:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
new_run = Run(
|
||||
agent_id=agent_id,
|
||||
status=run.status,
|
||||
output=run.output,
|
||||
error=run.error,
|
||||
metadata_=run.metadata,
|
||||
)
|
||||
if run.status in ("success", "failed"):
|
||||
new_run.finished_at = datetime.now(timezone.utc)
|
||||
db.add(new_run)
|
||||
db.commit()
|
||||
return {"id": new_run.id, "status": new_run.status}
|
||||
|
||||
|
||||
@app.put("/api/runs/{run_id}")
|
||||
def update_run(run_id: int, update: RunUpdate, db: Session = Depends(get_db)):
|
||||
run = db.query(Run).filter(Run.id == run_id).first()
|
||||
if not run:
|
||||
raise HTTPException(status_code=404, detail="Run not found")
|
||||
if update.status is not None:
|
||||
run.status = update.status
|
||||
if update.output is not None:
|
||||
run.output = update.output
|
||||
if update.error is not None:
|
||||
run.error = update.error
|
||||
if update.metadata is not None:
|
||||
run.metadata_ = update.metadata
|
||||
if update.finished_at is not None:
|
||||
run.finished_at = datetime.fromisoformat(update.finished_at)
|
||||
elif update.status in ("success", "failed"):
|
||||
run.finished_at = datetime.now(timezone.utc)
|
||||
db.commit()
|
||||
return {"id": run.id, "status": run.status}
|
||||
|
||||
|
||||
@app.get("/api/runs")
|
||||
def list_runs(limit: int = 50, db: Session = Depends(get_db)):
|
||||
runs = db.query(Run).order_by(Run.started_at.desc()).limit(limit).all()
|
||||
return [{
|
||||
"id": r.id,
|
||||
"agent_id": r.agent_id,
|
||||
"started_at": r.started_at.isoformat() if r.started_at else None,
|
||||
"finished_at": r.finished_at.isoformat() if r.finished_at else None,
|
||||
"status": r.status,
|
||||
"output": r.output,
|
||||
"error": r.error,
|
||||
"metadata": r.metadata_,
|
||||
} for r in runs]
|
||||
|
||||
|
||||
# --- Static files (frontend) ---
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
@app.get("/")
|
||||
def root():
|
||||
return FileResponse("static/index.html")
|
||||
|
||||
|
||||
# --- Startup ---
|
||||
|
||||
@app.on_event("startup")
|
||||
def startup():
|
||||
init_db()
|
||||
Reference in New Issue
Block a user