# OpenEnv API container # # Two-stage build: # 1. builder - compiles the vendored CityFlow Python extension # 2. runtime - installs the API dependencies and copies the repo-local data # # Runtime env vars: # DATA_DIR generated CityFlow dataset root # default: /app/data/generated # SPLITS_DIR train/val/test split files # default: /app/data/splits # CHECKPOINT_PATH optional DQN checkpoint # default: /app/artifacts/dqn_shared/best_validation.pt # --------------------------------------------------------------------------- # ── Stage 1: Build CityFlow ───────────────────────────────────────────────── FROM python:3.12-slim AS builder # Build tools needed by CityFlow's CMake build RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cmake \ libboost-all-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /build # Copy only the CityFlow source (pybind11 is bundled as an extern submodule) COPY third_party/CityFlow ./CityFlow # Build and install cityflow into the builder's site-packages RUN rm -rf ./CityFlow/build RUN pip install --no-cache-dir ./CityFlow # Locate the compiled .so so we can copy it to the runtime stage RUN python -c "import cityflow, os; print(os.path.dirname(cityflow.__file__))" # ── Stage 2: Runtime ──────────────────────────────────────────────────────── FROM python:3.12-slim AS runtime WORKDIR /app # Copy the compiled cityflow extension from the builder COPY --from=builder /usr/local/lib/python3.12/site-packages/cityflow* \ /usr/local/lib/python3.12/site-packages/ # Install Python dependencies (no build tools needed here) COPY openenv_app/requirements.txt ./requirements.txt RUN pip install --no-cache-dir -r requirements.txt # Copy application source (only what the OpenEnv API needs at runtime) COPY agents/ ./agents/ COPY district_llm/ ./district_llm/ COPY env/ ./env/ COPY openenv_app/ ./openenv_app/ COPY server/__init__.py ./server/__init__.py COPY server/path_validators.py ./server/path_validators.py COPY server/policy_runner.py ./server/policy_runner.py COPY server/roadnet_matcher.py ./server/roadnet_matcher.py COPY training/ ./training/ COPY data/splits/ ./data/splits/ COPY data/generated/city_0002/ ./data/generated/city_0002/ COPY artifacts/dqn_shared/best_validation.pt ./artifacts/dqn_shared/best_validation.pt COPY artifacts/district_llm_adapter_v3/main_run/adapter/ ./artifacts/district_llm_adapter_v3/main_run/adapter/ # Keep runtime paths present, but expect the actual generated dataset to be # mounted or synced separately instead of baked into the image. RUN mkdir -p /app/data/generated /app/data/splits # Default paths (overridable at runtime via env vars). # DATA_DIR must point at a mounted/generated dataset root that contains city_*/ # directories and scenario files; only data/splits is bundled here. ENV DATA_DIR=/app/data/generated ENV SPLITS_DIR=/app/data/splits ENV CHECKPOINT_PATH=/app/artifacts/dqn_shared/best_validation.pt ENV DISTRICT_LLM_ADAPTER_PATH=/app/artifacts/district_llm_adapter_v3/main_run/adapter # OpenEnv and HF Spaces commonly use port 7860. EXPOSE 7860 CMD ["sh", "-c", "uvicorn openenv_app.app:app --host 0.0.0.0 --port ${PORT:-7860}"]