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.
This commit is contained in:
Yiorgis Gozadinos 2026-08-27 15:12:22 +03:00
parent db7f0e0af6
commit c778fbf527
No known key found for this signature in database
3 changed files with 23 additions and 6 deletions

View file

@ -261,8 +261,9 @@ their documents.
none: `search` returns no results, and `ask` and `analyze` run with no evidence none: `search` returns no results, and `ask` and `analyze` run with no evidence
from any database. from any database.
On the constructor, `sources=[]` raises `ValueError` instead. A selection of On the constructor, `sources` is rejected alongside a database path, and `[]`
nothing to search is a legitimate question; a client over no database is not. raises `ValueError`. A selection of nothing to search is a legitimate question;
a client over no database is not.
#### Inspecting the client scope #### Inspecting the client scope

View file

@ -142,10 +142,11 @@ class HaikuRAG:
read_only: Whether to open the database in read-only mode. read_only: Whether to open the database in read-only mode.
sources: Names from ``config.lancedb.databases`` this client covers, sources: Names from ``config.lancedb.databases`` this client covers,
None for all of them. Only that setting names databases, so a None for all of them. Only that setting names databases, so a
name raises when ``lancedb.uri`` placed the database. Ignored name raises when ``lancedb.uri`` placed the database, and is
when ``db_path`` says which database to open. ``[]`` raises: a rejected alongside ``db_path``, which says the same thing
client over no database can do nothing, unlike ``sources=[]`` on another way. ``[]`` raises too: a client over no database can do
a search, which is a selection of nothing to search. 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() self._configured = config if config is not None else get_config()
# What the caller configured, kept intact: entering derives a # What the caller configured, kept intact: entering derives a
@ -153,6 +154,11 @@ class HaikuRAG:
# same set rather than the answer from last time. # same set rather than the answer from last time.
self._config = self._configured self._config = self._configured
self._requested_db_path = Path(db_path) if db_path is not None else None 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._skip_validation = skip_validation
self._create = create self._create = create
self._read_only = read_only self._read_only = read_only

View file

@ -6,6 +6,7 @@ from pydantic import ValidationError
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.client.scope import DatabaseScope from haiku.rag.client.scope import DatabaseScope
from haiku.rag.config.models import AppConfig, LanceDBConfig from haiku.rag.config.models import AppConfig, LanceDBConfig
from haiku.rag.store.exceptions import AmbiguousDatabaseError
from haiku.rag.utils import locate_database from haiku.rag.utils import locate_database
from tests.multi_db.helpers import ( from tests.multi_db.helpers import (
_config, _config,
@ -237,6 +238,15 @@ class TestPlacingADatabase:
async with HaikuRAG(temp_db_path, create=True) as rag: async with HaikuRAG(temp_db_path, create=True) as rag:
assert await rag.reader_for(None) is 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 @pytest.mark.asyncio
async def test_one_database_refuses_a_name_it_does_not_cover(self, tmp_path): 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 """Answering with itself would hand back the wrong database's reader for