From c778fbf52724313f6360a29d1bb980bbd4c268a8 Mon Sep 17 00:00:00 2001 From: Yiorgis Gozadinos Date: Thu, 27 Aug 2026 15:12:22 +0300 Subject: [PATCH] Refuse a path and `sources` together `sources` was ignored beside a database path, so `sources=["nope"]` opened the path and read as though the selection had been honoured. A path and a name already conflict inside `DatabaseScope.resolve`; this is the same rule where a caller can reach it. --- docs/python.md | 5 +++-- haiku_rag_slim/haiku/rag/client/__init__.py | 14 ++++++++++---- tests/multi_db/test_scope.py | 10 ++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/docs/python.md b/docs/python.md index ed0e906e..16ce527f 100644 --- a/docs/python.md +++ b/docs/python.md @@ -261,8 +261,9 @@ their documents. none: `search` returns no results, and `ask` and `analyze` run with no evidence from any database. -On the constructor, `sources=[]` raises `ValueError` instead. A selection of -nothing to search is a legitimate question; a client over no database is not. +On the constructor, `sources` is rejected alongside a database path, and `[]` +raises `ValueError`. A selection of nothing to search is a legitimate question; +a client over no database is not. #### Inspecting the client scope diff --git a/haiku_rag_slim/haiku/rag/client/__init__.py b/haiku_rag_slim/haiku/rag/client/__init__.py index bf5c0092..25fc3f33 100644 --- a/haiku_rag_slim/haiku/rag/client/__init__.py +++ b/haiku_rag_slim/haiku/rag/client/__init__.py @@ -142,10 +142,11 @@ class HaikuRAG: read_only: Whether to open the database in read-only mode. sources: Names from ``config.lancedb.databases`` this client covers, None for all of them. Only that setting names databases, so a - name raises when ``lancedb.uri`` placed the database. Ignored - when ``db_path`` says which database to open. ``[]`` raises: a - client over no database can do nothing, unlike ``sources=[]`` on - a search, which is a selection of nothing to search. + name raises when ``lancedb.uri`` placed the database, and is + rejected alongside ``db_path``, which says the same thing + another way. ``[]`` raises too: a client over no database can do + nothing, unlike ``sources=[]`` on a search, which is a selection + of nothing to search. """ self._configured = config if config is not None else get_config() # What the caller configured, kept intact: entering derives a @@ -153,6 +154,11 @@ class HaikuRAG: # same set rather than the answer from last time. self._config = self._configured self._requested_db_path = Path(db_path) if db_path is not None else None + if self._requested_db_path is not None and sources is not None: + raise AmbiguousDatabaseError( + "a path and `sources` both say which databases to open; pass " + "one of them" + ) self._skip_validation = skip_validation self._create = create self._read_only = read_only diff --git a/tests/multi_db/test_scope.py b/tests/multi_db/test_scope.py index c37dbfd6..aee4bb74 100644 --- a/tests/multi_db/test_scope.py +++ b/tests/multi_db/test_scope.py @@ -6,6 +6,7 @@ from pydantic import ValidationError from haiku.rag.client import HaikuRAG from haiku.rag.client.scope import DatabaseScope from haiku.rag.config.models import AppConfig, LanceDBConfig +from haiku.rag.store.exceptions import AmbiguousDatabaseError from haiku.rag.utils import locate_database from tests.multi_db.helpers import ( _config, @@ -237,6 +238,15 @@ class TestPlacingADatabase: async with HaikuRAG(temp_db_path, create=True) as rag: assert await rag.reader_for(None) is rag + def test_a_path_and_sources_cannot_both_choose(self, tmp_path): + """`sources` used to be ignored beside a path, so selecting a database + that is not the one at the path opened the path anyway.""" + config = _config(tmp_path, ["alpha", "beta"]) + + for sources in ([], ["alpha"], ["nope"]): + with pytest.raises(AmbiguousDatabaseError, match="pass one of them"): + HaikuRAG(tmp_path / "alpha.lancedb", config=config, sources=sources) + @pytest.mark.asyncio async def test_one_database_refuses_a_name_it_does_not_cover(self, tmp_path): """Answering with itself would hand back the wrong database's reader for