vision-language-engineering-lab 0.2.1__py3-none-any.whl

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.
@@ -0,0 +1,1308 @@
1
+ Metadata-Version: 2.4
2
+ Name: vision-language-engineering-lab
3
+ Version: 0.2.1
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<13,>=12.3
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: notebooks
39
+ Requires-Dist: jupyterlab<5,>=4.6.3; extra == "notebooks"
40
+ Requires-Dist: jupyter-server<3,>=2.21; extra == "notebooks"
41
+ Requires-Dist: matplotlib<4,>=3.9; extra == "notebooks"
42
+ Requires-Dist: pandas<3,>=2.2; extra == "notebooks"
43
+ Provides-Extra: dev
44
+ Requires-Dist: mypy<2,>=1.18; extra == "dev"
45
+ Requires-Dist: pip-audit<3,>=2.9; extra == "dev"
46
+ Requires-Dist: pytest<10,>=9.0.3; extra == "dev"
47
+ Requires-Dist: pytest-cov<8,>=7.1; extra == "dev"
48
+ Requires-Dist: ruff<1,>=0.12; extra == "dev"
49
+ Provides-Extra: all
50
+ Requires-Dist: accelerate<2,>=1.10; extra == "all"
51
+ Requires-Dist: huggingface-hub<2,>=0.34; extra == "all"
52
+ Requires-Dist: qwen-vl-utils<1,>=0.0.14; extra == "all"
53
+ Requires-Dist: sentence-transformers<7,>=5.4; extra == "all"
54
+ Requires-Dist: torch<3,>=2.8; extra == "all"
55
+ Requires-Dist: torchvision<1,>=0.23; extra == "all"
56
+ Requires-Dist: transformers<6,>=4.57; extra == "all"
57
+ Dynamic: license-file
58
+
59
+ <!--
60
+ Path: README.md
61
+ Author: GHANMI Helmi
62
+ Current Role: AI Engineer
63
+ Past Role: Researcher in Applied Mathematics
64
+ Research Profile: https://www.researchgate.net/profile/Ghanmi-Helmi
65
+ -->
66
+
67
+ # Vision-Language Engineering Lab
68
+
69
+ **CLIP, configurable Qwen3-VL, visual document understanding, semantic visual chunking, and multimodal RAG.**
70
+
71
+ `vision-language-engineering-lab` is a production-oriented learning and engineering package for building Vision-Language applications with CLIP, Qwen3-VL, document understanding, retrieval, reranking, and multimodal RAG.
72
+
73
+ The reusable implementation lives under `src/vlm_engineering`. Notebooks are analysis/demo clients only, while `examples/` and `scenarios/` provide runnable package usage patterns.
74
+
75
+ > **v0.2.1 compatibility target:** Python 3.11, 3.12, and 3.13. Compatibility is validated in CI on every supported Python version before release.
76
+
77
+ Contributions are welcome. See [Contributing](#20-contributing) and [`CONTRIBUTING.md`](CONTRIBUTING.md).
78
+
79
+ ---
80
+
81
+ ## 1. Quick start
82
+
83
+ ### Install from the repository
84
+
85
+ For the current development version:
86
+
87
+ ```bash
88
+ git clone https://github.com/helmighanmi/vision-language-engineering-lab.git
89
+ cd vision-language-engineering-lab
90
+
91
+ python -m venv .venv
92
+ source .venv/bin/activate
93
+ python -m pip install --upgrade pip setuptools wheel
94
+ python -m pip install -e ".[all]"
95
+ ```
96
+
97
+ Windows PowerShell:
98
+
99
+ ```powershell
100
+ py -m venv .venv
101
+ .venv\Scripts\Activate.ps1
102
+ python -m pip install --upgrade pip setuptools wheel
103
+ python -m pip install -e ".[all]"
104
+ ```
105
+
106
+ After a public PyPI release, users can install the package with:
107
+
108
+ ```bash
109
+ python -m pip install "vision-language-engineering-lab[all]"
110
+ ```
111
+
112
+ ### Example 1 — Real-image smoke test with the default Qwen3-VL 2B model
113
+
114
+ A repository clone includes `data/diagram_random_clean.png` as a real-image smoke-test asset. Run:
115
+
116
+ ```bash
117
+ vlm-lab describe data/diagram_random_clean.png \
118
+ --model-size 2b \
119
+ --prompt "Describe this image accurately. List the main objects, text, and important visual relationships."
120
+ ```
121
+
122
+ The first Hub-backed run may download several gigabytes of model weights. Later runs reuse the Hugging Face cache. For predictable local/offline and Docker deployments, use the project-local model workflow in [Hugging Face cache and project-local models](#7-hugging-face-cache-and-project-local-models).
123
+
124
+ The equivalent Python API is:
125
+
126
+ ```python
127
+ from vlm_engineering import QwenVLModel
128
+
129
+ model = QwenVLModel(model_size="2b")
130
+
131
+ answer = model.generate(
132
+ "data/diagram_random_clean.png",
133
+ "Describe this image accurately. List the main objects, text, and important visual relationships.",
134
+ )
135
+
136
+ print(answer)
137
+ ```
138
+
139
+ The default model is:
140
+
141
+ ```text
142
+ Qwen/Qwen3-VL-2B-Instruct
143
+ ```
144
+
145
+ If you installed the package from PyPI instead of cloning the repository, use your own image path:
146
+
147
+ ```bash
148
+ vlm-lab describe /path/to/your/image.png \
149
+ --model-size 2b \
150
+ --prompt "Describe this image accurately. List the main objects, text, and important visual relationships."
151
+ ```
152
+
153
+ ### Example 2 — Select the 4B or 8B Qwen3-VL model
154
+
155
+ ```python
156
+ from vlm_engineering import QwenVLModel
157
+
158
+ model_4b = QwenVLModel(model_size="4b")
159
+ model_8b = QwenVLModel(model_size="8b")
160
+
161
+ print(model_4b.model_source)
162
+ print(model_8b.model_source)
163
+ ```
164
+
165
+ Available presets:
166
+
167
+ | Preset | Hugging Face model | Size class | Typical use |
168
+ |---|---|---:|---|
169
+ | `2b` | `Qwen/Qwen3-VL-2B-Instruct` | ~2B parameters | Default, learning, local prototypes |
170
+ | `4b` | `Qwen/Qwen3-VL-4B-Instruct` | ~4B parameters | Balanced quality/resource trade-off |
171
+ | `8b` | `Qwen/Qwen3-VL-8B-Instruct` | ~8B parameters | Higher-capacity inference on stronger hardware |
172
+
173
+ ### Example 3 — Use an explicit compatible Hugging Face model
174
+
175
+ ```python
176
+ from vlm_engineering import QwenVLModel
177
+
178
+ model = QwenVLModel.from_hub(
179
+ "Qwen/Qwen3-VL-4B-Instruct"
180
+ )
181
+
182
+ print(
183
+ model.generate(
184
+ "data/architecture.png",
185
+ "Explain the architecture and the relationships between components.",
186
+ )
187
+ )
188
+ ```
189
+
190
+ ### Example 4 — Load a model locally for offline inference
191
+
192
+ ```python
193
+ from vlm_engineering import QwenVLModel
194
+
195
+ model = QwenVLModel.from_local(
196
+ "models/Qwen3-VL-4B-Instruct"
197
+ )
198
+
199
+ answer = model.generate(
200
+ "data/architecture.png",
201
+ "Describe the diagram using only the visual evidence.",
202
+ )
203
+
204
+ print(answer)
205
+ ```
206
+
207
+ `from_local()` sets local-only loading and does not silently fall back to the Hugging Face Hub.
208
+
209
+ ### Example 5 — Structured diagram/document analysis
210
+
211
+ ```python
212
+ from vlm_engineering import QwenVLModel
213
+ from vlm_engineering.documents import analyze_visual_document
214
+
215
+ model = QwenVLModel(model_size="4b")
216
+ analysis = analyze_visual_document(model, "data/architecture.png")
217
+
218
+ print(analysis.to_dict())
219
+ ```
220
+
221
+ The structured result can contain page type, title, summary, entities, relations, important text, and uncertainties.
222
+
223
+ ### Example 6 — Text embedding for visual RAG
224
+
225
+ A common pattern is to use a VLM to convert visual evidence into faithful text and then index that text with Sentence Transformers.
226
+
227
+ ```python
228
+ from vlm_engineering.retrieval import TextEmbedder
229
+
230
+ embedder = TextEmbedder()
231
+
232
+ embeddings = embedder.encode(
233
+ [
234
+ "Redis provides application caching.",
235
+ "PostgreSQL stores persistent application data.",
236
+ ]
237
+ )
238
+
239
+ print(embeddings.shape)
240
+ ```
241
+
242
+ ### Example 7 — CLI
243
+
244
+ ```bash
245
+ # List Qwen presets
246
+ vlm-lab models
247
+
248
+ # Default 2B
249
+ vlm-lab describe data/diagram_random_clean.png
250
+
251
+ # 4B
252
+ vlm-lab describe data/diagram_random_clean.png --model-size 4b
253
+
254
+ # 8B
255
+ vlm-lab analyze data/architecture.png --model-size 8b
256
+
257
+ # Explicit Hugging Face model
258
+ vlm-lab describe data/diagram_random_clean.png \
259
+ --model-id Qwen/Qwen3-VL-4B-Instruct
260
+
261
+ # Local/offline model
262
+ vlm-lab describe data/diagram_random_clean.png \
263
+ --model-path models/Qwen3-VL-4B-Instruct
264
+ ```
265
+
266
+ For complete end-to-end workflows, see [`scenarios/README.md`](scenarios/README.md).
267
+
268
+ ---
269
+
270
+ ## 2. What this project demonstrates
271
+
272
+ ```text
273
+ CLIP foundations
274
+ -> image/text embeddings and zero-shot similarity
275
+
276
+ Qwen3-VL Instruct (2B / 4B / 8B or custom model ID)
277
+ -> image description, VQA, diagram/table/screenshot understanding, structured JSON
278
+
279
+ Document understanding
280
+ -> native text + visual semantics -> traceable chunks
281
+
282
+ Visual RAG
283
+ -> VLM descriptions + normal text embedder
284
+
285
+ Multimodal RAG
286
+ -> Qwen3-VL-Embedding -> retrieval -> Qwen3-VL-Reranker -> Qwen3-VL answer
287
+ ```
288
+
289
+ The project intentionally separates:
290
+
291
+ - reusable package code under `src/vlm_engineering/`
292
+ - deterministic tests under `tests/`
293
+ - small API examples under `examples/`
294
+ - end-to-end application recipes under `scenarios/`
295
+ - exploratory notebooks under `notebooks/`
296
+ - architecture and operational documentation under `docs/`
297
+
298
+ ---
299
+
300
+ ## 3. Python compatibility
301
+
302
+ v0.2.1 targets:
303
+
304
+ | Python | Support |
305
+ |---|---:|
306
+ | 3.11 | ✅ Supported |
307
+ | 3.12 | ✅ Supported |
308
+ | 3.13 | ✅ Supported |
309
+
310
+ The package metadata declares:
311
+
312
+ ```text
313
+ >=3.11,<3.14
314
+ ```
315
+
316
+ GitHub Actions validates the supported versions independently. The compatibility matrix checks package installation, dependency resolution, runtime imports, Ruff, mypy, pytest, coverage, and dependency security auditing.
317
+
318
+ Ruff targets Python 3.11 syntax so package code does not accidentally rely on Python 3.12+ syntax.
319
+
320
+ ### Google Colab
321
+
322
+ Google Colab can use the package directly when its runtime Python version is within the supported range.
323
+
324
+ After the public v0.2.1 release:
325
+
326
+ ```python
327
+ !pip install "vision-language-engineering-lab[all]"
328
+ ```
329
+
330
+ Then:
331
+
332
+ ```python
333
+ from vlm_engineering import QwenVLModel
334
+
335
+ model = QwenVLModel(model_size="2b")
336
+ print(model.model_source)
337
+ ```
338
+
339
+ For GPU workloads, verify the Colab runtime has a GPU enabled before loading large model weights.
340
+
341
+ ---
342
+
343
+ ## 4. Installation options
344
+
345
+ ### Requirements
346
+
347
+ - Python **3.11, 3.12, or 3.13**
348
+ - Git for source installs
349
+ - Enough disk space for the model(s) you choose to download
350
+ - A GPU is strongly recommended for Qwen3-VL inference
351
+ - CLIP and lightweight package tests can run on CPU
352
+
353
+ ### Create a virtual environment
354
+
355
+ Linux/macOS/GitHub Codespaces:
356
+
357
+ ```bash
358
+ python -m venv .venv
359
+ source .venv/bin/activate
360
+ python -m pip install --upgrade pip setuptools wheel
361
+ ```
362
+
363
+ Windows PowerShell:
364
+
365
+ ```powershell
366
+ py -m venv .venv
367
+ .venv\Scripts\Activate.ps1
368
+ python -m pip install --upgrade pip setuptools wheel
369
+ ```
370
+
371
+ ### Choose what to install
372
+
373
+ Development/testing:
374
+
375
+ ```bash
376
+ python -m pip install -e ".[dev]"
377
+ ```
378
+
379
+ CLIP:
380
+
381
+ ```bash
382
+ python -m pip install -e ".[clip]"
383
+ ```
384
+
385
+ Qwen3-VL generation:
386
+
387
+ ```bash
388
+ python -m pip install -e ".[qwen]"
389
+ ```
390
+
391
+ Retrieval / Sentence Transformers:
392
+
393
+ ```bash
394
+ python -m pip install -e ".[retrieval]"
395
+ ```
396
+
397
+ All runtime features:
398
+
399
+ ```bash
400
+ python -m pip install -e ".[all]"
401
+ ```
402
+
403
+ Full contributor environment:
404
+
405
+ ```bash
406
+ python -m pip install -e ".[all,dev,notebooks]"
407
+ ```
408
+
409
+ A compatibility `requirements.txt` is also provided:
410
+
411
+ ```bash
412
+ python -m pip install -r requirements.txt
413
+ ```
414
+
415
+ `pyproject.toml` remains the dependency source of truth.
416
+
417
+ Confirm the CLI:
418
+
419
+ ```bash
420
+ vlm-lab --help
421
+ vlm-lab models
422
+ ```
423
+
424
+ ---
425
+
426
+ ## 5. Qwen models used by this project
427
+
428
+ The default generative VLM is **`Qwen/Qwen3-VL-2B-Instruct`**. The package also provides friendly 4B and 8B presets and supports explicit compatible Hugging Face model IDs.
429
+
430
+ | Preset | Hugging Face model | Size class | Recommended use |
431
+ |---|---|---:|---|
432
+ | `2b` | `Qwen/Qwen3-VL-2B-Instruct` | ~2B parameters | **Default.** Learning, local prototypes, lower resource use |
433
+ | `4b` | `Qwen/Qwen3-VL-4B-Instruct` | ~4B parameters | Balanced quality/resource option |
434
+ | `8b` | `Qwen/Qwen3-VL-8B-Instruct` | ~8B parameters | Higher-capacity inference on stronger hardware |
435
+
436
+ Official model pages:
437
+
438
+ - https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct
439
+ - https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct
440
+ - https://huggingface.co/Qwen/Qwen3-VL-8B-Instruct
441
+
442
+ The size labels are parameter classes, not exact runtime-memory requirements. A rough BF16 weight-only floor is about 2 bytes per parameter (~4 GB for 2B, ~8 GB for 4B, ~16 GB for 8B). Real inference needs additional memory for the vision encoder, activations, KV cache, image tokens, framework overhead, and generation state.
443
+
444
+ Other Qwen models in the retrieval stack:
445
+
446
+ | Model | Role |
447
+ |---|---|
448
+ | `Qwen/Qwen3-VL-Embedding-2B` | Multimodal retrieval embeddings |
449
+ | `Qwen/Qwen3-VL-Reranker-2B` | Second-stage candidate reranking |
450
+
451
+ Qwen3-VL is supported through current Hugging Face Transformers. `trust_remote_code` is disabled by default and is not required for the normal Qwen3-VL path implemented by this package.
452
+
453
+ ---
454
+
455
+ ## 6. Model selection and loading
456
+
457
+ ### Default 2B
458
+
459
+ ```python
460
+ from vlm_engineering import QwenVLModel
461
+
462
+ model = QwenVLModel()
463
+ ```
464
+
465
+ ### 4B or 8B preset
466
+
467
+ ```python
468
+ model_4b = QwenVLModel(model_size="4b")
469
+ model_8b = QwenVLModel.from_preset("8b")
470
+ ```
471
+
472
+ ### Explicit Hugging Face model ID
473
+
474
+ ```python
475
+ model = QwenVLModel.from_hub(
476
+ "Qwen/Qwen3-VL-4B-Instruct"
477
+ )
478
+ ```
479
+
480
+ ### Explicit local model
481
+
482
+ ```python
483
+ model = QwenVLModel.from_local(
484
+ "models/Qwen3-VL-4B-Instruct"
485
+ )
486
+ ```
487
+
488
+ CLI model-selection flags are mutually exclusive:
489
+
490
+ ```text
491
+ --model-size
492
+ --model-id
493
+ --model-path
494
+ ```
495
+
496
+ This prevents the CLI from silently selecting a different source than the user intended.
497
+
498
+ ### Programmatic registry access
499
+
500
+ ```python
501
+ from vlm_engineering import QWEN3_VL_INSTRUCT_MODELS
502
+
503
+ for alias, preset in QWEN3_VL_INSTRUCT_MODELS.items():
504
+ print(alias, preset.model_id, preset.parameter_class)
505
+ ```
506
+
507
+ ---
508
+
509
+ ## 7. Hugging Face cache and project-local models
510
+
511
+ ### Two supported storage modes
512
+
513
+ For a quick experiment, Hub-backed inference can use the normal Hugging Face cache:
514
+
515
+ ```bash
516
+ vlm-lab describe data/diagram_random_clean.png --model-size 2b
517
+ ```
518
+
519
+ For Docker, offline execution, explicit cleanup, and reproducible deployments, prefer an explicit project-local model under `models/`.
520
+
521
+ ### Recommended: download the 2B preset into `models/`
522
+
523
+ ```bash
524
+ vlm-lab download-model \
525
+ --model-size 2b \
526
+ --output models/Qwen3-VL-2B-Instruct
527
+ ```
528
+
529
+ The `--output` argument is optional. This shorter command uses the same deterministic destination:
530
+
531
+ ```bash
532
+ vlm-lab download-model --model-size 2b
533
+ ```
534
+
535
+ Default destination:
536
+
537
+ ```text
538
+ models/Qwen3-VL-2B-Instruct/
539
+ ```
540
+
541
+ Hugging Face `snapshot_download(..., local_dir=...)` places the model files under the requested directory instead of the normal global Hub cache. Hugging Face may create a small `.cache/huggingface/` metadata directory inside the local model directory.
542
+
543
+ ### Run from the explicit local model directory
544
+
545
+ ```bash
546
+ vlm-lab describe data/diagram_random_clean.png \
547
+ --model-path models/Qwen3-VL-2B-Instruct \
548
+ --prompt "Describe this image accurately. List the main objects, text, and important visual relationships."
549
+ ```
550
+
551
+ ### Verify fully offline loading
552
+
553
+ After the download succeeds:
554
+
555
+ ```bash
556
+ HF_HUB_OFFLINE=1 vlm-lab describe data/diagram_random_clean.png \
557
+ --model-path models/Qwen3-VL-2B-Instruct \
558
+ --prompt "Describe this image accurately."
559
+ ```
560
+
561
+ `QwenVLModel.from_local()` enables local-files-only loading and does not silently fall back to the Hub.
562
+
563
+ ### Download another preset or explicit model ID
564
+
565
+ ```bash
566
+ vlm-lab download-model --model-size 4b
567
+ vlm-lab download-model --model-size 8b
568
+ ```
569
+
570
+ ```bash
571
+ vlm-lab download-model \
572
+ --model-id Qwen/Qwen3-VL-8B-Instruct \
573
+ --output models/qwen8b
574
+ ```
575
+
576
+ ### Clean the old global cache only after offline verification
577
+
578
+ Never remove the global Hugging Face cache while a model is downloading or running. First verify the local model with `HF_HUB_OFFLINE=1`, then inspect the old cache:
579
+
580
+ ```bash
581
+ du -sh ~/.cache/huggingface/hub 2>/dev/null
582
+ hf cache list --sort size:desc
583
+ ```
584
+
585
+ If disk space is needed after the offline test succeeds, remove only the old Qwen cache entry you no longer need:
586
+
587
+ ```bash
588
+ rm -rf ~/.cache/huggingface/hub/models--Qwen--Qwen3-VL-2B-Instruct
589
+ ```
590
+
591
+ The package never deletes a user's shared Hugging Face cache automatically.
592
+
593
+ ---
594
+
595
+ ## 8. Advanced model-loading options
596
+
597
+ ### `trust_remote_code`
598
+
599
+ Default: **False**.
600
+
601
+ ```bash
602
+ vlm-lab describe image.jpg --trust-remote-code
603
+ ```
604
+
605
+ Only enable this for a model repository you trust and that genuinely requires repository-provided Python code. Enabling it expands the security trust boundary.
606
+
607
+ ### Device and dtype
608
+
609
+ ```python
610
+ from vlm_engineering import QwenVLModel
611
+
612
+ model = QwenVLModel(
613
+ model_size="4b",
614
+ device_map="auto",
615
+ dtype="auto",
616
+ )
617
+ ```
618
+
619
+ Validate the exact dtype, quantization strategy, backend, and model size on your target hardware before production deployment.
620
+
621
+ ---
622
+
623
+ ## 9. Core Qwen3-VL use cases
624
+
625
+ ### Image captioning / rich description
626
+
627
+ ```bash
628
+ vlm-lab describe photo.jpg \
629
+ --prompt "Write a factual, detailed description."
630
+ ```
631
+
632
+ ### Visual question answering
633
+
634
+ ```bash
635
+ vlm-lab describe dashboard.png \
636
+ --model-size 4b \
637
+ --prompt "Which metric increased the most and what evidence supports it?"
638
+ ```
639
+
640
+ ### Diagram / architecture understanding
641
+
642
+ ```bash
643
+ vlm-lab analyze architecture.png --model-size 4b
644
+ ```
645
+
646
+ The `analyze` command asks the VLM for structured JSON containing page type, title, summary, entities, relations, important text, and uncertainties.
647
+
648
+ ### Screenshot / UI understanding
649
+
650
+ Use `describe` or `analyze` for dashboards, application screenshots, forms, and technical UI states.
651
+
652
+ ### Tables and document pages
653
+
654
+ Render the page as an image and request structured extraction. For production documents, preserve native/OCR text separately and use the VLM for layout, grouping, relationships, and visual semantics.
655
+
656
+ ---
657
+
658
+ ## 10. VLM + OCR/native parsing: hybrid by default
659
+
660
+ ```text
661
+ Native extraction / OCR Qwen3-VL
662
+ exact text, IDs, numbers layout, arrows, groups,
663
+ long deterministic text relationships, visual meaning
664
+ \ /
665
+ \ /
666
+ fusion
667
+ |
668
+ RAG-ready chunk
669
+ ```
670
+
671
+ A VLM can replace OCR in some workflows, but the strongest document pipeline usually keeps native/OCR evidence for literal accuracy and adds VLM semantics where visual structure matters.
672
+
673
+ ---
674
+
675
+ ## 11. Build a RAG-ready visual chunk
676
+
677
+ ```bash
678
+ vlm-lab chunk data/page_001.png \
679
+ --model-size 4b \
680
+ --document-id architecture-v1 \
681
+ --source-file architecture.pdf \
682
+ --page 1 \
683
+ --native-text-file data/page_001.txt
684
+ ```
685
+
686
+ The resulting `VisualChunk` preserves traceability such as document ID, source file, page, image reference, entities, relations, metadata, and fused retrieval text.
687
+
688
+ ---
689
+
690
+ ## 12. Visual RAG with a text embedder
691
+
692
+ ```text
693
+ image/page
694
+ -> Qwen3-VL description / structured analysis
695
+ -> retrieval text
696
+ -> Sentence Transformers text embedder
697
+ -> vector index / vector database
698
+ ```
699
+
700
+ This is useful when an existing RAG platform only supports text embeddings. The VLM turns visual evidence into searchable text while the original `image_ref` is retained for final grounding.
701
+
702
+ ```bash
703
+ python examples/text_only_visual_rag.py
704
+ ```
705
+
706
+ After retrieval, the final VLM can inspect the original image again rather than trusting only the generated description.
707
+
708
+ ---
709
+
710
+ ## 13. True multimodal embeddings and reranking
711
+
712
+ ### Multimodal embeddings
713
+
714
+ ```python
715
+ from vlm_engineering.retrieval import QwenMultimodalEmbedder
716
+
717
+ embedder = QwenMultimodalEmbedder(
718
+ "Qwen/Qwen3-VL-Embedding-2B"
719
+ )
720
+
721
+ queries = [
722
+ "Find the architecture diagram containing a cache and database."
723
+ ]
724
+
725
+ documents = [
726
+ "A textual architecture note",
727
+ "data/architecture.png",
728
+ {"text": "Payment architecture", "image": "data/architecture.png"},
729
+ ]
730
+
731
+ q = embedder.encode(
732
+ queries,
733
+ prompt="Retrieve relevant technical document evidence.",
734
+ )
735
+ d = embedder.encode(documents)
736
+
737
+ print(q @ d.T)
738
+ ```
739
+
740
+ ### Multimodal reranking
741
+
742
+ ```python
743
+ from vlm_engineering.retrieval import QwenMultimodalReranker
744
+
745
+ reranker = QwenMultimodalReranker()
746
+
747
+ scores = reranker.score(
748
+ "Which diagram shows the payment database?",
749
+ ["candidate text", "data/diagram.png"],
750
+ )
751
+
752
+ print(scores)
753
+ ```
754
+
755
+ Recommended production pattern:
756
+
757
+ ```text
758
+ query
759
+ -> Qwen3-VL-Embedding -> top K candidates
760
+ -> Qwen3-VL-Reranker -> top 3-5
761
+ -> retrieve chunk text + original image
762
+ -> selected Qwen3-VL-Instruct model
763
+ -> grounded answer with source/page
764
+ ```
765
+
766
+ ---
767
+
768
+ ## 14. CLIP foundations
769
+
770
+ ```python
771
+ from PIL import Image
772
+
773
+ from vlm_engineering import CLIPEncoder
774
+
775
+ image = Image.open("data/diagram_random_clean.png").convert("RGB")
776
+
777
+ clip = CLIPEncoder()
778
+ result = clip.zero_shot_classify(
779
+ image,
780
+ ["cat", "dog", "airplane"],
781
+ )
782
+
783
+ print(result)
784
+ ```
785
+
786
+ CLIP is useful for learning the shared image/text embedding-space idea that modern multimodal retrieval builds upon.
787
+
788
+ ---
789
+
790
+ ## 15. Examples and application scenarios
791
+
792
+ Small API examples:
793
+
794
+ ```text
795
+ examples/clip_zero_shot.py CLIP zero-shot classification
796
+ examples/qwen_describe_image.py Qwen image description
797
+ examples/qwen_model_selection.py 2B/4B/8B/custom model selection
798
+ examples/qwen_local_model.py explicit local/offline Qwen
799
+ examples/structured_visual_chunk.py RAG-ready visual chunk
800
+ examples/text_only_visual_rag.py VLM description + text embeddings
801
+ examples/qwen_multimodal_retrieval.py multimodal embedding/retrieval
802
+ ```
803
+
804
+ End-to-end application cookbook:
805
+
806
+ ```text
807
+ scenarios/scenario_01_image_captioning.py image captioning / description
808
+ scenarios/scenario_02_visual_question_answering.py grounded VQA
809
+ scenarios/scenario_03_diagram_to_json.py diagram -> structured JSON
810
+ scenarios/scenario_04_document_page_to_rag_chunk.py native/OCR + VLM -> RAG chunk
811
+ scenarios/scenario_05_text_only_visual_rag.py images -> VLM text -> text RAG
812
+ scenarios/scenario_06_true_multimodal_retrieval.py direct multimodal retrieval
813
+ scenarios/scenario_07_compare_qwen_presets.py compare Qwen 2B / 4B / 8B
814
+ scenarios/scenario_08_hub_vs_local_loading.py Hub/cache vs local/offline
815
+ scenarios/scenario_09_batch_structured_analysis.py batch image/page analysis
816
+ ```
817
+
818
+ Each applicable scenario accepts the same model choices used by the package:
819
+
820
+ ```bash
821
+ # Default 2B
822
+ python scenarios/scenario_01_image_captioning.py data/diagram_random_clean.png
823
+
824
+ # 4B / 8B
825
+ python scenarios/scenario_01_image_captioning.py \
826
+ data/diagram_random_clean.png --model-size 4b
827
+
828
+ python scenarios/scenario_01_image_captioning.py \
829
+ data/diagram_random_clean.png --model-size 8b
830
+
831
+ # Explicit Hugging Face model
832
+ python scenarios/scenario_01_image_captioning.py \
833
+ data/diagram_random_clean.png \
834
+ --model-id Qwen/Qwen3-VL-4B-Instruct
835
+
836
+ # Explicit local model
837
+ python scenarios/scenario_01_image_captioning.py \
838
+ data/diagram_random_clean.png \
839
+ --model-path models/Qwen3-VL-4B-Instruct
840
+ ```
841
+
842
+ See [`scenarios/README.md`](scenarios/README.md) for copy/paste commands, model guidance, RAG patterns, batch processing, and offline usage.
843
+
844
+ ---
845
+
846
+ ## 16. Notebooks
847
+
848
+ Notebooks are **analysis clients**, not the implementation layer:
849
+
850
+ - `00_clip_foundations.ipynb`
851
+ - `01_qwen3_vl_quickstart.ipynb`
852
+ - `02_visual_document_understanding.ipynb`
853
+ - `03_multimodal_rag.ipynb`
854
+
855
+ All reusable logic lives under `src/vlm_engineering` and can be used without Jupyter.
856
+
857
+ ---
858
+
859
+ ## 17. Docker and Docker Compose
860
+
861
+ The repository includes both a `Dockerfile` and `docker-compose.yml` so users can run the CLI inside a reproducible container instead of managing the Python environment directly on the host.
862
+
863
+ The current container is a runtime-oriented image. It installs the package with the Qwen and retrieval dependencies and does not include contributor/test/notebook tooling by default.
864
+
865
+ ### Prerequisites
866
+
867
+ Install one of:
868
+
869
+ - Docker Desktop, or
870
+ - Docker Engine with Docker Compose v2
871
+
872
+ Verify:
873
+
874
+ ```bash
875
+ docker --version
876
+ docker compose version
877
+ ```
878
+
879
+ ### Build with Docker Compose
880
+
881
+ From the repository root:
882
+
883
+ ```bash
884
+ docker compose build
885
+ ```
886
+
887
+ The image is built from the repository `Dockerfile`, which currently uses Python 3.12 as the reference container runtime. The package itself targets Python 3.11-3.13 in v0.2.1.
888
+
889
+ ### Check the CLI inside the container
890
+
891
+ ```bash
892
+ docker compose run --rm vlm-lab --help
893
+ ```
894
+
895
+ List the available Qwen presets:
896
+
897
+ ```bash
898
+ docker compose run --rm vlm-lab models
899
+ ```
900
+
901
+ ### Understand the mounted directories
902
+
903
+ The Compose configuration mounts:
904
+
905
+ ```text
906
+ Host Container
907
+ ./data /app/data read-only
908
+ ./models /app/models persistent bind mount
909
+ hf-cache named volume /home/appuser/.cache/huggingface
910
+ ```
911
+
912
+ This means:
913
+
914
+ - put user images/pages under `./data/`
915
+ - explicitly downloaded models under `./models/` survive container removal
916
+ - Hugging Face cache downloads survive normal `docker compose run --rm` commands
917
+ - the container does not need to copy your private data into the image
918
+
919
+ ### Describe an image with the default 2B model
920
+
921
+ Place an image at `data/diagram_random_clean.png`, then run:
922
+
923
+ ```bash
924
+ docker compose run --rm vlm-lab \
925
+ describe data/diagram_random_clean.png
926
+ ```
927
+
928
+ With a custom prompt:
929
+
930
+ ```bash
931
+ docker compose run --rm vlm-lab \
932
+ describe data/diagram_random_clean.png \
933
+ --prompt "Describe this image precisely."
934
+ ```
935
+
936
+ ### Select 4B or 8B inside the container
937
+
938
+ ```bash
939
+ docker compose run --rm vlm-lab \
940
+ describe data/diagram_random_clean.png \
941
+ --model-size 4b
942
+ ```
943
+
944
+ ```bash
945
+ docker compose run --rm vlm-lab \
946
+ analyze data/architecture.png \
947
+ --model-size 8b
948
+ ```
949
+
950
+ ### Persist a model explicitly under `./models`
951
+
952
+ Download the 4B preset from inside the container:
953
+
954
+ ```bash
955
+ docker compose run --rm vlm-lab \
956
+ download-model \
957
+ --model-size 4b \
958
+ --output models/Qwen3-VL-4B-Instruct
959
+ ```
960
+
961
+ Because `/app/models` is bind-mounted to host `./models`, the downloaded files remain available after the container exits.
962
+
963
+ Then use the local model explicitly:
964
+
965
+ ```bash
966
+ docker compose run --rm vlm-lab \
967
+ describe data/diagram_random_clean.png \
968
+ --model-path models/Qwen3-VL-4B-Instruct
969
+ ```
970
+
971
+ ### Fully offline container inference
972
+
973
+ First download the model while network access is available. Then run:
974
+
975
+ ```bash
976
+ docker compose run --rm \
977
+ -e HF_HUB_OFFLINE=1 \
978
+ vlm-lab \
979
+ describe data/diagram_random_clean.png \
980
+ --model-path models/Qwen3-VL-4B-Instruct
981
+ ```
982
+
983
+ `--model-path` enables local-only model loading in the package.
984
+
985
+ ### Hugging Face authentication without baking secrets into the image
986
+
987
+ For a private or gated compatible repository, keep the token on the host and pass it at runtime rather than storing it in the Dockerfile:
988
+
989
+ ```bash
990
+ export HF_TOKEN="..."
991
+
992
+ docker compose run --rm \
993
+ -e HF_TOKEN \
994
+ vlm-lab \
995
+ describe data/diagram_random_clean.png \
996
+ --model-id your-org/your-compatible-model
997
+ ```
998
+
999
+ Do not commit Hugging Face tokens or other credentials to the repository.
1000
+
1001
+ ### Open a shell for debugging
1002
+
1003
+ The image entrypoint is `vlm-lab`. To open a shell instead:
1004
+
1005
+ ```bash
1006
+ docker compose run --rm \
1007
+ --entrypoint /bin/sh \
1008
+ vlm-lab
1009
+ ```
1010
+
1011
+ Inside the container you can inspect:
1012
+
1013
+ ```bash
1014
+ python --version
1015
+ python -m pip list
1016
+ ls -la /app/data
1017
+ ls -la /app/models
1018
+ ```
1019
+
1020
+ ### Build and run without Compose
1021
+
1022
+ Build:
1023
+
1024
+ ```bash
1025
+ docker build \
1026
+ -t vision-language-engineering-lab:0.2.1 \
1027
+ .
1028
+ ```
1029
+
1030
+ List models:
1031
+
1032
+ ```bash
1033
+ docker run --rm \
1034
+ vision-language-engineering-lab:0.2.1 \
1035
+ models
1036
+ ```
1037
+
1038
+ Run against host data and persistent models:
1039
+
1040
+ ```bash
1041
+ docker run --rm \
1042
+ -v "$PWD/data:/app/data:ro" \
1043
+ -v "$PWD/models:/app/models" \
1044
+ -v vlm-hf-cache:/home/appuser/.cache/huggingface \
1045
+ vision-language-engineering-lab:0.2.1 \
1046
+ describe data/diagram_random_clean.png \
1047
+ --model-size 2b
1048
+ ```
1049
+
1050
+ ### Rebuild after package/dependency changes
1051
+
1052
+ ```bash
1053
+ docker compose build
1054
+ ```
1055
+
1056
+ If you specifically need a clean image rebuild:
1057
+
1058
+ ```bash
1059
+ docker compose build --no-cache
1060
+ ```
1061
+
1062
+ ### Remove containers while keeping explicit local models
1063
+
1064
+ ```bash
1065
+ docker compose down
1066
+ ```
1067
+
1068
+ The host `./models` directory remains because it is a bind mount.
1069
+
1070
+ To also delete the named Hugging Face cache volume:
1071
+
1072
+ ```bash
1073
+ docker compose down -v
1074
+ ```
1075
+
1076
+ Use `-v` carefully because cached model downloads in the named volume will be removed.
1077
+
1078
+ ### GPU execution
1079
+
1080
+ The checked-in Compose file is intentionally a portable baseline and does not force a GPU runtime configuration.
1081
+
1082
+ For NVIDIA GPU inference, configure a compatible host Docker/NVIDIA runtime and verify GPU visibility from the container before relying on it for Qwen workloads. GPU setup is host-specific; the project does not assume every user has NVIDIA hardware.
1083
+
1084
+ Qwen3-VL 4B and especially 8B require substantially more memory than the default 2B model. Start with 2B when validating a container deployment.
1085
+
1086
+ ### Why `docker compose run` instead of `docker compose up`?
1087
+
1088
+ `vlm-lab` is a command-line workload, not a long-running web server. The Compose service defaults to `--help`, so most users should execute individual tasks with:
1089
+
1090
+ ```bash
1091
+ docker compose run --rm vlm-lab <command> ...
1092
+ ```
1093
+
1094
+ This creates an isolated one-shot container, runs the command, and removes the container afterward while preserving the mounted data/model/cache volumes.
1095
+
1096
+ ---
1097
+
1098
+ ## 18. Quality gates and compatibility validation
1099
+
1100
+ Before pushing changes:
1101
+
1102
+ ```bash
1103
+ python -m pip check
1104
+ python -m ruff check .
1105
+ python -m mypy .
1106
+ python -m pytest --cov=vlm_engineering --cov-report=term-missing
1107
+ python -m pip_audit
1108
+ ```
1109
+
1110
+ The project uses:
1111
+
1112
+ | Tool | Purpose |
1113
+ |---|---|
1114
+ | Ruff | linting, import organization, source-quality rules |
1115
+ | mypy | static type checking |
1116
+ | pytest | functional, integration, and contract testing |
1117
+ | pytest-cov | coverage enforcement |
1118
+ | `pip check` | installed dependency consistency |
1119
+ | `pip-audit` | known dependency-vulnerability auditing |
1120
+
1121
+ CI additionally executes the package against Python 3.11, 3.12, and 3.13.
1122
+
1123
+ Heavy ML components are mocked or injected in normal unit tests, so CI does not need to download multi-gigabyte Qwen model weights merely to validate package contracts.
1124
+
1125
+ ---
1126
+
1127
+ ## 19. Troubleshooting
1128
+
1129
+ ### `ModuleNotFoundError`
1130
+
1131
+ Activate the environment and install the required extra:
1132
+
1133
+ ```bash
1134
+ source .venv/bin/activate
1135
+ python -m pip install -e ".[all]"
1136
+ ```
1137
+
1138
+ ### `vlm-lab: command not found`
1139
+
1140
+ Confirm the package is installed in the active environment:
1141
+
1142
+ ```bash
1143
+ python -m pip show vision-language-engineering-lab
1144
+ which python
1145
+ ```
1146
+
1147
+ Then reinstall:
1148
+
1149
+ ```bash
1150
+ python -m pip install -e ".[all]"
1151
+ ```
1152
+
1153
+ ### CUDA / out-of-memory error
1154
+
1155
+ Start with the default `2b` preset, reduce image resolution/output length, or use stronger hardware. Moving from 2B to 4B to 8B increases model capacity and memory pressure.
1156
+
1157
+ ### First run is slow
1158
+
1159
+ Hub/cache mode may be downloading model files. For a predictable deployment directory, use `vlm-lab download-model --model-size 2b`, then run with `--model-path models/Qwen3-VL-2B-Instruct`.
1160
+
1161
+ ### Fully offline inference
1162
+
1163
+ Download the model first, then use `--model-path` / `QwenVLModel.from_local()`. Optionally set:
1164
+
1165
+ ```bash
1166
+ export HF_HUB_OFFLINE=1
1167
+ ```
1168
+
1169
+ ### Model requires repository-provided Python code
1170
+
1171
+ Do not enable `--trust-remote-code` automatically. Verify the model repository and only opt in when genuinely required.
1172
+
1173
+ ### Sentence Transformers / dependency compatibility
1174
+
1175
+ Check the resolved environment:
1176
+
1177
+ ```bash
1178
+ python -m pip check
1179
+ python -m pip show sentence-transformers torch transformers
1180
+ ```
1181
+
1182
+ v0.2.1 allows Sentence Transformers 5.4 through the 6.x line, while CI validates the dependency stack on Python 3.11-3.13.
1183
+
1184
+ ### Which Qwen model should I choose?
1185
+
1186
+ - Start with **2B** while developing the pipeline.
1187
+ - Compare **4B** when reasoning/extraction quality is insufficient.
1188
+ - Test **8B** when the quality gain justifies the higher resource cost.
1189
+ - Evaluate on your own diagrams/documents; model size alone does not guarantee better RAG accuracy.
1190
+
1191
+ ### Docker cannot see my file
1192
+
1193
+ Confirm the file is under the host `data/` directory because Compose mounts that directory into `/app/data`:
1194
+
1195
+ ```bash
1196
+ ls -la data/
1197
+ docker compose run --rm --entrypoint /bin/sh vlm-lab -c "ls -la /app/data"
1198
+ ```
1199
+
1200
+ ### Docker keeps downloading models
1201
+
1202
+ For production-like Docker tests, prefer downloading the model explicitly into `./models` and using `--model-path`. The named Hugging Face cache volume remains useful for ad-hoc Hub-backed experiments.
1203
+
1204
+ ---
1205
+
1206
+ ## 20. Contributing
1207
+
1208
+ **Contributions are welcome.**
1209
+
1210
+ The project is intended to grow as an open Vision-Language engineering lab, and contributions from developers, AI/ML engineers, researchers, students, and practitioners are encouraged.
1211
+
1212
+ Useful contributions include:
1213
+
1214
+ - new VLM integrations
1215
+ - additional Qwen-compatible model support
1216
+ - multimodal retrieval strategies
1217
+ - document-understanding pipelines
1218
+ - RAG evaluation methods
1219
+ - performance and memory improvements
1220
+ - additional deterministic tests
1221
+ - Python-version compatibility improvements
1222
+ - Docker/deployment improvements
1223
+ - documentation and tutorials
1224
+ - reproducible application scenarios
1225
+ - bug fixes
1226
+
1227
+ ### Contribution workflow
1228
+
1229
+ 1. Fork the repository.
1230
+ 2. Create a focused branch.
1231
+ 3. Implement the change.
1232
+ 4. Add or update tests.
1233
+ 5. Run the quality gates locally.
1234
+ 6. Push the branch.
1235
+ 7. Open a Pull Request describing the motivation, implementation, validation, and any compatibility considerations.
1236
+
1237
+ Example development setup:
1238
+
1239
+ ```bash
1240
+ git clone https://github.com/helmighanmi/vision-language-engineering-lab.git
1241
+ cd vision-language-engineering-lab
1242
+
1243
+ python -m venv .venv
1244
+ source .venv/bin/activate
1245
+ python -m pip install --upgrade pip
1246
+ python -m pip install -e ".[all,dev]"
1247
+ ```
1248
+
1249
+ Validation:
1250
+
1251
+ ```bash
1252
+ python -m pip check
1253
+ python -m ruff check .
1254
+ python -m mypy .
1255
+ python -m pytest --cov=vlm_engineering --cov-report=term-missing
1256
+ python -m pip_audit
1257
+ ```
1258
+
1259
+ Please read [`CONTRIBUTING.md`](CONTRIBUTING.md) for the complete contribution workflow.
1260
+
1261
+ Questions, feature proposals, documentation improvements, and bug reports are welcome through GitHub Issues.
1262
+
1263
+ ---
1264
+
1265
+ ## 21. Documentation
1266
+
1267
+ - `docs/model-loading.md` — model presets, Hub/custom/local modes and troubleshooting
1268
+ - `docs/qwen-model-selection.md` — model-size selection and memory guidance
1269
+ - `docs/rag-patterns.md` — visual and multimodal RAG patterns
1270
+ - `docs/architecture.md` — package architecture
1271
+ - `docs/testing.md` — testing strategy
1272
+ - `docs/pdf/Vision_Language_Engineering_EN.pdf` — English teaching course
1273
+ - `docs/pdf/Ingenierie_Vision_Langage_FR.pdf` — French teaching course
1274
+
1275
+ The PDFs are conceptual teaching references. The README and Markdown docs are the operational source of truth for the current package CLI/API.
1276
+
1277
+ ---
1278
+
1279
+ ## 22. Repository structure
1280
+
1281
+ ```text
1282
+ vision-language-engineering-lab/
1283
+ ├── .github/ CI, security, and publishing workflows
1284
+ ├── data/ user-provided images/pages (gitignored except docs)
1285
+ ├── docs/ architecture, model-loading, RAG, testing guidance
1286
+ ├── examples/ small runnable API examples
1287
+ ├── models/ explicit local model downloads (gitignored)
1288
+ ├── notebooks/ analysis/demonstration clients
1289
+ ├── scenarios/ end-to-end application cookbook
1290
+ ├── src/
1291
+ │ └── vlm_engineering/ production Python package
1292
+ ├── tests/ deterministic unit/integration/contract tests
1293
+ ├── Dockerfile container image definition
1294
+ ├── docker-compose.yml reproducible CLI/container workflow
1295
+ ├── pyproject.toml package metadata and dependency source of truth
1296
+ ├── requirements.txt compatibility installer
1297
+ ├── README.md
1298
+ ├── CHANGELOG.md
1299
+ ├── CONTRIBUTING.md
1300
+ ├── SECURITY.md
1301
+ └── LICENSE
1302
+ ```
1303
+
1304
+ ---
1305
+
1306
+ ## License and third-party material
1307
+
1308
+ Project code is distributed under Apache-2.0. Model weights are downloaded separately and retain their respective licenses. Users are responsible for reviewing the license terms of any third-party models, datasets, or external assets they use with this package.