mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-12 20:15:26 +00:00
Merge pull request #2667 from arc53/chore/bump-docling-2.119
chore: bump docling
This commit is contained in:
7 files changed
+214
-1
No files matched your search
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}"
|
||||
)
|
||||
@@ -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]
|
||||
Reference in new issue
Block a user