Custom Layout Plugin Architecture#
Docling's plugin system lets third-party packages register custom layout models as first-class alternatives to the built-in engines. The same pattern applies to OCR, table-structure, and picture-description models, but this article focuses on the layout-specific path.
Core Interfaces#
BaseLayoutModel — the abstract base every custom layout model must implement. It inherits from both BasePageModel and BaseModelWithOptions and requires two abstract methods:
get_options_type()— returns the options class associated with this model.predict_layout(conv_res, pages)— takes aConversionResultand a sequence ofPageobjects and returns a sequence ofLayoutPredictionobjects.
The __call__ method is already implemented on BaseLayoutModel: it delegates to predict_layout and stores each result into page.predictions.layout.
BaseLayoutOptions — the Pydantic base for all layout configuration classes. Provides two shared flags: keep_empty_clusters (default False) and skip_cell_assignment (default False). Custom options classes must inherit from BaseLayoutOptions and declare a kind class variable — this string is the selector used to pick the model at runtime.
Plugin Registration via Entry Points#
Docling uses pluggy for plugin discovery . Plugins register under the "docling" entry-point group .
Step 1 — Declare the entry point in your package's pyproject.toml:
[project.entry-points."docling"]
your_plugin_name = "your_package.module"
See the docs for setup.cfg / setup.py / Poetry equivalents.
Step 2 — Expose a layout_engines() function in the referenced module that returns a dict keyed "layout_engines":
def layout_engines():
return {"layout_engines": [YourLayoutModel]}
The function name and dict key must both be "layout_engines" — this is the plugin_attr_name passed to LayoutFactory.__init__. Docling's own built-in engines follow the same pattern in defaults.py.
The built-in entry point is declared as :
[project.entry-points.docling]
"docling_defaults" = "docling.models.plugins.defaults"
Factory Loading & allow_external_plugins#
get_layout_factory(allow_external_plugins) is an @lru_cache-decorated function that creates a LayoutFactory and calls load_from_plugins. It is parameterised by allow_external_plugins, which means one factory instance is cached per flag value.
Inside BaseFactory.load_from_plugins:
- A
PluginManageris created and allsetuptoolsentry points under the"docling"group are loaded . - For each discovered plugin, the module name is checked :
- If
allow_external_plugins=False(the default), any plugin whose module name does not start with"docling."is skipped with a warning. - If
allow_external_plugins=True, all discovered plugins are loaded.
- If
- The module's
layout_engines()function is called and each returned class is registered viaregister(), which maps the class's options type to the class and records plugin metadata.
allow_external_plugins is a field on PipelineOptions (default False), described as: "Allow loading external third-party plugins for OCR, layout, table structure, or picture description models. Disabled by default for security."
Enabling a Custom Plugin at Runtime#
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.document_converter import DocumentConverter, PdfFormatOption
pipeline_options = PdfPipelineOptions()
pipeline_options.allow_external_plugins = True # unlock external plugins
pipeline_options.layout_options = YourLayoutOptions() # your options class
doc_converter = DocumentConverter(
format_options={
InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options)
}
)
See also the official plugin docs for the CLI equivalent (--allow-external-plugins --layout-engine=NAME).
Key Constraints#
- Single layout model per pipeline —
LayoutFactory.create_instancepicks exactly one model based on the options type. Running multiple layout models in parallel is not supported. - Unique
kind— registering two models with the same options-typekindraises aValueError. Plugin names must be globally unique across the Docling ecosystem . - Factory is cached —
get_layout_factoryuses@lru_cache, so theallow_external_pluginsvalue must be set before the first pipeline is constructed.
Key Source Files#
| File | Purpose |
|---|---|
docling/models/base_layout_model.py | BaseLayoutModel abstract interface |
docling/datamodel/pipeline_options.py | BaseLayoutOptions and concrete option types |
docling/models/factories/layout_factory.py | LayoutFactory — thin subclass of BaseFactory |
docling/models/factories/base_factory.py | BaseFactory — generic plugin loading, filtering, registration |
docling/models/factories/__init__.py | get_layout_factory() — cached factory accessor |
docling/models/plugins/defaults.py | Built-in layout_engines() registration function |
docs/concepts/plugins.md | Official plugin authoring guide |
pyproject.toml | Built-in docling_defaults entry point declaration |