paradox2
Fast, simple document extraction — a from-scratch rewrite of paradox-pdf, built around one idea: most PDFs are digital and don't need a GPU.
import paradox2 as pdx
pages = pdx.extract("invoice.pdf")
Why this rewrite exists
paradox-pdf grew into a 2,783-line facade doing routing, backend resolution, and overlay rendering all in one file. paradox2 starts over with a hard rule: digital pages never pay for OCR or vision models. Every PDF page is classified independently — a digital page (has a text layer) goes through a zero-ML fast path (PyMuPDF text + vector-line table detection); a scanned page routes to the GPU OCR pipeline. A single mixed document is handled correctly per-page, automatically.
Install
pip install paradox2
Everything above works with just that — digital PDFs, text, vector-line tables, key-value fields. Heavier features are opt-in extras so the base install stays small:
| Extra | Adds | When you need it |
|---|---|---|
paradox2[gpu] |
PaddleOCR + PP-DocLayoutV2 | Scanned/photographed pages |
paradox2[rapidai] |
TableStructureRec (RapidAI), ONNX-only | Mid-tier table structure for scanned pages the OCR-grid heuristic rejects — no torch/torchvision, CPU-only, ~5.9s/table |
paradox2[yolo] |
YOLO26-document-layout | Independent table-bbox detector — crops the page before RapidAI/VLM instead of feeding them the whole page. Needed for the router's full accuracy; see below |
paradox2[formats] |
docx/xlsx/pptx/msg/rtf/7z/rar/odf readers | Non-PDF documents |
paradox2[specialists] |
torch + transformers (handwriting, signatures, formulas) | Handwriting (TrOCR), signature detection (Conditional-DETR), formula-to-LaTeX (pix2tex) |
pip install "paradox2[gpu,rapidai,formats]"
What it does
import paradox2 as pdx
# Every page, as structured JSON — digital pages via the fast path,
# scanned pages via GPU OCR, decided per page.
pages = pdx.extract("document.pdf")
# Just the text
text = pdx.extract_text("document.pdf")
# Just the tables (vector-line detection on digital pages,
# OCR-grid heuristic + optional RapidAI/VLM fallback on scanned pages)
tables = pdx.extract_tables("document.pdf")
# Key-value pairs from an invoice/form-like page (digital only)
fields = pdx.extract_kie("invoice.pdf")
# Any non-PDF format too — same call, same output shape
data = pdx.extract("spreadsheet.xlsx")
data = pdx.extract("scan.docx")
Selecting pages, features, and output format
pdx.extract("report.pdf", pages="1-3,7") # specific pages
pdx.extract("report.pdf", feature="tables") # just one feature, still full extract() under the hood
pdx.extract("report.pdf", output_format="markdown") # rendered markdown instead of the raw IR
pdx.extract("report.pdf", fields=True) # turn on key-value extraction inline
Optional specialists (all off by default, all lazy-loaded)
pdx.extract("form.pdf", handwriting=True) # TrOCR on handwritten blocks
pdx.extract("contract.pdf", signatures=True) # Conditional-DETR signature boxes
pdx.extract("paper.pdf", formulas=True) # inline math -> LaTeX
None of these import their heavy dependencies unless the flag is set —
import paradox2 alone never touches torch, paddle, or transformers.
The scanned-table router
Scanned pages route tables through three tiers, each opt-in past the first:
OCR-grid heuristic (free, always on)
| rejects merged/borderless/dense tables by design,
| or under-reads a real table (low_confidence flag)
v
YOLO26 bbox (PARADOX2_YOLO_DETECT=1) -- crop the page to the detected
| table region before handing it to the tiers below. A
| borderline-confidence detection (<0.85) skips RapidAI
| entirely and goes straight to the VLM tier - VLM tolerates
| a loose/imprecise crop better than RapidAI's structure model
v
RapidAI mid-tier (PARADOX2_RAPIDAI_TABLES=1)
| ONNX wired/wireless classifier + structure model. Its own
| result is cross-checked against OCR line density in the same
| crop (table_rapidai.py's low_confidence) - implausibly few
| OR implausibly many rows both re-route to the VLM tier
v
VLM fallback (PARADOX2_VLM_TABLES=1)
| PaddleOCR-VL single-pass, reserved for tables that look
| genuinely complex - not run on everything RapidAI touches
v
best available result
Every tier is measured end-to-end (paradox2.extract(), not the isolated
engine wrapper) on the same 20 real full-page tables (OmniDocBench), not
simulated:
| config | exact row match | within +/-3 rows | mean time/page |
|---|---|---|---|
| heuristic only (pre-router) | 5% | 15% | 0.8s |
| + RapidAI, full page (bug, fixed) | 15% | 50% | 6.0s |
| + RapidAI, cropped to heuristic's own bbox (bug, fixed - made 3/20 pages worse) | 15% | 40% | 4.3s |
| + RapidAI, cropped to YOLO26 bbox | 25% | 75% | 5.5s |
| + selective VLM escalation (low YOLO confidence or implausible RapidAI row count) | 35% | 80% | 6.4s |
The isolated RapidAI engine alone scores 65% exact on tight ground-truth crops — the gap to the router's end-to-end number is the YOLO26 crop's margin versus a perfect crop, not a RapidAI accuracy problem. Cropping to the heuristic's own bbox instead of an independent detector actively hurts: that bbox only spans the rows the heuristic already found, so it bakes its own under-read into the pixels before the next tier ever sees them. A same-crop self-consistency check (RapidAI's row count vs. OCR line density) can't catch a loose crop itself, since both numbers inflate together on the same imprecise crop — that's what the YOLO-confidence trigger (<0.85) is for instead.
The heuristic never gets replaced by a worse result — a lower tier's output is kept unless a higher tier actually produces something.
export PARADOX2_YOLO_DETECT=1
export PARADOX2_RAPIDAI_TABLES=1
export PARADOX2_VLM_TABLES=1
Health check
from paradox2 import doctor
doctor() # {"cuda": True/False, "pymupdf": "1.24.x", ...} - never silently hides a backend mismatch
Design principles
- Digital-first: a PDF with a text layer never pays for OCR, vision layout models, or GPU inference — verified per-page, not per-document.
- Facade stays thin:
paradox2/api.pyis dispatch only; business logic lives inpipeline/,tables/,engines/,formats/. If the facade creeps toward 100+ lines, that's a signal something leaked out of place. - Specialists are lazy and swallow their own failures: an optional
engine (handwriting, signatures, formulas, RapidAI, VLM) that fails to
load or errors mid-call returns
[]/None— it never takes down the base extraction path. - No simulated benchmarks: every accuracy/speed number in this README and in the codebase's docstrings comes from a real run against real documents, with the script path noted alongside it.
Status
Early-stage rewrite, private while the API and table router stabilize. See
docs/PLAN.md for the phase breakdown and docs/TASKS.md for current work.
Release files for paradox2 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| paradox2-0.1.0.tar.gz | 275.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| paradox2-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 418.7 kB
Release files / paradox2-0.1.0.tar.gz
| Download URL | paradox2-0.1.0.tar.gz |
|---|---|
| Size | 275.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f7c22b547304fcd838126d7f5a5139c11f2cccaa4d51c13e317747797fc3939f
|
|
BLAKE2b-256 checksum How to use checksums |
cca883f7d90ffc9c6bef84e9b2039484720e434f5377318cff0a8f236e9a4205
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency logRelease files / paradox2-0.1.0-py3-none-any.whl
| Download URL | paradox2-0.1.0-py3-none-any.whl |
|---|---|
| Size | 143.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bce87d14ed4ffee83b2b7953abf81b935ac5b47b7e8a09f0c2344332e4a1585b
|
|
BLAKE2b-256 checksum How to use checksums |
11d12eaccd2fd52bf1059115819c69a8959fdb41c25a38fd726c0299805ca4d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency log