Spaces:
Running
Running
| """Native PDF parser using Docling. | |
| Converts a PDF file to a Markdown text representation suitable for passing | |
| to a text-capable extraction backend (Gemini with a text part, or Ollama). | |
| Docling handles layout analysis, table detection, and text ordering so the | |
| output preserves the document's logical structure rather than raw PDF stream | |
| order, which matters for invoices where totals and line items appear in tables. | |
| The ``DocumentConverter`` is built once (module-level singleton) because it | |
| loads layout models at construction time -- rebuilding it per document would | |
| re-load models on every call. The first call after import triggers a one-time | |
| model download if the models are not already cached locally. | |
| See ``docs/02_architecture.md`` section 5 (Docling) and build-plan task 2.2. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from pathlib import Path | |
| logger = logging.getLogger(__name__) | |
| # Module-level singleton: built on first call to parse_pdf so model loading | |
| # happens lazily (not at import time) and only once per process. | |
| _converter = None | |
| def _get_converter(): | |
| """Return the shared DocumentConverter, initialising it on first call. | |
| OCR is disabled because this parser is only used for the ``native_pdf`` | |
| modality -- the text is already embedded in the file and OCR would just | |
| pull in a large model dependency that fails on some platforms. | |
| Returns: | |
| The cached ``DocumentConverter`` instance. | |
| """ | |
| global _converter | |
| if _converter is None: | |
| from docling.datamodel.base_models import InputFormat | |
| from docling.datamodel.pipeline_options import PdfPipelineOptions | |
| from docling.document_converter import DocumentConverter, PdfFormatOption | |
| pipeline_options = PdfPipelineOptions() | |
| pipeline_options.do_ocr = False | |
| logger.info("docling: initialising DocumentConverter (OCR disabled)") | |
| _converter = DocumentConverter( | |
| format_options={ | |
| InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options) | |
| } | |
| ) | |
| logger.info("docling: DocumentConverter ready") | |
| return _converter | |
| def parse_pdf(path: Path) -> str: | |
| """Parse a native PDF to a Markdown text representation. | |
| Uses Docling's layout-aware conversion so tables, columns, and reading | |
| order are preserved in the output text that the extraction backend receives. | |
| Args: | |
| path: Path to the PDF file to parse. | |
| Returns: | |
| Markdown-formatted text of the document's content. | |
| Raises: | |
| RuntimeError: If Docling fails to convert the document. | |
| """ | |
| converter = _get_converter() | |
| logger.debug("docling: parsing %s", path) | |
| result = converter.convert(str(path)) | |
| text = result.document.export_to_markdown() | |
| logger.debug("docling: extracted %d chars from %s", len(text), path) | |
| return text | |