r0mant1c commited on
Commit
31f3123
·
1 Parent(s): 2f3c1e2

Select extraction backend by CUDA availability

Browse files
README.md CHANGED
@@ -46,28 +46,27 @@ The knowledge graph is educational context, not diagnosis. The lab-provided refe
46
 
47
  ## Hugging Face Space Deployment
48
 
49
- The Hugging Face Space is intentionally deployed as a **Gradio ZeroGPU Space**. This is the active
50
- deployment path.
51
 
52
- The badge-target backend runs the official OpenBMB MiniCPM-V 4.6 GGUF through `llama.cpp`
53
- inside a ZeroGPU-managed function, using a `llama-cpp-python` build that includes MiniCPM-V 4.6
54
- mtmd support. The deterministic knowledge-graph
55
- enrichment and UI rendering stay in normal Gradio/Python code.
56
 
57
  This workflow should not be further changed back to Docker unless the project intentionally gives up
58
- ZeroGPU. When the fine-tuned GGUF model is ready, only replace the model variables:
59
 
60
  ```bash
61
  EXTRACTOR_BACKEND=auto
 
62
  LLAMACPP_GGUF_REPO=openbmb/MiniCPM-V-4.6-gguf
63
  LLAMACPP_MODEL_FILE=MiniCPM-V-4_6-Q4_K_M.gguf
64
- LLAMACPP_MMPROJ_FILE=mmproj-model-f16.gguf
65
  ```
66
 
67
- The future fine-tuned deployment should keep the same Gradio + ZeroGPU + `llama.cpp` architecture
68
- and only insert the fine-tuned GGUF repository/path into the `LLAMACPP_*` variables. If
69
- `llama-cpp-python` proves incompatible with MiniCPM-V 4.6 on ZeroGPU, the fallback backend is
70
- `EXTRACTOR_BACKEND=zerogpu` with `ZEROGPU_MODEL_ID=openbmb/MiniCPM-V-4.6`.
71
 
72
  ## Local Setup
73
 
@@ -76,15 +75,13 @@ pip install -r requirements.txt
76
  EXTRACTOR_BACKEND=auto python app.py
77
  ```
78
 
79
- The Space runtime is intentionally slim. The Transformers fallback stack is kept out of the
80
- default Space dependency set so the build stays faster and more deterministic. The active
81
- llama.cpp path uses the official prebuilt CPU manylinux wheel for `llama-cpp-python`:
82
 
83
  ```text
84
  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
85
  ```
86
 
87
  That avoids both the CUDA runtime mismatch that was causing the Space to abort on
88
- `libcudart.so.12` and the slow source build that was timing out on Hugging Face. The current
89
- pipeline is PDF/text-only, so the extractor does not load an image encoder or mmproj. Install any
90
- extra backend-specific packages only when you actually need to run that backend locally.
 
46
 
47
  ## Hugging Face Space Deployment
48
 
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. The deterministic knowledge-graph enrichment and UI rendering stay
55
+ in normal Gradio/Python code.
56
 
57
  This workflow should not be further changed back to Docker unless the project intentionally gives up
58
+ ZeroGPU. When the fine-tuned models are ready, only replace the model variables:
59
 
60
  ```bash
61
  EXTRACTOR_BACKEND=auto
62
+ ZEROGPU_MODEL_ID=openbmb/MiniCPM-V-4.6
63
  LLAMACPP_GGUF_REPO=openbmb/MiniCPM-V-4.6-gguf
64
  LLAMACPP_MODEL_FILE=MiniCPM-V-4_6-Q4_K_M.gguf
 
65
  ```
66
 
67
+ The future fine-tuned deployment should keep the same Gradio adaptive architecture and only insert
68
+ the fine-tuned Transformers repo into `ZEROGPU_MODEL_ID` and the fine-tuned GGUF repo/path into the
69
+ `LLAMACPP_*` variables.
 
70
 
71
  ## Local Setup
72
 
 
75
  EXTRACTOR_BACKEND=auto python app.py
76
  ```
77
 
78
+ The Space runtime installs both extraction lanes so `auto` can choose at runtime. The CPU llama.cpp
79
+ path uses the official prebuilt CPU manylinux wheel for `llama-cpp-python`:
 
80
 
81
  ```text
82
  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
83
  ```
84
 
85
  That avoids both the CUDA runtime mismatch that was causing the Space to abort on
86
+ `libcudart.so.12` and the slow source build that was timing out on Hugging Face. The CPU fallback is
87
+ PDF/text-only, so the extractor does not load an image encoder or mmproj on CPU.
 
RUNBOOK.md CHANGED
@@ -1,6 +1,6 @@
1
- # Runbook — ZeroGPU Extraction + Fine-Tuned Model Swap
2
 
3
- The active deployment path is now **Gradio ZeroGPU**.
4
 
5
  This replaced the Docker + `llama-server` path because ZeroGPU is only available for Gradio SDK Spaces. The Docker build was also failing on free CPU hardware with `OOMKilled`.
6
 
@@ -9,12 +9,13 @@ This replaced the Docker + `llama-server` path because ZeroGPU is only available
9
  | Area | Current choice |
10
  |---|---|
11
  | Space SDK | `gradio` |
12
- | Hardware | ZeroGPU |
13
- | Badge-target runtime | `llama.cpp` through a `llama-cpp-python` build with MiniCPM-V 4.6 support |
14
- | Badge-target backend | `EXTRACTOR_BACKEND=auto` or `EXTRACTOR_BACKEND=llamacpp-gpu` |
15
- | Fallback backend | `EXTRACTOR_BACKEND=zerogpu` with Transformers |
16
- | Model variables | `LLAMACPP_GGUF_REPO`, `LLAMACPP_MODEL_FILE`, `LLAMACPP_MMPROJ_FILE` |
17
- | Extraction backends | `src/extraction/llamacpp_gpu.py`, `src/extraction/zerogpu_transformers.py` |
 
18
  | Report enrichment | `src/report_pipeline.py` + `kb/cbc_knowledge_graph.json` |
19
 
20
  Do not switch the Space back to Docker unless the project intentionally gives up ZeroGPU.
@@ -23,9 +24,9 @@ Do not switch the Space back to Docker unless the project intentionally gives up
23
 
24
  `EXTRACTOR_BACKEND`:
25
 
26
- - `auto`: default badge-target path, runs GGUF through `llama.cpp` inside `@spaces.GPU`.
27
- - `llamacpp-gpu`: explicit alias for the same badge-target path.
28
- - `zerogpu`: force the ZeroGPU Transformers fallback backend.
29
  - `api`: hosted OpenBMB endpoint for development fallback only.
30
  - `local` / `server` / `llamacpp`: local experimental backends, not the active HF Space path.
31
 
@@ -51,12 +52,14 @@ Install dependencies from `requirements.txt`, including:
51
 
52
  ```text
53
  spaces
 
 
54
  llama-cpp-python
55
  ```
56
 
57
- The Space build intentionally excludes the heavier Transformers fallback stack from the default
58
- runtime requirements so the Hugging Face build stays fast. Add backend-specific extras only when
59
- you are explicitly working on that backend locally.
60
 
61
  The active llama.cpp path now uses the official prebuilt CPU manylinux wheel for `llama-cpp-python`:
62
 
@@ -65,11 +68,12 @@ https://github.com/abetlen/llama-cpp-python/releases/download/v0.3.28/llama_cpp_
65
  ```
66
 
67
  This avoids both the CUDA runtime mismatch that was causing the Space to abort on
68
- `libcudart.so.12` and the slow source build that was timing out on Hugging Face. The current
69
- pipeline is PDF/text-only, so it keeps the Space on a simpler llama.cpp route without mmproj or
70
- an image encoder.
71
 
72
- Both ZeroGPU backends use `@spaces.GPU(duration=120)` for the model generation call.
 
73
 
74
  ## Current Model
75
 
@@ -79,34 +83,27 @@ Badge-target defaults:
79
  EXTRACTOR_BACKEND=auto
80
  LLAMACPP_GGUF_REPO=openbmb/MiniCPM-V-4.6-gguf
81
  LLAMACPP_MODEL_FILE=MiniCPM-V-4_6-Q4_K_M.gguf
82
- LLAMACPP_MMPROJ_FILE=mmproj-model-f16.gguf
83
- ```
84
-
85
- This is the official OpenBMB GGUF model path. No model files are committed to the Space repo.
86
-
87
- Fallback variables if llama.cpp is incompatible on ZeroGPU:
88
-
89
- ```bash
90
- EXTRACTOR_BACKEND=zerogpu
91
  ZEROGPU_MODEL_ID=openbmb/MiniCPM-V-4.6
92
  ```
93
 
 
 
94
  ## Fine-Tuned Model Swap
95
 
96
  When the fine-tuned model is ready:
97
 
98
- 1. Convert/quantize it to GGUF.
99
- 2. Upload it and the compatible mmproj to a Hugging Face model repo.
100
- 3. Keep the same Gradio + ZeroGPU + llama.cpp architecture.
101
  4. Change only:
102
 
103
  ```bash
 
104
  LLAMACPP_GGUF_REPO=<owner>/<fine-tuned-minicpm-v-gguf-repo>
105
  LLAMACPP_MODEL_FILE=<fine-tuned-model>.gguf
106
- LLAMACPP_MMPROJ_FILE=<compatible-mmproj>.gguf
107
  ```
108
 
109
- Do not add model files to the Space git repo. Do not reintroduce Docker or `llama-server` for the ZeroGPU deployment.
110
 
111
  ## Local Development
112
 
@@ -123,7 +120,7 @@ pip install -r requirements.txt
123
  EXTRACTOR_BACKEND=auto python app.py
124
  ```
125
 
126
- Local machines without a suitable GPU may be slow or may not have enough memory for full model inference. In that case, test UI/report rendering locally and test extraction on the HF ZeroGPU Space.
127
 
128
  ## Verification
129
 
@@ -134,4 +131,4 @@ python3 -m py_compile app.py src/*.py src/extraction/*.py
134
  .venv/bin/python -m pytest tests/test_report_pipeline.py
135
  ```
136
 
137
- Then verify the Space build uses Gradio, not Docker, and that ZeroGPU can be selected in the Space hardware panel.
 
1
+ # Runbook — Adaptive Extraction + Fine-Tuned Model Swap
2
 
3
+ The active deployment path is now **Gradio with adaptive extraction**.
4
 
5
  This replaced the Docker + `llama-server` path because ZeroGPU is only available for Gradio SDK Spaces. The Docker build was also failing on free CPU hardware with `OOMKilled`.
6
 
 
9
  | Area | Current choice |
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` |
17
+ | Transformers variables | `ZEROGPU_MODEL_ID`, `ZEROGPU_MAX_NEW_TOKENS`, `ZEROGPU_QUANTIZE` |
18
+ | Extraction backends | `src/extraction/auto.py`, `src/extraction/llamacpp_gpu.py`, `src/extraction/zerogpu_transformers.py` |
19
  | Report enrichment | `src/report_pipeline.py` + `kb/cbc_knowledge_graph.json` |
20
 
21
  Do not switch the Space back to Docker unless the project intentionally gives up ZeroGPU.
 
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.
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.
31
  - `local` / `server` / `llamacpp`: local experimental backends, not the active HF Space path.
32
 
 
52
 
53
  ```text
54
  spaces
55
+ torch
56
+ 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`:
65
 
 
68
  ```
69
 
70
  This avoids both the CUDA runtime mismatch that was causing the Space to abort on
71
+ `libcudart.so.12` and the slow source build that was timing out on Hugging Face. The CPU fallback
72
+ is PDF/text-only, so it keeps the Space on a simpler llama.cpp route without mmproj or an image
73
+ encoder.
74
 
75
+ The Transformers backend uses `@spaces.GPU(duration=120)` for GPU generation. The llama.cpp CPU
76
+ fallback keeps a longer duration budget because CPU inference is slower.
77
 
78
  ## Current Model
79
 
 
83
  EXTRACTOR_BACKEND=auto
84
  LLAMACPP_GGUF_REPO=openbmb/MiniCPM-V-4.6-gguf
85
  LLAMACPP_MODEL_FILE=MiniCPM-V-4_6-Q4_K_M.gguf
 
 
 
 
 
 
 
 
 
86
  ZEROGPU_MODEL_ID=openbmb/MiniCPM-V-4.6
87
  ```
88
 
89
+ No model files are committed to the Space repo.
90
+
91
  ## Fine-Tuned Model Swap
92
 
93
  When the fine-tuned model is ready:
94
 
95
+ 1. Upload the fine-tuned Transformers checkpoint to a Hugging Face model repo for the CUDA lane.
96
+ 2. Convert/quantize it to GGUF for the CPU llama.cpp lane.
97
+ 3. Keep the same Gradio adaptive architecture.
98
  4. Change only:
99
 
100
  ```bash
101
+ ZEROGPU_MODEL_ID=<owner>/<fine-tuned-minicpm-v-transformers-repo>
102
  LLAMACPP_GGUF_REPO=<owner>/<fine-tuned-minicpm-v-gguf-repo>
103
  LLAMACPP_MODEL_FILE=<fine-tuned-model>.gguf
 
104
  ```
105
 
106
+ Do not add model files to the Space git repo. Do not reintroduce Docker or `llama-server` for the Space deployment.
107
 
108
  ## Local Development
109
 
 
120
  EXTRACTOR_BACKEND=auto python app.py
121
  ```
122
 
123
+ Local machines without a suitable GPU may be slow or may not have enough memory for full model inference. In that case, test UI/report rendering locally and test extraction on the HF Space.
124
 
125
  ## Verification
126
 
 
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.
requirements.txt CHANGED
@@ -4,6 +4,14 @@ requests==2.32.5
4
  pillow==12.0.0
5
  pymupdf==1.26.6
6
  json-repair==0.60.1
 
 
 
 
 
 
 
 
7
  # Active HF Space path: run the official OpenBMB GGUF through llama.cpp on ZeroGPU.
8
  # Install the prebuilt CPU manylinux wheel directly to avoid a source build on Spaces.
9
  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"
 
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"
src/extraction/__init__.py CHANGED
@@ -1,11 +1,13 @@
1
  """Extraction backends behind one interface.
2
 
3
  `build_extractor()` returns the right backend for the environment:
4
- - **zerogpu** / **auto**: official OpenBMB MiniCPM-V through Transformers on HF ZeroGPU.
 
 
5
  - **local**: local llama-server / llama.cpp backends for local experimentation.
6
  - **api**: the original OpenBMB hosted endpoint, kept as a dev fallback only.
7
 
8
- Default is `auto`, which resolves to the ZeroGPU Transformers backend for the Space.
9
  """
10
 
11
  from src.extraction.base import Extractor, ExtractionResult
 
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.
8
  - **api**: the original OpenBMB hosted endpoint, kept as a dev fallback only.
9
 
10
+ Default is `auto`.
11
  """
12
 
13
  from src.extraction.base import Extractor, ExtractionResult
src/extraction/auto.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Adaptive extraction backend selection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from src.extraction.base import Extractor
6
+ from src.extraction.llamacpp_gpu import LlamaCppGPUExtractor
7
+ from src.extraction.zerogpu_transformers import ZeroGPUTransformersExtractor
8
+
9
+
10
+ class AutoExtractor:
11
+ """Use Transformers when CUDA is visible, otherwise fall back to CPU llama.cpp."""
12
+
13
+ def __init__(self, model_id: str | None = None) -> None:
14
+ self.model_id = model_id
15
+ self._selected: Extractor | None = None
16
+
17
+ def extract(self, file_path: str, max_pages: int = 3):
18
+ return self._backend().extract(file_path, max_pages=max_pages)
19
+
20
+ def _backend(self) -> Extractor:
21
+ if self._selected is None:
22
+ if cuda_available():
23
+ self._selected = ZeroGPUTransformersExtractor(model_id=self.model_id)
24
+ else:
25
+ self._selected = LlamaCppGPUExtractor()
26
+ return self._selected
27
+
28
+
29
+ def cuda_available() -> bool:
30
+ try:
31
+ import torch
32
+ except Exception:
33
+ return False
34
+
35
+ try:
36
+ return bool(torch.cuda.is_available())
37
+ except Exception:
38
+ return False
src/extraction/factory.py CHANGED
@@ -1,8 +1,9 @@
1
  """Backend selection.
2
 
3
  `EXTRACTOR_BACKEND` env:
4
- - `auto` / `llamacpp-gpu` / `llama-champion`: HF ZeroGPU + llama.cpp GGUF badge path.
5
- - `zerogpu` / `transformers`: HF ZeroGPU + official OpenBMB Transformers fallback.
 
6
  - `api`: hosted OpenBMB endpoint (dev fallback only).
7
  - `local` / `server`: local llama-server backend for local development.
8
  - `llamacpp`: in-process llama-cpp-python backend for local development.
@@ -13,6 +14,7 @@ from __future__ import annotations
13
  import os
14
 
15
  from src.extraction.base import Extractor
 
16
  from src.extraction.llamacpp_gpu import LlamaCppGPUExtractor
17
  from src.extraction.local_minicpmv import LocalMiniCPMVExtractor
18
  from src.extraction.local_server import LocalServerExtractor
@@ -27,8 +29,9 @@ def build_extractor(
27
  ) -> Extractor:
28
  backend = os.getenv("EXTRACTOR_BACKEND", "auto").strip().lower()
29
 
30
- if backend in ("auto", "llamacpp-gpu", "gpu-llamacpp", "llama-champion"):
31
- # llama.cpp on the ZeroGPU GPU -> earns the Llama Champion badge while staying off-grid.
 
32
  return LlamaCppGPUExtractor()
33
  if backend in ("zerogpu", "zero-gpu", "transformers"):
34
  return ZeroGPUTransformersExtractor(model_id=model)
 
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).
8
  - `local` / `server`: local llama-server backend for local development.
9
  - `llamacpp`: in-process llama-cpp-python backend for local development.
 
14
  import os
15
 
16
  from src.extraction.base import Extractor
17
+ from src.extraction.auto import AutoExtractor
18
  from src.extraction.llamacpp_gpu import LlamaCppGPUExtractor
19
  from src.extraction.local_minicpmv import LocalMiniCPMVExtractor
20
  from src.extraction.local_server import LocalServerExtractor
 
29
  ) -> Extractor:
30
  backend = os.getenv("EXTRACTOR_BACKEND", "auto").strip().lower()
31
 
32
+ if backend == "auto":
33
+ return AutoExtractor(model_id=model)
34
+ if backend in ("llamacpp-gpu", "gpu-llamacpp", "llama-champion"):
35
  return LlamaCppGPUExtractor()
36
  if backend in ("zerogpu", "zero-gpu", "transformers"):
37
  return ZeroGPUTransformersExtractor(model_id=model)