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
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

View file

@ -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

View file

@ -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