Buckets:
| Name | Size | Uploaded | Xet hash |
|---|---|---|---|
| extractions | 4 items | ||
| ground_truth | 1 items | ||
| invoices | 174 items | ||
| target | 2 items | ||
| README.md | 8.33 kB xet | 33c75233 |
Lumen Analytics invoices — an extraction evaluation set
Fictional. Every company, person, address, VAT id and IBAN here is invented; every page says so along its edge. Generated for the Docling × Confluent Cloud demo (example 04,
examples/04-invoice-extractionindocling-project/docling-confluent-cloud), and usable on its own to evaluate structured extraction from invoices.
174 supplier invoices of a fictional Munich company, Lumen Analytics GmbH, dated 2025-12-01 to 2026-06-19: 17 vendors, 7 layouts, 139 in EUR, 21 in USD, 7 in GBP, 7 in CHF; 10 scans (rasterized, tilted, noisy, no text layer) and 8 two-page invoices; 749 line items in all. Each comes with its ground truth — what is printed on it, field by field and line by line — because the generator drew it from that.
Files
| path | what |
|---|---|
invoices/<doc_id>.pdf |
the invoices |
ground_truth/manifest.jsonl |
one line per invoice: layout, scanned, pages, and truth |
target/extraction_target.json |
the extraction target the demo sends to Docling: JSON Schema + example + instructions |
target/invoice.schema.json |
its output_schema alone |
extractions/docling/<run>/results.jsonl |
Docling's answer per invoice, verbatim (ExtractionDocumentResult) |
extractions/docling/<run>/scores.jsonl |
per invoice and field: predicted, expected, match |
extractions/docling/<run>/run.json, SCORES.md |
how the run was made; its summary |
This is a private Hugging Face Storage Bucket, docling-project/docling-confluent-ex04:
hf buckets sync hf://buckets/docling-project/docling-confluent-ex04 ./lumen-invoices
The invoices
| layout | n | what it looks like |
|---|---|---|
de_classic |
96 | German DIN-style business letter: Rechnung, 1.234,56 €, zzgl. 19 % USt., dates 09.01.2026 |
de_modern |
31 | German, modern single-column variant of the above |
us_saas |
21 | US SaaS bill on Letter paper: $1,234.56, Jan 5, 2026 (or 01/05/2026), metered lines |
eu_saas |
10 | EU SaaS invoice on A4, reverse-charge VAT note, unit column |
uk_legal |
7 | UK solicitors' fee note: narrative time entries, hours × rate, 30 December 2025 |
ch_qr |
7 | Swiss invoice with a QR payment part: CHF 1'234.50 |
hotel_folio |
2 | Portuguese hotel folio: per-night lines, three IVA rates, tourist tax |
The variety is the point: the same fields printed seven ways — German and Swiss number formats, US and European dates, net/tax/total blocks with one, two or three VAT rates, a two-page cloud bill whose metered lines spill over, a hotel folio whose lines are nights. One template seventeen times would be template matching, not extraction.
Ground truth
ground_truth/manifest.jsonl, one JSON object per invoice:
{"doc_id": "lumen-inv-0001", "file": "invoices/lumen-inv-0001.pdf", "layout": "uk_legal",
"vendor_key": "kestrel", "scanned": false, "pages": 1,
"department": "Finance", "category": "Professional Services", "submitted_at": "2026-01-05T08:20:51Z",
"truth": {"vendor_name": "Kestrel & Finch LLP", "invoice_number": "KF/2025/0351",
"invoice_date": "2025-12-30", "currency": "GBP", "total_amount": 7510.5, "…": "…",
"line_items": [{"description": "…", "quantity": 6.3, "unit_price": 395.0, "amount": 2488.5,
"page": 1}]}}
truth holds vendor_name, vendor_vat_id, vendor_address, customer_name, invoice_number, invoice_date, due_date, currency, subtotal, tax_amount, total_amount, iban and line_items (description, quantity,
unit_price, amount, and the page it is printed on). Amounts are numbers
in the invoice's currency, dates ISO. department, category and
submitted_at are the demo's (who submitted the invoice, when); they are not
on the page.
What is asked: the extraction target
target/extraction_target.json is a Docling ExtractionTarget
(docling-project/docling#4218): output_schema (the contract, validated per
page), template (an example_json: one made-up invoice shown as an example
answer) and instructions. It asks for a subset of the truth:
| field | type | required | description |
|---|---|---|---|
vendor_name |
string | yes | Legal name of the company that issued the invoice (the seller, not the customer) |
vendor_vat_id |
string or null | The seller VAT or tax registration number | |
invoice_number |
string | yes | The invoice identifier, copied exactly |
invoice_date |
string, date |
yes | Date of issue |
currency |
string, ^[A-Z]{3}$ |
yes | ISO 4217 code of the invoice currency |
subtotal |
number or null | Net amount before tax | |
tax_amount |
number or null | Total VAT or sales tax | |
total_amount |
number | yes | Final amount due, including tax |
line_items |
array of LineItem | One entry per billed line |
LineItem:
| field | type | required |
|---|---|---|
description |
string | yes |
quantity |
number or null | |
unit_price |
number or null | |
amount |
number | yes |
Evaluating
Per invoice, the pages' answers are merged — header fields from the first page
that has them, totals from the last, line items from all — and compared with
truth:
| field | matches when |
|---|---|
vendor_name |
exact after case-folding and collapsing whitespace |
invoice_number |
exact, same normalisation |
invoice_date |
ISO date after parsing (local formats accepted; a slash date is month-first on USD invoices) |
currency |
exact ISO 4217 code |
subtotal |
within 0.01 |
tax_amount |
within 0.01 |
total_amount |
within 0.01 |
line_item_count |
number of line items, all pages merged |
ok is the demo pipeline's own verdict on the merged record: vendor, date,
currency and total present, and subtotal + tax = total. The reference
implementation is scripts/extract.py score with invoice_udf.invoice.build.
Line items are counted, not matched line by line — scores.jsonl and
results.jsonl have what is needed for a finer measure.
Runs
2026-10-05_granite-vision-v4
174 invoices, Docling for IBM watsonx (staging), POST /v1/extract/source/async, preset granite_vision_v4_kserve, extracted 2026-10-05; median 10.34 s per invoice. Files: extractions/docling/2026-10-05_granite-vision-v4/.
| n | ok | vendor name | invoice number | invoice date | currency | subtotal | tax amount | total amount | line item count | |
|---|---|---|---|---|---|---|---|---|---|---|
| all | 174 | 159 | 85% | 100% | 95% | 100% | 99% | 95% | 100% | 95% |
ch_qr |
7 | 7 | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% |
de_classic |
96 | 90 | 93% | 100% | 98% | 100% | 100% | 95% | 100% | 99% |
de_modern |
31 | 31 | 84% | 100% | 100% | 100% | 100% | 100% | 100% | 100% |
eu_saas |
10 | 10 | 100% | 100% | 100% | 100% | 100% | 100% | 100% | 100% |
hotel_folio |
2 | 0 | 100% | 100% | 100% | 100% | 100% | 0% | 100% | 100% |
uk_legal |
7 | 7 | 100% | 100% | 100% | 100% | 86% | 86% | 100% | 100% |
us_saas |
21 | 14 | 33% | 100% | 67% | 100% | 100% | 100% | 100% | 67% |
| scanned | 10 | 9 | 100% | 100% | 90% | 100% | 100% | 90% | 100% | 100% |
| single-page | 166 | 159 | 84% | 100% | 99% | 100% | 99% | 95% | 100% | 100% |
| multi-page | 8 | 0 | 100% | 100% | 0% | 100% | 100% | 100% | 100% | 0% |
Things worth knowing about these runs:
- Multi-page invoices: page 1 of each carries 20+ line items, and the service's model server timed out on it (20 s) on every attempt — the page has an error and no data, so dates and line counts miss. A service limit at the time, not the model's answer.
- Vendor names miss where the model "corrects" a name (
VolterraforVoltara,BiurebedarfforBürobedarf) or drops a suffix (, Inc.). - Dates come back as printed (
20.02.2026,30 December 2025), despite the schema'sformat: dateand the instructions; Docling does not validateformat. They are scored after parsing. - A page whose answer breaks the schema has
extracted_data: nullin Docling's result and the answer only inraw_text; the scoring reads it from there.
Generated by scripts/dataset.py on 2026-10-05; edit that, not this file.
- Total size
- 2.31 MB
- Files
- 182
- Last updated
- Oct 5
- Pre-warmed CDN
- US EU US EU