r0mant1c Codex commited on
Commit
87e13d2
·
1 Parent(s): 163f769

Route ZeroGPU auto backend to Transformers

Browse files

Co-authored-by: Codex <chatgpt-codex-connector[bot]@users.noreply.github.com>

DEPLOY.md CHANGED
@@ -5,12 +5,12 @@ The active Hugging Face deployment is a **Gradio ZeroGPU Space**.
5
  This workflow is intentionally fixed:
6
 
7
  1. The Space must stay a Gradio Space, not a Docker Space.
8
- 2. Runtime extraction should use the `llamacpp-gpu` backend when we are targeting the Llama Champion badge.
9
  3. The extraction call must run behind `@spaces.GPU` so Hugging Face allocates ZeroGPU only while the model is needed.
10
  4. Model files must not be committed to the Space git repo.
11
- 5. When the fine-tuned GGUF model is ready, only replace the `LLAMACPP_*` model variables.
12
 
13
- Do not change this architecture unless the project intentionally gives up ZeroGPU or the llama.cpp backend proves incompatible with ZeroGPU. The intended future model-serving change is inserting the fine-tuned GGUF repository path into the existing `LLAMACPP_*` variables.
14
 
15
  ## 1. Space Metadata
16
 
@@ -34,53 +34,51 @@ ZeroGPU is Gradio-only on Hugging Face. It is not available for Docker Spaces, w
34
 
35
  ## 2. Model Serving
36
 
37
- The badge-target model path is the official OpenBMB GGUF repo running through llama.cpp:
38
 
39
  ```text
40
- LLAMACPP_GGUF_REPO=openbmb/MiniCPM-V-4.6-gguf
41
- LLAMACPP_MODEL_FILE=MiniCPM-V-4_6-Q4_K_M.gguf
42
- LLAMACPP_MMPROJ_FILE=mmproj-model-f16.gguf
43
  EXTRACTOR_BACKEND=auto
 
44
  ```
45
 
46
  The backend lives in:
47
 
48
  ```text
49
- src/extraction/llamacpp_gpu.py
50
  ```
51
 
52
  It uses:
53
 
54
  ```python
55
- llama_cpp.Llama(...)
56
  @spaces.GPU(duration=120)
 
57
  ```
58
 
59
- This is a valid hackathon badge option because the submitted app remains a Gradio ZeroGPU Space,
60
- but the model runtime is `llama.cpp` over GGUF rather than a hosted inference API.
 
 
61
 
62
- The safe fallback backend is the official OpenBMB Transformers model:
63
 
64
  ```text
65
- EXTRACTOR_BACKEND=zerogpu
66
- ZEROGPU_MODEL_ID=openbmb/MiniCPM-V-4.6
67
  ```
68
 
69
- Use the fallback only if `llama-cpp-python` cannot load MiniCPM-V 4.6 on ZeroGPU.
70
-
71
  ## 3. Future Fine-Tuned Model
72
 
73
  When the fine-tuned model is ready:
74
 
75
- 1. Convert/quantize the fine-tuned model to GGUF.
76
- 2. Upload the fine-tuned GGUF model and compatible mmproj file to a Hugging Face model repo.
77
- 3. Keep the same Gradio + ZeroGPU + llama.cpp architecture.
78
  4. Change only these variables:
79
 
80
  ```bash
 
81
  LLAMACPP_GGUF_REPO=<owner>/<fine-tuned-minicpm-v-gguf-repo>
82
  LLAMACPP_MODEL_FILE=<fine-tuned-model>.gguf
83
- LLAMACPP_MMPROJ_FILE=<compatible-mmproj>.gguf
84
  ```
85
 
86
  Do not add model files to the Space git repo. Do not reintroduce Docker or `llama-server` for the ZeroGPU deployment.
@@ -93,8 +91,9 @@ This architecture keeps:
93
 
94
  - Free ZeroGPU eligibility.
95
  - No external hosted inference API calls.
96
- - A valid `llama.cpp` / GGUF runtime path for the Llama Champion badge, if `llama-cpp-python` is compatible.
97
- - A clean future swap to a fine-tuned GGUF model by changing only `LLAMACPP_*` variables.
 
98
 
99
  ## 5. Local Development
100
 
 
5
  This workflow is intentionally fixed:
6
 
7
  1. The Space must stay a Gradio Space, not a Docker Space.
8
+ 2. Runtime extraction should use the Transformers backend on ZeroGPU.
9
  3. The extraction call must run behind `@spaces.GPU` so Hugging Face allocates ZeroGPU only while the model is needed.
10
  4. Model files must not be committed to the Space git repo.
11
+ 5. When the fine-tuned model is ready, replace only the model variables for the active lanes.
12
 
13
+ Do not change this architecture unless the project intentionally gives up ZeroGPU. The intended future model-serving change is inserting the fine-tuned Transformers repository into `ZEROGPU_MODEL_ID`, and optionally inserting the fine-tuned GGUF repository into `LLAMACPP_*` for CPU fallback.
14
 
15
  ## 1. Space Metadata
16
 
 
34
 
35
  ## 2. Model Serving
36
 
37
+ The active ZeroGPU model path is the official OpenBMB Transformers repo:
38
 
39
  ```text
 
 
 
40
  EXTRACTOR_BACKEND=auto
41
+ ZEROGPU_MODEL_ID=openbmb/MiniCPM-V-4.6
42
  ```
43
 
44
  The backend lives in:
45
 
46
  ```text
47
+ src/extraction/zerogpu_transformers.py
48
  ```
49
 
50
  It uses:
51
 
52
  ```python
 
53
  @spaces.GPU(duration=120)
54
+ transformers.AutoModelForImageTextToText
55
  ```
56
 
57
+ This is the correct runtime for a ZeroGPU Space because the GPU is allocated only inside the
58
+ decorated worker. A normal app-level `torch.cuda.is_available()` check may be false before the
59
+ worker starts, so `auto` also checks Hugging Face's `ACCELERATOR` runtime variable for values such
60
+ as `zero-a10g`.
61
 
62
+ The CPU fallback model path is the official OpenBMB GGUF repo running through llama.cpp:
63
 
64
  ```text
65
+ LLAMACPP_GGUF_REPO=openbmb/MiniCPM-V-4.6-gguf
66
+ LLAMACPP_MODEL_FILE=MiniCPM-V-4_6-Q4_K_M.gguf
67
  ```
68
 
 
 
69
  ## 3. Future Fine-Tuned Model
70
 
71
  When the fine-tuned model is ready:
72
 
73
+ 1. Upload the fine-tuned Transformers checkpoint to a Hugging Face model repo.
74
+ 2. Optionally convert/quantize the fine-tuned model to GGUF for CPU fallback.
75
+ 3. Keep the same Gradio + ZeroGPU/CUDA Transformers + CPU llama.cpp architecture.
76
  4. Change only these variables:
77
 
78
  ```bash
79
+ ZEROGPU_MODEL_ID=<owner>/<fine-tuned-minicpm-v-transformers-repo>
80
  LLAMACPP_GGUF_REPO=<owner>/<fine-tuned-minicpm-v-gguf-repo>
81
  LLAMACPP_MODEL_FILE=<fine-tuned-model>.gguf
 
82
  ```
83
 
84
  Do not add model files to the Space git repo. Do not reintroduce Docker or `llama-server` for the ZeroGPU deployment.
 
91
 
92
  - Free ZeroGPU eligibility.
93
  - No external hosted inference API calls.
94
+ - The official OpenBMB Transformers runtime on ZeroGPU.
95
+ - A CPU `llama.cpp` / GGUF fallback when the Space is not on ZeroGPU or CUDA.
96
+ - A clean future swap to a fine-tuned model by changing only `ZEROGPU_MODEL_ID` and optional `LLAMACPP_*` variables.
97
 
98
  ## 5. Local Development
99
 
DEPLOYMENT_LOG.md CHANGED
@@ -1,5 +1,31 @@
1
  # Deployment Log
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ## 2026-06-10 — Switch from Docker Space to Gradio ZeroGPU
4
 
5
  Decision: use **Gradio ZeroGPU** as the active Hugging Face Space architecture.
 
1
  # Deployment Log
2
 
3
+ ## 2026-06-13 — Route ZeroGPU to Transformers, CPU to llama.cpp
4
+
5
+ Decision: keep `EXTRACTOR_BACKEND=auto`, but make ZeroGPU select the official OpenBMB
6
+ Transformers backend instead of relying on app-level CUDA visibility.
7
+
8
+ Why:
9
+ - On ZeroGPU, CUDA is allocated only inside a `@spaces.GPU` worker, so
10
+ `torch.cuda.is_available()` can be false in normal Gradio app code.
11
+ - The app was therefore selecting the CPU llama.cpp fallback even while the Space hardware was
12
+ configured as ZeroGPU.
13
+ - The intended runtime behavior is now explicit: `ACCELERATOR=zero-a10g`, `ZERO_GPU=TRUE`, or
14
+ visible CUDA selects Transformers; CPU-only runtime selects llama.cpp.
15
+
16
+ Space variables:
17
+
18
+ ```bash
19
+ EXTRACTOR_BACKEND=auto
20
+ ZEROGPU_MODEL_ID=openbmb/MiniCPM-V-4.6
21
+ ```
22
+
23
+ CPU fallback after a Transformers failure is now opt-in with:
24
+
25
+ ```bash
26
+ AUTO_FALLBACK_TO_LLAMACPP=1
27
+ ```
28
+
29
  ## 2026-06-10 — Switch from Docker Space to Gradio ZeroGPU
30
 
31
  Decision: use **Gradio ZeroGPU** as the active Hugging Face Space architecture.
README.md CHANGED
@@ -49,10 +49,11 @@ The knowledge graph is educational context, not diagnosis. The lab-provided refe
49
  The Hugging Face Space is intentionally deployed as a **Gradio Space** with adaptive extraction.
50
  This is the active deployment path.
51
 
52
- With `EXTRACTOR_BACKEND=auto`, the app checks CUDA availability at runtime. If CUDA is visible, it
53
- uses the official OpenBMB MiniCPM-V 4.6 Transformers path. If CUDA is not visible, it falls back to
54
- the CPU `llama.cpp` GGUF path. If the CUDA worker fails, `auto` retries with CPU llama.cpp unless
55
- `AUTO_FALLBACK_TO_LLAMACPP=0`. The deterministic knowledge-graph enrichment and UI rendering stay
 
56
  in normal Gradio/Python code.
57
 
58
  This workflow should not be further changed back to Docker unless the project intentionally gives up
 
49
  The Hugging Face Space is intentionally deployed as a **Gradio Space** with adaptive extraction.
50
  This is the active deployment path.
51
 
52
+ With `EXTRACTOR_BACKEND=auto`, the app uses the official OpenBMB MiniCPM-V 4.6 Transformers path
53
+ when the runtime is ZeroGPU or CUDA. On CPU-only hardware, it uses the CPU `llama.cpp` GGUF path.
54
+ ZeroGPU is detected from Hugging Face's `ACCELERATOR` runtime variable, for example
55
+ `ACCELERATOR=zero-a10g`, or from explicit runtime flags such as `ZERO_GPU=TRUE`. This matters
56
+ because CUDA is only visible inside the `@spaces.GPU` worker. The deterministic knowledge-graph enrichment and UI rendering stay
57
  in normal Gradio/Python code.
58
 
59
  This workflow should not be further changed back to Docker unless the project intentionally gives up
RUNBOOK.md CHANGED
@@ -10,7 +10,7 @@ This replaced the Docker + `llama-server` path because ZeroGPU is only available
10
  |---|---|
11
  | Space SDK | `gradio` |
12
  | Hardware | Adaptive: CUDA when available, CPU otherwise |
13
- | Auto backend | Transformers on CUDA, llama.cpp on CPU |
14
  | Force llama.cpp | `EXTRACTOR_BACKEND=llamacpp-gpu` |
15
  | Force Transformers | `EXTRACTOR_BACKEND=zerogpu` or `EXTRACTOR_BACKEND=transformers` |
16
  | llama.cpp variables | `LLAMACPP_GGUF_REPO`, `LLAMACPP_MODEL_FILE` |
@@ -24,7 +24,7 @@ Do not switch the Space back to Docker unless the project intentionally gives up
24
 
25
  `EXTRACTOR_BACKEND`:
26
 
27
- - `auto`: uses the Transformers backend when `torch.cuda.is_available()` is true; otherwise uses the CPU llama.cpp backend. If the Transformers worker fails, it retries with CPU llama.cpp unless `AUTO_FALLBACK_TO_LLAMACPP=0`.
28
  - `llamacpp-gpu`: force the GGUF llama.cpp backend.
29
  - `zerogpu` / `transformers`: force the OpenBMB Transformers backend.
30
  - `api`: hosted OpenBMB endpoint for development fallback only.
@@ -57,8 +57,8 @@ transformers
57
  llama-cpp-python
58
  ```
59
 
60
- The Space installs both runtime lanes so `EXTRACTOR_BACKEND=auto` can choose at runtime. CUDA
61
- hardware uses the official OpenBMB Transformers path. CPU hardware uses the prebuilt
62
  `llama-cpp-python` wheel and avoids a source build.
63
 
64
  The active llama.cpp path now uses the official prebuilt CPU manylinux wheel for `llama-cpp-python`:
@@ -131,4 +131,4 @@ python3 -m py_compile app.py src/*.py src/extraction/*.py
131
  .venv/bin/python -m pytest tests/test_report_pipeline.py
132
  ```
133
 
134
- Then verify the Space build uses Gradio, not Docker, and that CUDA hardware selects Transformers while CPU hardware selects llama.cpp.
 
10
  |---|---|
11
  | Space SDK | `gradio` |
12
  | Hardware | Adaptive: CUDA when available, CPU otherwise |
13
+ | Auto backend | Transformers on ZeroGPU/CUDA, llama.cpp on CPU |
14
  | Force llama.cpp | `EXTRACTOR_BACKEND=llamacpp-gpu` |
15
  | Force Transformers | `EXTRACTOR_BACKEND=zerogpu` or `EXTRACTOR_BACKEND=transformers` |
16
  | llama.cpp variables | `LLAMACPP_GGUF_REPO`, `LLAMACPP_MODEL_FILE` |
 
24
 
25
  `EXTRACTOR_BACKEND`:
26
 
27
+ - `auto`: uses the Transformers backend when Hugging Face reports a ZeroGPU accelerator such as `ACCELERATOR=zero-a10g`, when `ZERO_GPU=TRUE` is set, or when CUDA is visible; otherwise uses the CPU llama.cpp backend. This runtime signal matters because CUDA is only visible inside a `@spaces.GPU` worker. CPU fallback after a Transformers failure is opt-in with `AUTO_FALLBACK_TO_LLAMACPP=1`.
28
  - `llamacpp-gpu`: force the GGUF llama.cpp backend.
29
  - `zerogpu` / `transformers`: force the OpenBMB Transformers backend.
30
  - `api`: hosted OpenBMB endpoint for development fallback only.
 
57
  llama-cpp-python
58
  ```
59
 
60
+ The Space installs both runtime lanes so `EXTRACTOR_BACKEND=auto` can choose at runtime. ZeroGPU or
61
+ CUDA hardware uses the official OpenBMB Transformers path. CPU hardware uses the prebuilt
62
  `llama-cpp-python` wheel and avoids a source build.
63
 
64
  The active llama.cpp path now uses the official prebuilt CPU manylinux wheel for `llama-cpp-python`:
 
131
  .venv/bin/python -m pytest tests/test_report_pipeline.py
132
  ```
133
 
134
+ Then verify the Space build uses Gradio, not Docker, and that ZeroGPU/CUDA hardware selects Transformers while CPU hardware selects llama.cpp.
requirements.txt CHANGED
@@ -4,15 +4,14 @@ requests==2.32.5
4
  pillow==12.0.0
5
  pymupdf==1.26.6
6
  json-repair==0.60.1
7
- # Optional CUDA path for EXTRACTOR_BACKEND=auto: if torch sees CUDA, the app uses the official
8
- # OpenBMB Transformers pipeline; otherwise it stays on the CPU llama.cpp path below.
9
  torch==2.9.1 ; sys_platform == "linux" and platform_machine == "x86_64"
10
  transformers==4.57.3
11
  accelerate==1.12.0
12
  bitsandbytes==0.48.2 ; sys_platform == "linux" and platform_machine == "x86_64"
13
  torchvision==0.24.1 ; sys_platform == "linux" and platform_machine == "x86_64"
14
  av==16.0.1 ; sys_platform == "linux" and platform_machine == "x86_64"
15
- # Active HF Space path: run the official OpenBMB GGUF through llama.cpp on ZeroGPU.
16
- # Install the prebuilt CPU manylinux wheel directly to avoid a source build on Spaces.
17
  llama-cpp-python @ https://github.com/abetlen/llama-cpp-python/releases/download/v0.3.28/llama_cpp_python-0.3.28-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl ; sys_platform == "linux" and platform_machine == "x86_64"
18
  llama-cpp-python==0.3.28 ; sys_platform != "linux"
 
4
  pillow==12.0.0
5
  pymupdf==1.26.6
6
  json-repair==0.60.1
7
+ # ZeroGPU/CUDA path for EXTRACTOR_BACKEND=auto: the app uses the official OpenBMB
8
+ # Transformers pipeline when ACCELERATOR is ZeroGPU, ZERO_GPU=TRUE, or CUDA is visible.
9
  torch==2.9.1 ; sys_platform == "linux" and platform_machine == "x86_64"
10
  transformers==4.57.3
11
  accelerate==1.12.0
12
  bitsandbytes==0.48.2 ; sys_platform == "linux" and platform_machine == "x86_64"
13
  torchvision==0.24.1 ; sys_platform == "linux" and platform_machine == "x86_64"
14
  av==16.0.1 ; sys_platform == "linux" and platform_machine == "x86_64"
15
+ # CPU fallback path: install the prebuilt manylinux wheel directly to avoid a source build on Spaces.
 
16
  llama-cpp-python @ https://github.com/abetlen/llama-cpp-python/releases/download/v0.3.28/llama_cpp_python-0.3.28-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl ; sys_platform == "linux" and platform_machine == "x86_64"
17
  llama-cpp-python==0.3.28 ; sys_platform != "linux"
src/extraction/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
  """Extraction backends behind one interface.
2
 
3
  `build_extractor()` returns the right backend for the environment:
4
- - **auto**: Transformers when CUDA is visible; CPU llama.cpp otherwise.
5
  - **zerogpu** / **transformers**: force official OpenBMB MiniCPM-V through Transformers.
6
  - **llamacpp-gpu** / **llama-champion**: force GGUF through llama.cpp.
7
  - **local**: local llama-server / llama.cpp backends for local experimentation.
 
1
  """Extraction backends behind one interface.
2
 
3
  `build_extractor()` returns the right backend for the environment:
4
+ - **auto**: Transformers on ZeroGPU/CUDA; CPU llama.cpp otherwise.
5
  - **zerogpu** / **transformers**: force official OpenBMB MiniCPM-V through Transformers.
6
  - **llamacpp-gpu** / **llama-champion**: force GGUF through llama.cpp.
7
  - **local**: local llama-server / llama.cpp backends for local experimentation.
src/extraction/auto.py CHANGED
@@ -10,7 +10,7 @@ from src.extraction.zerogpu_transformers import ZeroGPUTransformersExtractor
10
 
11
 
12
  class AutoExtractor:
13
- """Use Transformers when CUDA is visible, otherwise fall back to CPU llama.cpp."""
14
 
15
  def __init__(self, model_id: str | None = None) -> None:
16
  self.model_id = model_id
@@ -34,24 +34,56 @@ class AutoExtractor:
34
 
35
  def _backend(self) -> Extractor:
36
  if self._selected is None:
37
- if cuda_available():
 
 
38
  self._selected = ZeroGPUTransformersExtractor(model_id=self.model_id)
39
  else:
40
  self._selected = LlamaCppGPUExtractor()
41
  return self._selected
42
 
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  def cuda_available() -> bool:
45
  try:
46
  import torch
47
  except Exception:
48
  return False
49
 
50
-
51
- def _fallback_enabled() -> bool:
52
- return os.getenv("AUTO_FALLBACK_TO_LLAMACPP", "1").strip().lower() not in {"0", "false", "no"}
53
-
54
  try:
55
  return bool(torch.cuda.is_available())
56
  except Exception:
57
  return False
 
 
 
 
 
10
 
11
 
12
  class AutoExtractor:
13
+ """Use Transformers on ZeroGPU/CUDA, otherwise use CPU llama.cpp."""
14
 
15
  def __init__(self, model_id: str | None = None) -> None:
16
  self.model_id = model_id
 
34
 
35
  def _backend(self) -> Extractor:
36
  if self._selected is None:
37
+ target = runtime_target()
38
+ print(f"[Blood Test Explainer] auto extractor selected {target}", flush=True)
39
+ if target == "transformers":
40
  self._selected = ZeroGPUTransformersExtractor(model_id=self.model_id)
41
  else:
42
  self._selected = LlamaCppGPUExtractor()
43
  return self._selected
44
 
45
 
46
+ def runtime_target() -> str:
47
+ """Return `transformers` for ZeroGPU/CUDA and `llamacpp` for CPU-only runtime."""
48
+ if zerogpu_runtime_requested() or cuda_available():
49
+ return "transformers"
50
+ return "llamacpp"
51
+
52
+
53
+ def zerogpu_runtime_requested() -> bool:
54
+ """Detect HF ZeroGPU from explicit Space/runtime environment flags.
55
+
56
+ ZeroGPU exposes CUDA only inside a `@spaces.GPU` worker, so checking
57
+ `torch.cuda.is_available()` in normal Gradio app code is not enough.
58
+ """
59
+ boolean_flags = ("ZERO_GPU", "SPACES_ZERO_GPU", "HF_ZERO_GPU", "BTE_ZERO_GPU")
60
+ for name in boolean_flags:
61
+ value = os.getenv(name, "").strip().lower()
62
+ if value in {"1", "true", "yes", "on", "zerogpu", "zero-gpu"}:
63
+ return True
64
+
65
+ hardware_flags = ("ACCELERATOR", "BTE_RUNTIME", "BTE_HARDWARE", "SPACE_HARDWARE", "HF_SPACE_HARDWARE")
66
+ for name in hardware_flags:
67
+ value = os.getenv(name, "").strip().lower()
68
+ if "zero" in value and "gpu" in value:
69
+ return True
70
+ if value.startswith("zero-"):
71
+ return True
72
+
73
+ return False
74
+
75
+
76
  def cuda_available() -> bool:
77
  try:
78
  import torch
79
  except Exception:
80
  return False
81
 
 
 
 
 
82
  try:
83
  return bool(torch.cuda.is_available())
84
  except Exception:
85
  return False
86
+
87
+
88
+ def _fallback_enabled() -> bool:
89
+ return os.getenv("AUTO_FALLBACK_TO_LLAMACPP", "0").strip().lower() in {"1", "true", "yes", "on"}
src/extraction/factory.py CHANGED
@@ -1,7 +1,7 @@
1
  """Backend selection.
2
 
3
  `EXTRACTOR_BACKEND` env:
4
- - `auto`: Transformers when CUDA is visible, CPU llama.cpp otherwise.
5
  - `llamacpp-gpu` / `llama-champion`: llama.cpp GGUF badge path.
6
  - `zerogpu` / `transformers`: official OpenBMB Transformers backend.
7
  - `api`: hosted OpenBMB endpoint (dev fallback only).
 
1
  """Backend selection.
2
 
3
  `EXTRACTOR_BACKEND` env:
4
+ - `auto`: Transformers on ZeroGPU/CUDA, CPU llama.cpp otherwise.
5
  - `llamacpp-gpu` / `llama-champion`: llama.cpp GGUF badge path.
6
  - `zerogpu` / `transformers`: official OpenBMB Transformers backend.
7
  - `api`: hosted OpenBMB endpoint (dev fallback only).