diff --git a/docs/configuration/storage.md b/docs/configuration/storage.md index 42b95916..1994dbee 100644 --- a/docs/configuration/storage.md +++ b/docs/configuration/storage.md @@ -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 diff --git a/docs/python.md b/docs/python.md index 53e0d817..82219137 100644 --- a/docs/python.md +++ b/docs/python.md @@ -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 diff --git a/haiku_rag_slim/haiku/rag/app.py b/haiku_rag_slim/haiku/rag/app.py index d9712a83..e9beb7bf 100644 --- a/haiku_rag_slim/haiku/rag/app.py +++ b/haiku_rag_slim/haiku/rag/app.py @@ -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 diff --git a/haiku_rag_slim/haiku/rag/chat/widgets/document_filter_modal.py b/haiku_rag_slim/haiku/rag/chat/widgets/document_filter_modal.py index 70e61147..874865e4 100644 --- a/haiku_rag_slim/haiku/rag/chat/widgets/document_filter_modal.py +++ b/haiku_rag_slim/haiku/rag/chat/widgets/document_filter_modal.py @@ -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( ( ( diff --git a/haiku_rag_slim/haiku/rag/client/__init__.py b/haiku_rag_slim/haiku/rag/client/__init__.py index 264cd12a..fa5bbdf7 100644 --- a/haiku_rag_slim/haiku/rag/client/__init__.py +++ b/haiku_rag_slim/haiku/rag/client/__init__.py @@ -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 diff --git a/haiku_rag_slim/haiku/rag/client/session.py b/haiku_rag_slim/haiku/rag/client/session.py index ee69032d..bfe9afb3 100644 --- a/haiku_rag_slim/haiku/rag/client/session.py +++ b/haiku_rag_slim/haiku/rag/client/session.py @@ -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}" ) diff --git a/haiku_rag_slim/haiku/rag/sandbox/sandbox.py b/haiku_rag_slim/haiku/rag/sandbox/sandbox.py index 9cf2d093..e7ab6a2a 100644 --- a/haiku_rag_slim/haiku/rag/sandbox/sandbox.py +++ b/haiku_rag_slim/haiku/rag/sandbox/sandbox.py @@ -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: