mekosotto Claude Opus 4.7 (1M context) commited on
Commit
7d54187
·
1 Parent(s): 389cf2a

feat(deploy): Hugging Face Spaces Dockerfile + supervisord launcher

Browse files

- Dockerfile.hf: python:3.12-slim base, system deps for RDKit /
nibabel / MNE, pip install requirements.txt, BUILD-TIME train of
the BBB model artifact (RUN python -m src.models.bbb_model) so the
first /predict/bbb call is instant on cold start.
- ENV defaults: DEPLOY_ENV=hf_spaces, NEUROBRIDGE_DISABLE_MLFLOW=1,
NEUROBRIDGE_DISABLE_LLM=1 (jury can opt back into LLM by setting
OPENROUTER_API_KEY in HF Space secrets and unsetting the disable
flag).
- supervisord.conf launches FastAPI on :8000 and Streamlit on :7860
in the same container; Streamlit exposes the HF public URL.
- .dockerignore trims build context (data/processed, mlruns, .venv,
tests/ except fixtures, docs).
- 2 new smoke tests: Dockerfile exists and contains expected stages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

.dockerignore CHANGED
@@ -1,12 +1,16 @@
1
  .venv/
2
- .venv312/
3
  __pycache__/
4
  *.pyc
5
  .pytest_cache/
6
  .mypy_cache/
7
- data/raw/*
8
- data/processed/*
9
  mlruns/
10
  .git/
 
11
  docs/
12
  tests/
 
 
 
 
1
  .venv/
2
+ .venv*/
3
  __pycache__/
4
  *.pyc
5
  .pytest_cache/
6
  .mypy_cache/
7
+ .ruff_cache/
8
+ data/processed/
9
  mlruns/
10
  .git/
11
+ .github/
12
  docs/
13
  tests/
14
+ !tests/fixtures/
15
+ .streamlit/
16
+ notebooks/
Dockerfile.hf ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # NeuroBridge Enterprise — Hugging Face Spaces deployment image
2
+ # Single container running FastAPI (port 8000) + Streamlit (port 7860).
3
+ # HF Spaces routes :7860 to the public URL automatically.
4
+
5
+ FROM python:3.12-slim AS base
6
+
7
+ ENV PYTHONDONTWRITEBYTECODE=1 \
8
+ PYTHONUNBUFFERED=1 \
9
+ PIP_DISABLE_PIP_VERSION_CHECK=1 \
10
+ PIP_NO_CACHE_DIR=1 \
11
+ DEPLOY_ENV=hf_spaces \
12
+ NEUROBRIDGE_DISABLE_MLFLOW=1 \
13
+ NEUROBRIDGE_DISABLE_LLM=1
14
+
15
+ # --- system deps for RDKit, nibabel, MNE ---
16
+ RUN apt-get update && apt-get install -y --no-install-recommends \
17
+ build-essential \
18
+ libgomp1 \
19
+ libxrender1 \
20
+ libsm6 \
21
+ libxext6 \
22
+ supervisor \
23
+ && rm -rf /var/lib/apt/lists/*
24
+
25
+ WORKDIR /app
26
+
27
+ # --- Python deps ---
28
+ COPY requirements.txt ./
29
+ RUN pip install -r requirements.txt
30
+
31
+ # --- project source ---
32
+ COPY src/ ./src/
33
+ COPY tests/fixtures/ ./tests/fixtures/
34
+ COPY data/raw/ ./data/raw/
35
+ COPY supervisord.conf ./supervisord.conf
36
+
37
+ # --- build BBB model artifact at image-build time ---
38
+ # This makes the first /predict/bbb call instant on cold start.
39
+ RUN python -m src.models.bbb_model
40
+
41
+ # --- HF Spaces convention ---
42
+ EXPOSE 7860
43
+
44
+ # --- launch FastAPI + Streamlit under supervisord ---
45
+ CMD ["supervisord", "-n", "-c", "/app/supervisord.conf"]
supervisord.conf ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [supervisord]
2
+ nodaemon=true
3
+ user=root
4
+ logfile=/dev/stdout
5
+ logfile_maxbytes=0
6
+ pidfile=/tmp/supervisord.pid
7
+
8
+ [program:fastapi]
9
+ command=uvicorn src.api.main:app --host 0.0.0.0 --port 8000
10
+ autostart=true
11
+ autorestart=true
12
+ stdout_logfile=/dev/stdout
13
+ stdout_logfile_maxbytes=0
14
+ stderr_logfile=/dev/stderr
15
+ stderr_logfile_maxbytes=0
16
+
17
+ [program:streamlit]
18
+ command=streamlit run src/frontend/app.py --server.port 7860 --server.address 0.0.0.0 --server.headless true --server.enableCORS false
19
+ environment=NEUROBRIDGE_API_URL="http://127.0.0.1:8000"
20
+ autostart=true
21
+ autorestart=true
22
+ stdout_logfile=/dev/stdout
23
+ stdout_logfile_maxbytes=0
24
+ stderr_logfile=/dev/stderr
25
+ stderr_logfile_maxbytes=0
tests/deploy/__init__.py ADDED
File without changes
tests/deploy/test_dockerfile_hf.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Smoke test: Dockerfile.hf is well-formed and contains expected stages.
2
+
3
+ We don't actually build the image (too slow for unit tests). We just verify
4
+ the file exists, is non-empty, and has the load-bearing instructions.
5
+ """
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+
11
+ REPO_ROOT = Path(__file__).resolve().parents[2]
12
+ DOCKERFILE = REPO_ROOT / "Dockerfile.hf"
13
+
14
+
15
+ @pytest.fixture(scope="module")
16
+ def dockerfile_text() -> str:
17
+ if not DOCKERFILE.exists():
18
+ pytest.skip(f"{DOCKERFILE} does not exist yet (Day-8 T3 RED phase)")
19
+ return DOCKERFILE.read_text()
20
+
21
+
22
+ class TestDockerfileHF:
23
+ """Day-8 T3: Hugging Face Spaces Dockerfile smoke."""
24
+
25
+ def test_dockerfile_exists_and_nonempty(self):
26
+ assert DOCKERFILE.exists(), f"missing {DOCKERFILE}"
27
+ assert DOCKERFILE.stat().st_size > 0, f"{DOCKERFILE} is empty"
28
+
29
+ def test_dockerfile_contains_required_stages(self, dockerfile_text):
30
+ """The HF Dockerfile must:
31
+ - Start FROM a Python base
32
+ - Install requirements.txt
33
+ - Build the BBB model artifact at build time
34
+ - Set NEUROBRIDGE_DISABLE_MLFLOW=1 by default
35
+ - Expose port 7860 (HF Spaces convention)
36
+ - Launch via supervisord
37
+ """
38
+ text = dockerfile_text.lower()
39
+ assert "from python" in text, "must FROM a Python base image"
40
+ assert "requirements.txt" in text, "must reference requirements.txt"
41
+ assert "src.models.bbb_model" in dockerfile_text, (
42
+ "must build the BBB model artifact at image-build time"
43
+ )
44
+ assert "neurobridge_disable_mlflow" in text, (
45
+ "must set NEUROBRIDGE_DISABLE_MLFLOW for HF deploy"
46
+ )
47
+ assert "7860" in text, "must expose port 7860 (HF Spaces convention)"
48
+ assert "supervisord" in text, (
49
+ "must launch FastAPI + Streamlit via supervisord"
50
+ )