Spaces:
Sleeping
Sleeping
| from contextlib import asynccontextmanager | |
| from pathlib import Path | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import FileResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from models import AttackAction, StepResult, ResetResponse, EpisodeState, AutoAttackRequest | |
| from server.environment import RedTeamEnvironment | |
| from server.config import get_settings | |
| from rewards.compute_rewards import RewardComputer | |
| from llm.pipeline import run_llm_pipeline, reset_conversation | |
| from llm.automated_attacker import generate_automated_attack | |
| env: RedTeamEnvironment = None | |
| async def lifespan(app: FastAPI): | |
| global env | |
| settings = get_settings() | |
| env = RedTeamEnvironment(max_turns=settings.max_turns) | |
| reward_computer = RewardComputer() | |
| env.set_reward_computer(reward_computer) | |
| env.set_llm_pipeline(run_llm_pipeline) | |
| yield | |
| app = FastAPI( | |
| title = "BreachOS", | |
| version = "0.1.0", | |
| lifespan = lifespan, | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins = ["*"], | |
| allow_credentials = True, | |
| allow_methods = ["*"], | |
| allow_headers = ["*"], | |
| ) | |
| _FRONTEND = Path(__file__).parent.parent / "frontend" | |
| if _FRONTEND.exists(): | |
| app.mount("/static", StaticFiles(directory=str(_FRONTEND)), name="static") | |
| async def serve_ui(): | |
| return FileResponse(str(_FRONTEND / "index.html")) | |
| async def health_check(): | |
| return {"status": "healthy", "version": "0.1.0"} | |
| async def reset_episode(): | |
| try: | |
| reset_conversation() | |
| observation = await env.reset() | |
| return ResetResponse(observation=observation, episode_id=observation.episode_id) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def step_episode(action: AttackAction): | |
| try: | |
| result = await env.step(action) | |
| return result | |
| except ValueError as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_state(): | |
| return env.get_state() | |
| async def get_history(): | |
| return {"history": env.get_history()} | |
| async def grade_episode(): | |
| if env.is_active: | |
| raise HTTPException(status_code=400, detail="Episode still active — finish it before grading.") | |
| history = env.get_history() | |
| if not history: | |
| raise HTTPException(status_code=400, detail="No episode history to grade.") | |
| from graders.programmatic_grader import grade_episode as do_grade | |
| result = do_grade(history) | |
| result["episode_id"] = env.episode_id | |
| return result | |
| async def auto_attack(request: AutoAttackRequest): | |
| if not env.is_active: | |
| raise HTTPException(status_code=400, detail="No active episode.") | |
| framing = generate_automated_attack(request.strategy_type.value, request.target_category.value) | |
| return {"framing": framing} | |
| def main(): | |
| import uvicorn | |
| uvicorn.run("server.app:app", host="0.0.0.0", port=7860, reload=False) | |
| if __name__ == "__main__": | |
| main() | |