"""FastAPI app exposing ``CERNCollisionEnvironment`` over the OpenEnv HTTP API. We delegate the standard OpenEnv routes (``/reset``, ``/step``, ``/state``, ``/schema``, ``/health``, ``/mcp``) to ``create_fastapi_app`` and add a human-friendly landing page at ``/`` so the Hugging Face Space preview shows the project description instead of a 404. """ from __future__ import annotations import os from typing import Optional from fastapi.responses import HTMLResponse from openenv.core.env_server import create_fastapi_app from models import CollisionObservation, ExperimentAction from server.environment import CERNCollisionEnvironment _LANDING_PAGE = """\
An LHC (Large Hadron Collider) particle-discovery RL environment for autonomous physicist agents — built for the Meta OpenEnv Hackathon.
OpenEnv POMDP 16 action types 3 difficulty levels HTTP + WebSocket
A Large Language Model (LLM) agent plays a high-energy physicist running an analysis at the LHC. Each step it picks one structured action — configure the beam, allocate luminosity, set a trigger, collect collisions, fit a resonance, estimate significance, submit a discovery claim, and so on — and receives a noisy detector-style observation. The latent particle (mass, decay channel, branching ratios, width) is hidden ground truth. Reward decomposes into per-step shaping + a dominant terminal calibration against the truth particle.
GET /health | liveness probe |
|---|---|
GET /schema | JSON schemas for actions, observations, state |
POST /reset | start a new episode (e.g. {"seed": 7, "scenario": "easy_diphoton_160"}) |
POST /step | execute one action ({"action": {"action_type": ..., "parameters": {...}, "justification": "..."}}) |
GET /state | current public state snapshot |
GET /docs | interactive Swagger UI |
GET /metadata | environment metadata |
# reset
curl -X POST $URL/reset \\
-H 'Content-Type: application/json' \\
-d '{"seed": 7, "scenario": "easy_diphoton_160"}'
# step
curl -X POST $URL/step \\
-H 'Content-Type: application/json' \\
-d '{"action": {"action_type": "configure_beam",
"parameters": {"sqrt_s_tev": 13.0},
"justification": "set 13 TeV"}}'
CERNenv · OpenEnv-compatible · BSD-3-Clause
""" def make_env_factory( max_steps: int, default_difficulty: Optional[str], ): def factory() -> CERNCollisionEnvironment: return CERNCollisionEnvironment( max_steps=max_steps, default_difficulty=default_difficulty, ) return factory def build_app( *, max_steps: int = 40, default_difficulty: Optional[str] = None, ): """Construct the FastAPI app with a per-session environment factory. The OpenEnv-provided routes (`/reset`, `/step`, `/state`, `/schema`, `/health`, `/mcp`) come from ``create_fastapi_app``. We then mount a friendly landing page at ``/`` so the Space preview is informative. """ factory = make_env_factory(max_steps=max_steps, default_difficulty=default_difficulty) fa_app = create_fastapi_app(factory, ExperimentAction, CollisionObservation) @fa_app.get("/", response_class=HTMLResponse, include_in_schema=False) def landing() -> HTMLResponse: # pragma: no cover - trivial return HTMLResponse(_LANDING_PAGE) return fa_app app = build_app( max_steps=int(os.getenv("CERNENV_MAX_STEPS", "40")), default_difficulty=os.getenv("CERNENV_DEFAULT_DIFFICULTY") or None, ) def main() -> None: # pragma: no cover - CLI entrypoint import uvicorn host = os.getenv("HOST", "0.0.0.0") port = int(os.getenv("PORT", "8000")) uvicorn.run("server.app:app", host=host, port=port, log_level="info") if __name__ == "__main__": # pragma: no cover main()