main.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. """
  2. FastAPI server for the SRE On-Call Agent.
  3. Run with: uvicorn src.api.main:app --reload --port 8000
  4. Endpoints:
  5. GET /health — liveness check
  6. GET /incidents/fixtures — list available sample incident IDs
  7. POST /incidents/investigate — run the full 3-agent pipeline
  8. GET /incidents/{id}/report — retrieve a previously generated report
  9. """
  10. import time
  11. from typing import Dict, Any
  12. from fastapi import FastAPI, HTTPException
  13. from fastapi.middleware.cors import CORSMiddleware
  14. from pydantic import BaseModel
  15. from src.agents.pipeline import run_pipeline, list_incidents, load_incident
  16. app = FastAPI(
  17. title="SRE On-Call Agent",
  18. description="AI-powered incident triage and post-mortem generation",
  19. version="1.0.0",
  20. )
  21. app.add_middleware(
  22. CORSMiddleware,
  23. allow_origins=["*"], # Tighten this when adding a specific frontend origin
  24. allow_methods=["*"],
  25. allow_headers=["*"],
  26. )
  27. # In-memory store for generated reports (replace with Redis/DB for production)
  28. _report_store: Dict[str, Any] = {}
  29. class InvestigateRequest(BaseModel):
  30. incident_id: str
  31. @app.get("/health")
  32. def health():
  33. return {"status": "ok"}
  34. @app.get("/incidents/fixtures")
  35. def get_fixtures():
  36. """List all available sample incident IDs."""
  37. return {"incidents": list_incidents()}
  38. @app.post("/incidents/investigate")
  39. def investigate(req: InvestigateRequest):
  40. """
  41. Run the full triage → investigation → post-mortem pipeline for an incident.
  42. This runs synchronously (suitable for demo; upgrade to background task + SSE for prod).
  43. """
  44. try:
  45. load_incident(req.incident_id) # Validate early
  46. except FileNotFoundError as e:
  47. raise HTTPException(status_code=404, detail=str(e))
  48. start = time.time()
  49. try:
  50. result = run_pipeline(req.incident_id, verbose=False)
  51. except Exception as e:
  52. raise HTTPException(status_code=500, detail=f"Pipeline error: {e}")
  53. elapsed = round(time.time() - start, 1)
  54. result["elapsed_seconds"] = elapsed
  55. _report_store[req.incident_id] = result
  56. return result
  57. @app.get("/incidents/{incident_id}/report")
  58. def get_report(incident_id: str):
  59. """Retrieve a previously generated post-mortem report."""
  60. if incident_id not in _report_store:
  61. raise HTTPException(
  62. status_code=404,
  63. detail=f"No report found for '{incident_id}'. Call POST /incidents/investigate first.",
  64. )
  65. return {
  66. "incident_id": incident_id,
  67. "report": _report_store[incident_id]["report"],
  68. }