Merge release-1.9.6 into release-1.10.0

This commit is contained in:
Eric Hare
2026-06-02 09:58:24 -07:00
7 changed files with 105 additions and 39 deletions

View File

@ -25,8 +25,9 @@ The Docling dependency is required to use the Docling components in Langflow.
* **Langflow Desktop**: Set `LANGFLOW_DOCLING=True` in your `.env` file to enable Docling dependency installation. For more information, see [Set environment variables for Langflow Desktop](/environment-variables#set-environment-variables-for-langflow-desktop).
* **Langflow OSS**:
* If you installed `langflow` (`uv pip install langflow`), Docling is included automatically through bundled extras.
* If you installed `langflow` (`uv pip install langflow`), Docling is included automatically through bundled extras, without Docling's optional chunking dependencies.
* If you installed `langflow-base` directly, install Docling with an extra, for example `uv pip install "langflow-base[docling]"`.
* To use the **Chunk DoclingDocument** component, install the chunking extra with `uv pip install "langflow[docling-chunking]"` or `uv pip install "langflow-base[docling-chunking]"`.
* **macOS Intel (x86_64)**: Use the [Docling installation guide](https://docling-project.github.io/docling/installation/) to install the Docling dependency.
@ -103,6 +104,8 @@ For more information, see the [Docling serve project repository](https://github.
The **Chunk DoclingDocument** component splits `DoclingDocument` objects into chunks.
This component requires Docling's optional chunking dependencies. Install them with `uv pip install "langflow[docling-chunking]"` or `uv pip install "langflow-base[docling-chunking]"`.
It outputs the chunked documents as a [`Table`](/data-types#table).
For more information, see the [Docling core project repository](https://github.com/docling-project/docling-core).

View File

@ -109,7 +109,7 @@ Documentation = "https://docs.langflow.org"
[project.optional-dependencies]
docling = [
"langchain-docling>=1.1.0",
"langflow-base[docling]>=0.9.5",
"tesserocr>=2.8.0",
"rapidocr-onnxruntime>=1.4.4",
"ocrmac>=1.0.0; sys_platform == 'darwin'",
@ -120,6 +120,16 @@ docling = [
"torchvision>=0.21.0", # torchvision 0.21+ is compatible with PyTorch 2.6+
]
docling-chunking = [
"langflow-base[docling-chunking]>=0.9.5",
]
docling-image-description = [
"langflow-base[docling-image-description]>=0.9.5",
"langchain-docling>=1.1.0",
]
audio = [
"webrtcvad>=2.0.10",
]

View File

@ -355,9 +355,19 @@ openlayer = ["openlayer>=0.9.0,<1.0.0"]
# Document processing / OCR
docling = [
"docling-core>=2.36.1,<3.0.0",
"docling>=2.36.1,<3.0.0; sys_platform != 'darwin' or platform_machine != 'x86_64'",
"docling-core>=2.77.0,<3.0.0",
"docling-slim[cli,convert-core,extract-core,feat-ocr-rapidocr,format-latex,format-office,format-pdf,format-web,models-local,service-client]>=2.36.1,<3.0.0; sys_platform != 'darwin' or platform_machine != 'x86_64'",
]
docling-chunking = [
"langflow-base[docling]",
"docling-core[chunking]>=2.77.0,<3.0.0",
"tiktoken>=0.7.0",
]
docling-image-description = [
"langflow-base[docling]",
"langchain-docling>=1.1.0",
]
easyocr = ["easyocr>=1.7.2,<2.0.0; sys_platform != 'darwin' or platform_machine != 'x86_64'"]
# Additional tools

View File

@ -1,14 +1,14 @@
"""Tests for ChunkDoclingDocumentComponent HybridChunker parameters."""
import builtins
import sys
import types
import pytest
pytest.importorskip("tiktoken")
pytest.importorskip("docling_core")
from lfx.components.docling.chunk_docling_document import ChunkDoclingDocumentComponent
from lfx.components.docling.chunk_docling_document import (
ChunkDoclingDocumentComponent,
_load_docling_chunker_dependencies,
)
def _base_build_config():
@ -95,6 +95,11 @@ class TestChunkDoclingDocumentComponentHybridChunker:
captured["max_tokens"] = max_tokens
return "tokenizer"
class DummyDocMeta:
@classmethod
def model_validate(cls, meta):
return meta
hybrid_chunker_module = types.ModuleType("docling_core.transforms.chunker.hybrid_chunker")
hybrid_chunker_module.HybridChunker = DummyHybridChunker
monkeypatch.setitem(sys.modules, "docling_core.transforms.chunker.hybrid_chunker", hybrid_chunker_module)
@ -110,8 +115,8 @@ class TestChunkDoclingDocumentComponentHybridChunker:
huggingface_tokenizer_module,
)
monkeypatch.setattr(
"lfx.components.docling.chunk_docling_document.HierarchicalChunker",
DummyHierarchicalChunker,
"lfx.components.docling.chunk_docling_document._load_docling_chunker_dependencies",
lambda: (DummyDocMeta, DummyHierarchicalChunker),
)
monkeypatch.setattr(
"lfx.components.docling.chunk_docling_document.extract_docling_documents",
@ -182,3 +187,20 @@ class TestChunkDoclingDocumentComponentHybridChunker:
always_emit_headings_input=True,
)
assert captured["hierarchical_called"] is True
def test_missing_chunking_extra_has_actionable_error(self, monkeypatch):
original_import = builtins.__import__
def fake_import(name, globals_=None, locals_=None, fromlist=(), level=0):
if name in {
"docling_core.transforms.chunker.doc_chunk",
"docling_core.transforms.chunker.hierarchical_chunker",
}:
msg = "Module requires 'chunking' extra"
raise RuntimeError(msg)
return original_import(name, globals_, locals_, fromlist, level)
monkeypatch.setattr(builtins, "__import__", fake_import)
with pytest.raises(ImportError, match=r"langflow-base\[docling-chunking\]"):
_load_docling_chunker_dependencies()

View File

@ -292,7 +292,6 @@ def docling_worker(
from docling.document_converter import DocumentConverter, FormatOption, PdfFormatOption # noqa: F401
from docling.models.factories import get_ocr_factory # noqa: F401
from docling.pipeline.vlm_pipeline import VlmPipeline # noqa: F401
from langchain_docling.picture_description import PictureDescriptionLangChainOptions # noqa: F401
# Check for shutdown after imports
check_shutdown()
@ -332,7 +331,6 @@ def docling_worker(
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.document_converter import DocumentConverter, FormatOption, PdfFormatOption
from docling.models.factories import get_ocr_factory
from langchain_docling.picture_description import PictureDescriptionLangChainOptions
pipeline_options = PdfPipelineOptions()
pipeline_options.do_ocr = ocr_engine not in {"", "None"}
@ -342,6 +340,14 @@ def docling_worker(
pipeline_options.ocr_options = ocr_options
pipeline_options.do_picture_classification = do_picture_classification
try:
from langchain_docling.picture_description import PictureDescriptionLangChainOptions
except ImportError as e:
msg = (
"langchain-docling is not installed. Please install it with `pip install langchain-docling` "
"or `pip install 'langflow[docling-image-description]'`."
)
raise ImportError(msg) from e
pic_desc_llm = _deserialize_pydantic_model(pic_desc_config)
logger.info("Docling enabling the picture description stage.")
pipeline_options.do_picture_description = True

View File

@ -1,14 +1,27 @@
import json
import tiktoken
from docling_core.transforms.chunker import BaseChunker, DocMeta
from docling_core.transforms.chunker.hierarchical_chunker import HierarchicalChunker
from typing import Any
from lfx.base.data.docling_utils import extract_docling_documents
from lfx.custom import Component
from lfx.io import BoolInput, DropdownInput, HandleInput, IntInput, MessageTextInput, Output, StrInput
from lfx.schema import Data, DataFrame
_CHUNKING_INSTALL_HINT = (
"Install them with `uv pip install 'langflow[docling-chunking]'`, "
"`uv pip install 'langflow-base[docling-chunking]'`, or "
"`uv pip install 'docling-core[chunking]' tiktoken`."
)
def _load_docling_chunker_dependencies() -> tuple[type[Any], type[Any]]:
try:
from docling_core.transforms.chunker.doc_chunk import DocMeta as DocMetaCls
from docling_core.transforms.chunker.hierarchical_chunker import HierarchicalChunker as HierarchicalChunkerCls
except (ImportError, RuntimeError) as e:
msg = f"Docling chunking dependencies are not installed. {_CHUNKING_INSTALL_HINT}"
raise ImportError(msg) from e
return DocMetaCls, HierarchicalChunkerCls
class ChunkDoclingDocumentComponent(Component):
display_name: str = "Chunk DoclingDocument"
@ -144,25 +157,20 @@ class ChunkDoclingDocumentComponent(Component):
if warning:
self.status = warning
chunker: BaseChunker
doc_meta_cls, hierarchical_chunker_cls = _load_docling_chunker_dependencies()
chunker: Any
if self.chunker == "HybridChunker":
try:
from docling_core.transforms.chunker.hybrid_chunker import HybridChunker
except ImportError as e:
msg = (
"HybridChunker is not installed. Please install it with `uv pip install docling-core[chunking] "
"or `uv pip install transformers`"
)
except (ImportError, RuntimeError) as e:
msg = f"HybridChunker is not installed. {_CHUNKING_INSTALL_HINT}"
raise ImportError(msg) from e
max_tokens: int | None = self.max_tokens if self.max_tokens else None
if self.provider == "Hugging Face":
try:
from docling_core.transforms.chunker.tokenizer.huggingface import HuggingFaceTokenizer
except ImportError as e:
msg = (
"HuggingFaceTokenizer is not installed."
" Please install it with `uv pip install docling-core[chunking]`"
)
except (ImportError, RuntimeError) as e:
msg = f"HuggingFaceTokenizer is not installed. {_CHUNKING_INSTALL_HINT}"
raise ImportError(msg) from e
tokenizer = HuggingFaceTokenizer.from_pretrained(
model_name=self.hf_model_name,
@ -170,13 +178,10 @@ class ChunkDoclingDocumentComponent(Component):
)
elif self.provider == "OpenAI":
try:
import tiktoken
from docling_core.transforms.chunker.tokenizer.openai import OpenAITokenizer
except ImportError as e:
msg = (
"OpenAITokenizer is not installed."
" Please install it with `uv pip install docling-core[chunking]`"
" or `uv pip install transformers`"
)
except (ImportError, RuntimeError) as e:
msg = f"OpenAITokenizer is not installed. {_CHUNKING_INSTALL_HINT}"
raise ImportError(msg) from e
if max_tokens is None:
max_tokens = 128 * 1024 # context window length required for OpenAI tokenizers
@ -190,7 +195,7 @@ class ChunkDoclingDocumentComponent(Component):
)
elif self.chunker == "HierarchicalChunker":
chunker = HierarchicalChunker()
chunker = hierarchical_chunker_cls()
else:
msg = f"Unknown chunker: {self.chunker}"
raise ValueError(msg)
@ -200,7 +205,7 @@ class ChunkDoclingDocumentComponent(Component):
for doc in documents:
for chunk in chunker.chunk(dl_doc=doc):
enriched_text = chunker.contextualize(chunk=chunk)
meta = DocMeta.model_validate(chunk.meta)
meta = doc_meta_cls.model_validate(chunk.meta)
results.append(
Data(

View File

@ -140,9 +140,18 @@ class DoclingInlineComponent(BaseFileComponent):
try:
import importlib
from pydantic import TypeAdapter
from langchain_docling.picture_description import (
PictureDescriptionLangChainOptions,
)
try:
from langchain_docling.picture_description import PictureDescriptionLangChainOptions
except ImportError as e:
print(json.dumps({
"ok": False,
"error": (
"langchain-docling is not installed. Please install it with "
"`pip install langchain-docling` or "
"`pip install 'langflow[docling-image-description]'`."
)
}))
return
mod_name, cls_name = pic_desc_config["__class_path__"].rsplit(".", 1)
mod = importlib.import_module(mod_name)
cls = getattr(mod, cls_name)
@ -157,6 +166,7 @@ class DoclingInlineComponent(BaseFileComponent):
print(json.dumps({"ok": False, "error": f"Picture description setup failed: {e}"}))
return
if pipeline == "vlm":
try:
from docling.datamodel.pipeline_options import VlmPipelineOptions