Spaces:
Sleeping
Sleeping
| """FastAPI application for the Cybersec OpenEnv environment. | |
| The OpenEnv ``openenv.yaml`` declares ``app: server.app:app`` so this module | |
| must expose a module-level ``app`` and a ``main`` callable for uvicorn. | |
| Endpoints (provided by ``openenv.core.env_server.create_app``): | |
| * POST /reset - one-shot reset of a fresh environment instance | |
| * POST /step - one-shot step of a fresh environment instance | |
| * GET /state - current environment state | |
| * GET /schema - action / observation schemas | |
| * WS /ws - persistent WebSocket session (use this for episodes) | |
| Configuration knobs (read at process start, not per-request): | |
| ``CYBERSEC_SCENARIO_ID`` - default scenario; if unset, scenario follows | |
| reset ``seed`` (see ``CybersecEnvironment.reset``; | |
| omitted seed uses a random seed per reset). | |
| ``CYBERSEC_ATTACKER_PERSONALITY`` - default personality; if unset, sampled by RNG. | |
| ``CYBERSEC_MAX_CONCURRENT_ENVS`` - max concurrent WebSocket sessions (default 8). | |
| """ | |
| from __future__ import annotations | |
| import os | |
| from typing import Optional | |
| from openenv.core.env_server.http_server import create_app | |
| try: | |
| from ..models import AttackerPersonality, CybersecAction, CybersecObservation | |
| from .cybersec_environment import CybersecEnvironment | |
| except ImportError: # pragma: no cover - hit only when CWD == cybersec/ and we're run as `server.app` | |
| from models import AttackerPersonality, CybersecAction, CybersecObservation # type: ignore[no-redef] | |
| from server.cybersec_environment import CybersecEnvironment # type: ignore[no-redef] | |
| def _env_factory() -> CybersecEnvironment: | |
| scenario_id: Optional[str] = os.getenv("CYBERSEC_SCENARIO_ID") or None | |
| personality_raw: Optional[str] = os.getenv("CYBERSEC_ATTACKER_PERSONALITY") or None | |
| personality = AttackerPersonality(personality_raw) if personality_raw else None | |
| return CybersecEnvironment( | |
| default_scenario_id=scenario_id, | |
| default_personality=personality, | |
| ) | |
| _max_concurrent = int(os.getenv("CYBERSEC_MAX_CONCURRENT_ENVS", "8")) | |
| app = create_app( | |
| env=_env_factory, | |
| action_cls=CybersecAction, | |
| observation_cls=CybersecObservation, | |
| env_name="cybersec", | |
| max_concurrent_envs=_max_concurrent, | |
| ) | |
| def main() -> None: | |
| """Console-script entry point. | |
| Used by: | |
| * the ``server`` console script declared in ``pyproject.toml`` | |
| (``openenv validate`` requires this exact name). | |
| * ``python -m cybersec.server.app`` and ``python server/app.py``. | |
| CLI flags are honored for local development; ``CYBERSEC_HOST`` / | |
| ``CYBERSEC_PORT`` env vars take precedence so the Docker image and | |
| Hugging Face Spaces deploy can override the bind address without code | |
| changes. | |
| """ | |
| import argparse | |
| import uvicorn | |
| parser = argparse.ArgumentParser(description="Run the Cybersec OpenEnv server.") | |
| parser.add_argument("--host", default="0.0.0.0") | |
| parser.add_argument("--port", type=int, default=8000) | |
| args = parser.parse_args() | |
| host = os.getenv("CYBERSEC_HOST", args.host) | |
| port = int(os.getenv("CYBERSEC_PORT", str(args.port))) | |
| uvicorn.run(app, host=host, port=port, reload=False) | |
| if __name__ == "__main__": | |
| main() | |
| __all__ = ["app", "main"] | |