staghado Bapt120 commited on
Commit
b71010f
·
0 Parent(s):

LightOnOCR-3 release

Browse files

Co-authored-by: Bapt120 <Bapt120@users.noreply.huggingface.co>

.eval_results/olmocrbench.yaml ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - dataset:
2
+ id: allenai/olmOCR-bench
3
+ task_id: overall
4
+ value: 86.3
5
+ date: '2026-10-08'
6
+ source:
7
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
8
+ name: LightOnOCR-3 results
9
+ user: staghado
10
+ - dataset:
11
+ id: allenai/olmOCR-bench
12
+ task_id: arxiv_math
13
+ value: 91.1
14
+ date: '2026-10-08'
15
+ source:
16
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
17
+ name: LightOnOCR-3 results
18
+ user: staghado
19
+ - dataset:
20
+ id: allenai/olmOCR-bench
21
+ task_id: old_scans_math
22
+ value: 89.1
23
+ date: '2026-10-08'
24
+ source:
25
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
26
+ name: LightOnOCR-3 results
27
+ user: staghado
28
+ - dataset:
29
+ id: allenai/olmOCR-bench
30
+ task_id: table_tests
31
+ value: 90.2
32
+ date: '2026-10-08'
33
+ source:
34
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
35
+ name: LightOnOCR-3 results
36
+ user: staghado
37
+ - dataset:
38
+ id: allenai/olmOCR-bench
39
+ task_id: old_scans
40
+ value: 52.5
41
+ date: '2026-10-08'
42
+ source:
43
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
44
+ name: LightOnOCR-3 results
45
+ user: staghado
46
+ - dataset:
47
+ id: allenai/olmOCR-bench
48
+ task_id: headers_footers
49
+ value: 89.2
50
+ date: '2026-10-08'
51
+ source:
52
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
53
+ name: LightOnOCR-3 results
54
+ user: staghado
55
+ - dataset:
56
+ id: allenai/olmOCR-bench
57
+ task_id: multi_column
58
+ value: 85.6
59
+ date: '2026-10-08'
60
+ source:
61
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
62
+ name: LightOnOCR-3 results
63
+ user: staghado
64
+ - dataset:
65
+ id: allenai/olmOCR-bench
66
+ task_id: long_tiny_text
67
+ value: 93.2
68
+ date: '2026-10-08'
69
+ source:
70
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
71
+ name: LightOnOCR-3 results
72
+ user: staghado
73
+ - dataset:
74
+ id: allenai/olmOCR-bench
75
+ task_id: baseline
76
+ value: 99.5
77
+ date: '2026-10-08'
78
+ source:
79
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
80
+ name: LightOnOCR-3 results
81
+ user: staghado
.eval_results/parsebench.yaml ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - dataset:
2
+ id: llamaindex/ParseBench
3
+ task_id: mean
4
+ value: 75.1
5
+ date: '2026-10-08'
6
+ source:
7
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
8
+ name: LightOnOCR-3 results
9
+ user: staghado
10
+ - dataset:
11
+ id: llamaindex/ParseBench
12
+ task_id: table
13
+ value: 83.8
14
+ date: '2026-10-08'
15
+ source:
16
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
17
+ name: LightOnOCR-3 results
18
+ user: staghado
19
+ - dataset:
20
+ id: llamaindex/ParseBench
21
+ task_id: chart
22
+ value: 66.1
23
+ date: '2026-10-08'
24
+ source:
25
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
26
+ name: LightOnOCR-3 results
27
+ user: staghado
28
+ - dataset:
29
+ id: llamaindex/ParseBench
30
+ task_id: text_content
31
+ value: 89.9
32
+ date: '2026-10-08'
33
+ source:
34
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
35
+ name: LightOnOCR-3 results
36
+ user: staghado
37
+ - dataset:
38
+ id: llamaindex/ParseBench
39
+ task_id: text_formatting
40
+ value: 67.6
41
+ date: '2026-10-08'
42
+ source:
43
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
44
+ name: LightOnOCR-3 results
45
+ user: staghado
46
+ - dataset:
47
+ id: llamaindex/ParseBench
48
+ task_id: layout
49
+ value: 68.3
50
+ date: '2026-10-08'
51
+ source:
52
+ url: https://huggingface.co/blog/lightonai/lightonocr-3
53
+ name: LightOnOCR-3 results
54
+ user: staghado
.gitattributes ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
37
+ lightonocr3_logo.png filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ pipeline_tag: image-text-to-text
4
+ library_name: transformers
5
+ tags:
6
+ - ocr
7
+ - document-understanding
8
+ - visual-grounding
9
+ - vision-language
10
+ - tables
11
+ - forms
12
+ ---
13
+
14
+ <div align="center">
15
+
16
+ <img src="https://huggingface.co/lightonai/LightOnOCR-3-4B/resolve/main/lightonocr3_logo.png" alt="LightOnOCR-3 logo" width="600">
17
+
18
+ [![Website](https://img.shields.io/badge/LightOn-Website-blue?logo=google-chrome)](https://lighton.ai)
19
+ [![LinkedIn](https://img.shields.io/badge/LightOn-LinkedIn-0A66C2?logo=linkedin)](https://www.linkedin.com/company/lighton/)
20
+ [![X](https://img.shields.io/badge/@LightOnIO-X-black?logo=x)](https://x.com/LightOnIO)
21
+
22
+ 📄 [Paper](https://arxiv.org/pdf/2601.14251) | 📝 [Blog](https://huggingface.co/blog/lightonai/lightonocr-3) | 🚀 [Demo](https://huggingface.co/spaces/lightonai/LightOnOCR-3-Demo) | 💻 [GitHub](https://github.com/lightonai/LightOnOCR) | 🤗 [LightOnOCR-2-1B](https://huggingface.co/lightonai/LightOnOCR-2-1B)
23
+
24
+ </div>
25
+
26
+ # LightOnOCR-3-4B
27
+
28
+ **Best OCR model.** LightOnOCR-3-4B is the largest and most accurate model of the LightOnOCR-3 family, **[LightOn's](https://lighton.ai)** new generation of lightweight end-to-end OCR models with layout and visual understanding. We recommend it for most OCR tasks.
29
+
30
+ ## About LightOnOCR-3
31
+
32
+ LightOnOCR-3 is a new family of highly performant lightweight OCR models. Compared to the previous generation, they bring significant improvements in speed and transcription quality and introduce new visual understanding features: the models now output bounding box coordinates with labels for all visual elements of a document, short descriptions of images, and the numerical data of figures and charts. With these capabilities, they offer a ready-to-use, easier-to-maintain alternative to complex document understanding pipelines.
33
+
34
+ The models come in three sizes. The 1B keeps the LightOnOCR-2-1B architecture, while the 0.8B and 4B adopt the Qwen3.5 vision-language architecture, which simplifies integration with existing tools and brings a significant speed-up. All models are released under the Apache 2.0 license for research and commercial use.
35
+
36
+ ## Highlights
37
+
38
+ * 📝 **Transcription mode:** call the model with an empty prompt and get the full page text, as before. Switching from LightOnOCR-2 requires no change
39
+ * 📍 **Grounding mode:** call it with the `grounding` prompt and every block comes back with a label and a bounding box
40
+ * 🖼️ **Visual understanding:** images get a short description, charts become a table of their data points
41
+ * 🧠 **End-to-End:** one model instead of a document understanding pipeline, easier to deploy and maintain
42
+ * 🧾 **Versatile:** handles tables, receipts, forms, charts, multi-column layouts, and math notation
43
+
44
+ ---
45
+
46
+ ## Model Variants
47
+
48
+ | Variant | Description |
49
+ |---------|-------------|
50
+ | **[LightOnOCR-3-4B](https://huggingface.co/lightonai/LightOnOCR-3-4B)** | Best OCR model, recommended for most tasks |
51
+ | **[LightOnOCR-3-1B](https://huggingface.co/lightonai/LightOnOCR-3-1B)** | LightOnOCR-2 architecture, drop-in upgrade for existing deployments |
52
+ | **[LightOnOCR-3-0.8B](https://huggingface.co/lightonai/LightOnOCR-3-0.8B)** | Fast and Efficient model |
53
+
54
+ ---
55
+
56
+ ## Prompt Modes
57
+
58
+ The models can be used as before in a **transcription-only mode**: called with an empty prompt (`""`; the image only), they output all textual elements of the page as markdown. If you are currently using our previous models, switching to LightOnOCR-3 requires no change, as this is the default behavior.
59
+
60
+ The new usage mode is to call the models with the `grounding` prompt. They then output the new vision features alongside the transcribed text. Each block of content is prepended with a placeholder giving its type and its bounding box, in page coordinates normalized to 0–1000:
61
+
62
+ ```
63
+ ![title](80,70,920,140)
64
+ # Company report 2025
65
+
66
+ ![text](80,175,920,225)
67
+ Revenue rose 20%.
68
+
69
+ ![image](80,285,460,545)
70
+ A solar-powered factory.
71
+
72
+ ![chart](540,285,920,545)
73
+ <table>
74
+ <tr><th>Year</th><th>Revenue</th></tr>
75
+ <tr><td>2024</td><td>€10M</td></tr>
76
+ <tr><td>2025</td><td>€12M</td></tr>
77
+ </table>
78
+ ```
79
+
80
+ * **Text blocks** contain the transcribed content of paragraphs, titles and other text elements.
81
+ * **Image blocks** pair a bounding box with a short description, making visual content accessible to retrieval and question-answering pipelines.
82
+ * **Chart blocks** contain an HTML table of the data points extracted from the figure, turning visual information into structured data.
83
+
84
+ The placeholder label is one of:
85
+
86
+ | Label | Definition |
87
+ |---|---|
88
+ | `text` | Ordinary body text and paragraphs. |
89
+ | `title` | Document, section, or paragraph headings. |
90
+ | `list` | Bulleted or numbered-list content. |
91
+ | `header` | Text in the page header. |
92
+ | `footer` | Text in the page footer. |
93
+ | `page_number` | Page-number regions. |
94
+ | `footnote` | Footnote text. |
95
+ | `caption` | Captions associated with figures, tables, or other visual elements. |
96
+ | `formula` | Mathematical formulas and equations. |
97
+ | `code` | Code blocks or code-like text. |
98
+ | `table` | Table regions and their content. |
99
+ | `image` | Illustrations, photographs, and other image regions. |
100
+ | `chart` | Charts, graphs, and plotted visual data. |
101
+ | `header_image` | Image regions in page headers, such as logos. |
102
+ | `footer_image` | Image regions in page footers. |
103
+ | `aside_text` | Marginal or side text outside the main text flow. |
104
+ | `+` suffix | Marks a continuation block, not a separate semantic class. |
105
+
106
+ Grounding adds about 25% output tokens over plain transcription (1,433 vs 1,158 tokens per page on average for the 4B). Other instructions are out of distribution: use the empty prompt (`""`) or `grounding`.
107
+
108
+ ---
109
+
110
+ ## Usage with Transformers
111
+
112
+ > **Note:** LightOnOCR-3-4B loads with the Qwen3.5 classes (Transformers ≥ 5.2, saved with 5.5.4). It is trained and evaluated with thinking disabled, which is the chat template default.
113
+
114
+ ```bash
115
+ uv pip install "transformers>=5.5.4" pillow pypdfium2
116
+ ```
117
+
118
+ ```python
119
+ import torch
120
+ from transformers import AutoProcessor, Qwen3_5ForConditionalGeneration
121
+
122
+ model_id = "lightonai/LightOnOCR-3-4B"
123
+ model = Qwen3_5ForConditionalGeneration.from_pretrained(model_id, dtype=torch.bfloat16, device_map="auto")
124
+ processor = AutoProcessor.from_pretrained(model_id)
125
+
126
+ url = "https://huggingface.co/datasets/hf-internal-testing/fixtures_ocr/resolve/main/SROIE-receipt.jpeg"
127
+ prompt = "grounding" # or "" for transcription-only
128
+
129
+ content = [{"type": "image", "url": url}]
130
+ if prompt:
131
+ content.append({"type": "text", "text": prompt})
132
+ conversation = [{"role": "user", "content": content}]
133
+
134
+ inputs = processor.apply_chat_template(
135
+ conversation,
136
+ add_generation_prompt=True,
137
+ tokenize=True,
138
+ return_dict=True,
139
+ return_tensors="pt",
140
+ enable_thinking=False,
141
+ ).to(model.device)
142
+
143
+ output_ids = model.generate(**inputs, max_new_tokens=1024, do_sample=True, temperature=0.2)
144
+ generated_ids = output_ids[0, inputs["input_ids"].shape[1]:]
145
+ print(processor.decode(generated_ids, skip_special_tokens=True))
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Usage with vLLM
151
+
152
+ ```bash
153
+ vllm serve lightonai/LightOnOCR-3-4B \
154
+ --trust-remote-code \
155
+ --default-chat-template-kwargs '{"enable_thinking": false}'
156
+ ```
157
+
158
+ ```python
159
+ import base64
160
+ import io
161
+
162
+ import pypdfium2 as pdfium
163
+ import requests
164
+
165
+ ENDPOINT = "http://localhost:8000/v1/chat/completions"
166
+ MODEL = "lightonai/LightOnOCR-3-4B"
167
+ PROMPT = "grounding" # or "" for transcription-only
168
+
169
+ # Render the first page of a PDF at 400 DPI, then resize to a longest edge of 2048 px (aspect ratio preserved)
170
+ pdf = pdfium.PdfDocument(requests.get("https://arxiv.org/pdf/2412.13663").content)
171
+ pil_image = pdf[0].render(scale=400 / 72).to_pil()
172
+ pil_image.thumbnail((2048, 2048))
173
+
174
+ buffer = io.BytesIO()
175
+ pil_image.save(buffer, format="PNG")
176
+ image_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
177
+
178
+ content = [{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}}]
179
+ if PROMPT:
180
+ content.append({"type": "text", "text": PROMPT})
181
+
182
+ payload = {
183
+ "model": MODEL,
184
+ "messages": [{"role": "user", "content": content}],
185
+ "max_tokens": 4096,
186
+ "temperature": 0.2,
187
+ "top_p": 0.9,
188
+ }
189
+
190
+ response = requests.post(ENDPOINT, json=payload)
191
+ print(response.json()["choices"][0]["message"]["content"])
192
+ ```
193
+
194
+ The [LightOnOCR repository](https://github.com/lightonai/LightOnOCR) on GitHub provides a minimal client, CLI and viewer for the models served with vLLM, plus the code to reproduce our benchmarks.
195
+
196
+ ---
197
+
198
+ ## Rendering and Preprocessing Tips
199
+
200
+ * Render PDFs at 400 DPI to images using a target longest dimension of **2048px**
201
+ * Maintain aspect ratio to preserve text geometry
202
+
203
+ ---
204
+
205
+ ## License
206
+
207
+ Apache License 2.0
208
+
209
+ ---
210
+
211
+ ## Citation
212
+
213
+ ```bibtex
214
+ @misc{lightonocr2_2026,
215
+ title = {LightOnOCR: A 1B End-to-End Multilingual Vision-Language Model for State-of-the-Art OCR},
216
+ author = {Said Taghadouini and Adrien Cavaill\`{e}s and Baptiste Aubertin},
217
+ year = {2026},
218
+ howpublished = {\url{https://arxiv.org/abs/2601.14251}}
219
+ }
220
+ ```
221
+
222
+ [![Downloads](https://img.shields.io/badge/dynamic/json?url=https://huggingface.co/api/models/lightonai/LightOnOCR-3-4B&query=downloads&label=Downloads&color=blue)](https://huggingface.co/lightonai/LightOnOCR-3-4B)
223
+ [![EU](https://img.shields.io/badge/🇪🇺%20Made%20in-Europe-blue)](https://huggingface.co/lightonai)
README.no_thinking.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Serving default
2
+
3
+ The bundled `chat_template.jinja` defaults to `enable_thinking=false` when the
4
+ caller omits the chat-template kwarg. Serving code should still pass the
5
+ explicit vLLM flag for clarity:
6
+
7
+ ```bash
8
+ --default-chat-template-kwargs '{"enable_thinking": false}'
9
+ ```
10
+
11
+ Passing `enable_thinking=true` explicitly remains an opt-in override.
chat_template.jinja ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- set image_count = namespace(value=0) %}
2
+ {%- set video_count = namespace(value=0) %}
3
+ {%- macro render_content(content, do_vision_count, is_system_content=false) %}
4
+ {%- if content is string %}
5
+ {{- content }}
6
+ {%- elif content is iterable and content is not mapping %}
7
+ {%- for item in content %}
8
+ {%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
9
+ {%- if is_system_content %}
10
+ {{- raise_exception('System message cannot contain images.') }}
11
+ {%- endif %}
12
+ {%- if do_vision_count %}
13
+ {%- set image_count.value = image_count.value + 1 %}
14
+ {%- endif %}
15
+ {%- if add_vision_id %}
16
+ {{- 'Picture ' ~ image_count.value ~ ': ' }}
17
+ {%- endif %}
18
+ {{- '<|vision_start|><|image_pad|><|vision_end|>' }}
19
+ {%- elif 'video' in item or item.type == 'video' %}
20
+ {%- if is_system_content %}
21
+ {{- raise_exception('System message cannot contain videos.') }}
22
+ {%- endif %}
23
+ {%- if do_vision_count %}
24
+ {%- set video_count.value = video_count.value + 1 %}
25
+ {%- endif %}
26
+ {%- if add_vision_id %}
27
+ {{- 'Video ' ~ video_count.value ~ ': ' }}
28
+ {%- endif %}
29
+ {{- '<|vision_start|><|video_pad|><|vision_end|>' }}
30
+ {%- elif 'text' in item %}
31
+ {{- item.text }}
32
+ {%- else %}
33
+ {{- raise_exception('Unexpected item type in content.') }}
34
+ {%- endif %}
35
+ {%- endfor %}
36
+ {%- elif content is none or content is undefined %}
37
+ {{- '' }}
38
+ {%- else %}
39
+ {{- raise_exception('Unexpected content type.') }}
40
+ {%- endif %}
41
+ {%- endmacro %}
42
+ {%- if not messages %}
43
+ {{- raise_exception('No messages provided.') }}
44
+ {%- endif %}
45
+ {%- if tools and tools is iterable and tools is not mapping %}
46
+ {{- '<|im_start|>system\n' }}
47
+ {{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
48
+ {%- for tool in tools %}
49
+ {{- "\n" }}
50
+ {{- tool | tojson }}
51
+ {%- endfor %}
52
+ {{- "\n</tools>" }}
53
+ {{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
54
+ {%- if messages[0].role == 'system' %}
55
+ {%- set content = render_content(messages[0].content, false, true)|trim %}
56
+ {%- if content %}
57
+ {{- '\n\n' + content }}
58
+ {%- endif %}
59
+ {%- endif %}
60
+ {{- '<|im_end|>\n' }}
61
+ {%- else %}
62
+ {%- if messages[0].role == 'system' %}
63
+ {%- set content = render_content(messages[0].content, false, true)|trim %}
64
+ {{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
65
+ {%- endif %}
66
+ {%- endif %}
67
+ {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
68
+ {%- for message in messages[::-1] %}
69
+ {%- set index = (messages|length - 1) - loop.index0 %}
70
+ {%- if ns.multi_step_tool and message.role == "user" %}
71
+ {%- set content = render_content(message.content, false)|trim %}
72
+ {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
73
+ {%- set ns.multi_step_tool = false %}
74
+ {%- set ns.last_query_index = index %}
75
+ {%- endif %}
76
+ {%- endif %}
77
+ {%- endfor %}
78
+ {%- if ns.multi_step_tool %}
79
+ {{- raise_exception('No user query found in messages.') }}
80
+ {%- endif %}
81
+ {%- for message in messages %}
82
+ {%- set content = render_content(message.content, true)|trim %}
83
+ {%- if message.role == "system" %}
84
+ {%- if not loop.first %}
85
+ {{- raise_exception('System message must be at the beginning.') }}
86
+ {%- endif %}
87
+ {%- elif message.role == "user" %}
88
+ {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
89
+ {%- elif message.role == "assistant" %}
90
+ {%- set reasoning_content = '' %}
91
+ {%- if message.reasoning_content is string %}
92
+ {%- set reasoning_content = message.reasoning_content %}
93
+ {%- else %}
94
+ {%- if '</think>' in content %}
95
+ {%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
96
+ {%- set content = content.split('</think>')[-1].lstrip('\n') %}
97
+ {%- endif %}
98
+ {%- endif %}
99
+ {%- set reasoning_content = reasoning_content|trim %}
100
+ {%- if loop.index0 > ns.last_query_index %}
101
+ {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
102
+ {%- else %}
103
+ {{- '<|im_start|>' + message.role + '\n' + content }}
104
+ {%- endif %}
105
+ {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
106
+ {%- for tool_call in message.tool_calls %}
107
+ {%- if tool_call.function is defined %}
108
+ {%- set tool_call = tool_call.function %}
109
+ {%- endif %}
110
+ {%- if loop.first %}
111
+ {%- if content|trim %}
112
+ {{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
113
+ {%- else %}
114
+ {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
115
+ {%- endif %}
116
+ {%- else %}
117
+ {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
118
+ {%- endif %}
119
+ {%- if tool_call.arguments is defined %}
120
+ {%- for args_name, args_value in tool_call.arguments|items %}
121
+ {{- '<parameter=' + args_name + '>\n' }}
122
+ {%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
123
+ {{- args_value }}
124
+ {{- '\n</parameter>\n' }}
125
+ {%- endfor %}
126
+ {%- endif %}
127
+ {{- '</function>\n</tool_call>' }}
128
+ {%- endfor %}
129
+ {%- endif %}
130
+ {{- '<|im_end|>\n' }}
131
+ {%- elif message.role == "tool" %}
132
+ {%- if loop.previtem and loop.previtem.role != "tool" %}
133
+ {{- '<|im_start|>user' }}
134
+ {%- endif %}
135
+ {{- '\n<tool_response>\n' }}
136
+ {{- content }}
137
+ {{- '\n</tool_response>' }}
138
+ {%- if not loop.last and loop.nextitem.role != "tool" %}
139
+ {{- '<|im_end|>\n' }}
140
+ {%- elif loop.last %}
141
+ {{- '<|im_end|>\n' }}
142
+ {%- endif %}
143
+ {%- else %}
144
+ {{- raise_exception('Unexpected message role.') }}
145
+ {%- endif %}
146
+ {%- endfor %}
147
+ {%- if add_generation_prompt %}
148
+ {{- '<|im_start|>assistant\n' }}
149
+ {%- if enable_thinking is not defined or enable_thinking is false %}
150
+ {{- '<think>\n\n</think>\n\n' }}
151
+ {%- else %}
152
+ {{- '<think>\n' }}
153
+ {%- endif %}
154
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Qwen3_5ForConditionalGeneration"
4
+ ],
5
+ "bos_token_id": null,
6
+ "dtype": "bfloat16",
7
+ "eos_token_id": 248046,
8
+ "image_token_id": 248056,
9
+ "model_type": "qwen3_5",
10
+ "pad_token_id": 248044,
11
+ "text_config": {
12
+ "attention_bias": false,
13
+ "attention_dropout": 0.0,
14
+ "attn_output_gate": true,
15
+ "bos_token_id": null,
16
+ "dtype": "bfloat16",
17
+ "eos_token_id": 248044,
18
+ "full_attention_interval": 4,
19
+ "head_dim": 256,
20
+ "hidden_act": "silu",
21
+ "hidden_size": 2560,
22
+ "initializer_range": 0.02,
23
+ "intermediate_size": 9216,
24
+ "layer_types": [
25
+ "linear_attention",
26
+ "linear_attention",
27
+ "linear_attention",
28
+ "full_attention",
29
+ "linear_attention",
30
+ "linear_attention",
31
+ "linear_attention",
32
+ "full_attention",
33
+ "linear_attention",
34
+ "linear_attention",
35
+ "linear_attention",
36
+ "full_attention",
37
+ "linear_attention",
38
+ "linear_attention",
39
+ "linear_attention",
40
+ "full_attention",
41
+ "linear_attention",
42
+ "linear_attention",
43
+ "linear_attention",
44
+ "full_attention",
45
+ "linear_attention",
46
+ "linear_attention",
47
+ "linear_attention",
48
+ "full_attention",
49
+ "linear_attention",
50
+ "linear_attention",
51
+ "linear_attention",
52
+ "full_attention",
53
+ "linear_attention",
54
+ "linear_attention",
55
+ "linear_attention",
56
+ "full_attention"
57
+ ],
58
+ "linear_conv_kernel_dim": 4,
59
+ "linear_key_head_dim": 128,
60
+ "linear_num_key_heads": 16,
61
+ "linear_num_value_heads": 32,
62
+ "linear_value_head_dim": 128,
63
+ "mamba_ssm_dtype": "float32",
64
+ "max_position_embeddings": 262144,
65
+ "mlp_only_layers": [],
66
+ "model_type": "qwen3_5_text",
67
+ "mtp_num_hidden_layers": 0,
68
+ "mtp_use_dedicated_embeddings": false,
69
+ "num_attention_heads": 16,
70
+ "num_hidden_layers": 32,
71
+ "num_key_value_heads": 4,
72
+ "pad_token_id": null,
73
+ "partial_rotary_factor": 0.25,
74
+ "rms_norm_eps": 1e-06,
75
+ "rope_parameters": {
76
+ "mrope_interleaved": true,
77
+ "mrope_section": [
78
+ 11,
79
+ 11,
80
+ 10
81
+ ],
82
+ "partial_rotary_factor": 0.25,
83
+ "rope_theta": 10000000,
84
+ "rope_type": "default"
85
+ },
86
+ "tie_word_embeddings": true,
87
+ "use_cache": true,
88
+ "vocab_size": 248320
89
+ },
90
+ "tie_word_embeddings": true,
91
+ "transformers_version": "5.5.4",
92
+ "video_token_id": 248057,
93
+ "vision_config": {
94
+ "deepstack_visual_indexes": [],
95
+ "depth": 24,
96
+ "dtype": "bfloat16",
97
+ "hidden_act": "gelu_pytorch_tanh",
98
+ "hidden_size": 1024,
99
+ "in_channels": 3,
100
+ "initializer_range": 0.02,
101
+ "intermediate_size": 4096,
102
+ "model_type": "qwen3_5",
103
+ "num_heads": 16,
104
+ "num_position_embeddings": 2304,
105
+ "out_hidden_size": 2560,
106
+ "patch_size": 16,
107
+ "spatial_merge_size": 2,
108
+ "temporal_patch_size": 2
109
+ },
110
+ "vision_end_token_id": 248054,
111
+ "vision_start_token_id": 248053
112
+ }
generation_config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "eos_token_id": 248046,
4
+ "output_attentions": false,
5
+ "output_hidden_states": false,
6
+ "pad_token_id": 248044,
7
+ "transformers_version": "5.5.4",
8
+ "use_cache": true
9
+ }
lightonocr3_logo.png ADDED

Git LFS Details

  • SHA256: 768eee79d378518cfea9ed8d81ba3a53a0cff17ccf4b12c22a9e5ebedde32bcf
  • Pointer size: 132 Bytes
  • Size of remote file: 1.65 MB
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6b0dab74a2dab02d5b10ca56322255024ed8dca35c02746b42e9f296aaeb4e67
3
+ size 9078620536
preprocessor_config.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "size": {
3
+ "longest_edge": 16777216,
4
+ "shortest_edge": 65536
5
+ },
6
+ "patch_size": 16,
7
+ "temporal_patch_size": 2,
8
+ "merge_size": 2,
9
+ "image_mean": [
10
+ 0.5,
11
+ 0.5,
12
+ 0.5
13
+ ],
14
+ "image_std": [
15
+ 0.5,
16
+ 0.5,
17
+ 0.5
18
+ ],
19
+ "processor_class": "Qwen3VLProcessor",
20
+ "image_processor_type": "Qwen2VLImageProcessorFast"
21
+ }
processor_config.json ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor": {
3
+ "do_convert_rgb": true,
4
+ "do_normalize": true,
5
+ "do_rescale": true,
6
+ "do_resize": true,
7
+ "image_mean": [
8
+ 0.5,
9
+ 0.5,
10
+ 0.5
11
+ ],
12
+ "image_processor_type": "Qwen2VLImageProcessor",
13
+ "image_std": [
14
+ 0.5,
15
+ 0.5,
16
+ 0.5
17
+ ],
18
+ "merge_size": 2,
19
+ "patch_size": 16,
20
+ "resample": 3,
21
+ "rescale_factor": 0.00392156862745098,
22
+ "size": {
23
+ "longest_edge": 16777216,
24
+ "shortest_edge": 65536
25
+ },
26
+ "temporal_patch_size": 2
27
+ },
28
+ "processor_class": "Qwen3VLProcessor",
29
+ "video_processor": {
30
+ "do_convert_rgb": true,
31
+ "do_normalize": true,
32
+ "do_rescale": true,
33
+ "do_resize": true,
34
+ "do_sample_frames": true,
35
+ "fps": 2,
36
+ "image_mean": [
37
+ 0.5,
38
+ 0.5,
39
+ 0.5
40
+ ],
41
+ "image_std": [
42
+ 0.5,
43
+ 0.5,
44
+ 0.5
45
+ ],
46
+ "max_frames": 768,
47
+ "merge_size": 2,
48
+ "min_frames": 4,
49
+ "patch_size": 16,
50
+ "resample": 3,
51
+ "rescale_factor": 0.00392156862745098,
52
+ "return_metadata": false,
53
+ "size": {
54
+ "longest_edge": 25165824,
55
+ "shortest_edge": 4096
56
+ },
57
+ "temporal_patch_size": 2,
58
+ "video_processor_type": "Qwen3VLVideoProcessor"
59
+ }
60
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:87a7830d63fcf43bf241c3c5242e96e62dd3fdc29224ca26fed8ea333db72de4
3
+ size 19989343
tokenizer_config.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "audio_bos_token": "<|audio_start|>",
4
+ "audio_eos_token": "<|audio_end|>",
5
+ "audio_token": "<|audio_pad|>",
6
+ "backend": "tokenizers",
7
+ "bos_token": null,
8
+ "clean_up_tokenization_spaces": false,
9
+ "eos_token": "<|im_end|>",
10
+ "errors": "replace",
11
+ "image_token": "<|image_pad|>",
12
+ "is_local": true,
13
+ "model_max_length": 262144,
14
+ "model_specific_special_tokens": {
15
+ "audio_bos_token": "<|audio_start|>",
16
+ "audio_eos_token": "<|audio_end|>",
17
+ "audio_token": "<|audio_pad|>",
18
+ "image_token": "<|image_pad|>",
19
+ "video_token": "<|video_pad|>",
20
+ "vision_bos_token": "<|vision_start|>",
21
+ "vision_eos_token": "<|vision_end|>"
22
+ },
23
+ "pad_token": "<|endoftext|>",
24
+ "pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
25
+ "processor_class": "Qwen3VLProcessor",
26
+ "split_special_tokens": false,
27
+ "tokenizer_class": "TokenizersBackend",
28
+ "unk_token": null,
29
+ "video_token": "<|video_pad|>",
30
+ "vision_bos_token": "<|vision_start|>",
31
+ "vision_eos_token": "<|vision_end|>"
32
+ }
vocab.json ADDED
The diff for this file is too large to render. See raw diff