Receipt Parse

2026-07-23 04:27 UTC · 6bc8930

About this benchmark

Reads a short, clean PDF receipt and fills a fixed JSON schema: invoice and receipt numbers, date paid, vendor, bill-to name and email, currency, subtotal, discount, total, amount paid, payment method and the line-item table. The model is told to use only values printed on the page.

This is the floor of the document suite: it asks nothing of long-context reading, only whether a model can hold a whole schema at once without dropping or inventing a field. Grading is per field: exact match on identifiers and dates, case-insensitive containment on names, a numeric tolerance on money, and absolute value on the discount so a sign convention does not decide the score.

Test data

  • receipt-a: One synthetic single-page receipt from Northwind Labs billed to Jane Doe, in USD. A 50% discount line takes a $22.00 subtotal down to an $11.00 total, so a model that reads the discount as positive, or reports the subtotal as the total, is caught.

Results

10/10 targets scored · 3 trials each · $0.15 total

Cost vs latencylower-left is better
Latency & costshorter is better
TargetScoreStddevLatencyTok/sCostErrors
bestGeminigemini-3.5-flash-lite@google-ai-studio100.00.0best2.5s94.3best$0.00230/3
OpenAIgpt-5.6-luna@openai100.00.04.1s48.2$0.00500/3
OpenAIgpt-5.6-terra@openai100.00.03s46.7$0.00990/3
Claudeclaude-haiku-4.5@anthropic100.00.03.8s52.1$0.01050/3
Qwenqwen3.7-plus@alibaba100.00.035.8s51.8$0.01350/3
Grokgrok-4.5@xai100.00.07.4s100.7$0.02560/3
Geminigemini-3.6-flash@google-ai-studio100.00.06.5s183.7$0.02930/3
Geminigemini-3.5-flash@google-ai-studio100.00.06.1s182.8$0.03240/3
Z.aiglm-4.6v@z-ai40 field missesreceipt-a: amount_paid (3/3)receipt-a: bill_to_email (3/3)receipt-a: bill_to_name (3/3)receipt-a: currency (1/3)receipt-a: date_paid (3/3)receipt-a: discount (3/3)receipt-a: invoice_number (1/3)receipt-a: line_items.0.amount (3/3)receipt-a: line_items.0.description (3/3)receipt-a: line_items.0.quantity (3/3)receipt-a: line_items.0.unit_price (3/3)receipt-a: payment_method (1/3)receipt-a: receipt_number (1/3)receipt-a: subtotal (3/3)receipt-a: total (3/3)receipt-a: vendor_name (3/3)16.711.815.6s58.8$0.00880/3
Z.aiglm-5v-turbo@z-ai48 field missesreceipt-a: amount_paid (3/3)receipt-a: bill_to_email (3/3)receipt-a: bill_to_name (3/3)receipt-a: currency (3/3)receipt-a: date_paid (3/3)receipt-a: discount (3/3)receipt-a: invoice_number (3/3)receipt-a: line_items.0.amount (3/3)receipt-a: line_items.0.description (3/3)receipt-a: line_items.0.quantity (3/3)receipt-a: line_items.0.unit_price (3/3)receipt-a: payment_method (3/3)receipt-a: receipt_number (3/3)receipt-a: subtotal (3/3)receipt-a: total (3/3)receipt-a: vendor_name (3/3)0.00.08.5s76.9$0.01510/3

Note

  • Running the @z-ai targets at all required dropping OpenRouter's require_parameters filter (commit 6bc8930); it keys on structured_outputs, which no GLM endpoint advertises, so both previously hard-failed before the call.
  • The two GLM scores in this file are post-processed, unlike every other run here: their stored raw_output was re-graded after stripping the ``` fence that all 6 GLM trials wrap the JSON in, and the result written back. The shipped grader does no stripping, so a re-run at this commit reproduces 0.0 for both.
  • That post-processing recovers almost nothing: glm-4.6v reaches 16.7 (±11.8), which is 25.0, 25.0 and 0.0 across its trials, and glm-5v-turbo stays 0.0 because its output is not valid JSON even unfenced, carrying a literal newline inside the vendor address. The fence was the trigger, not the cause.

Conclusion

Eight targets tie on score, so cost and latency decide, and gemini-3.5-flash-lite wins both at $0.0023 and 2.5s, a fourteenth of gemini-3.5-flash's $0.0324 for the same score. One case is not enough to separate the eight; treat the tie as "all clear this bar" rather than a ranking.

The GLM results are unsafe rather than fixable. glm-4.6v extracts the right values and puts them in a shape nobody asked for: money as "$11.00", dates as "June 3, 2026", bill_to.name instead of bill_to_name, items instead of a line_items array. Only the 4 plain-string fields ever match, and trial 3 nests everything under receipt and scores nothing. The pinned Z.AI endpoint serves no response_format, so nothing constrains the output. Repinning glm-4.6v to @novita, the only endpoint of either model advertising it, is what a real GLM number here would need.