# HydroPD — single-container Hugging Face Space (Docker SDK). # One FastAPI process serves the built React UI (/) and the real inference API (/api), # password-gated by $APP_PASSWORD. Built from a staging dir assembled by # scripts/build_hf_space.py (mirrors the repo layout: hydropd/, website/, data/Used Papers/). # # Layout in the image (paths the code resolves relative to): # /app/hydropd the pipeline package (PYTHONPATH=/app) # /app/website/backend FastAPI app (registry -> ../src/data/generated) # /app/website/src/data/generated/models.json the 30 usable models (registry source of truth) # /app/website/dist built frontend (app.py serves it) # /app/data/Used Papers//step4/models/*.joblib + _oof.npy (HYDROPD_ROOT=/app/data) # ---- frontend build ---- FROM node:20-slim AS web WORKDIR /web COPY website/package.json website/package-lock.json ./ RUN npm ci COPY website/ ./ RUN npm run build # -> /web/dist # ---- python runtime ---- FROM python:3.10-slim ENV PYTHONUNBUFFERED=1 \ PYTHONPATH=/app \ HYDROPD_ROOT=/app/data \ HF_HUB_OFFLINE=1 \ TRANSFORMERS_OFFLINE=1 \ HF_HOME=/app/.cache/huggingface \ TORCH_HOME=/app/.cache/torch \ PORT=7860 WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends libgomp1 \ && rm -rf /var/lib/apt/lists/* # Pinned deps so the trained joblibs unpickle exactly (sklearn/xgboost minors are load-bearing). COPY website/backend/requirements.txt /tmp/req.txt # numpy<2 is load-bearing: torch 2.2.2 was compiled against NumPy 1.x and fails to # initialise ("_ARRAY_API not found") under NumPy 2.x. pyahocorasick (module # `ahocorasick`) + pyteomics are import-time deps of hydropd (index/proteome/biopep). RUN pip install --no-cache-dir torch==2.2.2 --index-url https://download.pytorch.org/whl/cpu \ && pip install --no-cache-dir "numpy<2" "scikit-learn==1.7.2" "xgboost==1.7.6" "fair-esm==2.0.0" \ peptides pandas scipy pyarrow joblib huggingface_hub tokenizers pyahocorasick pyteomics \ requests pyyaml \ && pip install --no-cache-dir -r /tmp/req.txt # Pfly training runtime (dlomix BiGRU on TensorFlow). CPU-only TF; pinned to the versions the # `pfly` conda env validated (tf 2.15 / dlomix 0.2.7 / keras-2). TF 2.15 supports numpy<2, so it # is consistent with the numpy pin above. Isolated layer: only imported on demand by the Pfly # training branch (hydropd.train_adapter -> hydropd.pfly), so it never slows normal startup. RUN pip install --no-cache-dir "tensorflow-cpu==2.15.0" "dlomix==0.2.7" datasets COPY hydropd/ /app/hydropd/ COPY bioactivity_tools/ /app/bioactivity_tools/ COPY website/backend/ /app/website/backend/ COPY website/src/data/generated/ /app/website/src/data/generated/ COPY data/ /app/data/ COPY --from=web /web/dist /app/website/dist # Pre-fetch the embedder weights at BUILD (so first request isn't a cold ~190 MB download). # HF_HUB_OFFLINE is flipped on only for runtime; build fetches are allowed here. RUN HF_HUB_OFFLINE=0 TRANSFORMERS_OFFLINE=0 python -c "import esm; esm.pretrained.esm2_t12_35M_UR50D()" || echo "esm2 prefetch skipped" RUN HF_HUB_OFFLINE=0 python -c "from huggingface_hub import snapshot_download; snapshot_download('dzjxzyd/PepBERT-large-UniParc')" || echo "pepbert prefetch skipped" EXPOSE 7860 WORKDIR /app/website/backend CMD ["python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]