From 75c8e83515f88aeb4dac98d4ceb01fd4c0f2b668 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Thu, 30 Apr 2026 16:36:35 +0300 Subject: [PATCH] Allow uri override on create_document_from_source --- evaluations/evaluations/benchmark.py | 14 ++--- haiku_rag_slim/haiku/rag/client/__init__.py | 3 +- haiku_rag_slim/haiku/rag/client/documents.py | 37 +++++++++--- ...ment_from_source_uri_override_dedupes.yaml | 42 ++++++++++++++ ...ocument_from_source_with_uri_override.yaml | 42 ++++++++++++++ tests/test_client.py | 58 +++++++++++++++++++ 6 files changed, 181 insertions(+), 15 deletions(-) create mode 100644 tests/cassettes/test_client/test_client_create_document_from_source_uri_override_dedupes.yaml create mode 100644 tests/cassettes/test_client/test_client_create_document_from_source_with_uri_override.yaml diff --git a/evaluations/evaluations/benchmark.py b/evaluations/evaluations/benchmark.py index 5fcb8388..7849d04b 100644 --- a/evaluations/evaluations/benchmark.py +++ b/evaluations/evaluations/benchmark.py @@ -133,13 +133,12 @@ async def populate_db( progress.advance(task) continue - # Use the actual URI that will be stored in the database - if payload.source_path is not None: - lookup_uri = payload.source_path.absolute().as_uri() - else: - lookup_uri = payload.uri - - existing = await rag.get_document_by_uri(lookup_uri) + # `payload.uri` is the canonical document identifier and is now + # honored by both `create_document` and (via the `uri=` override) + # `create_document_from_source`, so it's also the right key to + # look up an existing document, regardless of whether the source + # is a file path or inline content. + existing = await rag.get_document_by_uri(payload.uri) if existing is not None: assert existing.id chunks = await rag.chunk_repository.get_by_document_id(existing.id) @@ -153,6 +152,7 @@ async def populate_db( source=payload.source_path, title=payload.title, metadata=payload.metadata, + uri=payload.uri, ) else: assert payload.content is not None diff --git a/haiku_rag_slim/haiku/rag/client/__init__.py b/haiku_rag_slim/haiku/rag/client/__init__.py index 2b9c4e9e..206ce331 100644 --- a/haiku_rag_slim/haiku/rag/client/__init__.py +++ b/haiku_rag_slim/haiku/rag/client/__init__.py @@ -195,10 +195,11 @@ class HaikuRAG: source: str | Path, title: str | None = None, metadata: dict | None = None, + uri: str | None = None, ) -> Document | list[Document]: from haiku.rag.client.documents import create_document_from_source - return await create_document_from_source(self, source, title, metadata) + return await create_document_from_source(self, source, title, metadata, uri=uri) async def update_document( self, diff --git a/haiku_rag_slim/haiku/rag/client/documents.py b/haiku_rag_slim/haiku/rag/client/documents.py index 1cd0861e..4666c259 100644 --- a/haiku_rag_slim/haiku/rag/client/documents.py +++ b/haiku_rag_slim/haiku/rag/client/documents.py @@ -184,6 +184,7 @@ async def create_document_from_source( source: str | Path, title: str | None = None, metadata: dict | None = None, + uri: str | None = None, ) -> Document | list[Document]: """Create or update document(s) from a file path, directory, or URL. @@ -192,6 +193,12 @@ async def create_document_from_source( - If MD5 changed, updates the document - If no document exists, creates a new one + If ``uri`` is provided, it overrides the URI auto-derived from the source + (which is normally ``file://`` for local files or the URL for remote + sources). This is useful when callers want to persist documents under a + logical identifier (e.g. an ArXiv ID) rather than the on-disk path. Not + supported for directory sources, which produce one document per file. + Returns a single Document for files/URLs, a list for directories. """ metadata = metadata or {} @@ -200,7 +207,7 @@ async def create_document_from_source( parsed_url = urlparse(source_str) if parsed_url.scheme in ("http", "https"): return await _create_or_update_document_from_url( - client, source_str, title=title, metadata=metadata + client, source_str, title=title, metadata=metadata, uri=uri ) elif parsed_url.scheme == "file": source_path = Path(parsed_url.path) @@ -208,6 +215,11 @@ async def create_document_from_source( source_path = Path(source) if isinstance(source, str) else source if source_path.is_dir(): + if uri is not None: + raise ValueError( + "uri override is not supported for directory sources; each file " + "produces its own document with its own auto-derived URI." + ) from haiku.rag.monitor import FileFilter documents = [] @@ -224,7 +236,7 @@ async def create_document_from_source( return documents return await _create_document_from_file( - client, source_path, title=title, metadata=metadata + client, source_path, title=title, metadata=metadata, uri=uri ) @@ -233,8 +245,13 @@ async def _create_document_from_file( source_path: Path, title: str | None = None, metadata: dict | None = None, + uri: str | None = None, ) -> Document: - """Create or update a document from a single file path.""" + """Create or update a document from a single file path. + + ``uri`` overrides the auto-derived ``file://`` URI; it's used as the + canonical document identifier for lookup and storage. + """ from haiku.rag.embeddings import embed_chunks metadata = metadata or {} @@ -246,7 +263,8 @@ async def _create_document_from_file( if not source_path.exists(): raise ValueError(f"File does not exist: {source_path}") - uri = source_path.absolute().as_uri() + if uri is None: + uri = source_path.absolute().as_uri() md5_hash = hashlib.md5(source_path.read_bytes(), usedforsecurity=False).hexdigest() content_type, _ = mimetypes.guess_type(str(source_path)) @@ -315,12 +333,17 @@ async def _create_or_update_document_from_url( url: str, title: str | None = None, metadata: dict | None = None, + uri: str | None = None, ) -> Document: - """Create or update a document from a URL by downloading and parsing the content.""" + """Create or update a document from a URL by downloading and parsing the content. + + ``uri`` overrides the URL as the stored document identifier. + """ from haiku.rag.client.processing import get_extension_from_content_type_or_url from haiku.rag.embeddings import embed_chunks metadata = metadata or {} + stored_uri = uri if uri is not None else url converter = get_converter(client._config) supported_extensions = converter.supported_extensions @@ -334,7 +357,7 @@ async def _create_or_update_document_from_url( content_type = response.headers.get("content-type", "").lower() # Check if document already exists - existing_doc = await client.get_document_by_uri(url) + existing_doc = await client.get_document_by_uri(stored_uri) if existing_doc and existing_doc.metadata.get("md5") == md5_hash: updated = False if title is not None and title != existing_doc.title: @@ -396,7 +419,7 @@ async def _create_or_update_document_from_url( ) document = Document( content=stored_content, - uri=url, + uri=stored_uri, title=title, metadata=metadata, ) diff --git a/tests/cassettes/test_client/test_client_create_document_from_source_uri_override_dedupes.yaml b/tests/cassettes/test_client/test_client_create_document_from_source_uri_override_dedupes.yaml new file mode 100644 index 00000000..b111897c --- /dev/null +++ b/tests/cassettes/test_client/test_client_create_document_from_source_uri_override_dedupes.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '100' + content-type: + - application/json + host: + - localhost:11434 + method: POST + parsed_body: + encoding_format: base64 + input: + - Stable content for dedup test. + model: qwen3-embedding:4b + uri: http://localhost:11434/v1/embeddings + response: + headers: + content-type: + - application/json + transfer-encoding: + - chunked + parsed_body: + data: + - embedding: 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 + index: 0 + object: embedding + model: qwen3-embedding:4b + object: list + usage: + prompt_tokens: 9 + total_tokens: 9 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_client/test_client_create_document_from_source_with_uri_override.yaml b/tests/cassettes/test_client/test_client_create_document_from_source_with_uri_override.yaml new file mode 100644 index 00000000..3c6fee6b --- /dev/null +++ b/tests/cassettes/test_client/test_client_create_document_from_source_with_uri_override.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '110' + content-type: + - application/json + host: + - localhost:11434 + method: POST + parsed_body: + encoding_format: base64 + input: + - Synthetic content for URI-override test. + model: qwen3-embedding:4b + uri: http://localhost:11434/v1/embeddings + response: + headers: + content-type: + - application/json + transfer-encoding: + - chunked + parsed_body: + data: + - embedding: 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 + index: 0 + object: embedding + model: qwen3-embedding:4b + object: list + usage: + prompt_tokens: 10 + total_tokens: 10 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_client.py b/tests/test_client.py index 360163dc..dd9a347e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -263,6 +263,64 @@ async def test_client_update_title_noop_behavior(temp_db_path): assert got.title == "Title B" +@pytest.mark.vcr() +async def test_client_create_document_from_source_with_uri_override(temp_db_path): + """A `uri` override is honored as the canonical document identifier.""" + async with HaikuRAG(temp_db_path, create=True) as client: + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) / "2412.06611v2.pdf-ish.txt" + temp_path.write_text("Synthetic content for URI-override test.") + + doc = await client.create_document_from_source( + source=temp_path, uri="2412.06611v2" + ) + assert isinstance(doc, Document) + assert doc.uri == "2412.06611v2" + assert doc.uri != temp_path.as_uri() + + # The override URI is the lookup key for subsequent reads. + looked_up = await client.get_document_by_uri("2412.06611v2") + assert looked_up is not None + assert looked_up.id == doc.id + + # The original file URI is NOT a key. + assert await client.get_document_by_uri(temp_path.as_uri()) is None + + +@pytest.mark.vcr() +async def test_client_create_document_from_source_uri_override_dedupes(temp_db_path): + """Re-creating from the same source with the same override is a no-op.""" + async with HaikuRAG(temp_db_path, create=True) as client: + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) / "doc.txt" + temp_path.write_text("Stable content for dedup test.") + + doc1 = await client.create_document_from_source( + source=temp_path, uri="paper-id-1" + ) + doc2 = await client.create_document_from_source( + source=temp_path, uri="paper-id-1" + ) + assert isinstance(doc1, Document) and isinstance(doc2, Document) + assert doc1.id == doc2.id + + +@pytest.mark.vcr() +async def test_client_create_document_from_source_uri_override_rejected_for_dir( + temp_db_path, +): + """Directory sources reject the `uri` override (would collide on multiple files).""" + async with HaikuRAG(temp_db_path, create=True) as client: + with tempfile.TemporaryDirectory() as temp_dir: + (Path(temp_dir) / "a.txt").write_text("a") + (Path(temp_dir) / "b.txt").write_text("b") + + with pytest.raises(ValueError, match="directory sources"): + await client.create_document_from_source( + source=Path(temp_dir), uri="some-uri" + ) + + @pytest.mark.vcr() async def test_client_create_document_from_source_unsupported(temp_db_path): """Test creating a document from an unsupported file type."""