VLM Token Limits and Truncation#
Token limits cap how many tokens a VLM can generate per page. When a limit is hit, the pipeline detects it via the VlmStopReason enum and surfaces a PARTIAL_SUCCESS result rather than silently returning truncated output.
VlmStopReason enum#
Defined in docling/datamodel/base_models.py, this str enum has five values:
| Value | String | Meaning |
|---|---|---|
LENGTH | "length" | Max token budget exhausted — output is truncated |
STOP_SEQUENCE | "stop_sequence" | A configured stop string triggered generation end |
END_OF_SEQUENCE | "end_of_sequence" | Model emitted its end-of-text token normally |
CONTENT_FILTERED | "content_filter" | API provider blocked the output |
UNSPECIFIED | "unspecified" | Default; backends that don't expose finish reason |
The stop_reason field lives on VlmPrediction (the per-page prediction object) and defaults to UNSPECIFIED.
Configuring the token budget#
max_new_tokens is set on InlineVlmOptions with a default of 4096. Override it per model spec:
GRANITEDOCLING_TRANSFORMERS/GRANITEDOCLING_2STAGE_TRANSFORMERS—8192NANONETS_OCR2_TRANSFORMERS/NANONETS_OCR2_MLX—15000LIGHTONOCR_TRANSFORMERS/LIGHTONOCR_MLX/LIGHTONOCR_VLLM_API—4096
For API-based models (ApiVlmOptions), the limit is passed through the params dict (e.g., max_tokens=4096) because the API server controls generation; there is no max_new_tokens field on ApiVlmOptions.
stop_strings on both InlineVlmOptions and ApiVlmOptions provides early stopping before the token budget is exhausted (e.g. ["</doctag>", "<|end_of_text|>"] for GraniteDocling) . When a stop string fires, the reason becomes STOP_SEQUENCE.
How backends populate stop_reason#
Inference backends produce a string value which is later promoted to the enum in _prediction_from_engine_output:
| Backend | Source | → VlmStopReason |
|---|---|---|
| OpenAI-compatible API | finish_reason field | "length" → LENGTH; "content_filter" → CONTENT_FILTERED; anything else → END_OF_SEQUENCE |
| vLLM engine | output.outputs[0].stop_reason (truthy/falsy) | Truthy → END_OF_SEQUENCE; falsy → LENGTH |
| MLX engine | Custom stop-string/criteria logic | "stop_string" or "custom_criteria" |
| HuggingFace Transformers | None exposed | UNSPECIFIED |
| TransformersExtractionModel (extraction path) | None exposed | Always yields UNSPECIFIED |
Note: The MLX engine produces string values
"stop_string"and"custom_criteria"that do not match anyVlmStopReasonvalue string (which would need"stop_sequence"), so they fall back toUNSPECIFIEDafter the enum conversion in_prediction_from_engine_output.
Truncation detection and pipeline consequences#
Standard VLM pipeline (vlm_pipeline.py): when stop_reason is LENGTH or CONTENT_FILTERED, the pipeline appends an ErrorItem with category=INFERENCE_FAILURE and sets ConversionStatus.PARTIAL_SUCCESS for that page .
Extraction VLM pipeline (extraction_vlm_pipeline.py): uses the same logic — LENGTH or STOP_SEQUENCE stop reasons on a page's VlmPrediction set ext_res.status = ConversionStatus.PARTIAL_SUCCESS . The raw VLM text is still stored in ExtractedPageData.raw_text even when JSON parsing fails .
The final _determine_status method in ExtractionVlmPipeline preserves PARTIAL_SUCCESS if it was set during the per-page loop .
Key source files#
| File | Purpose |
|---|---|
docling/datamodel/base_models.py | VlmStopReason enum, VlmPrediction model |
docling/datamodel/pipeline_options_vlm_model.py | max_new_tokens, stop_strings on InlineVlmOptions |
docling/datamodel/vlm_model_specs.py | Per-model token limit overrides |
docling/utils/api_image_request.py | _map_stop_reason — API finish_reason → enum |
docling/models/stages/vlm_convert/vlm_convert_model.py | String → enum conversion for new inference engine layer |
docling/pipeline/vlm_pipeline.py | Truncation → PARTIAL_SUCCESS in standard pipeline |
docling/pipeline/extraction_vlm_pipeline.py | Truncation → PARTIAL_SUCCESS in extraction pipeline |