This project wires together:
doclingfor PDF parsing, structure recovery, page images, and bounding-box provenancelangextractfor schema-guided extraction and animated HTML visualization- a small grounding/report layer that maps extracted spans back to Docling items and page regions
It is built as a Python package with a CLI, typed JSON configs, and sample demo configs for the two PDFs in this workspace.
Given:
- a PDF
- a JSON config that defines entity classes and few-shot examples for LangExtract
the pipeline will:
- Parse the PDF with Docling.
- Build a canonical extraction text while keeping segment-to-page provenance.
- Run LangExtract on that canonical text.
- Map each extraction span back to Docling source segments and page bounding boxes.
- Save:
- Docling JSON / Markdown / HTML exports
- canonical extraction text
- grounded extraction JSON
- LangExtract JSONL + animated HTML visualization
- a combined HTML report with page overlays and grounded evidence
Use Python 3.12.
cp .env.example .env
set -a; source .env; set +a
uv venv --python 3.12 .venv
UV_CACHE_DIR=$PWD/.uv-cache uv pip install --python .venv/bin/python -e .Download the minimal Docling artifacts needed for PDF parsing:
.venv/bin/docling-langextract-demo download-artifacts --output-dir .docling-artifactsRun the bundled demos:
Without an LLM API key, the sample configs automatically fall back to deterministic seeded matches so the end-to-end demo still produces grounded HTML reports locally.
.venv/bin/docling-langextract-demo demo --output-root demo_outputsThis creates:
demo_outputs/sample_invoicedemo_outputs/sample_rental_agreement
Each output directory contains report.html plus the Docling and LangExtract artifacts.
The sample configs are set up for OpenAI by default. Add a real key to .env, reload it, then run:
set -a; source .env; set +a
.venv/bin/docling-langextract-demo run \
sample_invoice.pdf \
examples/invoice_demo.json \
demo_outputs/invoice_liveYou can also switch the config to Gemini or Ollama by changing the provider section.
Minimal structure:
{
"name": "invoice-demo",
"prompt_description": "Extract the key invoice fields.",
"entities": [
{"name": "invoice_number", "description": "Invoice identifier"},
{"name": "total_amount", "description": "Grand total due"}
],
"examples": [
{
"text": "Invoice # INV-100 Total $123.45",
"extractions": [
{"extraction_class": "invoice_number", "extraction_text": "INV-100"},
{"extraction_class": "total_amount", "extraction_text": "$123.45"}
]
}
],
"provider": {
"provider": "openai",
"model_id": "gpt-4o-mini",
"api_key_env": "OPENAI_API_KEY"
},
"docling": {
"artifacts_path": ".docling-artifacts",
"do_ocr": false,
"do_table_structure": true,
"generate_page_images": true
}
}Optional fallback.seed_extractions lets you keep a fully offline showcase path for demos.
.venv/bin/docling-langextract-demo --help
.venv/bin/docling-langextract-demo run --help
.venv/bin/docling-langextract-demo demo --help
.venv/bin/docling-langextract-demo download-artifacts --help- The first real Docling run needs local model artifacts. The
download-artifactscommand handles that. - The project keeps source text grounding exact. It does not normalize extracted values unless you explicitly do that in a downstream step.
- The sample demo configs prefer OpenAI for live extraction, but they will still run offline through the deterministic fallback path.