Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/bigos/backends/docling.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ def uri_to_path(uri: str) -> Path:


def _page_1_indexed_from_item(item: Any) -> int | None:
"""Return the (1-indexed) page number of a DocItem, or None if unknown.

Docling's ``ProvenanceItem.page_no`` is already 1-indexed: it matches the
keys of ``DoclingDocument.pages`` (e.g. ``doc.pages[prov.page_no]``), so we
must NOT add +1 on top — doing so would shift every block's ``page`` by one
and cause RAG citations to point at the wrong page.
"""
prov = getattr(item, "prov", None) or []
if not prov:
return None
Expand All @@ -76,7 +83,9 @@ def _page_1_indexed_from_item(item: Any) -> int | None:
n = int(page_no)
except (TypeError, ValueError):
return None
return n + 1
if n < 1:
return None
return n


def _safe_model_dict(obj: Any) -> Any:
Expand Down
39 changes: 39 additions & 0 deletions tests/test_docling_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,42 @@ def test_cache_key_version_differs_with_vlm() -> None:
a = DoclingBackend()
b = DoclingBackend(enable_vlm=True)
assert a.version != b.version


def test_page_helper_passes_through_1_indexed() -> None:
"""Docling's ProvenanceItem.page_no is already 1-indexed (matches DoclingDocument.pages).

Regression test for an off-by-one where the helper added +1 on top, causing
every block to report its page one higher than the true page number — which
would point RAG citations at the wrong page (or off the end of the document).
"""
from types import SimpleNamespace

from bigos.backends.docling import _page_1_indexed_from_item

item = SimpleNamespace(prov=[SimpleNamespace(page_no=1)])
assert _page_1_indexed_from_item(item) == 1

item5 = SimpleNamespace(prov=[SimpleNamespace(page_no=5)])
assert _page_1_indexed_from_item(item5) == 5

no_prov = SimpleNamespace(prov=[])
assert _page_1_indexed_from_item(no_prov) is None

bad_value = SimpleNamespace(prov=[SimpleNamespace(page_no="not-a-number")])
assert _page_1_indexed_from_item(bad_value) is None


@pytest.mark.slow
async def test_parse_simple_text_page_number_is_one(simple_text_pdf: Path) -> None:
"""Single-page PDF must report ``page=1`` for its blocks (not 2)."""
backend = DoclingBackend()
src = Source(
uri=simple_text_pdf.as_uri(),
mime_type="application/pdf",
sha256=sha256_file(simple_text_pdf),
)
doc = await backend.run(src)
page_values = {b.page for b in doc.blocks if b.page is not None}
assert page_values, "expected at least one block with a page number"
assert page_values == {1}, f"expected all blocks on page 1, got {sorted(page_values)}"
Loading