Correct what the client and the docs claim

`HaikuRAG.__init__` said an omitted `db_path` uses `storage.data_dir`, which
is the last of three; and that `sources` is ignored for a single `uri`, where
it raises, since only `lancedb.databases` names databases.

The name is not the only identity leaving the configuration: results,
citations, model input and errors opening a named database carry it, while
`info`, `init` and `tag` print the location. `sources=[]` returns no search
results, but `ask` and `analyze` still answer, without evidence.

Trim the comments that still narrated a failure or an alternative to the
invariant they were there for.
This commit is contained in:
Yiorgis Gozadinos 2026-08-26 13:58:38 +03:00
parent ca2e28559e
commit 8f41d42ab2
No known key found for this signature in database
7 changed files with 31 additions and 40 deletions

View file

@ -214,9 +214,10 @@ lancedb:
A location is a URI or a local path. `databases` and `uri` are mutually
exclusive, and setting both fails validation.
The name is the only identity that leaves the configuration. Results, citations
and error messages carry it, so a path or a bucket never reaches a log, a trace
or a model.
Results, citations, model input and errors opening a named database carry the
configured name rather than the location, so a path or a bucket does not reach a
trace or a model. Commands that report on a database — `info`, `init`, `tag`
print its location, as does an error about a path.
Every database in the set is opened with the same embedding configuration. A
different `vector_dim` raises `ConfigMismatchError` on open. A different provider

View file

@ -258,8 +258,9 @@ result = await client.analyze("How many documents mention it?", sources=["medic"
A question scoped to some databases can only cite those, and the analysis
sandbox mounts only their documents.
`sources=None` covers every database the client covers; `sources=[]` covers none
and returns nothing, which is not the same thing.
`sources=None` covers every database the client covers. `sources=[]` covers
none: `search` returns no results, and `ask` and `analyze` run with no evidence
from any database.
#### Asking a client what it covers

View file

@ -59,9 +59,8 @@ class HaikuRAGApp:
def _connection(self) -> "tuple[AppConfig, Path]":
"""How to open the one database this command works on, directly.
The configuration a command was given names a *set*; the one it opens
needs its own, or a named database behind a URI is opened as the local
path that stands in for it.
Derived per database: opening one of a set against the set's own
configuration would reach the local path that stands in for it.
"""
from haiku.rag.client.session import default_db_path

View file

@ -146,9 +146,8 @@ class DocumentFilterModal(ModalScreen):
filter_list = self.query_one("#filter-list", VerticalScroll)
await filter_list.remove_children()
# The page is picked to represent every database; sorting is so it reads
# like a list rather than in whatever order the tables returned. The label
# names the database, since a title alone does not say which one it is in.
# Sort the interleaved page and label each document's database, which
# a title alone does not say.
labelled = sorted(
(
(

View file

@ -133,15 +133,17 @@ class HaikuRAG:
"""Initialize the RAG client with a database path.
Args:
db_path: Path or string path to the database file. If None, uses
config.storage.data_dir.
db_path: Path or string path to the database. When omitted, resolves
``lancedb.databases``, then ``lancedb.uri``, then the default
path under ``storage.data_dir``.
config: Configuration to use. Defaults to the current global config.
skip_validation: Whether to skip configuration validation on database load.
create: Whether to create the database if it doesn't exist.
read_only: Whether to open the database in read-only mode.
sources: Names from ``config.lancedb.databases`` this client covers.
None means all of them. Ignored when a single ``uri`` or an
explicit ``db_path`` is given.
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.
"""
self._configured = config if config is not None else get_config()
# What the caller configured, kept intact: entering derives a
@ -290,14 +292,9 @@ class HaikuRAG:
async def __aenter__(self):
"""Async context manager entry — initializes store and repositories.
A client borrowing a database is already open and returns itself.
Opening a second session would leak it: teardown declines to close what
this client did not open.
A client covering several databases opens none of them here: which are
searched is a per-query choice, so they open on first use. `store` and the
repositories stay unset in that case, since they have no unambiguous
meaning across a set.
A borrowed client reuses its session, which its owner closes. A client
covering several opens their sessions lazily, so `store` and the
repositories stay unset until one database is named.
"""
if not self._owns_session:
assert self._session is not None

View file

@ -93,32 +93,27 @@ class SingleDatabaseSession:
create=self._create,
read_only=self.read_only,
)
# If _initialize fails mid-way (e.g. migration check raises after
# connect), close the store so we don't leak the LanceDB connection —
# the caller's `async with` never entered, so its exit won't run.
# Close a partially initialized store: the caller's `async with`
# never entered, so its exit will not run.
try:
await self.store._initialize()
except BaseException:
self.store.close()
raise
except _NAMEABLE_FAILURES as error:
# These say what to run and never where the database is, so the name
# is added to the message rather than replacing it: the operator needs
# both which database failed and what to do about it.
# These name the remedy and not the database, so the name is added
# rather than substituted.
if self.source is None:
raise
raise type(error)(f"database {self.source!r}: {error}") from error
except Exception as error:
# A legacy `uri` or `db_path` session has no name to report instead,
# so its error passes through as it always has.
# Without a name there is nothing to report in the location's place.
if self.source is None:
raise
failure = type(error).__name__
if failure is not None:
# Raised outside the except block on purpose. A database named in
# config is reported by name, and the original spells out the path or
# the bucket: `from None` would only stop it being *printed*, leaving
# it on `__context__` for anything that walks the chain.
# Raised outside the handler to discard the location-bearing
# context, which `from None` would only stop printing.
raise SourceUnavailableError(
f"database {self.source!r} could not be opened: {failure}"
)

View file

@ -247,10 +247,9 @@ class Sandbox:
async def _documents(self) -> "tuple[list[Any], dict[str, HaikuRAG]]":
"""Every document in scope, and the client holding each of them.
The owners are empty where one connection serves every read: a single
database, or the ephemeral connection opened per read when no client was
supplied. The selection is resolved the same way a search resolves it, so
a database the question excluded cannot be mounted.
Owners are empty where one connection serves every read. The selection
resolves as a search resolves it, so a database the question excluded
cannot be mounted.
"""
async with self._connection() as rag:
if not rag.covers_multiple: