| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Autonomous Enterprise Payment Orchestrator (AEPO) | |
| # Hugging Face Spaces β Production Container | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # | |
| # Architecture: | |
| # Stage 1 (frontend-build) β Node.js 20 builds the Next.js dashboard to a | |
| # static export (out/) that requires no Node runtime. | |
| # Stage 2 (runtime) β Python 3.10-slim runs the FastAPI server which | |
| # serves BOTH the OpenEnv API (POST /reset, /step, | |
| # GET /state) AND the static dashboard at /. | |
| # | |
| # Port mapping: | |
| # 7860 β the ONLY port exposed; Hugging Face Spaces routes external traffic here. | |
| # | |
| # OpenEnv compliance: | |
| # POST /reset β initialise episode | |
| # POST /step β advance one step | |
| # GET /state β inspect current observation | |
| # GET / β dashboard UI (static HTML/JS served by FastAPI) | |
| # | |
| # Usage: | |
| # docker build -t aepo . | |
| # docker run -p 7860:7860 aepo | |
| # | |
| # Space (browser): https://huggingface.co/spaces/unknown1321/autonomous-enterprise-payment-orchestrator | |
| # API (OpenEnv): https://unknown1321-autonomous-enterprise-payment-orchestrator.hf.space | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Stage 1 β Build the Next.js dashboard as a fully-static export | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| FROM node:20-alpine AS frontend-build | |
| # Prevent Node.js from running out of memory during the Next.js build | |
| ENV NODE_OPTIONS="--max_old_space_size=4096" | |
| WORKDIR /build | |
| # Install deps in a separate layer so Docker cache is reused when only source | |
| # files change (not package.json). | |
| COPY frontend/package.json frontend/package-lock.json ./ | |
| RUN npm ci --prefer-offline | |
| # Copy the rest of the frontend source and build. | |
| # next.config.mjs must set output: 'export' β generates out/ | |
| COPY frontend/ ./ | |
| RUN npm run build | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Stage 2 β Python runtime: FastAPI + static dashboard files | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| FROM python:3.10-slim AS runtime | |
| # ββ Labels βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| LABEL maintainer="Umesh Maurya <unknown1321>" \ | |
| description="AEPO β Autonomous Enterprise Payment Orchestrator (OpenEnv + HF Spaces)" \ | |
| version="0.2.0" | |
| # ββ Python environment hardening βββββββββββββββββββββββββββββββββββββββββββββ | |
| ENV PYTHONDONTWRITEBYTECODE=1 \ | |
| PYTHONUNBUFFERED=1 \ | |
| PIP_NO_CACHE_DIR=1 \ | |
| PIP_DISABLE_PIP_VERSION_CHECK=1 \ | |
| OMP_NUM_THREADS=1 \ | |
| MKL_NUM_THREADS=1 \ | |
| OPENBLAS_NUM_THREADS=1 \ | |
| MALLOC_ARENA_MAX=2 | |
| # ββ HF Spaces mandatory: non-root user UID=1000 ββββββββββββββββββββββββββββββ | |
| # Hugging Face Spaces executes containers with UID 1000. Creating a matching | |
| # user prevents permission errors on /home/user writes (logs, caches, etc.). | |
| RUN useradd -m -u 1000 -s /bin/bash user | |
| ENV HOME=/home/user | |
| WORKDIR /app | |
| # ββ Python dependencies βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Install all non-torch deps first (fast), then fetch the CPU-only torch wheel | |
| # directly by URL to avoid the SHA mismatch that the simple-index advertises. | |
| COPY requirements.txt . | |
| RUN sed -e '/^--extra-index-url/d' -e '/^torch==/d' requirements.txt \ | |
| > /tmp/req_base.txt \ | |
| && pip install --no-cache-dir -r /tmp/req_base.txt \ | |
| && pip install --no-cache-dir \ | |
| "https://download.pytorch.org/whl/cpu/torch-2.2.0%2Bcpu-cp310-cp310-linux_x86_64.whl" \ | |
| && rm -f /tmp/req_base.txt | |
| # ββ Application source ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Copy only the files needed at runtime β .venv, java-mirror, node_modules, | |
| # tests, and docs are excluded via .dockerignore. | |
| COPY aepo_types.py unified_gateway.py dynamics_model.py graders.py inference.py openenv.yaml ./ | |
| COPY server/ ./server/ | |
| # Pre-trained artefacts β needed by inference.py (Q-table, MLP weights). | |
| # These are generated by train.py and committed / built before the Docker push. | |
| COPY results/ ./results/ | |
| # ββ Static frontend (built in Stage 1) βββββββββββββββββββββββββββββββββββββββ | |
| # FastAPI mounts this directory at "/" so the dashboard is served at the Space | |
| # root while OpenEnv API routes (/reset, /step, /state) retain priority because | |
| # FastAPI resolves explicit routes before mounted sub-apps. | |
| COPY --from=frontend-build /build/out ./frontend/out | |
| # Transfer ownership to the non-root user so all files are readable/writable. | |
| RUN chown -R user:user /app | |
| # ββ Switch to non-root user (HF Spaces requirement) ββββββββββββββββββββββββββ | |
| USER user | |
| # ββ Expose the single public port ββββββββββββββββββββββββββββββββββββββββββββ | |
| EXPOSE 7860 | |
| # ββ Healthcheck β HF Spaces and openenv validate both probe GET / βββββββββββββ | |
| HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ | |
| CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:7860/')" \ | |
| || exit 1 | |
| # ββ Default command βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860", \ | |
| "--workers", "2", "--log-level", "info"] | |