feat(envelope,conversations): recall hook under the user text, user_sink for the setup's reply log

This commit is contained in:
hh
2026-09-01 16:08:53 +02:00
parent 63014a487d
commit acc79c2e42
5 changed files with 170 additions and 13 deletions
+69 -1
View File
@@ -5,7 +5,8 @@ from pathlib import Path
from test_conversations import ScriptedClient, World, world
from beaver_gateway.core.envelope import HEADER, Envelope, render
from beaver_gateway.core.conversations import UserSaid
from beaver_gateway.core.envelope import HEADER, Envelope, RecallContext, render
from beaver_gateway.core.watch import Change, VaultWatch, WatchRules
__all__ = ["world"]
@@ -148,3 +149,70 @@ async def test_master_turn_gets_envelope_after_text_and_before_injects(
await world.settle(branch, 1)
assert "[конверт" not in ScriptedClient.instances[-1].prompts[0]
await asyncio.sleep(0)
async def test_recall_lines_follow_the_vault_block_and_reach_branches(
world: World,
) -> None:
root, watch = vault()
seen: list[tuple[str, str]] = []
def recall(ctx: RecallContext) -> str | None:
seen.append((ctx.kind, ctx.text))
return "👤 Прохор - карточка `люди/Прохор.md`" if "Прохор" in ctx.text else None
noted: list[UserSaid] = []
world.conversations._envelope = Envelope(watch=watch, tz="UTC", recall=recall) # noqa: SLF001
world.conversations._user_sink = noted.append # noqa: SLF001
master = await world.conversations.create(kind="master", agent="a", origin="test")
append(root / "люди" / "Прохор.md", "новое\n")
watch.note(root / "люди" / "Прохор.md")
await world.conversations.post(master, "что там у Прохор")
await world.settle(master, 1)
prompt = ScriptedClient.instances[0].prompts[0]
head, _, rest = prompt.partition("\n\n")
assert head == "что там у Прохор"
lines = rest.splitlines()
assert lines[0] == HEADER
assert "люди/Прохор.md (+1)" in rest
assert lines[-1] == "👤 Прохор - карточка `люди/Прохор.md`"
assert rest.index("люди/Прохор.md (+1)") < rest.index("👤 Прохор")
assert seen == [("master", "что там у Прохор")]
assert [(m.kind, m.text) for m in noted] == [("master", "что там у Прохор")]
branch = await world.conversations.spawn(
kind="branch", parent=master, seed="clean", text="про Прохор подробнее"
)
await world.settle(branch, 1)
branch_prompt = ScriptedClient.instances[-1].prompts[0]
assert "про Прохор подробнее\n\n" + HEADER in branch_prompt
assert branch_prompt.endswith(f"{HEADER}\n👤 Прохор - карточка `люди/Прохор.md`")
assert "vault, изменено" not in branch_prompt
# the first branch turn carries the seed line above the text
assert seen[-1][0] == "branch" and seen[-1][1].endswith("про Прохор подробнее")
assert noted[-1].kind == "branch"
await world.conversations.post(master, "ничего про людей")
await world.settle(master, 2)
second = ScriptedClient.instances[0].prompts[1]
assert "👤" not in second
assert second.partition("\n\n")[2].startswith(HEADER)
await asyncio.sleep(0)
async def test_failing_recall_or_sink_never_blocks_the_turn(world: World) -> None:
root, watch = vault()
def boom(_: object) -> str:
msg = "nope"
raise RuntimeError(msg)
world.conversations._envelope = Envelope(watch=watch, tz="UTC", recall=boom) # noqa: SLF001
world.conversations._user_sink = boom # noqa: SLF001
master = await world.conversations.create(kind="master", agent="a", origin="test")
await world.conversations.post(master, "hello")
await world.settle(master, 1)
prompt = ScriptedClient.instances[0].prompts[0]
assert prompt.startswith("hello\n\n" + HEADER)
assert root.exists()
await asyncio.sleep(0)