Custom Enrichment Models#
Docling's enrichment pipeline is a post-document-assembly processing phase. After layout analysis, OCR, and table extraction produce a DoclingDocument, an ordered list of enrichment models annotates specific element types with additional information. All built-in enrichments are disabled by default because they require extra model inference.
Base Classes#
All enrichment models live in docling/models/base_model.py and extend one of two abstract bases, both of which inherit from GenericEnrichmentModel[T].
GenericEnrichmentModel declares three required abstract methods :
| Method | Signature | Purpose |
|---|---|---|
is_processable | (doc, element) → bool | Gates which elements enter the pipeline |
prepare_element | (conv_res, element) → Optional[T] | Prepares each element (and optionally crops its image) |
__call__ | (doc, element_batch) → Iterable[NodeItem] | Runs inference on a batch and yields enriched items |
BaseEnrichmentModel#
BaseEnrichmentModel is the simplest variant: prepare_element returns the element unchanged if is_processable returns True. No image is involved. Use this for text-only enrichment.
BaseItemAndImageEnrichmentModel#
BaseItemAndImageEnrichmentModel is used when inference requires a cropped image of the element's bounding box. Its prepare_element implementation:
- For
PictureItemwith an embedded image: returns it directly . - Otherwise: crops the element's region from the source page image at
self.images_scale, optionally expanding the bounding box byself.expansion_factor.
The result is an ItemAndImageEnrichmentElement — a (item, image) pair ready for batch inference. This handles both rendered pages (PDFs) and embedded-image documents (Word, HTML) transparently .
Two class attributes control image extraction :
images_scale: float— rendering scale (e.g.,2inDocumentPictureClassifier,1.67inCodeFormulaVlmModel)expansion_factor: float = 0.0— fractional bounding-box expansion (e.g.,0.18inCodeFormulaVlmModel)
Execution Loop#
BasePipeline._enrich_document() iterates each model in self.enrichment_pipe, calls prepare_element() to filter and prepare candidates, chunks them into batches via elements_batch_size, and calls the model on each batch. The inner iterator must always be fully exhausted.
Implementing a Custom Enrichment Model#
The scaffold example in docs/examples/develop_picture_enrichment.py shows the full integration pattern:
-
Subclass
BaseEnrichmentModel(for text-only) orBaseItemAndImageEnrichmentModel(for image+item):class MyEnrichmentModel(BaseEnrichmentModel): def is_processable(self, doc, element): return self.enabled and isinstance(element, PictureItem) def __call__(self, doc, element_batch): for element in element_batch: # run inference, annotate element yield element -
Subclass the pipeline and replace
self.enrichment_pipe:class MyPipeline(StandardPdfPipeline): def __init__(self, pipeline_options): super().__init__(pipeline_options) self.enrichment_pipe = [MyEnrichmentModel(enabled=True)] -
Wire it into
DocumentConverterviaPdfFormatOption(pipeline_cls=MyPipeline).
Note: The example scaffold uses
BaseEnrichmentModel(no image crop) for simplicity. If your model needs a page-cropped image, subclassBaseItemAndImageEnrichmentModeland setimages_scale. SeeDocumentPictureClassifierfor a production example.
Production Implementations#
| Model | Base class | Element type | Output |
|---|---|---|---|
DocumentPictureClassifier | BaseItemAndImageEnrichmentModel | PictureItem | item.meta.classification |
CodeFormulaVlmModel | BaseItemAndImageEnrichmentModel | CodeItem, TextItem(FORMULA) | item.text as LaTeX or source code |
GraniteVisionChartExtractionModel | BaseItemAndImageEnrichmentModel | PictureItem (chart types only) | item.meta.tabular_chart, item.meta.code, item.meta.description |
DocumentPictureClassifier sets images_scale = 2 , filters to PictureItem instances , runs batched inference via a classification engine, and stores results in item.meta.classification as PictureClassificationMetaField .
Plugin Architecture for External Models#
Custom enrichment models can be injected in two ways :
1. Pipeline subclass (recommended for development)#
Subclass StandardPdfPipeline, override __init__, and reassign self.enrichment_pipe. Pass via PdfFormatOption(pipeline_cls=...). This is the approach shown in the scaffold example.
2. Setuptools entry-point plugins#
Register models via the "docling" entry-point group. The factory system uses pluggy's PluginManager to discover them. External plugins (modules not starting with "docling.") are only loaded when allow_external_plugins=True is set in PipelineOptions — disabled by default for security.
Handling Handwritten Text and Mixed-Content Documents#
BaseItemAndImageEnrichmentModel is the right base class for handwriting classifiers. A custom model follows the same pattern as DocumentPictureClassifier: filter TextItem regions, crop their page images, classify as printed vs. handwritten, and store a flag in element metadata .
Architecture note: An enrichment-based handwriting classifier runs after the full pipeline (layout → OCR → assembly). This means handwritten regions still pass through Docling's print-oriented OCR before being flagged. For scenarios where routing handwritten regions to a dedicated HTR tool before OCR is required, an external orchestrator approach using
LayoutPredictorstandalone is more appropriate.
Key Files#
| File | Purpose |
|---|---|
docling/models/base_model.py | GenericEnrichmentModel, BaseEnrichmentModel, BaseItemAndImageEnrichmentModel |
docling/pipeline/base_pipeline.py | _enrich_document() execution loop |
docling/pipeline/standard_pdf_pipeline.py | enrichment_pipe construction |
docs/examples/develop_picture_enrichment.py | Scaffold for building a custom enrichment model |
docling/models/stages/picture_classifier/document_picture_classifier.py | Production BaseItemAndImageEnrichmentModel example |
docling/models/factories/base_factory.py | Plugin discovery via pluggy |