BioPatch / Dockerfile
Jerry
BioPatch backend (clean state, history rewritten)
10795db
Raw History Blame Contribute Delete
4.96 kB
# syntax=docker/dockerfile:1.7
# ---------------------------------------------------------------------------
# BioPatch backend — Hugging Face Spaces + Fly.io compatible.
#
# IMPORTANT — LLM backend on this image:
# Default: JARVIS_LLM_BACKEND=local → llama-cpp-python in-process
# Default model: BioMistral-7B Q4_K_M (~4.4 GB weights, ~6 GB RAM with
# 8K context). Tuned for the HF Spaces cpu-upgrade tier (8 vCPU /
# 32 GB RAM). On the free tier this OOMs at load time — fall back to
# a smaller model by overriding JARVIS_LLM_REPO / JARVIS_LLM_FILE /
# JARVIS_LLM_CHAT_FORMAT in the Space Variables.
#
# Why local and not HF Inference on a Space:
# HF Spaces sandbox blocks outbound DNS to api-inference.huggingface.co
# (anti-loop policy), so the hosted Inference API is unreachable from
# inside a Space. Local llama-cpp works.
#
# Set JARVIS_LLM_BACKEND=hf if you deploy this image OUTSIDE a Space
# (e.g. Fly.io) and prefer the hosted API; you'll also need HF_TOKEN.
#
# Persistent storage:
# When the Space has persistent storage enabled, /data is mounted as
# a writable volume. The env vars below default to /data paths so the
# SQLite DB, wound photos and the GGUF model cache survive restarts.
# Without persistent storage, override these to /app paths via the
# Space Variables.
#
# Thread safety: every BLAS / OpenMP / sklearn parallel pool is capped
# at 1 thread + the RF model is forced to n_jobs=1 in code. This
# eliminates the GGML_ASSERT / 'Failed joining thread' cascade that
# crashed the container under simulator load (joblib pool corrupted
# llama's GGML workspace).
# ---------------------------------------------------------------------------
ARG PYTHON_VERSION=3.12
FROM python:${PYTHON_VERSION}-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_PREFER_BINARY=1
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libopenblas-dev \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*
# HF Spaces runs as UID 1000.
RUN useradd -m -u 1000 user
WORKDIR /app
# Core deps first.
COPY backend/requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt
# LLM stack — pull pre-built CPU wheels from abetlen's dedicated index.
# llama-cpp-python intentionally publishes only source dists to PyPI;
# the binary wheels (built per Python version × platform) live at
# https://abetlen.github.io/llama-cpp-python/whl/cpu. With --only-binary
# pip refuses to compile from source, so if the wheel is genuinely
# missing for our Python it errors out in seconds with a clear message
# instead of OOMing 8 minutes into a C++ compile on the HF free tier.
COPY backend/requirements-llm.txt /app/requirements-llm.txt
RUN pip install --only-binary=:all: \
--extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu \
-r requirements-llm.txt
COPY backend /app
RUN python -m scripts.seed_clinical_data || true
# Thread caps — keep sklearn / OpenBLAS / OpenMP / MKL single-threaded.
# Combined with the n_jobs=1 forced on the loaded RandomForest model,
# this guarantees the joblib threadpool never spawns and never collides
# with llama-cpp's GGML workspace.
ENV OMP_NUM_THREADS=1 \
OPENBLAS_NUM_THREADS=1 \
MKL_NUM_THREADS=1 \
BLIS_NUM_THREADS=1 \
NUMEXPR_MAX_THREADS=1 \
JARVIS_LLM_BACKEND=local \
JARVIS_LLM_REPO=MaziyarPanahi/BioMistral-7B-GGUF \
JARVIS_LLM_FILE=BioMistral-7B.Q4_K_M.gguf \
JARVIS_LLM_CHAT_FORMAT=mistral-instruct \
JARVIS_LLM_CACHE=/data/models \
JARVIS_LLM_CTX=4096 \
JARVIS_LLM_THREADS=8 \
JARVIS_HF_MODEL=meta-llama/Llama-3.2-3B-Instruct \
DATABASE_URL=sqlite:////data/biopatch.db \
WOUND_PHOTO_DIR=/data/wound_photos \
HF_HOME=/data/.cache/huggingface \
PORT=7860 \
ENABLE_SIMULATOR=true \
SIMULATOR_INTERVAL_SECONDS=15.0 \
AUTO_SEED=true
# Pre-create fallback dirs under /app for hosts WITHOUT persistent /data
# (Fly.io, local docker run, etc.). On a HF Space with persistent storage
# enabled, /data is mounted at runtime and the env vars above override
# these paths.
RUN mkdir -p /app/ai/artifacts/models /app/data/wound_photos /app/.cache/huggingface \
&& chown -R user:user /app
# Stay as root for the runtime. HF Spaces persistent /data is mounted
# with FUSE-style perms that silently reject chown from inside the
# container, so dropping to uid 1000 leaves the volume unwritable
# (sqlite OperationalError: unable to open database file). Running
# uvicorn as root sidesteps the perm check; the HF sandbox confines
# the container regardless. The threading caps + RF n_jobs=1 already
# neutralise the previous GGML / joblib collision, so non-root is no
# longer required for stability.
EXPOSE 8000 7860
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port ${PORT:-8000} --workers 1"]