| """ |
| Thin FastAPI server — marshals JSON in/out. |
| No simulation logic lives here. |
| """ |
|
|
| from __future__ import annotations |
|
|
| from typing import Any, Dict, Optional |
|
|
| from fastapi import FastAPI, HTTPException |
| from fastapi.middleware.cors import CORSMiddleware |
| from pydantic import BaseModel |
|
|
| from .incident_environment import IncidentEnvironment |
|
|
| |
| |
| |
|
|
| app = FastAPI( |
| title="SRE Incident Response Environment", |
| description="An OpenEnv environment for training AI agents on production incident response.", |
| version="0.1.0", |
| ) |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| env = IncidentEnvironment() |
|
|
|
|
| |
| |
| |
|
|
| class ResetRequest(BaseModel): |
| task_name: Optional[str] = None |
| seed: Optional[int] = None |
|
|
|
|
| class StepRequest(BaseModel): |
| action_type: str |
| target_service: Optional[str] = None |
| parameters: Dict[str, Any] = {} |
|
|
|
|
| |
| |
| |
|
|
| @app.get("/health") |
| def health() -> Dict[str, str]: |
| """Health check — the validator pings this first.""" |
| return {"status": "healthy"} |
|
|
|
|
| from fastapi import Request |
|
|
| @app.post("/reset") |
| async def reset(request: Request) -> Dict[str, Any]: |
| """ |
| Initialize a new incident episode. |
| POST /reset {"task_name": "memory_leak", "seed": 42} |
| """ |
| try: |
| body = await request.json() |
| except Exception: |
| body = {} |
| |
| if not isinstance(body, dict): |
| body = {} |
| |
| result = env.reset( |
| task_name=body.get("task_name"), |
| seed=body.get("seed"), |
| ) |
| return result |
|
|
|
|
| @app.post("/step") |
| def step(request: StepRequest) -> Dict[str, Any]: |
| """ |
| Execute one agent action. |
| POST /step {"action_type": "view_alerts"} |
| """ |
| action_data = { |
| "action_type": request.action_type, |
| "target_service": request.target_service, |
| "parameters": request.parameters, |
| } |
| result = env.step(action_data) |
| return result |
|
|
|
|
| @app.get("/state") |
| def state() -> Dict[str, Any]: |
| """ |
| Get current episode metadata. |
| GET /state |
| """ |
| return env.get_state() |
|
|
|
|
| @app.get("/tasks") |
| def list_tasks() -> Dict[str, Any]: |
| """List available tasks with descriptions.""" |
| from ..tasks import TASK_REGISTRY |
| tasks = {} |
| for name, cls in TASK_REGISTRY.items(): |
| scenario = cls() |
| tasks[name] = { |
| "display_name": scenario.display_name, |
| "severity": scenario.severity, |
| "max_steps": scenario.max_steps, |
| "time_budget_minutes": scenario.time_budget_minutes, |
| } |
| return {"tasks": tasks} |
|
|
| def main(): |
| import uvicorn |
| uvicorn.run("incident_env.server.app:app", host="0.0.0.0", port=8000, reload=False) |
|
|
| if __name__ == "__main__": |
| main() |
|
|
|
|