"""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 = """\ CERNenv — LHC Discovery RL Environment

⚛️ CERNenv

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

What this is

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.

API

GET /healthliveness probe
GET /schemaJSON schemas for actions, observations, state
POST /resetstart a new episode (e.g. {"seed": 7, "scenario": "easy_diphoton_160"})
POST /stepexecute one action ({"action": {"action_type": ..., "parameters": {...}, "justification": "..."}})
GET /statecurrent public state snapshot
GET /docsinteractive Swagger UI
GET /metadataenvironment metadata

Quickstart

# 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"}}'

Companion Spaces

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()