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