fester/backend/api/debugger.py

101 lines
2.5 KiB
Python

"""
Debugger API — pause / resume / step an in-flight pipeline.
The engine is set by main.py on startup via `set_engine(...)`. If no
engine is registered (e.g. when running the mock server), endpoints
return a structured error rather than crashing.
"""
from fastapi import APIRouter
from typing import Optional, Any
router = APIRouter(prefix="/debugger", tags=["debugger"])
# Single in-memory engine reference (set by main.py)
_ENGINE: Optional[Any] = None
_DEBUG_STATE: dict = {
"paused": False,
"current_step": 0,
"history": [],
}
def set_engine(engine):
global _ENGINE
_ENGINE = engine
def get_engine():
return _ENGINE
# -----------------------------
# RESUME
# -----------------------------
@router.post("/resume")
def resume():
_DEBUG_STATE["paused"] = False
if _ENGINE and hasattr(_ENGINE, "resume"):
_ENGINE.resume()
return {"state": "running"}
# -----------------------------
# PAUSE
# -----------------------------
@router.post("/pause")
def pause():
_DEBUG_STATE["paused"] = True
if _ENGINE and hasattr(_ENGINE, "pause"):
_ENGINE.pause()
return {"state": "paused"}
# -----------------------------
# STEP
# -----------------------------
@router.post("/step")
def step():
if _ENGINE and hasattr(_ENGINE, "step"):
_ENGINE.step()
_DEBUG_STATE["current_step"] += 1
return {"state": "stepped", "step": _DEBUG_STATE["current_step"]}
# -----------------------------
# STEP BACK (replay-only)
# -----------------------------
@router.post("/step-back")
def step_back():
if _DEBUG_STATE["current_step"] > 0:
_DEBUG_STATE["current_step"] -= 1
return {"state": "stepped_back", "step": _DEBUG_STATE["current_step"]}
# -----------------------------
# STATE
# -----------------------------
@router.get("/state")
def state():
eng = _ENGINE
active = eng and hasattr(eng, "is_active") and eng.is_active()
return {
"paused": _DEBUG_STATE["paused"],
"current_step": _DEBUG_STATE["current_step"],
"engine_attached": eng is not None,
"engine_active": active,
}
# -----------------------------
# Legacy wrapper for api.py facade
# -----------------------------
def debugger_endpoint(session_id, registry=None, bus=None):
"""Legacy — return current debugger state for a session."""
return {
"session_id": session_id,
"paused": _DEBUG_STATE["paused"],
"current_step": _DEBUG_STATE["current_step"],
"engine_attached": _ENGINE is not None,
}