Merge pull request #437 from bd-mkt/invalid-attachments
fix for issue with parsing pdf attachment such as *.joboptions files
This commit is contained in:
commit
ab9fea4833
3 changed files with 41 additions and 5 deletions
|
|
@ -1,6 +1,10 @@
|
|||
# Changelog
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Embedded PDF attachment extension is derived from the attachment filename, not the parent's synthetic `...#attachment=<name>` URI; non-PDF attachments (e.g. `.joboptions`) are no longer misrouted to docling's PDF backend, and unsupported extensions are skipped.
|
||||
|
||||
## [0.57.0] - 2026-06-11
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -332,16 +332,25 @@ async def _ingest_fetch_result(
|
|||
stored_uri: str,
|
||||
existing_doc: Document | None,
|
||||
depth: int = 0,
|
||||
filename: str | None = None,
|
||||
) -> Document:
|
||||
"""Convert / chunk / embed / store a fetched document. Replaces an
|
||||
existing document if one is supplied. ``depth`` tracks position in an
|
||||
attachment chain so the reconciliation step can bound recursion."""
|
||||
attachment chain so the reconciliation step can bound recursion.
|
||||
|
||||
``filename``, when given, makes its suffix authoritative for the file
|
||||
extension (and thus the docling format), overriding the URI/content-type
|
||||
fallback. Callers pass it when ``result.uri`` cannot yield the right
|
||||
extension, e.g. embedded attachments whose name lives in a URI fragment."""
|
||||
from haiku.rag.embeddings import embed_chunks
|
||||
|
||||
converter = get_converter(client._config)
|
||||
file_extension = get_extension_from_content_type_or_url(
|
||||
result.uri, result.content_type
|
||||
)
|
||||
if filename is not None:
|
||||
file_extension = Path(filename).suffix.lower()
|
||||
else:
|
||||
file_extension = get_extension_from_content_type_or_url(
|
||||
result.uri, result.content_type
|
||||
)
|
||||
if file_extension not in converter.supported_extensions:
|
||||
raise UnsupportedSourceError(
|
||||
f"Unsupported content type/extension: {result.content_type}/{file_extension}"
|
||||
|
|
@ -508,12 +517,15 @@ async def _reconcile_pdf_attachments(
|
|||
stored_uri=child_uri,
|
||||
existing_doc=existing_child,
|
||||
depth=depth + 1,
|
||||
filename=name,
|
||||
)
|
||||
except UnsupportedSourceError:
|
||||
logger.warning(
|
||||
"Skipping attachment %r in %s: unsupported content type %r",
|
||||
"Skipping attachment %r in %s: unsupported extension %r "
|
||||
"(content type %r)",
|
||||
name,
|
||||
parent_doc.uri,
|
||||
Path(name).suffix.lower(),
|
||||
content_type,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ async def fake_ingest_fetch_result(
|
|||
stored_uri,
|
||||
existing_doc,
|
||||
depth=0,
|
||||
filename=None,
|
||||
):
|
||||
"""A stand-in for ``_ingest_fetch_result`` that skips docling/embedder
|
||||
entirely: it writes the document with content_type/md5/parent_uri set
|
||||
|
|
@ -306,6 +307,7 @@ async def test_unsupported_attachment_continues_loop(temp_db_path, monkeypatch):
|
|||
stored_uri,
|
||||
existing_doc,
|
||||
depth=0,
|
||||
filename=None,
|
||||
):
|
||||
if stored_uri.endswith("unsupported.xyz"):
|
||||
raise UnsupportedSourceError("nope")
|
||||
|
|
@ -329,6 +331,24 @@ async def test_unsupported_attachment_continues_loop(temp_db_path, monkeypatch):
|
|||
assert {c.uri for c in children} == {f"{parent_uri}#attachment=ok.txt"}
|
||||
|
||||
|
||||
async def test_joboptions_attachment_skipped_not_routed_as_pdf(temp_db_path, caplog):
|
||||
"""An attachment's own name drives its extension, not the synthetic
|
||||
``...#attachment=<name>`` URI (whose fragment the URL-suffix fallback drops,
|
||||
inheriting the parent's ``.pdf``). ``.joboptions`` is unsupported, so the
|
||||
child is skipped before any converter/embedder call (real ingest, no fake)."""
|
||||
import logging
|
||||
|
||||
pdf_bytes = build_pdf([("Press Quality.joboptions", b"/CompressObjects /Tags\n")])
|
||||
async with HaikuRAG(temp_db_path, create=True) as client:
|
||||
parent_uri = "file:///fixtures/brochure.pdf"
|
||||
parent = await _make_parent(client, parent_uri, pdf_bytes)
|
||||
|
||||
with caplog.at_level(logging.WARNING, logger="haiku.rag.client.documents"):
|
||||
await _reconcile_pdf_attachments(client, parent, pdf_bytes, depth=0)
|
||||
|
||||
assert await client.list_documents(filter=parent_uri_filter(parent_uri)) == []
|
||||
|
||||
|
||||
async def test_cascade_delete_removes_reconciled_children(temp_db_path, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"haiku.rag.client.documents._ingest_fetch_result",
|
||||
|
|
|
|||
Loading…
Reference in a new issue