Skip to content

[MarkdownTableSerializer] Chunk overflow with landscape tables in non-landscape mode #3428

Description

@mathewmeconry

Bug

When we convert and chunk the attached PDF (the original is much bigger, but this is enough to trigger the bug) with the HybridChunker and a limit of 8192 tokens per chunk, the resulting chunk is bigger than 8192 because of the prefix.
I believe that the root issue is the table in the wrong orientation, which causes the "table headers" to be repeated multiple times with the MarkdownTableSerializer enabled:

Tabelle 2: Muster der Wahrnehmung in Entscheidungssituationen\n\n| Typ 4: »Abweichendes »breite Problemstreu- | Typ 4: »Abweichendes Verhalten der Kinder« »breite Problemstreu- | Typ 4: »Abweichendes Verhalten der Kinder« »breite Problemstreu- | Typ 4: »Abweichendes Verhalten der Kinder« »breite Problemstreu- | »Abweichendes Verhalten der Kinder« »breite Problemstreu- | »Abweichendes Verhalten der Kinder« »problematische Verhal- »breite Problemstreu- | »Abweichendes Verhalten der Kinder« »problematische Verhal- »besondere Problemlagen | Verhalten der Kinder« »problematische Verhal- »besondere Problemlagen | »problematische Verhal- tensweisen des Kindes »besondere Problemlagen | »problematische Verhal- tensweisen des Kindes »besondere Problemlagen | »problematische Verhal- tensweisen des Kindes »besondere Problemlagen | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- | »problematische Verhal- | Verhalten der Kinder« »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes | »problematische Verhal- tensweisen des Kindes vor allem mit Gesetzes- | »problematische Verhal- tensweisen des Kindes vor allem mit Gesetzes- | »problematische Verhal- tensweisen des Kindes vor allem mit Gesetzes- »besondere Problemlagen Drogenabhängigkeit, [...] | »problematische Verhal- tensweisen des Kindes vor allem mit Gesetzes- »besondere Problemlagen Drogenabhängigkeit, [...] | »problematische Verhal- tensweisen des Kindes vor allem mit Gesetzes- »besondere Problemlagen Drogenabhängigkeit, [...] | »problematische Verhal- tensweisen des Kindes vor allem mit Gesetzes- »besondere Problemlagen Drogenabhängigkeit, [...] | »problematische Verhal- tensweisen des Kindes vor allem mit Gesetzes- »besondere Problemlagen Drogenabhängigkeit, [...] | 

PDF:

f2552dc4-ad0d-4086-bc3c-cb4cfc2a8aa5.pdf

Steps to reproduce

  1. Download the PDF
  2. Run the following script
import logging

logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)

from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import (
    ThreadedPdfPipelineOptions,
    TableStructureV2Options,
    TableFormerMode,
    EasyOcrOptions,
)
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.accelerator_options import AcceleratorDevice, AcceleratorOptions
from docling_core.transforms.chunker.hybrid_chunker import HybridChunker
from docling_core.transforms.chunker.tokenizer.openai import OpenAITokenizer
from docling_core.transforms.serializer.markdown import (
    MarkdownParams,
    MarkdownTableSerializer,
)

from docling_core.transforms.chunker.hierarchical_chunker import (
    ChunkingDocSerializer,
    ChunkingSerializerProvider,
)
import tiktoken

log.info("Starting bug reproduction script...")
log.info("Configuring document converter with CPU acceleration...")
pdf_pipline_options = ThreadedPdfPipelineOptions(
    accelerator_options=AcceleratorOptions(
        device=AcceleratorDevice.CPU
    ),  # Use CPU acceleration
    threads=4,
    max_concurrent_results=4,
    ocr_batch_size=4,  # default 4
    layout_batch_size=4,  # default 4
    table_batch_size=4,  # currently not using GPU batching
    ocr_options=EasyOcrOptions(
        lang=[
            "en",
            "de",
            "fr",
            "es",
            "it",
            "pt",
        ],  # Add more languages as needed
    ),
)
pdf_pipline_options.enable_remote_services = True
pdf_pipline_options.allow_external_plugins = True
pdf_pipline_options.do_ocr = True
pdf_pipline_options.do_table_structure = True
pdf_pipline_options.do_code_enrichment = False
pdf_pipline_options.do_formula_enrichment = False
pdf_pipline_options.do_chart_extraction = False
pdf_pipline_options.table_structure_options = TableStructureV2Options(
    do_cell_matching=True, mode=TableFormerMode.ACCURATE
)
doc_converter = DocumentConverter(
    allowed_formats=[
        InputFormat.PDF,
        # Plain Text formats
        InputFormat.HTML,
        InputFormat.MD,
        InputFormat.CSV,
        # Office formats
        InputFormat.DOCX,
        InputFormat.XLSX,
        InputFormat.PPTX,
    ],
    format_options={
        InputFormat.PDF: PdfFormatOption(pipeline_options=pdf_pipline_options)
    },
)

log.info("Document converter configured. Starting conversion of test document...")
conv_result = doc_converter.convert(
    "./f2552dc4-ad0d-4086-bc3c-cb4cfc2a8aa5.pdf"
)
tokenizer = OpenAITokenizer(
    tokenizer=tiktoken.encoding_for_model("text-embedding-3-small"),
    max_tokens=8192,
)
log.info("Document conversion completed. Starting chunking of converted document...")


class SerializerProvider(ChunkingSerializerProvider):
    def get_serializer(self, doc):
        return ChunkingDocSerializer(
            doc=doc,
            table_serializer=MarkdownTableSerializer(),
        )

chunker = HybridChunker(
    tokenizer=tokenizer,
    max_tokens=8192,
    repeat_table_headers=True,
    merge_peers=True,
    serializer_provider=SerializerProvider(),
)
chunk_iter = chunker.chunk(dl_doc=conv_result.document)
for i, chunk in enumerate(chunk_iter):
    log.debug(f"Chunk {i}: {chunk}")
    enriched_text = chunker.contextualize(chunk=chunk)
    if tokenizer.count_tokens(enriched_text) > 8192:
        log.warning(f"Chunk {i} exceeds token limit after contextualization: {tokenizer.count_tokens(enriched_text)} tokens")

Output

WARNING:__main__:Chunk 4 exceeds token limit after contextualization: 8241 tokens
WARNING:__main__:Chunk 9 exceeds token limit after contextualization: 8193 tokens

Docling version

"docling=2.91.0",
"docling-core[chunking-openai]=2.74.1",

Python version

Python 3.12.13

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions