Refuse a name the client does not cover, however many it covers
`reader_for` returned itself before looking at the name, so an "alpha" client
answered `reader_for("beta")` with alpha's reader. A citation naming another
database would have been read from the wrong one.
Both paths decide through `clients_covering` now, so one database refuses a
wrong name the way a set already did.
The chat citation test said mounting lends the client without showing it; a
chat test shows it and the docstring points there.
This commit is contained in:
parent
45a5e68da5
commit
d93cb5c891
4 changed files with 48 additions and 11 deletions
|
|
@ -198,15 +198,14 @@ class HaikuRAG:
|
|||
database.
|
||||
|
||||
None only when a client covering a set is given no name, as for evidence
|
||||
recorded before databases could be named. A name outside the set raises
|
||||
`KeyError`, the same as `clients_for`: provenance naming a database this
|
||||
client does not cover is wrong rather than absent.
|
||||
recorded before databases could be named. A name this client does not
|
||||
cover raises `KeyError`, decided by `clients_covering` so that one
|
||||
database answers a wrong name the same way a set does: provenance naming
|
||||
another database is wrong rather than absent.
|
||||
"""
|
||||
if not self.covers_multiple:
|
||||
return self
|
||||
if source is None:
|
||||
return None
|
||||
(owner,) = await self.clients_for([source])
|
||||
return None if self.covers_multiple else self
|
||||
(owner,) = await self.clients_covering([source])
|
||||
return owner
|
||||
|
||||
def _single_session(self, operation: str) -> SingleDatabaseSession:
|
||||
|
|
|
|||
|
|
@ -625,6 +625,25 @@ async def test_visual_grounding_uses_the_database_holding_the_citation(tmp_path)
|
|||
assert push.await_args.args[0].client is owner
|
||||
|
||||
|
||||
class TestLendingTheClient:
|
||||
@pytest.mark.asyncio
|
||||
async def test_mounting_lends_its_client_to_every_capability(self, temp_db_path):
|
||||
"""Capabilities are built before the client exists, so each reads through
|
||||
the one the app opened rather than opening its own."""
|
||||
client = _make_mock_client()
|
||||
app, _ = _make_app(temp_db_path, client)
|
||||
|
||||
with (
|
||||
patch("haiku.rag.chat.app.HaikuRAG") as stub_rag,
|
||||
_covering_returns(stub_rag, client),
|
||||
):
|
||||
async with app.run_test():
|
||||
borrowed = [c.borrowed_rag for c in app._capabilities]
|
||||
|
||||
assert borrowed == [client] * len(app._capabilities)
|
||||
assert borrowed
|
||||
|
||||
|
||||
class TestDocumentSelectionIdentity:
|
||||
"""Two documents can share a title, within a corpus and across databases, so
|
||||
the selection is by id and the label says which database."""
|
||||
|
|
|
|||
|
|
@ -203,9 +203,9 @@ class TestCollectionIdentityForTheModel:
|
|||
class TestLendingANamedClient:
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_lent_named_client_names_the_citation(self, tmp_path):
|
||||
"""The chat TUI builds capabilities before its client exists and lends
|
||||
it on mount, so what a citation records is the lent client's database
|
||||
and not whatever scope the capability was constructed with."""
|
||||
"""What a citation records is the lent client's database, not the scope
|
||||
the capability was constructed with. That chat lends its client is
|
||||
`TestLendingTheClient` in `tests/chat/test_chat_app.py`."""
|
||||
from tests.capabilities.test_capabilities import Deps, make_context
|
||||
|
||||
config = _config(tmp_path, ["alpha", "beta"])
|
||||
|
|
|
|||
|
|
@ -236,7 +236,26 @@ class TestPlacingADatabase:
|
|||
async def test_a_client_reading_one_database_is_its_own_reader(self, temp_db_path):
|
||||
async with HaikuRAG(temp_db_path, create=True) as rag:
|
||||
assert await rag.reader_for(None) is rag
|
||||
assert await rag.reader_for("anything") is rag
|
||||
|
||||
@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
|
||||
a citation that named another."""
|
||||
config = _config(tmp_path, ["alpha", "beta"])
|
||||
await _seed(config, "alpha", ["alpha one"])
|
||||
await _seed(config, "beta", ["beta one"])
|
||||
|
||||
async with HaikuRAG(config=config, sources=["alpha"]) as alpha:
|
||||
assert await alpha.reader_for("alpha") is alpha
|
||||
with pytest.raises(KeyError, match="beta"):
|
||||
await alpha.reader_for("beta")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_an_unnamed_database_refuses_any_name(self, temp_db_path):
|
||||
"""Nothing names it, so no name can be the one it covers."""
|
||||
async with HaikuRAG(temp_db_path, create=True) as rag:
|
||||
with pytest.raises(KeyError, match="single unnamed database"):
|
||||
await rag.reader_for("anything")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_set_cannot_place_evidence_that_names_no_database(self, tmp_path):
|
||||
|
|
|
|||
Loading…
Reference in a new issue