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.
Files changed (52) hide show
  1. vision_language_engineering_lab-0.3.2/PKG-INFO +1784 -0
  2. vision_language_engineering_lab-0.3.2/README.md +1712 -0
  3. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/pyproject.toml +26 -5
  4. vision_language_engineering_lab-0.3.2/src/vision_language_engineering_lab.egg-info/PKG-INFO +1784 -0
  5. {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
  6. {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
  7. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/__init__.py +4 -0
  8. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/__main__.py +2 -1
  9. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/cli.py +110 -16
  10. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/chunking.py +31 -16
  11. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/exceptions.py +4 -0
  12. vision_language_engineering_lab-0.3.2/src/vlm_engineering/inputs.py +103 -0
  13. vision_language_engineering_lab-0.3.2/src/vlm_engineering/operational.py +69 -0
  14. vision_language_engineering_lab-0.3.2/src/vlm_engineering/py.typed +0 -0
  15. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/downloader.py +11 -9
  16. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/model.py +59 -70
  17. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/registry.py +5 -3
  18. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/retrieval/__init__.py +6 -2
  19. vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/backend.py +46 -0
  20. vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/in_memory.py +65 -0
  21. vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/model_options.py +104 -0
  22. vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/multimodal_embedding.py +107 -0
  23. vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/multimodal_rag.py +149 -0
  24. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/retrieval/rag.py +8 -8
  25. vision_language_engineering_lab-0.3.2/src/vlm_engineering/retrieval/reranker.py +126 -0
  26. vision_language_engineering_lab-0.3.2/src/vlm_engineering/runtime_config.py +119 -0
  27. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_chunking.py +9 -0
  28. vision_language_engineering_lab-0.2.1/PKG-INFO +0 -1308
  29. vision_language_engineering_lab-0.2.1/README.md +0 -1250
  30. vision_language_engineering_lab-0.2.1/src/vision_language_engineering_lab.egg-info/PKG-INFO +0 -1308
  31. vision_language_engineering_lab-0.2.1/src/vlm_engineering/retrieval/in_memory.py +0 -48
  32. vision_language_engineering_lab-0.2.1/src/vlm_engineering/retrieval/multimodal_embedding.py +0 -56
  33. vision_language_engineering_lab-0.2.1/src/vlm_engineering/retrieval/reranker.py +0 -53
  34. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/LICENSE +0 -0
  35. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/setup.cfg +0 -0
  36. {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
  37. {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
  38. {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
  39. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/clip/__init__.py +0 -0
  40. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/clip/encoder.py +0 -0
  41. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/config.py +0 -0
  42. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/__init__.py +0 -0
  43. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/fusion.py +0 -0
  44. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/schemas.py +0 -0
  45. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/documents/visual_analysis.py +0 -0
  46. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/qwen/__init__.py +0 -0
  47. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/retrieval/text_embedding.py +0 -0
  48. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/src/vlm_engineering/utils.py +0 -0
  49. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_qwen_loader.py +0 -0
  50. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_retrieval.py +0 -0
  51. {vision_language_engineering_lab-0.2.1 → vision_language_engineering_lab-0.3.2}/tests/test_utils.py +0 -0
  52. {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.