syntheogenesis / Dockerfile
Tengo Gzirishvili
Reliability: bake ESM-2 weights + bound in-memory jobs/rate-limit
843e792
Raw
History Blame Contribute Delete
2.89 kB
# 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", "-"]