Pure-Python, RAG-friendly document parsers (PDF/HTML/text/XML/Markdown). Java- and Tika-free.
Project description
Warp-Ingest
Warp-Ingest is a state-of-the-art, deterministic PDF parser. It turns a PDF into layout-aware structure — accurate word- and block-level bounding boxes, structural labels (section header, paragraph, list item, table row), and the relationships between them (the section/sub-section/paragraph parent ↔ child hierarchy) — and renders it as blocks, JSON, HTML, or an OpenContracts structural export.
It is rule-based, not model-based: structure comes from text coordinates, graphics, and font data — no GPU, no per-page rasterization, no training set. That makes it fast and predictable on long, text-layer documents (hundreds of pages), which is where vision parsers are slowest and least stable. See Rule-based vs. model-based for the rationale.
Warp-Ingest is a pure-Python rewrite of the nlmatics nlm-ingestor engine, with the
Java/Apache-Tika and Tesseract dependencies removed.
How the pipeline works
PDF ──pdfplumber──► per-word boxes + fonts ──► Tika-format XHTML ──► visual_ingestor ──► blocks / JSON / HTML / OpenContracts
(scanned pages ──rapidocr──► OCR words ──► same XHTML ──┘)
pdfplumber(MIT) extracts each word's real bounding box and font data in absolute, top-left-origin PDF points.- A scanned or sparse page is detected automatically and routed to the optional
rapidocr-onnxruntimeOCR backend (Apache-2.0, no Tesseract binary, no GPU), which emits the same word-box format — so a scanned page and a born-digital page flow through the identical layout engine. - The front-end emits an intermediate Tika-format XHTML (one
<p>per visual line, carrying per-word positions and fonts).visual_ingestor— the ~6,000-line rule engine — groups those lines into typed blocks, detects tables from rule-line graphics, strips repeating headers/footers, removes watermarks, and fixes reading order.
The word-level boxes use the same technique as OpenContracts (see docs/bbox_architecture.md).
What the parser produces
- Sections and sub-sections with their nesting levels.
- Paragraphs (lines joined into coherent blocks).
- The parent ↔ child links between sections and paragraphs.
- Tables, with the section each table sits in.
- Lists and nested lists.
- Content joined across page breaks.
- Removal of repeating headers and footers.
- Watermark removal.
- OCR with bounding boxes for scanned pages.
- An OpenContracts structural export: PAWLS
word tokens, one structural annotation per block, and the
parent_idheading hierarchy as explicit relationships.
Benchmarks
Warp-Ingest is scored on the official LlamaIndex ParseBench — 2,078 human-verified pages of real enterprise documents — by running it through the official framework with deterministic, rule-based metrics (no LLM-as-a-judge). Among the 8 deterministic, local, no-API parsers, Warp-Ingest is a top-2 result (2nd overall), it is the only local parser that carries real visual grounding, and it leads on Charts. Full numbers, methodology, and the reproduction commands are in benchmarks/parsebench/RESULTS.md.
Installation
# install the project and dev tools with uv
uv sync --group dev
# include the optional OCR backend for scanned PDFs
uv sync --group dev --extra ocr
# one-time NLTK data download
uv run python -m nltk.downloader punkt punkt_tab stopwords
Running the service
python -m warp_ingest.ingestion_daemon # or: ./run.sh (FastAPI/uvicorn, port 5001)
The launcher budgets concurrency automatically from the CPUs actually available
(CPU affinity and the container cgroup quota, so a docker --cpus / K8s
limits.cpu deployment uses exactly its slice): one uvicorn worker per
effective CPU, with the front-end page-striping pool and the OCR session
threads sized so the layers never oversubscribe the box. Every knob can be
overridden via environment variables:
| Env var | Default | Effect |
|---|---|---|
WARP_API_KEY |
abc123 |
API key required by /api/parse (send as X-API-Key or Authorization: Bearer) |
WARP_WEB_WORKERS (or WEB_CONCURRENCY) |
effective CPUs | uvicorn worker processes |
WARP_FE_WORKERS |
max(1, min(8, cpus // workers)) |
per-worker front-end page-striping pool (≤1 = serial) |
WARP_OCR_THREADS |
max(1, cpus // (workers × fe_workers)) |
onnxruntime intra-op threads per OCR session |
WARP_WORKER_PARSE_SLOTS |
1 |
concurrent parses allowed inside one worker |
WARP_HOST / WARP_PORT (or PORT) |
0.0.0.0 / 5001 |
bind address |
POST /api/parse with a file form field and the API key; the response body is
the parse result ({"page_dim": ..., "num_pages": ..., "result": ...}) and
errors are standard {"detail": ...} bodies. Interactive OpenAPI docs at /docs.
curl -H "X-API-Key: abc123" -F file=@document.pdf \
"http://localhost:5001/api/parse?render_format=all"
Query parameters (booleans accept true/false, 1/0, yes/no, on/off):
| Param | Values | Effect |
|---|---|---|
render_format |
all | json | html | opencontracts |
output rendering |
apply_ocr |
bool | force OCR on every page (scanned pages are OCR'd automatically regardless) |
disable_ocr |
bool | keep every page on its embedded text layer — no OCR for this request |
semantic_units |
bool | append the additive Semantic-Unit clause layer (render_format=opencontracts) |
GET / and GET /healthz are unauthenticated health endpoints (/healthz
reports the resolved concurrency settings and OCR availability). Warp-Ingest
parses PDF only; a non-PDF upload returns HTTP 415.
Docker
docker build -t warp-ingest .
docker run -p 5010:5001 -e WARP_API_KEY=change-me warp-ingest
Every GitHub release publishes a versioned multi-arch image to GHCR
(ghcr.io/open-source-legal/warp-ingest:X.Y.Z / :X.Y / :X / :latest,
cosign-signed) via .github/workflows/docker-publish.yml.
Library use
from warp_ingest.ingestor import pdf_ingestor
export = pdf_ingestor.parse_to_opencontracts("document.pdf") # OpenContracts export
markdown = pdf_ingestor.parse_to_markdown("document.pdf") # Markdown export
payload = pdf_ingestor.parse_to_markdown_payload("document.pdf") # Blocks + tables + geometry
layout = pdf_ingestor.parse_to_layout_predictions("document.pdf") # Generic layout predictions
ingestor = pdf_ingestor.PDFIngestor("document.pdf", {"render_format": "all"})
blocks = ingestor.blocks
The notebook pdf_visual_ingestor_step_by_step.ipynb walks the whole pipeline on a sample PDF.
Testing
No Java or Tika is needed.
make test # full pytest suite (unit + fixture parsing, incl. OCR)
uv run pytest tests/ # same, directly
Beyond unit tests, the suite includes cross-engine regression against the original Java/Tika engine (S-1), an OpenContracts-export regression, a Docling layout oracle, and vision-adjudicated structural-correctness suites — all floored against committed baselines so engine changes can only improve, never silently regress. See CLAUDE.md for the map of which suite guards what.
Rule-based vs. model-based
Over four years the nlmatics team evaluated many options, including a YOLO-based vision parser, and settled on the rule-based approach for these reasons:
- Speed. It is ~100× faster than a vision parser, which must rasterize every page (even text-layer ones). A vision parser is the better tool for scanned PDFs with no text layer or small form-like documents; for large text-layer PDFs spanning hundreds of pages, a rule-based parser is far more practical.
- No special hardware. It runs on CPU.
- Fixable. Vision-parser errors are fixed either by adding training examples (which can degrade previously-correct behavior) or by layering on rules anyway — at which point you are writing rules again.
Credits
The PDF parser visual_ingestor and its indent parsers were written by Ambika Sukla, with contributions from Reshav Abraham, Tom Liu (the original Indent Parser), and Kiran Panicker (parsing speed, table-parsing, indent-parsing, and reordering accuracy). The core line_parser was written by Ambika Sukla.
Thanks to the pdfplumber / pdfminer.six, pypdfium2, and RapidOCR open-source
communities, and to the Apache PDFBox and Tika developers whose XHTML format the engine
is built around.
History
Earlier versions depended on an nlmatics-modified Apache Tika (nlm-tika) on the JVM
plus Tesseract for OCR. That is gone — the pure-Python front-end
(pdf_plumber_parser.py and
ocr_parser.py) reproduces the same
intermediate XHTML contract, so the layout engine did not have to change.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file warp_ingest-1.0.2.tar.gz.
File metadata
- Download URL: warp_ingest-1.0.2.tar.gz
- Upload date:
- Size: 763.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
731d5eeea9eb3a4f1607034d57dc94214ee6fb2d3b6293d5553453f54c68289a
|
|
| MD5 |
5414344903269ac414705defa874ad9c
|
|
| BLAKE2b-256 |
4cd0f7e94e1f703d3c71fb2d9d423830a306194675435ec88844991e0a506f7c
|
Provenance
The following attestation bundles were made for warp_ingest-1.0.2.tar.gz:
Publisher:
python-publish.yml on Open-Source-Legal/Warp-Ingest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
warp_ingest-1.0.2.tar.gz -
Subject digest:
731d5eeea9eb3a4f1607034d57dc94214ee6fb2d3b6293d5553453f54c68289a - Sigstore transparency entry: 2084158001
- Sigstore integration time:
-
Permalink:
Open-Source-Legal/Warp-Ingest@7d38c9ea33ddda658e360e22a9aae2f7da9865dd -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/Open-Source-Legal
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@7d38c9ea33ddda658e360e22a9aae2f7da9865dd -
Trigger Event:
release
-
Statement type:
File details
Details for the file warp_ingest-1.0.2-py3-none-any.whl.
File metadata
- Download URL: warp_ingest-1.0.2-py3-none-any.whl
- Upload date:
- Size: 777.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1e952bd5b61bb8582188626ca69527794af32ebc5b7a8d0029711926953c2cf
|
|
| MD5 |
30a770939d511ea05323feb2b0d04986
|
|
| BLAKE2b-256 |
365e8892c617f44b0f5ac11199a154546bea1ff33e691140f1cbfcce6dbb8159
|
Provenance
The following attestation bundles were made for warp_ingest-1.0.2-py3-none-any.whl:
Publisher:
python-publish.yml on Open-Source-Legal/Warp-Ingest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
warp_ingest-1.0.2-py3-none-any.whl -
Subject digest:
c1e952bd5b61bb8582188626ca69527794af32ebc5b7a8d0029711926953c2cf - Sigstore transparency entry: 2084158127
- Sigstore integration time:
-
Permalink:
Open-Source-Legal/Warp-Ingest@7d38c9ea33ddda658e360e22a9aae2f7da9865dd -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/Open-Source-Legal
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@7d38c9ea33ddda658e360e22a9aae2f7da9865dd -
Trigger Event:
release
-
Statement type: