Andrej Janchevski commited on
Commit
b385912
·
1 Parent(s): 9b94c0c

build(deploy): add docker image and compose for hf spaces

Browse files

- Dockerfile: multi-stage build. Node 20 builds the Vue SPA, then a
mambaorg/micromamba runtime mirrors the local website_c env from
environment.yml + src/backend/requirements.txt (Python 3.9, rdkit
2023.03.2, boost 1.78, GPU torch+cu118 wheels). Exposes :7860 with
RESEARCH_ROOT=/app/research and CHECKPOINTS_ROOT=/app/checkpoints.
- entrypoint.sh: pre-warms huggingface_hub.snapshot_download from
bani57/checkpoints into /app/checkpoints before exec'ing gunicorn,
so worker startup never blocks on the network.
- docker-compose.yml: local-dev parity on http://localhost:7860 with a
named volume so checkpoints persist across runs. DJANGO_SECRET_KEY
is a required interpolation (${VAR:?...}) — compose fails fast if a
.env file isn't supplied.
- .env.example: template with the secrets.token_urlsafe recipe.
- .dockerignore: keeps node_modules, screenshots, datasets, local
checkpoints, .claude and plans/ out of the build context.
- .gitignore: ignore .env / build artefacts (staticfiles, dist), keep
.env.example and the Vite envs allow-listed.
- README.md: HF Space landing card with the required YAML front-matter
(sdk: docker, app_port: 7860).

Files changed (8) hide show
  1. .dockerignore +34 -0
  2. .env.example +16 -0
  3. .gitignore +11 -0
  4. Dockerfile +53 -0
  5. README.md +21 -0
  6. docker-compose.yml +29 -0
  7. entrypoint.sh +39 -0
  8. environment.yml +15 -0
.dockerignore ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Keep the build context lean so Docker doesn't pipe gigabytes of unrelated
2
+ # files (model weights, datasets, screenshots) to the daemon.
3
+ .git
4
+ .gitignore
5
+ .idea
6
+ .vscode
7
+ .claude
8
+ *.pyc
9
+ __pycache__
10
+ node_modules
11
+ src/frontend/node_modules
12
+ src/frontend/dist
13
+
14
+ # Local checkpoints — pulled at runtime from HF Hub instead.
15
+ src/research/**/checkpoints/*
16
+ src/research/**/results/**/*.tar
17
+ src/research/**/results/**/*.ckpt
18
+ src/research/**/results/**/*.npz
19
+ src/research/**/results/**/*.gz
20
+
21
+ # Cached precomputations (regenerated on first boot if needed).
22
+ src/research/**/*.npz
23
+ src/research/**/*.gz
24
+
25
+ # Local dev artefacts that don't belong in the image.
26
+ docs
27
+ plans
28
+ .env
29
+ .env.*
30
+ !src/frontend/.env.production
31
+
32
+ # OS
33
+ .DS_Store
34
+ Thumbs.db
.env.example ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Local environment for `docker compose up`. Copy to `.env` (which is
2
+ # gitignored) and fill in the values. In production these are configured as
3
+ # Space secrets at https://huggingface.co/spaces/bani57/website/settings.
4
+
5
+ # REQUIRED. Generate with:
6
+ # python -c "import secrets; print(secrets.token_urlsafe(50))"
7
+ DJANGO_SECRET_KEY=replace-me
8
+
9
+ # Optional overrides (defaults shown).
10
+ DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
11
+ CORS_ALLOWED_ORIGINS=http://localhost:7860
12
+ HF_CHECKPOINTS_REPO=bani57/checkpoints
13
+ TORCH_DEVICE=cpu
14
+
15
+ # Only needed if the checkpoint repo is private.
16
+ # HF_TOKEN=hf_...
.gitignore CHANGED
@@ -5,3 +5,14 @@ src/research/COINs-KGGeneration/graph_completion/checkpoints/
5
  src/research/COINs-KGGeneration/graph_generation/checkpoints/
6
  src/research/MultiProxAn/checkpoints/
7
  src/backend/.checkpoint_staging/
 
 
 
 
 
 
 
 
 
 
 
 
5
  src/research/COINs-KGGeneration/graph_generation/checkpoints/
6
  src/research/MultiProxAn/checkpoints/
7
  src/backend/.checkpoint_staging/
8
+
9
+ # Local secrets — keep out of git. Compose / docker auto-loads .env.
10
+ .env
11
+ .env.local
12
+ !.env.example
13
+ !src/frontend/.env.development
14
+ !src/frontend/.env.production
15
+
16
+ # Docker build artefacts that don't belong in source control.
17
+ src/backend/staticfiles/
18
+ src/backend/dist/
Dockerfile ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1.6
2
+
3
+ # ---- Stage 1: build the Vue SPA ----
4
+ FROM node:20-alpine AS frontend
5
+ WORKDIR /app
6
+ COPY src/frontend/package*.json ./
7
+ RUN npm ci
8
+ COPY src/frontend ./
9
+ RUN npm run build
10
+
11
+ # ---- Stage 2: backend runtime, mirrors the local website_c mamba env ----
12
+ FROM mambaorg/micromamba:1.5.10
13
+
14
+ USER root
15
+ RUN apt-get update && apt-get install -y --no-install-recommends \
16
+ build-essential libgl1 libglib2.0-0 git curl ca-certificates \
17
+ && rm -rf /var/lib/apt/lists/*
18
+ USER $MAMBA_USER
19
+
20
+ # Conda half: Python 3.9, rdkit, boost, cairo, …
21
+ COPY --chown=$MAMBA_USER:$MAMBA_USER environment.yml /tmp/environment.yml
22
+ RUN micromamba install -y -n base -f /tmp/environment.yml \
23
+ && micromamba clean --all --yes
24
+ ARG MAMBA_DOCKERFILE_ACTIVATE=1
25
+
26
+ # Pip half: GPU torch (cu118), torch-geometric, Django, DRF, …
27
+ COPY --chown=$MAMBA_USER:$MAMBA_USER src/backend/requirements.txt /tmp/requirements.txt
28
+ RUN pip install --no-cache-dir \
29
+ --extra-index-url https://download.pytorch.org/whl/cu118 \
30
+ -r /tmp/requirements.txt
31
+
32
+ # Application code + research repo + built SPA
33
+ WORKDIR /app
34
+ COPY --chown=$MAMBA_USER:$MAMBA_USER src/backend /app/backend
35
+ COPY --chown=$MAMBA_USER:$MAMBA_USER src/research /app/research
36
+ COPY --chown=$MAMBA_USER:$MAMBA_USER --from=frontend /app/dist /app/backend/dist
37
+ COPY --chown=$MAMBA_USER:$MAMBA_USER entrypoint.sh /entrypoint.sh
38
+
39
+ # Settings need RESEARCH_ROOT and CHECKPOINTS_ROOT to point at the in-image
40
+ # research tree. CHECKPOINTS_ROOT lives on a writable path so snapshot_download
41
+ # can populate it at boot.
42
+ ENV RESEARCH_ROOT=/app/research \
43
+ CHECKPOINTS_ROOT=/app/checkpoints \
44
+ SPA_DIST_DIR=/app/backend/dist \
45
+ DJANGO_SETTINGS_MODULE=research_api.settings \
46
+ PYTHONUNBUFFERED=1 \
47
+ PORT=7860
48
+
49
+ WORKDIR /app/backend
50
+ RUN python manage.py collectstatic --noinput
51
+
52
+ EXPOSE 7860
53
+ ENTRYPOINT ["/usr/local/bin/_entrypoint.sh", "/entrypoint.sh"]
README.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Andrej Janchevski - PhD Research
3
+ emoji: 📚
4
+ colorFrom: green
5
+ colorTo: gray
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: false
9
+ license: mit
10
+ ---
11
+
12
+ # Andrej Janchevski — PhD Research
13
+
14
+ Live demos for the PhD thesis _Scalable Knowledge Graph Reasoning and Scalable Graph Generation_ (EPFL, 2025):
15
+
16
+ - **COINs** — knowledge-graph reasoning (link prediction, multi-hop query answering).
17
+ - **MultiProxAn** — molecular and KG graph generation by diffusion.
18
+ - **KG anomaly correction** — diffusion-based denoising of corrupted knowledge subgraphs.
19
+
20
+ The Space runs a single Django + Vue Docker container. The backend pulls model checkpoints
21
+ from <https://huggingface.co/bani57/checkpoints> on first boot. Source on GitHub.
docker-compose.yml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Local-dev convenience. HF Spaces ignores this file and consumes only the
2
+ # Dockerfile. Run `docker compose up --build` to reproduce the production
3
+ # container locally on http://localhost:7860.
4
+ services:
5
+ website:
6
+ build:
7
+ context: .
8
+ dockerfile: Dockerfile
9
+ image: bani57-website:local
10
+ ports:
11
+ - "7860:7860"
12
+ environment:
13
+ DJANGO_DEBUG: "False"
14
+ # Required. Generate locally with:
15
+ # python -c "import secrets; print(secrets.token_urlsafe(50))"
16
+ # then put it in .env at the repo root (see .env.example).
17
+ DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:?DJANGO_SECRET_KEY must be set in .env or your shell}
18
+ DJANGO_ALLOWED_HOSTS: ${DJANGO_ALLOWED_HOSTS:-localhost,127.0.0.1}
19
+ CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:7860}
20
+ HF_CHECKPOINTS_REPO: ${HF_CHECKPOINTS_REPO:-bani57/checkpoints}
21
+ HF_TOKEN: ${HF_TOKEN:-}
22
+ CHECKPOINTS_ROOT: /app/checkpoints
23
+ TORCH_DEVICE: ${TORCH_DEVICE:-cpu}
24
+ volumes:
25
+ # Persist the downloaded checkpoints between runs so we don't re-pull
26
+ # ~5.4 GB on every `docker compose up`.
27
+ - checkpoints:/app/checkpoints
28
+ volumes:
29
+ checkpoints:
entrypoint.sh ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Entry point for the Hugging Face Space container.
3
+ #
4
+ # Pre-warms the checkpoint download from HF Hub before starting gunicorn so
5
+ # that worker processes never block on the network on first request. The
6
+ # download is idempotent — on warm restarts (with persistent storage) every
7
+ # file is already present and snapshot_download is a no-op.
8
+ set -euo pipefail
9
+
10
+ : "${HF_CHECKPOINTS_REPO:=bani57/checkpoints}"
11
+ : "${CHECKPOINTS_ROOT:=/app/checkpoints}"
12
+ : "${PORT:=7860}"
13
+
14
+ mkdir -p "${CHECKPOINTS_ROOT}"
15
+
16
+ echo "[entrypoint] Pre-warming checkpoints from ${HF_CHECKPOINTS_REPO} -> ${CHECKPOINTS_ROOT}"
17
+ python - <<'PY'
18
+ import os
19
+ from huggingface_hub import snapshot_download
20
+
21
+ snapshot_download(
22
+ repo_id=os.environ["HF_CHECKPOINTS_REPO"],
23
+ repo_type="model",
24
+ local_dir=os.environ["CHECKPOINTS_ROOT"],
25
+ local_dir_use_symlinks=False,
26
+ max_workers=4,
27
+ token=os.environ.get("HF_TOKEN"),
28
+ )
29
+ print("[entrypoint] checkpoints ready")
30
+ PY
31
+
32
+ echo "[entrypoint] starting gunicorn on 0.0.0.0:${PORT}"
33
+ exec gunicorn research_api.wsgi:application \
34
+ --bind "0.0.0.0:${PORT}" \
35
+ --workers 2 \
36
+ --threads 2 \
37
+ --timeout 300 \
38
+ --access-logfile - \
39
+ --error-logfile -
environment.yml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: base
2
+ channels:
3
+ - conda-forge
4
+ dependencies:
5
+ - python=3.9
6
+ - rdkit=2023.03.2
7
+ - boost=1.78
8
+ - cairo
9
+ - pycairo
10
+ - freetype-py
11
+ - rlpycairo
12
+ - libblas=*=*mkl
13
+ - matplotlib=3.7.1
14
+ - fonttools
15
+ - pip