CyberAttack-PLL / src /api.py
krishuggingface's picture
Update: Sync all modules, add detector/tests/validation, fix inference agent logic
20bc5e4
"""
FastAPI application for the PLL Cyberattack Detection OpenEnv.
Exposes HTTP endpoints for environment interaction:
POST /reset — Reset environment with task_id
POST /step — Submit an action and advance one step
GET /state — Get current internal state
GET /health — Health check (returns 200)
GET /tasks — List available tasks
"""
import asyncio
from typing import Any, Dict, Optional
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel
from src.models import Observation, Action, Reward, State
from src.env import PLLAttackEnv
app = FastAPI(
title="PLL Cyberattack Detection OpenEnv",
description="OpenEnv for AI-driven cyberattack detection on SRF-PLLs",
version="1.0.0",
)
env = PLLAttackEnv()
env_lock = asyncio.Lock()
class ResetRequest(BaseModel):
task_id: int = 0
seed: Optional[int] = None
class StepResponse(BaseModel):
observation: Observation
reward: Reward
done: bool
info: Dict[str, Any]
@app.post("/reset", response_model=Observation)
async def reset(req: Request):
"""Reset the environment and return initial observation."""
async with env_lock:
try:
body = await req.body()
if body:
data = await req.json()
request = ResetRequest(**data)
else:
request = ResetRequest()
except Exception:
request = ResetRequest()
return env.reset(task_id=request.task_id, seed=request.seed)
@app.post("/step", response_model=StepResponse)
async def step(action: Action):
async with env_lock:
if env.attack_generator is None:
raise HTTPException(status_code=400, detail="Call /reset before /step")
obs, reward, done, info = env.step(action)
return StepResponse(observation=obs, reward=reward, done=done, info=info)
@app.get("/state", response_model=State)
async def get_state():
async with env_lock:
return env.get_state()
@app.get("/health")
async def health():
"""Health check endpoint."""
return {
"status": "ok",
"version": "1.0.0",
"environment": "pll-cyberattack-detection",
"tasks_available": 3,
"episode_active": env.attack_generator is not None,
"current_step": env.step_count,
}
@app.get("/tasks")
async def list_tasks():
"""List all available tasks."""
return {
"tasks": [
{
"id": 0,
"name": "sinusoidal_fdi",
"difficulty": "easy",
"description": "Detect sinusoidal FDI attack"
},
{
"id": 1,
"name": "multi_attack_classification",
"difficulty": "medium",
"description": "Classify attack type from observations"
},
{
"id": 2,
"name": "stealthy_attack_detection",
"difficulty": "hard",
"description": "Detect stealthy attack before PLL lock loss"
},
]
}