agentsclimarketplace

Pdf parse qa

Skill okrapdf/skills/skills/pdf-parse-qa

Block-by-block A/B testing for PDF / VLM document parsers. Bring your own PDF and your own parser (HuggingFace Qwen2.5-VL, Gemini Flash, Mistral OCR, Reducto, LlamaParse, Azure Document Intelligence, AWS Textract, Unstructured, MinerU, Docling — or anything that emits text+bbox), run them through the same hosted validator UI, vote pass/partial/fail on every block with `↑↓ 1/2/3`, export JSON, compare per-label pass-rates. Triggered by "compare PDF parsers", "pick a VLM for PDFs", "bake-off Reducto vs LlamaParse", "evaluate Qwen2.5-VL on documents", "ParseBench-style accuracy QA", "which OCR is best for my docs". No SaaS account required — adapters produce a tiny JSON file, the hosted widget at embed.okrapdf.com renders it. Inspired by Label Studio + Daloopa + Adobe Acrobat "Find Suspects" review queues.From its SKILL.md

Install
npx -y skills add okrapdf/skills --skill pdf-parse-qa

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

10.1 KB, ~2.7k tokens by cl100k_base, as published. Nobody here has run it

pdf-parse-qa

A structured way to A/B test PDF parsers — VLMs, OCRs, layout APIs, anything that takes a PDF page and returns text + bounding boxes. You bring the parsers (HuggingFace models, vendor APIs, local libs), the skill brings the validator UI, the data contract, and adapter examples.

Why this exists

Picking a PDF parser is a benchmark problem disguised as a procurement problem. Vendor demos pick the easy pages. Per-block accuracy varies wildly by label (a parser may nail Title and Section-header but butcher Table or Picture). A summary "F1 score" hides that.

This skill gives you the per-block, per-label, source-anchored audit that vendor benchmarks don't.

Workflow

┌─────────────┐    ┌──────────────────┐    ┌─────────────────┐    ┌──────────────┐
│ your PDF    │ →  │ your parser      │ →  │ blocks.json     │ →  │ validator UI │
│ (CORS host) │    │ (one of adapters)│    │ (this contract) │    │ (vote ↑↓123) │
└─────────────┘    └──────────────────┘    └─────────────────┘    └──────────────┘
                                                                          ↓
                                                                  verdicts.json
                                                                  → spreadsheet / Langfuse
  1. Host the PDF somewhere CORS-open from embed.okrapdf.com (GitHub raw, R2, S3 with CORS, gist).
  2. Run a parser adapter (adapters/*.py) to produce blocks.json matching the data contract below. Host it the same way.
  3. Open the validator with both URLs as query params:
    https://embed.okrapdf.com/e/af8cc13f3d270c4b32f9245a?pdfUrl=<YOUR_PDF>&dataUrl=<YOUR_BLOCKS>&docTitle=Apple%2010-K%20(reducto)
    
  4. Vote: click a row or use ↑↓ to navigate, 1/2/3 to score pass/partial/fail, 0 clears. Bias toward pass; mark only deviations. Bbox cross-highlights on the canvas as you go.
  5. Copy verdicts as JSON when done. Paste into a sheet keyed by (parser, label) → pass-rate. That's the bake-off currency.

Data contract

Adapters MUST emit JSON in this shape. Coordinates are 0–1 normalized (origin = top-left), so they survive any PDF render scale.

{
  "pages": [
    {
      "page_number": 1,
      "blocks": [
        {
          "label": "Title",
          "value": "Form 10-K",
          "bbox": { "x": 0.10, "y": 0.05, "w": 0.80, "h": 0.03 }
        },
        {
          "label": "Table",
          "value": "Title of each class | Name of each exchange ...",
          "bbox": { "x": 0.08, "y": 0.32, "w": 0.84, "h": 0.18 }
        }
      ]
    },
    { "page_number": 2, "blocks": [ ... ] }
  ]
}

Required: pages[].page_number, pages[].blocks[].bbox.{x,y,w,h}. Recommended: pages[].blocks[].label, pages[].blocks[].value.

A starter file lives at examples/sample-blocks.json — sanity-test the widget before writing an adapter.

Validator URL

https://embed.okrapdf.com/e/af8cc13f3d270c4b32f9245a
  ?pdfUrl=<CORS-open PDF URL>
  &dataUrl=<CORS-open blocks.json URL>
  &docTitle=<display title, URL-encoded>
  &scale=1.5   (optional, default 1.5 — pdf.js render scale)

Verdicts persist in sessionStorage keyed by dataUrl, so refreshing the tab won't lose your work.

Adapters

Reference scripts in adapters/. Each one: takes a PDF file path, writes blocks.json in the contract above. ~30–80 lines. Add your own — PRs welcome.

FileParserAuthNotes
adapters/gemini-flash.pyGoogle Gemini 3 Flash (Thinking)GEMINI_API_KEYUses the ParseBench prompt; native [y_min, x_min, y_max, x_max] bbox order normalized to {x,y,w,h}
adapters/qwen2-vl-hf.pyHuggingFace Qwen/Qwen2.5-VL-7B-Instructnone (local)Local GPU; ~8GB VRAM @ fp16. Latest open-weights VL model.
adapters/reducto.pyReducto /parseREDUCTO_API_KEYOne of the strongest commercial table extractors as of 2026.
adapters/llamaparse.pyLlamaIndex LlamaParse (Premium / Agentic modes)LLAMA_CLOUD_API_KEYStalwart; agentic mode handles charts.
adapters/azure-di.pyAzure Document Intelligence (prebuilt-layout)AZURE_DI_ENDPOINT, AZURE_DI_KEYEnterprise default; strong layout, weaker on free-form OCR.
adapters/okra.pyOkraPDF facets (any of 14: gemini-3-flash-minimal, reducto-parse, llamaparse-premium, mistral-ocr, mineru, unstructured, chandra-ocr, ...)OKRA_API_KEYOne adapter, 14 parsers — useful if you already have an OkraPDF account.

Adapters worth adding (PRs welcome)

  • Mistral OCRmistral-ocr-latest is fast and cheap; good Mistral-vs-Gemini cost angle
  • AWS Textractanalyze_document with FORMS|TABLES|LAYOUT
  • Google Document AI — layout parser processor
  • MinerU — local Python, strong on academic layout
  • Docling — IBM open-source, fast on scientific PDFs
  • Unstructuredunstructured.partition.pdf with hi_res
  • Chandra OCR — emerging open-source OCR

Bake-off recipe

Same PDF, N parsers. Each adapter writes blocks-<parser>.json. Host all of them. Open one validator tab per parser. Score each. Aggregate by (parser, label) → pass-rate. That's a buyer-grade comparison your CFO can read.

# Same source PDF, four parsers in parallel
PDF=https://raw.githubusercontent.com/you/repo/main/test.pdf

python adapters/gemini-flash.py test.pdf > blocks-gemini.json
python adapters/reducto.py     test.pdf > blocks-reducto.json
python adapters/llamaparse.py  test.pdf > blocks-llamaparse.json
python adapters/qwen2-vl-hf.py test.pdf > blocks-qwen25vl.json

# Upload to gist / R2 / S3 (CORS open) — then:
for P in gemini reducto llamaparse qwen25vl; do
  echo "https://embed.okrapdf.com/e/af8cc13f3d270c4b32f9245a?pdfUrl=$PDF&dataUrl=https://.../blocks-$P.json&docTitle=$P"
done

Open each URL in a tab, score every page, copy verdicts, paste into:

parserTitle passTable passPicture passSection-header passoverall
gemini19/208/124/422/2253/58 (91%)
reducto18/2011/123/422/2254/58 (93%)
...

Verdict export shape

Copy verdicts as JSON → clipboard:

{
  "source": { "dataUrl": "https://.../blocks-reducto.json", "pdfUrl": "https://.../test.pdf" },
  "docTitle": "Apple 10-K (reducto)",
  "verdicts": [
    { "page": 1, "blockIndex": 0, "verdict": "pass" },
    { "page": 1, "blockIndex": 7, "verdict": "fail" },
    { "page": 2, "blockIndex": 3, "verdict": "partial" }
  ],
  "capturedAt": "2026-05-18T05:40:52Z"
}

When run against an OkraPDF facet (no dataUrl), source becomes { "docId": "...", "facet": "..." } instead. Same shape, different provenance.

Keyboard

KeyAction
/ kprevious block
/ jnext block
1mark pass
2mark partial
3mark fail
0clear verdict
Click rowfocus block
Click bboxfocus row
Show sourcere-scroll + flash the focused block

Hosting tips (CORS-open URLs)

The widget runs in the browser at embed.okrapdf.com and fetches your URLs cross-origin. They must respond with Access-Control-Allow-Origin: * (or include embed.okrapdf.com).

HostWorks out-of-the-boxNotes
GitHub raw (raw.githubusercontent.com)Use for small JSON + sample PDFs in your repo
GitHub gist (gist.githubusercontent.com/.../raw/...)Best for one-off blocks.json — gh gist create blocks.json --public
Cloudflare R2 with public bucket + CORSBest for many large PDFs
AWS S3 with CORS policy AllowedOrigin: *Add <CORSRule> to bucket
Vercel/Netlify staticDefault headers are CORS-open
Local file://Browser blocks cross-origin file:// — host even short-lived runs via python -m http.server + a tunnel

For private PDFs you don't want to host: see Standalone mode below.

Standalone mode (no embed.okrapdf.com)

The widget is a single HTML file. Clone the repo's parent (this skill ships SKILL.md only; the widget source lives at okrapdf/manual-app under embeds/parse-validator/) and open dist/index.html directly. Same ?pdfUrl=&dataUrl= params work. Use this for confidential PDFs that can't leave your machine.

See also

  • OkraPDF facets (okra-curl skill in this repo) — if you want a hosted parser store rather than running 6 SDKs yourself, OkraPDF's /v1/documents/{id}/facets/{name}/run exposes 14 parsers behind one API. Use the adapters/okra.py adapter then.
  • Label Studio — heavier, server-installed, supports per-cell table validation. Better when you're building a ground-truth dataset.
  • Daloopa — commercial, finance-vertical, similar UX inspired this widget.
  • ParseBench (arXiv 2604.08538) — the benchmark this skill operationalizes.

License

MIT. Adapter scripts are reference implementations; PRs welcome at okrapdf/skills.

What ships with it: 5 files

16.4 KB alongside SKILL.md, 4 of them executable

adapters/

examples/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.