Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License: MIT Python 3.9+ Zero dependencies VAA Series

docling-cite

Automatic source citation for Docling-parsed documents — EU AI Act compliant provenance in one line.

Most RAG pipelines retrieve a chunk and hope the model cites the right source. docling-cite makes citation deterministic: every retrieved chunk is matched back to its origin node in the Docling document tree and returned with section, page, node_id, and confidence.

from docling_cite import CitationIndex

index    = CitationIndex.from_dict(docling_doc)
citation = index.cite("Ibuprofen: interaction type CONTRAINDICATED.")

print(citation.section)    # "Drug Interactions"
print(citation.page)       # 14
print(citation.node_id)    # "item_011"
print(citation.confidence) # 1.0
print(citation.format())   # "[Drug Interactions, p. 14, item_011 - warfarin_monograph.pdf]"

Why this exists

The EU AI Act (enforced August 2026) classifies AI systems in healthcare, finance, and legal as high-risk. High-risk systems must be able to explain why an answer came from which source. "The embedding was close" does not satisfy this requirement. docling-cite makes every retrieval auditable without changing your pipeline.

Article 15 of the Verifiable AI Architecture series shows why vector search cannot produce an audit trail and what the compliance gap looks like in practice. This package is the mechanical layer that closes it.


Installation

pip install docling-cite

Zero hard dependencies. The core matching uses pure stdlib (difflib, re).

Optional extras:

pip install docling-cite[docling]    # Use with live DoclingDocument objects
pip install docling-cite[langchain]  # CitedRetriever wrapper
pip install docling-cite[llamaindex] # CitationPostprocessor
pip install docling-cite[all]        # Everything

How it works

Docling-parsed document (dict or DoclingDocument)
        │
        ▼
  CitationIndex.from_dict(doc)
  ─────────────────────────────
  Walk items list.
  Track current section heading.
  Assign node_id to every element.
  Extract page + bounding box from provenance metadata.
        │
        ▼
  index.cite("retrieved chunk text")
  ─────────────────────────────────
  Tier 1: Exact substring match   → confidence 1.0
  Tier 2: Token Jaccard overlap   → confidence = overlap ratio
  Tier 3: SequenceMatcher ratio   → character-level similarity
        │
        ▼
  Citation(section, page, node_id, element_type, confidence, ...)

The three-tier strategy means paraphrased chunks still get attributed correctly — useful when the LLM reformulates retrieved text before passing it back to your logging layer.


Usage

Basic

from docling_cite import CitationIndex

# Build the index once per document
index = CitationIndex.from_dict(docling_doc)

# Cite a single chunk
citation = index.cite("Revenue growth was driven by APAC expansion.")
print(citation.format())  # "[Financial Highlights, p. 3, item_004 - report.pdf]"

# Cite a list of chunks (e.g. all retrieved docs)
citations = index.cite_all(retrieved_chunks)

With a live Docling document

from docling.document_converter import DocumentConverter
from docling_cite import CitationIndex

result = DocumentConverter().convert("annual_report.pdf")
index  = CitationIndex.from_docling_document(result.document)

citation = index.cite(retrieved_chunk)

LangChain

from docling_cite import CitationIndex
from docling_cite.integrations.langchain import CitedRetriever

index     = CitationIndex.from_dict(docling_doc)
retriever = CitedRetriever(vectorstore.as_retriever(), index)

docs = retriever.invoke("What drove revenue growth?")
for doc in docs:
    print(doc.metadata["citation"])
    # {"section": "MD&A", "page": 12, "node_id": "item_031", "confidence": 0.94, ...}

llama-index

from docling_cite import CitationIndex
from docling_cite.integrations.llamaindex import CitationPostprocessor

index         = CitationIndex.from_dict(docling_doc)
postprocessor = CitationPostprocessor(index)

query_engine = vector_index.as_query_engine(
    node_postprocessors=[postprocessor]
)
response = query_engine.query("What are the renal dosing adjustments?")
for node in response.source_nodes:
    print(node.metadata["citation"])

Serialisation

citation = index.cite(chunk)
data     = citation.to_dict()   # plain dict, JSON-serialisable
import json
print(json.dumps(data, indent=2))

The Citation object

Field Type Description
text str The retrieved chunk text
document str Source document name
node_id str Position in the document tree, e.g. item_031
element_type str paragraph, table, heading, etc.
section str | None Nearest ancestor heading
page int | None Page number (when available from Docling provenance)
confidence float Match confidence 0.0–1.0. 0.0 = no match found
bounding_box dict | None {l, t, r, b} when available from Docling

bool(citation) is False when confidence == 0.0 (no match).

citation.format() returns an inline citation string: [Section, p. N, item_XXX — document.pdf]


Run the example

git clone https://github.com/elyas-karbouch/docling-cite
cd docling-cite
python example.py

No Docling installation required — the example uses a synthetic drug monograph dict.


Run the tests

pip install -e ".[dev]"
pytest

Part of the Verifiable AI Architecture series

This package is the implementation layer for the citation and provenance concepts covered in the VAA series — specifically:

  • Article 6: Why vector search destroys document structure at ingestion
  • Article 15: Framework decision guide — when PageIndex beats vector search
  • Article 17: EU AI Act compliance requirements for high-risk AI systems

License

MIT — see LICENSE.

About

Automatic source citation for Docling-parsed documents — EU AI Act compliant provenance in one line.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages