""" FastAPI server wrapping DebugOps-RX. Exposes OpenEnv-compliant endpoints: /reset, /step, /state, /grader, /health """ from fastapi import FastAPI, HTTPException, Query from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager import sys import os # Add parent directory to path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from models import Action # pyre-ignore from server.env import DebugOpsEnv # pyre-ignore @asynccontextmanager async def lifespan(app: FastAPI): app.state.envs = {} yield app = FastAPI( title="DebugOps-RX API", description="Realistic eXecution benchmark for debugging agents.", version="1.0.0", lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) def _get_env() -> DebugOpsEnv: env = app.state.envs.get("current") if env is None: raise HTTPException(status_code=400, detail="No active environment. Call /reset first.") return env @app.post("/reset") def reset( difficulty: str = Query("easy", description="easy, medium, hard, extreme"), split: str = Query("test", description="train, test, ood"), seed: int = Query(42), ): env = DebugOpsEnv(seed=seed) app.state.envs["current"] = env obs = env.reset(difficulty=difficulty, split=split) return obs.model_dump() @app.post("/step") def step(action: Action): env = _get_env() try: obs, reward, done, info = env.step(action) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) return { "observation": obs.model_dump(), "reward": reward, "done": done, "info": info } @app.get("/state") def state(): env = _get_env() return env.state.model_dump() @app.get("/grader") def grader(): env = _get_env() score = env.grade(env.trajectory) return { "score": score.final(), "breakdown": score.model_dump() } @app.get("/health") def health(): return {"status": "ok", "environment": "DebugOps-RX", "version": "1.0.0"}