Spaces:
Running
Running
| # Syntheogenesis HF Space — Docker SDK. | |
| # | |
| # Bake the Flask app + ESM-2 dependencies into a single image. HF Spaces | |
| # routes traffic to whatever port the container listens on (default 7860); | |
| # we honor that via the PORT env var. | |
| FROM python:3.12-slim | |
| # Run as a non-root user — HF Spaces convention. The user has uid 1000 | |
| # which matches the Space's writable directories. | |
| ARG USER_UID=1000 | |
| ARG USER_GID=1000 | |
| RUN groupadd --gid $USER_GID app \ | |
| && useradd --uid $USER_UID --gid $USER_GID -m -s /bin/bash app | |
| # System packages needed by Biopython / Pillow / general compile of any | |
| # native Python wheel that doesn't ship binary for Python 3.12 yet. | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| build-essential gcc git curl ca-certificates \ | |
| && rm -rf /var/lib/apt/lists/* | |
| WORKDIR /app | |
| # Install Python deps first so Docker can cache the layer when source changes. | |
| COPY requirements.txt ./ | |
| RUN pip install --no-cache-dir --upgrade pip \ | |
| && pip install --no-cache-dir -r requirements.txt | |
| # Now copy source + install the package itself. | |
| COPY --chown=app:app pyproject.toml ./ | |
| COPY --chown=app:app dee ./dee | |
| RUN pip install --no-cache-dir -e . | |
| # Pre-create writable dirs the app expects under /tmp (HF Spaces gives the | |
| # non-root user a writable /tmp but not /app/.dee). | |
| ENV HOME=/tmp \ | |
| XDG_CACHE_HOME=/tmp/.cache \ | |
| HF_HOME=/opt/hf-cache \ | |
| HF_HUB_DISABLE_TELEMETRY=1 \ | |
| HOST=0.0.0.0 \ | |
| PORT=7860 \ | |
| PYTHONUNBUFFERED=1 | |
| # HF_HOME now points at a BAKED, image-resident dir (not ephemeral /tmp) so the | |
| # default model's weights persist across Space restarts instead of re-downloading. | |
| RUN mkdir -p /tmp/.cache /tmp/.dee/output /tmp/.dee/state /opt/hf-cache \ | |
| && chown -R app:app /tmp/.dee /tmp/.cache /opt/hf-cache | |
| USER app | |
| # Bake the default ESM-2 (35M) weights into the image so a cold start doesn't pay | |
| # the re-download cost. Best-effort: if the prefetch fails at build time, the | |
| # build still succeeds and the app simply lazy-loads the model at runtime. | |
| RUN python -c "from transformers import AutoTokenizer, AutoModelForMaskedLM; m='facebook/esm2_t12_35M_UR50D'; AutoTokenizer.from_pretrained(m); AutoModelForMaskedLM.from_pretrained(m)" \ | |
| || echo 'ESM-2 prefetch skipped — model will lazy-load at runtime' | |
| EXPOSE 7860 | |
| # Production WSGI server. ONE gthread worker (state lives in-process), threads | |
| # for concurrency, generous timeout (the model load happens in a background | |
| # thread so requests stay fast). gunicorn's master auto-restarts the worker on a | |
| # crash, so an unhandled error no longer downs the whole Space until a reboot. | |
| CMD ["gunicorn", "dee.server:build_app()", \ | |
| "--workers", "1", "--threads", "8", "--worker-class", "gthread", \ | |
| "--bind", "0.0.0.0:7860", "--timeout", "120", "--graceful-timeout", "30", \ | |
| "--keep-alive", "5", "--access-logfile", "-", "--error-logfile", "-"] | |