chore: bump docling

This commit is contained in:
Alex committed 2026-08-12 09:05:00 +01:00
1 parent 0d7fd529ef
commit ca53a3ac4b
7 files changed
+214 -1

No files matched your search

+1
View File
@@ -95,6 +95,7 @@ class Settings(BaseSettings):
# Pages docling's threaded pipeline buffers in flight; the library
# default (100) drives worker RSS to ~3 GB on a mid-size PDF.
DOCLING_PIPELINE_QUEUE_MAX_SIZE: int = 2
DOCLING_COMPILE_TORCH_MODELS: bool = False
DOCLING_TABULAR_MAX_BYTES: int = 2_000_000
DOCLING_MARKUP_MAX_BYTES: int = 8_000_000
ATTACHMENT_TEXT_MAX_BYTES: int = 5_000_000
+3
View File
@@ -252,6 +252,9 @@ def _docling_structured(path: Path, *, ocr_enabled: bool, include_tables: bool,
else:
from docling.document_converter import DocumentConverter
from application.parser.file.docling_parser import _apply_inference_settings
_apply_inference_settings()
converter = DocumentConverter()
doc = converter.convert(str(path)).document
markdown = doc.export_to_markdown()
+25
View File
@@ -43,6 +43,29 @@ def _apply_pipeline_caps(pipeline_options) -> None:
setattr(pipeline_options, name, value)
def _apply_inference_settings() -> None:
"""Set docling's global torch.compile toggle from ``DOCLING_COMPILE_TORCH_MODELS``.
docling compiles its layout/table/OCR models with ``torch.compile`` by
default. For DocsGPT's one-shot, per-file parses that warmup is never
amortized, and it hard-fails wherever TorchInductor cannot build: it emits
invalid Metal on Apple Silicon, and its C++ path does not quote the ``-L``
library path, so any install directory containing a space fails to build.
Guarded so docling builds without the inference settings are unaffected.
"""
from application.core.settings import settings
try:
from docling.datamodel.settings import settings as docling_settings
except ImportError: # pragma: no cover - docling always present here
return
inference = getattr(docling_settings, "inference", None)
if inference is not None and hasattr(inference, "compile_torch_models"):
inference.compile_torch_models = settings.DOCLING_COMPILE_TORCH_MODELS
def _tabular_content_size(file: Path) -> int:
"""Effective content size of a tabular file, in bytes.
@@ -256,6 +279,8 @@ class DoclingParser(BaseParser):
)
from docling.datamodel.pipeline_options import PdfPipelineOptions
_apply_inference_settings()
pipeline_options = PdfPipelineOptions(
do_ocr=self.ocr_enabled,
do_table_structure=self.table_structure,
+1 -1
View File
@@ -12,7 +12,7 @@ cryptography==50.0.0
dataclasses-json==0.6.7
daytona==0.203.0
defusedxml==0.7.1
docling==2.84.0
docling==2.119.0
rapidocr==3.9.2
onnxruntime==1.28.0
docx2txt==0.9
+16
View File
@@ -51,6 +51,22 @@ By default, Docling parser classes use RapidOCR options (language default: `engl
Parser internals like OCR language and force-full-page OCR are currently set by code defaults, not separate `.env` settings.
</Callout>
### Model compilation
Docling runs its layout, table, and OCR models through `torch.compile` by
default. DocsGPT turns that off. On x86-64 Linux, compiling raised the first
parse of a two-page PDF from 8.4s to 58.6s while steady-state parsing stayed
at 1.5s either way, so the warmup is overhead a per-file parse never recovers.
Compiling also fails outright on Apple Silicon, on install paths containing a
space, on Windows without MSVC, and in slim images with no C compiler.
```env
DOCLING_COMPILE_TORCH_MODELS=false
```
Set it to `true` only if you are parsing large batches on a machine where the
warmup pays for itself.
## Attachment Behavior by Model Support
When attachments are used in chat, behavior depends on the selected model/provider:
+120
View File
@@ -891,3 +891,123 @@ class TestTabularGateSeam:
with pytest.raises(DocumentParseError):
DoclingCSVParser().parse_file(path)
# =====================================================================
# torch.compile inference setting
# =====================================================================
@pytest.mark.unit
class TestApplyInferenceSettings:
"""_apply_inference_settings governs docling's torch.compile of its models.
docling >= 2.92 compiles its layout/OCR models with torch.compile by
default, which hard-fails TorchInductor's Metal codegen on Apple Silicon
and buys nothing for one-shot parses.
"""
def test_disables_torch_compile_by_default(self, monkeypatch):
from application.parser.file.docling_parser import _apply_inference_settings
class Inference:
compile_torch_models = True
class DoclingSettings:
inference = Inference()
docling_settings = DoclingSettings()
monkeypatch.setattr(
"docling.datamodel.settings.settings", docling_settings, raising=False
)
_apply_inference_settings()
assert docling_settings.inference.compile_torch_models is False
def test_opt_in_reenables_torch_compile(self, monkeypatch):
from application.core.settings import settings
from application.parser.file.docling_parser import _apply_inference_settings
monkeypatch.setattr(
settings, "DOCLING_COMPILE_TORCH_MODELS", True, raising=False
)
class Inference:
compile_torch_models = False
class DoclingSettings:
inference = Inference()
docling_settings = DoclingSettings()
monkeypatch.setattr(
"docling.datamodel.settings.settings", docling_settings, raising=False
)
_apply_inference_settings()
assert docling_settings.inference.compile_torch_models is True
def test_noop_on_docling_without_inference_settings(self, monkeypatch):
"""Builds predating the inference settings must be a silent no-op."""
from application.parser.file.docling_parser import _apply_inference_settings
class DoclingSettings:
pass
monkeypatch.setattr(
"docling.datamodel.settings.settings", DoclingSettings(), raising=False
)
_apply_inference_settings() # must not raise
def test_create_converter_applies_inference_settings(self, monkeypatch):
"""The cap is worthless unless the converter path actually calls it."""
from application.parser.file.docling_parser import DoclingParser
called = []
monkeypatch.setattr(
"application.parser.file.docling_parser._apply_inference_settings",
lambda: called.append(True),
)
monkeypatch.setattr(
"docling.document_converter.DocumentConverter",
MagicMock(),
)
DoclingParser(ocr_enabled=False)._create_converter()
assert called == [True]
def test_inference_settings_applied_before_options_are_built(self, monkeypatch):
"""Ordering is load-bearing, not incidental.
``compile_model`` is a ``default_factory`` that reads docling's global
at *option construction* time, so flipping the global after building
``PdfPipelineOptions`` is silently ignored. Pin the order.
"""
import docling.datamodel.pipeline_options as dpo
from application.parser.file.docling_parser import DoclingParser
events = []
monkeypatch.setattr(
"application.parser.file.docling_parser._apply_inference_settings",
lambda: events.append("settings"),
)
real_options = dpo.PdfPipelineOptions
def _tracking_options(*args, **kwargs):
events.append("options")
return real_options(*args, **kwargs)
monkeypatch.setattr(dpo, "PdfPipelineOptions", _tracking_options)
monkeypatch.setattr("docling.document_converter.DocumentConverter", MagicMock())
DoclingParser(ocr_enabled=False)._create_converter()
assert events == ["settings", "options"], (
"torch.compile must be disabled before PdfPipelineOptions is "
f"constructed, got {events}"
)
+48
View File
@@ -516,3 +516,51 @@ def test_bound_parse_payload_small_content_not_flagged():
out = bound_parse_payload({"output": "text", "content": "hi"}, max_chars=10)
assert out["content"] == "hi"
assert out["truncated"] is False
# ---------------------------------------------------------------------------
# vanilla-converter fallback honors the torch.compile toggle
# ---------------------------------------------------------------------------
@pytest.mark.unit
def test_vanilla_converter_applies_inference_settings(monkeypatch, tmp_path):
"""The fallback builds its own DocumentConverter, so it must disable
torch.compile itself — the configured-parser path can't do it for it."""
import sys
import types
called = []
monkeypatch.setattr(
"application.parser.file.docling_parser._apply_inference_settings",
lambda: called.append(True),
)
class _Doc:
texts = []
tables = []
pages = {}
def export_to_markdown(self):
return "# md"
def export_to_dict(self):
return {"texts": []}
class _Converter:
def convert(self, *a, **k):
assert called == [True], "converter built before torch.compile was disabled"
return types.SimpleNamespace(document=_Doc())
fake_docling = types.ModuleType("docling")
fake_dc_module = types.ModuleType("docling.document_converter")
fake_dc_module.DocumentConverter = _Converter
fake_docling.document_converter = fake_dc_module
monkeypatch.setitem(sys.modules, "docling", fake_docling)
monkeypatch.setitem(sys.modules, "docling.document_converter", fake_dc_module)
path = tmp_path / "doc.pdf"
path.write_bytes(b"%PDF")
out = dr._docling_structured(path, ocr_enabled=False, include_tables=False, parser=None)
assert out["markdown"] == "# md"
assert called == [True]