diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ea10427..d51849e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixed - Concurrent ingestion of the same URI no longer creates duplicate documents; the URI is re-checked under the write lock and a colliding create becomes an update. +- Document filters can reference `id` (`list_documents`/`count_documents`/`search`/`analyze` `filter=`); the `document_meta` identity column is renamed `document_id` → `id` with a migration. ## [0.63.2] - 2026-07-03 diff --git a/app/backend/pyproject.toml b/app/backend/pyproject.toml index 87075dc2..397019f1 100644 --- a/app/backend/pyproject.toml +++ b/app/backend/pyproject.toml @@ -8,7 +8,7 @@ dependencies = [ "uvicorn[standard]>=0.40.0", "pydantic-ai-slim[ag-ui,anthropic,openai]>=1.81.0", "python-dotenv>=1.2.1", - "haiku.rag-slim>=0.63.2", + "haiku.rag-slim>=0.64.0", "logfire[pydantic-ai]>=3.17.0", ] diff --git a/evaluations/pyproject.toml b/evaluations/pyproject.toml index 18f07510..0fd52c5e 100644 --- a/evaluations/pyproject.toml +++ b/evaluations/pyproject.toml @@ -2,7 +2,7 @@ name = "haiku.rag-evals" description = "Benchmarking and evaluation scripts for haiku.rag" -version = "0.63.2" +version = "0.64.0" authors = [{ name = "Yiorgis Gozadinos", email = "ggozadinos@gmail.com" }] license = { text = "MIT" } requires-python = ">=3.12" diff --git a/haiku_rag_slim/haiku/rag/client/rebuild.py b/haiku_rag_slim/haiku/rag/client/rebuild.py index 0af3cd83..38be804e 100644 --- a/haiku_rag_slim/haiku/rag/client/rebuild.py +++ b/haiku_rag_slim/haiku/rag/client/rebuild.py @@ -520,7 +520,7 @@ async def _flush_rebuild_batch( ) meta_records.append( DocumentMetaRecord( - document_id=doc.id, + id=doc.id, uri=doc.uri, title=doc.title, metadata=json.dumps(doc.metadata), @@ -535,7 +535,7 @@ async def _flush_rebuild_batch( .execute(doc_records) ) await ( - client.store.document_meta_table.merge_insert("document_id") + client.store.document_meta_table.merge_insert("id") .when_matched_update_all() .when_not_matched_insert_all() .execute(meta_records) diff --git a/haiku_rag_slim/haiku/rag/doctor.py b/haiku_rag_slim/haiku/rag/doctor.py index 8b7c3f06..64ba5b36 100644 --- a/haiku_rag_slim/haiku/rag/doctor.py +++ b/haiku_rag_slim/haiku/rag/doctor.py @@ -454,18 +454,16 @@ async def run_db_checks( doc_ids = set(await _column_values(store.documents_table, "id")) meta_rows = ( await store.document_meta_table.query() - .select(["document_id", "metadata", "uri", "title"]) + .select(["id", "metadata", "uri", "title"]) .to_list() ) - meta_doc_ids = {row["document_id"] for row in meta_rows} + meta_doc_ids = {row["id"] for row in meta_rows} content_type_by_doc = { - row["document_id"]: json.loads(row.get("metadata") or "{}").get( - "content_type", "" - ) + row["id"]: json.loads(row.get("metadata") or "{}").get("content_type", "") for row in meta_rows } - uri_by_doc = {row["document_id"]: row.get("uri") for row in meta_rows} - title_by_doc = {row["document_id"]: row.get("title") for row in meta_rows} + uri_by_doc = {row["id"]: row.get("uri") for row in meta_rows} + title_by_doc = {row["id"]: row.get("title") for row in meta_rows} notify("Reading chunks") chunk_rows = ( diff --git a/haiku_rag_slim/haiku/rag/store/engine.py b/haiku_rag_slim/haiku/rag/store/engine.py index 860afd6d..72c0dd40 100644 --- a/haiku_rag_slim/haiku/rag/store/engine.py +++ b/haiku_rag_slim/haiku/rag/store/engine.py @@ -87,7 +87,7 @@ class DocumentMetaRecord(LanceModel): write-once content/blobs in `documents`. Updating these (metadata, title, source_revision) must not rewrite the multi-MB docling row.""" - document_id: str + id: str uri: str | None = None title: str | None = None metadata: str = Field(default="{}") @@ -610,7 +610,7 @@ class Store: "document_meta", schema=DocumentMetaRecord ) await self.document_meta_table.create_index( - "document_id", config=BTree(), replace=True + "id", config=BTree(), replace=True ) await self.document_meta_table.create_index( "uri", config=BTree(), replace=True diff --git a/haiku_rag_slim/haiku/rag/store/repositories/chunk.py b/haiku_rag_slim/haiku/rag/store/repositories/chunk.py index ad319121..1960b5c9 100644 --- a/haiku_rag_slim/haiku/rag/store/repositories/chunk.py +++ b/haiku_rag_slim/haiku/rag/store/repositories/chunk.py @@ -272,13 +272,13 @@ class ChunkRepository: # whenever the top-N window lacked `limit` matching chunks. docs_df = await ( self.store.document_meta_table.query() - .select(["document_id"]) + .select(["id"]) .where(filter) .to_pandas() ) if docs_df.empty: return [] - id_list = ", ".join(f"'{d}'" for d in docs_df["document_id"]) + id_list = ", ".join(f"'{d}'" for d in docs_df["id"]) chunk_filter = f"document_id IN ({id_list})" if query_vector is not None: @@ -346,8 +346,8 @@ class ChunkRepository: # Get document info from the mutable attributes table doc_rows = await ( self.store.document_meta_table.query() - .select(["document_id", "uri", "title", "metadata"]) - .where(f"document_id = '{document_id}'") + .select(["id", "uri", "title", "metadata"]) + .where(f"id = '{document_id}'") .limit(1) .to_list() ) @@ -503,14 +503,14 @@ class ChunkRepository: documents_map: dict[str, dict] = {} if document_ids: id_list = "', '".join(document_ids) - where_clause = f"document_id IN ('{id_list}')" + where_clause = f"id IN ('{id_list}')" doc_rows = await ( self.store.document_meta_table.query() - .select(["document_id", "uri", "title", "metadata"]) + .select(["id", "uri", "title", "metadata"]) .where(where_clause) .to_list() ) - documents_map = {str(row["document_id"]): row for row in doc_rows} + documents_map = {str(row["id"]): row for row in doc_rows} # Build final results with document info chunks_with_scores = [] diff --git a/haiku_rag_slim/haiku/rag/store/repositories/document.py b/haiku_rag_slim/haiku/rag/store/repositories/document.py index 8e18e028..76e0b7fe 100644 --- a/haiku_rag_slim/haiku/rag/store/repositories/document.py +++ b/haiku_rag_slim/haiku/rag/store/repositories/document.py @@ -94,7 +94,7 @@ class DocumentRepository: updated_at: str, ) -> DocumentMetaRecord: return DocumentMetaRecord( - document_id=doc_id, + id=doc_id, uri=entity.uri, title=entity.title, metadata=json.dumps(entity.metadata), @@ -105,9 +105,7 @@ class DocumentRepository: async def _meta_by_id(self, doc_id: str) -> DocumentMetaRecord | None: safe_id = escape_sql_string(doc_id) results = await query_to_pydantic( - self.store.document_meta_table.query() - .where(f"document_id = '{safe_id}'") - .limit(1), + self.store.document_meta_table.query().where(f"id = '{safe_id}'").limit(1), DocumentMetaRecord, ) return results[0] if results else None @@ -146,9 +144,7 @@ class DocumentRepository: ) except Exception: safe_id = escape_sql_string(doc_id) - await self.store.document_meta_table.delete( - f"document_id = '{safe_id}'" - ) + await self.store.document_meta_table.delete(f"id = '{safe_id}'") raise entity.id = doc_id entity.created_at = datetime.fromisoformat(now) @@ -178,7 +174,7 @@ class DocumentRepository: await self.store.documents_table.add(doc_records) except Exception: ids = ", ".join(f"'{escape_sql_string(d)}'" for d in doc_ids) - await self.store.document_meta_table.delete(f"document_id IN ({ids})") + await self.store.document_meta_table.delete(f"id IN ({ids})") raise return documents @@ -271,7 +267,7 @@ class DocumentRepository: # from create()/migration; inserting on no-match would manufacture a # ghost row (visible to list_all/count) for an id with no documents row. await ( - self.store.document_meta_table.merge_insert("document_id") + self.store.document_meta_table.merge_insert("id") .when_matched_update_all() .execute([record]) ) @@ -310,7 +306,7 @@ class DocumentRepository: # Delete the document row, its mutable attributes safe_id = escape_sql_string(entity_id) await self.store.documents_table.delete(f"id = '{safe_id}'") - await self.store.document_meta_table.delete(f"document_id = '{safe_id}'") + await self.store.document_meta_table.delete(f"id = '{safe_id}'") return True async def list_all( @@ -348,13 +344,13 @@ class DocumentRepository: if not include_content: return [ - self._merge_to_document(DocumentRecord(id=m.document_id, content=""), m) + self._merge_to_document(DocumentRecord(id=m.id, content=""), m) for m in meta_records ] documents: list[Document] = [] for meta in meta_records: - safe_id = escape_sql_string(meta.document_id) + safe_id = escape_sql_string(meta.id) doc_results = await query_to_pydantic( self.store.documents_table.query().where(f"id = '{safe_id}'").limit(1), DocumentRecord, @@ -362,7 +358,7 @@ class DocumentRepository: doc_record = ( doc_results[0] if doc_results - else DocumentRecord(id=meta.document_id, content="") + else DocumentRecord(id=meta.id, content="") ) documents.append(self._merge_to_document(doc_record, meta)) return documents @@ -385,7 +381,7 @@ class DocumentRepository: return None meta = meta_results[0] - safe_id = escape_sql_string(meta.document_id) + safe_id = escape_sql_string(meta.id) doc_results = await query_to_pydantic( self.store.documents_table.query().where(f"id = '{safe_id}'").limit(1), DocumentRecord, @@ -433,7 +429,7 @@ class DocumentRepository: "document_meta", schema=DocumentMetaRecord ) await self.store.document_meta_table.create_index( - "document_id", config=BTree(), replace=True + "id", config=BTree(), replace=True ) await self.store.document_meta_table.create_index( "uri", config=BTree(), replace=True diff --git a/haiku_rag_slim/haiku/rag/store/upgrades/__init__.py b/haiku_rag_slim/haiku/rag/store/upgrades/__init__.py index 115f9a1c..4280ff35 100644 --- a/haiku_rag_slim/haiku/rag/store/upgrades/__init__.py +++ b/haiku_rag_slim/haiku/rag/store/upgrades/__init__.py @@ -96,6 +96,9 @@ from haiku.rag.store.upgrades.v0_50_0 import ( from haiku.rag.store.upgrades.v0_58_0 import ( upgrade_split_document_meta as upgrade_0_58_0_split_document_meta, ) +from haiku.rag.store.upgrades.v0_64_0 import ( + upgrade_rename_document_meta_id as upgrade_0_64_0_rename_document_meta_id, +) upgrades.append(upgrade_0_20_0_docling) upgrades.append(upgrade_0_23_1_contextualize) @@ -106,3 +109,4 @@ upgrades.append(upgrade_0_45_0_extract_picture_bytes) upgrades.append(upgrade_0_48_0_heading_hierarchy) upgrades.append(upgrade_0_50_0_canonical_metadata_keys) upgrades.append(upgrade_0_58_0_split_document_meta) +upgrades.append(upgrade_0_64_0_rename_document_meta_id) diff --git a/haiku_rag_slim/haiku/rag/store/upgrades/v0_58_0.py b/haiku_rag_slim/haiku/rag/store/upgrades/v0_58_0.py index 99fc78e8..f0de5aaa 100644 --- a/haiku_rag_slim/haiku/rag/store/upgrades/v0_58_0.py +++ b/haiku_rag_slim/haiku/rag/store/upgrades/v0_58_0.py @@ -31,10 +31,8 @@ async def _apply_split_document_meta(store: Store) -> None: # Resume support: skip documents whose meta row already exists. existing_meta = { - row["document_id"] - for row in await store.document_meta_table.query() - .select(["document_id"]) - .to_list() + row["id"] + for row in await store.document_meta_table.query().select(["id"]).to_list() } rows = await store.documents_table.query().select(["id", *present]).to_list() @@ -45,7 +43,7 @@ async def _apply_split_document_meta(store: Store) -> None: meta = row.get("metadata") records.append( DocumentMetaRecord( - document_id=row["id"], + id=row["id"], uri=row.get("uri"), title=row.get("title"), metadata=meta if isinstance(meta, str) and meta else "{}", diff --git a/haiku_rag_slim/haiku/rag/store/upgrades/v0_64_0.py b/haiku_rag_slim/haiku/rag/store/upgrades/v0_64_0.py new file mode 100644 index 00000000..350172cb --- /dev/null +++ b/haiku_rag_slim/haiku/rag/store/upgrades/v0_64_0.py @@ -0,0 +1,35 @@ +import logging + +from lancedb.index import BTree + +from haiku.rag.store.engine import Store +from haiku.rag.store.upgrades import Upgrade + +logger = logging.getLogger(__name__) + + +async def _apply_rename_document_meta_id(store: Store) -> None: + """Rename the `document_meta` identity column `document_id` → `id`. + + In `document_meta` the column is the document's own identity (1:1 with the + row), so `id` matches the `documents` table and the public `Document.id`. + Callers filter/list/count against `document_meta`, so with the old name a + filter on `id` raised "No field named id". Idempotent: skips if the column + is already `id`. + """ + schema = await store.document_meta_table.schema() + if "id" in schema.names: + logger.info("document_meta.id already present; nothing to rename") + return + + await store.document_meta_table.alter_columns( + {"path": "document_id", "rename": "id"} # ty: ignore[invalid-argument-type] + ) + await store.document_meta_table.create_index("id", config=BTree(), replace=True) + + +upgrade_rename_document_meta_id = Upgrade( + version="0.64.0", + apply=_apply_rename_document_meta_id, + description="Rename the document_meta identity column document_id to id", +) diff --git a/haiku_rag_slim/pyproject.toml b/haiku_rag_slim/pyproject.toml index e61bab08..50c44501 100644 --- a/haiku_rag_slim/pyproject.toml +++ b/haiku_rag_slim/pyproject.toml @@ -2,7 +2,7 @@ name = "haiku.rag-slim" description = "Opinionated agentic RAG powered by LanceDB, Pydantic AI, and Docling - Minimal dependencies" -version = "0.63.2" +version = "0.64.0" authors = [{ name = "Yiorgis Gozadinos", email = "ggozadinos@gmail.com" }] license = { text = "MIT" } readme = { file = "README.md", content-type = "text/markdown" } diff --git a/pyproject.toml b/pyproject.toml index 0db45c20..f7686b07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "haiku.rag" description = "Opinionated agentic RAG powered by LanceDB, Pydantic AI, and Docling" -version = "0.63.2" +version = "0.64.0" authors = [{ name = "Yiorgis Gozadinos", email = "ggozadinos@gmail.com" }] license = { text = "MIT" } readme = { file = "README.md", content-type = "text/markdown" } @@ -30,7 +30,7 @@ classifiers = [ ] dependencies = [ - "haiku.rag-slim[docling,voyageai,mxbai,cohere,zeroentropy,tui,cross-encoder]==0.63.2", + "haiku.rag-slim[docling,voyageai,mxbai,cohere,zeroentropy,tui,cross-encoder]==0.64.0", ] [project.scripts] @@ -38,9 +38,9 @@ haiku-rag = "haiku.rag.cli:cli" [project.optional-dependencies] tui = ["textual>=8.2.4"] -s3 = ["haiku.rag-slim[s3]==0.63.2"] -cross-encoder = ["haiku.rag-slim[cross-encoder]==0.63.2"] -ingester = ["haiku.rag-slim[ingester]==0.63.2"] +s3 = ["haiku.rag-slim[s3]==0.64.0"] +cross-encoder = ["haiku.rag-slim[cross-encoder]==0.64.0"] +ingester = ["haiku.rag-slim[ingester]==0.64.0"] [build-system] requires = ["hatchling"] diff --git a/tests/cassettes/test_filter/test_filter_by_document_id.yaml b/tests/cassettes/test_filter/test_filter_by_document_id.yaml new file mode 100644 index 00000000..b23b8a72 --- /dev/null +++ b/tests/cassettes/test_filter/test_filter_by_document_id.yaml @@ -0,0 +1,122 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '103' + content-type: + - application/json + host: + - localhost:11434 + method: POST + parsed_body: + encoding_format: base64 + input: + - Filterable content about pelicans + 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: 7 + total_tokens: 7 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '98' + content-type: + - application/json + host: + - localhost:11434 + method: POST + parsed_body: + encoding_format: base64 + input: + - Other content about penguins + 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: 6 + total_tokens: 6 + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + connection: + - keep-alive + content-length: + - '77' + content-type: + - application/json + host: + - localhost:11434 + method: POST + parsed_body: + encoding_format: base64 + input: + - content + 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: 2 + total_tokens: 2 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/store/test_document_meta_split.py b/tests/store/test_document_meta_split.py index 52321d16..4da4941a 100644 --- a/tests/store/test_document_meta_split.py +++ b/tests/store/test_document_meta_split.py @@ -156,6 +156,6 @@ async def test_get_by_uri_with_orphan_meta_returns_none(temp_db_path): async with Store(temp_db_path, create=True, skip_migration_check=True) as store: repo = DocumentRepository(store) await store.document_meta_table.add( - [DocumentMetaRecord(document_id="ghost", uri="u-ghost", metadata="{}")] + [DocumentMetaRecord(id="ghost", uri="u-ghost", metadata="{}")] ) assert await repo.get_by_uri("u-ghost") is None diff --git a/tests/store/test_v0_58_0_migration.py b/tests/store/test_v0_58_0_migration.py index dab7ac5b..286b4d05 100644 --- a/tests/store/test_v0_58_0_migration.py +++ b/tests/store/test_v0_58_0_migration.py @@ -52,7 +52,7 @@ class TestV0_58_0Migration: meta_rows = await store.document_meta_table.query().to_list() assert len(meta_rows) == 1 row = meta_rows[0] - assert row["document_id"] == "doc-1" + assert row["id"] == "doc-1" assert row["uri"] == "s3://b/one" assert row["title"] == "One" assert json.loads(row["metadata"]) == {"source_revision": "r1", "md5": "a"} @@ -95,7 +95,7 @@ class TestV0_58_0Migration: meta_rows = await store.document_meta_table.query().to_list() assert len(meta_rows) == 1 - assert meta_rows[0]["document_id"] == "doc-1" + assert meta_rows[0]["id"] == "doc-1" @pytest.mark.asyncio @@ -115,14 +115,14 @@ class TestV0_58_0MigrationEdgeCases: ) # Pretend a prior run already moved doc "a". await store.document_meta_table.add( - [DocumentMetaRecord(document_id="a", uri="u-a", metadata="{}")] + [DocumentMetaRecord(id="a", uri="u-a", metadata="{}")] ) await store.set_haiku_version("0.57.0") async with Store(temp_db_path, skip_migration_check=True) as store: await store.migrate() rows = await store.document_meta_table.query().to_list() - by_id = {r["document_id"]: r for r in rows} + by_id = {r["id"]: r for r in rows} assert set(by_id) == {"a", "b"} # exactly one row each, no duplicates assert len(rows) == 2 diff --git a/tests/store/test_v0_64_0_migration.py b/tests/store/test_v0_64_0_migration.py new file mode 100644 index 00000000..ee8af8b6 --- /dev/null +++ b/tests/store/test_v0_64_0_migration.py @@ -0,0 +1,51 @@ +import pytest +from lancedb.pydantic import LanceModel + +from haiku.rag.store.engine import Store +from haiku.rag.store.upgrades.v0_64_0 import _apply_rename_document_meta_id + + +class LegacyMetaRecord(LanceModel): + """The pre-0.63.2 `document_meta` record (identity column `document_id`).""" + + document_id: str + uri: str | None = None + title: str | None = None + metadata: str = "{}" + created_at: str = "" + updated_at: str = "" + + +async def _seed_legacy_meta(store: Store, doc_id: str) -> None: + await store.db.drop_table("document_meta") + store.document_meta_table = await store.db.create_table( + "document_meta", schema=LegacyMetaRecord + ) + await store.document_meta_table.add( + [LegacyMetaRecord(document_id=doc_id, uri="u", metadata="{}")] + ) + + +@pytest.mark.asyncio +async def test_renames_document_id_to_id_and_keeps_rows(temp_db_path): + async with Store(temp_db_path, create=True, skip_migration_check=True) as store: + await _seed_legacy_meta(store, "doc-1") + + await _apply_rename_document_meta_id(store) + + names = {f.name for f in await store.document_meta_table.schema()} + assert "id" in names and "document_id" not in names + rows = await store.document_meta_table.query().to_list() + assert [r["id"] for r in rows] == ["doc-1"] + + +@pytest.mark.asyncio +async def test_idempotent_when_already_renamed(temp_db_path): + async with Store(temp_db_path, create=True, skip_migration_check=True) as store: + await _seed_legacy_meta(store, "doc-1") + await _apply_rename_document_meta_id(store) + # Second run must be a no-op, not an error. + await _apply_rename_document_meta_id(store) + + names = {f.name for f in await store.document_meta_table.schema()} + assert "id" in names and "document_id" not in names diff --git a/tests/test_doctor.py b/tests/test_doctor.py index 0f6ddc2f..d6d0d3a2 100644 --- a/tests/test_doctor.py +++ b/tests/test_doctor.py @@ -114,7 +114,7 @@ async def _build_db( ] ) await docs_tbl.add([DocumentRecord(id="d1", content="hello")]) - await meta_tbl.add([DocumentMetaRecord(document_id="d1", uri="test://d1")]) + await meta_tbl.add([DocumentMetaRecord(id="d1", uri="test://d1")]) await items_tbl.add( [ DocumentItemRecord( @@ -240,7 +240,7 @@ async def _add_doc(db, doc_id, *, items, metadata=None, chunks=None): await meta_tbl.add( [ DocumentMetaRecord( - document_id=doc_id, + id=doc_id, uri=f"test://{doc_id}", metadata=json.dumps(metadata or {}), ) @@ -1143,9 +1143,7 @@ async def _build_dup_db(path, docs: dict[str, list[int]], *, vector_dim: int = 8 ) for doc_id, idxs in docs.items(): await docs_tbl.add([DocumentRecord(id=doc_id, content="x")]) - await meta_tbl.add( - [DocumentMetaRecord(document_id=doc_id, uri=f"test://{doc_id}")] - ) + await meta_tbl.add([DocumentMetaRecord(id=doc_id, uri=f"test://{doc_id}")]) await items_tbl.add( [ DocumentItemRecord( diff --git a/tests/test_filter.py b/tests/test_filter.py index 4fd96fbb..6e2e7b88 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -37,6 +37,32 @@ async def test_search_with_uri_filter(temp_db_path): assert result.document_uri == "https://other.com/java.html" +@pytest.mark.vcr() +async def test_filter_by_document_id(temp_db_path): + """A document's identity is `id` everywhere user-facing, so filtering by + `id` must resolve against the document_meta table (list/count/search).""" + async with HaikuRAG(db_path=temp_db_path, create=True) as client: + doc = await client.create_document( + content="Filterable content about pelicans", + uri="https://example.com/pelican.html", + title="Pelican Guide", + ) + await client.create_document( + content="Other content about penguins", + uri="https://example.com/penguin.html", + title="Penguin Guide", + ) + + listed = await client.list_documents(filter=f"id = '{doc.id}'") + assert [d.id for d in listed] == [doc.id] + + assert await client.count_documents(filter=f"id = '{doc.id}'") == 1 + + results = await client.search("content", limit=5, filter=f"id = '{doc.id}'") + assert len(results) > 0 + assert all(r.document_id == doc.id for r in results) + + @pytest.mark.vcr() async def test_search_with_title_filter(temp_db_path): """Test filtering by document title.""" diff --git a/uv.lock b/uv.lock index 56f3f261..39317cb8 100644 --- a/uv.lock +++ b/uv.lock @@ -751,7 +751,7 @@ name = "cuda-bindings" version = "13.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-pathfinder", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "cuda-pathfinder" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, @@ -782,37 +782,37 @@ wheels = [ [package.optional-dependencies] cublas = [ - { name = "nvidia-cublas", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cublas" }, ] cudart = [ - { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime" }, ] cufft = [ - { name = "nvidia-cufft", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cufft" }, ] cufile = [ - { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cufile" }, ] cupti = [ - { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti" }, ] curand = [ - { name = "nvidia-curand", marker = "sys_platform == 'linux'" }, + { name = "nvidia-curand" }, ] cusolver = [ - { name = "nvidia-cusolver", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusolver" }, ] cusparse = [ - { name = "nvidia-cusparse", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse" }, ] nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink" }, ] nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc" }, ] nvtx = [ - { name = "nvidia-nvtx", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvtx" }, ] [[package]] @@ -1275,7 +1275,7 @@ name = "ffmpeg-python" version = "0.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "future", marker = "python_full_version < '3.14'" }, + { name = "future" }, ] sdist = { url = "https://files.pythonhosted.org/packages/dd/5e/d5f9105d59c1325759d838af4e973695081fbbc97182baf73afc78dec266/ffmpeg-python-0.2.0.tar.gz", hash = "sha256:65225db34627c578ef0e11c8b1eb528bb35e024752f6f10b78c011f6f64c4127", size = 21543, upload-time = "2019-07-06T00:19:08.989Z" } wheels = [ @@ -1580,7 +1580,7 @@ wheels = [ [[package]] name = "haiku-rag" -version = "0.63.2" +version = "0.64.0" source = { editable = "." } dependencies = [ { name = "haiku-rag-slim", extra = ["cohere", "cross-encoder", "docling", "mxbai", "tui", "voyageai", "zeroentropy"] }, @@ -1647,7 +1647,7 @@ dev = [ [[package]] name = "haiku-rag-evals" -version = "0.63.2" +version = "0.64.0" source = { editable = "evaluations" } dependencies = [ { name = "datasets" }, @@ -1670,7 +1670,7 @@ requires-dist = [ [[package]] name = "haiku-rag-slim" -version = "0.63.2" +version = "0.64.0" source = { editable = "haiku_rag_slim" } dependencies = [ { name = "docling-core" }, @@ -2292,15 +2292,15 @@ name = "langchain-core" version = "1.4.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jsonpatch", marker = "python_full_version < '3.14'" }, - { name = "langchain-protocol", marker = "python_full_version < '3.14'" }, - { name = "langsmith", marker = "python_full_version < '3.14'" }, - { name = "packaging", marker = "python_full_version < '3.14'" }, - { name = "pydantic", marker = "python_full_version < '3.14'" }, - { name = "pyyaml", marker = "python_full_version < '3.14'" }, - { name = "tenacity", marker = "python_full_version < '3.14'" }, - { name = "typing-extensions", marker = "python_full_version < '3.14'" }, - { name = "uuid-utils", marker = "python_full_version < '3.14'" }, + { name = "jsonpatch" }, + { name = "langchain-protocol" }, + { name = "langsmith" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "uuid-utils" }, ] sdist = { url = "https://files.pythonhosted.org/packages/12/e3/bea6d0080acf183332f24dcd74c208aee5857cf8f783c3fb0bd86027d8fb/langchain_core-1.4.8.tar.gz", hash = "sha256:5bf1f8411077c904182ad8f975943d36adcbf579c4e017b3a118b719229ebf9a", size = 957974, upload-time = "2026-06-18T19:39:23.636Z" } wheels = [ @@ -2312,7 +2312,7 @@ name = "langchain-protocol" version = "0.0.18" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.14'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d2/59/b5959aea96faa9146e2e49a7a22882b3528c62efafe9a6a95beab30c2305/langchain_protocol-0.0.18.tar.gz", hash = "sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6", size = 6150, upload-time = "2026-06-18T17:08:26.959Z" } wheels = [ @@ -2324,7 +2324,7 @@ name = "langchain-text-splitters" version = "1.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "langchain-core", marker = "python_full_version < '3.14'" }, + { name = "langchain-core" }, ] sdist = { url = "https://files.pythonhosted.org/packages/26/9f/6c545900fefb7b00ddfa3f16b80d61338a0ec68c31c5451eeeab99082760/langchain_text_splitters-1.1.2.tar.gz", hash = "sha256:782a723db0a4746ac91e251c7c1d57fd23636e4f38ed733074e28d7a86f41627", size = 293580, upload-time = "2026-04-16T14:20:39.162Z" } wheels = [ @@ -2336,20 +2336,20 @@ name = "langsmith" version = "0.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "python_full_version < '3.14'" }, - { name = "distro", marker = "python_full_version < '3.14'" }, - { name = "httpx", marker = "python_full_version < '3.14'" }, - { name = "orjson", marker = "python_full_version < '3.14' and platform_python_implementation != 'PyPy'" }, - { name = "packaging", marker = "python_full_version < '3.14'" }, - { name = "pydantic", marker = "python_full_version < '3.14'" }, - { name = "requests", marker = "python_full_version < '3.14'" }, - { name = "requests-toolbelt", marker = "python_full_version < '3.14'" }, - { name = "sniffio", marker = "python_full_version < '3.14'" }, - { name = "typing-extensions", marker = "python_full_version < '3.14'" }, - { name = "uuid-utils", marker = "python_full_version < '3.14'" }, - { name = "websockets", marker = "python_full_version < '3.14'" }, - { name = "xxhash", marker = "python_full_version < '3.14'" }, - { name = "zstandard", marker = "python_full_version < '3.14'" }, + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "sniffio" }, + { name = "typing-extensions" }, + { name = "uuid-utils" }, + { name = "websockets" }, + { name = "xxhash" }, + { name = "zstandard" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/26/b72987d947278f63ec1e85f01ce85ca7ab2621c7efc0845d4a3a8e5d5dfb/langsmith-0.9.1.tar.gz", hash = "sha256:e5eb905224d156bcece4985285c55b51fffcb06c9353b2c4adb42e1c48b0d05d", size = 4557557, upload-time = "2026-06-23T17:04:23.233Z" } wheels = [ @@ -2951,7 +2951,7 @@ name = "nvidia-cudnn-cu13" version = "9.19.0.56" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cublas" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, @@ -2963,7 +2963,7 @@ name = "nvidia-cufft" version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, @@ -2993,9 +2993,9 @@ name = "nvidia-cusolver" version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "nvidia-cusparse", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cublas" }, + { name = "nvidia-cusparse" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, @@ -3007,7 +3007,7 @@ name = "nvidia-cusparse" version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, @@ -4503,7 +4503,7 @@ name = "requests-toolbelt" version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "requests", marker = "python_full_version < '3.14'" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } wheels = [ @@ -4809,8 +4809,8 @@ name = "secretstorage" version = "3.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "jeepney", marker = "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "cryptography" }, + { name = "jeepney" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } wheels = [ @@ -5679,16 +5679,16 @@ name = "voyageai" version = "0.3.7" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp", marker = "python_full_version < '3.14'" }, - { name = "aiolimiter", marker = "python_full_version < '3.14'" }, - { name = "ffmpeg-python", marker = "python_full_version < '3.14'" }, - { name = "langchain-text-splitters", marker = "python_full_version < '3.14'" }, - { name = "numpy", marker = "python_full_version < '3.14'" }, - { name = "pillow", marker = "python_full_version < '3.14'" }, - { name = "pydantic", marker = "python_full_version < '3.14'" }, - { name = "requests", marker = "python_full_version < '3.14'" }, - { name = "tenacity", marker = "python_full_version < '3.14'" }, - { name = "tokenizers", marker = "python_full_version < '3.14'" }, + { name = "aiohttp" }, + { name = "aiolimiter" }, + { name = "ffmpeg-python" }, + { name = "langchain-text-splitters" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "tokenizers" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/16/1b46b3cd401e1717a68197c1fe336d7bb4e0a1833f8105e1738f5b1add05/voyageai-0.3.7.tar.gz", hash = "sha256:826cd97f97223f42b5babc5c459c9c80f3a8215ce5c0e007b0b276550f790d24", size = 26485, upload-time = "2025-12-16T18:43:05.26Z" } wheels = [