physix / Dockerfile
Pratyush-01's picture
Upload folder using huggingface_hub
74ddb14 verified
# PhysiX-Live demo Space — CPU-only env + UI.
#
# What this Space hosts:
#
# :7860 uvicorn _space_app:app
# ├─ /reset, /step (OpenEnv stateless API)
# ├─ /interactive/* (browser session API)
# ├─ /web/ (built React SPA)
# └─ /interactive/.../llm-step (LLM-driven episode)
#
# What this Space does NOT host:
# * Inference. The demo is CPU-only — no torch, no vLLM, no GPU. When
# the UI calls `/interactive/.../llm-step` the server forwards to
# whatever OpenAI-compatible base URL the browser handed us
# (HF Router, OpenAI, Ollama, or our sister L4 Space at
# `Pratyush-01/physix-infer` for the trained 3B + Qwen baseline).
#
# Why a separate inference Space:
# Keeps this CPU image tiny (sub-second cold-start) so the demo URL
# never feels like it's stalled. The L4 Space pays GPU rates only
# while it's actually serving requests — its `sleep_time=300s` shuts
# it down between sessions. Two Spaces, two failure surfaces; if
# inference is broken the verifier-only demo (Custom URL → Ollama
# etc.) still works.
############################
# Stage 1: build the SPA
############################
# WORKDIR renamed (was /build) to break HF BuildKit's poisoned cache.
# The previous /build mount kept a stale pnpm symlink at
# /build/node_modules/@types/katex
# from an earlier failed deploy, and every subsequent `COPY frontend/ ./`
# blew up with `cannot copy to non-directory`. Switching paths gets us
# a fresh cache bucket; nothing in the project depends on /build itself.
FROM node:20-alpine AS frontend
WORKDIR /spa
RUN corepack enable
# Copy ALL of frontend/ first — including package.json/pnpm-lock.yaml —
# THEN install. Order matters: install runs ON TOP OF the source tree
# instead of the source tree being overlaid on top of a pre-installed
# node_modules, eliminating the directory-vs-symlink collision class
# of failure entirely.
COPY frontend/ ./
# Same-origin API fetches (relative paths). The Space serves both API and UI.
ENV VITE_PHYSIX_API_URL=""
# Cache-bust marker. Bump when an SPA change isn't taking on the Space.
# physix-spa-rebuild: 9
RUN pnpm install --frozen-lockfile \
&& pnpm exec tsc -b \
&& pnpm exec vite build --base=/web/
############################
# Stage 2: runtime (FastAPI + SPA)
############################
FROM python:3.11-slim AS runtime
ENV PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
HOME=/tmp/home \
HF_HOME=/tmp/hf_cache \
XDG_CACHE_HOME=/tmp/xdg-cache \
PORT=7860 \
PHYSIX_HOST=0.0.0.0 \
PHYSIX_CORS_ORIGINS=*
# curl for healthchecks; the slim image has neither curl nor build tools
# by default. Everything else (numpy, scipy, sympy) is a wheel install.
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Pin the server-side runtime stack. NO torch / unsloth / trl here —
# this Space never trains and never runs a model locally.
RUN pip install \
"openenv-core[core]>=0.2.2" \
"numpy>=1.24" \
"scipy>=1.10" \
"sympy>=1.12" \
"fastapi>=0.110" \
"uvicorn[standard]>=0.29" \
"pydantic>=2.5" \
"openai>=1.40" \
"requests>=2.31"
COPY pyproject.toml ./
COPY physix ./physix
COPY README.md ./
RUN pip install --no-deps -e .
# Built SPA from stage 1.
COPY --from=frontend /spa/dist /app/static
# Space wrapper — mounts the React SPA at /web/, registers / -> /web/
# redirect (OpenEnv's create_fastapi_app doesn't add one for us).
COPY scripts/space_app.py /app/_space_app.py
# Pre-create writable dirs. HF Spaces runs containers as a non-root UID
# with no /etc/passwd entry, so any cache path under $HOME must exist
# and be world-writable BEFORE the runtime user shows up.
RUN mkdir -p "$HOME" "$HF_HOME" "$XDG_CACHE_HOME" \
&& chmod -R 0777 /tmp /app
EXPOSE 7860
# /health is OpenEnv's stock endpoint and turns 200 once uvicorn binds.
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -fsS "http://127.0.0.1:${PORT}/health" || exit 1
ENV ENABLE_WEB_INTERFACE=true
CMD ["python3", "-m", "uvicorn", "_space_app:app", "--host", "0.0.0.0", "--port", "7860"]