Spaces:
Running on Zero
Running on Zero
electblake commited on
Commit ·
9f51ad0
1
Parent(s): 2c91fea
Refactor application implementation
Browse files- .agents/references/nuextract3/2026-07-20/README.md +5 -0
- .agents/references/nuextract3/2026-07-20/REFERENCE_INDEX.md +22 -0
- .agents/references/nuextract3/2026-07-20/REFERENCE_SUMMARY.md +20 -0
- .agents/references/nuextract3/2026-07-20/SOURCE_URLS.md +10 -0
- .agents/references/nuextract3/2026-07-20/fixtures/user-drop-001.txt +942 -0
- .agents/references/nuextract3/2026-07-20/huggingface.co/numind/NuExtract3.md +1010 -0
- .agents/references/nuextract3/2026-07-20/huggingface.co/numind/NuExtract3/blob/main/README.md +1001 -0
- AGENTS.md +9 -0
- app/config.py +0 -2
- app/nuextract.py +71 -7
- notebooks/NuExtract3.ipynb +66 -0
- notebooks/nuextract-2.0_inference.ipynb +0 -876
- pyproject.toml +8 -3
- uv.lock +496 -31
.agents/references/nuextract3/2026-07-20/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# nuextract3
|
| 2 |
+
|
| 3 |
+
- Topic: `nuextract3`
|
| 4 |
+
- Capture Date: `2026-07-20`
|
| 5 |
+
- Scope: TODO
|
.agents/references/nuextract3/2026-07-20/REFERENCE_INDEX.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Reference Index
|
| 2 |
+
|
| 3 |
+
- Bundle Path: `S:\Spaces\Data-Extraction\NuMarkApp\.agents\references\nuextract3\2026-07-20`
|
| 4 |
+
- Mirrored References: `1`
|
| 5 |
+
- Fixtures: `1`
|
| 6 |
+
|
| 7 |
+
- `README.md`: bundle scope
|
| 8 |
+
- `SOURCE_URLS.md`: verified sources
|
| 9 |
+
- `REFERENCE_SUMMARY.md`: findings and version notes
|
| 10 |
+
- `fixtures/`: user-provided artifacts
|
| 11 |
+
- `<domain>/`: mirrored markdown references by domain
|
| 12 |
+
|
| 13 |
+
## References
|
| 14 |
+
|
| 15 |
+
### huggingface.co
|
| 16 |
+
|
| 17 |
+
- `huggingface.co/numind/NuExtract3.md`: `https://huggingface.co/numind/NuExtract3`
|
| 18 |
+
|
| 19 |
+
## Fixtures
|
| 20 |
+
|
| 21 |
+
- `fixtures/user-drop-001.txt`
|
| 22 |
+
|
.agents/references/nuextract3/2026-07-20/REFERENCE_SUMMARY.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NuExtract3 Reference Summary
|
| 2 |
+
|
| 3 |
+
- Reference date: 2026-07-20
|
| 4 |
+
- Official model: `numind/NuExtract3`
|
| 5 |
+
- Library: Transformers
|
| 6 |
+
- Base model: Qwen/Qwen3.5-4B
|
| 7 |
+
- License: Apache-2.0
|
| 8 |
+
|
| 9 |
+
NuExtract3 is a 4B vision-language reasoning model for structured extraction and document-to-Markdown conversion. It accepts text, images, or both.
|
| 10 |
+
|
| 11 |
+
For direct Transformers inference, the official README loads `AutoProcessor` and `AutoModelForImageTextToText`, then passes `template`, `mode`, and `enable_thinking` through `processor.apply_chat_template`. Non-thinking structured extraction uses `enable_thinking=False`; the documented starting temperature is 0.2.
|
| 12 |
+
|
| 13 |
+
Runtime-specific note: this project uses Transformers 5.14.1, Python 3.12, BF16, automatic device placement, and FlashAttention 2.
|
| 14 |
+
|
| 15 |
+
- Reference Date: `2026-07-20`
|
| 16 |
+
- Official Version: TODO
|
| 17 |
+
- Key Findings:
|
| 18 |
+
- TODO
|
| 19 |
+
- Compatibility Notes:
|
| 20 |
+
- TODO
|
.agents/references/nuextract3/2026-07-20/SOURCE_URLS.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Source URLs
|
| 2 |
+
|
| 3 |
+
- Bundle: `2026-07-20`
|
| 4 |
+
- Generated: `2026-07-20T17:29:26.022745+00:00`
|
| 5 |
+
- Mirrored References: `1`
|
| 6 |
+
|
| 7 |
+
## huggingface.co
|
| 8 |
+
|
| 9 |
+
- `https://huggingface.co/numind/NuExtract3` -> `huggingface.co/numind/NuExtract3.md`
|
| 10 |
+
|
.agents/references/nuextract3/2026-07-20/fixtures/user-drop-001.txt
ADDED
|
@@ -0,0 +1,942 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
license_link: https://huggingface.co/numind/NuExtract3/blob/main/LICENSE
|
| 4 |
+
library_name: transformers
|
| 5 |
+
pipeline_tag: image-to-text
|
| 6 |
+
tags:
|
| 7 |
+
- image-text-to-text
|
| 8 |
+
- transformers
|
| 9 |
+
- safetensors
|
| 10 |
+
- qwen3_5
|
| 11 |
+
- vision-language
|
| 12 |
+
- vlm
|
| 13 |
+
- document-understanding
|
| 14 |
+
- structured-extraction
|
| 15 |
+
- information-extraction
|
| 16 |
+
- ocr
|
| 17 |
+
- document-to-markdown
|
| 18 |
+
- markdown
|
| 19 |
+
- rag
|
| 20 |
+
- reasoning
|
| 21 |
+
- multilingual
|
| 22 |
+
- conversational
|
| 23 |
+
base_model:
|
| 24 |
+
- Qwen/Qwen3.5-4B
|
| 25 |
+
model_name: NuExtract3
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
<p align="center">
|
| 29 |
+
<a href="https://nuextract.ai/">
|
| 30 |
+
<img src="header.svg" width="900px"/>
|
| 31 |
+
</a>
|
| 32 |
+
</p>
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
<p align="center">
|
| 36 |
+
🖥️ <a href="https://nuextract.ai/">API / Platform</a> |
|
| 37 |
+
📑 <a href="https://numind.ai/blog">Blog</a> |
|
| 38 |
+
🗣️ <a href="https://discord.gg/3tsEtJNCDe">Discord</a> |
|
| 39 |
+
🛠️ <a href="https://github.com/numindai/nuextract">GitHub</a>
|
| 40 |
+
</p>
|
| 41 |
+
|
| 42 |
+
**NuExtract3** is a unified **4B** vision-language reasoning model for document understanding.
|
| 43 |
+
|
| 44 |
+
It combines strong **structured information extraction** with high-quality **image-to-Markdown** conversion, making it suitable for extraction pipelines, OCR, and RAG preprocessing for all types of documents such as scans, receipts, forms, invoices, contracts or tables.
|
| 45 |
+
|
| 46 |
+
Try it out in [the 🤗 space!](https://huggingface.co/spaces/numind/NuExtract-3-4B)
|
| 47 |
+
|
| 48 |
+
## Overview
|
| 49 |
+
|
| 50 |
+
- **Structured extraction**: input (text/images) + JSON template + instructions --> JSON output
|
| 51 |
+
- **Markdown conversion**: input (text/images) --> Markdown
|
| 52 |
+
- **Multimodal inputs**: text, images, or text + images.
|
| 53 |
+
- **Multilingual** documents.
|
| 54 |
+
- **Reasoning** and non-reasoning inference modes.
|
| 55 |
+
- **Template generation** for structured extraction from natural language or input document.
|
| 56 |
+
|
| 57 |
+
# Benchmark results
|
| 58 |
+
|
| 59 |
+
## Structured Extraction
|
| 60 |
+
|
| 61 |
+
We benchmarked NuExtract on NuMind's internal structured benchmark, measuring model's performances on ~600 documents of diverse types including invoices, movie posters or floor plans. These documents and their ground-truth cover diverse use-cases testing model visual understanding, OCR, reasoning and ability to handle long input and output contexts.
|
| 62 |
+
We plan to open-source this benchmark in the coming weeks, along with a extensive leaderboard including most popular open-weight and closed-sourced APIs and a Python library allowing to easily measure model performances on structured extraction.
|
| 63 |
+
|
| 64 |
+
<img src="st.svg" width="1000"/>
|
| 65 |
+
|
| 66 |
+
To measure a pair of predicted and ground-truth JSONs, we represent both as trees which we align based on node names, compute metric scores for aligned leaves and report the average of these scores. `string` and `verbatim-string` leaves are evaluated with indel distance (i.e. Levenshtein without replacement), while all others are evaluated with exact-match.
|
| 67 |
+
Models were evaluated using vllm, with a temperature of 0.25 and a maximum of 65000 output token (for both thinking and answer), which largely exceeds 22000 which is the number of tokens of the largest ground truth output.
|
| 68 |
+
|
| 69 |
+
<figure>
|
| 70 |
+
|
| 71 |
+
|Model name |Average score|Num. failed⁽¹⁾|Avg. num tokens thinking|Avg. num tokens answer|
|
| 72 |
+
|--------------------|-------------|-----------|------------------------|----------------------|
|
| 73 |
+
|NuExtract3.4_4B-RL |**0.651 ± 0.019**|27 |2036 |1856 |
|
| 74 |
+
|gemma-4-E4B-it |0.538 ± 0.023|31 |3005 |1287 |
|
| 75 |
+
|Qwen3.5-9B |0.479 ± 0.030|170 |22409 |1257 |
|
| 76 |
+
|Qwen3.5-4B |0.417 ± 0.031|229 |27177 |1201 |
|
| 77 |
+
|GLM-4.6V-Flash |0.435 ± 0.026|153 |2989 |1357 |
|
| 78 |
+
|Nemotron-3-Nano-Omni|0.387 ± 0.028|204 |25827 |522 |
|
| 79 |
+
|Ministral-3-3B |0.240 ± 0.022|344 |27586 |362 |
|
| 80 |
+
|
| 81 |
+
<figcaption>
|
| 82 |
+
<small>
|
| 83 |
+
(1) number of model outputs that were not JSON deserializable, either directly or by removing leading and trailing backticks.<br>
|
| 84 |
+
95% confidence intervals computed using a nonparametric bootstrap over scores distributions.
|
| 85 |
+
</small>
|
| 86 |
+
</figcaption>
|
| 87 |
+
</figure>
|
| 88 |
+
|
| 89 |
+
The benchmark include samples containing multiple images resulting in large input context, and some with ground-truth containing large numbers of items to extract resulting in large outputs. We found that the reasoning of small models significantly negatively impact their performances. The reason is that many models ended up falling in repetition loops, hitting the output tokens limit and resulting in failed requests.
|
| 90 |
+
|
| 91 |
+
## Document to Markdown
|
| 92 |
+
|
| 93 |
+
NuExtract can also convert document images into clean Markdown. Output will be Markdown for text (headers etc), HTML for tables, LaTeX for math and ```<figure data-type="image" data-id="img_n"><img src="/NM-dev/model_card-A/resolve/main/img_n.png" alt="Detail description of the images"/> ```
|
| 94 |
+
|
| 95 |
+
Modern, format-agnostic benchmarks for complex document understanding are limited, so we explored a new evaluation approach.
|
| 96 |
+
We selected 100 documents with challenging layouts and tables, asked each model to convert them into a structured representation, then used Gemini 3 Flash to compare model outputs against the source document and choose the most accurate result.
|
| 97 |
+
The rankings aligned with human votes, suggesting this is a promising method for evaluating document-to-Markdown capabilities. More details will be shared in an upcoming technical report.
|
| 98 |
+
Here are some results:
|
| 99 |
+
|
| 100 |
+
<img src="ocr_preferences.svg" width="1000"/>
|
| 101 |
+
|
| 102 |
+
### Using "Markdown-to-structured"
|
| 103 |
+
|
| 104 |
+
To add other evaluate references, we used our structured extraction benchmark to evaluate models in a two-step fashion: convert the benchmark inputs to Markdown, then use Qwen3.6 27B to perform the structured extraction task on them. Intuitively, it allows to evaluate how models achieve to keep the input document content and layout: good models will allow the "structured extractor" model to perform better scores.
|
| 105 |
+
|
| 106 |
+
<img src="md2st.svg" width="1000"/>
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# Using NuExtract
|
| 110 |
+
|
| 111 |
+
## Structured extraction
|
| 112 |
+
|
| 113 |
+
Structured extraction takes as inputs:
|
| 114 |
+
|
| 115 |
+
1. An input document, which can be text, image, or both;
|
| 116 |
+
2. A JSON template describing the information to extract;
|
| 117 |
+
3. (Optional) Instructions, allowing to specify expected output formats or values, to provide with the `instructions` chat template kwarg;
|
| 118 |
+
4. (Optional) In-Context Learning (ICL) examples.
|
| 119 |
+
|
| 120 |
+
### Input JSON template
|
| 121 |
+
|
| 122 |
+
NuExtract uses a input JSON template whose structure is identical to the output JSON. Its leaf values are specify the **types** of the output JSON leaves. For examples:
|
| 123 |
+
|
| 124 |
+
```json
|
| 125 |
+
{
|
| 126 |
+
"invoice_number": "verbatim-string",
|
| 127 |
+
"invoice_date": "date",
|
| 128 |
+
"total_amount": "number",
|
| 129 |
+
"currency": "currency",
|
| 130 |
+
"line_items": [
|
| 131 |
+
{
|
| 132 |
+
"description": "verbatim-string",
|
| 133 |
+
"item_type": ["electronics", "clothing", "vehicle", "furniture", "other"],
|
| 134 |
+
"quantity": "integer",
|
| 135 |
+
"unit_price": "number",
|
| 136 |
+
"total": "number"
|
| 137 |
+
}
|
| 138 |
+
]
|
| 139 |
+
}
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
Supported template types include:
|
| 143 |
+
|
| 144 |
+
- `verbatim-string`: extract text exactly as it appears in the document;
|
| 145 |
+
- `string`: generic string field, allowing abstraction or light paraphrasing;
|
| 146 |
+
- `integer`: whole number;
|
| 147 |
+
- `number`: integer or decimal number;
|
| 148 |
+
- `date-time`: ISO-8601 date, time or date-time;
|
| 149 |
+
- Other specific types such as `data`, `time`, `country`, `currency`, `email` and so on.
|
| 150 |
+
[**For more details, read the complete types specifications and examples**](TYPES.md)
|
| 151 |
+
|
| 152 |
+
Template constructors:
|
| 153 |
+
|
| 154 |
+
- Arrays, for example `["string"]`;
|
| 155 |
+
- Enums, for example `["yes", "no", "maybe"]`;
|
| 156 |
+
- Multi-enums (multiple possible values), for example `[["A", "B", "C"]]`.
|
| 157 |
+
|
| 158 |
+
If the model does not find relevant information for a field, it returns `null` or `[]`.
|
| 159 |
+
|
| 160 |
+
### Converting JSON schema / Pydantic models to NuExtract template
|
| 161 |
+
|
| 162 |
+
Our Python SDK (`pip install numind`) offers a method to convert JSON schemas to NuExtract templates:
|
| 163 |
+
|
| 164 |
+
```Python
|
| 165 |
+
from typing import Literal
|
| 166 |
+
|
| 167 |
+
from pydantic import Field, BaseModel
|
| 168 |
+
from numind.nuextract_utils import convert_json_schema_to_nuextract_template
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
class HotelBooking(BaseModel):
|
| 172 |
+
city: str
|
| 173 |
+
check_in_date: str = Field(description="date")
|
| 174 |
+
check_out_date: str = Field(description="date")
|
| 175 |
+
number_of_guests: int
|
| 176 |
+
room_type: Literal["single", "double", "suite"]
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
template, dropped_branches = convert_json_schema_to_nuextract_template(
|
| 180 |
+
HotelBooking.model_json_schema()
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
# {'check_in_date': 'date', 'check_out_date': 'date', 'city': 'string', 'number_of_guests': 'integer', 'room_type': ['single', 'double', 'suite']}
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
## Document-to-Markdown
|
| 187 |
+
|
| 188 |
+
NuExtract can also convert document images into clean Markdown. Output will be markdown for text (headers etc), html for tables, latex for mat and ```<figure data-type="image" data-id="img_n"><img src="/NM-dev/model_card-A/resolve/main/img_n.png" alt="Detail description of the images"/> ```
|
| 189 |
+
|
| 190 |
+
Markdown example:
|
| 191 |
+
|
| 192 |
+
```markdown
|
| 193 |
+
<figure data-type="image" data-id="img_1">
|
| 194 |
+
<img src="img_1.png" alt="Logo of Mobilier 2000 with contact information: Tél.: (418) 275-4232, 1654, boul. Marcotte, Roberval (Qc) G8H 2P2"/>
|
| 195 |
+
</figure>
|
| 196 |
+
|
| 197 |
+
# COMMANDE
|
| 198 |
+
**NUMÉRO 72259**
|
| 199 |
+
|
| 200 |
+
1
|
| 201 |
+
|
| 202 |
+
**Vendu à**
|
| 203 |
+
TREMBLAY ERIC
|
| 204 |
+
ERIC TREMBLAY
|
| 205 |
+
348 BOUL. DE L'ANSE
|
| 206 |
+
ROBERVAL
|
| 207 |
+
G8H 1Y9
|
| 208 |
+
|
| 209 |
+
**Livré à**
|
| 210 |
+
TREMBLAY ERIC
|
| 211 |
+
ERIC TREMBLAY
|
| 212 |
+
348 BOUL. DE L'ANSE
|
| 213 |
+
ROBERVAL
|
| 214 |
+
G8H 1Y9
|
| 215 |
+
|
| 216 |
+
<table>
|
| 217 |
+
<thead>
|
| 218 |
+
<tr>
|
| 219 |
+
<th># CLIENT</th>
|
| 220 |
+
<th>EXPÉDITEUR</th>
|
| 221 |
+
<th>TERME DE CRÉDIT</th>
|
| 222 |
+
<th>DATE</th>
|
| 223 |
+
</tr>
|
| 224 |
+
</thead>
|
| 225 |
+
<tbody>
|
| 226 |
+
<tr>
|
| 227 |
+
<td>2753133</td>
|
| 228 |
+
<td>Notre camion</td>
|
| 229 |
+
<td>à la livraison</td>
|
| 230 |
+
<td>22/06/2023</td>
|
| 231 |
+
</tr>
|
| 232 |
+
</tbody>
|
| 233 |
+
</table>
|
| 234 |
+
|
| 235 |
+
<table>
|
| 236 |
+
<thead>
|
| 237 |
+
<tr>
|
| 238 |
+
<th>NOM DU VENDEUR</th>
|
| 239 |
+
<th>VOTRE ÉCONOMIE !</th>
|
| 240 |
+
<th># COMMANDE</th>
|
| 241 |
+
</tr>
|
| 242 |
+
</thead>
|
| 243 |
+
<tbody>
|
| 244 |
+
<tr>
|
| 245 |
+
<td>Éric</td>
|
| 246 |
+
<td>0.00</td>
|
| 247 |
+
<td></td>
|
| 248 |
+
</tr>
|
| 249 |
+
</tbody>
|
| 250 |
+
</table>
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
---
|
| 254 |
+
|
| 255 |
+
## Reasoning and non-reasoning modes
|
| 256 |
+
|
| 257 |
+
NuExtract supports both reasoning and non-reasoning inference.
|
| 258 |
+
|
| 259 |
+
### Non-thinking mode
|
| 260 |
+
|
| 261 |
+
Use this for fast and deterministic extraction or Markdown conversion.
|
| 262 |
+
|
| 263 |
+
```python
|
| 264 |
+
enable_thinking = False
|
| 265 |
+
temperature = 0.2
|
| 266 |
+
```
|
| 267 |
+
|
| 268 |
+
### Thinking mode
|
| 269 |
+
|
| 270 |
+
Use this for difficult documents, complex layouts, ambiguous fields, or cases where the document structure requires additional reasoning.
|
| 271 |
+
|
| 272 |
+
```python
|
| 273 |
+
enable_thinking = True
|
| 274 |
+
temperature = 0.6
|
| 275 |
+
```
|
| 276 |
+
|
| 277 |
+
For production extraction workloads, we recommend starting with **non-reasoning mode** and enabling reasoning only for difficult examples.
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
---
|
| 281 |
+
|
| 282 |
+
## vLLM deployment
|
| 283 |
+
|
| 284 |
+
NuExtract can be served with vLLM using an OpenAI-compatible API.
|
| 285 |
+
|
| 286 |
+
```bash
|
| 287 |
+
vllm serve numind/NuExtract3 \
|
| 288 |
+
--trust-remote-code \
|
| 289 |
+
--limit-mm-per-prompt '{"image": 99, "video": 0}' \
|
| 290 |
+
--chat-template-content-format openai \
|
| 291 |
+
--generation-config vllm \
|
| 292 |
+
--max-model-len 131072 \
|
| 293 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 294 |
+
```
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
### Multi Token Prediction
|
| 298 |
+
<details>
|
| 299 |
+
The deployment commands above enable Multi Token Prediction (MTP) through vLLM speculative decoding:
|
| 300 |
+
|
| 301 |
+
```bash
|
| 302 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 303 |
+
```
|
| 304 |
+
|
| 305 |
+
MTP can improve decoding throughput without changing the OpenAI-compatible request payload. You can tune `num_speculative_tokens` for your hardware and workload, or remove `--speculative-config` if your vLLM version or environment does not support this speculative decoding method.
|
| 306 |
+
|
| 307 |
+
If you encounter memory issues, reduce the maximum model length and the maximum number of images:
|
| 308 |
+
|
| 309 |
+
```bash
|
| 310 |
+
vllm serve numind/NuExtract-3 \
|
| 311 |
+
--trust-remote-code \
|
| 312 |
+
--limit-mm-per-prompt '{"image": 6, "video": 0}' \
|
| 313 |
+
--chat-template-content-format openai \
|
| 314 |
+
--generation-config vllm \
|
| 315 |
+
--max-model-len 16384 \
|
| 316 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 317 |
+
```
|
| 318 |
+
</details>
|
| 319 |
+
|
| 320 |
+
## vLLM inference: structured extraction: text
|
| 321 |
+
```python
|
| 322 |
+
import json
|
| 323 |
+
from openai import OpenAI
|
| 324 |
+
|
| 325 |
+
client = OpenAI(
|
| 326 |
+
api_key="EMPTY",
|
| 327 |
+
base_url="http://localhost:8000/v1",
|
| 328 |
+
)
|
| 329 |
+
|
| 330 |
+
template = {
|
| 331 |
+
"store": "verbatim-string",
|
| 332 |
+
"date": "date-time",
|
| 333 |
+
"total": "number",
|
| 334 |
+
"currency": ["USD", "EUR", "GBP", "JPY", "Other"],
|
| 335 |
+
"items": [
|
| 336 |
+
{
|
| 337 |
+
"name": "verbatim-string",
|
| 338 |
+
"price": "number"
|
| 339 |
+
}
|
| 340 |
+
]
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
response = client.chat.completions.create(
|
| 344 |
+
model="numind/NuExtract3",
|
| 345 |
+
temperature=0.2,
|
| 346 |
+
messages=[
|
| 347 |
+
{
|
| 348 |
+
"role": "user",
|
| 349 |
+
"content": [
|
| 350 |
+
{
|
| 351 |
+
"type": "text",
|
| 352 |
+
"text": "Yesterday I bought apples and coffee at Trader Joe's for a total of $12.40."
|
| 353 |
+
}
|
| 354 |
+
],
|
| 355 |
+
}
|
| 356 |
+
],
|
| 357 |
+
extra_body={
|
| 358 |
+
"chat_template_kwargs": {
|
| 359 |
+
"template": json.dumps(template),
|
| 360 |
+
"instructions": "Specify the time for the `date` entry only if it is present, otherwise only output the date component.",
|
| 361 |
+
"enable_thinking": False
|
| 362 |
+
}
|
| 363 |
+
}
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
+
print(response.choices[0].message.content)
|
| 367 |
+
```
|
| 368 |
+
|
| 369 |
+
Example output:
|
| 370 |
+
|
| 371 |
+
```json
|
| 372 |
+
{
|
| 373 |
+
"store": "Trader Joe's",
|
| 374 |
+
"date": null,
|
| 375 |
+
"total": 12.40,
|
| 376 |
+
"currency": "USD",
|
| 377 |
+
"items": [
|
| 378 |
+
{
|
| 379 |
+
"name": "apples",
|
| 380 |
+
"price": null
|
| 381 |
+
},
|
| 382 |
+
{
|
| 383 |
+
"name": "coffee",
|
| 384 |
+
"price": null
|
| 385 |
+
}
|
| 386 |
+
]
|
| 387 |
+
}
|
| 388 |
+
```
|
| 389 |
+
|
| 390 |
+
---
|
| 391 |
+
|
| 392 |
+
## vLLM inference: structured extraction: image
|
| 393 |
+
|
| 394 |
+
```python
|
| 395 |
+
import json
|
| 396 |
+
import base64
|
| 397 |
+
from openai import OpenAI
|
| 398 |
+
|
| 399 |
+
client = OpenAI(
|
| 400 |
+
api_key="EMPTY",
|
| 401 |
+
base_url="http://localhost:8000/v1",
|
| 402 |
+
)
|
| 403 |
+
|
| 404 |
+
def encode_image(image_path):
|
| 405 |
+
with open(image_path, "rb") as image_file:
|
| 406 |
+
return base64.b64encode(image_file.read()).decode("utf-8")
|
| 407 |
+
|
| 408 |
+
image_base64 = encode_image("receipt.png")
|
| 409 |
+
data_url = f"data:image/png;base64,{image_base64}"
|
| 410 |
+
|
| 411 |
+
template = {
|
| 412 |
+
"store": "verbatim-string",
|
| 413 |
+
"date": "date-time",
|
| 414 |
+
"total": "number",
|
| 415 |
+
"payment_method": "verbatim-string"
|
| 416 |
+
}
|
| 417 |
+
|
| 418 |
+
response = client.chat.completions.create(
|
| 419 |
+
model="numind/NuExtract3",
|
| 420 |
+
temperature=0.2,
|
| 421 |
+
messages=[
|
| 422 |
+
{
|
| 423 |
+
"role": "user",
|
| 424 |
+
"content": [
|
| 425 |
+
{
|
| 426 |
+
"type": "image_url",
|
| 427 |
+
"image_url": {"url": data_url}
|
| 428 |
+
}
|
| 429 |
+
],
|
| 430 |
+
}
|
| 431 |
+
],
|
| 432 |
+
extra_body={
|
| 433 |
+
"chat_template_kwargs": {
|
| 434 |
+
"template": json.dumps(template, indent=4),
|
| 435 |
+
"enable_thinking": False
|
| 436 |
+
}
|
| 437 |
+
}
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
print(response.choices[0].message.content)
|
| 441 |
+
```
|
| 442 |
+
|
| 443 |
+
Example output:
|
| 444 |
+
|
| 445 |
+
```json
|
| 446 |
+
{
|
| 447 |
+
"store": "Trader Joe's",
|
| 448 |
+
"date": "2025-04-12",
|
| 449 |
+
"total": 42.85,
|
| 450 |
+
"payment_method": "Visa"
|
| 451 |
+
}
|
| 452 |
+
```
|
| 453 |
+
|
| 454 |
+
### Multiple page PDF
|
| 455 |
+
<details>
|
| 456 |
+
You can render a PDF to one PNG image per page with PyMuPDF, then pass the images to vLLM in page order.
|
| 457 |
+
|
| 458 |
+
```python
|
| 459 |
+
import base64
|
| 460 |
+
import json
|
| 461 |
+
|
| 462 |
+
import fitz # pip install pymupdf
|
| 463 |
+
from openai import OpenAI
|
| 464 |
+
|
| 465 |
+
client = OpenAI(
|
| 466 |
+
api_key="EMPTY",
|
| 467 |
+
base_url="http://localhost:8000/v1",
|
| 468 |
+
)
|
| 469 |
+
|
| 470 |
+
def pdf_to_png_data_urls(pdf_path, dpi=170):
|
| 471 |
+
data_urls = []
|
| 472 |
+
|
| 473 |
+
with fitz.open(pdf_path) as doc:
|
| 474 |
+
for page in doc:
|
| 475 |
+
pix = page.get_pixmap(dpi=dpi, alpha=False)
|
| 476 |
+
png_bytes = pix.tobytes("png")
|
| 477 |
+
png_base64 = base64.b64encode(png_bytes).decode("utf-8")
|
| 478 |
+
data_urls.append(f"data:image/png;base64,{png_base64}")
|
| 479 |
+
|
| 480 |
+
return data_urls
|
| 481 |
+
|
| 482 |
+
data_urls = pdf_to_png_data_urls("invoice.pdf", dpi=170)
|
| 483 |
+
|
| 484 |
+
template = {
|
| 485 |
+
"invoice_number": "verbatim-string",
|
| 486 |
+
"invoice_date": "date",
|
| 487 |
+
"total": "number",
|
| 488 |
+
"currency": "currency",
|
| 489 |
+
"line_items": [
|
| 490 |
+
{
|
| 491 |
+
"description": "verbatim-string",
|
| 492 |
+
"quantity": "number",
|
| 493 |
+
"unit_price": "number",
|
| 494 |
+
"total": "number"
|
| 495 |
+
}
|
| 496 |
+
]
|
| 497 |
+
}
|
| 498 |
+
|
| 499 |
+
response = client.chat.completions.create(
|
| 500 |
+
model="numind/NuExtract3",
|
| 501 |
+
temperature=0.2,
|
| 502 |
+
messages=[
|
| 503 |
+
{
|
| 504 |
+
"role": "user",
|
| 505 |
+
"content": [
|
| 506 |
+
{
|
| 507 |
+
"type": "image_url",
|
| 508 |
+
"image_url": {"url": data_url}
|
| 509 |
+
}
|
| 510 |
+
for data_url in data_urls
|
| 511 |
+
],
|
| 512 |
+
}
|
| 513 |
+
],
|
| 514 |
+
extra_body={
|
| 515 |
+
"chat_template_kwargs": {
|
| 516 |
+
"template": json.dumps(template, indent=4),
|
| 517 |
+
"enable_thinking": False
|
| 518 |
+
}
|
| 519 |
+
}
|
| 520 |
+
)
|
| 521 |
+
|
| 522 |
+
print(response.choices[0].message.content)
|
| 523 |
+
```
|
| 524 |
+
</details>
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
|
| 528 |
+
## vLLM inference: document-to-Markdown
|
| 529 |
+
|
| 530 |
+
For Markdown OCR, use `mode="markdown"` or `mode="content"` without a template.
|
| 531 |
+
|
| 532 |
+
```python
|
| 533 |
+
import base64
|
| 534 |
+
from openai import OpenAI
|
| 535 |
+
|
| 536 |
+
client = OpenAI(
|
| 537 |
+
api_key="EMPTY",
|
| 538 |
+
base_url="http://localhost:8000/v1",
|
| 539 |
+
)
|
| 540 |
+
|
| 541 |
+
def encode_image(image_path):
|
| 542 |
+
with open(image_path, "rb") as image_file:
|
| 543 |
+
return base64.b64encode(image_file.read()).decode("utf-8")
|
| 544 |
+
|
| 545 |
+
image_base64 = encode_image("document.png")
|
| 546 |
+
data_url = f"data:image/png;base64,{image_base64}"
|
| 547 |
+
|
| 548 |
+
response = client.chat.completions.create(
|
| 549 |
+
model="numind/NuExtract3",
|
| 550 |
+
temperature=1,
|
| 551 |
+
messages=[
|
| 552 |
+
{
|
| 553 |
+
"role": "user",
|
| 554 |
+
"content": [
|
| 555 |
+
{
|
| 556 |
+
"type": "image_url",
|
| 557 |
+
"image_url": {"url": data_url}
|
| 558 |
+
}
|
| 559 |
+
],
|
| 560 |
+
}
|
| 561 |
+
],
|
| 562 |
+
extra_body={
|
| 563 |
+
"chat_template_kwargs": {
|
| 564 |
+
"mode": "markdown",
|
| 565 |
+
"enable_thinking": False
|
| 566 |
+
}
|
| 567 |
+
}
|
| 568 |
+
)
|
| 569 |
+
|
| 570 |
+
print(response.choices[0].message.content)
|
| 571 |
+
```
|
| 572 |
+
|
| 573 |
+
---
|
| 574 |
+
|
| 575 |
+
## vLLM inference: reasoning mode
|
| 576 |
+
<details>
|
| 577 |
+
Reasoning can be enabled for harder structured extraction or Markdown tasks.
|
| 578 |
+
|
| 579 |
+
```python
|
| 580 |
+
response = client.chat.completions.create(
|
| 581 |
+
model="numind/NuExtract3",
|
| 582 |
+
temperature=1,
|
| 583 |
+
messages=[
|
| 584 |
+
{
|
| 585 |
+
"role": "user",
|
| 586 |
+
"content": [
|
| 587 |
+
{
|
| 588 |
+
"type": "image_url",
|
| 589 |
+
"image_url": {"url": data_url}
|
| 590 |
+
}
|
| 591 |
+
],
|
| 592 |
+
}
|
| 593 |
+
],
|
| 594 |
+
extra_body={
|
| 595 |
+
"chat_template_kwargs": {
|
| 596 |
+
"mode": "markdown",
|
| 597 |
+
"enable_thinking": True
|
| 598 |
+
}
|
| 599 |
+
}
|
| 600 |
+
)
|
| 601 |
+
|
| 602 |
+
result = response.choices[0].message.content
|
| 603 |
+
|
| 604 |
+
if "</think>" in result:
|
| 605 |
+
reasoning, answer = [part.strip() for part in result.split("</think>")]
|
| 606 |
+
else:
|
| 607 |
+
reasoning, answer = None, result
|
| 608 |
+
|
| 609 |
+
print(answer)
|
| 610 |
+
```
|
| 611 |
+
</details>
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
## In-context examples for extraction
|
| 615 |
+
<details>
|
| 616 |
+
NuExtract supports in-context examples for structured extraction.
|
| 617 |
+
|
| 618 |
+
Examples are especially useful when the desired formatting is ambiguous or when the schema requires task-specific conventions. Examples can be provided by using `developer` messages, for which all items of the contents except the last one are the input, and the last one is the expected output.
|
| 619 |
+
|
| 620 |
+
```python
|
| 621 |
+
import json
|
| 622 |
+
from openai import OpenAI
|
| 623 |
+
|
| 624 |
+
client = OpenAI(
|
| 625 |
+
api_key="EMPTY",
|
| 626 |
+
base_url="http://localhost:8000/v1",
|
| 627 |
+
)
|
| 628 |
+
|
| 629 |
+
template = {
|
| 630 |
+
"names": ["string"]
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
response = client.chat.completions.create(
|
| 634 |
+
model="numind/NuExtract3",
|
| 635 |
+
temperature=0.2,
|
| 636 |
+
messages=[
|
| 637 |
+
{
|
| 638 |
+
"role": "developer",
|
| 639 |
+
"content": [
|
| 640 |
+
{
|
| 641 |
+
"type": "text",
|
| 642 |
+
"text": "Stephen is the manager at Susan's store.",
|
| 643 |
+
},
|
| 644 |
+
{
|
| 645 |
+
"type": "text",
|
| 646 |
+
"text": "{\"names\": [\"-STEPHEN-\", \"-SUSAN-\"]}",
|
| 647 |
+
}
|
| 648 |
+
],
|
| 649 |
+
},
|
| 650 |
+
{
|
| 651 |
+
"role": "user",
|
| 652 |
+
"content": [
|
| 653 |
+
{
|
| 654 |
+
"type": "text",
|
| 655 |
+
"text": "John went to the restaurant with Mary. James went to the cinema."
|
| 656 |
+
}
|
| 657 |
+
],
|
| 658 |
+
}
|
| 659 |
+
],
|
| 660 |
+
extra_body={
|
| 661 |
+
"chat_template_kwargs": {
|
| 662 |
+
"template": json.dumps(template, indent=4),
|
| 663 |
+
"enable_thinking": False
|
| 664 |
+
}
|
| 665 |
+
}
|
| 666 |
+
)
|
| 667 |
+
|
| 668 |
+
print(response.choices[0].message.content)
|
| 669 |
+
```
|
| 670 |
+
|
| 671 |
+
Example output:
|
| 672 |
+
|
| 673 |
+
```json
|
| 674 |
+
{
|
| 675 |
+
"names": ["-JOHN-", "-MARY-", "-JAMES-"]
|
| 676 |
+
}
|
| 677 |
+
```
|
| 678 |
+
</details>
|
| 679 |
+
|
| 680 |
+
|
| 681 |
+
## vLLM inference: template generation
|
| 682 |
+
|
| 683 |
+
NuExtract can generate an extraction template from a natural language description.
|
| 684 |
+
|
| 685 |
+
```python
|
| 686 |
+
from openai import OpenAI
|
| 687 |
+
|
| 688 |
+
client = OpenAI(
|
| 689 |
+
api_key="EMPTY",
|
| 690 |
+
base_url="http://localhost:8000/v1",
|
| 691 |
+
)
|
| 692 |
+
|
| 693 |
+
response = client.chat.completions.create(
|
| 694 |
+
model="numind/NuExtract3",
|
| 695 |
+
temperature=0.2,
|
| 696 |
+
messages=[
|
| 697 |
+
{
|
| 698 |
+
"role": "user",
|
| 699 |
+
"content": [
|
| 700 |
+
{
|
| 701 |
+
"type": "text",
|
| 702 |
+
"text": "I want to extract the key details from a rental contract."
|
| 703 |
+
}
|
| 704 |
+
],
|
| 705 |
+
}
|
| 706 |
+
],
|
| 707 |
+
extra_body={
|
| 708 |
+
"chat_template_kwargs": {
|
| 709 |
+
"mode": "template-generation"
|
| 710 |
+
}
|
| 711 |
+
}
|
| 712 |
+
)
|
| 713 |
+
|
| 714 |
+
print(response.choices[0].message.content)
|
| 715 |
+
```
|
| 716 |
+
|
| 717 |
+
Example output:
|
| 718 |
+
|
| 719 |
+
```json
|
| 720 |
+
{
|
| 721 |
+
"contract_title": "verbatim-string",
|
| 722 |
+
"landlord": "verbatim-string",
|
| 723 |
+
"tenant": "verbatim-string",
|
| 724 |
+
"property_address": "verbatim-string",
|
| 725 |
+
"start_date": "date-time",
|
| 726 |
+
"end_date": "date-time",
|
| 727 |
+
"monthly_rent": "number",
|
| 728 |
+
"currency": "verbatim-string",
|
| 729 |
+
"deposit": "number",
|
| 730 |
+
"signatories": ["verbatim-string"]
|
| 731 |
+
}
|
| 732 |
+
```
|
| 733 |
+
|
| 734 |
+
## Curl examples
|
| 735 |
+
<details>
|
| 736 |
+
|
| 737 |
+
The following examples assume that vLLM is running locally on port 8000. They use `jq` to build valid JSON request bodies without manually escaping the image data or template string.
|
| 738 |
+
|
| 739 |
+
### Single image structured extraction
|
| 740 |
+
|
| 741 |
+
```bash
|
| 742 |
+
API_KEY="EMPTY"
|
| 743 |
+
IMAGE_BASE64_FILE=$(mktemp)
|
| 744 |
+
REQUEST_BODY_FILE=$(mktemp)
|
| 745 |
+
|
| 746 |
+
base64 < receipt.png | tr -d '\n' > "$IMAGE_BASE64_FILE"
|
| 747 |
+
|
| 748 |
+
TEMPLATE=$(cat <<'JSON'
|
| 749 |
+
{
|
| 750 |
+
"store": "verbatim-string",
|
| 751 |
+
"date": "date-time",
|
| 752 |
+
"total": "number",
|
| 753 |
+
"payment_method": "verbatim-string"
|
| 754 |
+
}
|
| 755 |
+
JSON
|
| 756 |
+
)
|
| 757 |
+
|
| 758 |
+
jq -n \
|
| 759 |
+
--rawfile image_base64 "$IMAGE_BASE64_FILE" \
|
| 760 |
+
--arg template "$TEMPLATE" \
|
| 761 |
+
'{
|
| 762 |
+
model: "numind/NuExtract3",
|
| 763 |
+
temperature: 0.6,
|
| 764 |
+
messages: [
|
| 765 |
+
{
|
| 766 |
+
role: "user",
|
| 767 |
+
content: [
|
| 768 |
+
{
|
| 769 |
+
type: "image_url",
|
| 770 |
+
image_url: {url: ("data:image/png;base64," + $image_base64)}
|
| 771 |
+
}
|
| 772 |
+
]
|
| 773 |
+
}
|
| 774 |
+
],
|
| 775 |
+
chat_template_kwargs: {
|
| 776 |
+
template: $template,
|
| 777 |
+
enable_thinking: false
|
| 778 |
+
}
|
| 779 |
+
}' > "$REQUEST_BODY_FILE"
|
| 780 |
+
|
| 781 |
+
curl http://localhost:8000/v1/chat/completions \
|
| 782 |
+
-H "Content-Type: application/json" \
|
| 783 |
+
-H "Authorization: Bearer $API_KEY" \
|
| 784 |
+
--data-binary "@$REQUEST_BODY_FILE"
|
| 785 |
+
|
| 786 |
+
rm "$IMAGE_BASE64_FILE" "$REQUEST_BODY_FILE"
|
| 787 |
+
```
|
| 788 |
+
|
| 789 |
+
### Single image content extraction
|
| 790 |
+
|
| 791 |
+
```bash
|
| 792 |
+
API_KEY="EMPTY"
|
| 793 |
+
IMAGE_BASE64_FILE=$(mktemp)
|
| 794 |
+
REQUEST_BODY_FILE=$(mktemp)
|
| 795 |
+
|
| 796 |
+
base64 < document.png | tr -d '\n' > "$IMAGE_BASE64_FILE"
|
| 797 |
+
|
| 798 |
+
jq -n \
|
| 799 |
+
--rawfile image_base64 "$IMAGE_BASE64_FILE" \
|
| 800 |
+
'{
|
| 801 |
+
model: "numind/NuExtract3",
|
| 802 |
+
temperature: 0.6,
|
| 803 |
+
messages: [
|
| 804 |
+
{
|
| 805 |
+
role: "user",
|
| 806 |
+
content: [
|
| 807 |
+
{
|
| 808 |
+
type: "image_url",
|
| 809 |
+
image_url: {url: ("data:image/png;base64," + $image_base64)}
|
| 810 |
+
}
|
| 811 |
+
]
|
| 812 |
+
}
|
| 813 |
+
],
|
| 814 |
+
chat_template_kwargs: {
|
| 815 |
+
mode: "content",
|
| 816 |
+
enable_thinking: false
|
| 817 |
+
}
|
| 818 |
+
}' > "$REQUEST_BODY_FILE"
|
| 819 |
+
|
| 820 |
+
curl http://localhost:8000/v1/chat/completions \
|
| 821 |
+
-H "Content-Type: application/json" \
|
| 822 |
+
-H "Authorization: Bearer $API_KEY" \
|
| 823 |
+
--data-binary "@$REQUEST_BODY_FILE"
|
| 824 |
+
|
| 825 |
+
rm "$IMAGE_BASE64_FILE" "$REQUEST_BODY_FILE"
|
| 826 |
+
```
|
| 827 |
+
</details>
|
| 828 |
+
|
| 829 |
+
|
| 830 |
+
## Transformers example
|
| 831 |
+
<details>
|
| 832 |
+
You can also run NuExtract directly with `transformers`. The same `template`, `mode`, and `enable_thinking` options are passed to `processor.apply_chat_template`.
|
| 833 |
+
|
| 834 |
+
```python
|
| 835 |
+
import json
|
| 836 |
+
|
| 837 |
+
import torch
|
| 838 |
+
from PIL import Image
|
| 839 |
+
from transformers import AutoModelForImageTextToText, AutoProcessor
|
| 840 |
+
|
| 841 |
+
model_id = "numind/NuExtract3"
|
| 842 |
+
|
| 843 |
+
processor = AutoProcessor.from_pretrained(
|
| 844 |
+
model_id,
|
| 845 |
+
trust_remote_code=True,
|
| 846 |
+
)
|
| 847 |
+
model = AutoModelForImageTextToText.from_pretrained(
|
| 848 |
+
model_id,
|
| 849 |
+
dtype=torch.bfloat16,
|
| 850 |
+
device_map="auto",
|
| 851 |
+
trust_remote_code=True,
|
| 852 |
+
).eval()
|
| 853 |
+
|
| 854 |
+
def run_nuextract(messages, **chat_template_kwargs):
|
| 855 |
+
inputs = processor.apply_chat_template(
|
| 856 |
+
messages,
|
| 857 |
+
add_generation_prompt=True,
|
| 858 |
+
tokenize=True,
|
| 859 |
+
return_dict=True,
|
| 860 |
+
return_tensors="pt",
|
| 861 |
+
**chat_template_kwargs,
|
| 862 |
+
).to(model.device)
|
| 863 |
+
|
| 864 |
+
with torch.inference_mode():
|
| 865 |
+
generated_ids = model.generate(
|
| 866 |
+
**inputs,
|
| 867 |
+
max_new_tokens=4096,
|
| 868 |
+
do_sample=False,
|
| 869 |
+
)
|
| 870 |
+
|
| 871 |
+
generated_ids = generated_ids[:, inputs.input_ids.shape[1]:]
|
| 872 |
+
return processor.batch_decode(
|
| 873 |
+
generated_ids,
|
| 874 |
+
skip_special_tokens=True,
|
| 875 |
+
clean_up_tokenization_spaces=False,
|
| 876 |
+
)[0].strip()
|
| 877 |
+
|
| 878 |
+
# Single image structured extraction
|
| 879 |
+
receipt_image = Image.open("receipt.png").convert("RGB")
|
| 880 |
+
receipt_messages = [
|
| 881 |
+
{
|
| 882 |
+
"role": "user",
|
| 883 |
+
"content": [
|
| 884 |
+
{
|
| 885 |
+
"type": "image",
|
| 886 |
+
"image": receipt_image,
|
| 887 |
+
}
|
| 888 |
+
],
|
| 889 |
+
}
|
| 890 |
+
]
|
| 891 |
+
|
| 892 |
+
template = {
|
| 893 |
+
"store": "verbatim-string",
|
| 894 |
+
"date": "date-time",
|
| 895 |
+
"total": "number",
|
| 896 |
+
"payment_method": "verbatim-string"
|
| 897 |
+
}
|
| 898 |
+
|
| 899 |
+
structured_output = run_nuextract(
|
| 900 |
+
receipt_messages,
|
| 901 |
+
template=json.dumps(template, indent=4),
|
| 902 |
+
enable_thinking=False,
|
| 903 |
+
)
|
| 904 |
+
print(structured_output)
|
| 905 |
+
|
| 906 |
+
# Single image content extraction
|
| 907 |
+
document_image = Image.open("document.png").convert("RGB")
|
| 908 |
+
document_messages = [
|
| 909 |
+
{
|
| 910 |
+
"role": "user",
|
| 911 |
+
"content": [
|
| 912 |
+
{
|
| 913 |
+
"type": "image",
|
| 914 |
+
"image": document_image,
|
| 915 |
+
}
|
| 916 |
+
],
|
| 917 |
+
}
|
| 918 |
+
]
|
| 919 |
+
|
| 920 |
+
content_output = run_nuextract(
|
| 921 |
+
document_messages,
|
| 922 |
+
mode="content",
|
| 923 |
+
enable_thinking=False,
|
| 924 |
+
)
|
| 925 |
+
print(content_output)
|
| 926 |
+
```
|
| 927 |
+
</details>
|
| 928 |
+
|
| 929 |
+
Special thanks to the Lambda.ai team for the compute that made this project a success.
|
| 930 |
+
|
| 931 |
+
## Citation
|
| 932 |
+
|
| 933 |
+
If you use NuExtract, please cite NuMind and link to the model page.
|
| 934 |
+
|
| 935 |
+
```bibtex
|
| 936 |
+
@misc{nuextract3,
|
| 937 |
+
title = {NuExtract3},
|
| 938 |
+
author = {NuMind},
|
| 939 |
+
year = {2026},
|
| 940 |
+
url = {https://nuextract.ai/}
|
| 941 |
+
}
|
| 942 |
+
```
|
.agents/references/nuextract3/2026-07-20/huggingface.co/numind/NuExtract3.md
ADDED
|
@@ -0,0 +1,1010 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!--
|
| 2 |
+
source: https://huggingface.co/numind/NuExtract3
|
| 3 |
+
retrieved: 2026-07-20T17:29:25.403047+00:00
|
| 4 |
+
final_url: https://huggingface.co/numind/NuExtract3
|
| 5 |
+
content_type: text/html
|
| 6 |
+
-->
|
| 7 |
+
|
| 8 |
+
# numind/NuExtract3 · Hugging Face
|
| 9 |
+
|
| 10 |
+
[Hugging Face](https://huggingface.co/)
|
| 11 |
+
|
| 12 |
+
#
|
| 13 |
+
|
| 14 |
+
[https://huggingface.co/numind](https://huggingface.co/numind)
|
| 15 |
+
|
| 16 |
+
[numind](https://huggingface.co/numind)
|
| 17 |
+
|
| 18 |
+
/
|
| 19 |
+
|
| 20 |
+
[NuExtract3](https://huggingface.co/numind/NuExtract3)
|
| 21 |
+
|
| 22 |
+
like 295
|
| 23 |
+
|
| 24 |
+
Follow
|
| 25 |
+
|
| 26 |
+
NuMind 649
|
| 27 |
+
|
| 28 |
+
[Image-to-Text](https://huggingface.co/models?pipeline_tag=image-to-text)
|
| 29 |
+
|
| 30 |
+
[Transformers](https://huggingface.co/models?library=transformers)
|
| 31 |
+
|
| 32 |
+
[Safetensors](https://huggingface.co/models?library=safetensors)
|
| 33 |
+
|
| 34 |
+
[qwen3_5](https://huggingface.co/models?other=qwen3_5)
|
| 35 |
+
|
| 36 |
+
[image-text-to-text](https://huggingface.co/models?other=image-text-to-text)
|
| 37 |
+
|
| 38 |
+
[vision-language](https://huggingface.co/models?other=vision-language)
|
| 39 |
+
|
| 40 |
+
[vlm](https://huggingface.co/models?other=vlm)
|
| 41 |
+
|
| 42 |
+
[document-understanding](https://huggingface.co/models?other=document-understanding)
|
| 43 |
+
|
| 44 |
+
[structured-extraction](https://huggingface.co/models?other=structured-extraction)
|
| 45 |
+
|
| 46 |
+
[information-extraction](https://huggingface.co/models?other=information-extraction)
|
| 47 |
+
|
| 48 |
+
[ocr](https://huggingface.co/models?other=ocr)
|
| 49 |
+
|
| 50 |
+
[document-to-markdown](https://huggingface.co/models?other=document-to-markdown)
|
| 51 |
+
|
| 52 |
+
[markdown](https://huggingface.co/models?other=markdown)
|
| 53 |
+
|
| 54 |
+
[rag](https://huggingface.co/models?other=rag)
|
| 55 |
+
|
| 56 |
+
[reasoning](https://huggingface.co/models?other=reasoning)
|
| 57 |
+
|
| 58 |
+
[multilingual](https://huggingface.co/models?other=multilingual)
|
| 59 |
+
|
| 60 |
+
[conversational](https://huggingface.co/models?other=conversational)
|
| 61 |
+
|
| 62 |
+
[Eval Results](https://huggingface.co/models?other=eval-results)
|
| 63 |
+
|
| 64 |
+
License: apache-2.0
|
| 65 |
+
|
| 66 |
+
[Model card](https://huggingface.co/numind/NuExtract3)
|
| 67 |
+
|
| 68 |
+
[Files Files and versions xet](https://huggingface.co/numind/NuExtract3/tree/main)
|
| 69 |
+
|
| 70 |
+
[Community 2](https://huggingface.co/numind/NuExtract3/discussions)
|
| 71 |
+
|
| 72 |
+
Deploy
|
| 73 |
+
|
| 74 |
+
Copy to bucket new
|
| 75 |
+
|
| 76 |
+
Use this model
|
| 77 |
+
|
| 78 |
+
[https://nuextract.ai/](https://nuextract.ai/)
|
| 79 |
+
|
| 80 |
+
🖥️ [API / Platform](https://nuextract.ai/) | 📑 [Blog](https://numind.ai/blog) | 🗣️ [Discord](https://discord.gg/3tsEtJNCDe) | 🛠️ [GitHub](https://github.com/numindai/nuextract)
|
| 81 |
+
|
| 82 |
+
NuExtract3 is a unified 4B vision-language reasoning model for document understanding.
|
| 83 |
+
|
| 84 |
+
It combines strong structured information extraction with high-quality image-to-Markdown conversion, making it suitable for extraction pipelines, OCR, and RAG preprocessing for all types of documents such as scans, receipts, forms, invoices, contracts or tables.
|
| 85 |
+
|
| 86 |
+
Try it out in [the 🤗 space!](https://huggingface.co/spaces/numind/NuExtract-3-4B)
|
| 87 |
+
|
| 88 |
+
## [https://huggingface.co/numind/NuExtract3#overview](https://huggingface.co/numind/NuExtract3#overview) Overview
|
| 89 |
+
|
| 90 |
+
- Structured extraction : input (text/images) + JSON template + instructions --> JSON output
|
| 91 |
+
|
| 92 |
+
- Markdown conversion : input (text/images) --> Markdown
|
| 93 |
+
|
| 94 |
+
- Multimodal inputs : text, images, or text + images.
|
| 95 |
+
|
| 96 |
+
- Multilingual documents.
|
| 97 |
+
|
| 98 |
+
- Reasoning and non-reasoning inference modes.
|
| 99 |
+
|
| 100 |
+
- Template generation for structured extraction from natural language or input document.
|
| 101 |
+
|
| 102 |
+
# [https://huggingface.co/numind/NuExtract3#benchmark-results](https://huggingface.co/numind/NuExtract3#benchmark-results) Benchmark results
|
| 103 |
+
|
| 104 |
+
## [https://huggingface.co/numind/NuExtract3#structured-extraction](https://huggingface.co/numind/NuExtract3#structured-extraction) Structured Extraction
|
| 105 |
+
|
| 106 |
+
We benchmarked NuExtract on NuMind's internal structured benchmark, measuring model's performances on ~600 documents of diverse types including invoices, movie posters or floor plans. These documents and their ground-truth cover diverse use-cases testing model visual understanding, OCR, reasoning and ability to handle long input and output contexts. We plan to open-source this benchmark in the coming weeks, along with a extensive leaderboard including most popular open-weight and closed-sourced APIs and a Python library allowing to easily measure model performances on structured extraction.
|
| 107 |
+
|
| 108 |
+
To measure a pair of predicted and ground-truth JSONs, we represent both as trees which we align based on node names, compute metric scores for aligned leaves and report the average of these scores. `string` and `verbatim-string` leaves are evaluated with indel distance (i.e. Levenshtein without replacement), while all others are evaluated with exact-match. Models were evaluated using vllm, with a temperature of 0.25 and a maximum of 65000 output token (for both thinking and answer), which largely exceeds 22000 which is the number of tokens of the largest ground truth output.
|
| 109 |
+
|
| 110 |
+
Model name Average score Num. failed⁽¹⁾ Avg. num tokens thinking Avg. num tokens answer NuExtract3.4_4B-RL 0.651 ± 0.019 27 2036 1856 gemma-4-E4B-it 0.538 ± 0.023 31 3005 1287 Qwen3.5-9B 0.479 ± 0.030 170 22409 1257 Qwen3.5-4B 0.417 ± 0.031 229 27177 1201 GLM-4.6V-Flash 0.435 ± 0.026 153 2989 1357 Nemotron-3-Nano-Omni 0.387 ± 0.028 204 25827 522 Ministral-3-3B 0.240 ± 0.022 344 27586 362
|
| 111 |
+
|
| 112 |
+
(1) number of model outputs that were not JSON deserializable, either directly or by removing leading and trailing backticks.
|
| 113 |
+
95% confidence intervals computed using a nonparametric bootstrap over scores distributions.
|
| 114 |
+
|
| 115 |
+
The benchmark include samples containing multiple images resulting in large input context, and some with ground-truth containing large numbers of items to extract resulting in large outputs. We found that the reasoning of small models significantly negatively impact their performances. The reason is that many models ended up falling in repetition loops, hitting the output tokens limit and resulting in failed requests.
|
| 116 |
+
|
| 117 |
+
## [https://huggingface.co/numind/NuExtract3#document-to-markdown](https://huggingface.co/numind/NuExtract3#document-to-markdown) Document to Markdown
|
| 118 |
+
|
| 119 |
+
NuExtract can also convert document images into clean Markdown. Output will be Markdown for text (headers etc), HTML for tables, LaTeX for math and `<figure data-type="image" data-id="img_n"><img src="/NM-dev/model_card-A/resolve/main/img_n.png" alt="Detail description of the images"/> `
|
| 120 |
+
|
| 121 |
+
Modern, format-agnostic benchmarks for complex document understanding are limited, so we explored a new evaluation approach. We selected 100 documents with challenging layouts and tables, asked each model to convert them into a structured representation, then used Gemini 3 Flash to compare model outputs against the source document and choose the most accurate result. The rankings aligned with human votes, suggesting this is a promising method for evaluating document-to-Markdown capabilities. More details will be shared in an upcoming technical report. Here are some results:
|
| 122 |
+
|
| 123 |
+
### [https://huggingface.co/numind/NuExtract3#using-markdown-to-structured](https://huggingface.co/numind/NuExtract3#using-markdown-to-structured) Using "Markdown-to-structured"
|
| 124 |
+
|
| 125 |
+
To add other evaluate references, we used our structured extraction benchmark to evaluate models in a two-step fashion: convert the benchmark inputs to Markdown, then use Qwen3.6 27B to perform the structured extraction task on them. Intuitively, it allows to evaluate how models achieve to keep the input document content and layout: good models will allow the "structured extractor" model to perform better scores.
|
| 126 |
+
|
| 127 |
+
# [https://huggingface.co/numind/NuExtract3#using-nuextract](https://huggingface.co/numind/NuExtract3#using-nuextract) Using NuExtract
|
| 128 |
+
|
| 129 |
+
## [https://huggingface.co/numind/NuExtract3#structured-extraction-1](https://huggingface.co/numind/NuExtract3#structured-extraction-1) Structured extraction
|
| 130 |
+
|
| 131 |
+
Structured extraction takes as inputs:
|
| 132 |
+
|
| 133 |
+
1. An input document, which can be text, image, or both;
|
| 134 |
+
|
| 135 |
+
2. A JSON template describing the information to extract;
|
| 136 |
+
|
| 137 |
+
3. (Optional) Instructions, allowing to specify expected output formats or values, to provide with the `instructions` chat template kwarg;
|
| 138 |
+
|
| 139 |
+
4. (Optional) In-Context Learning (ICL) examples.
|
| 140 |
+
|
| 141 |
+
### [https://huggingface.co/numind/NuExtract3#input-json-template](https://huggingface.co/numind/NuExtract3#input-json-template) Input JSON template
|
| 142 |
+
|
| 143 |
+
NuExtract uses a input JSON template whose structure is identical to the output JSON. Its leaf values are specify the types of the output JSON leaves. For examples:
|
| 144 |
+
|
| 145 |
+
```
|
| 146 |
+
{
|
| 147 |
+
"invoice_number": "verbatim-string",
|
| 148 |
+
"invoice_date": "date",
|
| 149 |
+
"total_amount": "number",
|
| 150 |
+
"currency": "currency",
|
| 151 |
+
"line_items": [
|
| 152 |
+
{
|
| 153 |
+
"description": "verbatim-string",
|
| 154 |
+
"item_type": ["electronics", "clothing", "vehicle", "furniture", "other"],
|
| 155 |
+
"quantity": "integer",
|
| 156 |
+
"unit_price": "number",
|
| 157 |
+
"total": "number"
|
| 158 |
+
}
|
| 159 |
+
]
|
| 160 |
+
}
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
Supported template types include:
|
| 164 |
+
|
| 165 |
+
- `verbatim-string`: extract text exactly as it appears in the document;
|
| 166 |
+
|
| 167 |
+
- `string`: generic string field, allowing abstraction or light paraphrasing;
|
| 168 |
+
|
| 169 |
+
- `integer`: whole number;
|
| 170 |
+
|
| 171 |
+
- `number`: integer or decimal number;
|
| 172 |
+
|
| 173 |
+
- `date-time`: ISO-8601 date, time or date-time;
|
| 174 |
+
|
| 175 |
+
- Other specific types such as `data`, `time`, `country`, `currency`, `email` and so on. [For more details, read the complete types specifications and examples](https://huggingface.co/numind/NuExtract3/blob/main/TYPES.md)
|
| 176 |
+
|
| 177 |
+
Template constructors:
|
| 178 |
+
|
| 179 |
+
- Arrays, for example `["string"]`;
|
| 180 |
+
|
| 181 |
+
- Enums, for example `["yes", "no", "maybe"]`;
|
| 182 |
+
|
| 183 |
+
- Multi-enums (multiple possible values), for example `[["A", "B", "C"]]`.
|
| 184 |
+
|
| 185 |
+
If the model does not find relevant information for a field, it returns `null` or `[]`.
|
| 186 |
+
|
| 187 |
+
### [https://huggingface.co/numind/NuExtract3#converting-json-schema--pydantic-models-to-nuextract-template](https://huggingface.co/numind/NuExtract3#converting-json-schema--pydantic-models-to-nuextract-template) Converting JSON schema / Pydantic models to NuExtract template
|
| 188 |
+
|
| 189 |
+
Our Python SDK (`pip install numind`) offers a method to convert JSON schemas to NuExtract templates:
|
| 190 |
+
|
| 191 |
+
```
|
| 192 |
+
from typing import Literal
|
| 193 |
+
|
| 194 |
+
from pydantic import Field, BaseModel
|
| 195 |
+
from numind.nuextract_utils import convert_json_schema_to_nuextract_template
|
| 196 |
+
|
| 197 |
+
class HotelBooking(BaseModel):
|
| 198 |
+
city: str
|
| 199 |
+
check_in_date: str = Field(description="date")
|
| 200 |
+
check_out_date: str = Field(description="date")
|
| 201 |
+
number_of_guests: int
|
| 202 |
+
room_type: Literal["single", "double", "suite"]
|
| 203 |
+
|
| 204 |
+
template, dropped_branches = convert_json_schema_to_nuextract_template(
|
| 205 |
+
HotelBooking.model_json_schema()
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
# {'check_in_date': 'date', 'check_out_date': 'date', 'city': 'string', 'number_of_guests': 'integer', 'room_type': ['single', 'double', 'suite']}
|
| 209 |
+
```
|
| 210 |
+
|
| 211 |
+
## [https://huggingface.co/numind/NuExtract3#document-to-markdown-1](https://huggingface.co/numind/NuExtract3#document-to-markdown-1) Document-to-Markdown
|
| 212 |
+
|
| 213 |
+
NuExtract can also convert document images into clean Markdown. Output will be markdown for text (headers etc), html for tables, latex for mat and `<figure data-type="image" data-id="img_n"><img src="/NM-dev/model_card-A/resolve/main/img_n.png" alt="Detail description of the images"/> `
|
| 214 |
+
|
| 215 |
+
Markdown example:
|
| 216 |
+
|
| 217 |
+
```
|
| 218 |
+
<figure data-type="image" data-id="img_1">
|
| 219 |
+
<img src="/numind/NuExtract3/resolve/main/img_1.png" alt="Logo of Mobilier 2000 with contact information: Tél.: (418) 275-4232, 1654, boul. Marcotte, Roberval (Qc) G8H 2P2"/>
|
| 220 |
+
</figure>
|
| 221 |
+
|
| 222 |
+
# COMMANDE
|
| 223 |
+
**NUMÉRO 72259**
|
| 224 |
+
|
| 225 |
+
1
|
| 226 |
+
|
| 227 |
+
**Vendu à**
|
| 228 |
+
TREMBLAY ERIC
|
| 229 |
+
ERIC TREMBLAY
|
| 230 |
+
348 BOUL. DE L'ANSE
|
| 231 |
+
ROBERVAL
|
| 232 |
+
G8H 1Y9
|
| 233 |
+
|
| 234 |
+
**Livré à**
|
| 235 |
+
TREMBLAY ERIC
|
| 236 |
+
ERIC TREMBLAY
|
| 237 |
+
348 BOUL. DE L'ANSE
|
| 238 |
+
ROBERVAL
|
| 239 |
+
G8H 1Y9
|
| 240 |
+
|
| 241 |
+
<table>
|
| 242 |
+
<thead>
|
| 243 |
+
<tr>
|
| 244 |
+
<th># CLIENT</th>
|
| 245 |
+
<th>EXPÉDITEUR</th>
|
| 246 |
+
<th>TERME DE CRÉDIT</th>
|
| 247 |
+
<th>DATE</th>
|
| 248 |
+
</tr>
|
| 249 |
+
</thead>
|
| 250 |
+
<tbody>
|
| 251 |
+
<tr>
|
| 252 |
+
<td>2753133</td>
|
| 253 |
+
<td>Notre camion</td>
|
| 254 |
+
<td>à la livraison</td>
|
| 255 |
+
<td>22/06/2023</td>
|
| 256 |
+
</tr>
|
| 257 |
+
</tbody>
|
| 258 |
+
</table>
|
| 259 |
+
|
| 260 |
+
<table>
|
| 261 |
+
<thead>
|
| 262 |
+
<tr>
|
| 263 |
+
<th>NOM DU VENDEUR</th>
|
| 264 |
+
<th>VOTRE ÉCONOMIE !</th>
|
| 265 |
+
<th># COMMANDE</th>
|
| 266 |
+
</tr>
|
| 267 |
+
</thead>
|
| 268 |
+
<tbody>
|
| 269 |
+
<tr>
|
| 270 |
+
<td>Éric</td>
|
| 271 |
+
<td>0.00</td>
|
| 272 |
+
<td></td>
|
| 273 |
+
</tr>
|
| 274 |
+
</tbody>
|
| 275 |
+
</table>
|
| 276 |
+
```
|
| 277 |
+
|
| 278 |
+
## [https://huggingface.co/numind/NuExtract3#reasoning-and-non-reasoning-modes](https://huggingface.co/numind/NuExtract3#reasoning-and-non-reasoning-modes) Reasoning and non-reasoning modes
|
| 279 |
+
|
| 280 |
+
NuExtract supports both reasoning and non-reasoning inference.
|
| 281 |
+
|
| 282 |
+
### [https://huggingface.co/numind/NuExtract3#non-thinking-mode](https://huggingface.co/numind/NuExtract3#non-thinking-mode) Non-thinking mode
|
| 283 |
+
|
| 284 |
+
Use this for fast and deterministic extraction or Markdown conversion.
|
| 285 |
+
|
| 286 |
+
```
|
| 287 |
+
enable_thinking = False
|
| 288 |
+
temperature = 0.2
|
| 289 |
+
```
|
| 290 |
+
|
| 291 |
+
### [https://huggingface.co/numind/NuExtract3#thinking-mode](https://huggingface.co/numind/NuExtract3#thinking-mode) Thinking mode
|
| 292 |
+
|
| 293 |
+
Use this for difficult documents, complex layouts, ambiguous fields, or cases where the document structure requires additional reasoning.
|
| 294 |
+
|
| 295 |
+
```
|
| 296 |
+
enable_thinking = True
|
| 297 |
+
temperature = 0.6
|
| 298 |
+
```
|
| 299 |
+
|
| 300 |
+
For production extraction workloads, we recommend starting with non-reasoning mode and enabling reasoning only for difficult examples.
|
| 301 |
+
|
| 302 |
+
## [https://huggingface.co/numind/NuExtract3#vllm-deployment](https://huggingface.co/numind/NuExtract3#vllm-deployment) vLLM deployment
|
| 303 |
+
|
| 304 |
+
NuExtract can be served with vLLM using an OpenAI-compatible API.
|
| 305 |
+
|
| 306 |
+
```
|
| 307 |
+
vllm serve numind/NuExtract3 \
|
| 308 |
+
--trust-remote-code \
|
| 309 |
+
--limit-mm-per-prompt '{"image": 99, "video": 0}' \
|
| 310 |
+
--chat-template-content-format openai \
|
| 311 |
+
--generation-config vllm \
|
| 312 |
+
--max-model-len 131072 \
|
| 313 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 314 |
+
```
|
| 315 |
+
|
| 316 |
+
### [https://huggingface.co/numind/NuExtract3#multi-token-prediction](https://huggingface.co/numind/NuExtract3#multi-token-prediction) Multi Token Prediction
|
| 317 |
+
|
| 318 |
+
The deployment commands above enable Multi Token Prediction (MTP) through vLLM speculative decoding:
|
| 319 |
+
|
| 320 |
+
```
|
| 321 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 322 |
+
```
|
| 323 |
+
|
| 324 |
+
MTP can improve decoding throughput without changing the OpenAI-compatible request payload. You can tune `num_speculative_tokens` for your hardware and workload, or remove `--speculative-config` if your vLLM version or environment does not support this speculative decoding method.
|
| 325 |
+
|
| 326 |
+
If you encounter memory issues, reduce the maximum model length and the maximum number of images:
|
| 327 |
+
|
| 328 |
+
```
|
| 329 |
+
vllm serve numind/NuExtract-3 \
|
| 330 |
+
--trust-remote-code \
|
| 331 |
+
--limit-mm-per-prompt '{"image": 6, "video": 0}' \
|
| 332 |
+
--chat-template-content-format openai \
|
| 333 |
+
--generation-config vllm \
|
| 334 |
+
--max-model-len 16384 \
|
| 335 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 336 |
+
```
|
| 337 |
+
|
| 338 |
+
## [https://huggingface.co/numind/NuExtract3#vllm-inference-structured-extraction-text](https://huggingface.co/numind/NuExtract3#vllm-inference-structured-extraction-text) vLLM inference: structured extraction: text
|
| 339 |
+
|
| 340 |
+
```
|
| 341 |
+
import json
|
| 342 |
+
from openai import OpenAI
|
| 343 |
+
|
| 344 |
+
client = OpenAI(
|
| 345 |
+
api_key="EMPTY",
|
| 346 |
+
base_url="http://localhost:8000/v1",
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
template = {
|
| 350 |
+
"store": "verbatim-string",
|
| 351 |
+
"date": "date-time",
|
| 352 |
+
"total": "number",
|
| 353 |
+
"currency": ["USD", "EUR", "GBP", "JPY", "Other"],
|
| 354 |
+
"items": [
|
| 355 |
+
{
|
| 356 |
+
"name": "verbatim-string",
|
| 357 |
+
"price": "number"
|
| 358 |
+
}
|
| 359 |
+
]
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
response = client.chat.completions.create(
|
| 363 |
+
model="numind/NuExtract3",
|
| 364 |
+
temperature=0.2,
|
| 365 |
+
messages=[
|
| 366 |
+
{
|
| 367 |
+
"role": "user",
|
| 368 |
+
"content": [
|
| 369 |
+
{
|
| 370 |
+
"type": "text",
|
| 371 |
+
"text": "Yesterday I bought apples and coffee at Trader Joe's for a total of $12.40."
|
| 372 |
+
}
|
| 373 |
+
],
|
| 374 |
+
}
|
| 375 |
+
],
|
| 376 |
+
extra_body={
|
| 377 |
+
"chat_template_kwargs": {
|
| 378 |
+
"template": json.dumps(template),
|
| 379 |
+
"instructions": "Specify the time for the `date` entry only if it is present, otherwise only output the date component.",
|
| 380 |
+
"enable_thinking": False
|
| 381 |
+
}
|
| 382 |
+
}
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
print(response.choices[0].message.content)
|
| 386 |
+
```
|
| 387 |
+
|
| 388 |
+
Example output:
|
| 389 |
+
|
| 390 |
+
```
|
| 391 |
+
{
|
| 392 |
+
"store": "Trader Joe's",
|
| 393 |
+
"date": null,
|
| 394 |
+
"total": 12.40,
|
| 395 |
+
"currency": "USD",
|
| 396 |
+
"items": [
|
| 397 |
+
{
|
| 398 |
+
"name": "apples",
|
| 399 |
+
"price": null
|
| 400 |
+
},
|
| 401 |
+
{
|
| 402 |
+
"name": "coffee",
|
| 403 |
+
"price": null
|
| 404 |
+
}
|
| 405 |
+
]
|
| 406 |
+
}
|
| 407 |
+
```
|
| 408 |
+
|
| 409 |
+
## [https://huggingface.co/numind/NuExtract3#vllm-inference-structured-extraction-image](https://huggingface.co/numind/NuExtract3#vllm-inference-structured-extraction-image) vLLM inference: structured extraction: image
|
| 410 |
+
|
| 411 |
+
```
|
| 412 |
+
import json
|
| 413 |
+
import base64
|
| 414 |
+
from openai import OpenAI
|
| 415 |
+
|
| 416 |
+
client = OpenAI(
|
| 417 |
+
api_key="EMPTY",
|
| 418 |
+
base_url="http://localhost:8000/v1",
|
| 419 |
+
)
|
| 420 |
+
|
| 421 |
+
def encode_image(image_path):
|
| 422 |
+
with open(image_path, "rb") as image_file:
|
| 423 |
+
return base64.b64encode(image_file.read()).decode("utf-8")
|
| 424 |
+
|
| 425 |
+
image_base64 = encode_image("receipt.png")
|
| 426 |
+
data_url = f"data:image/png;base64,{image_base64}"
|
| 427 |
+
|
| 428 |
+
template = {
|
| 429 |
+
"store": "verbatim-string",
|
| 430 |
+
"date": "date-time",
|
| 431 |
+
"total": "number",
|
| 432 |
+
"payment_method": "verbatim-string"
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
response = client.chat.completions.create(
|
| 436 |
+
model="numind/NuExtract3",
|
| 437 |
+
temperature=0.2,
|
| 438 |
+
messages=[
|
| 439 |
+
{
|
| 440 |
+
"role": "user",
|
| 441 |
+
"content": [
|
| 442 |
+
{
|
| 443 |
+
"type": "image_url",
|
| 444 |
+
"image_url": {"url": data_url}
|
| 445 |
+
}
|
| 446 |
+
],
|
| 447 |
+
}
|
| 448 |
+
],
|
| 449 |
+
extra_body={
|
| 450 |
+
"chat_template_kwargs": {
|
| 451 |
+
"template": json.dumps(template, indent=4),
|
| 452 |
+
"enable_thinking": False
|
| 453 |
+
}
|
| 454 |
+
}
|
| 455 |
+
)
|
| 456 |
+
|
| 457 |
+
print(response.choices[0].message.content)
|
| 458 |
+
```
|
| 459 |
+
|
| 460 |
+
Example output:
|
| 461 |
+
|
| 462 |
+
```
|
| 463 |
+
{
|
| 464 |
+
"store": "Trader Joe's",
|
| 465 |
+
"date": "2025-04-12",
|
| 466 |
+
"total": 42.85,
|
| 467 |
+
"payment_method": "Visa"
|
| 468 |
+
}
|
| 469 |
+
```
|
| 470 |
+
|
| 471 |
+
### [https://huggingface.co/numind/NuExtract3#multiple-page-pdf](https://huggingface.co/numind/NuExtract3#multiple-page-pdf) Multiple page PDF
|
| 472 |
+
|
| 473 |
+
You can render a PDF to one PNG image per page with PyMuPDF, then pass the images to vLLM in page order.
|
| 474 |
+
|
| 475 |
+
```
|
| 476 |
+
import base64
|
| 477 |
+
import json
|
| 478 |
+
|
| 479 |
+
import fitz # pip install pymupdf
|
| 480 |
+
from openai import OpenAI
|
| 481 |
+
|
| 482 |
+
client = OpenAI(
|
| 483 |
+
api_key="EMPTY",
|
| 484 |
+
base_url="http://localhost:8000/v1",
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
def pdf_to_png_data_urls(pdf_path, dpi=170):
|
| 488 |
+
data_urls = []
|
| 489 |
+
|
| 490 |
+
with fitz.open(pdf_path) as doc:
|
| 491 |
+
for page in doc:
|
| 492 |
+
pix = page.get_pixmap(dpi=dpi, alpha=False)
|
| 493 |
+
png_bytes = pix.tobytes("png")
|
| 494 |
+
png_base64 = base64.b64encode(png_bytes).decode("utf-8")
|
| 495 |
+
data_urls.append(f"data:image/png;base64,{png_base64}")
|
| 496 |
+
|
| 497 |
+
return data_urls
|
| 498 |
+
|
| 499 |
+
data_urls = pdf_to_png_data_urls("invoice.pdf", dpi=170)
|
| 500 |
+
|
| 501 |
+
template = {
|
| 502 |
+
"invoice_number": "verbatim-string",
|
| 503 |
+
"invoice_date": "date",
|
| 504 |
+
"total": "number",
|
| 505 |
+
"currency": "currency",
|
| 506 |
+
"line_items": [
|
| 507 |
+
{
|
| 508 |
+
"description": "verbatim-string",
|
| 509 |
+
"quantity": "number",
|
| 510 |
+
"unit_price": "number",
|
| 511 |
+
"total": "number"
|
| 512 |
+
}
|
| 513 |
+
]
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
response = client.chat.completions.create(
|
| 517 |
+
model="numind/NuExtract3",
|
| 518 |
+
temperature=0.2,
|
| 519 |
+
messages=[
|
| 520 |
+
{
|
| 521 |
+
"role": "user",
|
| 522 |
+
"content": [
|
| 523 |
+
{
|
| 524 |
+
"type": "image_url",
|
| 525 |
+
"image_url": {"url": data_url}
|
| 526 |
+
}
|
| 527 |
+
for data_url in data_urls
|
| 528 |
+
],
|
| 529 |
+
}
|
| 530 |
+
],
|
| 531 |
+
extra_body={
|
| 532 |
+
"chat_template_kwargs": {
|
| 533 |
+
"template": json.dumps(template, indent=4),
|
| 534 |
+
"enable_thinking": False
|
| 535 |
+
}
|
| 536 |
+
}
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
print(response.choices[0].message.content)
|
| 540 |
+
```
|
| 541 |
+
|
| 542 |
+
## [https://huggingface.co/numind/NuExtract3#vllm-inference-document-to-markdown](https://huggingface.co/numind/NuExtract3#vllm-inference-document-to-markdown) vLLM inference: document-to-Markdown
|
| 543 |
+
|
| 544 |
+
For Markdown OCR, use `mode="markdown"` or `mode="content"` without a template.
|
| 545 |
+
|
| 546 |
+
```
|
| 547 |
+
import base64
|
| 548 |
+
from openai import OpenAI
|
| 549 |
+
|
| 550 |
+
client = OpenAI(
|
| 551 |
+
api_key="EMPTY",
|
| 552 |
+
base_url="http://localhost:8000/v1",
|
| 553 |
+
)
|
| 554 |
+
|
| 555 |
+
def encode_image(image_path):
|
| 556 |
+
with open(image_path, "rb") as image_file:
|
| 557 |
+
return base64.b64encode(image_file.read()).decode("utf-8")
|
| 558 |
+
|
| 559 |
+
image_base64 = encode_image("document.png")
|
| 560 |
+
data_url = f"data:image/png;base64,{image_base64}"
|
| 561 |
+
|
| 562 |
+
response = client.chat.completions.create(
|
| 563 |
+
model="numind/NuExtract3",
|
| 564 |
+
temperature=1,
|
| 565 |
+
messages=[
|
| 566 |
+
{
|
| 567 |
+
"role": "user",
|
| 568 |
+
"content": [
|
| 569 |
+
{
|
| 570 |
+
"type": "image_url",
|
| 571 |
+
"image_url": {"url": data_url}
|
| 572 |
+
}
|
| 573 |
+
],
|
| 574 |
+
}
|
| 575 |
+
],
|
| 576 |
+
extra_body={
|
| 577 |
+
"chat_template_kwargs": {
|
| 578 |
+
"mode": "markdown",
|
| 579 |
+
"enable_thinking": False
|
| 580 |
+
}
|
| 581 |
+
}
|
| 582 |
+
)
|
| 583 |
+
|
| 584 |
+
print(response.choices[0].message.content)
|
| 585 |
+
```
|
| 586 |
+
|
| 587 |
+
## [https://huggingface.co/numind/NuExtract3#vllm-inference-reasoning-mode](https://huggingface.co/numind/NuExtract3#vllm-inference-reasoning-mode) vLLM inference: reasoning mode
|
| 588 |
+
|
| 589 |
+
Reasoning can be enabled for harder structured extraction or Markdown tasks.
|
| 590 |
+
|
| 591 |
+
```
|
| 592 |
+
response = client.chat.completions.create(
|
| 593 |
+
model="numind/NuExtract3",
|
| 594 |
+
temperature=1,
|
| 595 |
+
messages=[
|
| 596 |
+
{
|
| 597 |
+
"role": "user",
|
| 598 |
+
"content": [
|
| 599 |
+
{
|
| 600 |
+
"type": "image_url",
|
| 601 |
+
"image_url": {"url": data_url}
|
| 602 |
+
}
|
| 603 |
+
],
|
| 604 |
+
}
|
| 605 |
+
],
|
| 606 |
+
extra_body={
|
| 607 |
+
"chat_template_kwargs": {
|
| 608 |
+
"mode": "markdown",
|
| 609 |
+
"enable_thinking": True
|
| 610 |
+
}
|
| 611 |
+
}
|
| 612 |
+
)
|
| 613 |
+
|
| 614 |
+
result = response.choices[0].message.content
|
| 615 |
+
|
| 616 |
+
if "</think>" in result:
|
| 617 |
+
reasoning, answer = [part.strip() for part in result.split("</think>")]
|
| 618 |
+
else:
|
| 619 |
+
reasoning, answer = None, result
|
| 620 |
+
|
| 621 |
+
print(answer)
|
| 622 |
+
```
|
| 623 |
+
|
| 624 |
+
## [https://huggingface.co/numind/NuExtract3#in-context-examples-for-extraction](https://huggingface.co/numind/NuExtract3#in-context-examples-for-extraction) In-context examples for extraction
|
| 625 |
+
|
| 626 |
+
NuExtract supports in-context examples for structured extraction.
|
| 627 |
+
|
| 628 |
+
Examples are especially useful when the desired formatting is ambiguous or when the schema requires task-specific conventions. Examples can be provided by using `developer` messages, for which all items of the contents except the last one are the input, and the last one is the expected output.
|
| 629 |
+
|
| 630 |
+
```
|
| 631 |
+
import json
|
| 632 |
+
from openai import OpenAI
|
| 633 |
+
|
| 634 |
+
client = OpenAI(
|
| 635 |
+
api_key="EMPTY",
|
| 636 |
+
base_url="http://localhost:8000/v1",
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
+
template = {
|
| 640 |
+
"names": ["string"]
|
| 641 |
+
}
|
| 642 |
+
|
| 643 |
+
response = client.chat.completions.create(
|
| 644 |
+
model="numind/NuExtract3",
|
| 645 |
+
temperature=0.2,
|
| 646 |
+
messages=[
|
| 647 |
+
{
|
| 648 |
+
"role": "developer",
|
| 649 |
+
"content": [
|
| 650 |
+
{
|
| 651 |
+
"type": "text",
|
| 652 |
+
"text": "Stephen is the manager at Susan's store.",
|
| 653 |
+
},
|
| 654 |
+
{
|
| 655 |
+
"type": "text",
|
| 656 |
+
"text": "{\"names\": [\"-STEPHEN-\", \"-SUSAN-\"]}",
|
| 657 |
+
}
|
| 658 |
+
],
|
| 659 |
+
},
|
| 660 |
+
{
|
| 661 |
+
"role": "user",
|
| 662 |
+
"content": [
|
| 663 |
+
{
|
| 664 |
+
"type": "text",
|
| 665 |
+
"text": "John went to the restaurant with Mary. James went to the cinema."
|
| 666 |
+
}
|
| 667 |
+
],
|
| 668 |
+
}
|
| 669 |
+
],
|
| 670 |
+
extra_body={
|
| 671 |
+
"chat_template_kwargs": {
|
| 672 |
+
"template": json.dumps(template, indent=4),
|
| 673 |
+
"enable_thinking": False
|
| 674 |
+
}
|
| 675 |
+
}
|
| 676 |
+
)
|
| 677 |
+
|
| 678 |
+
print(response.choices[0].message.content)
|
| 679 |
+
```
|
| 680 |
+
|
| 681 |
+
Example output:
|
| 682 |
+
|
| 683 |
+
```
|
| 684 |
+
{
|
| 685 |
+
"names": ["-JOHN-", "-MARY-", "-JAMES-"]
|
| 686 |
+
}
|
| 687 |
+
```
|
| 688 |
+
|
| 689 |
+
## [https://huggingface.co/numind/NuExtract3#vllm-inference-template-generation](https://huggingface.co/numind/NuExtract3#vllm-inference-template-generation) vLLM inference: template generation
|
| 690 |
+
|
| 691 |
+
NuExtract can generate an extraction template from a natural language description.
|
| 692 |
+
|
| 693 |
+
```
|
| 694 |
+
from openai import OpenAI
|
| 695 |
+
|
| 696 |
+
client = OpenAI(
|
| 697 |
+
api_key="EMPTY",
|
| 698 |
+
base_url="http://localhost:8000/v1",
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
+
response = client.chat.completions.create(
|
| 702 |
+
model="numind/NuExtract3",
|
| 703 |
+
temperature=0.2,
|
| 704 |
+
messages=[
|
| 705 |
+
{
|
| 706 |
+
"role": "user",
|
| 707 |
+
"content": [
|
| 708 |
+
{
|
| 709 |
+
"type": "text",
|
| 710 |
+
"text": "I want to extract the key details from a rental contract."
|
| 711 |
+
}
|
| 712 |
+
],
|
| 713 |
+
}
|
| 714 |
+
],
|
| 715 |
+
extra_body={
|
| 716 |
+
"chat_template_kwargs": {
|
| 717 |
+
"mode": "template-generation"
|
| 718 |
+
}
|
| 719 |
+
}
|
| 720 |
+
)
|
| 721 |
+
|
| 722 |
+
print(response.choices[0].message.content)
|
| 723 |
+
```
|
| 724 |
+
|
| 725 |
+
Example output:
|
| 726 |
+
|
| 727 |
+
```
|
| 728 |
+
{
|
| 729 |
+
"contract_title": "verbatim-string",
|
| 730 |
+
"landlord": "verbatim-string",
|
| 731 |
+
"tenant": "verbatim-string",
|
| 732 |
+
"property_address": "verbatim-string",
|
| 733 |
+
"start_date": "date-time",
|
| 734 |
+
"end_date": "date-time",
|
| 735 |
+
"monthly_rent": "number",
|
| 736 |
+
"currency": "verbatim-string",
|
| 737 |
+
"deposit": "number",
|
| 738 |
+
"signatories": ["verbatim-string"]
|
| 739 |
+
}
|
| 740 |
+
```
|
| 741 |
+
|
| 742 |
+
## [https://huggingface.co/numind/NuExtract3#curl-examples](https://huggingface.co/numind/NuExtract3#curl-examples) Curl examples
|
| 743 |
+
|
| 744 |
+
The following examples assume that vLLM is running locally on port 8000. They use `jq` to build valid JSON request bodies without manually escaping the image data or template string.
|
| 745 |
+
|
| 746 |
+
### [https://huggingface.co/numind/NuExtract3#single-image-structured-extraction](https://huggingface.co/numind/NuExtract3#single-image-structured-extraction) Single image structured extraction
|
| 747 |
+
|
| 748 |
+
```
|
| 749 |
+
API_KEY="EMPTY"
|
| 750 |
+
IMAGE_BASE64_FILE=$(mktemp)
|
| 751 |
+
REQUEST_BODY_FILE=$(mktemp)
|
| 752 |
+
|
| 753 |
+
base64 < receipt.png | tr -d '\n' > "$IMAGE_BASE64_FILE"
|
| 754 |
+
|
| 755 |
+
TEMPLATE=$(cat <<'JSON'
|
| 756 |
+
{
|
| 757 |
+
"store": "verbatim-string",
|
| 758 |
+
"date": "date-time",
|
| 759 |
+
"total": "number",
|
| 760 |
+
"payment_method": "verbatim-string"
|
| 761 |
+
}
|
| 762 |
+
JSON
|
| 763 |
+
)
|
| 764 |
+
|
| 765 |
+
jq -n \
|
| 766 |
+
--rawfile image_base64 "$IMAGE_BASE64_FILE" \
|
| 767 |
+
--arg template "$TEMPLATE" \
|
| 768 |
+
'{
|
| 769 |
+
model: "numind/NuExtract3",
|
| 770 |
+
temperature: 0.6,
|
| 771 |
+
messages: [
|
| 772 |
+
{
|
| 773 |
+
role: "user",
|
| 774 |
+
content: [
|
| 775 |
+
{
|
| 776 |
+
type: "image_url",
|
| 777 |
+
image_url: {url: ("data:image/png;base64," + $image_base64)}
|
| 778 |
+
}
|
| 779 |
+
]
|
| 780 |
+
}
|
| 781 |
+
],
|
| 782 |
+
chat_template_kwargs: {
|
| 783 |
+
template: $template,
|
| 784 |
+
enable_thinking: false
|
| 785 |
+
}
|
| 786 |
+
}' > "$REQUEST_BODY_FILE"
|
| 787 |
+
|
| 788 |
+
curl http://localhost:8000/v1/chat/completions \
|
| 789 |
+
-H "Content-Type: application/json" \
|
| 790 |
+
-H "Authorization: Bearer $API_KEY" \
|
| 791 |
+
--data-binary "@$REQUEST_BODY_FILE"
|
| 792 |
+
|
| 793 |
+
rm "$IMAGE_BASE64_FILE" "$REQUEST_BODY_FILE"
|
| 794 |
+
```
|
| 795 |
+
|
| 796 |
+
### [https://huggingface.co/numind/NuExtract3#single-image-content-extraction](https://huggingface.co/numind/NuExtract3#single-image-content-extraction) Single image content extraction
|
| 797 |
+
|
| 798 |
+
```
|
| 799 |
+
API_KEY="EMPTY"
|
| 800 |
+
IMAGE_BASE64_FILE=$(mktemp)
|
| 801 |
+
REQUEST_BODY_FILE=$(mktemp)
|
| 802 |
+
|
| 803 |
+
base64 < document.png | tr -d '\n' > "$IMAGE_BASE64_FILE"
|
| 804 |
+
|
| 805 |
+
jq -n \
|
| 806 |
+
--rawfile image_base64 "$IMAGE_BASE64_FILE" \
|
| 807 |
+
'{
|
| 808 |
+
model: "numind/NuExtract3",
|
| 809 |
+
temperature: 0.6,
|
| 810 |
+
messages: [
|
| 811 |
+
{
|
| 812 |
+
role: "user",
|
| 813 |
+
content: [
|
| 814 |
+
{
|
| 815 |
+
type: "image_url",
|
| 816 |
+
image_url: {url: ("data:image/png;base64," + $image_base64)}
|
| 817 |
+
}
|
| 818 |
+
]
|
| 819 |
+
}
|
| 820 |
+
],
|
| 821 |
+
chat_template_kwargs: {
|
| 822 |
+
mode: "content",
|
| 823 |
+
enable_thinking: false
|
| 824 |
+
}
|
| 825 |
+
}' > "$REQUEST_BODY_FILE"
|
| 826 |
+
|
| 827 |
+
curl http://localhost:8000/v1/chat/completions \
|
| 828 |
+
-H "Content-Type: application/json" \
|
| 829 |
+
-H "Authorization: Bearer $API_KEY" \
|
| 830 |
+
--data-binary "@$REQUEST_BODY_FILE"
|
| 831 |
+
|
| 832 |
+
rm "$IMAGE_BASE64_FILE" "$REQUEST_BODY_FILE"
|
| 833 |
+
```
|
| 834 |
+
|
| 835 |
+
## [https://huggingface.co/numind/NuExtract3#transformers-example](https://huggingface.co/numind/NuExtract3#transformers-example) Transformers example
|
| 836 |
+
|
| 837 |
+
You can also run NuExtract directly with `transformers`. The same `template`, `mode`, and `enable_thinking` options are passed to `processor.apply_chat_template`.
|
| 838 |
+
|
| 839 |
+
```
|
| 840 |
+
import json
|
| 841 |
+
|
| 842 |
+
import torch
|
| 843 |
+
from PIL import Image
|
| 844 |
+
from transformers import AutoModelForImageTextToText, AutoProcessor
|
| 845 |
+
|
| 846 |
+
model_id = "numind/NuExtract3"
|
| 847 |
+
|
| 848 |
+
processor = AutoProcessor.from_pretrained(
|
| 849 |
+
model_id,
|
| 850 |
+
trust_remote_code=True,
|
| 851 |
+
)
|
| 852 |
+
model = AutoModelForImageTextToText.from_pretrained(
|
| 853 |
+
model_id,
|
| 854 |
+
dtype=torch.bfloat16,
|
| 855 |
+
device_map="auto",
|
| 856 |
+
trust_remote_code=True,
|
| 857 |
+
).eval()
|
| 858 |
+
|
| 859 |
+
def run_nuextract(messages, **chat_template_kwargs):
|
| 860 |
+
inputs = processor.apply_chat_template(
|
| 861 |
+
messages,
|
| 862 |
+
add_generation_prompt=True,
|
| 863 |
+
tokenize=True,
|
| 864 |
+
return_dict=True,
|
| 865 |
+
return_tensors="pt",
|
| 866 |
+
**chat_template_kwargs,
|
| 867 |
+
).to(model.device)
|
| 868 |
+
|
| 869 |
+
with torch.inference_mode():
|
| 870 |
+
generated_ids = model.generate(
|
| 871 |
+
**inputs,
|
| 872 |
+
max_new_tokens=4096,
|
| 873 |
+
do_sample=False,
|
| 874 |
+
)
|
| 875 |
+
|
| 876 |
+
generated_ids = generated_ids[:, inputs.input_ids.shape[1]:]
|
| 877 |
+
return processor.batch_decode(
|
| 878 |
+
generated_ids,
|
| 879 |
+
skip_special_tokens=True,
|
| 880 |
+
clean_up_tokenization_spaces=False,
|
| 881 |
+
)[0].strip()
|
| 882 |
+
|
| 883 |
+
# Single image structured extraction
|
| 884 |
+
receipt_image = Image.open("receipt.png").convert("RGB")
|
| 885 |
+
receipt_messages = [
|
| 886 |
+
{
|
| 887 |
+
"role": "user",
|
| 888 |
+
"content": [
|
| 889 |
+
{
|
| 890 |
+
"type": "image",
|
| 891 |
+
"image": receipt_image,
|
| 892 |
+
}
|
| 893 |
+
],
|
| 894 |
+
}
|
| 895 |
+
]
|
| 896 |
+
|
| 897 |
+
template = {
|
| 898 |
+
"store": "verbatim-string",
|
| 899 |
+
"date": "date-time",
|
| 900 |
+
"total": "number",
|
| 901 |
+
"payment_method": "verbatim-string"
|
| 902 |
+
}
|
| 903 |
+
|
| 904 |
+
structured_output = run_nuextract(
|
| 905 |
+
receipt_messages,
|
| 906 |
+
template=json.dumps(template, indent=4),
|
| 907 |
+
enable_thinking=False,
|
| 908 |
+
)
|
| 909 |
+
print(structured_output)
|
| 910 |
+
|
| 911 |
+
# Single image content extraction
|
| 912 |
+
document_image = Image.open("document.png").convert("RGB")
|
| 913 |
+
document_messages = [
|
| 914 |
+
{
|
| 915 |
+
"role": "user",
|
| 916 |
+
"content": [
|
| 917 |
+
{
|
| 918 |
+
"type": "image",
|
| 919 |
+
"image": document_image,
|
| 920 |
+
}
|
| 921 |
+
],
|
| 922 |
+
}
|
| 923 |
+
]
|
| 924 |
+
|
| 925 |
+
content_output = run_nuextract(
|
| 926 |
+
document_messages,
|
| 927 |
+
mode="content",
|
| 928 |
+
enable_thinking=False,
|
| 929 |
+
)
|
| 930 |
+
print(content_output)
|
| 931 |
+
```
|
| 932 |
+
|
| 933 |
+
Special thanks to the Lambda.ai team for the compute that made this project a success.
|
| 934 |
+
|
| 935 |
+
## [https://huggingface.co/numind/NuExtract3#citation](https://huggingface.co/numind/NuExtract3#citation) Citation
|
| 936 |
+
|
| 937 |
+
If you use NuExtract, please cite NuMind and link to the model page.
|
| 938 |
+
|
| 939 |
+
```
|
| 940 |
+
@misc{nuextract3,
|
| 941 |
+
title = {NuExtract3},
|
| 942 |
+
author = {NuMind},
|
| 943 |
+
year = {2026},
|
| 944 |
+
url = {https://nuextract.ai/}
|
| 945 |
+
}
|
| 946 |
+
```
|
| 947 |
+
|
| 948 |
+
Downloads last month 1,236,855
|
| 949 |
+
|
| 950 |
+
Safetensors[https://huggingface.co/docs/safetensors](https://huggingface.co/docs/safetensors)
|
| 951 |
+
|
| 952 |
+
Model size
|
| 953 |
+
|
| 954 |
+
5B params
|
| 955 |
+
|
| 956 |
+
Tensor type
|
| 957 |
+
|
| 958 |
+
BF16
|
| 959 |
+
|
| 960 |
+
·
|
| 961 |
+
|
| 962 |
+
Chat template
|
| 963 |
+
|
| 964 |
+
Files info
|
| 965 |
+
|
| 966 |
+
Inference Providers[NEW](https://huggingface.co/docs/inference-providers)
|
| 967 |
+
|
| 968 |
+
[Image-to-Text](https://huggingface.co/tasks/image-to-text)
|
| 969 |
+
|
| 970 |
+
This model isn't deployed by any Inference Provider.[🙋 3 Ask for provider support](https://huggingface.co/spaces/huggingface/InferenceSupport/discussions/10487)
|
| 971 |
+
|
| 972 |
+
## Model tree for numind/NuExtract3[https://huggingface.co/docs/hub/model-cards#specifying-a-base-model](https://huggingface.co/docs/hub/model-cards#specifying-a-base-model)
|
| 973 |
+
|
| 974 |
+
Base model
|
| 975 |
+
|
| 976 |
+
[Qwen/Qwen3.5-4B-Base](https://huggingface.co/Qwen/Qwen3.5-4B-Base)
|
| 977 |
+
|
| 978 |
+
Finetuned
|
| 979 |
+
|
| 980 |
+
[Qwen/Qwen3.5-4B](https://huggingface.co/Qwen/Qwen3.5-4B)
|
| 981 |
+
|
| 982 |
+
Finetuned
|
| 983 |
+
|
| 984 |
+
([392](https://huggingface.co/models?other=base_model:finetune:Qwen/Qwen3.5-4B) )
|
| 985 |
+
|
| 986 |
+
this model
|
| 987 |
+
|
| 988 |
+
Finetunes
|
| 989 |
+
|
| 990 |
+
[4 models](https://huggingface.co/models?other=base_model:finetune:numind/NuExtract3)
|
| 991 |
+
|
| 992 |
+
Quantizations
|
| 993 |
+
|
| 994 |
+
[11 models](https://huggingface.co/models?other=base_model:quantized:numind/NuExtract3)
|
| 995 |
+
|
| 996 |
+
## Spaces using numind/NuExtract3 9
|
| 997 |
+
|
| 998 |
+
## Collection including numind/NuExtract3
|
| 999 |
+
|
| 1000 |
+
####
|
| 1001 |
+
|
| 1002 |
+
[NuExtract3 Collection 12 items • Updated May 26 • 15](https://huggingface.co/collections/numind/nuextract3)
|
| 1003 |
+
|
| 1004 |
+
## Evaluation results [https://huggingface.co/docs/hub/eval-results](https://huggingface.co/docs/hub/eval-results)
|
| 1005 |
+
|
| 1006 |
+
- [allenai/olmOCR-bench](https://huggingface.co/datasets/allenai/olmOCR-bench) · Old Scans[View evaluation results](https://huggingface.co/numind/NuExtract3/blob/main/.eval_results/olmocrbench.yaml)
|
| 1007 |
+
|
| 1008 |
+
[source](https://github.com/davanstrien/ocr-bench/blob/99f7550c/experiments/olmocr-bench-oldscans/BENCHMARKING.md)[leaderboard](https://huggingface.co/datasets/allenai/olmOCR-bench?eval_result=numind/NuExtract3&leaderboard_task_id=old_scans)
|
| 1009 |
+
|
| 1010 |
+
37.8 *
|
.agents/references/nuextract3/2026-07-20/huggingface.co/numind/NuExtract3/blob/main/README.md
ADDED
|
@@ -0,0 +1,1001 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!--
|
| 2 |
+
source: https://huggingface.co/numind/NuExtract3/blob/main/README.md
|
| 3 |
+
retrieved: 2026-07-20T17:29:25.828402+00:00
|
| 4 |
+
final_url: https://huggingface.co/numind/NuExtract3/blob/main/README.md
|
| 5 |
+
content_type: text/html
|
| 6 |
+
-->
|
| 7 |
+
|
| 8 |
+
# README.md · numind/NuExtract3 at main
|
| 9 |
+
|
| 10 |
+
[Hugging Face](https://huggingface.co/)
|
| 11 |
+
|
| 12 |
+
#
|
| 13 |
+
|
| 14 |
+
[https://huggingface.co/numind](https://huggingface.co/numind)
|
| 15 |
+
|
| 16 |
+
[numind](https://huggingface.co/numind)
|
| 17 |
+
|
| 18 |
+
/
|
| 19 |
+
|
| 20 |
+
[NuExtract3](https://huggingface.co/numind/NuExtract3)
|
| 21 |
+
|
| 22 |
+
like 295
|
| 23 |
+
|
| 24 |
+
Follow
|
| 25 |
+
|
| 26 |
+
NuMind 649
|
| 27 |
+
|
| 28 |
+
[Image-to-Text](https://huggingface.co/models?pipeline_tag=image-to-text)
|
| 29 |
+
|
| 30 |
+
[Transformers](https://huggingface.co/models?library=transformers)
|
| 31 |
+
|
| 32 |
+
[Safetensors](https://huggingface.co/models?library=safetensors)
|
| 33 |
+
|
| 34 |
+
[qwen3_5](https://huggingface.co/models?other=qwen3_5)
|
| 35 |
+
|
| 36 |
+
[image-text-to-text](https://huggingface.co/models?other=image-text-to-text)
|
| 37 |
+
|
| 38 |
+
[vision-language](https://huggingface.co/models?other=vision-language)
|
| 39 |
+
|
| 40 |
+
[vlm](https://huggingface.co/models?other=vlm)
|
| 41 |
+
|
| 42 |
+
[document-understanding](https://huggingface.co/models?other=document-understanding)
|
| 43 |
+
|
| 44 |
+
[structured-extraction](https://huggingface.co/models?other=structured-extraction)
|
| 45 |
+
|
| 46 |
+
[information-extraction](https://huggingface.co/models?other=information-extraction)
|
| 47 |
+
|
| 48 |
+
[ocr](https://huggingface.co/models?other=ocr)
|
| 49 |
+
|
| 50 |
+
[document-to-markdown](https://huggingface.co/models?other=document-to-markdown)
|
| 51 |
+
|
| 52 |
+
[markdown](https://huggingface.co/models?other=markdown)
|
| 53 |
+
|
| 54 |
+
[rag](https://huggingface.co/models?other=rag)
|
| 55 |
+
|
| 56 |
+
[reasoning](https://huggingface.co/models?other=reasoning)
|
| 57 |
+
|
| 58 |
+
[multilingual](https://huggingface.co/models?other=multilingual)
|
| 59 |
+
|
| 60 |
+
[conversational](https://huggingface.co/models?other=conversational)
|
| 61 |
+
|
| 62 |
+
[Eval Results](https://huggingface.co/models?other=eval-results)
|
| 63 |
+
|
| 64 |
+
License: apache-2.0
|
| 65 |
+
|
| 66 |
+
[Model card](https://huggingface.co/numind/NuExtract3)
|
| 67 |
+
|
| 68 |
+
[Files Files and versions xet](https://huggingface.co/numind/NuExtract3/tree/main)
|
| 69 |
+
|
| 70 |
+
[Community 2](https://huggingface.co/numind/NuExtract3/discussions)
|
| 71 |
+
|
| 72 |
+
Deploy
|
| 73 |
+
|
| 74 |
+
Copy to bucket new
|
| 75 |
+
|
| 76 |
+
Use this model
|
| 77 |
+
|
| 78 |
+
main
|
| 79 |
+
|
| 80 |
+
[NuExtract3](https://huggingface.co/numind/NuExtract3/tree/main) / README.md
|
| 81 |
+
|
| 82 |
+
[NathanFradet](https://huggingface.co/NathanFradet)
|
| 83 |
+
|
| 84 |
+
Update README.md
|
| 85 |
+
|
| 86 |
+
[acaf70e](https://huggingface.co/numind/NuExtract3/commit/acaf70ecff9c3dbbfcbae651b82b66a0d8dbd0c6) verified about 2 months ago
|
| 87 |
+
|
| 88 |
+
[preview](https://huggingface.co/numind/NuExtract3/blob/main/README.md)[code](https://huggingface.co/numind/NuExtract3/blob/main/README.md?code=true)
|
| 89 |
+
|
| 90 |
+
|
|
| 91 |
+
|
| 92 |
+
[Raw](https://huggingface.co/numind/NuExtract3/raw/main/README.md)
|
| 93 |
+
|
| 94 |
+
Download with hf CLI
|
| 95 |
+
|
| 96 |
+
Copy download link
|
| 97 |
+
|
| 98 |
+
[History](https://huggingface.co/numind/NuExtract3/commits/main/README.md)[Blame](https://huggingface.co/numind/NuExtract3/blame/main/README.md)[Contribute](https://huggingface.co/numind/NuExtract3/edit/main/README.md)[Delete](https://huggingface.co/numind/NuExtract3/delete/main/README.md)
|
| 99 |
+
|
| 100 |
+
Safe
|
| 101 |
+
|
| 102 |
+
25.5 kB
|
| 103 |
+
|
| 104 |
+
[metadata](https://huggingface.co/docs/hub/model-cards#model-card-metadata)
|
| 105 |
+
|
| 106 |
+
```
|
| 107 |
+
license: apache-2.0
|
| 108 |
+
license_link: https://huggingface.co/numind/NuExtract3/blob/main/LICENSE
|
| 109 |
+
library_name: transformers
|
| 110 |
+
pipeline_tag: image-to-text
|
| 111 |
+
tags:
|
| 112 |
+
- image-text-to-text
|
| 113 |
+
- transformers
|
| 114 |
+
- safetensors
|
| 115 |
+
- qwen3_5
|
| 116 |
+
- vision-language
|
| 117 |
+
- vlm
|
| 118 |
+
- document-understanding
|
| 119 |
+
- structured-extraction
|
| 120 |
+
- information-extraction
|
| 121 |
+
- ocr
|
| 122 |
+
- document-to-markdown
|
| 123 |
+
- markdown
|
| 124 |
+
- rag
|
| 125 |
+
- reasoning
|
| 126 |
+
- multilingual
|
| 127 |
+
- conversational
|
| 128 |
+
base_model:
|
| 129 |
+
- Qwen/Qwen3.5-4B
|
| 130 |
+
model_name: NuExtract3
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
[https://nuextract.ai/](https://nuextract.ai/)
|
| 134 |
+
|
| 135 |
+
🖥️ [API / Platform](https://nuextract.ai/) | 📑 [Blog](https://numind.ai/blog) | 🗣️ [Discord](https://discord.gg/3tsEtJNCDe) | 🛠️ [GitHub](https://github.com/numindai/nuextract)
|
| 136 |
+
|
| 137 |
+
NuExtract3 is a unified 4B vision-language reasoning model for document understanding.
|
| 138 |
+
|
| 139 |
+
It combines strong structured information extraction with high-quality image-to-Markdown conversion, making it suitable for extraction pipelines, OCR, and RAG preprocessing for all types of documents such as scans, receipts, forms, invoices, contracts or tables.
|
| 140 |
+
|
| 141 |
+
Try it out in [the 🤗 space!](https://huggingface.co/spaces/numind/NuExtract-3-4B)
|
| 142 |
+
|
| 143 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#overview](https://huggingface.co/numind/NuExtract3/blob/main/README.md#overview) Overview
|
| 144 |
+
|
| 145 |
+
- Structured extraction : input (text/images) + JSON template + instructions --> JSON output
|
| 146 |
+
|
| 147 |
+
- Markdown conversion : input (text/images) --> Markdown
|
| 148 |
+
|
| 149 |
+
- Multimodal inputs : text, images, or text + images.
|
| 150 |
+
|
| 151 |
+
- Multilingual documents.
|
| 152 |
+
|
| 153 |
+
- Reasoning and non-reasoning inference modes.
|
| 154 |
+
|
| 155 |
+
- Template generation for structured extraction from natural language or input document.
|
| 156 |
+
|
| 157 |
+
# [https://huggingface.co/numind/NuExtract3/blob/main/README.md#benchmark-results](https://huggingface.co/numind/NuExtract3/blob/main/README.md#benchmark-results) Benchmark results
|
| 158 |
+
|
| 159 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#structured-extraction](https://huggingface.co/numind/NuExtract3/blob/main/README.md#structured-extraction) Structured Extraction
|
| 160 |
+
|
| 161 |
+
We benchmarked NuExtract on NuMind's internal structured benchmark, measuring model's performances on ~600 documents of diverse types including invoices, movie posters or floor plans. These documents and their ground-truth cover diverse use-cases testing model visual understanding, OCR, reasoning and ability to handle long input and output contexts. We plan to open-source this benchmark in the coming weeks, along with a extensive leaderboard including most popular open-weight and closed-sourced APIs and a Python library allowing to easily measure model performances on structured extraction.
|
| 162 |
+
|
| 163 |
+
To measure a pair of predicted and ground-truth JSONs, we represent both as trees which we align based on node names, compute metric scores for aligned leaves and report the average of these scores. `string` and `verbatim-string` leaves are evaluated with indel distance (i.e. Levenshtein without replacement), while all others are evaluated with exact-match. Models were evaluated using vllm, with a temperature of 0.25 and a maximum of 65000 output token (for both thinking and answer), which largely exceeds 22000 which is the number of tokens of the largest ground truth output.
|
| 164 |
+
|
| 165 |
+
Model name Average score Num. failed⁽¹⁾ Avg. num tokens thinking Avg. num tokens answer NuExtract3.4_4B-RL 0.651 ± 0.019 27 2036 1856 gemma-4-E4B-it 0.538 ± 0.023 31 3005 1287 Qwen3.5-9B 0.479 ± 0.030 170 22409 1257 Qwen3.5-4B 0.417 ± 0.031 229 27177 1201 GLM-4.6V-Flash 0.435 ± 0.026 153 2989 1357 Nemotron-3-Nano-Omni 0.387 ± 0.028 204 25827 522 Ministral-3-3B 0.240 ± 0.022 344 27586 362
|
| 166 |
+
|
| 167 |
+
(1) number of model outputs that were not JSON deserializable, either directly or by removing leading and trailing backticks.
|
| 168 |
+
95% confidence intervals computed using a nonparametric bootstrap over scores distributions.
|
| 169 |
+
|
| 170 |
+
The benchmark include samples containing multiple images resulting in large input context, and some with ground-truth containing large numbers of items to extract resulting in large outputs. We found that the reasoning of small models significantly negatively impact their performances. The reason is that many models ended up falling in repetition loops, hitting the output tokens limit and resulting in failed requests.
|
| 171 |
+
|
| 172 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#document-to-markdown](https://huggingface.co/numind/NuExtract3/blob/main/README.md#document-to-markdown) Document to Markdown
|
| 173 |
+
|
| 174 |
+
NuExtract can also convert document images into clean Markdown. Output will be Markdown for text (headers etc), HTML for tables, LaTeX for math and `<figure data-type="image" data-id="img_n"><img src="/NM-dev/model_card-A/resolve/main/img_n.png" alt="Detail description of the images"/> `
|
| 175 |
+
|
| 176 |
+
Modern, format-agnostic benchmarks for complex document understanding are limited, so we explored a new evaluation approach. We selected 100 documents with challenging layouts and tables, asked each model to convert them into a structured representation, then used Gemini 3 Flash to compare model outputs against the source document and choose the most accurate result. The rankings aligned with human votes, suggesting this is a promising method for evaluating document-to-Markdown capabilities. More details will be shared in an upcoming technical report. Here are some results:
|
| 177 |
+
|
| 178 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#using-markdown-to-structured](https://huggingface.co/numind/NuExtract3/blob/main/README.md#using-markdown-to-structured) Using "Markdown-to-structured"
|
| 179 |
+
|
| 180 |
+
To add other evaluate references, we used our structured extraction benchmark to evaluate models in a two-step fashion: convert the benchmark inputs to Markdown, then use Qwen3.6 27B to perform the structured extraction task on them. Intuitively, it allows to evaluate how models achieve to keep the input document content and layout: good models will allow the "structured extractor" model to perform better scores.
|
| 181 |
+
|
| 182 |
+
# [https://huggingface.co/numind/NuExtract3/blob/main/README.md#using-nuextract](https://huggingface.co/numind/NuExtract3/blob/main/README.md#using-nuextract) Using NuExtract
|
| 183 |
+
|
| 184 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#structured-extraction-1](https://huggingface.co/numind/NuExtract3/blob/main/README.md#structured-extraction-1) Structured extraction
|
| 185 |
+
|
| 186 |
+
Structured extraction takes as inputs:
|
| 187 |
+
|
| 188 |
+
1. An input document, which can be text, image, or both;
|
| 189 |
+
|
| 190 |
+
2. A JSON template describing the information to extract;
|
| 191 |
+
|
| 192 |
+
3. (Optional) Instructions, allowing to specify expected output formats or values, to provide with the `instructions` chat template kwarg;
|
| 193 |
+
|
| 194 |
+
4. (Optional) In-Context Learning (ICL) examples.
|
| 195 |
+
|
| 196 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#input-json-template](https://huggingface.co/numind/NuExtract3/blob/main/README.md#input-json-template) Input JSON template
|
| 197 |
+
|
| 198 |
+
NuExtract uses a input JSON template whose structure is identical to the output JSON. Its leaf values are specify the types of the output JSON leaves. For examples:
|
| 199 |
+
|
| 200 |
+
```
|
| 201 |
+
{
|
| 202 |
+
"invoice_number": "verbatim-string",
|
| 203 |
+
"invoice_date": "date",
|
| 204 |
+
"total_amount": "number",
|
| 205 |
+
"currency": "currency",
|
| 206 |
+
"line_items": [
|
| 207 |
+
{
|
| 208 |
+
"description": "verbatim-string",
|
| 209 |
+
"item_type": ["electronics", "clothing", "vehicle", "furniture", "other"],
|
| 210 |
+
"quantity": "integer",
|
| 211 |
+
"unit_price": "number",
|
| 212 |
+
"total": "number"
|
| 213 |
+
}
|
| 214 |
+
]
|
| 215 |
+
}
|
| 216 |
+
```
|
| 217 |
+
|
| 218 |
+
Supported template types include:
|
| 219 |
+
|
| 220 |
+
- `verbatim-string`: extract text exactly as it appears in the document;
|
| 221 |
+
|
| 222 |
+
- `string`: generic string field, allowing abstraction or light paraphrasing;
|
| 223 |
+
|
| 224 |
+
- `integer`: whole number;
|
| 225 |
+
|
| 226 |
+
- `number`: integer or decimal number;
|
| 227 |
+
|
| 228 |
+
- `date-time`: ISO-8601 date, time or date-time;
|
| 229 |
+
|
| 230 |
+
- Other specific types such as `data`, `time`, `country`, `currency`, `email` and so on. [For more details, read the complete types specifications and examples](https://huggingface.co/numind/NuExtract3/blob/main/TYPES.md)
|
| 231 |
+
|
| 232 |
+
Template constructors:
|
| 233 |
+
|
| 234 |
+
- Arrays, for example `["string"]`;
|
| 235 |
+
|
| 236 |
+
- Enums, for example `["yes", "no", "maybe"]`;
|
| 237 |
+
|
| 238 |
+
- Multi-enums (multiple possible values), for example `[["A", "B", "C"]]`.
|
| 239 |
+
|
| 240 |
+
If the model does not find relevant information for a field, it returns `null` or `[]`.
|
| 241 |
+
|
| 242 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#converting-json-schema--pydantic-models-to-nuextract-template](https://huggingface.co/numind/NuExtract3/blob/main/README.md#converting-json-schema--pydantic-models-to-nuextract-template) Converting JSON schema / Pydantic models to NuExtract template
|
| 243 |
+
|
| 244 |
+
Our Python SDK (`pip install numind`) offers a method to convert JSON schemas to NuExtract templates:
|
| 245 |
+
|
| 246 |
+
```
|
| 247 |
+
from typing import Literal
|
| 248 |
+
|
| 249 |
+
from pydantic import Field, BaseModel
|
| 250 |
+
from numind.nuextract_utils import convert_json_schema_to_nuextract_template
|
| 251 |
+
|
| 252 |
+
class HotelBooking(BaseModel):
|
| 253 |
+
city: str
|
| 254 |
+
check_in_date: str = Field(description="date")
|
| 255 |
+
check_out_date: str = Field(description="date")
|
| 256 |
+
number_of_guests: int
|
| 257 |
+
room_type: Literal["single", "double", "suite"]
|
| 258 |
+
|
| 259 |
+
template, dropped_branches = convert_json_schema_to_nuextract_template(
|
| 260 |
+
HotelBooking.model_json_schema()
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
# {'check_in_date': 'date', 'check_out_date': 'date', 'city': 'string', 'number_of_guests': 'integer', 'room_type': ['single', 'double', 'suite']}
|
| 264 |
+
```
|
| 265 |
+
|
| 266 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#document-to-markdown-1](https://huggingface.co/numind/NuExtract3/blob/main/README.md#document-to-markdown-1) Document-to-Markdown
|
| 267 |
+
|
| 268 |
+
NuExtract can also convert document images into clean Markdown. Output will be markdown for text (headers etc), html for tables, latex for mat and `<figure data-type="image" data-id="img_n"><img src="/NM-dev/model_card-A/resolve/main/img_n.png" alt="Detail description of the images"/> `
|
| 269 |
+
|
| 270 |
+
Markdown example:
|
| 271 |
+
|
| 272 |
+
```
|
| 273 |
+
<figure data-type="image" data-id="img_1">
|
| 274 |
+
<img src="/numind/NuExtract3/resolve/main/img_1.png" alt="Logo of Mobilier 2000 with contact information: Tél.: (418) 275-4232, 1654, boul. Marcotte, Roberval (Qc) G8H 2P2"/>
|
| 275 |
+
</figure>
|
| 276 |
+
|
| 277 |
+
# COMMANDE
|
| 278 |
+
**NUMÉRO 72259**
|
| 279 |
+
|
| 280 |
+
1
|
| 281 |
+
|
| 282 |
+
**Vendu à**
|
| 283 |
+
TREMBLAY ERIC
|
| 284 |
+
ERIC TREMBLAY
|
| 285 |
+
348 BOUL. DE L'ANSE
|
| 286 |
+
ROBERVAL
|
| 287 |
+
G8H 1Y9
|
| 288 |
+
|
| 289 |
+
**Livré à**
|
| 290 |
+
TREMBLAY ERIC
|
| 291 |
+
ERIC TREMBLAY
|
| 292 |
+
348 BOUL. DE L'ANSE
|
| 293 |
+
ROBERVAL
|
| 294 |
+
G8H 1Y9
|
| 295 |
+
|
| 296 |
+
<table>
|
| 297 |
+
<thead>
|
| 298 |
+
<tr>
|
| 299 |
+
<th># CLIENT</th>
|
| 300 |
+
<th>EXPÉDITEUR</th>
|
| 301 |
+
<th>TERME DE CRÉDIT</th>
|
| 302 |
+
<th>DATE</th>
|
| 303 |
+
</tr>
|
| 304 |
+
</thead>
|
| 305 |
+
<tbody>
|
| 306 |
+
<tr>
|
| 307 |
+
<td>2753133</td>
|
| 308 |
+
<td>Notre camion</td>
|
| 309 |
+
<td>à la livraison</td>
|
| 310 |
+
<td>22/06/2023</td>
|
| 311 |
+
</tr>
|
| 312 |
+
</tbody>
|
| 313 |
+
</table>
|
| 314 |
+
|
| 315 |
+
<table>
|
| 316 |
+
<thead>
|
| 317 |
+
<tr>
|
| 318 |
+
<th>NOM DU VENDEUR</th>
|
| 319 |
+
<th>VOTRE ÉCONOMIE !</th>
|
| 320 |
+
<th># COMMANDE</th>
|
| 321 |
+
</tr>
|
| 322 |
+
</thead>
|
| 323 |
+
<tbody>
|
| 324 |
+
<tr>
|
| 325 |
+
<td>Éric</td>
|
| 326 |
+
<td>0.00</td>
|
| 327 |
+
<td></td>
|
| 328 |
+
</tr>
|
| 329 |
+
</tbody>
|
| 330 |
+
</table>
|
| 331 |
+
```
|
| 332 |
+
|
| 333 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#reasoning-and-non-reasoning-modes](https://huggingface.co/numind/NuExtract3/blob/main/README.md#reasoning-and-non-reasoning-modes) Reasoning and non-reasoning modes
|
| 334 |
+
|
| 335 |
+
NuExtract supports both reasoning and non-reasoning inference.
|
| 336 |
+
|
| 337 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#non-thinking-mode](https://huggingface.co/numind/NuExtract3/blob/main/README.md#non-thinking-mode) Non-thinking mode
|
| 338 |
+
|
| 339 |
+
Use this for fast and deterministic extraction or Markdown conversion.
|
| 340 |
+
|
| 341 |
+
```
|
| 342 |
+
enable_thinking = False
|
| 343 |
+
temperature = 0.2
|
| 344 |
+
```
|
| 345 |
+
|
| 346 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#thinking-mode](https://huggingface.co/numind/NuExtract3/blob/main/README.md#thinking-mode) Thinking mode
|
| 347 |
+
|
| 348 |
+
Use this for difficult documents, complex layouts, ambiguous fields, or cases where the document structure requires additional reasoning.
|
| 349 |
+
|
| 350 |
+
```
|
| 351 |
+
enable_thinking = True
|
| 352 |
+
temperature = 0.6
|
| 353 |
+
```
|
| 354 |
+
|
| 355 |
+
For production extraction workloads, we recommend starting with non-reasoning mode and enabling reasoning only for difficult examples.
|
| 356 |
+
|
| 357 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-deployment](https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-deployment) vLLM deployment
|
| 358 |
+
|
| 359 |
+
NuExtract can be served with vLLM using an OpenAI-compatible API.
|
| 360 |
+
|
| 361 |
+
```
|
| 362 |
+
vllm serve numind/NuExtract3 \
|
| 363 |
+
--trust-remote-code \
|
| 364 |
+
--limit-mm-per-prompt '{"image": 99, "video": 0}' \
|
| 365 |
+
--chat-template-content-format openai \
|
| 366 |
+
--generation-config vllm \
|
| 367 |
+
--max-model-len 131072 \
|
| 368 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 369 |
+
```
|
| 370 |
+
|
| 371 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#multi-token-prediction](https://huggingface.co/numind/NuExtract3/blob/main/README.md#multi-token-prediction) Multi Token Prediction
|
| 372 |
+
|
| 373 |
+
The deployment commands above enable Multi Token Prediction (MTP) through vLLM speculative decoding:
|
| 374 |
+
|
| 375 |
+
```
|
| 376 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 377 |
+
```
|
| 378 |
+
|
| 379 |
+
MTP can improve decoding throughput without changing the OpenAI-compatible request payload. You can tune `num_speculative_tokens` for your hardware and workload, or remove `--speculative-config` if your vLLM version or environment does not support this speculative decoding method.
|
| 380 |
+
|
| 381 |
+
If you encounter memory issues, reduce the maximum model length and the maximum number of images:
|
| 382 |
+
|
| 383 |
+
```
|
| 384 |
+
vllm serve numind/NuExtract-3 \
|
| 385 |
+
--trust-remote-code \
|
| 386 |
+
--limit-mm-per-prompt '{"image": 6, "video": 0}' \
|
| 387 |
+
--chat-template-content-format openai \
|
| 388 |
+
--generation-config vllm \
|
| 389 |
+
--max-model-len 16384 \
|
| 390 |
+
--speculative-config '{"method": "qwen3_next_mtp", "num_speculative_tokens": 2}'
|
| 391 |
+
```
|
| 392 |
+
|
| 393 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-structured-extraction-text](https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-structured-extraction-text) vLLM inference: structured extraction: text
|
| 394 |
+
|
| 395 |
+
```
|
| 396 |
+
import json
|
| 397 |
+
from openai import OpenAI
|
| 398 |
+
|
| 399 |
+
client = OpenAI(
|
| 400 |
+
api_key="EMPTY",
|
| 401 |
+
base_url="http://localhost:8000/v1",
|
| 402 |
+
)
|
| 403 |
+
|
| 404 |
+
template = {
|
| 405 |
+
"store": "verbatim-string",
|
| 406 |
+
"date": "date-time",
|
| 407 |
+
"total": "number",
|
| 408 |
+
"currency": ["USD", "EUR", "GBP", "JPY", "Other"],
|
| 409 |
+
"items": [
|
| 410 |
+
{
|
| 411 |
+
"name": "verbatim-string",
|
| 412 |
+
"price": "number"
|
| 413 |
+
}
|
| 414 |
+
]
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
response = client.chat.completions.create(
|
| 418 |
+
model="numind/NuExtract3",
|
| 419 |
+
temperature=0.2,
|
| 420 |
+
messages=[
|
| 421 |
+
{
|
| 422 |
+
"role": "user",
|
| 423 |
+
"content": [
|
| 424 |
+
{
|
| 425 |
+
"type": "text",
|
| 426 |
+
"text": "Yesterday I bought apples and coffee at Trader Joe's for a total of $12.40."
|
| 427 |
+
}
|
| 428 |
+
],
|
| 429 |
+
}
|
| 430 |
+
],
|
| 431 |
+
extra_body={
|
| 432 |
+
"chat_template_kwargs": {
|
| 433 |
+
"template": json.dumps(template),
|
| 434 |
+
"instructions": "Specify the time for the `date` entry only if it is present, otherwise only output the date component.",
|
| 435 |
+
"enable_thinking": False
|
| 436 |
+
}
|
| 437 |
+
}
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
print(response.choices[0].message.content)
|
| 441 |
+
```
|
| 442 |
+
|
| 443 |
+
Example output:
|
| 444 |
+
|
| 445 |
+
```
|
| 446 |
+
{
|
| 447 |
+
"store": "Trader Joe's",
|
| 448 |
+
"date": null,
|
| 449 |
+
"total": 12.40,
|
| 450 |
+
"currency": "USD",
|
| 451 |
+
"items": [
|
| 452 |
+
{
|
| 453 |
+
"name": "apples",
|
| 454 |
+
"price": null
|
| 455 |
+
},
|
| 456 |
+
{
|
| 457 |
+
"name": "coffee",
|
| 458 |
+
"price": null
|
| 459 |
+
}
|
| 460 |
+
]
|
| 461 |
+
}
|
| 462 |
+
```
|
| 463 |
+
|
| 464 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-structured-extraction-image](https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-structured-extraction-image) vLLM inference: structured extraction: image
|
| 465 |
+
|
| 466 |
+
```
|
| 467 |
+
import json
|
| 468 |
+
import base64
|
| 469 |
+
from openai import OpenAI
|
| 470 |
+
|
| 471 |
+
client = OpenAI(
|
| 472 |
+
api_key="EMPTY",
|
| 473 |
+
base_url="http://localhost:8000/v1",
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
def encode_image(image_path):
|
| 477 |
+
with open(image_path, "rb") as image_file:
|
| 478 |
+
return base64.b64encode(image_file.read()).decode("utf-8")
|
| 479 |
+
|
| 480 |
+
image_base64 = encode_image("receipt.png")
|
| 481 |
+
data_url = f"data:image/png;base64,{image_base64}"
|
| 482 |
+
|
| 483 |
+
template = {
|
| 484 |
+
"store": "verbatim-string",
|
| 485 |
+
"date": "date-time",
|
| 486 |
+
"total": "number",
|
| 487 |
+
"payment_method": "verbatim-string"
|
| 488 |
+
}
|
| 489 |
+
|
| 490 |
+
response = client.chat.completions.create(
|
| 491 |
+
model="numind/NuExtract3",
|
| 492 |
+
temperature=0.2,
|
| 493 |
+
messages=[
|
| 494 |
+
{
|
| 495 |
+
"role": "user",
|
| 496 |
+
"content": [
|
| 497 |
+
{
|
| 498 |
+
"type": "image_url",
|
| 499 |
+
"image_url": {"url": data_url}
|
| 500 |
+
}
|
| 501 |
+
],
|
| 502 |
+
}
|
| 503 |
+
],
|
| 504 |
+
extra_body={
|
| 505 |
+
"chat_template_kwargs": {
|
| 506 |
+
"template": json.dumps(template, indent=4),
|
| 507 |
+
"enable_thinking": False
|
| 508 |
+
}
|
| 509 |
+
}
|
| 510 |
+
)
|
| 511 |
+
|
| 512 |
+
print(response.choices[0].message.content)
|
| 513 |
+
```
|
| 514 |
+
|
| 515 |
+
Example output:
|
| 516 |
+
|
| 517 |
+
```
|
| 518 |
+
{
|
| 519 |
+
"store": "Trader Joe's",
|
| 520 |
+
"date": "2025-04-12",
|
| 521 |
+
"total": 42.85,
|
| 522 |
+
"payment_method": "Visa"
|
| 523 |
+
}
|
| 524 |
+
```
|
| 525 |
+
|
| 526 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#multiple-page-pdf](https://huggingface.co/numind/NuExtract3/blob/main/README.md#multiple-page-pdf) Multiple page PDF
|
| 527 |
+
|
| 528 |
+
You can render a PDF to one PNG image per page with PyMuPDF, then pass the images to vLLM in page order.
|
| 529 |
+
|
| 530 |
+
```
|
| 531 |
+
import base64
|
| 532 |
+
import json
|
| 533 |
+
|
| 534 |
+
import fitz # pip install pymupdf
|
| 535 |
+
from openai import OpenAI
|
| 536 |
+
|
| 537 |
+
client = OpenAI(
|
| 538 |
+
api_key="EMPTY",
|
| 539 |
+
base_url="http://localhost:8000/v1",
|
| 540 |
+
)
|
| 541 |
+
|
| 542 |
+
def pdf_to_png_data_urls(pdf_path, dpi=170):
|
| 543 |
+
data_urls = []
|
| 544 |
+
|
| 545 |
+
with fitz.open(pdf_path) as doc:
|
| 546 |
+
for page in doc:
|
| 547 |
+
pix = page.get_pixmap(dpi=dpi, alpha=False)
|
| 548 |
+
png_bytes = pix.tobytes("png")
|
| 549 |
+
png_base64 = base64.b64encode(png_bytes).decode("utf-8")
|
| 550 |
+
data_urls.append(f"data:image/png;base64,{png_base64}")
|
| 551 |
+
|
| 552 |
+
return data_urls
|
| 553 |
+
|
| 554 |
+
data_urls = pdf_to_png_data_urls("invoice.pdf", dpi=170)
|
| 555 |
+
|
| 556 |
+
template = {
|
| 557 |
+
"invoice_number": "verbatim-string",
|
| 558 |
+
"invoice_date": "date",
|
| 559 |
+
"total": "number",
|
| 560 |
+
"currency": "currency",
|
| 561 |
+
"line_items": [
|
| 562 |
+
{
|
| 563 |
+
"description": "verbatim-string",
|
| 564 |
+
"quantity": "number",
|
| 565 |
+
"unit_price": "number",
|
| 566 |
+
"total": "number"
|
| 567 |
+
}
|
| 568 |
+
]
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
response = client.chat.completions.create(
|
| 572 |
+
model="numind/NuExtract3",
|
| 573 |
+
temperature=0.2,
|
| 574 |
+
messages=[
|
| 575 |
+
{
|
| 576 |
+
"role": "user",
|
| 577 |
+
"content": [
|
| 578 |
+
{
|
| 579 |
+
"type": "image_url",
|
| 580 |
+
"image_url": {"url": data_url}
|
| 581 |
+
}
|
| 582 |
+
for data_url in data_urls
|
| 583 |
+
],
|
| 584 |
+
}
|
| 585 |
+
],
|
| 586 |
+
extra_body={
|
| 587 |
+
"chat_template_kwargs": {
|
| 588 |
+
"template": json.dumps(template, indent=4),
|
| 589 |
+
"enable_thinking": False
|
| 590 |
+
}
|
| 591 |
+
}
|
| 592 |
+
)
|
| 593 |
+
|
| 594 |
+
print(response.choices[0].message.content)
|
| 595 |
+
```
|
| 596 |
+
|
| 597 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-document-to-markdown](https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-document-to-markdown) vLLM inference: document-to-Markdown
|
| 598 |
+
|
| 599 |
+
For Markdown OCR, use `mode="markdown"` or `mode="content"` without a template.
|
| 600 |
+
|
| 601 |
+
```
|
| 602 |
+
import base64
|
| 603 |
+
from openai import OpenAI
|
| 604 |
+
|
| 605 |
+
client = OpenAI(
|
| 606 |
+
api_key="EMPTY",
|
| 607 |
+
base_url="http://localhost:8000/v1",
|
| 608 |
+
)
|
| 609 |
+
|
| 610 |
+
def encode_image(image_path):
|
| 611 |
+
with open(image_path, "rb") as image_file:
|
| 612 |
+
return base64.b64encode(image_file.read()).decode("utf-8")
|
| 613 |
+
|
| 614 |
+
image_base64 = encode_image("document.png")
|
| 615 |
+
data_url = f"data:image/png;base64,{image_base64}"
|
| 616 |
+
|
| 617 |
+
response = client.chat.completions.create(
|
| 618 |
+
model="numind/NuExtract3",
|
| 619 |
+
temperature=1,
|
| 620 |
+
messages=[
|
| 621 |
+
{
|
| 622 |
+
"role": "user",
|
| 623 |
+
"content": [
|
| 624 |
+
{
|
| 625 |
+
"type": "image_url",
|
| 626 |
+
"image_url": {"url": data_url}
|
| 627 |
+
}
|
| 628 |
+
],
|
| 629 |
+
}
|
| 630 |
+
],
|
| 631 |
+
extra_body={
|
| 632 |
+
"chat_template_kwargs": {
|
| 633 |
+
"mode": "markdown",
|
| 634 |
+
"enable_thinking": False
|
| 635 |
+
}
|
| 636 |
+
}
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
+
print(response.choices[0].message.content)
|
| 640 |
+
```
|
| 641 |
+
|
| 642 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-reasoning-mode](https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-reasoning-mode) vLLM inference: reasoning mode
|
| 643 |
+
|
| 644 |
+
Reasoning can be enabled for harder structured extraction or Markdown tasks.
|
| 645 |
+
|
| 646 |
+
```
|
| 647 |
+
response = client.chat.completions.create(
|
| 648 |
+
model="numind/NuExtract3",
|
| 649 |
+
temperature=1,
|
| 650 |
+
messages=[
|
| 651 |
+
{
|
| 652 |
+
"role": "user",
|
| 653 |
+
"content": [
|
| 654 |
+
{
|
| 655 |
+
"type": "image_url",
|
| 656 |
+
"image_url": {"url": data_url}
|
| 657 |
+
}
|
| 658 |
+
],
|
| 659 |
+
}
|
| 660 |
+
],
|
| 661 |
+
extra_body={
|
| 662 |
+
"chat_template_kwargs": {
|
| 663 |
+
"mode": "markdown",
|
| 664 |
+
"enable_thinking": True
|
| 665 |
+
}
|
| 666 |
+
}
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
result = response.choices[0].message.content
|
| 670 |
+
|
| 671 |
+
if "</think>" in result:
|
| 672 |
+
reasoning, answer = [part.strip() for part in result.split("</think>")]
|
| 673 |
+
else:
|
| 674 |
+
reasoning, answer = None, result
|
| 675 |
+
|
| 676 |
+
print(answer)
|
| 677 |
+
```
|
| 678 |
+
|
| 679 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#in-context-examples-for-extraction](https://huggingface.co/numind/NuExtract3/blob/main/README.md#in-context-examples-for-extraction) In-context examples for extraction
|
| 680 |
+
|
| 681 |
+
NuExtract supports in-context examples for structured extraction.
|
| 682 |
+
|
| 683 |
+
Examples are especially useful when the desired formatting is ambiguous or when the schema requires task-specific conventions. Examples can be provided by using `developer` messages, for which all items of the contents except the last one are the input, and the last one is the expected output.
|
| 684 |
+
|
| 685 |
+
```
|
| 686 |
+
import json
|
| 687 |
+
from openai import OpenAI
|
| 688 |
+
|
| 689 |
+
client = OpenAI(
|
| 690 |
+
api_key="EMPTY",
|
| 691 |
+
base_url="http://localhost:8000/v1",
|
| 692 |
+
)
|
| 693 |
+
|
| 694 |
+
template = {
|
| 695 |
+
"names": ["string"]
|
| 696 |
+
}
|
| 697 |
+
|
| 698 |
+
response = client.chat.completions.create(
|
| 699 |
+
model="numind/NuExtract3",
|
| 700 |
+
temperature=0.2,
|
| 701 |
+
messages=[
|
| 702 |
+
{
|
| 703 |
+
"role": "developer",
|
| 704 |
+
"content": [
|
| 705 |
+
{
|
| 706 |
+
"type": "text",
|
| 707 |
+
"text": "Stephen is the manager at Susan's store.",
|
| 708 |
+
},
|
| 709 |
+
{
|
| 710 |
+
"type": "text",
|
| 711 |
+
"text": "{\"names\": [\"-STEPHEN-\", \"-SUSAN-\"]}",
|
| 712 |
+
}
|
| 713 |
+
],
|
| 714 |
+
},
|
| 715 |
+
{
|
| 716 |
+
"role": "user",
|
| 717 |
+
"content": [
|
| 718 |
+
{
|
| 719 |
+
"type": "text",
|
| 720 |
+
"text": "John went to the restaurant with Mary. James went to the cinema."
|
| 721 |
+
}
|
| 722 |
+
],
|
| 723 |
+
}
|
| 724 |
+
],
|
| 725 |
+
extra_body={
|
| 726 |
+
"chat_template_kwargs": {
|
| 727 |
+
"template": json.dumps(template, indent=4),
|
| 728 |
+
"enable_thinking": False
|
| 729 |
+
}
|
| 730 |
+
}
|
| 731 |
+
)
|
| 732 |
+
|
| 733 |
+
print(response.choices[0].message.content)
|
| 734 |
+
```
|
| 735 |
+
|
| 736 |
+
Example output:
|
| 737 |
+
|
| 738 |
+
```
|
| 739 |
+
{
|
| 740 |
+
"names": ["-JOHN-", "-MARY-", "-JAMES-"]
|
| 741 |
+
}
|
| 742 |
+
```
|
| 743 |
+
|
| 744 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-template-generation](https://huggingface.co/numind/NuExtract3/blob/main/README.md#vllm-inference-template-generation) vLLM inference: template generation
|
| 745 |
+
|
| 746 |
+
NuExtract can generate an extraction template from a natural language description.
|
| 747 |
+
|
| 748 |
+
```
|
| 749 |
+
from openai import OpenAI
|
| 750 |
+
|
| 751 |
+
client = OpenAI(
|
| 752 |
+
api_key="EMPTY",
|
| 753 |
+
base_url="http://localhost:8000/v1",
|
| 754 |
+
)
|
| 755 |
+
|
| 756 |
+
response = client.chat.completions.create(
|
| 757 |
+
model="numind/NuExtract3",
|
| 758 |
+
temperature=0.2,
|
| 759 |
+
messages=[
|
| 760 |
+
{
|
| 761 |
+
"role": "user",
|
| 762 |
+
"content": [
|
| 763 |
+
{
|
| 764 |
+
"type": "text",
|
| 765 |
+
"text": "I want to extract the key details from a rental contract."
|
| 766 |
+
}
|
| 767 |
+
],
|
| 768 |
+
}
|
| 769 |
+
],
|
| 770 |
+
extra_body={
|
| 771 |
+
"chat_template_kwargs": {
|
| 772 |
+
"mode": "template-generation"
|
| 773 |
+
}
|
| 774 |
+
}
|
| 775 |
+
)
|
| 776 |
+
|
| 777 |
+
print(response.choices[0].message.content)
|
| 778 |
+
```
|
| 779 |
+
|
| 780 |
+
Example output:
|
| 781 |
+
|
| 782 |
+
```
|
| 783 |
+
{
|
| 784 |
+
"contract_title": "verbatim-string",
|
| 785 |
+
"landlord": "verbatim-string",
|
| 786 |
+
"tenant": "verbatim-string",
|
| 787 |
+
"property_address": "verbatim-string",
|
| 788 |
+
"start_date": "date-time",
|
| 789 |
+
"end_date": "date-time",
|
| 790 |
+
"monthly_rent": "number",
|
| 791 |
+
"currency": "verbatim-string",
|
| 792 |
+
"deposit": "number",
|
| 793 |
+
"signatories": ["verbatim-string"]
|
| 794 |
+
}
|
| 795 |
+
```
|
| 796 |
+
|
| 797 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#curl-examples](https://huggingface.co/numind/NuExtract3/blob/main/README.md#curl-examples) Curl examples
|
| 798 |
+
|
| 799 |
+
The following examples assume that vLLM is running locally on port 8000. They use `jq` to build valid JSON request bodies without manually escaping the image data or template string.
|
| 800 |
+
|
| 801 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#single-image-structured-extraction](https://huggingface.co/numind/NuExtract3/blob/main/README.md#single-image-structured-extraction) Single image structured extraction
|
| 802 |
+
|
| 803 |
+
```
|
| 804 |
+
API_KEY="EMPTY"
|
| 805 |
+
IMAGE_BASE64_FILE=$(mktemp)
|
| 806 |
+
REQUEST_BODY_FILE=$(mktemp)
|
| 807 |
+
|
| 808 |
+
base64 < receipt.png | tr -d '\n' > "$IMAGE_BASE64_FILE"
|
| 809 |
+
|
| 810 |
+
TEMPLATE=$(cat <<'JSON'
|
| 811 |
+
{
|
| 812 |
+
"store": "verbatim-string",
|
| 813 |
+
"date": "date-time",
|
| 814 |
+
"total": "number",
|
| 815 |
+
"payment_method": "verbatim-string"
|
| 816 |
+
}
|
| 817 |
+
JSON
|
| 818 |
+
)
|
| 819 |
+
|
| 820 |
+
jq -n \
|
| 821 |
+
--rawfile image_base64 "$IMAGE_BASE64_FILE" \
|
| 822 |
+
--arg template "$TEMPLATE" \
|
| 823 |
+
'{
|
| 824 |
+
model: "numind/NuExtract3",
|
| 825 |
+
temperature: 0.6,
|
| 826 |
+
messages: [
|
| 827 |
+
{
|
| 828 |
+
role: "user",
|
| 829 |
+
content: [
|
| 830 |
+
{
|
| 831 |
+
type: "image_url",
|
| 832 |
+
image_url: {url: ("data:image/png;base64," + $image_base64)}
|
| 833 |
+
}
|
| 834 |
+
]
|
| 835 |
+
}
|
| 836 |
+
],
|
| 837 |
+
chat_template_kwargs: {
|
| 838 |
+
template: $template,
|
| 839 |
+
enable_thinking: false
|
| 840 |
+
}
|
| 841 |
+
}' > "$REQUEST_BODY_FILE"
|
| 842 |
+
|
| 843 |
+
curl http://localhost:8000/v1/chat/completions \
|
| 844 |
+
-H "Content-Type: application/json" \
|
| 845 |
+
-H "Authorization: Bearer $API_KEY" \
|
| 846 |
+
--data-binary "@$REQUEST_BODY_FILE"
|
| 847 |
+
|
| 848 |
+
rm "$IMAGE_BASE64_FILE" "$REQUEST_BODY_FILE"
|
| 849 |
+
```
|
| 850 |
+
|
| 851 |
+
### [https://huggingface.co/numind/NuExtract3/blob/main/README.md#single-image-content-extraction](https://huggingface.co/numind/NuExtract3/blob/main/README.md#single-image-content-extraction) Single image content extraction
|
| 852 |
+
|
| 853 |
+
```
|
| 854 |
+
API_KEY="EMPTY"
|
| 855 |
+
IMAGE_BASE64_FILE=$(mktemp)
|
| 856 |
+
REQUEST_BODY_FILE=$(mktemp)
|
| 857 |
+
|
| 858 |
+
base64 < document.png | tr -d '\n' > "$IMAGE_BASE64_FILE"
|
| 859 |
+
|
| 860 |
+
jq -n \
|
| 861 |
+
--rawfile image_base64 "$IMAGE_BASE64_FILE" \
|
| 862 |
+
'{
|
| 863 |
+
model: "numind/NuExtract3",
|
| 864 |
+
temperature: 0.6,
|
| 865 |
+
messages: [
|
| 866 |
+
{
|
| 867 |
+
role: "user",
|
| 868 |
+
content: [
|
| 869 |
+
{
|
| 870 |
+
type: "image_url",
|
| 871 |
+
image_url: {url: ("data:image/png;base64," + $image_base64)}
|
| 872 |
+
}
|
| 873 |
+
]
|
| 874 |
+
}
|
| 875 |
+
],
|
| 876 |
+
chat_template_kwargs: {
|
| 877 |
+
mode: "content",
|
| 878 |
+
enable_thinking: false
|
| 879 |
+
}
|
| 880 |
+
}' > "$REQUEST_BODY_FILE"
|
| 881 |
+
|
| 882 |
+
curl http://localhost:8000/v1/chat/completions \
|
| 883 |
+
-H "Content-Type: application/json" \
|
| 884 |
+
-H "Authorization: Bearer $API_KEY" \
|
| 885 |
+
--data-binary "@$REQUEST_BODY_FILE"
|
| 886 |
+
|
| 887 |
+
rm "$IMAGE_BASE64_FILE" "$REQUEST_BODY_FILE"
|
| 888 |
+
```
|
| 889 |
+
|
| 890 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#transformers-example](https://huggingface.co/numind/NuExtract3/blob/main/README.md#transformers-example) Transformers example
|
| 891 |
+
|
| 892 |
+
You can also run NuExtract directly with `transformers`. The same `template`, `mode`, and `enable_thinking` options are passed to `processor.apply_chat_template`.
|
| 893 |
+
|
| 894 |
+
```
|
| 895 |
+
import json
|
| 896 |
+
|
| 897 |
+
import torch
|
| 898 |
+
from PIL import Image
|
| 899 |
+
from transformers import AutoModelForImageTextToText, AutoProcessor
|
| 900 |
+
|
| 901 |
+
model_id = "numind/NuExtract3"
|
| 902 |
+
|
| 903 |
+
processor = AutoProcessor.from_pretrained(
|
| 904 |
+
model_id,
|
| 905 |
+
trust_remote_code=True,
|
| 906 |
+
)
|
| 907 |
+
model = AutoModelForImageTextToText.from_pretrained(
|
| 908 |
+
model_id,
|
| 909 |
+
dtype=torch.bfloat16,
|
| 910 |
+
device_map="auto",
|
| 911 |
+
trust_remote_code=True,
|
| 912 |
+
).eval()
|
| 913 |
+
|
| 914 |
+
def run_nuextract(messages, **chat_template_kwargs):
|
| 915 |
+
inputs = processor.apply_chat_template(
|
| 916 |
+
messages,
|
| 917 |
+
add_generation_prompt=True,
|
| 918 |
+
tokenize=True,
|
| 919 |
+
return_dict=True,
|
| 920 |
+
return_tensors="pt",
|
| 921 |
+
**chat_template_kwargs,
|
| 922 |
+
).to(model.device)
|
| 923 |
+
|
| 924 |
+
with torch.inference_mode():
|
| 925 |
+
generated_ids = model.generate(
|
| 926 |
+
**inputs,
|
| 927 |
+
max_new_tokens=4096,
|
| 928 |
+
do_sample=False,
|
| 929 |
+
)
|
| 930 |
+
|
| 931 |
+
generated_ids = generated_ids[:, inputs.input_ids.shape[1]:]
|
| 932 |
+
return processor.batch_decode(
|
| 933 |
+
generated_ids,
|
| 934 |
+
skip_special_tokens=True,
|
| 935 |
+
clean_up_tokenization_spaces=False,
|
| 936 |
+
)[0].strip()
|
| 937 |
+
|
| 938 |
+
# Single image structured extraction
|
| 939 |
+
receipt_image = Image.open("receipt.png").convert("RGB")
|
| 940 |
+
receipt_messages = [
|
| 941 |
+
{
|
| 942 |
+
"role": "user",
|
| 943 |
+
"content": [
|
| 944 |
+
{
|
| 945 |
+
"type": "image",
|
| 946 |
+
"image": receipt_image,
|
| 947 |
+
}
|
| 948 |
+
],
|
| 949 |
+
}
|
| 950 |
+
]
|
| 951 |
+
|
| 952 |
+
template = {
|
| 953 |
+
"store": "verbatim-string",
|
| 954 |
+
"date": "date-time",
|
| 955 |
+
"total": "number",
|
| 956 |
+
"payment_method": "verbatim-string"
|
| 957 |
+
}
|
| 958 |
+
|
| 959 |
+
structured_output = run_nuextract(
|
| 960 |
+
receipt_messages,
|
| 961 |
+
template=json.dumps(template, indent=4),
|
| 962 |
+
enable_thinking=False,
|
| 963 |
+
)
|
| 964 |
+
print(structured_output)
|
| 965 |
+
|
| 966 |
+
# Single image content extraction
|
| 967 |
+
document_image = Image.open("document.png").convert("RGB")
|
| 968 |
+
document_messages = [
|
| 969 |
+
{
|
| 970 |
+
"role": "user",
|
| 971 |
+
"content": [
|
| 972 |
+
{
|
| 973 |
+
"type": "image",
|
| 974 |
+
"image": document_image,
|
| 975 |
+
}
|
| 976 |
+
],
|
| 977 |
+
}
|
| 978 |
+
]
|
| 979 |
+
|
| 980 |
+
content_output = run_nuextract(
|
| 981 |
+
document_messages,
|
| 982 |
+
mode="content",
|
| 983 |
+
enable_thinking=False,
|
| 984 |
+
)
|
| 985 |
+
print(content_output)
|
| 986 |
+
```
|
| 987 |
+
|
| 988 |
+
Special thanks to the Lambda.ai team for the compute that made this project a success.
|
| 989 |
+
|
| 990 |
+
## [https://huggingface.co/numind/NuExtract3/blob/main/README.md#citation](https://huggingface.co/numind/NuExtract3/blob/main/README.md#citation) Citation
|
| 991 |
+
|
| 992 |
+
If you use NuExtract, please cite NuMind and link to the model page.
|
| 993 |
+
|
| 994 |
+
```
|
| 995 |
+
@misc{nuextract3,
|
| 996 |
+
title = {NuExtract3},
|
| 997 |
+
author = {NuMind},
|
| 998 |
+
year = {2026},
|
| 999 |
+
url = {https://nuextract.ai/}
|
| 1000 |
+
}
|
| 1001 |
+
```
|
AGENTS.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Repository implementation policy
|
| 2 |
+
|
| 3 |
+
- This repository has one supported execution path: the explicitly requested path.
|
| 4 |
+
- Never add error handling, `try`/`except`, recovery logic, fallback values, fallback providers, compatibility shims, silent substitution, or degraded modes.
|
| 5 |
+
- Never catch, suppress, translate, wrap, retry, or replace failures.
|
| 6 |
+
- Never add validation branches whose purpose is to produce a custom error path.
|
| 7 |
+
- Let failures surface naturally at their source.
|
| 8 |
+
- Do not preserve legacy behavior unless the user explicitly requests it.
|
| 9 |
+
- When a requested implementation cannot run as specified, report the blocker; do not create an alternate implementation.
|
app/config.py
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
TRANSFORMERS_MODEL = os.environ.get("TRANSFORMERS_MODEL", "mradermacher/NuMarkdown-8B-Thinking-i1-GGUF")
|
|
|
|
|
|
|
|
|
app/nuextract.py
CHANGED
|
@@ -1,7 +1,71 @@
|
|
| 1 |
-
from
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
from PIL import Image
|
| 5 |
+
from transformers import AutoModelForImageTextToText, AutoProcessor
|
| 6 |
+
|
| 7 |
+
model_id = "numind/NuExtract3"
|
| 8 |
+
|
| 9 |
+
processor = AutoProcessor.from_pretrained(
|
| 10 |
+
model_id,
|
| 11 |
+
trust_remote_code=True,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
model = AutoModelForImageTextToText.from_pretrained(
|
| 15 |
+
model_id,
|
| 16 |
+
dtype=torch.bfloat16,
|
| 17 |
+
device_map="auto",
|
| 18 |
+
trust_remote_code=True,
|
| 19 |
+
).eval()
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def run_nuextract(messages, **chat_template_kwargs):
|
| 23 |
+
inputs = processor.apply_chat_template(
|
| 24 |
+
messages,
|
| 25 |
+
add_generation_prompt=True,
|
| 26 |
+
tokenize=True,
|
| 27 |
+
return_dict=True,
|
| 28 |
+
return_tensors="pt",
|
| 29 |
+
**chat_template_kwargs,
|
| 30 |
+
).to(model.device)
|
| 31 |
+
|
| 32 |
+
with torch.inference_mode():
|
| 33 |
+
generated_ids = model.generate(
|
| 34 |
+
**inputs,
|
| 35 |
+
max_new_tokens=4096,
|
| 36 |
+
do_sample=False,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
generated_ids = generated_ids[:, inputs.input_ids.shape[1]:]
|
| 40 |
+
return processor.batch_decode(
|
| 41 |
+
generated_ids,
|
| 42 |
+
skip_special_tokens=True,
|
| 43 |
+
clean_up_tokenization_spaces=False,
|
| 44 |
+
)[0].strip()
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
sample_path = (
|
| 48 |
+
Path(__file__).parents[1]
|
| 49 |
+
/ "data"
|
| 50 |
+
/ "samples"
|
| 51 |
+
/ "Screenshot 2026-07-18 171416.png"
|
| 52 |
+
)
|
| 53 |
+
sample_image = Image.open(sample_path).convert("RGB")
|
| 54 |
+
messages = [
|
| 55 |
+
{
|
| 56 |
+
"role": "user",
|
| 57 |
+
"content": [
|
| 58 |
+
{
|
| 59 |
+
"type": "image",
|
| 60 |
+
"image": sample_image,
|
| 61 |
+
}
|
| 62 |
+
],
|
| 63 |
+
}
|
| 64 |
+
]
|
| 65 |
+
|
| 66 |
+
output = run_nuextract(
|
| 67 |
+
messages,
|
| 68 |
+
mode="content",
|
| 69 |
+
enable_thinking=False,
|
| 70 |
+
)
|
| 71 |
+
print(output)
|
notebooks/NuExtract3.ipynb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 3,
|
| 6 |
+
"id": "38837f4a",
|
| 7 |
+
"metadata": {},
|
| 8 |
+
"outputs": [
|
| 9 |
+
{
|
| 10 |
+
"data": {
|
| 11 |
+
"application/vnd.jupyter.widget-view+json": {
|
| 12 |
+
"model_id": "282526cb378b487a8b6743dcdbaaf51d",
|
| 13 |
+
"version_major": 2,
|
| 14 |
+
"version_minor": 0
|
| 15 |
+
},
|
| 16 |
+
"text/plain": [
|
| 17 |
+
"NuExtract3-Q4_K_M.gguf: 0%| | 0.00/2.71G [00:00<?, ?B/s]"
|
| 18 |
+
]
|
| 19 |
+
},
|
| 20 |
+
"metadata": {},
|
| 21 |
+
"output_type": "display_data"
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"ename": "ValueError",
|
| 25 |
+
"evalue": "Could not load model numind/NuExtract3-GGUF with any of the following classes: (<class 'transformers.models.auto.modeling_auto.AutoModelForImageTextToText'>, <class 'transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5ForConditionalGeneration'>). See the original errors:\n\nwhile loading with AutoModelForImageTextToText, an error is thrown:\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 240, in load_model\n model = model_class.from_pretrained(model, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\auto_factory.py\", line 336, in from_pretrained\n config, kwargs = AutoConfig.from_pretrained(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\configuration_auto.py\", line 376, in from_pretrained\n config_dict, unused_kwargs = PreTrainedConfig.get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 721, in get_config_dict\n config_dict, kwargs = cls._get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 807, in _get_config_dict\n config_dict = load_gguf_checkpoint(resolved_config_file, return_tensors=False)[\"config\"]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_gguf_pytorch_utils.py\", line 590, in load_gguf_checkpoint\n if is_gguf_available() and is_torch_available():\n ^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\utils\\import_utils.py\", line 1207, in is_gguf_available\n return is_available and version.parse(gguf_version) >= version.parse(min_version)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 56, in parse\n return Version(version)\n ^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 202, in __init__\n raise InvalidVersion(f\"Invalid version: '{version}'\")\npackaging.version.InvalidVersion: Invalid version: 'N/A'\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 256, in load_model\n model = model_class.from_pretrained(model, **fp32_kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\auto_factory.py\", line 336, in from_pretrained\n config, kwargs = AutoConfig.from_pretrained(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\configuration_auto.py\", line 376, in from_pretrained\n config_dict, unused_kwargs = PreTrainedConfig.get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 721, in get_config_dict\n config_dict, kwargs = cls._get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 807, in _get_config_dict\n config_dict = load_gguf_checkpoint(resolved_config_file, return_tensors=False)[\"config\"]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_gguf_pytorch_utils.py\", line 590, in load_gguf_checkpoint\n if is_gguf_available() and is_torch_available():\n ^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\utils\\import_utils.py\", line 1207, in is_gguf_available\n return is_available and version.parse(gguf_version) >= version.parse(min_version)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 56, in parse\n return Version(version)\n ^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 202, in __init__\n raise InvalidVersion(f\"Invalid version: '{version}'\")\npackaging.version.InvalidVersion: Invalid version: 'N/A'\n\nwhile loading with Qwen3_5ForConditionalGeneration, an error is thrown:\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 240, in load_model\n model = model_class.from_pretrained(model, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_utils.py\", line 4281, in from_pretrained\n raise ValueError(\"accelerate is required when loading a GGUF file `pip install accelerate`.\")\nValueError: accelerate is required when loading a GGUF file `pip install accelerate`.\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 256, in load_model\n model = model_class.from_pretrained(model, **fp32_kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_utils.py\", line 4281, in from_pretrained\n raise ValueError(\"accelerate is required when loading a GGUF file `pip install accelerate`.\")\nValueError: accelerate is required when loading a GGUF file `pip install accelerate`.\n\n\n",
|
| 26 |
+
"output_type": "error",
|
| 27 |
+
"traceback": [
|
| 28 |
+
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
| 29 |
+
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
|
| 30 |
+
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 6\u001b[39m\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m transformers \u001b[38;5;28;01mimport\u001b[39;00m AutoConfig, AutoProcessor, pipeline\n\u001b[32m 3\u001b[39m \n\u001b[32m 4\u001b[39m config = AutoConfig.from_pretrained(\u001b[33m\"numind/NuExtract3\"\u001b[39m)\n\u001b[32m 5\u001b[39m processor = AutoProcessor.from_pretrained(\u001b[33m\"numind/NuExtract3\"\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m6\u001b[39m pipe = pipeline(\u001b[33m\"image-text-to-text\"\u001b[39m, model=\u001b[33m\"numind/NuExtract3-GGUF\"\u001b[39m, config=config, processor=processor, device_map=\u001b[33m\"auto\"\u001b[39m, model_kwargs={\u001b[33m\"gguf_file\"\u001b[39m: \u001b[33m\"NuExtract3-Q4_K_M.gguf\"\u001b[39m})\n\u001b[32m 7\u001b[39m image = str(next((Path.cwd() / \u001b[33m\"data\"\u001b[39m / \u001b[33m\"samples\"\u001b[39m).glob(\u001b[33m\"*.png\"\u001b[39m)))\n\u001b[32m 8\u001b[39m messages = [{\"role\": \"user\", \"content\": [\n\u001b[32m 9\u001b[39m {\u001b[33m\"type\"\u001b[39m: \u001b[33m\"image\"\u001b[39m, \u001b[33m\"url\"\u001b[39m: image},\n",
|
| 31 |
+
"\u001b[36mFile \u001b[39m\u001b[32ms:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\__init__.py:1033\u001b[39m, in \u001b[36mpipeline\u001b[39m\u001b[34m(task, model, config, tokenizer, feature_extractor, image_processor, video_processor, processor, revision, use_fast, token, device, device_map, dtype, trust_remote_code, model_kwargs, pipeline_class, **kwargs)\u001b[39m\n\u001b[32m 1031\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(model, \u001b[38;5;28mstr\u001b[39m):\n\u001b[32m 1032\u001b[39m model_classes = targeted_task[\u001b[33m\"\u001b[39m\u001b[33mpt\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m-> \u001b[39m\u001b[32m1033\u001b[39m model = \u001b[30;43mload_model\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 1034\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43madapter_path\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43;01mif\u001b[39;49;00m\u001b[30;43m \u001b[39;49m\u001b[30;43madapter_path\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43;01mis\u001b[39;49;00m\u001b[30;43m \u001b[39;49m\u001b[30;43;01mnot\u001b[39;49;00m\u001b[30;43m \u001b[39;49m\u001b[30;43;01mNone\u001b[39;49;00m\u001b[30;43m \u001b[39;49m\u001b[30;43;01melse\u001b[39;49;00m\u001b[30;43m \u001b[39;49m\u001b[30;43mmodel\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1035\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mmodel_classes\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mmodel_classes\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1036\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mconfig\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mconfig\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1037\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mtask\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mtask\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1038\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43mhub_kwargs\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1039\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43mmodel_kwargs\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1040\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 1042\u001b[39m hub_kwargs[\u001b[33m\"\u001b[39m\u001b[33m_commit_hash\u001b[39m\u001b[33m\"\u001b[39m] = model.config._commit_hash\n\u001b[32m 1044\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m pipeline_class \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n",
|
| 32 |
+
"\u001b[36mFile \u001b[39m\u001b[32ms:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py:276\u001b[39m, in \u001b[36mload_model\u001b[39m\u001b[34m(model, config, model_classes, task, **model_kwargs)\u001b[39m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m class_name, trace \u001b[38;5;129;01min\u001b[39;00m all_traceback.items():\n\u001b[32m 275\u001b[39m error += \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mwhile loading with \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mclass_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, an error is thrown:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mtrace\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m\n\u001b[32m--> \u001b[39m\u001b[32m276\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 277\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mCould not load model \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmodel\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with any of the following classes: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mclass_tuple\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m. See the original errors:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00merror\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m\n\u001b[32m 278\u001b[39m )\n\u001b[32m 280\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m model\n",
|
| 33 |
+
"\u001b[31mValueError\u001b[39m: Could not load model numind/NuExtract3-GGUF with any of the following classes: (<class 'transformers.models.auto.modeling_auto.AutoModelForImageTextToText'>, <class 'transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5ForConditionalGeneration'>). See the original errors:\n\nwhile loading with AutoModelForImageTextToText, an error is thrown:\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 240, in load_model\n model = model_class.from_pretrained(model, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\auto_factory.py\", line 336, in from_pretrained\n config, kwargs = AutoConfig.from_pretrained(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\configuration_auto.py\", line 376, in from_pretrained\n config_dict, unused_kwargs = PreTrainedConfig.get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 721, in get_config_dict\n config_dict, kwargs = cls._get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 807, in _get_config_dict\n config_dict = load_gguf_checkpoint(resolved_config_file, return_tensors=False)[\"config\"]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_gguf_pytorch_utils.py\", line 590, in load_gguf_checkpoint\n if is_gguf_available() and is_torch_available():\n ^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\utils\\import_utils.py\", line 1207, in is_gguf_available\n return is_available and version.parse(gguf_version) >= version.parse(min_version)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 56, in parse\n return Version(version)\n ^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 202, in __init__\n raise InvalidVersion(f\"Invalid version: '{version}'\")\npackaging.version.InvalidVersion: Invalid version: 'N/A'\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 256, in load_model\n model = model_class.from_pretrained(model, **fp32_kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\auto_factory.py\", line 336, in from_pretrained\n config, kwargs = AutoConfig.from_pretrained(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\models\\auto\\configuration_auto.py\", line 376, in from_pretrained\n config_dict, unused_kwargs = PreTrainedConfig.get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 721, in get_config_dict\n config_dict, kwargs = cls._get_config_dict(pretrained_model_name_or_path, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\configuration_utils.py\", line 807, in _get_config_dict\n config_dict = load_gguf_checkpoint(resolved_config_file, return_tensors=False)[\"config\"]\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_gguf_pytorch_utils.py\", line 590, in load_gguf_checkpoint\n if is_gguf_available() and is_torch_available():\n ^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\utils\\import_utils.py\", line 1207, in is_gguf_available\n return is_available and version.parse(gguf_version) >= version.parse(min_version)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 56, in parse\n return Version(version)\n ^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\packaging\\version.py\", line 202, in __init__\n raise InvalidVersion(f\"Invalid version: '{version}'\")\npackaging.version.InvalidVersion: Invalid version: 'N/A'\n\nwhile loading with Qwen3_5ForConditionalGeneration, an error is thrown:\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 240, in load_model\n model = model_class.from_pretrained(model, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_utils.py\", line 4281, in from_pretrained\n raise ValueError(\"accelerate is required when loading a GGUF file `pip install accelerate`.\")\nValueError: accelerate is required when loading a GGUF file `pip install accelerate`.\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\pipelines\\base.py\", line 256, in load_model\n model = model_class.from_pretrained(model, **fp32_kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\transformers\\modeling_utils.py\", line 4281, in from_pretrained\n raise ValueError(\"accelerate is required when loading a GGUF file `pip install accelerate`.\")\nValueError: accelerate is required when loading a GGUF file `pip install accelerate`.\n\n\n"
|
| 34 |
+
]
|
| 35 |
+
}
|
| 36 |
+
],
|
| 37 |
+
"source": [
|
| 38 |
+
"from pathlib import Path\n",
|
| 39 |
+
"from transformers import AutoConfig, AutoProcessor, pipeline\n",
|
| 40 |
+
"\n",
|
| 41 |
+
"config = AutoConfig.from_pretrained(\"numind/NuExtract3\")\n",
|
| 42 |
+
"processor = AutoProcessor.from_pretrained(\"numind/NuExtract3\")\n",
|
| 43 |
+
"pipe = pipeline(\"image-text-to-text\", model=\"numind/NuExtract3-GGUF\", config=config, processor=processor, device_map=\"auto\", model_kwargs={\"gguf_file\": \"NuExtract3-Q4_K_M.gguf\"})\n",
|
| 44 |
+
"image = str(next((Path.cwd() / \"data\" / \"samples\").glob(\"*.png\")))\n",
|
| 45 |
+
"messages = [{\"role\": \"user\", \"content\": [\n",
|
| 46 |
+
" {\"type\": \"image\", \"url\": image},\n",
|
| 47 |
+
" {\"type\": \"text\", \"text\": \"Extract all document content as structured JSON.\"},\n",
|
| 48 |
+
"]}]\n",
|
| 49 |
+
"pipe(text=messages, max_new_tokens=4096)"
|
| 50 |
+
]
|
| 51 |
+
}
|
| 52 |
+
],
|
| 53 |
+
"metadata": {
|
| 54 |
+
"kernelspec": {
|
| 55 |
+
"display_name": ".venv",
|
| 56 |
+
"language": "python",
|
| 57 |
+
"name": "python3"
|
| 58 |
+
},
|
| 59 |
+
"language_info": {
|
| 60 |
+
"name": "python",
|
| 61 |
+
"version": "3.12.10"
|
| 62 |
+
}
|
| 63 |
+
},
|
| 64 |
+
"nbformat": 4,
|
| 65 |
+
"nbformat_minor": 5
|
| 66 |
+
}
|
notebooks/nuextract-2.0_inference.ipynb
DELETED
|
@@ -1,876 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "markdown",
|
| 5 |
-
"id": "cbe8b1f6-ec66-4fbc-a9f8-774acd8a255a",
|
| 6 |
-
"metadata": {},
|
| 7 |
-
"source": [
|
| 8 |
-
"# NuExtract 2.0 Inference\n",
|
| 9 |
-
"\n",
|
| 10 |
-
"In this notebook we will provide examples of how to use the NuExtract 2.0 models for inference.\n",
|
| 11 |
-
"\n",
|
| 12 |
-
"First, let's load a model."
|
| 13 |
-
]
|
| 14 |
-
},
|
| 15 |
-
{
|
| 16 |
-
"cell_type": "code",
|
| 17 |
-
"execution_count": null,
|
| 18 |
-
"id": "a561e38e-58e5-4f7e-80ee-eabc2d05e49a",
|
| 19 |
-
"metadata": {
|
| 20 |
-
"tags": []
|
| 21 |
-
},
|
| 22 |
-
"outputs": [
|
| 23 |
-
{
|
| 24 |
-
"name": "stderr",
|
| 25 |
-
"output_type": "stream",
|
| 26 |
-
"text": [
|
| 27 |
-
"s:\\Spaces\\Data-Extraction\\NuMarkApp\\.venv\\Lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
|
| 28 |
-
" from .autonotebook import tqdm as notebook_tqdm\n"
|
| 29 |
-
]
|
| 30 |
-
}
|
| 31 |
-
],
|
| 32 |
-
"source": [
|
| 33 |
-
"import torch\n",
|
| 34 |
-
"from transformers import AutoModelForImageTextToText, AutoProcessor\n",
|
| 35 |
-
"model_name = \"numind/NuExtract-2.0-2B\"\n",
|
| 36 |
-
"# model_name = \"numind/NuExtract-2.0-8B\"\n",
|
| 37 |
-
"\n",
|
| 38 |
-
"processor = AutoProcessor.from_pretrained(model_name,\n",
|
| 39 |
-
" trust_remote_code=True,\n",
|
| 40 |
-
" padding_side='left',\n",
|
| 41 |
-
" use_fast=True)\n",
|
| 42 |
-
"model = AutoModelForImageTextToText.from_pretrained(model_name,\n",
|
| 43 |
-
" trust_remote_code=True,\n",
|
| 44 |
-
" dtype=torch.bfloat16,\n",
|
| 45 |
-
" attn_implementation=\"flash_attention_2\",\n",
|
| 46 |
-
" device_map=\"auto\")\n",
|
| 47 |
-
"\n",
|
| 48 |
-
"# we choose greedy sampling here, which works well for most information extraction tasks\n",
|
| 49 |
-
"generation_config = {\n",
|
| 50 |
-
" \"do_sample\": False,\n",
|
| 51 |
-
" \"num_beams\": 1,\n",
|
| 52 |
-
" \"max_new_tokens\": 2048,\n",
|
| 53 |
-
" \"temperature\": None,\n",
|
| 54 |
-
" \"top_p\": None,\n",
|
| 55 |
-
" \"top_k\": None\n",
|
| 56 |
-
"}"
|
| 57 |
-
]
|
| 58 |
-
},
|
| 59 |
-
{
|
| 60 |
-
"cell_type": "markdown",
|
| 61 |
-
"id": "d739f582-16ad-4e52-b012-429757945a84",
|
| 62 |
-
"metadata": {},
|
| 63 |
-
"source": [
|
| 64 |
-
"## Preparing Model Inputs\n",
|
| 65 |
-
"\n",
|
| 66 |
-
"Before using the model, we also need to make sure our prompts are properly formatted to work with NuExtract. NuExtract expects all input information to come as a single user chat prompt, formatted as follows:\n",
|
| 67 |
-
"\n",
|
| 68 |
-
"```python\n",
|
| 69 |
-
"f\"\"\"\n",
|
| 70 |
-
"# Template:\n",
|
| 71 |
-
"{template}\n",
|
| 72 |
-
"# Context:\n",
|
| 73 |
-
"{text}\n",
|
| 74 |
-
"\"\"\"\n",
|
| 75 |
-
"```\n",
|
| 76 |
-
"and if in-context examples are provided:\n",
|
| 77 |
-
"```python\n",
|
| 78 |
-
"f\"\"\"\n",
|
| 79 |
-
"# Template:\n",
|
| 80 |
-
"{template}\n",
|
| 81 |
-
"# Examples\n",
|
| 82 |
-
"## Input:\n",
|
| 83 |
-
"{input1}\n",
|
| 84 |
-
"## Output:\n",
|
| 85 |
-
"{output1}\n",
|
| 86 |
-
"## Input:\n",
|
| 87 |
-
"{input2}\n",
|
| 88 |
-
"## Output:\n",
|
| 89 |
-
"{output2}\n",
|
| 90 |
-
"# Context:\n",
|
| 91 |
-
"{text}\n",
|
| 92 |
-
"\"\"\"\n",
|
| 93 |
-
"```\n",
|
| 94 |
-
"\n",
|
| 95 |
-
"If you are working with image inputs, you should use image placeholders for `text`, `input1`, etc. Later, we will inject tokens representing the actual image content in the location of these placeholders.\n",
|
| 96 |
-
"\n",
|
| 97 |
-
"The following function can make this formatting more convenient for us (the model can actually do this for us internally, but we will use this function initially to illustrate how things work). "
|
| 98 |
-
]
|
| 99 |
-
},
|
| 100 |
-
{
|
| 101 |
-
"cell_type": "code",
|
| 102 |
-
"execution_count": null,
|
| 103 |
-
"id": "58b017a4-4802-4afe-b2b4-8f034f572c24",
|
| 104 |
-
"metadata": {
|
| 105 |
-
"tags": []
|
| 106 |
-
},
|
| 107 |
-
"outputs": [],
|
| 108 |
-
"source": [
|
| 109 |
-
"def construct_messages(document, template, examples=None, image_placeholder=\"<|vision_start|><|image_pad|><|vision_end|>\"):\n",
|
| 110 |
-
" \"\"\"\n",
|
| 111 |
-
" Construct the individual NuExtract message texts, prior to chat template formatting.\n",
|
| 112 |
-
" \"\"\"\n",
|
| 113 |
-
" images = []\n",
|
| 114 |
-
" # add few-shot examples if needed\n",
|
| 115 |
-
" if examples is not None and len(examples) > 0:\n",
|
| 116 |
-
" icl = \"# Examples:\\n\"\n",
|
| 117 |
-
" for row in examples:\n",
|
| 118 |
-
" example_input = row['input']\n",
|
| 119 |
-
"\n",
|
| 120 |
-
" if not isinstance(row['input'], str):\n",
|
| 121 |
-
" example_input = image_placeholder\n",
|
| 122 |
-
" images.append(row['input'])\n",
|
| 123 |
-
"\n",
|
| 124 |
-
" icl += f\"## Input:\\n{example_input}\\n## Output:\\n{row['output']}\\n\"\n",
|
| 125 |
-
" else:\n",
|
| 126 |
-
" icl = \"\"\n",
|
| 127 |
-
"\n",
|
| 128 |
-
" # if input document is an image, set text to an image placeholder\n",
|
| 129 |
-
" text = document\n",
|
| 130 |
-
" if not isinstance(document, str):\n",
|
| 131 |
-
" text = image_placeholder\n",
|
| 132 |
-
" images.append(document)\n",
|
| 133 |
-
" text = f\"\"\"# Template:\\n{template}\\n{icl}# Context:\\n{text}\"\"\"\n",
|
| 134 |
-
"\n",
|
| 135 |
-
" messages = [\n",
|
| 136 |
-
" {\n",
|
| 137 |
-
" \"role\": \"system\",\n",
|
| 138 |
-
" \"content\": \"You are NuExtract, an information extraction tool created by NuMind.\"\n",
|
| 139 |
-
" },\n",
|
| 140 |
-
" {\n",
|
| 141 |
-
" \"role\": \"user\",\n",
|
| 142 |
-
" \"content\": [{\"type\": \"text\", \"text\": text}] + images,\n",
|
| 143 |
-
" }\n",
|
| 144 |
-
" ]\n",
|
| 145 |
-
" return messages"
|
| 146 |
-
]
|
| 147 |
-
},
|
| 148 |
-
{
|
| 149 |
-
"cell_type": "markdown",
|
| 150 |
-
"id": "e918d64e-bc31-4d68-8ef5-2d485b5e5745",
|
| 151 |
-
"metadata": {},
|
| 152 |
-
"source": [
|
| 153 |
-
"## Inference\n",
|
| 154 |
-
"### Basic Example\n",
|
| 155 |
-
"\n",
|
| 156 |
-
"Now we are ready to run the model!\n",
|
| 157 |
-
"\n",
|
| 158 |
-
"Let's start with a basic text-only example, where we want to extract peoples' names from a short text."
|
| 159 |
-
]
|
| 160 |
-
},
|
| 161 |
-
{
|
| 162 |
-
"cell_type": "code",
|
| 163 |
-
"execution_count": null,
|
| 164 |
-
"id": "b1cad1ec-4753-4064-aca0-c89fec4b5bfd",
|
| 165 |
-
"metadata": {
|
| 166 |
-
"tags": []
|
| 167 |
-
},
|
| 168 |
-
"outputs": [],
|
| 169 |
-
"source": [
|
| 170 |
-
"from qwen_vl_utils import process_vision_info\n",
|
| 171 |
-
"\n",
|
| 172 |
-
"template = \"\"\"{\"names\": [\"verbatim-string\"]}\"\"\"\n",
|
| 173 |
-
"document = \"John went to the restaurant with Mary. James went to the cinema.\"\n",
|
| 174 |
-
"\n",
|
| 175 |
-
"# prepare the user message content\n",
|
| 176 |
-
"messages = construct_messages(document, template)\n",
|
| 177 |
-
"text = processor.apply_chat_template(\n",
|
| 178 |
-
" messages, tokenize=False, add_generation_prompt=True,\n",
|
| 179 |
-
")\n",
|
| 180 |
-
"\n",
|
| 181 |
-
"image_inputs = process_vision_info(messages)[0]\n",
|
| 182 |
-
"inputs = processor(\n",
|
| 183 |
-
" text=[text],\n",
|
| 184 |
-
" images=image_inputs,\n",
|
| 185 |
-
" padding=True,\n",
|
| 186 |
-
" return_tensors=\"pt\",\n",
|
| 187 |
-
").to(\"cuda\")"
|
| 188 |
-
]
|
| 189 |
-
},
|
| 190 |
-
{
|
| 191 |
-
"cell_type": "markdown",
|
| 192 |
-
"id": "6c0e4f92-94dd-4b37-8772-591246f9b71a",
|
| 193 |
-
"metadata": {},
|
| 194 |
-
"source": [
|
| 195 |
-
"Our NuExtract message is now formatted in standard chat template formatting; the tokenized version (`inputs`) will be given directly to the model."
|
| 196 |
-
]
|
| 197 |
-
},
|
| 198 |
-
{
|
| 199 |
-
"cell_type": "code",
|
| 200 |
-
"execution_count": null,
|
| 201 |
-
"id": "42bd5add-b48a-44e4-9843-2f187e768678",
|
| 202 |
-
"metadata": {
|
| 203 |
-
"tags": []
|
| 204 |
-
},
|
| 205 |
-
"outputs": [
|
| 206 |
-
{
|
| 207 |
-
"name": "stdout",
|
| 208 |
-
"output_type": "stream",
|
| 209 |
-
"text": [
|
| 210 |
-
"<|im_start|>system\n",
|
| 211 |
-
"You are NuExtract, an information extraction tool created by NuMind.<|im_end|>\n",
|
| 212 |
-
"<|im_start|>user\n",
|
| 213 |
-
"# Template:\n",
|
| 214 |
-
"{\"names\": [\"verbatim-string\"]}\n",
|
| 215 |
-
"# Context:\n",
|
| 216 |
-
"John went to the restaurant with Mary. James went to the cinema.<|im_end|>\n",
|
| 217 |
-
"<|im_start|>assistant\n",
|
| 218 |
-
"\n"
|
| 219 |
-
]
|
| 220 |
-
}
|
| 221 |
-
],
|
| 222 |
-
"source": [
|
| 223 |
-
"print(text)"
|
| 224 |
-
]
|
| 225 |
-
},
|
| 226 |
-
{
|
| 227 |
-
"cell_type": "markdown",
|
| 228 |
-
"id": "df0979a7-ea46-4d25-a838-baeaeeca6ee5",
|
| 229 |
-
"metadata": {},
|
| 230 |
-
"source": [
|
| 231 |
-
"The other `image_inputs` are empty in this case because this is a text-only example."
|
| 232 |
-
]
|
| 233 |
-
},
|
| 234 |
-
{
|
| 235 |
-
"cell_type": "code",
|
| 236 |
-
"execution_count": null,
|
| 237 |
-
"id": "e3493013-313f-4318-a813-d492732bb0c1",
|
| 238 |
-
"metadata": {
|
| 239 |
-
"tags": []
|
| 240 |
-
},
|
| 241 |
-
"outputs": [
|
| 242 |
-
{
|
| 243 |
-
"name": "stdout",
|
| 244 |
-
"output_type": "stream",
|
| 245 |
-
"text": [
|
| 246 |
-
"None\n"
|
| 247 |
-
]
|
| 248 |
-
}
|
| 249 |
-
],
|
| 250 |
-
"source": [
|
| 251 |
-
"print(image_inputs)"
|
| 252 |
-
]
|
| 253 |
-
},
|
| 254 |
-
{
|
| 255 |
-
"cell_type": "markdown",
|
| 256 |
-
"id": "0e300775-48a9-42fb-9c92-3e1ceb530101",
|
| 257 |
-
"metadata": {},
|
| 258 |
-
"source": [
|
| 259 |
-
"Now let's actually run the model."
|
| 260 |
-
]
|
| 261 |
-
},
|
| 262 |
-
{
|
| 263 |
-
"cell_type": "code",
|
| 264 |
-
"execution_count": null,
|
| 265 |
-
"id": "ed374bf3-1af1-4a3e-a9eb-9e2e7ca113b8",
|
| 266 |
-
"metadata": {
|
| 267 |
-
"tags": []
|
| 268 |
-
},
|
| 269 |
-
"outputs": [
|
| 270 |
-
{
|
| 271 |
-
"name": "stdout",
|
| 272 |
-
"output_type": "stream",
|
| 273 |
-
"text": [
|
| 274 |
-
"['{\"names\": [\"John\", \"Mary\", \"James\"]}']\n"
|
| 275 |
-
]
|
| 276 |
-
}
|
| 277 |
-
],
|
| 278 |
-
"source": [
|
| 279 |
-
"# Inference: Generation of the output\n",
|
| 280 |
-
"generated_ids = model.generate(\n",
|
| 281 |
-
" **inputs,\n",
|
| 282 |
-
" **generation_config\n",
|
| 283 |
-
")\n",
|
| 284 |
-
"generated_ids_trimmed = [\n",
|
| 285 |
-
" out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)\n",
|
| 286 |
-
"]\n",
|
| 287 |
-
"output_text = processor.batch_decode(\n",
|
| 288 |
-
" generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False\n",
|
| 289 |
-
")\n",
|
| 290 |
-
"print(output_text)"
|
| 291 |
-
]
|
| 292 |
-
},
|
| 293 |
-
{
|
| 294 |
-
"cell_type": "markdown",
|
| 295 |
-
"id": "21d37d33-85ad-421c-a014-523ddbec5a2f",
|
| 296 |
-
"metadata": {},
|
| 297 |
-
"source": [
|
| 298 |
-
"Alternatively, you can directly provide the template and in-context examples to `.apply_chat_template()`, rather then manually preparing the prompt via `construct_messages()`. This is a bit more convenient and so we will use this from now on."
|
| 299 |
-
]
|
| 300 |
-
},
|
| 301 |
-
{
|
| 302 |
-
"cell_type": "code",
|
| 303 |
-
"execution_count": null,
|
| 304 |
-
"id": "27fecb60-b0eb-46bc-ba2d-ef6a32d04f0c",
|
| 305 |
-
"metadata": {
|
| 306 |
-
"tags": []
|
| 307 |
-
},
|
| 308 |
-
"outputs": [
|
| 309 |
-
{
|
| 310 |
-
"name": "stdout",
|
| 311 |
-
"output_type": "stream",
|
| 312 |
-
"text": [
|
| 313 |
-
"<|im_start|>system\n",
|
| 314 |
-
"You are NuExtract, an information extraction tool created by NuMind.<|im_end|>\n",
|
| 315 |
-
"<|im_start|>user\n",
|
| 316 |
-
"# Template:\n",
|
| 317 |
-
"{\"names\": [\"verbatim-string\"]}\n",
|
| 318 |
-
"# Context:\n",
|
| 319 |
-
"John went to the restaurant with Mary. James went to the cinema.<|im_end|>\n",
|
| 320 |
-
"<|im_start|>assistant\n",
|
| 321 |
-
"\n",
|
| 322 |
-
"['{\"names\": [\"John\", \"Mary\", \"James\"]}']\n"
|
| 323 |
-
]
|
| 324 |
-
}
|
| 325 |
-
],
|
| 326 |
-
"source": [
|
| 327 |
-
"template = \"\"\"{\"names\": [\"verbatim-string\"]}\"\"\"\n",
|
| 328 |
-
"document = \"John went to the restaurant with Mary. James went to the cinema.\"\n",
|
| 329 |
-
"\n",
|
| 330 |
-
"# prepare the user message content\n",
|
| 331 |
-
"messages = [{\"role\": \"user\", \"content\": document}]\n",
|
| 332 |
-
"text = processor.tokenizer.apply_chat_template(\n",
|
| 333 |
-
" messages,\n",
|
| 334 |
-
" template=template, # template is specified here\n",
|
| 335 |
-
" tokenize=False,\n",
|
| 336 |
-
" add_generation_prompt=True,\n",
|
| 337 |
-
")\n",
|
| 338 |
-
"\n",
|
| 339 |
-
"print(text)\n",
|
| 340 |
-
"\n",
|
| 341 |
-
"image_inputs = process_vision_info(messages)[0]\n",
|
| 342 |
-
"inputs = processor(\n",
|
| 343 |
-
" text=[text],\n",
|
| 344 |
-
" images=image_inputs,\n",
|
| 345 |
-
" padding=True,\n",
|
| 346 |
-
" return_tensors=\"pt\",\n",
|
| 347 |
-
").to(\"cuda\")\n",
|
| 348 |
-
"\n",
|
| 349 |
-
"# Inference: Generation of the output\n",
|
| 350 |
-
"generated_ids = model.generate(\n",
|
| 351 |
-
" **inputs,\n",
|
| 352 |
-
" **generation_config\n",
|
| 353 |
-
")\n",
|
| 354 |
-
"generated_ids_trimmed = [\n",
|
| 355 |
-
" out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)\n",
|
| 356 |
-
"]\n",
|
| 357 |
-
"output_text = processor.batch_decode(\n",
|
| 358 |
-
" generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False\n",
|
| 359 |
-
")\n",
|
| 360 |
-
"\n",
|
| 361 |
-
"print(output_text)"
|
| 362 |
-
]
|
| 363 |
-
},
|
| 364 |
-
{
|
| 365 |
-
"cell_type": "markdown",
|
| 366 |
-
"id": "c18002f2-447a-45c2-90f6-0ff5a63c5dc2",
|
| 367 |
-
"metadata": {},
|
| 368 |
-
"source": [
|
| 369 |
-
"### In-Context Examples\n",
|
| 370 |
-
"\n",
|
| 371 |
-
"Sometimes the model might not perform as well as we want because our task is challenging or involves some degree of ambiguity. Alternatively, we may want the model to follow some specific formatting, or just give it a bit more help. In cases like this it can be valuable to provide \"in-context examples\" to help NuExtract better understand the task.\n",
|
| 372 |
-
"\n",
|
| 373 |
-
"To do so, we can provide a list `examples` to `apply_chat_template()` (or `construct_messages()`) which contains dictionaries of input/output pairs. In the example below, we show to the model that we want the extracted names to be in captial letters with `-` on either side (for the sake of illustration)."
|
| 374 |
-
]
|
| 375 |
-
},
|
| 376 |
-
{
|
| 377 |
-
"cell_type": "code",
|
| 378 |
-
"execution_count": null,
|
| 379 |
-
"id": "146baa04-2206-4eca-86bd-66f94924e293",
|
| 380 |
-
"metadata": {
|
| 381 |
-
"tags": []
|
| 382 |
-
},
|
| 383 |
-
"outputs": [],
|
| 384 |
-
"source": [
|
| 385 |
-
"template = \"\"\"{\"names\": [\"verbatim-string\"]}\"\"\"\n",
|
| 386 |
-
"document = \"John went to the restaurant with Mary. James went to the cinema.\"\n",
|
| 387 |
-
"examples = [\n",
|
| 388 |
-
" {\n",
|
| 389 |
-
" \"input\": \"Stephen is the manager at Susan's store.\",\n",
|
| 390 |
-
" \"output\": \"\"\"{\"names\": [\"-STEPHEN-\", \"-SUSAN-\"]}\"\"\"\n",
|
| 391 |
-
" }\n",
|
| 392 |
-
"]\n",
|
| 393 |
-
"\n",
|
| 394 |
-
"messages = [{\"role\": \"user\", \"content\": document}]\n",
|
| 395 |
-
"text = processor.tokenizer.apply_chat_template(\n",
|
| 396 |
-
" messages,\n",
|
| 397 |
-
" template=template,\n",
|
| 398 |
-
" examples=examples, # examples provided here\n",
|
| 399 |
-
" tokenize=False,\n",
|
| 400 |
-
" add_generation_prompt=True,\n",
|
| 401 |
-
")\n",
|
| 402 |
-
"\n",
|
| 403 |
-
"image_inputs = process_vision_info(messages)[0]\n",
|
| 404 |
-
"inputs = processor(\n",
|
| 405 |
-
" text=[text],\n",
|
| 406 |
-
" images=image_inputs,\n",
|
| 407 |
-
" padding=True,\n",
|
| 408 |
-
" return_tensors=\"pt\",\n",
|
| 409 |
-
").to(\"cuda\")"
|
| 410 |
-
]
|
| 411 |
-
},
|
| 412 |
-
{
|
| 413 |
-
"cell_type": "markdown",
|
| 414 |
-
"id": "1d2561cc-a662-4898-b226-41d12394fdb4",
|
| 415 |
-
"metadata": {},
|
| 416 |
-
"source": [
|
| 417 |
-
"We can see below that the in-context example has now been included in the model prompt, specifically between the template and context components."
|
| 418 |
-
]
|
| 419 |
-
},
|
| 420 |
-
{
|
| 421 |
-
"cell_type": "code",
|
| 422 |
-
"execution_count": null,
|
| 423 |
-
"id": "79b10e44-5bb4-40eb-af74-e013897948ea",
|
| 424 |
-
"metadata": {
|
| 425 |
-
"tags": []
|
| 426 |
-
},
|
| 427 |
-
"outputs": [
|
| 428 |
-
{
|
| 429 |
-
"name": "stdout",
|
| 430 |
-
"output_type": "stream",
|
| 431 |
-
"text": [
|
| 432 |
-
"<|im_start|>system\n",
|
| 433 |
-
"You are NuExtract, an information extraction tool created by NuMind.<|im_end|>\n",
|
| 434 |
-
"<|im_start|>user\n",
|
| 435 |
-
"# Template:\n",
|
| 436 |
-
"{\"names\": [\"verbatim-string\"]}\n",
|
| 437 |
-
"# Examples:\n",
|
| 438 |
-
"## Input:\n",
|
| 439 |
-
"Stephen is the manager at Susan's store.\n",
|
| 440 |
-
"## Output:\n",
|
| 441 |
-
"{\"names\": [\"-STEPHEN-\", \"-SUSAN-\"]}\n",
|
| 442 |
-
"# Context:\n",
|
| 443 |
-
"John went to the restaurant with Mary. James went to the cinema.<|im_end|>\n",
|
| 444 |
-
"<|im_start|>assistant\n",
|
| 445 |
-
"\n"
|
| 446 |
-
]
|
| 447 |
-
}
|
| 448 |
-
],
|
| 449 |
-
"source": [
|
| 450 |
-
"print(text)"
|
| 451 |
-
]
|
| 452 |
-
},
|
| 453 |
-
{
|
| 454 |
-
"cell_type": "code",
|
| 455 |
-
"execution_count": null,
|
| 456 |
-
"id": "3f1364bf-2588-4935-a478-7a85b87866fc",
|
| 457 |
-
"metadata": {
|
| 458 |
-
"tags": []
|
| 459 |
-
},
|
| 460 |
-
"outputs": [
|
| 461 |
-
{
|
| 462 |
-
"name": "stdout",
|
| 463 |
-
"output_type": "stream",
|
| 464 |
-
"text": [
|
| 465 |
-
"['{\"names\": [\"-JOHN-\", \"-MARY-\", \"-JAMES-\"]}']\n"
|
| 466 |
-
]
|
| 467 |
-
}
|
| 468 |
-
],
|
| 469 |
-
"source": [
|
| 470 |
-
"# Inference: Generation of the output\n",
|
| 471 |
-
"generated_ids = model.generate(\n",
|
| 472 |
-
" **inputs,\n",
|
| 473 |
-
" **generation_config\n",
|
| 474 |
-
")\n",
|
| 475 |
-
"generated_ids_trimmed = [\n",
|
| 476 |
-
" out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)\n",
|
| 477 |
-
"]\n",
|
| 478 |
-
"output_text = processor.batch_decode(\n",
|
| 479 |
-
" generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False\n",
|
| 480 |
-
")\n",
|
| 481 |
-
"print(output_text)"
|
| 482 |
-
]
|
| 483 |
-
},
|
| 484 |
-
{
|
| 485 |
-
"cell_type": "markdown",
|
| 486 |
-
"id": "bc0c3ecf-80ee-40b7-b240-12b5a9e10042",
|
| 487 |
-
"metadata": {},
|
| 488 |
-
"source": [
|
| 489 |
-
"To get even better performance, add multiple in-context examples to your input."
|
| 490 |
-
]
|
| 491 |
-
},
|
| 492 |
-
{
|
| 493 |
-
"cell_type": "markdown",
|
| 494 |
-
"id": "99faf117-18b7-4b5e-8cd0-b6f8941db581",
|
| 495 |
-
"metadata": {
|
| 496 |
-
"tags": []
|
| 497 |
-
},
|
| 498 |
-
"source": [
|
| 499 |
-
"### Image Inputs\n",
|
| 500 |
-
"\n",
|
| 501 |
-
"If we want to give image inputs to NuExtract, instead of text, we simply provide a dictionary specifying the desired image file as the message content, instead of a string. E.g. `{\"type\": \"image\", \"image\": \"file://image.jpg\"}`.\n",
|
| 502 |
-
"\n",
|
| 503 |
-
"You can also specify an image URL (e.g. `{\"type\": \"image\", \"image\": \"http://path/to/your/image.jpg\"}`) or base64 encoding (e.g. `{\"type\": \"image\", \"image\": \"data:image;base64,/9j/...\"}`).\n",
|
| 504 |
-
"\n",
|
| 505 |
-
"First, we will need a modified version of `process_vision_info()` that handles image-based in-context examples as well as primary inputs."
|
| 506 |
-
]
|
| 507 |
-
},
|
| 508 |
-
{
|
| 509 |
-
"cell_type": "code",
|
| 510 |
-
"execution_count": null,
|
| 511 |
-
"id": "1aaf1912-4f50-483f-a9a0-f5f57caf66e0",
|
| 512 |
-
"metadata": {
|
| 513 |
-
"tags": []
|
| 514 |
-
},
|
| 515 |
-
"outputs": [],
|
| 516 |
-
"source": [
|
| 517 |
-
"def process_all_vision_info(messages, examples=None):\n",
|
| 518 |
-
" \"\"\"\n",
|
| 519 |
-
" Process vision information from both messages and in-context examples, supporting batch processing.\n",
|
| 520 |
-
"\n",
|
| 521 |
-
" Args:\n",
|
| 522 |
-
" messages: List of message dictionaries (single input) OR list of message lists (batch input)\n",
|
| 523 |
-
" examples: Optional list of example dictionaries (single input) OR list of example lists (batch)\n",
|
| 524 |
-
"\n",
|
| 525 |
-
" Returns:\n",
|
| 526 |
-
" A flat list of all images in the correct order:\n",
|
| 527 |
-
" - For single input: example images followed by message images\n",
|
| 528 |
-
" - For batch input: interleaved as (item1 examples, item1 input, item2 examples, item2 input, etc.)\n",
|
| 529 |
-
" - Returns None if no images were found\n",
|
| 530 |
-
" \"\"\"\n",
|
| 531 |
-
" from qwen_vl_utils import process_vision_info, fetch_image\n",
|
| 532 |
-
"\n",
|
| 533 |
-
" # Helper function to extract images from examples\n",
|
| 534 |
-
" def extract_example_images(example_item):\n",
|
| 535 |
-
" if not example_item:\n",
|
| 536 |
-
" return []\n",
|
| 537 |
-
"\n",
|
| 538 |
-
" # Handle both list of examples and single example\n",
|
| 539 |
-
" examples_to_process = example_item if isinstance(example_item, list) else [example_item]\n",
|
| 540 |
-
" images = []\n",
|
| 541 |
-
"\n",
|
| 542 |
-
" for example in examples_to_process:\n",
|
| 543 |
-
" if isinstance(example.get('input'), dict) and example['input'].get('type') == 'image':\n",
|
| 544 |
-
" images.append(fetch_image(example['input']))\n",
|
| 545 |
-
"\n",
|
| 546 |
-
" return images\n",
|
| 547 |
-
"\n",
|
| 548 |
-
" # Normalize inputs to always be batched format\n",
|
| 549 |
-
" is_batch = messages and isinstance(messages[0], list)\n",
|
| 550 |
-
" messages_batch = messages if is_batch else [messages]\n",
|
| 551 |
-
" is_batch_examples = examples and isinstance(examples, list) and (isinstance(examples[0], list) or examples[0] is None)\n",
|
| 552 |
-
" examples_batch = examples if is_batch_examples else ([examples] if examples is not None else None)\n",
|
| 553 |
-
"\n",
|
| 554 |
-
" # Ensure examples batch matches messages batch if provided\n",
|
| 555 |
-
" if examples and len(examples_batch) != len(messages_batch):\n",
|
| 556 |
-
" if not is_batch and len(examples_batch) == 1:\n",
|
| 557 |
-
" # Single example set for a single input is fine\n",
|
| 558 |
-
" pass\n",
|
| 559 |
-
" else:\n",
|
| 560 |
-
" raise ValueError(\"Examples batch length must match messages batch length\")\n",
|
| 561 |
-
"\n",
|
| 562 |
-
" # Process all inputs, maintaining correct order\n",
|
| 563 |
-
" all_images = []\n",
|
| 564 |
-
" for i, message_group in enumerate(messages_batch):\n",
|
| 565 |
-
" # Get example images for this input\n",
|
| 566 |
-
" if examples and i < len(examples_batch):\n",
|
| 567 |
-
" input_example_images = extract_example_images(examples_batch[i])\n",
|
| 568 |
-
" all_images.extend(input_example_images)\n",
|
| 569 |
-
"\n",
|
| 570 |
-
" # Get message images for this input\n",
|
| 571 |
-
" input_message_images = process_vision_info(message_group)[0] or []\n",
|
| 572 |
-
" all_images.extend(input_message_images)\n",
|
| 573 |
-
"\n",
|
| 574 |
-
" return all_images if all_images else None\n"
|
| 575 |
-
]
|
| 576 |
-
},
|
| 577 |
-
{
|
| 578 |
-
"cell_type": "markdown",
|
| 579 |
-
"id": "8db9904c-451d-4ff4-b776-5a557abc1437",
|
| 580 |
-
"metadata": {},
|
| 581 |
-
"source": [
|
| 582 |
-
"In the example below, we give an image of a receipt from Trader Joe's (`data/1.jpg`) and ask the model to extract the name of the store. We also provide an ICL example of a receipt from Walmart (`data/0.jpg`)."
|
| 583 |
-
]
|
| 584 |
-
},
|
| 585 |
-
{
|
| 586 |
-
"cell_type": "code",
|
| 587 |
-
"execution_count": null,
|
| 588 |
-
"id": "fd11e855-594b-4fd7-af01-7695a65b75a9",
|
| 589 |
-
"metadata": {
|
| 590 |
-
"tags": []
|
| 591 |
-
},
|
| 592 |
-
"outputs": [],
|
| 593 |
-
"source": [
|
| 594 |
-
"from pathlib import Path\n",
|
| 595 |
-
"\n",
|
| 596 |
-
"project_root = Path.cwd().parent if Path.cwd().name == \"notebooks\" else Path.cwd()\n",
|
| 597 |
-
"samples_dir = project_root / \"data\" / \"samples\"\n",
|
| 598 |
-
"sample_images = sorted(\n",
|
| 599 |
-
" path for path in samples_dir.iterdir()\n",
|
| 600 |
-
" if path.suffix.lower() in {\".jpg\", \".jpeg\", \".png\", \".webp\"}\n",
|
| 601 |
-
")\n",
|
| 602 |
-
"if not sample_images:\n",
|
| 603 |
-
" raise FileNotFoundError(f\"No supported images found in {samples_dir}\")\n",
|
| 604 |
-
"\n",
|
| 605 |
-
"# Pick a different entry from sample_images to use another file.\n",
|
| 606 |
-
"selected_image = sample_images[0]\n",
|
| 607 |
-
"input_image_uri = selected_image.resolve().as_uri()\n",
|
| 608 |
-
"print(f\"Using input image: {selected_image}\")\n",
|
| 609 |
-
"\n",
|
| 610 |
-
"template = \"\"\"{\"store\": \"verbatim-string\"}\"\"\"\n",
|
| 611 |
-
"document = {\"type\": \"image\", \"image\": input_image_uri}\n",
|
| 612 |
-
"examples = [\n",
|
| 613 |
-
" {\n",
|
| 614 |
-
" \"input\": {\"type\": \"image\", \"image\": input_image_uri},\n",
|
| 615 |
-
" \"output\": \"\"\"{\"store\": \"WALMART\"}\"\"\"\n",
|
| 616 |
-
" }\n",
|
| 617 |
-
"]\n",
|
| 618 |
-
"\n",
|
| 619 |
-
"messages = [{\"role\": \"user\", \"content\": [document]}]\n",
|
| 620 |
-
"text = processor.tokenizer.apply_chat_template(\n",
|
| 621 |
-
" messages,\n",
|
| 622 |
-
" template=template,\n",
|
| 623 |
-
" examples=examples,\n",
|
| 624 |
-
" tokenize=False,\n",
|
| 625 |
-
" add_generation_prompt=True,\n",
|
| 626 |
-
")\n",
|
| 627 |
-
"\n",
|
| 628 |
-
"image_inputs = process_all_vision_info(messages, examples)\n",
|
| 629 |
-
"inputs = processor(\n",
|
| 630 |
-
" text=[text],\n",
|
| 631 |
-
" images=image_inputs,\n",
|
| 632 |
-
" padding=True,\n",
|
| 633 |
-
" return_tensors=\"pt\",\n",
|
| 634 |
-
").to(\"cuda\")"
|
| 635 |
-
]
|
| 636 |
-
},
|
| 637 |
-
{
|
| 638 |
-
"cell_type": "markdown",
|
| 639 |
-
"id": "f14a8e8a-a091-410a-a964-9d275b936e75",
|
| 640 |
-
"metadata": {},
|
| 641 |
-
"source": [
|
| 642 |
-
"Just like in the text-only case above, our in-context example has been included in the prompt before the main context."
|
| 643 |
-
]
|
| 644 |
-
},
|
| 645 |
-
{
|
| 646 |
-
"cell_type": "code",
|
| 647 |
-
"execution_count": null,
|
| 648 |
-
"id": "ba00c8d6-56e2-43fc-a646-61f693c6b51a",
|
| 649 |
-
"metadata": {
|
| 650 |
-
"tags": []
|
| 651 |
-
},
|
| 652 |
-
"outputs": [
|
| 653 |
-
{
|
| 654 |
-
"name": "stdout",
|
| 655 |
-
"output_type": "stream",
|
| 656 |
-
"text": [
|
| 657 |
-
"<|im_start|>system\n",
|
| 658 |
-
"You are NuExtract, an information extraction tool created by NuMind.<|im_end|>\n",
|
| 659 |
-
"<|im_start|>user\n",
|
| 660 |
-
"# Template:\n",
|
| 661 |
-
"{\"store\": \"verbatim-string\"}\n",
|
| 662 |
-
"# Examples:\n",
|
| 663 |
-
"## Input:\n",
|
| 664 |
-
"<|vision_start|><|image_pad|><|vision_end|>\n",
|
| 665 |
-
"## Output:\n",
|
| 666 |
-
"{\"store\": \"WALMART\"}\n",
|
| 667 |
-
"# Context:\n",
|
| 668 |
-
"<|vision_start|><|image_pad|><|vision_end|><|im_end|>\n",
|
| 669 |
-
"<|im_start|>assistant\n",
|
| 670 |
-
"\n"
|
| 671 |
-
]
|
| 672 |
-
}
|
| 673 |
-
],
|
| 674 |
-
"source": [
|
| 675 |
-
"print(text)"
|
| 676 |
-
]
|
| 677 |
-
},
|
| 678 |
-
{
|
| 679 |
-
"cell_type": "markdown",
|
| 680 |
-
"id": "4820e6da-544b-431a-86fb-a6f9f0eb6bdd",
|
| 681 |
-
"metadata": {},
|
| 682 |
-
"source": [
|
| 683 |
-
"Now if we look at `image_inputs` we will see that it contains actual images. When we pass this along with `text` to `processor()` it automatically encodes the images and injects a tokenized representation into the image placeholders within `text`."
|
| 684 |
-
]
|
| 685 |
-
},
|
| 686 |
-
{
|
| 687 |
-
"cell_type": "code",
|
| 688 |
-
"execution_count": null,
|
| 689 |
-
"id": "1aa9fba6-eb58-4401-abc2-79b7fcd205c0",
|
| 690 |
-
"metadata": {
|
| 691 |
-
"tags": []
|
| 692 |
-
},
|
| 693 |
-
"outputs": [
|
| 694 |
-
{
|
| 695 |
-
"name": "stdout",
|
| 696 |
-
"output_type": "stream",
|
| 697 |
-
"text": [
|
| 698 |
-
"[<PIL.Image.Image image mode=RGB size=588x896 at 0x7FDD295E5600>, <PIL.Image.Image image mode=RGB size=476x980 at 0x7FDD295E4460>]\n"
|
| 699 |
-
]
|
| 700 |
-
}
|
| 701 |
-
],
|
| 702 |
-
"source": [
|
| 703 |
-
"print(image_inputs)"
|
| 704 |
-
]
|
| 705 |
-
},
|
| 706 |
-
{
|
| 707 |
-
"cell_type": "code",
|
| 708 |
-
"execution_count": null,
|
| 709 |
-
"id": "fbed800c-519b-4ee1-90d9-7c27e383d904",
|
| 710 |
-
"metadata": {
|
| 711 |
-
"tags": []
|
| 712 |
-
},
|
| 713 |
-
"outputs": [
|
| 714 |
-
{
|
| 715 |
-
"name": "stdout",
|
| 716 |
-
"output_type": "stream",
|
| 717 |
-
"text": [
|
| 718 |
-
"['{\"store\": \"TRADER JOE\\'S\"}']\n"
|
| 719 |
-
]
|
| 720 |
-
}
|
| 721 |
-
],
|
| 722 |
-
"source": [
|
| 723 |
-
"# Inference: Generation of the output\n",
|
| 724 |
-
"generated_ids = model.generate(\n",
|
| 725 |
-
" **inputs,\n",
|
| 726 |
-
" **generation_config\n",
|
| 727 |
-
")\n",
|
| 728 |
-
"generated_ids_trimmed = [\n",
|
| 729 |
-
" out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)\n",
|
| 730 |
-
"]\n",
|
| 731 |
-
"output_text = processor.batch_decode(\n",
|
| 732 |
-
" generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False\n",
|
| 733 |
-
")\n",
|
| 734 |
-
"print(output_text)"
|
| 735 |
-
]
|
| 736 |
-
},
|
| 737 |
-
{
|
| 738 |
-
"cell_type": "markdown",
|
| 739 |
-
"id": "c6beb77f-2de5-4846-b244-2cda27f5f553",
|
| 740 |
-
"metadata": {
|
| 741 |
-
"tags": []
|
| 742 |
-
},
|
| 743 |
-
"source": [
|
| 744 |
-
"### Batched Inference\n",
|
| 745 |
-
"\n",
|
| 746 |
-
"Finally, we can run batched inference over a list of input examples, regardless of whether they contain text, images, and/or ICL examples."
|
| 747 |
-
]
|
| 748 |
-
},
|
| 749 |
-
{
|
| 750 |
-
"cell_type": "code",
|
| 751 |
-
"execution_count": null,
|
| 752 |
-
"id": "947b8090-236c-4821-b295-5a8fb5247943",
|
| 753 |
-
"metadata": {
|
| 754 |
-
"tags": []
|
| 755 |
-
},
|
| 756 |
-
"outputs": [
|
| 757 |
-
{
|
| 758 |
-
"name": "stdout",
|
| 759 |
-
"output_type": "stream",
|
| 760 |
-
"text": [
|
| 761 |
-
"{\"store_name\": \"WAL-MART\"}\n",
|
| 762 |
-
"{\"store_name\": \"Walmart\"}\n",
|
| 763 |
-
"{\"names\": [\"John\", \"Mary\", \"James\"]}\n",
|
| 764 |
-
"{\"names\": [\"-JOHN-\", \"-MARY-\", \"-JAMES-\"]}\n"
|
| 765 |
-
]
|
| 766 |
-
}
|
| 767 |
-
],
|
| 768 |
-
"source": [
|
| 769 |
-
"inputs = [\n",
|
| 770 |
-
" # image input with no ICL examples\n",
|
| 771 |
-
" {\n",
|
| 772 |
-
" \"document\": {\"type\": \"image\", \"image\": input_image_uri},\n",
|
| 773 |
-
" \"template\": \"\"\"{\"store_name\": \"verbatim-string\"}\"\"\",\n",
|
| 774 |
-
" },\n",
|
| 775 |
-
" # image input with 1 ICL example\n",
|
| 776 |
-
" {\n",
|
| 777 |
-
" \"document\": {\"type\": \"image\", \"image\": input_image_uri},\n",
|
| 778 |
-
" \"template\": \"\"\"{\"store_name\": \"verbatim-string\"}\"\"\",\n",
|
| 779 |
-
" \"examples\": [\n",
|
| 780 |
-
" {\n",
|
| 781 |
-
" \"input\": {\"type\": \"image\", \"image\": input_image_uri},\n",
|
| 782 |
-
" \"output\": \"\"\"{\"store_name\": \"Trader Joe's\"}\"\"\",\n",
|
| 783 |
-
" }\n",
|
| 784 |
-
" ],\n",
|
| 785 |
-
" },\n",
|
| 786 |
-
" # text input with no ICL examples\n",
|
| 787 |
-
" {\n",
|
| 788 |
-
" \"document\": {\"type\": \"text\", \"text\": \"John went to the restaurant with Mary. James went to the cinema.\"},\n",
|
| 789 |
-
" \"template\": \"\"\"{\"names\": [\"verbatim-string\"]}\"\"\",\n",
|
| 790 |
-
" },\n",
|
| 791 |
-
" # text input with ICL example\n",
|
| 792 |
-
" {\n",
|
| 793 |
-
" \"document\": {\"type\": \"text\", \"text\": \"John went to the restaurant with Mary. James went to the cinema.\"},\n",
|
| 794 |
-
" \"template\": \"\"\"{\"names\": [\"verbatim-string\"]}\"\"\",\n",
|
| 795 |
-
" \"examples\": [\n",
|
| 796 |
-
" {\n",
|
| 797 |
-
" \"input\": \"Stephen is the manager at Susan's store.\",\n",
|
| 798 |
-
" \"output\": \"\"\"{\"names\": [\"-STEPHEN-\", \"-SUSAN-\"]}\"\"\"\n",
|
| 799 |
-
" }\n",
|
| 800 |
-
" ],\n",
|
| 801 |
-
" },\n",
|
| 802 |
-
"]\n",
|
| 803 |
-
"\n",
|
| 804 |
-
"# messages should be a list of lists for batch processing\n",
|
| 805 |
-
"messages = [[{\"role\": \"user\", \"content\": [x['document']]}] for x in inputs]\n",
|
| 806 |
-
"\n",
|
| 807 |
-
"# apply chat template to each example individually\n",
|
| 808 |
-
"texts = [\n",
|
| 809 |
-
" processor.tokenizer.apply_chat_template(\n",
|
| 810 |
-
" messages[i], # Now this is a list containing one message\n",
|
| 811 |
-
" template=x['template'],\n",
|
| 812 |
-
" examples=x.get('examples', None),\n",
|
| 813 |
-
" tokenize=False,\n",
|
| 814 |
-
" add_generation_prompt=True)\n",
|
| 815 |
-
" for i, x in enumerate(inputs)\n",
|
| 816 |
-
"]\n",
|
| 817 |
-
"\n",
|
| 818 |
-
"image_inputs = process_all_vision_info(messages, [x.get('examples') for x in inputs])\n",
|
| 819 |
-
"inputs = processor(\n",
|
| 820 |
-
" text=texts,\n",
|
| 821 |
-
" images=image_inputs,\n",
|
| 822 |
-
" padding=True,\n",
|
| 823 |
-
" return_tensors=\"pt\",\n",
|
| 824 |
-
").to(\"cuda\")\n",
|
| 825 |
-
"\n",
|
| 826 |
-
"# Batch Inference\n",
|
| 827 |
-
"generated_ids = model.generate(**inputs, **generation_config)\n",
|
| 828 |
-
"generated_ids_trimmed = [\n",
|
| 829 |
-
" out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)\n",
|
| 830 |
-
"]\n",
|
| 831 |
-
"output_texts = processor.batch_decode(\n",
|
| 832 |
-
" generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False\n",
|
| 833 |
-
")\n",
|
| 834 |
-
"for y in output_texts:\n",
|
| 835 |
-
" print(y)"
|
| 836 |
-
]
|
| 837 |
-
},
|
| 838 |
-
{
|
| 839 |
-
"cell_type": "code",
|
| 840 |
-
"execution_count": null,
|
| 841 |
-
"id": "cf456dcc-52ce-495d-872f-a641c6f40f7d",
|
| 842 |
-
"metadata": {
|
| 843 |
-
"tags": []
|
| 844 |
-
},
|
| 845 |
-
"outputs": [],
|
| 846 |
-
"source": []
|
| 847 |
-
}
|
| 848 |
-
],
|
| 849 |
-
"metadata": {
|
| 850 |
-
"environment": {
|
| 851 |
-
"kernel": "conda-base-py",
|
| 852 |
-
"name": "workbench-notebooks.m127",
|
| 853 |
-
"type": "gcloud",
|
| 854 |
-
"uri": "us-docker.pkg.dev/deeplearning-platform-release/gcr.io/workbench-notebooks:m127"
|
| 855 |
-
},
|
| 856 |
-
"kernelspec": {
|
| 857 |
-
"display_name": ".venv",
|
| 858 |
-
"language": "python",
|
| 859 |
-
"name": "python3"
|
| 860 |
-
},
|
| 861 |
-
"language_info": {
|
| 862 |
-
"codemirror_mode": {
|
| 863 |
-
"name": "ipython",
|
| 864 |
-
"version": 3
|
| 865 |
-
},
|
| 866 |
-
"file_extension": ".py",
|
| 867 |
-
"mimetype": "text/x-python",
|
| 868 |
-
"name": "python",
|
| 869 |
-
"nbconvert_exporter": "python",
|
| 870 |
-
"pygments_lexer": "ipython3",
|
| 871 |
-
"version": "3.12.10"
|
| 872 |
-
}
|
| 873 |
-
},
|
| 874 |
-
"nbformat": 4,
|
| 875 |
-
"nbformat_minor": 5
|
| 876 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pyproject.toml
CHANGED
|
@@ -5,12 +5,17 @@ description = "Add your description here"
|
|
| 5 |
readme = "README.md"
|
| 6 |
requires-python = ">=3.12,<3.13"
|
| 7 |
dependencies = [
|
|
|
|
| 8 |
"flash-attn",
|
|
|
|
| 9 |
"gradio",
|
| 10 |
"huggingface-hub",
|
| 11 |
-
"
|
| 12 |
-
"
|
| 13 |
-
"
|
|
|
|
|
|
|
|
|
|
| 14 |
]
|
| 15 |
|
| 16 |
[tool.uv.sources]
|
|
|
|
| 5 |
readme = "README.md"
|
| 6 |
requires-python = ">=3.12,<3.13"
|
| 7 |
dependencies = [
|
| 8 |
+
"accelerate>=1.14.0",
|
| 9 |
"flash-attn",
|
| 10 |
+
"gguf>=0.19.0",
|
| 11 |
"gradio",
|
| 12 |
"huggingface-hub",
|
| 13 |
+
"ipykernel>=7.3.0",
|
| 14 |
+
"ipython>=9.15.0",
|
| 15 |
+
"ipywidgets>=8.1.8",
|
| 16 |
+
"openai>=2.46.0",
|
| 17 |
+
"torch==2.9.1",
|
| 18 |
+
"transformers",
|
| 19 |
]
|
| 20 |
|
| 21 |
[tool.uv.sources]
|
uv.lock
CHANGED
|
@@ -7,6 +7,24 @@ resolution-markers = [
|
|
| 7 |
"sys_platform != 'emscripten' and sys_platform != 'win32'",
|
| 8 |
]
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
[[package]]
|
| 11 |
name = "annotated-doc"
|
| 12 |
version = "0.0.4"
|
|
@@ -39,20 +57,21 @@ wheels = [
|
|
| 39 |
]
|
| 40 |
|
| 41 |
[[package]]
|
| 42 |
-
name = "
|
| 43 |
-
version = "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
source = { registry = "https://pypi.org/simple" }
|
| 45 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 46 |
wheels = [
|
| 47 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 48 |
-
{ url = "https://files.pythonhosted.org/packages/77/b3/2576a44b4f39c7462ced4c17fec04c756f7b0f3c5cb940d124173e417d6a/av-18.0.0-cp311-abi3-macosx_14_0_arm64.whl", hash = "sha256:35274c20d2ad3b4774fe632bcef2e34af79858ddf899352339cc3babbc13a484", size = 18175248, upload-time = "2026-07-02T06:37:01.741Z" },
|
| 49 |
-
{ url = "https://files.pythonhosted.org/packages/84/74/6732f17b96dc23fd23b876b2805435855abdc8a3b397142be4e581165de8/av-18.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4d683b7747a0ba9222b8a5f81e41db5f796e7f64473454ec4fe2548e083c2fa0", size = 33387843, upload-time = "2026-07-02T06:37:05.097Z" },
|
| 50 |
-
{ url = "https://files.pythonhosted.org/packages/6d/b9/7708c43fed7ae28b4a1bad060b4221e3334cd827cec24f7165902a6ac1f4/av-18.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ae56b40b6f8b067a8ad2dac664fbfbabac7f7a55b9a7bb031eb99289252bc017", size = 35536910, upload-time = "2026-07-02T06:37:08.806Z" },
|
| 51 |
-
{ url = "https://files.pythonhosted.org/packages/5a/94/eba99691d184f6a395a242d54dc370e2fd2265e95bbc98e2963a0fdbdd6c/av-18.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ea2e8ebbce521f21b55df9400e00d721623c9020ef158f5a188a96130be0743f", size = 38984619, upload-time = "2026-07-02T06:37:11.861Z" },
|
| 52 |
-
{ url = "https://files.pythonhosted.org/packages/c9/cf/0d7aee07fe16aa9ffdf96043c14bed5485a52c0dea4259de87aa306ecab4/av-18.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef96dabb3e50dac249913145dff5424b302b257fd95dcb64be3c7b7a8aef16d1", size = 34451176, upload-time = "2026-07-02T06:37:15.154Z" },
|
| 53 |
-
{ url = "https://files.pythonhosted.org/packages/76/92/810da80b12680d4c4fe235bd1b4003289be9213ac7f114b77b8ecf0e3b3e/av-18.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:0f65518a184613e41536f29e8758c8e3d8293e46bf5bef108f04f925bbfa3f44", size = 36619869, upload-time = "2026-07-02T06:37:18.495Z" },
|
| 54 |
-
{ url = "https://files.pythonhosted.org/packages/11/85/0f121ff43dc5a70696676c98a8f1674e2fa787614c2abaacb15fa1a9bc99/av-18.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:aaf4d354d2beaa6651e4f92e54409a578bde64f79c0beef9a30b388d06f7c629", size = 27556236, upload-time = "2026-07-02T06:37:21.388Z" },
|
| 55 |
-
{ url = "https://files.pythonhosted.org/packages/8b/f6/2509754d4d2356abc6fc0ea3d57c12ade29bac23a1fb7fc215a53ca518fb/av-18.0.0-cp311-abi3-win_arm64.whl", hash = "sha256:adac2b3833b6cb9bd6cb52664a522b94db453615b3675b1dbb26e13fe1c80da6", size = 20221133, upload-time = "2026-07-02T06:37:23.88Z" },
|
| 56 |
]
|
| 57 |
|
| 58 |
[[package]]
|
|
@@ -82,6 +101,29 @@ wheels = [
|
|
| 82 |
{ url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
|
| 83 |
]
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
[[package]]
|
| 86 |
name = "charset-normalizer"
|
| 87 |
version = "3.4.9"
|
|
@@ -125,6 +167,46 @@ wheels = [
|
|
| 125 |
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
| 126 |
]
|
| 127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
[[package]]
|
| 129 |
name = "einops"
|
| 130 |
version = "0.8.2"
|
|
@@ -134,6 +216,15 @@ wheels = [
|
|
| 134 |
{ url = "https://files.pythonhosted.org/packages/2a/09/f8d8f8f31e4483c10a906437b4ce31bdf3d6d417b73fe33f1a8b59e34228/einops-0.8.2-py3-none-any.whl", hash = "sha256:54058201ac7087911181bfec4af6091bb59380360f069276601256a76af08193", size = 65638, upload-time = "2026-01-26T04:13:18.546Z" },
|
| 135 |
]
|
| 136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
[[package]]
|
| 138 |
name = "fastapi"
|
| 139 |
version = "0.139.2"
|
|
@@ -186,6 +277,21 @@ wheels = [
|
|
| 186 |
{ url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" },
|
| 187 |
]
|
| 188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
[[package]]
|
| 190 |
name = "gradio"
|
| 191 |
version = "6.20.0"
|
|
@@ -344,6 +450,92 @@ wheels = [
|
|
| 344 |
{ url = "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2", size = 61538, upload-time = "2022-09-14T00:24:23.22Z" },
|
| 345 |
]
|
| 346 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
[[package]]
|
| 348 |
name = "jinja2"
|
| 349 |
version = "3.1.6"
|
|
@@ -356,6 +548,71 @@ wheels = [
|
|
| 356 |
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
| 357 |
]
|
| 358 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
[[package]]
|
| 360 |
name = "markdown-it-py"
|
| 361 |
version = "4.2.0"
|
|
@@ -387,6 +644,18 @@ wheels = [
|
|
| 387 |
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
| 388 |
]
|
| 389 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
[[package]]
|
| 391 |
name = "mdurl"
|
| 392 |
version = "0.1.2"
|
|
@@ -405,6 +674,15 @@ wheels = [
|
|
| 405 |
{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
|
| 406 |
]
|
| 407 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
[[package]]
|
| 409 |
name = "networkx"
|
| 410 |
version = "3.6.1"
|
|
@@ -419,22 +697,32 @@ name = "numarkapp"
|
|
| 419 |
version = "0.1.0"
|
| 420 |
source = { virtual = "." }
|
| 421 |
dependencies = [
|
|
|
|
| 422 |
{ name = "flash-attn" },
|
|
|
|
| 423 |
{ name = "gradio" },
|
| 424 |
{ name = "huggingface-hub" },
|
| 425 |
-
{ name = "
|
|
|
|
|
|
|
|
|
|
| 426 |
{ name = "torch" },
|
| 427 |
{ name = "transformers" },
|
| 428 |
]
|
| 429 |
|
| 430 |
[package.metadata]
|
| 431 |
requires-dist = [
|
|
|
|
| 432 |
{ name = "flash-attn", url = "https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.28/flash_attn-2.8.3+cu130torch2.9-cp312-cp312-win_amd64.whl" },
|
|
|
|
| 433 |
{ name = "gradio" },
|
| 434 |
{ name = "huggingface-hub" },
|
| 435 |
-
{ name = "
|
| 436 |
-
{ name = "
|
| 437 |
-
{ name = "
|
|
|
|
|
|
|
|
|
|
| 438 |
]
|
| 439 |
|
| 440 |
[[package]]
|
|
@@ -605,6 +893,25 @@ wheels = [
|
|
| 605 |
{ url = "https://files.pythonhosted.org/packages/86/91/8b486ba85f71a2859dd705a4ec6aab38c37a389b8b7f94343db027732999/nvidia_nvtx-13.0.39-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cddd2e08b35144f1000631c3880c9ebbcb8a2863d762e76f92d47d30ecaf87cc", size = 148037, upload-time = "2025-08-04T10:18:31.763Z" },
|
| 606 |
]
|
| 607 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 608 |
[[package]]
|
| 609 |
name = "orjson"
|
| 610 |
version = "3.11.9"
|
|
@@ -658,6 +965,27 @@ wheels = [
|
|
| 658 |
{ url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" },
|
| 659 |
]
|
| 660 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 661 |
[[package]]
|
| 662 |
name = "pillow"
|
| 663 |
version = "12.3.0"
|
|
@@ -675,6 +1003,70 @@ wheels = [
|
|
| 675 |
{ url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" },
|
| 676 |
]
|
| 677 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 678 |
[[package]]
|
| 679 |
name = "pydantic"
|
| 680 |
version = "2.13.4"
|
|
@@ -787,18 +1179,24 @@ wheels = [
|
|
| 787 |
]
|
| 788 |
|
| 789 |
[[package]]
|
| 790 |
-
name = "
|
| 791 |
-
version = "
|
| 792 |
source = { registry = "https://pypi.org/simple" }
|
| 793 |
dependencies = [
|
| 794 |
-
{ name = "
|
| 795 |
-
{ name = "packaging" },
|
| 796 |
-
{ name = "pillow" },
|
| 797 |
-
{ name = "requests" },
|
| 798 |
]
|
| 799 |
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
| 800 |
wheels = [
|
| 801 |
-
{ url = "https://files.pythonhosted.org/packages/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
]
|
| 803 |
|
| 804 |
[[package]]
|
|
@@ -925,6 +1323,29 @@ wheels = [
|
|
| 925 |
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
| 926 |
]
|
| 927 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 928 |
[[package]]
|
| 929 |
name = "starlette"
|
| 930 |
version = "1.3.1"
|
|
@@ -987,7 +1408,7 @@ wheels = [
|
|
| 987 |
|
| 988 |
[[package]]
|
| 989 |
name = "torch"
|
| 990 |
-
version = "2.9.
|
| 991 |
source = { registry = "https://download.pytorch.org/whl/cu130" }
|
| 992 |
dependencies = [
|
| 993 |
{ name = "filelock" },
|
|
@@ -1015,9 +1436,26 @@ dependencies = [
|
|
| 1015 |
{ name = "typing-extensions" },
|
| 1016 |
]
|
| 1017 |
wheels = [
|
| 1018 |
-
{ url = "https://download-r2.pytorch.org/whl/cu130/torch-2.9.
|
| 1019 |
-
{ url = "https://download-r2.pytorch.org/whl/cu130/torch-2.9.
|
| 1020 |
-
{ url = "https://download-r2.pytorch.org/whl/cu130/torch-2.9.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
]
|
| 1022 |
|
| 1023 |
[[package]]
|
|
@@ -1032,6 +1470,15 @@ wheels = [
|
|
| 1032 |
{ url = "https://files.pythonhosted.org/packages/48/5d/acf5905c36149bbaec41ccf7f2b68814647347b72075ac0b1fe3022fdc73/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", size = 78351, upload-time = "2024-08-03T22:35:36.644Z" },
|
| 1033 |
]
|
| 1034 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1035 |
[[package]]
|
| 1036 |
name = "transformers"
|
| 1037 |
version = "5.14.1"
|
|
@@ -1054,11 +1501,11 @@ wheels = [
|
|
| 1054 |
|
| 1055 |
[[package]]
|
| 1056 |
name = "triton"
|
| 1057 |
-
version = "3.5.
|
| 1058 |
source = { registry = "https://pypi.org/simple" }
|
| 1059 |
wheels = [
|
| 1060 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1061 |
-
{ url = "https://files.pythonhosted.org/packages/
|
| 1062 |
]
|
| 1063 |
|
| 1064 |
[[package]]
|
|
@@ -1127,3 +1574,21 @@ sdist = { url = "https://files.pythonhosted.org/packages/a2/65/b7c6c443ccc58678c
|
|
| 1127 |
wheels = [
|
| 1128 |
{ url = "https://files.pythonhosted.org/packages/45/ec/dbb7e5a6b91f86bfb9eb7d2988a2730907b6a729875b949c7f022e8b88fa/uvicorn-0.51.0-py3-none-any.whl", hash = "sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b", size = 73219, upload-time = "2026-07-08T10:59:04.44Z" },
|
| 1129 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
"sys_platform != 'emscripten' and sys_platform != 'win32'",
|
| 8 |
]
|
| 9 |
|
| 10 |
+
[[package]]
|
| 11 |
+
name = "accelerate"
|
| 12 |
+
version = "1.14.0"
|
| 13 |
+
source = { registry = "https://pypi.org/simple" }
|
| 14 |
+
dependencies = [
|
| 15 |
+
{ name = "huggingface-hub" },
|
| 16 |
+
{ name = "numpy" },
|
| 17 |
+
{ name = "packaging" },
|
| 18 |
+
{ name = "psutil" },
|
| 19 |
+
{ name = "pyyaml" },
|
| 20 |
+
{ name = "safetensors" },
|
| 21 |
+
{ name = "torch" },
|
| 22 |
+
]
|
| 23 |
+
sdist = { url = "https://files.pythonhosted.org/packages/8d/75/94cd5d389649578aca399e5aa822637eec18319a1dadc400ffe2f9a7493f/accelerate-1.14.0.tar.gz", hash = "sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d", size = 412167, upload-time = "2026-06-11T13:45:52.326Z" }
|
| 24 |
+
wheels = [
|
| 25 |
+
{ url = "https://files.pythonhosted.org/packages/a8/db/253133d7e7cb40d3af384bb2f5c0b4a2b7fdcffbc95c688cc67a20a3c103/accelerate-1.14.0-py3-none-any.whl", hash = "sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6", size = 389246, upload-time = "2026-06-11T13:45:50.477Z" },
|
| 26 |
+
]
|
| 27 |
+
|
| 28 |
[[package]]
|
| 29 |
name = "annotated-doc"
|
| 30 |
version = "0.0.4"
|
|
|
|
| 57 |
]
|
| 58 |
|
| 59 |
[[package]]
|
| 60 |
+
name = "appnope"
|
| 61 |
+
version = "0.1.4"
|
| 62 |
+
source = { registry = "https://pypi.org/simple" }
|
| 63 |
+
sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" }
|
| 64 |
+
wheels = [
|
| 65 |
+
{ url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" },
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
[[package]]
|
| 69 |
+
name = "asttokens"
|
| 70 |
+
version = "3.0.2"
|
| 71 |
source = { registry = "https://pypi.org/simple" }
|
| 72 |
+
sdist = { url = "https://files.pythonhosted.org/packages/25/1e/faf0f247f6f881b98fc4d6d07e14085cb89d13665084e6d6ac1dc2c03d0b/asttokens-3.0.2.tar.gz", hash = "sha256:3ecdbd8f2cc195f53ccada3a613538bb5f9ef6f6869129f13e03c30a677b8fe2", size = 63136, upload-time = "2026-07-12T03:31:49.084Z" }
|
| 73 |
wheels = [
|
| 74 |
+
{ url = "https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl", hash = "sha256:9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933", size = 28702, upload-time = "2026-07-12T03:31:47.542Z" },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
]
|
| 76 |
|
| 77 |
[[package]]
|
|
|
|
| 101 |
{ url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
|
| 102 |
]
|
| 103 |
|
| 104 |
+
[[package]]
|
| 105 |
+
name = "cffi"
|
| 106 |
+
version = "2.1.0"
|
| 107 |
+
source = { registry = "https://pypi.org/simple" }
|
| 108 |
+
dependencies = [
|
| 109 |
+
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
| 110 |
+
]
|
| 111 |
+
sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" }
|
| 112 |
+
wheels = [
|
| 113 |
+
{ url = "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f", size = 184829, upload-time = "2026-07-06T21:32:44.324Z" },
|
| 114 |
+
{ url = "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde", size = 184728, upload-time = "2026-07-06T21:32:45.683Z" },
|
| 115 |
+
{ url = "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d", size = 214815, upload-time = "2026-07-06T21:32:48.557Z" },
|
| 116 |
+
{ url = "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7", size = 222429, upload-time = "2026-07-06T21:32:49.848Z" },
|
| 117 |
+
{ url = "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b", size = 210315, upload-time = "2026-07-06T21:32:51.221Z" },
|
| 118 |
+
{ url = "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7", size = 208859, upload-time = "2026-07-06T21:32:52.512Z" },
|
| 119 |
+
{ url = "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66", size = 221844, upload-time = "2026-07-06T21:32:53.704Z" },
|
| 120 |
+
{ url = "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe", size = 225287, upload-time = "2026-07-06T21:32:54.907Z" },
|
| 121 |
+
{ url = "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b", size = 223681, upload-time = "2026-07-06T21:32:56.329Z" },
|
| 122 |
+
{ url = "https://files.pythonhosted.org/packages/b0/80/c138990aa2a70b1a269f6e06348729836d733d6f970867943f61d367f8cc/cffi-2.1.0-cp312-cp312-win32.whl", hash = "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a", size = 175269, upload-time = "2026-07-06T21:32:57.777Z" },
|
| 123 |
+
{ url = "https://files.pythonhosted.org/packages/a8/eb/f636456ff21a83fc13c032b58cc5dde061691546ac79efa284b2989b7982/cffi-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384", size = 185881, upload-time = "2026-07-06T21:32:59.253Z" },
|
| 124 |
+
{ url = "https://files.pythonhosted.org/packages/dd/2c/400ea43e721727dca8a65c4521390e9196757caba4a45643acb2b63271b8/cffi-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6", size = 180088, upload-time = "2026-07-06T21:33:02.278Z" },
|
| 125 |
+
]
|
| 126 |
+
|
| 127 |
[[package]]
|
| 128 |
name = "charset-normalizer"
|
| 129 |
version = "3.4.9"
|
|
|
|
| 167 |
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
| 168 |
]
|
| 169 |
|
| 170 |
+
[[package]]
|
| 171 |
+
name = "comm"
|
| 172 |
+
version = "0.2.3"
|
| 173 |
+
source = { registry = "https://pypi.org/simple" }
|
| 174 |
+
sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" }
|
| 175 |
+
wheels = [
|
| 176 |
+
{ url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" },
|
| 177 |
+
]
|
| 178 |
+
|
| 179 |
+
[[package]]
|
| 180 |
+
name = "debugpy"
|
| 181 |
+
version = "1.8.21"
|
| 182 |
+
source = { registry = "https://pypi.org/simple" }
|
| 183 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f2/aa/12037145b7a56eaa5b29b41872f7a21b538e807e13f32c4d3c46e59be084/debugpy-1.8.21.tar.gz", hash = "sha256:a3c53278e84c94e11bd87c53970ec391d1a67396c8b22609fcac576520e611a6", size = 1697577, upload-time = "2026-06-01T19:30:35.156Z" }
|
| 184 |
+
wheels = [
|
| 185 |
+
{ url = "https://files.pythonhosted.org/packages/a2/df/bf625547431a9cadc9f4cbfeda38866e2b17f6aed147b625377e87834449/debugpy-1.8.21-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:9f96713896f39c3dff0ee841f47320c3f2983d33c341e009361bb0ebc79adc4e", size = 2483609, upload-time = "2026-06-01T19:30:50.794Z" },
|
| 186 |
+
{ url = "https://files.pythonhosted.org/packages/bf/09/59324b903599031ff9faaec1758292409f6561a0ec2492fe4b703327705a/debugpy-1.8.21-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:c193d474f0a211191f2b4449d2d06157c689013035bd952f3b617e0ef422b176", size = 3968900, upload-time = "2026-06-01T19:30:52.341Z" },
|
| 187 |
+
{ url = "https://files.pythonhosted.org/packages/14/cd/27f65b805d7fe005c44e1a36b9183ecdfbcdbf9d3e721a5115d461ecc7ee/debugpy-1.8.21-cp312-cp312-win32.whl", hash = "sha256:4743373c1cac7f9e74a1b9915bf1dbe0e900eca657ffb170ae07ac8363205ae9", size = 5336340, upload-time = "2026-06-01T19:30:54.047Z" },
|
| 188 |
+
{ url = "https://files.pythonhosted.org/packages/77/1d/c84e30c0c674184948b66f076ab271c01d940618a2824c23cd035a27bc20/debugpy-1.8.21-cp312-cp312-win_amd64.whl", hash = "sha256:bd7ba9dd3daa7c2f942c6ca8d4695a16bf9ac16b63615261c7982bc74f7ed20c", size = 5374751, upload-time = "2026-06-01T19:30:55.891Z" },
|
| 189 |
+
{ url = "https://files.pythonhosted.org/packages/95/51/67e7cf11a53e40694f720457d5b3a1cdaaa3d5a9a633e482f225456b93ff/debugpy-1.8.21-py2.py3-none-any.whl", hash = "sha256:b1e37d333663c8851516a47364ef473da127f9caebe4417e6df6f5825a7e9a92", size = 5352888, upload-time = "2026-06-01T19:31:25.186Z" },
|
| 190 |
+
]
|
| 191 |
+
|
| 192 |
+
[[package]]
|
| 193 |
+
name = "decorator"
|
| 194 |
+
version = "5.3.1"
|
| 195 |
+
source = { registry = "https://pypi.org/simple" }
|
| 196 |
+
sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084, upload-time = "2026-05-18T06:03:28.057Z" }
|
| 197 |
+
wheels = [
|
| 198 |
+
{ url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365, upload-time = "2026-05-18T06:03:26.517Z" },
|
| 199 |
+
]
|
| 200 |
+
|
| 201 |
+
[[package]]
|
| 202 |
+
name = "distro"
|
| 203 |
+
version = "1.9.0"
|
| 204 |
+
source = { registry = "https://pypi.org/simple" }
|
| 205 |
+
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
|
| 206 |
+
wheels = [
|
| 207 |
+
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
|
| 208 |
+
]
|
| 209 |
+
|
| 210 |
[[package]]
|
| 211 |
name = "einops"
|
| 212 |
version = "0.8.2"
|
|
|
|
| 216 |
{ url = "https://files.pythonhosted.org/packages/2a/09/f8d8f8f31e4483c10a906437b4ce31bdf3d6d417b73fe33f1a8b59e34228/einops-0.8.2-py3-none-any.whl", hash = "sha256:54058201ac7087911181bfec4af6091bb59380360f069276601256a76af08193", size = 65638, upload-time = "2026-01-26T04:13:18.546Z" },
|
| 217 |
]
|
| 218 |
|
| 219 |
+
[[package]]
|
| 220 |
+
name = "executing"
|
| 221 |
+
version = "2.2.1"
|
| 222 |
+
source = { registry = "https://pypi.org/simple" }
|
| 223 |
+
sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" }
|
| 224 |
+
wheels = [
|
| 225 |
+
{ url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" },
|
| 226 |
+
]
|
| 227 |
+
|
| 228 |
[[package]]
|
| 229 |
name = "fastapi"
|
| 230 |
version = "0.139.2"
|
|
|
|
| 277 |
{ url = "https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl", hash = "sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2", size = 203402, upload-time = "2026-04-29T20:42:36.842Z" },
|
| 278 |
]
|
| 279 |
|
| 280 |
+
[[package]]
|
| 281 |
+
name = "gguf"
|
| 282 |
+
version = "0.19.0"
|
| 283 |
+
source = { registry = "https://pypi.org/simple" }
|
| 284 |
+
dependencies = [
|
| 285 |
+
{ name = "numpy" },
|
| 286 |
+
{ name = "pyyaml" },
|
| 287 |
+
{ name = "requests" },
|
| 288 |
+
{ name = "tqdm" },
|
| 289 |
+
]
|
| 290 |
+
sdist = { url = "https://files.pythonhosted.org/packages/48/ae/17f1308ae45cd7b08ebb521747d5b23f4efc4d172038a4e228dd5106c3ff/gguf-0.19.0.tar.gz", hash = "sha256:dbadcd6cc7ccd44256f2229fe7c2dff5e8aa5cf0612ab987fd2b1a57e428923f", size = 111220, upload-time = "2026-05-06T13:04:03.667Z" }
|
| 291 |
+
wheels = [
|
| 292 |
+
{ url = "https://files.pythonhosted.org/packages/b3/bb/d71d6da82763528c2c2ed6b59a9d6142c6595545a4c448e2085d155e88c2/gguf-0.19.0-py3-none-any.whl", hash = "sha256:70bcd10edfe697fb2dad6e40af2234b9d8ece9a41a99761405121ebda1c3c1cd", size = 118475, upload-time = "2026-05-06T13:04:02.588Z" },
|
| 293 |
+
]
|
| 294 |
+
|
| 295 |
[[package]]
|
| 296 |
name = "gradio"
|
| 297 |
version = "6.20.0"
|
|
|
|
| 450 |
{ url = "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2", size = 61538, upload-time = "2022-09-14T00:24:23.22Z" },
|
| 451 |
]
|
| 452 |
|
| 453 |
+
[[package]]
|
| 454 |
+
name = "ipykernel"
|
| 455 |
+
version = "7.3.0"
|
| 456 |
+
source = { registry = "https://pypi.org/simple" }
|
| 457 |
+
dependencies = [
|
| 458 |
+
{ name = "appnope", marker = "sys_platform == 'darwin'" },
|
| 459 |
+
{ name = "comm" },
|
| 460 |
+
{ name = "debugpy" },
|
| 461 |
+
{ name = "ipython" },
|
| 462 |
+
{ name = "jupyter-client" },
|
| 463 |
+
{ name = "jupyter-core" },
|
| 464 |
+
{ name = "matplotlib-inline" },
|
| 465 |
+
{ name = "nest-asyncio2" },
|
| 466 |
+
{ name = "packaging" },
|
| 467 |
+
{ name = "psutil" },
|
| 468 |
+
{ name = "pyzmq" },
|
| 469 |
+
{ name = "tornado" },
|
| 470 |
+
{ name = "traitlets" },
|
| 471 |
+
]
|
| 472 |
+
sdist = { url = "https://files.pythonhosted.org/packages/3d/c4/e4a38f579de4225a561305666f7541cdabb30075def2aa1ac17bd73c1fb5/ipykernel-7.3.0.tar.gz", hash = "sha256:9acaaaf97d16355166e4085afe9d225bfbdf2b7ef520f9df3be8f2b248275e09", size = 184899, upload-time = "2026-06-10T08:41:25.481Z" }
|
| 473 |
+
wheels = [
|
| 474 |
+
{ url = "https://files.pythonhosted.org/packages/3d/02/77b271f5dc58bfbc0b577c877b2365d1ffea2afe66a80c13f2312820348c/ipykernel-7.3.0-py3-none-any.whl", hash = "sha256:897eb64da762549ef610698fca5e9675195ec6ac8ec7f19d81ce1ca20c876057", size = 120583, upload-time = "2026-06-10T08:41:23.648Z" },
|
| 475 |
+
]
|
| 476 |
+
|
| 477 |
+
[[package]]
|
| 478 |
+
name = "ipython"
|
| 479 |
+
version = "9.15.0"
|
| 480 |
+
source = { registry = "https://pypi.org/simple" }
|
| 481 |
+
dependencies = [
|
| 482 |
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
| 483 |
+
{ name = "decorator" },
|
| 484 |
+
{ name = "ipython-pygments-lexers" },
|
| 485 |
+
{ name = "jedi" },
|
| 486 |
+
{ name = "matplotlib-inline" },
|
| 487 |
+
{ name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
| 488 |
+
{ name = "prompt-toolkit" },
|
| 489 |
+
{ name = "psutil", marker = "sys_platform != 'cygwin' and sys_platform != 'emscripten'" },
|
| 490 |
+
{ name = "pygments" },
|
| 491 |
+
{ name = "stack-data" },
|
| 492 |
+
{ name = "traitlets" },
|
| 493 |
+
]
|
| 494 |
+
sdist = { url = "https://files.pythonhosted.org/packages/53/59/165d3b4d75cc34add3122c4417ecb229085140ac573103c223cd01dde96f/ipython-9.15.0.tar.gz", hash = "sha256:da2819ce2aa83135257df830660b1176d986c3d2876db24df01974fa955b2756", size = 4442580, upload-time = "2026-06-26T11:03:35.913Z" }
|
| 495 |
+
wheels = [
|
| 496 |
+
{ url = "https://files.pythonhosted.org/packages/40/3a/948263ca3b9d65bb2b1b0c521b3a49fad5d59ada58724bd87d2bd5ff3f36/ipython-9.15.0-py3-none-any.whl", hash = "sha256:515ad9c3cdf0c932a5a9f6245419e8aba706b7bd03c3e1d3a1c83d9351d6aa6e", size = 630895, upload-time = "2026-06-26T11:03:33.809Z" },
|
| 497 |
+
]
|
| 498 |
+
|
| 499 |
+
[[package]]
|
| 500 |
+
name = "ipython-pygments-lexers"
|
| 501 |
+
version = "1.1.1"
|
| 502 |
+
source = { registry = "https://pypi.org/simple" }
|
| 503 |
+
dependencies = [
|
| 504 |
+
{ name = "pygments" },
|
| 505 |
+
]
|
| 506 |
+
sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
|
| 507 |
+
wheels = [
|
| 508 |
+
{ url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" },
|
| 509 |
+
]
|
| 510 |
+
|
| 511 |
+
[[package]]
|
| 512 |
+
name = "ipywidgets"
|
| 513 |
+
version = "8.1.8"
|
| 514 |
+
source = { registry = "https://pypi.org/simple" }
|
| 515 |
+
dependencies = [
|
| 516 |
+
{ name = "comm" },
|
| 517 |
+
{ name = "ipython" },
|
| 518 |
+
{ name = "jupyterlab-widgets" },
|
| 519 |
+
{ name = "traitlets" },
|
| 520 |
+
{ name = "widgetsnbextension" },
|
| 521 |
+
]
|
| 522 |
+
sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" }
|
| 523 |
+
wheels = [
|
| 524 |
+
{ url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" },
|
| 525 |
+
]
|
| 526 |
+
|
| 527 |
+
[[package]]
|
| 528 |
+
name = "jedi"
|
| 529 |
+
version = "0.20.0"
|
| 530 |
+
source = { registry = "https://pypi.org/simple" }
|
| 531 |
+
dependencies = [
|
| 532 |
+
{ name = "parso" },
|
| 533 |
+
]
|
| 534 |
+
sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416, upload-time = "2026-05-01T23:38:47.814Z" }
|
| 535 |
+
wheels = [
|
| 536 |
+
{ url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812, upload-time = "2026-05-01T23:38:43.919Z" },
|
| 537 |
+
]
|
| 538 |
+
|
| 539 |
[[package]]
|
| 540 |
name = "jinja2"
|
| 541 |
version = "3.1.6"
|
|
|
|
| 548 |
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
| 549 |
]
|
| 550 |
|
| 551 |
+
[[package]]
|
| 552 |
+
name = "jiter"
|
| 553 |
+
version = "0.16.0"
|
| 554 |
+
source = { registry = "https://pypi.org/simple" }
|
| 555 |
+
sdist = { url = "https://files.pythonhosted.org/packages/1d/1f/10936e16d8860c70698a1aa939a46aa0224813b782bce4e000e637da0b2d/jiter-0.16.0.tar.gz", hash = "sha256:7b24c3492c5f4f84a37946ad9cf504910cf6a782d6a4e0689b6673c5894b4a1c", size = 176431, upload-time = "2026-06-29T13:05:13.657Z" }
|
| 556 |
+
wheels = [
|
| 557 |
+
{ url = "https://files.pythonhosted.org/packages/83/2b/52ace16ed031354f0539749a49e4bf33797d82bea5137910835fa4b09793/jiter-0.16.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:67c3bc1760f8c99d805dcab4e644027142a53b1d5d861f18780ebdbd5d40b72a", size = 306943, upload-time = "2026-06-29T13:03:14.035Z" },
|
| 558 |
+
{ url = "https://files.pythonhosted.org/packages/94/2e/34957c2c1b661c252ba9bcc60ae0bddc27e0f7202c6073326a13c5390eec/jiter-0.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5af7780e4a26bd7d0d989592bf9ef12ebf806b74ab709223ecca37c749872ea9", size = 307779, upload-time = "2026-06-29T13:03:15.418Z" },
|
| 559 |
+
{ url = "https://files.pythonhosted.org/packages/88/6c/59bd309cab4460c54cf1079f3eb7fe7af6a4c895c5c957a53378693bad2b/jiter-0.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5bf78d0e05e45cfdd66558893938d59afe3d1b1a824a202039b20e607d25a72", size = 335826, upload-time = "2026-06-29T13:03:17.11Z" },
|
| 560 |
+
{ url = "https://files.pythonhosted.org/packages/3b/8c/f5ef7b65f0df47afa16596969defb281ebb86e96df346d62be6fd853d620/jiter-0.16.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4444a83f946605990c98f625cdd3d2725bfb818158760c5748c653170a20e0e", size = 362573, upload-time = "2026-06-29T13:03:18.781Z" },
|
| 561 |
+
{ url = "https://files.pythonhosted.org/packages/2b/0b/ace4354da061ee38844a0c27dc2c21eecd27aea119e8da324bea987522d0/jiter-0.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a23f0e4f957e1be65752d2dfac9a5a06b1917af8dc85deb639c3b9d02e31290", size = 457979, upload-time = "2026-06-29T13:03:20.293Z" },
|
| 562 |
+
{ url = "https://files.pythonhosted.org/packages/55/40/c0253d3772eb9dcd8e6606ee9b2d53ec8e5b814589c47f140aa585f21eaa/jiter-0.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c22a488f7b9218e245a0025a9ba6b100e2e54700831cf4cf16833a27fba3ad01", size = 372302, upload-time = "2026-06-29T13:03:21.739Z" },
|
| 563 |
+
{ url = "https://files.pythonhosted.org/packages/a8/d2/4839422241aa12860ce597b20068727094ba0bc480723c74924ca5bad483/jiter-0.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46add52f4ad47a08bfb1219f3e673da972191489a33016edefdb5ea55bfa8c48", size = 343805, upload-time = "2026-06-29T13:03:23.384Z" },
|
| 564 |
+
{ url = "https://files.pythonhosted.org/packages/e2/59/e196888a05befdda7dbe299b722d56f2f6eec65402bc34c0a3306d595feb/jiter-0.16.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9c8a956fd72c2cf1e730d01ea080341f13aa0a97a4a33b51abebe725b7ae9ca9", size = 351107, upload-time = "2026-06-29T13:03:24.815Z" },
|
| 565 |
+
{ url = "https://files.pythonhosted.org/packages/ec/74/4cd9e0fca65232136400354b630fbfcd2de634e22ccbb96567725981b548/jiter-0.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:561926e0573ffe4a32498420a76d64b16c513e1ab413b9d28158a8764ac701e5", size = 388441, upload-time = "2026-06-29T13:03:26.266Z" },
|
| 566 |
+
{ url = "https://files.pythonhosted.org/packages/d9/8c/554691e48bc711299c0a293dd8a6179e24b2d66a54dc295421fcf64569c0/jiter-0.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:44d019fa8cdaf89bf29c71b39e3712143fdd0ac76725c6ef954f9957a5ea8730", size = 516354, upload-time = "2026-06-29T13:03:28.02Z" },
|
| 567 |
+
{ url = "https://files.pythonhosted.org/packages/a4/cb/01e9d69dc2cc6759d4f91e230b34489c4fdb2518992650633f9e20bece89/jiter-0.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0df91907609837f33341b8e6fe73b95991fdaa57caf1a0fbd343dffe826f386f", size = 547880, upload-time = "2026-06-29T13:03:29.534Z" },
|
| 568 |
+
{ url = "https://files.pythonhosted.org/packages/79/70/2953195f1c6ad00f49fa67e13df7e60acb3dd4f387101bc15abccddd905e/jiter-0.16.0-cp312-cp312-win32.whl", hash = "sha256:51d7b836acb0108d7c77df1742332cac2a1fa04a74d6dacec46e7091f0e91274", size = 203473, upload-time = "2026-06-29T13:03:31.025Z" },
|
| 569 |
+
{ url = "https://files.pythonhosted.org/packages/2d/05/2909a8b10699a4d560f8c502b6b2c5f3991b682b1922c1eedda242b225bd/jiter-0.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:1878349266f8ee36ecb1375cc5ba2f115f35fd9f0a1a4119e725e379126647f7", size = 196905, upload-time = "2026-06-29T13:03:32.472Z" },
|
| 570 |
+
{ url = "https://files.pythonhosted.org/packages/e9/a9/6b82bb1c8d7790d602489b967b982a909e5d092875a6c2ade96444c8dfc5/jiter-0.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:2ed5738ae4af18271a51a528b8811b0cbfa4a1858de9d83359e4169855d6a331", size = 190618, upload-time = "2026-06-29T13:03:34.672Z" },
|
| 571 |
+
{ url = "https://files.pythonhosted.org/packages/98/ab/664fd8c4be028b2bedd3d2ff08769c4ede23d0dbc87a77c62384a0515b5d/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:f17d61a28b4b3e0e3e2ba98490c70501403b4d196f78732439160e7fd3678127", size = 303106, upload-time = "2026-06-29T13:05:07.118Z" },
|
| 572 |
+
{ url = "https://files.pythonhosted.org/packages/1a/07/421f1d5b65493a76e16027b848aba6a7d28073ae75944fa4289cc914d39f/jiter-0.16.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:96e38eea538c8ddf853a35727c7be0741c76c13f04148ac5c116222f50ece3b3", size = 304658, upload-time = "2026-06-29T13:05:08.708Z" },
|
| 573 |
+
{ url = "https://files.pythonhosted.org/packages/0a/db/bba1155f01a01c3c37a89425d571da751bbedf5c54247b831a04cb971798/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d284fb8d94d5855d60c44fefcab4bf966f1da6fada73992b01f6f0c9bc0c6702", size = 339719, upload-time = "2026-06-29T13:05:10.41Z" },
|
| 574 |
+
{ url = "https://files.pythonhosted.org/packages/78/f7/18a1afcd64f35314b68c1f23afcd9994d0bc13e65cc77517afff4e83986d/jiter-0.16.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d613743df53199b1aa256a7d328340da6d7078aac7705a7db9d7a791e9cfd2", size = 343885, upload-time = "2026-06-29T13:05:12.087Z" },
|
| 575 |
+
]
|
| 576 |
+
|
| 577 |
+
[[package]]
|
| 578 |
+
name = "jupyter-client"
|
| 579 |
+
version = "8.9.1"
|
| 580 |
+
source = { registry = "https://pypi.org/simple" }
|
| 581 |
+
dependencies = [
|
| 582 |
+
{ name = "jupyter-core" },
|
| 583 |
+
{ name = "python-dateutil" },
|
| 584 |
+
{ name = "pyzmq" },
|
| 585 |
+
{ name = "tornado" },
|
| 586 |
+
{ name = "traitlets" },
|
| 587 |
+
{ name = "typing-extensions" },
|
| 588 |
+
]
|
| 589 |
+
sdist = { url = "https://files.pythonhosted.org/packages/7d/dc/5512503b088997c2250b8bf18258fba9d9ce5ead641183700960d3c9d342/jupyter_client-8.9.1.tar.gz", hash = "sha256:a58f730dd9e728ba16ba1d62ebccf7ffe1ebbdbce4e95cfae941b7321ae1f4fa", size = 359256, upload-time = "2026-06-09T13:15:01.033Z" }
|
| 590 |
+
wheels = [
|
| 591 |
+
{ url = "https://files.pythonhosted.org/packages/3f/6f/56d39bf385c5c27988aebaf0c18a2a17e960575740100973511018bd904e/jupyter_client-8.9.1-py3-none-any.whl", hash = "sha256:0b7a295bc46e8751e9adae84781f726c851c1d911bd793edc4a3bde942e3da81", size = 109828, upload-time = "2026-06-09T13:14:58.835Z" },
|
| 592 |
+
]
|
| 593 |
+
|
| 594 |
+
[[package]]
|
| 595 |
+
name = "jupyter-core"
|
| 596 |
+
version = "5.9.1"
|
| 597 |
+
source = { registry = "https://pypi.org/simple" }
|
| 598 |
+
dependencies = [
|
| 599 |
+
{ name = "platformdirs" },
|
| 600 |
+
{ name = "traitlets" },
|
| 601 |
+
]
|
| 602 |
+
sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" }
|
| 603 |
+
wheels = [
|
| 604 |
+
{ url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" },
|
| 605 |
+
]
|
| 606 |
+
|
| 607 |
+
[[package]]
|
| 608 |
+
name = "jupyterlab-widgets"
|
| 609 |
+
version = "3.0.16"
|
| 610 |
+
source = { registry = "https://pypi.org/simple" }
|
| 611 |
+
sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" }
|
| 612 |
+
wheels = [
|
| 613 |
+
{ url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" },
|
| 614 |
+
]
|
| 615 |
+
|
| 616 |
[[package]]
|
| 617 |
name = "markdown-it-py"
|
| 618 |
version = "4.2.0"
|
|
|
|
| 644 |
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
| 645 |
]
|
| 646 |
|
| 647 |
+
[[package]]
|
| 648 |
+
name = "matplotlib-inline"
|
| 649 |
+
version = "0.2.2"
|
| 650 |
+
source = { registry = "https://pypi.org/simple" }
|
| 651 |
+
dependencies = [
|
| 652 |
+
{ name = "traitlets" },
|
| 653 |
+
]
|
| 654 |
+
sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" }
|
| 655 |
+
wheels = [
|
| 656 |
+
{ url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" },
|
| 657 |
+
]
|
| 658 |
+
|
| 659 |
[[package]]
|
| 660 |
name = "mdurl"
|
| 661 |
version = "0.1.2"
|
|
|
|
| 674 |
{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
|
| 675 |
]
|
| 676 |
|
| 677 |
+
[[package]]
|
| 678 |
+
name = "nest-asyncio2"
|
| 679 |
+
version = "1.7.2"
|
| 680 |
+
source = { registry = "https://pypi.org/simple" }
|
| 681 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b4/73/731debf26e27e0a0323d7bda270dc2f634b398e38f040a09da1f4351d0aa/nest_asyncio2-1.7.2.tar.gz", hash = "sha256:1921d70b92cc4612c374928d081552efb59b83d91b2b789d935c665fa01729a8", size = 14743, upload-time = "2026-02-13T00:34:04.386Z" }
|
| 682 |
+
wheels = [
|
| 683 |
+
{ url = "https://files.pythonhosted.org/packages/c5/3c/3179b85b0e1c3659f0369940200cd6d0fa900e6cefcc7ea0bc6dd0e29ffb/nest_asyncio2-1.7.2-py3-none-any.whl", hash = "sha256:f5dfa702f3f81f6a03857e9a19e2ba578c0946a4ad417b4c50a24d7ba641fe01", size = 7843, upload-time = "2026-02-13T00:34:02.691Z" },
|
| 684 |
+
]
|
| 685 |
+
|
| 686 |
[[package]]
|
| 687 |
name = "networkx"
|
| 688 |
version = "3.6.1"
|
|
|
|
| 697 |
version = "0.1.0"
|
| 698 |
source = { virtual = "." }
|
| 699 |
dependencies = [
|
| 700 |
+
{ name = "accelerate" },
|
| 701 |
{ name = "flash-attn" },
|
| 702 |
+
{ name = "gguf" },
|
| 703 |
{ name = "gradio" },
|
| 704 |
{ name = "huggingface-hub" },
|
| 705 |
+
{ name = "ipykernel" },
|
| 706 |
+
{ name = "ipython" },
|
| 707 |
+
{ name = "ipywidgets" },
|
| 708 |
+
{ name = "openai" },
|
| 709 |
{ name = "torch" },
|
| 710 |
{ name = "transformers" },
|
| 711 |
]
|
| 712 |
|
| 713 |
[package.metadata]
|
| 714 |
requires-dist = [
|
| 715 |
+
{ name = "accelerate", specifier = ">=1.14.0" },
|
| 716 |
{ name = "flash-attn", url = "https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.28/flash_attn-2.8.3+cu130torch2.9-cp312-cp312-win_amd64.whl" },
|
| 717 |
+
{ name = "gguf", specifier = ">=0.19.0" },
|
| 718 |
{ name = "gradio" },
|
| 719 |
{ name = "huggingface-hub" },
|
| 720 |
+
{ name = "ipykernel", specifier = ">=7.3.0" },
|
| 721 |
+
{ name = "ipython", specifier = ">=9.15.0" },
|
| 722 |
+
{ name = "ipywidgets", specifier = ">=8.1.8" },
|
| 723 |
+
{ name = "openai", specifier = ">=2.46.0" },
|
| 724 |
+
{ name = "torch", specifier = "==2.9.1", index = "https://download.pytorch.org/whl/cu130" },
|
| 725 |
+
{ name = "transformers" },
|
| 726 |
]
|
| 727 |
|
| 728 |
[[package]]
|
|
|
|
| 893 |
{ url = "https://files.pythonhosted.org/packages/86/91/8b486ba85f71a2859dd705a4ec6aab38c37a389b8b7f94343db027732999/nvidia_nvtx-13.0.39-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cddd2e08b35144f1000631c3880c9ebbcb8a2863d762e76f92d47d30ecaf87cc", size = 148037, upload-time = "2025-08-04T10:18:31.763Z" },
|
| 894 |
]
|
| 895 |
|
| 896 |
+
[[package]]
|
| 897 |
+
name = "openai"
|
| 898 |
+
version = "2.46.0"
|
| 899 |
+
source = { registry = "https://pypi.org/simple" }
|
| 900 |
+
dependencies = [
|
| 901 |
+
{ name = "anyio" },
|
| 902 |
+
{ name = "distro" },
|
| 903 |
+
{ name = "httpx" },
|
| 904 |
+
{ name = "jiter" },
|
| 905 |
+
{ name = "pydantic" },
|
| 906 |
+
{ name = "sniffio" },
|
| 907 |
+
{ name = "tqdm" },
|
| 908 |
+
{ name = "typing-extensions" },
|
| 909 |
+
]
|
| 910 |
+
sdist = { url = "https://files.pythonhosted.org/packages/af/ac/f725c4efbda8657d02be684607e5a2e5ce362e4790fdbcbdfb7c15018647/openai-2.46.0.tar.gz", hash = "sha256:0421e0735ac41451cad894af4cddf0435bfbf8cbc538ac0e15b3c062f2ddc06a", size = 1114628, upload-time = "2026-07-17T02:48:06.05Z" }
|
| 911 |
+
wheels = [
|
| 912 |
+
{ url = "https://files.pythonhosted.org/packages/ea/7b/206238ebcb50b235942b1c66dba4974776f2057402a8d91c399be587d66a/openai-2.46.0-py3-none-any.whl", hash = "sha256:672381db55efb3a1e2610f29304c130cccdd0b319bace4d492b2443cb64c1e7c", size = 1637556, upload-time = "2026-07-17T02:48:03.695Z" },
|
| 913 |
+
]
|
| 914 |
+
|
| 915 |
[[package]]
|
| 916 |
name = "orjson"
|
| 917 |
version = "3.11.9"
|
|
|
|
| 965 |
{ url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" },
|
| 966 |
]
|
| 967 |
|
| 968 |
+
[[package]]
|
| 969 |
+
name = "parso"
|
| 970 |
+
version = "0.8.7"
|
| 971 |
+
source = { registry = "https://pypi.org/simple" }
|
| 972 |
+
sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824, upload-time = "2026-05-01T23:13:02.138Z" }
|
| 973 |
+
wheels = [
|
| 974 |
+
{ url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025, upload-time = "2026-05-01T23:12:58.867Z" },
|
| 975 |
+
]
|
| 976 |
+
|
| 977 |
+
[[package]]
|
| 978 |
+
name = "pexpect"
|
| 979 |
+
version = "4.9.0"
|
| 980 |
+
source = { registry = "https://pypi.org/simple" }
|
| 981 |
+
dependencies = [
|
| 982 |
+
{ name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
|
| 983 |
+
]
|
| 984 |
+
sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" }
|
| 985 |
+
wheels = [
|
| 986 |
+
{ url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" },
|
| 987 |
+
]
|
| 988 |
+
|
| 989 |
[[package]]
|
| 990 |
name = "pillow"
|
| 991 |
version = "12.3.0"
|
|
|
|
| 1003 |
{ url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" },
|
| 1004 |
]
|
| 1005 |
|
| 1006 |
+
[[package]]
|
| 1007 |
+
name = "platformdirs"
|
| 1008 |
+
version = "4.10.1"
|
| 1009 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1010 |
+
sdist = { url = "https://files.pythonhosted.org/packages/52/cd/4f25b2f95b23f5d2c9c1fe43e49841bff5800562149b2666afc09309aa8f/platformdirs-4.10.1.tar.gz", hash = "sha256:ceab4084426fe6319ce18e86deada8ab1b7487c7aee7040c55e277c9ae793695", size = 31678, upload-time = "2026-07-18T03:53:43.808Z" }
|
| 1011 |
+
wheels = [
|
| 1012 |
+
{ url = "https://files.pythonhosted.org/packages/ec/73/6fd0bb9ce84138c3857f12e9de63bc901852975a092d545f18087a204aa2/platformdirs-4.10.1-py3-none-any.whl", hash = "sha256:0e4eff26be2d75293977f7cddc153fd9b8eaa7fb0c7b64ffe4076cb443117443", size = 22906, upload-time = "2026-07-18T03:53:42.576Z" },
|
| 1013 |
+
]
|
| 1014 |
+
|
| 1015 |
+
[[package]]
|
| 1016 |
+
name = "prompt-toolkit"
|
| 1017 |
+
version = "3.0.52"
|
| 1018 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1019 |
+
dependencies = [
|
| 1020 |
+
{ name = "wcwidth" },
|
| 1021 |
+
]
|
| 1022 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" }
|
| 1023 |
+
wheels = [
|
| 1024 |
+
{ url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" },
|
| 1025 |
+
]
|
| 1026 |
+
|
| 1027 |
+
[[package]]
|
| 1028 |
+
name = "psutil"
|
| 1029 |
+
version = "7.2.2"
|
| 1030 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1031 |
+
sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" }
|
| 1032 |
+
wheels = [
|
| 1033 |
+
{ url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" },
|
| 1034 |
+
{ url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" },
|
| 1035 |
+
{ url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" },
|
| 1036 |
+
{ url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" },
|
| 1037 |
+
{ url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" },
|
| 1038 |
+
{ url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" },
|
| 1039 |
+
{ url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" },
|
| 1040 |
+
{ url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
|
| 1041 |
+
]
|
| 1042 |
+
|
| 1043 |
+
[[package]]
|
| 1044 |
+
name = "ptyprocess"
|
| 1045 |
+
version = "0.7.0"
|
| 1046 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1047 |
+
sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" }
|
| 1048 |
+
wheels = [
|
| 1049 |
+
{ url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" },
|
| 1050 |
+
]
|
| 1051 |
+
|
| 1052 |
+
[[package]]
|
| 1053 |
+
name = "pure-eval"
|
| 1054 |
+
version = "0.2.3"
|
| 1055 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1056 |
+
sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" }
|
| 1057 |
+
wheels = [
|
| 1058 |
+
{ url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" },
|
| 1059 |
+
]
|
| 1060 |
+
|
| 1061 |
+
[[package]]
|
| 1062 |
+
name = "pycparser"
|
| 1063 |
+
version = "3.0"
|
| 1064 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1065 |
+
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
|
| 1066 |
+
wheels = [
|
| 1067 |
+
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
|
| 1068 |
+
]
|
| 1069 |
+
|
| 1070 |
[[package]]
|
| 1071 |
name = "pydantic"
|
| 1072 |
version = "2.13.4"
|
|
|
|
| 1179 |
]
|
| 1180 |
|
| 1181 |
[[package]]
|
| 1182 |
+
name = "pyzmq"
|
| 1183 |
+
version = "27.1.0"
|
| 1184 |
source = { registry = "https://pypi.org/simple" }
|
| 1185 |
dependencies = [
|
| 1186 |
+
{ name = "cffi", marker = "implementation_name == 'pypy'" },
|
|
|
|
|
|
|
|
|
|
| 1187 |
]
|
| 1188 |
+
sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" }
|
| 1189 |
wheels = [
|
| 1190 |
+
{ url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" },
|
| 1191 |
+
{ url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" },
|
| 1192 |
+
{ url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" },
|
| 1193 |
+
{ url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" },
|
| 1194 |
+
{ url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" },
|
| 1195 |
+
{ url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" },
|
| 1196 |
+
{ url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" },
|
| 1197 |
+
{ url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" },
|
| 1198 |
+
{ url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" },
|
| 1199 |
+
{ url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" },
|
| 1200 |
]
|
| 1201 |
|
| 1202 |
[[package]]
|
|
|
|
| 1323 |
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
| 1324 |
]
|
| 1325 |
|
| 1326 |
+
[[package]]
|
| 1327 |
+
name = "sniffio"
|
| 1328 |
+
version = "1.3.1"
|
| 1329 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1330 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
| 1331 |
+
wheels = [
|
| 1332 |
+
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
| 1333 |
+
]
|
| 1334 |
+
|
| 1335 |
+
[[package]]
|
| 1336 |
+
name = "stack-data"
|
| 1337 |
+
version = "0.6.3"
|
| 1338 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1339 |
+
dependencies = [
|
| 1340 |
+
{ name = "asttokens" },
|
| 1341 |
+
{ name = "executing" },
|
| 1342 |
+
{ name = "pure-eval" },
|
| 1343 |
+
]
|
| 1344 |
+
sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" }
|
| 1345 |
+
wheels = [
|
| 1346 |
+
{ url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" },
|
| 1347 |
+
]
|
| 1348 |
+
|
| 1349 |
[[package]]
|
| 1350 |
name = "starlette"
|
| 1351 |
version = "1.3.1"
|
|
|
|
| 1408 |
|
| 1409 |
[[package]]
|
| 1410 |
name = "torch"
|
| 1411 |
+
version = "2.9.1+cu130"
|
| 1412 |
source = { registry = "https://download.pytorch.org/whl/cu130" }
|
| 1413 |
dependencies = [
|
| 1414 |
{ name = "filelock" },
|
|
|
|
| 1436 |
{ name = "typing-extensions" },
|
| 1437 |
]
|
| 1438 |
wheels = [
|
| 1439 |
+
{ url = "https://download-r2.pytorch.org/whl/cu130/torch-2.9.1%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:6e7f84cb10c7e7d9f862c318f056d64840544ab4f0bcbf8cf7ed6047fe04051f", upload-time = "2026-01-26T16:37:40Z" },
|
| 1440 |
+
{ url = "https://download-r2.pytorch.org/whl/cu130/torch-2.9.1%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e70e1b18881e6b3c1ce402d0a989da39f956a3a057526e03c354df23d704ce9b", upload-time = "2026-01-26T16:37:43Z" },
|
| 1441 |
+
{ url = "https://download-r2.pytorch.org/whl/cu130/torch-2.9.1%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:cd3232a562ad2a2699d48130255e1b24c07dfe694a40dcd24fad683c752de121", upload-time = "2026-01-26T16:37:50Z" },
|
| 1442 |
+
]
|
| 1443 |
+
|
| 1444 |
+
[[package]]
|
| 1445 |
+
name = "tornado"
|
| 1446 |
+
version = "6.5.7"
|
| 1447 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1448 |
+
sdist = { url = "https://files.pythonhosted.org/packages/64/24/95ec527ad67b76d59299e5465b3935d05e4294b7e0290a3924b7487df30b/tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2", size = 519252, upload-time = "2026-06-08T17:34:51.232Z" }
|
| 1449 |
+
wheels = [
|
| 1450 |
+
{ url = "https://files.pythonhosted.org/packages/02/dc/c7043cab6fed8ae159fc1923ce829ada35c4dbd797d408a43858ffaf9639/tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163", size = 448543, upload-time = "2026-06-08T17:34:38.052Z" },
|
| 1451 |
+
{ url = "https://files.pythonhosted.org/packages/92/4f/090b1431e5a43df696feceffc268c5383cc079ecb5f08ce58f917109aafe/tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100", size = 446707, upload-time = "2026-06-08T17:34:39.594Z" },
|
| 1452 |
+
{ url = "https://files.pythonhosted.org/packages/37/d8/ef374952fd5da67d4463122c2b8e5a96536ec10b4b339254c6dcde81d01c/tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972", size = 449774, upload-time = "2026-06-08T17:34:41.204Z" },
|
| 1453 |
+
{ url = "https://files.pythonhosted.org/packages/35/37/d434c73f4c6e014b745b9b37085f34f40c022f007efff3d7fe65991899f3/tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b", size = 450745, upload-time = "2026-06-08T17:34:42.531Z" },
|
| 1454 |
+
{ url = "https://files.pythonhosted.org/packages/b6/2b/56b9aff361d7f1ab728a805ec7d7ea835f8807afa9f5cc690ea0e630efb9/tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92", size = 450578, upload-time = "2026-06-08T17:34:43.787Z" },
|
| 1455 |
+
{ url = "https://files.pythonhosted.org/packages/02/30/a7444fb23aa76860a14198fab96ac79f1866b0a6e19e26c4381b0938e50f/tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5", size = 449985, upload-time = "2026-06-08T17:34:45.326Z" },
|
| 1456 |
+
{ url = "https://files.pythonhosted.org/packages/5c/42/5f0e56c01e8d9d36f4e23f367b85ae6cae0c1ecddd5e6977d8388ad27488/tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4", size = 451047, upload-time = "2026-06-08T17:34:46.784Z" },
|
| 1457 |
+
{ url = "https://files.pythonhosted.org/packages/c9/a4/b393076ffb21b469eec5b328a0534cf03a3b90bfc6b1f09507cdd075d938/tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4", size = 451485, upload-time = "2026-06-08T17:34:48.248Z" },
|
| 1458 |
+
{ url = "https://files.pythonhosted.org/packages/71/2e/7b1c769803121b809112cf9a00681c472eae1d80e32d7ec0e0bd61d0d0e1/tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796", size = 450506, upload-time = "2026-06-08T17:34:49.702Z" },
|
| 1459 |
]
|
| 1460 |
|
| 1461 |
[[package]]
|
|
|
|
| 1470 |
{ url = "https://files.pythonhosted.org/packages/48/5d/acf5905c36149bbaec41ccf7f2b68814647347b72075ac0b1fe3022fdc73/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", size = 78351, upload-time = "2024-08-03T22:35:36.644Z" },
|
| 1471 |
]
|
| 1472 |
|
| 1473 |
+
[[package]]
|
| 1474 |
+
name = "traitlets"
|
| 1475 |
+
version = "5.15.1"
|
| 1476 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1477 |
+
sdist = { url = "https://files.pythonhosted.org/packages/57/a9/a2584b8313b89f94869ddb3c4074617a691de1812a614d2d50e32ca5a7a6/traitlets-5.15.1.tar.gz", hash = "sha256:7b1c07854fe25acb39e009bae49f11b79ff6cbb2f27999104e9110e7a6b53722", size = 163344, upload-time = "2026-06-03T12:26:06.181Z" }
|
| 1478 |
+
wheels = [
|
| 1479 |
+
{ url = "https://files.pythonhosted.org/packages/96/8d/1080ee4c231f361b6ce4470d556c8c435b67c7e0753aaa641497ee92f88b/traitlets-5.15.1-py3-none-any.whl", hash = "sha256:770a53705f84b81ac107e83a1b3328ff2dae16094d8fc3cfc004e4b22dfd8e92", size = 85858, upload-time = "2026-06-03T12:26:04.395Z" },
|
| 1480 |
+
]
|
| 1481 |
+
|
| 1482 |
[[package]]
|
| 1483 |
name = "transformers"
|
| 1484 |
version = "5.14.1"
|
|
|
|
| 1501 |
|
| 1502 |
[[package]]
|
| 1503 |
name = "triton"
|
| 1504 |
+
version = "3.5.1"
|
| 1505 |
source = { registry = "https://pypi.org/simple" }
|
| 1506 |
wheels = [
|
| 1507 |
+
{ url = "https://files.pythonhosted.org/packages/db/53/2bcc46879910991f09c063eea07627baef2bc62fe725302ba8f46a2c1ae5/triton-3.5.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:275a045b6ed670dd1bd005c3e6c2d61846c74c66f4512d6f33cc027b11de8fd4", size = 159940689, upload-time = "2025-11-11T17:51:55.938Z" },
|
| 1508 |
+
{ url = "https://files.pythonhosted.org/packages/f2/50/9a8358d3ef58162c0a415d173cfb45b67de60176e1024f71fbc4d24c0b6d/triton-3.5.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d2c6b915a03888ab931a9fd3e55ba36785e1fe70cbea0b40c6ef93b20fc85232", size = 170470207, upload-time = "2025-11-11T17:41:00.253Z" },
|
| 1509 |
]
|
| 1510 |
|
| 1511 |
[[package]]
|
|
|
|
| 1574 |
wheels = [
|
| 1575 |
{ url = "https://files.pythonhosted.org/packages/45/ec/dbb7e5a6b91f86bfb9eb7d2988a2730907b6a729875b949c7f022e8b88fa/uvicorn-0.51.0-py3-none-any.whl", hash = "sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b", size = 73219, upload-time = "2026-07-08T10:59:04.44Z" },
|
| 1576 |
]
|
| 1577 |
+
|
| 1578 |
+
[[package]]
|
| 1579 |
+
name = "wcwidth"
|
| 1580 |
+
version = "0.8.2"
|
| 1581 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1582 |
+
sdist = { url = "https://files.pythonhosted.org/packages/34/74/c6428f875774288bec1396f5bfcbc2d925700a4dad61727fd5f2b12f249d/wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda", size = 1466253, upload-time = "2026-06-29T18:11:11.601Z" }
|
| 1583 |
+
wheels = [
|
| 1584 |
+
{ url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" },
|
| 1585 |
+
]
|
| 1586 |
+
|
| 1587 |
+
[[package]]
|
| 1588 |
+
name = "widgetsnbextension"
|
| 1589 |
+
version = "4.0.15"
|
| 1590 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1591 |
+
sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" }
|
| 1592 |
+
wheels = [
|
| 1593 |
+
{ url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" },
|
| 1594 |
+
]
|