VLM Formula and Code Extraction#
Docling's VLM code/formula enrichment stage runs after the main layout analysis pass and enriches already-detected CodeItem and FORMULA TextItem elements with accurate text extracted via a Vision Language Model — converting formulas to LaTeX and code blocks to their original source text with language tag. It is off by default and must be enabled explicitly via PdfPipelineOptions.do_code_enrichment and/or do_formula_enrichment .
The central implementation lives in CodeFormulaVlmModel, which inherits from BaseItemAndImageEnrichmentModel. It is instantiated in StandardPdfPipeline._init_models() and prepended to the enrichment_pipe list, running after per-page assembly completes.
Activation flags in PdfPipelineOptions:
| Flag | Default | Effect |
|---|---|---|
do_code_enrichment | False | Enriches CodeItem elements |
do_formula_enrichment | False | Enriches TextItem with label FORMULA |
code_formula_options | codeformulav2 preset | Selects model and inference backend |
Preset System and Available Models#
Model selection uses a named-preset system defined in stage_model_specs.py. Each StageModelPreset bundles a VlmModelSpec (HuggingFace repo ID, prompt template, response format, engine-specific overrides) with default inference engine and image scaling configuration .
Two presets are registered for CODE_FORMULA :
| Preset ID | Model | Repo | Default Engine |
|---|---|---|---|
codeformulav2 (default) | CodeFormulaV2 | docling-project/CodeFormulaV2 | AUTO_INLINE |
granite_docling | Granite-Docling-258M | ibm-granite/granite-docling-258M | AUTO_INLINE |
CodeFormulaV2 uses stop strings </doctag> and <end_of_utterance> . granite_docling shares the GRANITE_DOCLING_MODEL_SPEC_BASE with the VLM-convert stage (max 8192 new tokens, Ollama alias ibm/granite-docling:258m) .
The default options instance is constructed at import time: _default_code_formula_options = CodeFormulaVlmOptions.from_preset("codeformulav2") .
To switch presets or add a custom engine:
from docling.datamodel.pipeline_options import CodeFormulaVlmOptions, PdfPipelineOptions
# Use the granite_docling preset instead
code_formula_options = CodeFormulaVlmOptions.from_preset("granite_docling")
# Or use a specific engine (e.g., MLX on Apple Silicon)
from docling.datamodel.vlm_engine_options import MlxVlmEngineOptions
code_formula_options = CodeFormulaVlmOptions.from_preset(
"codeformulav2",
engine_options=MlxVlmEngineOptions()
)
pipeline_options = PdfPipelineOptions(
do_code_enrichment=True,
do_formula_enrichment=True,
code_formula_options=code_formula_options,
)
See the full preset comparison example for side-by-side output comparison.
Inference Backends (VlmEngineType)#
The VlmEngineType enum in vlm/base.py defines the available backends:
| Engine | Value | Notes |
|---|---|---|
AUTO_INLINE | "auto_inline" | Picks best local backend automatically |
TRANSFORMERS | "transformers" | HuggingFace Transformers |
MLX | "mlx" | Apple Silicon (separate model checkpoint may apply) |
VLLM | "vllm" | vLLM server |
API / API_OLLAMA / API_LMSTUDIO / API_OPENAI | "api*" | OpenAI-compatible HTTP endpoints |
VlmModelSpec.engine_overrides lets presets specify per-engine model checkpoints, torch dtypes, and generation options. For example, the granite_docling preset maps VlmEngineType.MLX → ibm-granite/granite-docling-258M-mlx and VlmEngineType.API_OLLAMA → model name ibm/granite-docling:258m .
Engine resolution happens in StagePresetMixin.from_preset(). If no engine_options are provided, it infers them from preset.default_engine_type. Auto-inline selects MLX only when the model has an explicit MLX export declared .
Processing Flow#
-
Element filtering —
is_processable()checks if an item is aCodeItem(withextract_code=True) or aTextItemwithlabel == FORMULA(withextract_formulas=True). -
Image cropping — inherited from
BaseItemAndImageEnrichmentModel; each element's bounding box is cropped from the page image at 1.67× scale (≈120 DPI) with an 18% expansion factor . -
Prompt construction —
_get_prompt()returns<code>for code items and<formula>for formulas. These sentinel prompts signal extraction intent to the model. -
Batch inference — elements are batched in groups of 5 and passed as
VlmEngineInputobjects toengine.predict_batch(), withtemperature=0.0andmax_new_tokens=2048. TheBaseVlmEngineabstract interface (base.py) hides backend differences. -
Post-processing —
_post_process()removes</code>,</formula>,<loc_0><loc_0><loc_500><loc_500>tokens, and truncates at<end_of_utterance>. -
Language detection (code only) —
_extract_code_language()parses a leading<_language_>tag (e.g.,<_javascript_>) from the model output. The matched value is converted to aCodeLanguageLabelenum via_get_code_language_enum(), defaulting toUNKNOWNif unrecognized . -
Document update —
item.textis set to the cleaned output;CodeItem.code_languageis also updated .
Key Files and Entry Points#
| File | Purpose |
|---|---|
docling/models/stages/code_formula/code_formula_vlm_model.py | Core enrichment model — filtering, inference, post-processing |
docling/datamodel/pipeline_options.py | CodeFormulaVlmOptions class + preset registration |
docling/datamodel/stage_model_specs.py | CODE_FORMULA_CODEFORMULAV2 and CODE_FORMULA_GRANITE_DOCLING preset definitions |
docling/models/inference_engines/vlm/base.py | VlmEngineType, BaseVlmEngine, VlmEngineInput/Output contracts |
docling/pipeline/standard_pdf_pipeline.py | Pipeline wiring — where CodeFormulaVlmModel is instantiated |
docs/examples/code_formula_granite_docling.py | Runnable example comparing both presets |
tests/test_code_formula.py | Integration test verifying code + formula extraction |