Merge pull request #5 from r0m4k/feat/docker-space
Browse files- .dockerignore +14 -0
- DEPLOY.md +50 -0
- Dockerfile +46 -0
- start.sh +24 -0
.dockerignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Keep the build context small; the model is downloaded inside the image, not copied in.
|
| 2 |
+
.git/
|
| 3 |
+
.venv/
|
| 4 |
+
venv/
|
| 5 |
+
models/
|
| 6 |
+
__pycache__/
|
| 7 |
+
**/__pycache__/
|
| 8 |
+
*.pyc
|
| 9 |
+
.pytest_cache/
|
| 10 |
+
.gradio/
|
| 11 |
+
flagged/
|
| 12 |
+
train/data/
|
| 13 |
+
eval/data/synth*
|
| 14 |
+
.DS_Store
|
DEPLOY.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deploying the offline Space (Docker)
|
| 2 |
+
|
| 3 |
+
The submission Space runs the model **on-device** via a Docker image: it builds current
|
| 4 |
+
llama.cpp, **bakes the MiniCPM-V GGUF into the image at build time**, and launches
|
| 5 |
+
`llama-server` + the Gradio app. No external API at runtime β **off-grid**.
|
| 6 |
+
|
| 7 |
+
## 1. Make the Space a Docker Space (under the org)
|
| 8 |
+
The Space `build-small-hackathon/blood-test-explainer` must use **`sdk: docker`**. Set its
|
| 9 |
+
`README.md` frontmatter to exactly:
|
| 10 |
+
|
| 11 |
+
```yaml
|
| 12 |
+
---
|
| 13 |
+
title: Blood Test Explainer
|
| 14 |
+
emoji: π
|
| 15 |
+
colorFrom: green
|
| 16 |
+
colorTo: green
|
| 17 |
+
sdk: docker
|
| 18 |
+
app_port: 7860
|
| 19 |
+
pinned: false
|
| 20 |
+
---
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## 2. Get these files into the Space repo
|
| 24 |
+
The Space needs `Dockerfile`, `start.sh`, `.dockerignore`, `app.py`, and `src/` (plus the
|
| 25 |
+
sample reports under `eval/data/real/`). If your Space mirrors this GitHub repo, push this
|
| 26 |
+
branch's contents. If the Space is a separate HF git repo, copy these files into it and push.
|
| 27 |
+
|
| 28 |
+
## 3. HF builds it
|
| 29 |
+
First build takes ~10β15 min (compiles llama.cpp + downloads the ~1.6 GB model). When it's
|
| 30 |
+
up, upload a report β it extracts fully offline.
|
| 31 |
+
|
| 32 |
+
## 4. Ship the fine-tuned model (later)
|
| 33 |
+
Once you've fine-tuned and converted to GGUF, upload it to an HF model repo, then rebuild the
|
| 34 |
+
Space with the build-args pointing at it:
|
| 35 |
+
|
| 36 |
+
```
|
| 37 |
+
MODEL_REPO=<you>/minicpmv-lab-gguf MODEL_FILE=<your-model>.Q4_K_M.gguf MMPROJ_FILE=mmproj-model-f16.gguf
|
| 38 |
+
```
|
| 39 |
+
(In a Space, set these as build-time variables, or edit the `ARG` defaults in the Dockerfile.)
|
| 40 |
+
|
| 41 |
+
## 5. Verify off-grid (badge)
|
| 42 |
+
- Model is baked into the image at build time; `HF_HUB_OFFLINE=1` + `TRANSFORMERS_OFFLINE=1`.
|
| 43 |
+
- The only network at runtime is `127.0.0.1:8080` (llama-server in the same container).
|
| 44 |
+
- Grep check: nothing on the request path hits an external host β the API backend (the only
|
| 45 |
+
outbound HTTP) is disabled when `EXTRACTOR_BACKEND=local`.
|
| 46 |
+
|
| 47 |
+
## Hardware
|
| 48 |
+
Free **CPU** Space works (first inference is slower than your M3, ~30β60s). Upgrade the Space
|
| 49 |
+
hardware if you want it snappier for live demos; the demo **video** should be recorded locally
|
| 50 |
+
where it's fast.
|
Dockerfile
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Offline Blood Test Explainer β MiniCPM-V 4.6 served on-device by llama.cpp, no external APIs.
|
| 2 |
+
# HF Docker Space. The model is baked into the image at build time, so there is zero network
|
| 3 |
+
# at runtime (off-grid badge). Swap MODEL_REPO to your fine-tuned GGUF repo when it's ready.
|
| 4 |
+
FROM python:3.11-slim
|
| 5 |
+
|
| 6 |
+
ENV DEBIAN_FRONTEND=noninteractive
|
| 7 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 8 |
+
git cmake build-essential curl ca-certificates libgomp1 \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# 1) Build current llama.cpp (its server supports MiniCPM-V 4.6). GGML_NATIVE=OFF for portability.
|
| 12 |
+
WORKDIR /opt
|
| 13 |
+
RUN git clone --depth 1 https://github.com/ggml-org/llama.cpp.git && \
|
| 14 |
+
cmake -S llama.cpp -B llama.cpp/build \
|
| 15 |
+
-DGGML_NATIVE=OFF -DLLAMA_CURL=OFF -DBUILD_SHARED_LIBS=OFF -DLLAMA_BUILD_TESTS=OFF && \
|
| 16 |
+
cmake --build llama.cpp/build --config Release -j --target llama-server && \
|
| 17 |
+
cp llama.cpp/build/bin/llama-server /usr/local/bin/llama-server && \
|
| 18 |
+
rm -rf /opt/llama.cpp
|
| 19 |
+
|
| 20 |
+
# 2) Bake the model + vision projector into the image (no runtime download).
|
| 21 |
+
# Override these build-args to ship your fine-tuned model instead of the base.
|
| 22 |
+
ARG MODEL_REPO=openbmb/MiniCPM-V-4.6-gguf
|
| 23 |
+
ARG MODEL_FILE=MiniCPM-V-4_6-Q4_K_M.gguf
|
| 24 |
+
ARG MMPROJ_FILE=mmproj-model-f16.gguf
|
| 25 |
+
RUN pip install --no-cache-dir "huggingface_hub[cli]" && \
|
| 26 |
+
mkdir -p /models && \
|
| 27 |
+
hf download "$MODEL_REPO" "$MODEL_FILE" "$MMPROJ_FILE" --local-dir /models
|
| 28 |
+
|
| 29 |
+
# 3) App + runtime deps (no llama-cpp-python: we use the llama-server backend).
|
| 30 |
+
WORKDIR /app
|
| 31 |
+
RUN pip install --no-cache-dir \
|
| 32 |
+
gradio==6.17.3 requests==2.32.5 pillow==12.0.0 pymupdf==1.26.6 json-repair==0.60.1
|
| 33 |
+
COPY . /app
|
| 34 |
+
|
| 35 |
+
ENV EXTRACTOR_BACKEND=local \
|
| 36 |
+
LLAMA_SERVER_URL=http://127.0.0.1:8080/v1/chat/completions \
|
| 37 |
+
LLAMA_SERVER_MODEL=minicpm-v \
|
| 38 |
+
LOCAL_MODEL_PATH=/models/MiniCPM-V-4_6-Q4_K_M.gguf \
|
| 39 |
+
LOCAL_MMPROJ_PATH=/models/mmproj-model-f16.gguf \
|
| 40 |
+
GRADIO_SERVER_NAME=0.0.0.0 \
|
| 41 |
+
GRADIO_SERVER_PORT=7860 \
|
| 42 |
+
HF_HUB_OFFLINE=1 \
|
| 43 |
+
TRANSFORMERS_OFFLINE=1
|
| 44 |
+
|
| 45 |
+
EXPOSE 7860
|
| 46 |
+
CMD ["bash", "start.sh"]
|
start.sh
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Launch the on-device model server, wait for it, then start the Gradio UI.
|
| 3 |
+
# Everything stays on localhost β no external network β so this is fully off-grid.
|
| 4 |
+
set -euo pipefail
|
| 5 |
+
|
| 6 |
+
echo "Starting llama-server (on-device MiniCPM-V)..."
|
| 7 |
+
llama-server \
|
| 8 |
+
-m "${LOCAL_MODEL_PATH}" \
|
| 9 |
+
--mmproj "${LOCAL_MMPROJ_PATH}" \
|
| 10 |
+
--host 127.0.0.1 --port 8080 \
|
| 11 |
+
--ctx-size 4096 &
|
| 12 |
+
|
| 13 |
+
# Wait until the model is loaded and the server answers /health (model load can take a while).
|
| 14 |
+
echo "Waiting for llama-server to be ready..."
|
| 15 |
+
for _ in $(seq 1 240); do
|
| 16 |
+
if curl -sf http://127.0.0.1:8080/health >/dev/null 2>&1; then
|
| 17 |
+
echo "llama-server is ready."
|
| 18 |
+
break
|
| 19 |
+
fi
|
| 20 |
+
sleep 2
|
| 21 |
+
done
|
| 22 |
+
|
| 23 |
+
echo "Starting Gradio app on port ${GRADIO_SERVER_PORT:-7860}..."
|
| 24 |
+
exec python app.py
|