vision-language-engineering-lab 0.2.1__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vision_language_engineering_lab-0.3.2/PKG-INFO +1784 -0
- vision_language_engineering_lab-0.3.2/README.md +1712 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/pyproject.toml +26 -5
- vision_language_engineering_lab-0.3.2/src/vision_language_engineering_lab.egg-info/PKG-INFO +1784 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vision_language_engineering_lab.egg-info/SOURCES.txt +7 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vision_language_engineering_lab.egg-info/requires.txt +19 -3
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/__init__.py +4 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/__main__.py +2 -1
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/cli.py +110 -16
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/chunking.py +31 -16
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/exceptions.py +4 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/inputs.py +103 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/operational.py +69 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/py.typed +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/downloader.py +11 -9
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/model.py +59 -70
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/registry.py +5 -3
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/retrieval/__init__.py +6 -2
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/backend.py +46 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/in_memory.py +65 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/model_options.py +104 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/multimodal_embedding.py +107 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/multimodal_rag.py +149 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/retrieval/rag.py +8 -8
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/reranker.py +126 -0
- vision_language_engineering_lab-0.3.2/src/vlm_engineering/runtime_config.py +119 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_chunking.py +9 -0
- vision_language_engineering_lab-0.2.1/PKG-INFO +0 -1308
- vision_language_engineering_lab-0.2.1/README.md +0 -1250
- vision_language_engineering_lab-0.2.1/src/vision_language_engineering_lab.egg-info/PKG-INFO +0 -1308
- vision_language_engineering_lab-0.2.1/src/vlm_engineering/retrieval/in_memory.py +0 -48
- vision_language_engineering_lab-0.2.1/src/vlm_engineering/retrieval/multimodal_embedding.py +0 -56
- vision_language_engineering_lab-0.2.1/src/vlm_engineering/retrieval/reranker.py +0 -53
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/LICENSE +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/setup.cfg +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vision_language_engineering_lab.egg-info/dependency_links.txt +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vision_language_engineering_lab.egg-info/entry_points.txt +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vision_language_engineering_lab.egg-info/top_level.txt +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/clip/__init__.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/clip/encoder.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/config.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/__init__.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/fusion.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/schemas.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/visual_analysis.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/__init__.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/retrieval/text_embedding.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/utils.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_qwen_loader.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_retrieval.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_utils.py +0 -0
- {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_visual_analysis.py +0 -0
|
@@ -0,0 +1,1784 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vision-language-engineering-lab
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Summary: Production-oriented Vision-Language engineering with CLIP, Qwen3-VL, visual document understanding and multimodal RAG.
|
|
5
|
+
Author: GHANMI Helmi
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/helmighanmi/vision-language-engineering-lab
|
|
8
|
+
Project-URL: Repository, https://github.com/helmighanmi/vision-language-engineering-lab
|
|
9
|
+
Project-URL: Issues, https://github.com/helmighanmi/vision-language-engineering-lab/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/helmighanmi/vision-language-engineering-lab/tree/main/docs
|
|
11
|
+
Keywords: vision-language-model,vlm,multimodal-ai,clip,qwen3-vl,multimodal-rag,visual-rag,document-ai,image-retrieval
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: <3.14,>=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy<3,>=1.26
|
|
24
|
+
Requires-Dist: pillow<14,>=13
|
|
25
|
+
Provides-Extra: clip
|
|
26
|
+
Requires-Dist: torch<3,>=2.8; extra == "clip"
|
|
27
|
+
Requires-Dist: torchvision<1,>=0.23; extra == "clip"
|
|
28
|
+
Requires-Dist: transformers<6,>=4.57; extra == "clip"
|
|
29
|
+
Provides-Extra: qwen
|
|
30
|
+
Requires-Dist: accelerate<2,>=1.10; extra == "qwen"
|
|
31
|
+
Requires-Dist: huggingface-hub<2,>=0.34; extra == "qwen"
|
|
32
|
+
Requires-Dist: qwen-vl-utils<1,>=0.0.14; extra == "qwen"
|
|
33
|
+
Requires-Dist: torch<3,>=2.8; extra == "qwen"
|
|
34
|
+
Requires-Dist: torchvision<1,>=0.23; extra == "qwen"
|
|
35
|
+
Requires-Dist: transformers<6,>=4.57; extra == "qwen"
|
|
36
|
+
Provides-Extra: retrieval
|
|
37
|
+
Requires-Dist: sentence-transformers<7,>=5.4; extra == "retrieval"
|
|
38
|
+
Provides-Extra: qwen-retrieval
|
|
39
|
+
Requires-Dist: sentence-transformers[image]<7,>=5.4; extra == "qwen-retrieval"
|
|
40
|
+
Requires-Dist: torch<3,>=2.8; extra == "qwen-retrieval"
|
|
41
|
+
Requires-Dist: torchvision<1,>=0.23; extra == "qwen-retrieval"
|
|
42
|
+
Requires-Dist: transformers<6,>=5.0; extra == "qwen-retrieval"
|
|
43
|
+
Requires-Dist: accelerate<2,>=1.10; extra == "qwen-retrieval"
|
|
44
|
+
Requires-Dist: huggingface-hub<2,>=0.34; extra == "qwen-retrieval"
|
|
45
|
+
Requires-Dist: qwen-vl-utils<1,>=0.0.14; extra == "qwen-retrieval"
|
|
46
|
+
Provides-Extra: config
|
|
47
|
+
Requires-Dist: PyYAML<7,>=6.0.2; extra == "config"
|
|
48
|
+
Provides-Extra: notebooks
|
|
49
|
+
Requires-Dist: jupyterlab<5,>=4.6.3; extra == "notebooks"
|
|
50
|
+
Requires-Dist: jupyter-server<3,>=2.21; extra == "notebooks"
|
|
51
|
+
Requires-Dist: matplotlib<4,>=3.9; extra == "notebooks"
|
|
52
|
+
Requires-Dist: pandas<3,>=2.2; extra == "notebooks"
|
|
53
|
+
Provides-Extra: dev
|
|
54
|
+
Requires-Dist: mypy<2,>=1.18; extra == "dev"
|
|
55
|
+
Requires-Dist: huggingface-hub<2,>=0.34; extra == "dev"
|
|
56
|
+
Requires-Dist: PyYAML<7,>=6.0.2; extra == "dev"
|
|
57
|
+
Requires-Dist: types-PyYAML<7,>=6; extra == "dev"
|
|
58
|
+
Requires-Dist: pip-audit<3,>=2.9; extra == "dev"
|
|
59
|
+
Requires-Dist: pytest<10,>=9.0.3; extra == "dev"
|
|
60
|
+
Requires-Dist: pytest-cov<8,>=7.1; extra == "dev"
|
|
61
|
+
Requires-Dist: ruff<1,>=0.12; extra == "dev"
|
|
62
|
+
Provides-Extra: all
|
|
63
|
+
Requires-Dist: PyYAML<7,>=6.0.2; extra == "all"
|
|
64
|
+
Requires-Dist: accelerate<2,>=1.10; extra == "all"
|
|
65
|
+
Requires-Dist: huggingface-hub<2,>=0.34; extra == "all"
|
|
66
|
+
Requires-Dist: qwen-vl-utils<1,>=0.0.14; extra == "all"
|
|
67
|
+
Requires-Dist: sentence-transformers[image]<7,>=5.4; extra == "all"
|
|
68
|
+
Requires-Dist: torch<3,>=2.8; extra == "all"
|
|
69
|
+
Requires-Dist: torchvision<1,>=0.23; extra == "all"
|
|
70
|
+
Requires-Dist: transformers<6,>=5.0; extra == "all"
|
|
71
|
+
Dynamic: license-file
|
|
72
|
+
|
|
73
|
+
<!--
|
|
74
|
+
Path: README.md
|
|
75
|
+
Author: GHANMI Helmi
|
|
76
|
+
Current Role: AI Engineer
|
|
77
|
+
Past Role: Researcher in Applied Mathematics
|
|
78
|
+
Research Profile: https://www.researchgate.net/profile/Ghanmi-Helmi
|
|
79
|
+
-->
|
|
80
|
+
|
|
81
|
+
# Vision-Language Engineering Lab
|
|
82
|
+
|
|
83
|
+
**Production-oriented Vision-Language engineering with CLIP, configurable Qwen3-VL, document understanding, multimodal retrieval, reranking, and RAG.**
|
|
84
|
+
|
|
85
|
+
`vision-language-engineering-lab` is a Python package for building and studying Vision-Language applications with reusable engineering patterns rather than notebook-only demos.
|
|
86
|
+
|
|
87
|
+
The implementation lives under `src/vlm_engineering/`. Notebooks are analysis/demo clients, `examples/` provides small API examples, and `scenarios/` contains end-to-end application recipes.
|
|
88
|
+
|
|
89
|
+
> **v0.3.2 compatibility target:** Python 3.11, 3.12, and 3.13.
|
|
90
|
+
|
|
91
|
+
v0.3.1 introduced the two-stage multimodal retrieval stack:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
query
|
|
95
|
+
↓
|
|
96
|
+
Qwen3-VL-Embedding
|
|
97
|
+
↓
|
|
98
|
+
vector retrieval / candidate_k
|
|
99
|
+
↓
|
|
100
|
+
Qwen3-VL-Reranker
|
|
101
|
+
↓
|
|
102
|
+
top_k multimodal evidence
|
|
103
|
+
↓
|
|
104
|
+
original text + images
|
|
105
|
+
↓
|
|
106
|
+
Qwen3-VL generation
|
|
107
|
+
↓
|
|
108
|
+
grounded answer
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Contributions are welcome. See [Contributing](#20-contributing) and [`CONTRIBUTING.md`](CONTRIBUTING.md).
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
### Interactive architecture map
|
|
115
|
+
|
|
116
|
+
Explore the package structure, services, and multimodal RAG flow:
|
|
117
|
+
|
|
118
|
+
[Open the interactive architecture map](https://helmighanmi.github.io/vision-language-engineering-lab/)
|
|
119
|
+
---
|
|
120
|
+
## 1. Quick start
|
|
121
|
+
|
|
122
|
+
### Install from PyPI
|
|
123
|
+
|
|
124
|
+
Core package:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
python -m pip install vision-language-engineering-lab
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Qwen3-VL generation:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
python -m pip install "vision-language-engineering-lab[qwen]"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Qwen multimodal embedding and reranking:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
python -m pip install "vision-language-engineering-lab[qwen-retrieval]"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
All runtime features:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
python -m pip install "vision-language-engineering-lab[all]"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Install from the repository
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
git clone https://github.com/helmighanmi/vision-language-engineering-lab.git
|
|
152
|
+
cd vision-language-engineering-lab
|
|
153
|
+
|
|
154
|
+
python -m venv .venv
|
|
155
|
+
source .venv/bin/activate
|
|
156
|
+
|
|
157
|
+
python -m pip install --upgrade pip setuptools wheel
|
|
158
|
+
python -m pip install -e ".[all]"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Windows PowerShell:
|
|
162
|
+
|
|
163
|
+
```powershell
|
|
164
|
+
py -m venv .venv
|
|
165
|
+
.venv\Scripts\Activate.ps1
|
|
166
|
+
|
|
167
|
+
python -m pip install --upgrade pip setuptools wheel
|
|
168
|
+
python -m pip install -e ".[all]"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Example 1 — Real-image smoke test with Qwen3-VL 2B
|
|
172
|
+
|
|
173
|
+
A repository clone includes `data/diagram_random_clean.png` as a real-image smoke-test asset:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
vlm-lab describe data/diagram_random_clean.png \
|
|
177
|
+
--model-size 2b \
|
|
178
|
+
--prompt "Describe this image accurately. List the main objects, text, and important visual relationships."
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Equivalent Python API:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from vlm_engineering import QwenVLModel
|
|
185
|
+
|
|
186
|
+
model = QwenVLModel(model_size="2b")
|
|
187
|
+
|
|
188
|
+
answer = model.generate(
|
|
189
|
+
"data/diagram_random_clean.png",
|
|
190
|
+
(
|
|
191
|
+
"Describe this image accurately. "
|
|
192
|
+
"List the main objects, text, and important visual relationships."
|
|
193
|
+
),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
print(answer)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The default generative model is:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
Qwen/Qwen3-VL-2B-Instruct
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If you installed from PyPI rather than cloning the repository, use your own image path:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
vlm-lab describe /path/to/your/image.png \
|
|
209
|
+
--model-size 2b \
|
|
210
|
+
--prompt "Describe this image accurately."
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Example 2 — Multimodal embeddings
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from vlm_engineering import QwenVLEmbedder
|
|
217
|
+
|
|
218
|
+
embedder = QwenVLEmbedder(
|
|
219
|
+
model_size="2b",
|
|
220
|
+
dimensions=1024,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
text_vector = embedder.embed_text(
|
|
224
|
+
"Find an architecture diagram containing a cache."
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
image_vector = embedder.embed_image(
|
|
228
|
+
"data/diagram_random_clean.png"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
mixed_vectors = embedder.encode(
|
|
232
|
+
[
|
|
233
|
+
{"text": "Redis caching architecture"},
|
|
234
|
+
{"image": "data/diagram_random_clean.png"},
|
|
235
|
+
{
|
|
236
|
+
"text": "Architecture diagram",
|
|
237
|
+
"image": "data/diagram_random_clean.png",
|
|
238
|
+
},
|
|
239
|
+
]
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
print(text_vector.shape)
|
|
243
|
+
print(image_vector.shape)
|
|
244
|
+
print(mixed_vectors.shape)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The retrieval input contract is intentionally explicit:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
{"text": "..."}
|
|
251
|
+
{"image": "..."}
|
|
252
|
+
{"text": "...", "image": "..."}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Unknown fields, empty inputs, missing local images, corrupt files, and unsupported inputs are rejected before expensive model inference whenever possible.
|
|
256
|
+
|
|
257
|
+
### Example 3 — Multimodal reranking
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
from vlm_engineering import QwenVLReranker
|
|
261
|
+
|
|
262
|
+
documents = [
|
|
263
|
+
{"text": "Redis provides application caching."},
|
|
264
|
+
{"text": "PostgreSQL stores persistent application data."},
|
|
265
|
+
{"image": "data/diagram_random_clean.png"},
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
reranker = QwenVLReranker(model_size="2b")
|
|
269
|
+
|
|
270
|
+
scores = reranker.score(
|
|
271
|
+
"Find the evidence related to caching.",
|
|
272
|
+
documents,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
ranked = reranker.rerank(
|
|
276
|
+
"Find the evidence related to caching.",
|
|
277
|
+
documents,
|
|
278
|
+
top_k=2,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
print(scores)
|
|
282
|
+
for result in ranked:
|
|
283
|
+
print(result.rank, result.score, result.item)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Raw scores and normalized scores are both supported. Normalized reranker scores are relevance transformations, not calibrated probabilities.
|
|
287
|
+
|
|
288
|
+
### Example 4 — True multimodal RAG
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from vlm_engineering import (
|
|
292
|
+
MultimodalRAGPipeline,
|
|
293
|
+
NativePageContent,
|
|
294
|
+
QwenVLEmbedder,
|
|
295
|
+
QwenVLModel,
|
|
296
|
+
QwenVLReranker,
|
|
297
|
+
VisualAnalysis,
|
|
298
|
+
VisualChunkBuilder,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
chunk = VisualChunkBuilder().build(
|
|
302
|
+
NativePageContent(
|
|
303
|
+
document_id="demo",
|
|
304
|
+
page=1,
|
|
305
|
+
source_file="architecture.pdf",
|
|
306
|
+
image_ref="data/diagram_random_clean.png",
|
|
307
|
+
),
|
|
308
|
+
VisualAnalysis(
|
|
309
|
+
page_type="diagram",
|
|
310
|
+
title="Architecture",
|
|
311
|
+
summary="Technical architecture diagram.",
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
pipeline = MultimodalRAGPipeline(
|
|
316
|
+
embedder=QwenVLEmbedder(model_size="2b"),
|
|
317
|
+
generator=QwenVLModel(model_size="2b"),
|
|
318
|
+
reranker=QwenVLReranker(model_size="2b"),
|
|
319
|
+
candidate_k=12,
|
|
320
|
+
top_k=3,
|
|
321
|
+
max_new_tokens=256,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
pipeline.index_chunks([chunk])
|
|
325
|
+
|
|
326
|
+
answer = pipeline.answer(
|
|
327
|
+
"Which components are connected?"
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
print(answer.answer)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
The pipeline performs:
|
|
334
|
+
|
|
335
|
+
```text
|
|
336
|
+
multimodal embedding
|
|
337
|
+
↓
|
|
338
|
+
candidate retrieval
|
|
339
|
+
↓
|
|
340
|
+
multimodal reranking
|
|
341
|
+
↓
|
|
342
|
+
selected original evidence
|
|
343
|
+
↓
|
|
344
|
+
Qwen3-VL generation
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Example 5 — Select 2B / 4B / 8B generative Qwen3-VL
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
from vlm_engineering import QwenVLModel
|
|
351
|
+
|
|
352
|
+
model_2b = QwenVLModel(model_size="2b")
|
|
353
|
+
model_4b = QwenVLModel(model_size="4b")
|
|
354
|
+
model_8b = QwenVLModel(model_size="8b")
|
|
355
|
+
|
|
356
|
+
print(model_2b.model_source)
|
|
357
|
+
print(model_4b.model_source)
|
|
358
|
+
print(model_8b.model_source)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
| Preset | Hugging Face model | Typical use |
|
|
362
|
+
|---|---|---|
|
|
363
|
+
| `2b` | `Qwen/Qwen3-VL-2B-Instruct` | default development / lower-resource inference |
|
|
364
|
+
| `4b` | `Qwen/Qwen3-VL-4B-Instruct` | balanced quality/resource option |
|
|
365
|
+
| `8b` | `Qwen/Qwen3-VL-8B-Instruct` | stronger hardware / higher-capacity inference |
|
|
366
|
+
|
|
367
|
+
### Example 6 — Local/offline model loading
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
from vlm_engineering import QwenVLModel
|
|
371
|
+
|
|
372
|
+
model = QwenVLModel.from_local(
|
|
373
|
+
"models/Qwen3-VL-2B-Instruct"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
answer = model.generate(
|
|
377
|
+
"data/diagram_random_clean.png",
|
|
378
|
+
"Describe the diagram using only the visible evidence.",
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
print(answer)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`from_local()` uses local-files-only loading and does not silently fall back to the Hugging Face Hub.
|
|
385
|
+
|
|
386
|
+
### Example 7 — Structured document analysis
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
from vlm_engineering import QwenVLModel
|
|
390
|
+
from vlm_engineering.documents import analyze_visual_document
|
|
391
|
+
|
|
392
|
+
model = QwenVLModel(model_size="4b")
|
|
393
|
+
|
|
394
|
+
analysis = analyze_visual_document(
|
|
395
|
+
model,
|
|
396
|
+
"data/architecture.png",
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
print(analysis.to_dict())
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
The structured result can contain page type, title, summary, entities, relations, important text, and uncertainties.
|
|
403
|
+
|
|
404
|
+
### Example 8 — Text-only visual RAG
|
|
405
|
+
|
|
406
|
+
When an existing RAG platform supports only text embeddings, the VLM can first turn visual evidence into faithful retrieval text:
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
from vlm_engineering.retrieval import TextEmbedder
|
|
410
|
+
|
|
411
|
+
embedder = TextEmbedder()
|
|
412
|
+
|
|
413
|
+
embeddings = embedder.encode(
|
|
414
|
+
[
|
|
415
|
+
"Redis provides application caching.",
|
|
416
|
+
"PostgreSQL stores persistent application data.",
|
|
417
|
+
]
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
print(embeddings.shape)
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Example 9 — CLI
|
|
424
|
+
|
|
425
|
+
```bash
|
|
426
|
+
# List generative Qwen presets
|
|
427
|
+
vlm-lab models
|
|
428
|
+
|
|
429
|
+
# Describe an image
|
|
430
|
+
vlm-lab describe data/diagram_random_clean.png --model-size 2b
|
|
431
|
+
|
|
432
|
+
# Structured analysis
|
|
433
|
+
vlm-lab analyze data/architecture.png --model-size 4b
|
|
434
|
+
|
|
435
|
+
# Multimodal embedding
|
|
436
|
+
vlm-lab embed \
|
|
437
|
+
--text "Find a cache architecture" \
|
|
438
|
+
--model-size 2b \
|
|
439
|
+
--dimensions 1024
|
|
440
|
+
|
|
441
|
+
# Reranking
|
|
442
|
+
vlm-lab rerank \
|
|
443
|
+
--query "Find the cache architecture" \
|
|
444
|
+
--documents-json candidates.json \
|
|
445
|
+
--model-size 2b
|
|
446
|
+
|
|
447
|
+
# Validate a runtime profile without loading large models
|
|
448
|
+
vlm-lab validate-config \
|
|
449
|
+
configs/qwen_multimodal_rag.example.yaml
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
For complete workflows, see [`scenarios/README.md`](scenarios/README.md).
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## 2. What this project demonstrates
|
|
457
|
+
|
|
458
|
+
```text
|
|
459
|
+
CLIP foundations
|
|
460
|
+
-> image/text embeddings and zero-shot similarity
|
|
461
|
+
|
|
462
|
+
Qwen3-VL Instruct
|
|
463
|
+
-> image description
|
|
464
|
+
-> VQA
|
|
465
|
+
-> diagram/table/screenshot understanding
|
|
466
|
+
-> structured visual analysis
|
|
467
|
+
|
|
468
|
+
Document understanding
|
|
469
|
+
-> native/OCR evidence + visual semantics
|
|
470
|
+
-> traceable VisualChunk objects
|
|
471
|
+
|
|
472
|
+
Text-only visual RAG
|
|
473
|
+
-> VLM descriptions
|
|
474
|
+
-> normal text embeddings
|
|
475
|
+
-> retrieval
|
|
476
|
+
-> original-image grounding
|
|
477
|
+
|
|
478
|
+
Multimodal retrieval
|
|
479
|
+
-> Qwen3-VL-Embedding
|
|
480
|
+
-> vector recall
|
|
481
|
+
-> Qwen3-VL-Reranker
|
|
482
|
+
-> top-k multimodal evidence
|
|
483
|
+
|
|
484
|
+
Multimodal RAG
|
|
485
|
+
-> embedding
|
|
486
|
+
-> candidate retrieval
|
|
487
|
+
-> reranking
|
|
488
|
+
-> original text/images
|
|
489
|
+
-> Qwen3-VL answer
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
The project intentionally separates:
|
|
493
|
+
|
|
494
|
+
- reusable package code under `src/vlm_engineering/`
|
|
495
|
+
- deterministic tests under `tests/`
|
|
496
|
+
- small API examples under `examples/`
|
|
497
|
+
- end-to-end recipes under `scenarios/`
|
|
498
|
+
- exploratory notebooks under `notebooks/`
|
|
499
|
+
- architecture and operational documentation under `docs/`
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## 3. Python compatibility
|
|
504
|
+
|
|
505
|
+
v0.3.1 targets:
|
|
506
|
+
|
|
507
|
+
| Python | Support |
|
|
508
|
+
|---|---:|
|
|
509
|
+
| 3.11 | ✅ Supported |
|
|
510
|
+
| 3.12 | ✅ Supported |
|
|
511
|
+
| 3.13 | ✅ Supported |
|
|
512
|
+
|
|
513
|
+
The package metadata declares:
|
|
514
|
+
|
|
515
|
+
```text
|
|
516
|
+
>=3.11,<3.14
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
GitHub Actions validates supported Python versions independently.
|
|
520
|
+
|
|
521
|
+
Ruff targets Python 3.11 syntax so package code does not accidentally depend on Python 3.12+ syntax.
|
|
522
|
+
|
|
523
|
+
### Google Colab
|
|
524
|
+
|
|
525
|
+
When the active Colab runtime Python version is supported:
|
|
526
|
+
|
|
527
|
+
```python
|
|
528
|
+
!pip install "vision-language-engineering-lab[all]"
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Then:
|
|
532
|
+
|
|
533
|
+
```python
|
|
534
|
+
from vlm_engineering import QwenVLModel
|
|
535
|
+
|
|
536
|
+
model = QwenVLModel(model_size="2b")
|
|
537
|
+
print(model.model_source)
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
For Qwen workloads, enable a GPU runtime where available.
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## 4. Installation options
|
|
545
|
+
|
|
546
|
+
### Requirements
|
|
547
|
+
|
|
548
|
+
- Python **3.11, 3.12, or 3.13**
|
|
549
|
+
- Git for source installs
|
|
550
|
+
- enough disk space for selected models
|
|
551
|
+
- a GPU is strongly recommended for practical Qwen3-VL inference
|
|
552
|
+
- CPU-only execution may work for some paths but can be slow or require offloading
|
|
553
|
+
|
|
554
|
+
### Create a virtual environment
|
|
555
|
+
|
|
556
|
+
Linux/macOS/GitHub Codespaces:
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
python -m venv .venv
|
|
560
|
+
source .venv/bin/activate
|
|
561
|
+
python -m pip install --upgrade pip setuptools wheel
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Windows PowerShell:
|
|
565
|
+
|
|
566
|
+
```powershell
|
|
567
|
+
py -m venv .venv
|
|
568
|
+
.venv\Scripts\Activate.ps1
|
|
569
|
+
python -m pip install --upgrade pip setuptools wheel
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### Choose what to install
|
|
573
|
+
|
|
574
|
+
Development/testing:
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
python -m pip install -e ".[dev]"
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
CLIP:
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
python -m pip install -e ".[clip]"
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
Qwen3-VL generation:
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
python -m pip install -e ".[qwen]"
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Text retrieval:
|
|
593
|
+
|
|
594
|
+
```bash
|
|
595
|
+
python -m pip install -e ".[retrieval]"
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Qwen multimodal retrieval:
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
python -m pip install -e ".[qwen-retrieval]"
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Runtime YAML profiles:
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
python -m pip install -e ".[config]"
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
All runtime features:
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
python -m pip install -e ".[all]"
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Full contributor environment:
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
python -m pip install -e ".[all,dev,notebooks]"
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
A compatibility `requirements.txt` is also provided:
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
python -m pip install -r requirements.txt
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
`pyproject.toml` remains the dependency source of truth.
|
|
629
|
+
|
|
630
|
+
Confirm the CLI:
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
vlm-lab --help
|
|
634
|
+
vlm-lab models
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## 5. Qwen models used by this project
|
|
640
|
+
|
|
641
|
+
### Generative VLM
|
|
642
|
+
|
|
643
|
+
| Preset | Hugging Face model | Role |
|
|
644
|
+
|---|---|---|
|
|
645
|
+
| `2b` | `Qwen/Qwen3-VL-2B-Instruct` | default generative VLM |
|
|
646
|
+
| `4b` | `Qwen/Qwen3-VL-4B-Instruct` | balanced generative option |
|
|
647
|
+
| `8b` | `Qwen/Qwen3-VL-8B-Instruct` | higher-capacity generative option |
|
|
648
|
+
|
|
649
|
+
### Multimodal embedding
|
|
650
|
+
|
|
651
|
+
| Preset | Hugging Face model | Role |
|
|
652
|
+
|---|---|---|
|
|
653
|
+
| `2b` | `Qwen/Qwen3-VL-Embedding-2B` | multimodal recall / shared embedding space |
|
|
654
|
+
| `8b` | `Qwen/Qwen3-VL-Embedding-8B` | higher-capacity multimodal embeddings |
|
|
655
|
+
|
|
656
|
+
### Multimodal reranking
|
|
657
|
+
|
|
658
|
+
| Preset | Hugging Face model | Role |
|
|
659
|
+
|---|---|---|
|
|
660
|
+
| `2b` | `Qwen/Qwen3-VL-Reranker-2B` | second-stage candidate reranking |
|
|
661
|
+
| `8b` | `Qwen/Qwen3-VL-Reranker-8B` | higher-capacity reranking |
|
|
662
|
+
|
|
663
|
+
The size labels are parameter classes, not exact runtime-memory requirements.
|
|
664
|
+
|
|
665
|
+
Real inference also needs memory for image processing, activations, framework overhead, KV cache/generation state, and model-specific runtime structures.
|
|
666
|
+
|
|
667
|
+
`trust_remote_code` remains disabled by default where supported by the package.
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
## 6. Model selection and loading
|
|
672
|
+
|
|
673
|
+
### Generative model
|
|
674
|
+
|
|
675
|
+
```python
|
|
676
|
+
from vlm_engineering import QwenVLModel
|
|
677
|
+
|
|
678
|
+
model = QwenVLModel(model_size="2b")
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
Explicit Hub model:
|
|
682
|
+
|
|
683
|
+
```python
|
|
684
|
+
model = QwenVLModel.from_hub(
|
|
685
|
+
"Qwen/Qwen3-VL-4B-Instruct"
|
|
686
|
+
)
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Explicit local model:
|
|
690
|
+
|
|
691
|
+
```python
|
|
692
|
+
model = QwenVLModel.from_local(
|
|
693
|
+
"models/Qwen3-VL-4B-Instruct"
|
|
694
|
+
)
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Embedding model
|
|
698
|
+
|
|
699
|
+
```python
|
|
700
|
+
from vlm_engineering import QwenVLEmbedder
|
|
701
|
+
|
|
702
|
+
embedder = QwenVLEmbedder(
|
|
703
|
+
model_size="2b",
|
|
704
|
+
dimensions=1024,
|
|
705
|
+
)
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
### Reranker
|
|
709
|
+
|
|
710
|
+
```python
|
|
711
|
+
from vlm_engineering import QwenVLReranker
|
|
712
|
+
|
|
713
|
+
reranker = QwenVLReranker(
|
|
714
|
+
model_size="2b",
|
|
715
|
+
)
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Model selectors are intentionally explicit. Depending on the API/CLI path, use one of:
|
|
719
|
+
|
|
720
|
+
```text
|
|
721
|
+
model_size
|
|
722
|
+
model_id
|
|
723
|
+
model_path
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
Mutually exclusive model-source options prevent accidental fallback to a different model than the one requested.
|
|
727
|
+
|
|
728
|
+
---
|
|
729
|
+
|
|
730
|
+
## 7. Hugging Face cache and project-local models
|
|
731
|
+
|
|
732
|
+
Two storage modes are supported.
|
|
733
|
+
|
|
734
|
+
### Hub/cache mode
|
|
735
|
+
|
|
736
|
+
For quick experimentation:
|
|
737
|
+
|
|
738
|
+
```bash
|
|
739
|
+
vlm-lab describe \
|
|
740
|
+
data/diagram_random_clean.png \
|
|
741
|
+
--model-size 2b
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
The Hugging Face Hub cache is typically reused automatically on subsequent runs.
|
|
745
|
+
|
|
746
|
+
### Project-local generative model
|
|
747
|
+
|
|
748
|
+
```bash
|
|
749
|
+
vlm-lab download-model \
|
|
750
|
+
--model-size 2b \
|
|
751
|
+
--output models/Qwen3-VL-2B-Instruct
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
Then:
|
|
755
|
+
|
|
756
|
+
```bash
|
|
757
|
+
HF_HUB_OFFLINE=1 vlm-lab describe \
|
|
758
|
+
data/diagram_random_clean.png \
|
|
759
|
+
--model-path models/Qwen3-VL-2B-Instruct \
|
|
760
|
+
--prompt "Describe this image accurately."
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Project-local embedding model
|
|
764
|
+
|
|
765
|
+
```bash
|
|
766
|
+
vlm-lab download-model \
|
|
767
|
+
--embedding-size 2b \
|
|
768
|
+
--output models/Qwen3-VL-Embedding-2B
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
Example offline use:
|
|
772
|
+
|
|
773
|
+
```bash
|
|
774
|
+
HF_HUB_OFFLINE=1 vlm-lab embed \
|
|
775
|
+
--text "architecture diagram" \
|
|
776
|
+
--model-path models/Qwen3-VL-Embedding-2B
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Project-local reranker
|
|
780
|
+
|
|
781
|
+
```bash
|
|
782
|
+
vlm-lab download-model \
|
|
783
|
+
--reranker-size 2b \
|
|
784
|
+
--output models/Qwen3-VL-Reranker-2B
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
The package does **not** delete a user's shared Hugging Face cache automatically.
|
|
788
|
+
|
|
789
|
+
Model directories under `models/` are intended for local deployment and remain outside source control.
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## 8. Advanced model-loading options
|
|
794
|
+
|
|
795
|
+
### `trust_remote_code`
|
|
796
|
+
|
|
797
|
+
Default: **False**.
|
|
798
|
+
|
|
799
|
+
Only enable repository-provided Python code for model repositories you have reviewed and trust.
|
|
800
|
+
|
|
801
|
+
### Device / cache / revision
|
|
802
|
+
|
|
803
|
+
The Qwen wrappers support configurable model-loading behavior appropriate to their backend, including device selection, cache location, revision selection, and local-only loading.
|
|
804
|
+
|
|
805
|
+
For generative inference:
|
|
806
|
+
|
|
807
|
+
```python
|
|
808
|
+
from vlm_engineering import QwenVLModel
|
|
809
|
+
|
|
810
|
+
model = QwenVLModel(
|
|
811
|
+
model_size="4b",
|
|
812
|
+
device_map="auto",
|
|
813
|
+
dtype="auto",
|
|
814
|
+
)
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
For large multimodal retrieval models, conservative defaults are preferred. Embedding/reranking default to small batch sizes to reduce accidental memory pressure.
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
## 9. Core Qwen3-VL use cases
|
|
822
|
+
|
|
823
|
+
### Image captioning / rich description
|
|
824
|
+
|
|
825
|
+
```bash
|
|
826
|
+
vlm-lab describe photo.jpg \
|
|
827
|
+
--prompt "Write a factual, detailed description."
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### Visual question answering
|
|
831
|
+
|
|
832
|
+
```bash
|
|
833
|
+
vlm-lab describe dashboard.png \
|
|
834
|
+
--model-size 4b \
|
|
835
|
+
--prompt "Which metric increased the most and what visual evidence supports it?"
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
### Diagram / architecture understanding
|
|
839
|
+
|
|
840
|
+
```bash
|
|
841
|
+
vlm-lab analyze architecture.png \
|
|
842
|
+
--model-size 4b
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### Screenshot / UI understanding
|
|
846
|
+
|
|
847
|
+
Use `describe` or `analyze` for dashboards, application screenshots, forms, and technical UI states.
|
|
848
|
+
|
|
849
|
+
### Tables and document pages
|
|
850
|
+
|
|
851
|
+
Render a page as an image and request structured extraction. For production document workflows, preserve native/OCR text separately and use the VLM for layout, grouping, relationships, and visual semantics.
|
|
852
|
+
|
|
853
|
+
---
|
|
854
|
+
|
|
855
|
+
## 10. VLM + OCR/native parsing: hybrid by default
|
|
856
|
+
|
|
857
|
+
```text
|
|
858
|
+
Native extraction / OCR Qwen3-VL
|
|
859
|
+
exact text, IDs, numbers layout, arrows, groups,
|
|
860
|
+
long deterministic text relationships, visual meaning
|
|
861
|
+
\ /
|
|
862
|
+
\ /
|
|
863
|
+
fusion
|
|
864
|
+
|
|
|
865
|
+
RAG-ready chunk
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
A VLM can replace OCR in some workflows, but robust document pipelines often preserve native/OCR evidence for literal accuracy while adding VLM semantics where visual structure matters.
|
|
869
|
+
|
|
870
|
+
---
|
|
871
|
+
|
|
872
|
+
## 11. Build a RAG-ready visual chunk
|
|
873
|
+
|
|
874
|
+
```bash
|
|
875
|
+
vlm-lab chunk data/page_001.png \
|
|
876
|
+
--model-size 4b \
|
|
877
|
+
--document-id architecture-v1 \
|
|
878
|
+
--source-file architecture.pdf \
|
|
879
|
+
--page 1 \
|
|
880
|
+
--native-text-file data/page_001.txt
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
The resulting `VisualChunk` preserves traceability such as document ID, source file, page, image reference, entities, relations, metadata, and fused retrieval text.
|
|
884
|
+
|
|
885
|
+
---
|
|
886
|
+
|
|
887
|
+
## 12. Visual RAG with a text embedder
|
|
888
|
+
|
|
889
|
+
```text
|
|
890
|
+
image/page
|
|
891
|
+
↓
|
|
892
|
+
Qwen3-VL description / structured analysis
|
|
893
|
+
↓
|
|
894
|
+
retrieval text
|
|
895
|
+
↓
|
|
896
|
+
Sentence Transformers text embedder
|
|
897
|
+
↓
|
|
898
|
+
vector index / vector database
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
This path is useful when an existing RAG platform supports only text embeddings.
|
|
902
|
+
|
|
903
|
+
```bash
|
|
904
|
+
python examples/text_only_visual_rag.py
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
The original `image_ref` is retained so the final VLM can inspect the original image rather than relying only on generated descriptions.
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
## 13. Multimodal retrieval and RAG — v0.3.1
|
|
912
|
+
|
|
913
|
+
### 13.1 Installation
|
|
914
|
+
|
|
915
|
+
```bash
|
|
916
|
+
python -m pip install -e ".[qwen-retrieval,config]"
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
The retrieval stack is designed around:
|
|
920
|
+
|
|
921
|
+
```text
|
|
922
|
+
Qwen3-VL-Embedding
|
|
923
|
+
↓
|
|
924
|
+
candidate recall
|
|
925
|
+
↓
|
|
926
|
+
Qwen3-VL-Reranker
|
|
927
|
+
↓
|
|
928
|
+
precision-oriented reranking
|
|
929
|
+
↓
|
|
930
|
+
Qwen3-VL generation
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### 13.2 Multimodal input contract
|
|
934
|
+
|
|
935
|
+
The public retrieval contract accepts:
|
|
936
|
+
|
|
937
|
+
```python
|
|
938
|
+
{"text": "..."}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
```python
|
|
942
|
+
{"image": "path/to/image.png"}
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
```python
|
|
946
|
+
{
|
|
947
|
+
"text": "Find diagrams similar to this",
|
|
948
|
+
"image": "path/to/query.png",
|
|
949
|
+
}
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
Plain text strings may be accepted as shorthand where documented.
|
|
953
|
+
|
|
954
|
+
Local images are validated before expensive inference where practical. Unknown fields, empty inputs, missing files, directories passed as images, corrupt files, and unsupported inputs fail early with readable errors.
|
|
955
|
+
|
|
956
|
+
### 13.3 Embeddings
|
|
957
|
+
|
|
958
|
+
```python
|
|
959
|
+
from vlm_engineering import QwenVLEmbedder
|
|
960
|
+
|
|
961
|
+
embedder = QwenVLEmbedder(
|
|
962
|
+
model_size="2b",
|
|
963
|
+
dimensions=1024,
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
text_vector = embedder.embed_text(
|
|
967
|
+
"architecture diagram"
|
|
968
|
+
)
|
|
969
|
+
|
|
970
|
+
image_vector = embedder.embed_image(
|
|
971
|
+
"data/diagram_random_clean.png"
|
|
972
|
+
)
|
|
973
|
+
|
|
974
|
+
vectors = embedder.encode(
|
|
975
|
+
[
|
|
976
|
+
{"text": "Redis caching"},
|
|
977
|
+
{"image": "data/diagram_random_clean.png"},
|
|
978
|
+
{
|
|
979
|
+
"text": "Architecture",
|
|
980
|
+
"image": "data/diagram_random_clean.png",
|
|
981
|
+
},
|
|
982
|
+
]
|
|
983
|
+
)
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
Embeddings are normalized by default where configured by the wrapper.
|
|
987
|
+
|
|
988
|
+
Supported Qwen embedding presets expose the dimensions supported by their model contract. Invalid dimensions are rejected before inference.
|
|
989
|
+
|
|
990
|
+
### 13.4 Reranking
|
|
991
|
+
|
|
992
|
+
```python
|
|
993
|
+
from vlm_engineering import QwenVLReranker
|
|
994
|
+
|
|
995
|
+
documents = [
|
|
996
|
+
{"text": "Redis provides caching."},
|
|
997
|
+
{"text": "PostgreSQL stores persistent state."},
|
|
998
|
+
{"image": "data/diagram_random_clean.png"},
|
|
999
|
+
]
|
|
1000
|
+
|
|
1001
|
+
reranker = QwenVLReranker(model_size="2b")
|
|
1002
|
+
|
|
1003
|
+
scores = reranker.score(
|
|
1004
|
+
"Find caching evidence",
|
|
1005
|
+
documents,
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
ranked = reranker.rerank(
|
|
1009
|
+
"Find caching evidence",
|
|
1010
|
+
documents,
|
|
1011
|
+
top_k=2,
|
|
1012
|
+
)
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
Ranking preserves the original candidate object in each result.
|
|
1016
|
+
|
|
1017
|
+
Where normalized scoring is requested, the transformed score should be interpreted as a relevance score rather than a calibrated probability.
|
|
1018
|
+
|
|
1019
|
+
### 13.5 Full multimodal RAG
|
|
1020
|
+
|
|
1021
|
+
```python
|
|
1022
|
+
from vlm_engineering import (
|
|
1023
|
+
MultimodalRAGPipeline,
|
|
1024
|
+
QwenVLEmbedder,
|
|
1025
|
+
QwenVLModel,
|
|
1026
|
+
QwenVLReranker,
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
pipeline = MultimodalRAGPipeline(
|
|
1030
|
+
embedder=QwenVLEmbedder(model_size="2b"),
|
|
1031
|
+
reranker=QwenVLReranker(model_size="2b"),
|
|
1032
|
+
generator=QwenVLModel(model_size="2b"),
|
|
1033
|
+
candidate_k=12,
|
|
1034
|
+
top_k=3,
|
|
1035
|
+
max_new_tokens=256,
|
|
1036
|
+
)
|
|
1037
|
+
|
|
1038
|
+
pipeline.index_chunks(chunks)
|
|
1039
|
+
|
|
1040
|
+
answer = pipeline.answer(
|
|
1041
|
+
"How does the cache interact with the rest of the architecture?"
|
|
1042
|
+
)
|
|
1043
|
+
|
|
1044
|
+
print(answer.answer)
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
Important pipeline behavior:
|
|
1048
|
+
|
|
1049
|
+
- `candidate_k >= top_k > 0`
|
|
1050
|
+
- re-indexing replaces previous in-memory state
|
|
1051
|
+
- empty re-indexing clears the previous collection
|
|
1052
|
+
- failed indexing does not intentionally leave a half-built index
|
|
1053
|
+
- original document/page/source metadata is preserved
|
|
1054
|
+
- original image references remain available for final grounding
|
|
1055
|
+
- retrieved evidence is reranked before generation
|
|
1056
|
+
- missing visual evidence is reported explicitly when the generation path requires it
|
|
1057
|
+
|
|
1058
|
+
### 13.6 Multimodal retrieval queries
|
|
1059
|
+
|
|
1060
|
+
A query can include text and image evidence where supported:
|
|
1061
|
+
|
|
1062
|
+
```python
|
|
1063
|
+
results = pipeline.retrieve(
|
|
1064
|
+
{
|
|
1065
|
+
"text": "Find architecture similar to this diagram.",
|
|
1066
|
+
"image": "data/query_diagram.png",
|
|
1067
|
+
}
|
|
1068
|
+
)
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
### 13.7 CLI
|
|
1072
|
+
|
|
1073
|
+
Embedding text:
|
|
1074
|
+
|
|
1075
|
+
```bash
|
|
1076
|
+
vlm-lab embed \
|
|
1077
|
+
--text "Find a cache diagram" \
|
|
1078
|
+
--model-size 2b \
|
|
1079
|
+
--dimensions 1024
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
Embedding an image:
|
|
1083
|
+
|
|
1084
|
+
```bash
|
|
1085
|
+
vlm-lab embed \
|
|
1086
|
+
--image data/diagram_random_clean.png \
|
|
1087
|
+
--model-size 2b
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
Reranking:
|
|
1091
|
+
|
|
1092
|
+
```bash
|
|
1093
|
+
vlm-lab rerank \
|
|
1094
|
+
--query "Find the architecture" \
|
|
1095
|
+
--documents-json candidates.json \
|
|
1096
|
+
--model-size 2b
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
### 13.8 Runtime YAML
|
|
1100
|
+
|
|
1101
|
+
Example profile:
|
|
1102
|
+
|
|
1103
|
+
```yaml
|
|
1104
|
+
version: 1
|
|
1105
|
+
|
|
1106
|
+
embedding:
|
|
1107
|
+
model:
|
|
1108
|
+
model_size: 2b
|
|
1109
|
+
revision: null
|
|
1110
|
+
device: null
|
|
1111
|
+
cache_folder: null
|
|
1112
|
+
trust_remote_code: false
|
|
1113
|
+
|
|
1114
|
+
batch_size: 1
|
|
1115
|
+
dimensions: 1024
|
|
1116
|
+
normalize_embeddings: true
|
|
1117
|
+
|
|
1118
|
+
reranker:
|
|
1119
|
+
model:
|
|
1120
|
+
model_size: 2b
|
|
1121
|
+
revision: null
|
|
1122
|
+
device: null
|
|
1123
|
+
cache_folder: null
|
|
1124
|
+
trust_remote_code: false
|
|
1125
|
+
|
|
1126
|
+
batch_size: 1
|
|
1127
|
+
normalize_scores: true
|
|
1128
|
+
|
|
1129
|
+
rag:
|
|
1130
|
+
top_k: 3
|
|
1131
|
+
candidate_k: 12
|
|
1132
|
+
max_new_tokens: 256
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
Validate without loading model weights:
|
|
1136
|
+
|
|
1137
|
+
```bash
|
|
1138
|
+
vlm-lab validate-config \
|
|
1139
|
+
configs/qwen_multimodal_rag.example.yaml
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
The configuration layer:
|
|
1143
|
+
|
|
1144
|
+
- uses safe YAML loading
|
|
1145
|
+
- has an explicit schema version
|
|
1146
|
+
- validates field types
|
|
1147
|
+
- rejects unknown keys
|
|
1148
|
+
- rejects conflicting model selectors
|
|
1149
|
+
- validates embedding dimensions where possible without model loading
|
|
1150
|
+
- rejects invalid `candidate_k` / `top_k` combinations
|
|
1151
|
+
|
|
1152
|
+
Example Python usage:
|
|
1153
|
+
|
|
1154
|
+
```python
|
|
1155
|
+
from vlm_engineering import QwenVLModel
|
|
1156
|
+
from vlm_engineering.runtime_config import load_runtime_config
|
|
1157
|
+
|
|
1158
|
+
config = load_runtime_config(
|
|
1159
|
+
"configs/qwen_multimodal_rag.example.yaml"
|
|
1160
|
+
)
|
|
1161
|
+
|
|
1162
|
+
pipeline = config.build_pipeline(
|
|
1163
|
+
QwenVLModel(model_size="2b")
|
|
1164
|
+
)
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
### 13.9 Reliability and operational errors
|
|
1168
|
+
|
|
1169
|
+
v0.3.1 strengthens user-facing validation around common operational failures, including:
|
|
1170
|
+
|
|
1171
|
+
- missing optional dependencies
|
|
1172
|
+
- invalid multimodal input
|
|
1173
|
+
- missing/corrupt image files
|
|
1174
|
+
- invalid local model directories
|
|
1175
|
+
- incomplete local model snapshots where detectable
|
|
1176
|
+
- Hugging Face authentication/offline failures
|
|
1177
|
+
- resource / out-of-memory failures
|
|
1178
|
+
- disk-space failures
|
|
1179
|
+
- invalid embedding dimensions
|
|
1180
|
+
- unexpected embedding shapes
|
|
1181
|
+
- NaN / Inf embeddings
|
|
1182
|
+
- invalid reranker outputs
|
|
1183
|
+
- NaN / Inf reranker scores
|
|
1184
|
+
- malformed YAML profiles
|
|
1185
|
+
|
|
1186
|
+
Expected operational problems are mapped to readable errors where possible. Unexpected programming errors should remain visible for debugging rather than being silently swallowed.
|
|
1187
|
+
|
|
1188
|
+
### 13.10 Resource safety
|
|
1189
|
+
|
|
1190
|
+
Qwen multimodal retrieval models are large.
|
|
1191
|
+
|
|
1192
|
+
The retrieval wrappers use conservative batch defaults such as:
|
|
1193
|
+
|
|
1194
|
+
```text
|
|
1195
|
+
batch_size = 1
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
Users with stronger hardware can tune batching after validating their memory envelope.
|
|
1199
|
+
|
|
1200
|
+
For practical workloads:
|
|
1201
|
+
|
|
1202
|
+
- GPU execution is recommended
|
|
1203
|
+
- CPU execution can be substantially slower
|
|
1204
|
+
- backend offloading may use CPU or disk depending on the model/backend
|
|
1205
|
+
- multiple images and high-resolution inputs increase memory requirements
|
|
1206
|
+
- local model storage requires several gigabytes depending on the selected model
|
|
1207
|
+
|
|
1208
|
+
### 13.11 Real-model validation
|
|
1209
|
+
|
|
1210
|
+
Large real-model tests are opt-in and excluded from normal PR CI.
|
|
1211
|
+
|
|
1212
|
+
Run:
|
|
1213
|
+
|
|
1214
|
+
```bash
|
|
1215
|
+
VLM_RUN_REAL_MODEL_TESTS=1 \
|
|
1216
|
+
python -m pytest \
|
|
1217
|
+
-m real_model \
|
|
1218
|
+
tests/e2e \
|
|
1219
|
+
-v
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
Using explicit local/offline models:
|
|
1223
|
+
|
|
1224
|
+
```bash
|
|
1225
|
+
export VLM_QWEN_EMBEDDING_MODEL_PATH=\
|
|
1226
|
+
models/Qwen3-VL-Embedding-2B
|
|
1227
|
+
|
|
1228
|
+
export VLM_QWEN_RERANKER_MODEL_PATH=\
|
|
1229
|
+
models/Qwen3-VL-Reranker-2B
|
|
1230
|
+
|
|
1231
|
+
export VLM_RUN_REAL_MODEL_TESTS=1
|
|
1232
|
+
|
|
1233
|
+
python -m pytest \
|
|
1234
|
+
-m real_model \
|
|
1235
|
+
tests/e2e \
|
|
1236
|
+
-v
|
|
1237
|
+
```
|
|
1238
|
+
|
|
1239
|
+
Normal CI relies on deterministic fake/injected backends rather than downloading multi-gigabyte weights.
|
|
1240
|
+
|
|
1241
|
+
### 13.12 Migration from v0.2.x
|
|
1242
|
+
|
|
1243
|
+
Where retained by the package, earlier retrieval class names remain compatibility aliases.
|
|
1244
|
+
|
|
1245
|
+
New code should prefer:
|
|
1246
|
+
|
|
1247
|
+
```python
|
|
1248
|
+
from vlm_engineering import (
|
|
1249
|
+
QwenVLEmbedder,
|
|
1250
|
+
QwenVLReranker,
|
|
1251
|
+
MultimodalRAGPipeline,
|
|
1252
|
+
)
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
For image inputs, prefer explicit multimodal objects:
|
|
1256
|
+
|
|
1257
|
+
```python
|
|
1258
|
+
{"image": "path/to/image.png"}
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
rather than ambiguous bare path strings.
|
|
1262
|
+
|
|
1263
|
+
---
|
|
1264
|
+
|
|
1265
|
+
## 14. CLIP foundations
|
|
1266
|
+
|
|
1267
|
+
```python
|
|
1268
|
+
from PIL import Image
|
|
1269
|
+
|
|
1270
|
+
from vlm_engineering import CLIPEncoder
|
|
1271
|
+
|
|
1272
|
+
image = Image.open(
|
|
1273
|
+
"data/diagram_random_clean.png"
|
|
1274
|
+
).convert("RGB")
|
|
1275
|
+
|
|
1276
|
+
clip = CLIPEncoder()
|
|
1277
|
+
|
|
1278
|
+
result = clip.zero_shot_classify(
|
|
1279
|
+
image,
|
|
1280
|
+
["cat", "dog", "airplane"],
|
|
1281
|
+
)
|
|
1282
|
+
|
|
1283
|
+
print(result)
|
|
1284
|
+
```
|
|
1285
|
+
|
|
1286
|
+
CLIP is useful for learning the shared image/text embedding-space idea that modern multimodal retrieval builds upon.
|
|
1287
|
+
|
|
1288
|
+
---
|
|
1289
|
+
|
|
1290
|
+
## 15. Examples and application scenarios
|
|
1291
|
+
|
|
1292
|
+
Small API examples:
|
|
1293
|
+
|
|
1294
|
+
```text
|
|
1295
|
+
examples/clip_zero_shot.py
|
|
1296
|
+
examples/qwen_describe_image.py
|
|
1297
|
+
examples/qwen_model_selection.py
|
|
1298
|
+
examples/qwen_local_model.py
|
|
1299
|
+
examples/structured_visual_chunk.py
|
|
1300
|
+
examples/text_only_visual_rag.py
|
|
1301
|
+
examples/qwen_multimodal_retrieval.py
|
|
1302
|
+
```
|
|
1303
|
+
|
|
1304
|
+
End-to-end scenarios:
|
|
1305
|
+
|
|
1306
|
+
```text
|
|
1307
|
+
scenarios/scenario_01_image_captioning.py
|
|
1308
|
+
scenarios/scenario_02_visual_question_answering.py
|
|
1309
|
+
scenarios/scenario_03_diagram_to_json.py
|
|
1310
|
+
scenarios/scenario_04_document_page_to_rag_chunk.py
|
|
1311
|
+
scenarios/scenario_05_text_only_visual_rag.py
|
|
1312
|
+
scenarios/scenario_06_true_multimodal_retrieval.py
|
|
1313
|
+
scenarios/scenario_07_compare_qwen_presets.py
|
|
1314
|
+
scenarios/scenario_08_hub_vs_local_loading.py
|
|
1315
|
+
scenarios/scenario_09_batch_structured_analysis.py
|
|
1316
|
+
```
|
|
1317
|
+
|
|
1318
|
+
See [`scenarios/README.md`](scenarios/README.md) for copy/paste commands, model guidance, RAG patterns, batch processing, and offline usage.
|
|
1319
|
+
|
|
1320
|
+
---
|
|
1321
|
+
|
|
1322
|
+
## 16. Notebooks
|
|
1323
|
+
|
|
1324
|
+
Notebooks are analysis clients, not the implementation layer:
|
|
1325
|
+
|
|
1326
|
+
```text
|
|
1327
|
+
00_clip_foundations.ipynb
|
|
1328
|
+
01_qwen3_vl_quickstart.ipynb
|
|
1329
|
+
02_visual_document_understanding.ipynb
|
|
1330
|
+
03_multimodal_rag.ipynb
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
Reusable logic belongs under `src/vlm_engineering/` and can be used without Jupyter.
|
|
1334
|
+
|
|
1335
|
+
---
|
|
1336
|
+
|
|
1337
|
+
## 17. Docker and Docker Compose
|
|
1338
|
+
|
|
1339
|
+
The repository includes `Dockerfile` and `docker-compose.yml` for reproducible CLI/container workflows.
|
|
1340
|
+
|
|
1341
|
+
The normal image does not bake model weights into the container.
|
|
1342
|
+
|
|
1343
|
+
### Build
|
|
1344
|
+
|
|
1345
|
+
```bash
|
|
1346
|
+
docker compose build
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
### CLI
|
|
1350
|
+
|
|
1351
|
+
```bash
|
|
1352
|
+
docker compose run --rm vlm-lab --help
|
|
1353
|
+
docker compose run --rm vlm-lab models
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
### Mounted directories
|
|
1357
|
+
|
|
1358
|
+
```text
|
|
1359
|
+
Host Container
|
|
1360
|
+
./data /app/data
|
|
1361
|
+
./models /app/models
|
|
1362
|
+
hf-cache named volume /home/appuser/.cache/huggingface
|
|
1363
|
+
```
|
|
1364
|
+
|
|
1365
|
+
Explicit model directories under `./models` therefore survive one-shot container removal.
|
|
1366
|
+
|
|
1367
|
+
### Generative inference
|
|
1368
|
+
|
|
1369
|
+
```bash
|
|
1370
|
+
docker compose run --rm vlm-lab \
|
|
1371
|
+
describe data/diagram_random_clean.png \
|
|
1372
|
+
--model-size 2b
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
### Download a local model
|
|
1376
|
+
|
|
1377
|
+
```bash
|
|
1378
|
+
docker compose run --rm vlm-lab \
|
|
1379
|
+
download-model \
|
|
1380
|
+
--model-size 2b \
|
|
1381
|
+
--output models/Qwen3-VL-2B-Instruct
|
|
1382
|
+
```
|
|
1383
|
+
|
|
1384
|
+
### Offline inference
|
|
1385
|
+
|
|
1386
|
+
```bash
|
|
1387
|
+
docker compose run --rm \
|
|
1388
|
+
-e HF_HUB_OFFLINE=1 \
|
|
1389
|
+
vlm-lab \
|
|
1390
|
+
describe data/diagram_random_clean.png \
|
|
1391
|
+
--model-path models/Qwen3-VL-2B-Instruct
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
### Retrieval model downloads
|
|
1395
|
+
|
|
1396
|
+
```bash
|
|
1397
|
+
docker compose run --rm vlm-lab \
|
|
1398
|
+
download-model \
|
|
1399
|
+
--embedding-size 2b \
|
|
1400
|
+
--output models/Qwen3-VL-Embedding-2B
|
|
1401
|
+
```
|
|
1402
|
+
|
|
1403
|
+
```bash
|
|
1404
|
+
docker compose run --rm vlm-lab \
|
|
1405
|
+
download-model \
|
|
1406
|
+
--reranker-size 2b \
|
|
1407
|
+
--output models/Qwen3-VL-Reranker-2B
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
### Hugging Face authentication
|
|
1411
|
+
|
|
1412
|
+
Pass credentials at runtime rather than baking them into the image:
|
|
1413
|
+
|
|
1414
|
+
```bash
|
|
1415
|
+
export HF_TOKEN="..."
|
|
1416
|
+
|
|
1417
|
+
docker compose run --rm \
|
|
1418
|
+
-e HF_TOKEN \
|
|
1419
|
+
vlm-lab \
|
|
1420
|
+
describe data/diagram_random_clean.png \
|
|
1421
|
+
--model-id your-org/your-compatible-model
|
|
1422
|
+
```
|
|
1423
|
+
|
|
1424
|
+
Do not commit tokens.
|
|
1425
|
+
|
|
1426
|
+
### Shell debugging
|
|
1427
|
+
|
|
1428
|
+
```bash
|
|
1429
|
+
docker compose run --rm \
|
|
1430
|
+
--entrypoint /bin/sh \
|
|
1431
|
+
vlm-lab
|
|
1432
|
+
```
|
|
1433
|
+
|
|
1434
|
+
### Direct Docker usage
|
|
1435
|
+
|
|
1436
|
+
```bash
|
|
1437
|
+
docker build \
|
|
1438
|
+
-t vision-language-engineering-lab:0.3.2 \
|
|
1439
|
+
.
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
```bash
|
|
1443
|
+
docker run --rm \
|
|
1444
|
+
vision-language-engineering-lab:0.3.2 \
|
|
1445
|
+
models
|
|
1446
|
+
```
|
|
1447
|
+
|
|
1448
|
+
### GPU execution
|
|
1449
|
+
|
|
1450
|
+
The checked-in Docker setup is a portable baseline and does not force a particular GPU runtime.
|
|
1451
|
+
|
|
1452
|
+
Configure the appropriate container GPU runtime on the host and verify GPU visibility before relying on Qwen workloads.
|
|
1453
|
+
|
|
1454
|
+
---
|
|
1455
|
+
|
|
1456
|
+
## 18. Quality gates and compatibility validation
|
|
1457
|
+
|
|
1458
|
+
Before pushing changes:
|
|
1459
|
+
|
|
1460
|
+
```bash
|
|
1461
|
+
python -m pip check
|
|
1462
|
+
python -m ruff check .
|
|
1463
|
+
python -m mypy .
|
|
1464
|
+
python -m pytest \
|
|
1465
|
+
-m "not real_model" \
|
|
1466
|
+
--cov=vlm_engineering \
|
|
1467
|
+
--cov-report=term-missing
|
|
1468
|
+
python -m pip_audit .
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
The project uses:
|
|
1472
|
+
|
|
1473
|
+
| Tool | Purpose |
|
|
1474
|
+
|---|---|
|
|
1475
|
+
| Ruff | linting, import organization, source-quality rules |
|
|
1476
|
+
| mypy | static type checking |
|
|
1477
|
+
| pytest | functional, integration, and contract testing |
|
|
1478
|
+
| pytest-cov | coverage enforcement |
|
|
1479
|
+
| `pip check` | installed dependency consistency |
|
|
1480
|
+
| `pip-audit` | known dependency-vulnerability auditing |
|
|
1481
|
+
|
|
1482
|
+
For the v0.3.2 release branch, deterministic validation reached:
|
|
1483
|
+
|
|
1484
|
+
```text
|
|
1485
|
+
347 passed
|
|
1486
|
+
2 skipped (opt-in real-model tests)
|
|
1487
|
+
95% coverage
|
|
1488
|
+
pip check: no broken requirements
|
|
1489
|
+
pip-audit: no known vulnerabilities
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
The deselected tests are resource-heavy / opt-in real-model validations rather than normal CI tests.
|
|
1493
|
+
|
|
1494
|
+
CI validates Python 3.11, 3.12, and 3.13 while avoiding automatic multi-gigabyte model downloads in ordinary pull requests.
|
|
1495
|
+
|
|
1496
|
+
---
|
|
1497
|
+
|
|
1498
|
+
## 19. Troubleshooting
|
|
1499
|
+
|
|
1500
|
+
### `ModuleNotFoundError`
|
|
1501
|
+
|
|
1502
|
+
Activate the environment and install the appropriate extra:
|
|
1503
|
+
|
|
1504
|
+
```bash
|
|
1505
|
+
source .venv/bin/activate
|
|
1506
|
+
python -m pip install -e ".[all]"
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
### `vlm-lab: command not found`
|
|
1510
|
+
|
|
1511
|
+
```bash
|
|
1512
|
+
python -m pip show vision-language-engineering-lab
|
|
1513
|
+
which python
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
Then reinstall:
|
|
1517
|
+
|
|
1518
|
+
```bash
|
|
1519
|
+
python -m pip install -e ".[all]"
|
|
1520
|
+
```
|
|
1521
|
+
|
|
1522
|
+
### Missing Qwen retrieval dependencies
|
|
1523
|
+
|
|
1524
|
+
```bash
|
|
1525
|
+
python -m pip install -e ".[qwen-retrieval]"
|
|
1526
|
+
```
|
|
1527
|
+
|
|
1528
|
+
### Missing `torchvision`
|
|
1529
|
+
|
|
1530
|
+
Install the relevant package extra rather than adding ad-hoc dependencies individually:
|
|
1531
|
+
|
|
1532
|
+
```bash
|
|
1533
|
+
python -m pip install -e ".[qwen]"
|
|
1534
|
+
```
|
|
1535
|
+
|
|
1536
|
+
or:
|
|
1537
|
+
|
|
1538
|
+
```bash
|
|
1539
|
+
python -m pip install -e ".[qwen-retrieval]"
|
|
1540
|
+
```
|
|
1541
|
+
|
|
1542
|
+
### CUDA / RAM / out-of-memory error
|
|
1543
|
+
|
|
1544
|
+
Start with 2B models and batch size 1.
|
|
1545
|
+
|
|
1546
|
+
Reduce:
|
|
1547
|
+
|
|
1548
|
+
- batch size
|
|
1549
|
+
- image resolution
|
|
1550
|
+
- number of simultaneous images
|
|
1551
|
+
- output token length
|
|
1552
|
+
|
|
1553
|
+
or move to hardware with more RAM/VRAM.
|
|
1554
|
+
|
|
1555
|
+
### Model offloading is slow
|
|
1556
|
+
|
|
1557
|
+
Automatic device placement can offload weights to CPU or disk on constrained machines. This is expected to be slower than running fully on an adequately sized GPU.
|
|
1558
|
+
|
|
1559
|
+
### First run is slow
|
|
1560
|
+
|
|
1561
|
+
The Hub may be downloading model weights.
|
|
1562
|
+
|
|
1563
|
+
For predictable storage:
|
|
1564
|
+
|
|
1565
|
+
```bash
|
|
1566
|
+
vlm-lab download-model \
|
|
1567
|
+
--model-size 2b
|
|
1568
|
+
```
|
|
1569
|
+
|
|
1570
|
+
Then use `--model-path`.
|
|
1571
|
+
|
|
1572
|
+
### Fully offline inference
|
|
1573
|
+
|
|
1574
|
+
Download the model first, then use a local model path and optionally:
|
|
1575
|
+
|
|
1576
|
+
```bash
|
|
1577
|
+
export HF_HUB_OFFLINE=1
|
|
1578
|
+
```
|
|
1579
|
+
|
|
1580
|
+
### Invalid/corrupt image
|
|
1581
|
+
|
|
1582
|
+
Local image validation intentionally fails early when possible.
|
|
1583
|
+
|
|
1584
|
+
Verify:
|
|
1585
|
+
|
|
1586
|
+
```bash
|
|
1587
|
+
file path/to/image.png
|
|
1588
|
+
```
|
|
1589
|
+
|
|
1590
|
+
and re-export the source image if necessary.
|
|
1591
|
+
|
|
1592
|
+
### Hugging Face authentication
|
|
1593
|
+
|
|
1594
|
+
For gated/private models, set:
|
|
1595
|
+
|
|
1596
|
+
```bash
|
|
1597
|
+
export HF_TOKEN="..."
|
|
1598
|
+
```
|
|
1599
|
+
|
|
1600
|
+
Do not commit credentials.
|
|
1601
|
+
|
|
1602
|
+
### Invalid YAML configuration
|
|
1603
|
+
|
|
1604
|
+
Validate before starting models:
|
|
1605
|
+
|
|
1606
|
+
```bash
|
|
1607
|
+
vlm-lab validate-config \
|
|
1608
|
+
configs/qwen_multimodal_rag.example.yaml
|
|
1609
|
+
```
|
|
1610
|
+
|
|
1611
|
+
### Dependency compatibility
|
|
1612
|
+
|
|
1613
|
+
```bash
|
|
1614
|
+
python -m pip check
|
|
1615
|
+
python -m pip show \
|
|
1616
|
+
sentence-transformers \
|
|
1617
|
+
torch \
|
|
1618
|
+
torchvision \
|
|
1619
|
+
transformers
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
### Which Qwen model should I choose?
|
|
1623
|
+
|
|
1624
|
+
Start with the 2B preset while validating the pipeline.
|
|
1625
|
+
|
|
1626
|
+
Move to larger models only when evaluation data demonstrates that the quality gain justifies the additional hardware cost.
|
|
1627
|
+
|
|
1628
|
+
---
|
|
1629
|
+
|
|
1630
|
+
## 20. Contributing
|
|
1631
|
+
|
|
1632
|
+
**Contributions are welcome.**
|
|
1633
|
+
|
|
1634
|
+
The project is intended to grow as an open Vision-Language engineering lab.
|
|
1635
|
+
|
|
1636
|
+
Useful contribution areas include:
|
|
1637
|
+
|
|
1638
|
+
- VLM integrations
|
|
1639
|
+
- multimodal retrieval strategies
|
|
1640
|
+
- document-understanding pipelines
|
|
1641
|
+
- RAG evaluation
|
|
1642
|
+
- persistent vector-database adapters
|
|
1643
|
+
- performance and memory improvements
|
|
1644
|
+
- deterministic and real-model testing
|
|
1645
|
+
- Docker/deployment improvements
|
|
1646
|
+
- documentation
|
|
1647
|
+
- application scenarios
|
|
1648
|
+
- bug fixes
|
|
1649
|
+
|
|
1650
|
+
### Contribution workflow
|
|
1651
|
+
|
|
1652
|
+
1. Fork or clone the repository.
|
|
1653
|
+
2. Create a focused branch.
|
|
1654
|
+
3. Implement the change.
|
|
1655
|
+
4. Add/update tests.
|
|
1656
|
+
5. Run quality gates.
|
|
1657
|
+
6. Push the branch.
|
|
1658
|
+
7. Open a Pull Request.
|
|
1659
|
+
|
|
1660
|
+
Example:
|
|
1661
|
+
|
|
1662
|
+
```bash
|
|
1663
|
+
git clone https://github.com/helmighanmi/vision-language-engineering-lab.git
|
|
1664
|
+
cd vision-language-engineering-lab
|
|
1665
|
+
|
|
1666
|
+
python -m venv .venv
|
|
1667
|
+
source .venv/bin/activate
|
|
1668
|
+
|
|
1669
|
+
python -m pip install --upgrade pip
|
|
1670
|
+
python -m pip install -e ".[all,dev]"
|
|
1671
|
+
```
|
|
1672
|
+
|
|
1673
|
+
Validation:
|
|
1674
|
+
|
|
1675
|
+
```bash
|
|
1676
|
+
python -m pip check
|
|
1677
|
+
python -m ruff check .
|
|
1678
|
+
python -m mypy .
|
|
1679
|
+
python -m pytest \
|
|
1680
|
+
-m "not real_model" \
|
|
1681
|
+
--cov=vlm_engineering \
|
|
1682
|
+
--cov-report=term-missing
|
|
1683
|
+
python -m pip_audit .
|
|
1684
|
+
```
|
|
1685
|
+
|
|
1686
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the complete contribution workflow.
|
|
1687
|
+
|
|
1688
|
+
Questions, feature proposals, documentation improvements, and bug reports are welcome through GitHub Issues.
|
|
1689
|
+
|
|
1690
|
+
---
|
|
1691
|
+
|
|
1692
|
+
## 21. Documentation
|
|
1693
|
+
|
|
1694
|
+
- `docs/model-loading.md` — model presets, Hub/custom/local modes and troubleshooting
|
|
1695
|
+
- `docs/qwen-model-selection.md` — model-size selection and resource guidance
|
|
1696
|
+
- `docs/rag-patterns.md` — visual and multimodal RAG patterns
|
|
1697
|
+
- `docs/multimodal-retrieval.md` — Qwen embedding/reranking design and resource guidance
|
|
1698
|
+
- `docs/architecture.md` — package architecture
|
|
1699
|
+
- `docs/testing.md` — deterministic and real-model testing
|
|
1700
|
+
- `docs/pdf/Vision_Language_Engineering_EN.pdf` — English teaching course
|
|
1701
|
+
- `docs/pdf/Ingenierie_Vision_Langage_FR.pdf` — French teaching course
|
|
1702
|
+
|
|
1703
|
+
The README and Markdown documentation are the operational source of truth for the current package API/CLI.
|
|
1704
|
+
|
|
1705
|
+
---
|
|
1706
|
+
|
|
1707
|
+
## 22. Repository structure
|
|
1708
|
+
|
|
1709
|
+
```text
|
|
1710
|
+
vision-language-engineering-lab/
|
|
1711
|
+
├── .github/ CI, security, and publishing workflows
|
|
1712
|
+
├── configs/ validated runtime YAML profiles
|
|
1713
|
+
├── data/ smoke-test/user images
|
|
1714
|
+
├── docs/ architecture, retrieval, RAG, testing guidance
|
|
1715
|
+
├── examples/ small runnable API examples
|
|
1716
|
+
├── models/ explicit local model downloads (gitignored)
|
|
1717
|
+
├── notebooks/ analysis/demonstration clients
|
|
1718
|
+
├── scenarios/ end-to-end application cookbook
|
|
1719
|
+
├── src/
|
|
1720
|
+
│ └── vlm_engineering/ production Python package
|
|
1721
|
+
├── tests/
|
|
1722
|
+
│ ├── contract/ metadata/dependency/public-contract tests
|
|
1723
|
+
│ ├── e2e/ opt-in real-model tests
|
|
1724
|
+
│ └── unit/ deterministic package tests
|
|
1725
|
+
├── Dockerfile
|
|
1726
|
+
├── docker-compose.yml
|
|
1727
|
+
├── pyproject.toml
|
|
1728
|
+
├── requirements.txt
|
|
1729
|
+
├── README.md
|
|
1730
|
+
├── CHANGELOG.md
|
|
1731
|
+
├── CONTRIBUTING.md
|
|
1732
|
+
├── SECURITY.md
|
|
1733
|
+
└── LICENSE
|
|
1734
|
+
```
|
|
1735
|
+
|
|
1736
|
+
---
|
|
1737
|
+
|
|
1738
|
+
## 23. Implemented vs roadmap
|
|
1739
|
+
|
|
1740
|
+
### Implemented
|
|
1741
|
+
|
|
1742
|
+
- CLIP foundations
|
|
1743
|
+
- Qwen3-VL 2B / 4B / 8B generation
|
|
1744
|
+
- image description and visual question answering
|
|
1745
|
+
- structured visual/document analysis
|
|
1746
|
+
- traceable visual chunks
|
|
1747
|
+
- text-only visual RAG
|
|
1748
|
+
- Qwen3-VL multimodal embeddings
|
|
1749
|
+
- Qwen3-VL multimodal reranking
|
|
1750
|
+
- two-stage multimodal retrieval
|
|
1751
|
+
- multimodal RAG with original visual evidence
|
|
1752
|
+
- project-local model downloads
|
|
1753
|
+
- local/offline execution
|
|
1754
|
+
- runtime YAML validation
|
|
1755
|
+
- CLI workflows
|
|
1756
|
+
- Docker workflows
|
|
1757
|
+
- Python 3.11–3.13 CI
|
|
1758
|
+
- deterministic tests and opt-in real-model tests
|
|
1759
|
+
|
|
1760
|
+
### Roadmap
|
|
1761
|
+
|
|
1762
|
+
Potential future directions include:
|
|
1763
|
+
|
|
1764
|
+
- persistent vector database adapters
|
|
1765
|
+
- retrieval/RAG evaluation and benchmarks
|
|
1766
|
+
- multi-image and richer multimodal sessions
|
|
1767
|
+
- video / temporal reasoning
|
|
1768
|
+
- richer document parsing and OCR workflows
|
|
1769
|
+
- visual grounding / bounding boxes
|
|
1770
|
+
- quantized / low-resource deployment
|
|
1771
|
+
- async and streaming interfaces
|
|
1772
|
+
- serving/API layer
|
|
1773
|
+
- observability and caching
|
|
1774
|
+
- visual-agent perception workflows
|
|
1775
|
+
|
|
1776
|
+
Roadmap items are not part of the current public API until implemented and validated.
|
|
1777
|
+
|
|
1778
|
+
---
|
|
1779
|
+
|
|
1780
|
+
## License and third-party material
|
|
1781
|
+
|
|
1782
|
+
Project code is distributed under Apache-2.0.
|
|
1783
|
+
|
|
1784
|
+
Model weights are downloaded separately and retain their respective licenses. Users are responsible for reviewing the license terms of third-party models, datasets, and external assets used with this package.
|