From 9fefcdb629cead99a46e5ef5c6752c4f9b5c9a02 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Mon, 24 Aug 2026 15:36:09 +0300 Subject: [PATCH] Refuse to create a database without naming one `create=True` had nothing to act on across a set and was accepted anyway, leaving the first query to fail on whichever database was missing. --- docs/configuration/storage.md | 4 +++ haiku_rag_slim/haiku/rag/client/__init__.py | 6 ++++ tests/test_multi_db.py | 33 +++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/docs/configuration/storage.md b/docs/configuration/storage.md index 3f82f894..695932e1 100644 --- a/docs/configuration/storage.md +++ b/docs/configuration/storage.md @@ -232,6 +232,10 @@ the same queries: retrieval MAP 0.9914 with a reranker against 0.9918 for the same corpus in a single database, and 0.6044 without one against 0.9798. The cost is that a reranker scores candidates in proportion to the number of databases. +Creating names a database: `create=True` on a client covering the set raises +`AmbiguousDatabaseError`, and `HaikuRAG(config=config, create=True, +sources=["name"])` creates that one. + Converting, chunking and title generation are functions of the configuration rather than of a database, so they work on a client covering the set. Writing, rebuilding and vacuuming name one database: asking a set-covering client raises diff --git a/haiku_rag_slim/haiku/rag/client/__init__.py b/haiku_rag_slim/haiku/rag/client/__init__.py index d29aa1a8..1a65c714 100644 --- a/haiku_rag_slim/haiku/rag/client/__init__.py +++ b/haiku_rag_slim/haiku/rag/client/__init__.py @@ -230,6 +230,12 @@ class HaikuRAG: """ selected = self._selected() if len(selected) > 1: + if self._create: + raise AmbiguousDatabaseError( + "create=True creates one database, and this client covers " + f"{', '.join(sorted(selected))}; name the one to create with " + "sources=[name]" + ) self._federated = selected return self if selected: diff --git a/tests/test_multi_db.py b/tests/test_multi_db.py index 9298fa4d..f0741747 100644 --- a/tests/test_multi_db.py +++ b/tests/test_multi_db.py @@ -437,6 +437,39 @@ class TestDatabaseIndependentWork: assert rag.embedder is rag.store.embedder +class TestCreatingNeedsOneDatabase: + """Creating names a database. Covering a set, the flag had nothing to act on + and was accepted anyway, leaving the first query to fail on whichever + database turned out to be missing.""" + + @pytest.mark.asyncio + async def test_creating_a_set_is_refused(self, tmp_path): + config = _config(tmp_path, ["alpha", "beta"]) + + with pytest.raises(AmbiguousDatabaseError, match="alpha, beta"): + async with HaikuRAG(config=config, create=True): + pass + + @pytest.mark.asyncio + async def test_naming_one_of_the_set_creates_it(self, tmp_path): + config = _config(tmp_path, ["alpha", "beta"]) + + async with HaikuRAG(config=config, create=True, sources=["alpha"]) as rag: + assert await rag.count_documents() == 0 + + assert (tmp_path / "alpha.lancedb").exists() + assert not (tmp_path / "beta.lancedb").exists() + + @pytest.mark.asyncio + async def test_covering_a_set_without_creating_is_unaffected(self, tmp_path): + config = _config(tmp_path, ["alpha", "beta"]) + await _seed(config, "alpha", ["alpha one"]) + await _seed(config, "beta", ["beta one"]) + + async with HaikuRAG(config=config) as rag: + assert await rag.count_documents() == 2 + + class TestOperationsThatNeedOneDatabase: @pytest.mark.asyncio async def test_writing_names_the_databases_it_covers(self, tmp_path):