{title}
{description}
"""FastAPI server exposing the Adaptive AI Firewall environment.
Endpoints:
POST /reset — Start a new episode
POST /step — Multi-session step (batch actions)
POST /step_single — Single-session step (Gymnasium-compatible)
GET /state — Current environment state
GET /tools — List available tool names
POST /tool/{name} — Call a specific tool
GET /health — Health check
GET /stats — Current episode statistics
"""
from __future__ import annotations
import csv
import json
import os
from pathlib import Path
from typing import Any
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
from dotenv import load_dotenv
from server.embedded_dashboard_images import IMAGES
from server.firewall_environment import FirewallEnvironment, ACTIONS
from models import (
HealthResponse,
NetworkStatsResponse,
ResetRequest,
StateResponse,
StepRequest,
StepResponse,
StepSingleRequest,
StepSingleResponse,
ToolRequest,
ToolsListResponse,
)
load_dotenv()
def _clean_env_value(value: str) -> str:
return value.strip().strip("`").strip().strip("'").strip('"').strip()
def _resolve_api_key(value: str | None) -> str:
return _clean_env_value(value or os.getenv("HF_TOKEN") or "")
def _resolve_model(value: str | None) -> str:
return _clean_env_value(value or os.getenv("MODEL_NAME") or "")
def _resolve_base_url(value: str | None) -> str:
return _clean_env_value(
value
or os.getenv("API_BASE_URL")
or ""
)
ROOT_DIR = Path(__file__).resolve().parent.parent
PERFORMANCE_MATRIX_PATH = ROOT_DIR / "output" / "performance_matrix.csv"
GRAPH_SPECS = [
(
"Training Loss",
"01_training_loss.png",
"Loss trend across self-play rounds.",
),
(
"Reward Analysis",
"02_reward_analysis.png",
"Raw score versus difficulty-normalized reward.",
),
(
"Elo Progression",
"03_elo_progression.png",
"Agent Elo compared with adaptive difficulty Elo.",
),
(
"Win Rate",
"04_win_rate.png",
"Rolling win rate and Elo delta per round.",
),
(
"Detection vs FP",
"05_detection_fp_rate.png",
"Detection, false-positive rate, and efficiency.",
),
(
"Difficulty Curve",
"06_difficulty_progression.png",
"Adaptive curriculum difficulty progression.",
),
]
def _load_performance_matrix(limit: int = 10) -> tuple[list[str], list[dict[str, str]]]:
"""Load a preview of the performance matrix CSV for dashboard rendering."""
if not PERFORMANCE_MATRIX_PATH.exists():
return [], []
with PERFORMANCE_MATRIX_PATH.open("r", encoding="utf-8", newline="") as handle:
reader = csv.DictReader(handle)
rows = list(reader)
headers = [
"Round",
"Raw_Score",
"Abs_Training_Loss",
"Detection_Rate",
"FP_Rate",
"Efficiency",
"Agent_Elo",
"Difficulty_Elo",
]
preview = [{key: row.get(key, "") for key in headers} for row in rows[:limit]]
return headers, preview
def _build_graph_cards() -> str:
cards: list[str] = []
for title, filename, description in GRAPH_SPECS:
image_src = IMAGES.get(filename, "")
media = (
f''
if image_src
else '
{description}
Monitor the firewall environment, step through actions, inspect live state, and review the training visuals from the current performance matrix in one place.
Click Reset to start a new episode, then use Step to apply a single action.
{}
Core state values update after every API action.
The dashboard below uses the generated training artifacts and the latest API responses.
Preview of the tracked performance matrix used for training analysis.
Embedded graphs generated from the available dashboard images so the Space can display them reliably.