diff --git a/src/beaver_gateway/conversations/envelope.py b/src/beaver_gateway/conversations/envelope.py index c5a9306..320ea53 100644 --- a/src/beaver_gateway/conversations/envelope.py +++ b/src/beaver_gateway/conversations/envelope.py @@ -25,6 +25,8 @@ class RecallContext: text: str kind: str now: datetime + agent: str = "" + """Agent whose turn it is; a setup with several tells them apart by it.""" @dataclass(slots=True) @@ -40,7 +42,12 @@ class Envelope: texts: EnvelopeTexts = field(default_factory=EnvelopeTexts) def build( - self, *, now: datetime | None = None, text: str = "", kind: str = "master" + self, + *, + now: datetime | None = None, + text: str = "", + kind: str = "master", + agent: str = "", ) -> str: now = now or datetime.now(UTC) changes = self.watch.take() if self.watch is not None else [] @@ -59,20 +66,26 @@ class Envelope: texts=self.texts, ) self.last_at = now - block = self.recall_block(text=text, kind=kind, now=now) + block = self.recall_block(text=text, kind=kind, now=now, agent=agent) return f"{out}\n{block}" if block else out def recall_only( - self, *, text: str, kind: str, now: datetime | None = None + self, *, text: str, kind: str, now: datetime | None = None, agent: str = "" ) -> str | None: - block = self.recall_block(text=text, kind=kind, now=now or datetime.now(UTC)) + block = self.recall_block( + text=text, kind=kind, now=now or datetime.now(UTC), agent=agent + ) return f"{self.texts.header}\n{block}" if block else None - def recall_block(self, *, text: str, kind: str, now: datetime) -> str | None: + def recall_block( + self, *, text: str, kind: str, now: datetime, agent: str = "" + ) -> str | None: if self.recall is None or not text.strip(): return None try: - block = self.recall(RecallContext(text=text, kind=kind, now=now)) + block = self.recall( + RecallContext(text=text, kind=kind, now=now, agent=agent) + ) except Exception: # noqa: BLE001 _log.exception("recall hook failed") return None diff --git a/src/beaver_gateway/conversations/texts.py b/src/beaver_gateway/conversations/texts.py index 43b73cc..cc77d63 100644 --- a/src/beaver_gateway/conversations/texts.py +++ b/src/beaver_gateway/conversations/texts.py @@ -51,6 +51,8 @@ class UserSaid: title: str | None text: str at: datetime + agent: str = "" + """Agent whose turn it is; a setup with several tells them apart by it.""" @dataclass(frozen=True, slots=True) diff --git a/src/beaver_gateway/conversations/turns.py b/src/beaver_gateway/conversations/turns.py index 132c933..be2117d 100644 --- a/src/beaver_gateway/conversations/turns.py +++ b/src/beaver_gateway/conversations/turns.py @@ -336,9 +336,11 @@ class Turns(Seeds): if self._envelope is None: return None if conv.kind == "master": - return self._envelope.build(text=text, kind="master") + return self._envelope.build(text=text, kind="master", agent=conv.agent_name) if conv.kind == "branch": - return self._envelope.recall_only(text=text, kind="branch") + return self._envelope.recall_only( + text=text, kind="branch", agent=conv.agent_name + ) return None async def _note_user(self, conv: Conversation, text: str) -> None: @@ -350,6 +352,7 @@ class Turns(Seeds): title=conv.title, text=text, at=datetime.now(UTC), + agent=conv.agent_name, ) try: result = self._user_sink(message) diff --git a/tests/test_envelope.py b/tests/test_envelope.py index b5731f6..804264b 100644 --- a/tests/test_envelope.py +++ b/tests/test_envelope.py @@ -219,3 +219,25 @@ async def test_failing_recall_or_sink_never_blocks_the_turn(world: World) -> Non assert prompt.startswith("hello\n\n" + HEADER) assert root.exists() await asyncio.sleep(0) + + +async def test_the_recall_hook_and_the_sink_see_whose_turn_it_is(world: World) -> None: + root, watch = vault() + seen: list[RecallContext] = [] + + def recall(ctx: RecallContext) -> str | None: + seen.append(ctx) + return None if ctx.agent == "a" else "👤 указатель" + + 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) + assert [ctx.agent for ctx in seen] == ["a"] + assert [m.agent for m in noted] == ["a"] + assert "👤" not in ScriptedClient.instances[0].prompts[0] + await asyncio.sleep(0)