Differential evaluation harness for .docx parsers: reference OOXML extraction vs any document-intelligence output (Docling, ...) on a shared schema
Project description
docx-parse-eval — differential evaluation for .docx parsers
Differential-testing harness for .docx document-intelligence pipelines:
project the source (OOXML reference extraction) and any parser's native output
(Docling DoclingDocument JSON, or one adapter per additional model) into one
schema, then diff — conservation metrics, a TEDS/TEDS-Struct table quality
tier, a snapshot drift tier, and a fixture self-test tier beneath them.
Spec: evaluation-framework-spec.md.
Use it when your .docx are too long to eyeball (100+ pages),
confidential (evaluation runs entirely on your machine; the corpus never
enters CI, agents, or third-party services — R8), or parsed recurringly
(the snapshot tier answers "did the parser upgrade change my extractions?").
Skip it for a few short one-off files, or for scanned PDFs/images — the
approach needs a machine-readable source format, so there is no
gold-by-construction for pixels.
What a result means: gold is an independent reading of the same OOXML, so
a fired flag is a guaranteed discrepancy between two readings — a defect on at
least one side, always worth inspecting — not a claim of absolute truth. The
bless/reconcile steps put a human in the arbitration seat.
Quick start
The harness never runs Docling itself — produce the DoclingDocument JSON
out-of-band, wherever docling is installed (R8/R11):
docling --to json --output dl/ my_document.docx
Install with uv (or plain pip in a virtualenv — both verified):
uv sync # dev env: package + pytest/ruff/mypy (uv.lock pinned)
uv run docx-parse-eval --help
pip install path/to/doc-parsing-evaluation-framework # or classic pip
# 1. reference extraction straight from the OOXML → "silver" draft record
docx-parse-eval bootstrap my_document.docx --out work/
# 2. eyeball the silver record against the document, then bless it
docx-parse-eval bless work/my_document.silver.json --out . # → gold/my_document.json
# 3. project the Docling output into the same schema; --source binds the
# prediction to the .docx bytes so compare enforces source identity (R7)
docx-parse-eval predict dl/my_document.json --out pred/ --source my_document.docx
# 4. diff the two sides — non-zero exit iff any defect flag fires
docx-parse-eval compare --gold gold/my_document.json \
--pred pred/my_document.docling-adapter.json --out results/
# 5. self-contained HTML page: flagged metrics, side-by-side table grids with
# per-cell diffs + TEDS, caption pairs (repeat --gold/--pred per doc)
docx-parse-eval report --gold gold/my_document.json \
--pred pred/my_document.docling-adapter.json --out report.html
Remaining verbs: reconcile --gold … --draft … (field-level diff of blessed
gold vs a re-bootstrapped draft, R6), run --manifest corpus.json --out …
(compare over a whole corpus, one combined table; manifest is
[{"doc_id": …, "gold": …, "pred": …}, …]), and snapshot <record.json>
(drift tier: content hash vs stored baseline, non-zero exit on drift).
compare/run write long-form CSV + Parquet, exit non-zero when any flag
fires (CI gate), and refuse records whose source_sha256 differ
(--allow-source-mismatch overrides). --mlflow logs a run when mlflow is
installed, no-op otherwise. Other parsers plug in via predict --adapter NAME:
a module with extract(json_path, **kw) -> EvaluationRecord, registered under
the docx_parse_eval.adapters entry-point group. The same verbs are importable
from Python: from docx_parse_eval import extract_reference, compare, ….
Development environment
The maintained environment is Guix (deps pinned in
manifest.scm; the profile has python3, no bare
python); uv sync or pip install .[test] also work.
guix shell -m manifest.scm -- python3 -m pytest tests -q # test suite
guix shell ruff -- ruff check src tests # lint
guix shell -m manifest.scm python-mypy -- python3 -m mypy src/docx_parse_eval --ignore-missing-imports
Layout
manifest.scm # Guix harness-core deps
src/docx_parse_eval/
schema.py # EvaluationRecord (Pydantic v2); SCHEMA_VERSION="0.4"
normalize.py # shared NFC/whitespace normalisation + tokenisation
config.py # policy decisions + flag thresholds (calibration record)
io.py # JSON records, content hashing, R6 record diff
teds.py # TEDS/TEDS-Struct (PubTabNet metric on python-apted)
comparator.py # Script 3 — metrics + flags, model-agnostic
output.py # CSV/Parquet long-form emit + optional MLflow hook
report.py # self-contained HTML failure report
cli.py # bootstrap/bless/reconcile/predict/compare/run/snapshot/report
adapters/
ooxml_reference.py # Script 1 — gold/reference adapter (python-docx)
docling_adapter.py # Script 2 — DoclingDocument JSON adapter (zero-dep)
tests/ # fixture self-test tier (green/red mutations, hard cases)
JSON records are the source of truth (gold/<doc_id>.json,
pred/<doc_id>.<producer>.json); results emit as CSV + Parquet long-form.
Status / roadmap
The harness (adapters, comparator, calibration, CLI, report) is complete and
green on fixtures. Applying it to a real corpus is deliberately a local, human
step (R8): bootstrap each document, bless silver → gold, run the parser
out-of-band, predict/compare, and inspect every flag on the first pass —
any extractor bug found there gets distilled into a new minimal synthetic
fixture rather than debugged on the real document. Flag thresholds in
config.py are fixture-calibrated first passes; expect to tune them on first
real-corpus contact. Deferred: HTML export-loss diagnostic, HuggingFace
dataset bundle, per-figure image-description scoring.
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 docx_parse_eval-0.1.0.tar.gz.
File metadata
- Download URL: docx_parse_eval-0.1.0.tar.gz
- Upload date:
- Size: 763.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
504753f43a5b4258ce3ab3263d6aa9af1b5471049bd30ef68169a546631229f4
|
|
| MD5 |
537ded26db003ff59d05144927b4f9e1
|
|
| BLAKE2b-256 |
c13e3af6c5604c8675c4af0c8d355358487ba31f636edcdbd46a19fa0ddd9ee5
|
File details
Details for the file docx_parse_eval-0.1.0-py3-none-any.whl.
File metadata
- Download URL: docx_parse_eval-0.1.0-py3-none-any.whl
- Upload date:
- Size: 45.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ea490e208b91e1fd800fcab0deb71beb886749bb7f279e8673c038f3c29138b
|
|
| MD5 |
5bc1c53ad5e74859d280e503b3bb87ac
|
|
| BLAKE2b-256 |
e06f5ff9e1ac781b5adeec53198cf45f0d8e3d462044d5c51ba679cd0ee33c98
|