cascade / DEPLOYMENT_GUIDE.md
bobbypaton
Initial CASCADE HF Space deployment
233f6d4

CASCADE β†’ Hugging Face Spaces: Deployment Guide

Overview

The HF Space replaces Django with Flask but keeps the exact same Redis job-queue pattern and frontend JS. The app is mounted at /cascade_v1/ so the live URL is:

https://patonlab-cascade.hf.space/cascade_v1/predict/

The Space is named cascade on HF β€” the _v1 is a subpath in Flask, not the repo name. This means you can add cascade_v2 later under the same Space without creating a new repo.

hf_space/
β”œβ”€β”€ Dockerfile               # Python 3.7, Redis, TF1.15, Keras 2.2.5
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ start.sh                 # Redis β†’ worker β†’ gunicorn
β”œβ”€β”€ app.py                   # Flask Blueprint mounted at /cascade_v1
β”œβ”€β”€ worker.py                # Redis consumer, runs GNN inference
β”œβ”€β”€ setup_assets.sh          # One-time script: copies binaries from nova app
β”œβ”€β”€ README.md                # HF Space card (YAML front-matter required)
β”œβ”€β”€ .gitattributes           # Git LFS tracking for .hdf5 and .p files
β”œβ”€β”€ .gitignore
β”œβ”€β”€ NMR_Prediction/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ apply.py             # ← copied + path-fixed by setup_assets.sh
β”‚   β”œβ”€β”€ genConf.py           # ← copied by setup_assets.sh
β”‚   β”œβ”€β”€ valid.py             # new (simple RDKit SMILES check)
β”‚   β”œβ”€β”€ preprocessor.p       # ← copied by setup_assets.sh
β”‚   └── schnet_edgeupdate/
β”‚       └── best_model.hdf5  # ← copied by setup_assets.sh (31 MB)
β”œβ”€β”€ nfp/                     # ← copied by setup_assets.sh
β”œβ”€β”€ static/                  # ← copied by setup_assets.sh
β”‚   β”œβ”€β”€ main.css
β”‚   β”œβ”€β”€ sketch.css
β”‚   β”œβ”€β”€ JSmol.min.js
β”‚   β”œβ”€β”€ images/
β”‚   β”œβ”€β”€ j2s/
β”‚   β”œβ”€β”€ jquery/
β”‚   β”œβ”€β”€ js/
β”‚   └── jsme/
└── templates/
    └── cascade/
        β”œβ”€β”€ base.html
        β”œβ”€β”€ home.html
        β”œβ”€β”€ predict.html
        β”œβ”€β”€ results.html
        └── about.html

Step 1 β€” Copy assets from the nova app

Run the setup script (copies model weights, inference code, static files, and applies the preprocessor path fix automatically):

cd ~/Documents/huggingface/cascade/hf_space
bash setup_assets.sh

You should see a βœ“ for each of the 5 asset groups. Afterwards confirm:

ls NMR_Prediction/   # apply.py  genConf.py  preprocessor.p  schnet_edgeupdate/  valid.py  __init__.py
ls nfp/              # __init__.py  layers/  models/  preprocessing/
ls static/           # main.css  JSmol.min.js  j2s/  jsme/  jquery/  js/  images/

Step 2 β€” Test locally with Docker

cd ~/Documents/huggingface/cascade/hf_space

# Build (takes ~5 min first time β€” TF1.15 + RDKit are large)
docker build -t cascade-local .

# Run
docker run -p 7860:7860 cascade-local

# Open in browser
open http://localhost:7860/cascade_v1/predict/

What to check:

  • Page loads, JSME editor opens
  • Submitting CC(=O)O (acetic acid) returns a task_id (check browser Network tab)
  • check_task eventually returns the results HTML with 2D SVG and shift table
  • Worker logs appear in the Docker terminal: Processing task ...

Common first-run issues:

Symptom Fix
ModuleNotFoundError: nfp Check nfp/ is in the root of hf_space, not nested inside another folder
OSError: preprocessor.p not found Re-run setup_assets.sh β€” the path fix may not have applied
keras load_model custom layer error Check custom_objects in worker.py match the nfp layer class names exactly
Redis connection refused Redis takes ~1 s to start; the worker retries automatically β€” wait a moment
500 on /cascade_v1/predict/ Check templates/cascade/predict.html exists
JSME editor blank Check static/jsme/jsme.nocache.js was copied across

Step 3 β€” Create the Hugging Face Space

# Install HF CLI if needed
pip install huggingface_hub

# Login (needs a write token from hf.co/settings/tokens)
huggingface-cli login

# Create the Space β€” named 'cascade', Docker SDK, public, under patonlab org
huggingface-cli repo create cascade \
    --type space \
    --space-sdk docker \
    --organization patonlab

# This creates: https://huggingface.co/spaces/patonlab/cascade
# Live URL will be: https://patonlab-cascade.hf.space/cascade_v1/predict/

Step 4 β€” Push to Hugging Face

cd ~/Documents/huggingface/cascade/hf_space

git init
git lfs install          # Required β€” .hdf5 and .p are tracked via Git LFS

# .gitattributes is already written; just add it
git add .gitattributes
git remote add origin https://huggingface.co/spaces/patonlab/cascade

git add .
git commit -m "Initial CASCADE HF Space deployment"
git push origin main

The Space builds automatically. Build logs at: https://huggingface.co/spaces/patonlab/cascade β†’ Logs tab

Build time is typically 8–12 minutes (TF1.15 + RDKit are large).


Step 5 β€” Verify the live Space

Once the build is green:

  1. https://patonlab-cascade.hf.space/cascade_v1/predict/ β€” page loads
  2. Submit CC(=O)O (acetic acid) β€” result in ~10–15 s
  3. Submit CC1=CC(=CC(=C1)O)C β€” result in ~30–45 s
  4. Test the JSME drawing tool β†’ OK β†’ predicted shifts labelled on 2D structure

Step 6 β€” Update the GitHub README

Update patonlab/CASCADE on GitHub to point to the new URL:

The production web server can be found here:
https://patonlab-cascade.hf.space/cascade_v1/predict/

Ongoing maintenance

Pushing updates:

cd ~/Documents/huggingface/cascade/hf_space
git add . && git commit -m "your message" && git push origin main

HF rebuilds automatically on each push.

Checking logs: HF Space β†’ Logs tab (worker output streams there in real time)

Cold starts: The free HF tier sleeps after inactivity. First request after sleep takes ~30 s. Upgrade to HF Pro ($9/mo) for persistent Spaces if needed.

Redis: Results have a 1-hour TTL and do not persist across container restarts. This is fine for interactive use.


What changed from the original Django app

Original (nova) New HF Space
Django 2.1 Flask 2.0 with Blueprint at /cascade_v1
uwsgi gunicorn
django.template tags Plain Jinja2 (/static/... paths)
ALLOWED_HOSTS = ['129.82.62.62'] Flask HOST=0.0.0.0
Hard-coded SECRET_KEY Not needed (no sessions/auth)
db.sqlite3 Removed (unused)
cascade/preprocessor.p path NMR_Prediction/preprocessor.p
Model loaded in Django views at startup Loaded once in worker.py at startup
Manual file copies + sed fix Automated via setup_assets.sh