feat(voice,agents,memory): voice points get their own branch, agent and keys

This commit is contained in:
hh
2026-09-06 23:26:35 +02:00
parent b9eb95d8c0
commit 4dd199147c
14 changed files with 747 additions and 4 deletions
+62
View File
@@ -0,0 +1,62 @@
"""Единственная ветка голосовых точек: как её открыть и как сменить за ночь."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from beaver_agent.voice.texts import KEY, ROTATED, TITLE
if TYPE_CHECKING:
from beaver_gateway.conversations.service import Conversations
from beaver_gateway.storage.models import Conversation
AGENT = "beaver-voice"
"""Кто сидит в голосовой ветке; ему не видно ни справки, ни vault."""
FRONTEND = "voice"
TELEGRAM = "telegram"
_log = logging.getLogger("beaver_agent.voice")
async def current(conversations: Conversations) -> Conversation | None:
conv = await conversations.find_bound(frontend=FRONTEND, external_id=KEY)
return conv if conv is not None and conv.status == "open" else None
async def open_thread(
conversations: Conversations, *, agent: str, window: str | None = None
) -> Conversation:
"""Завести ветку; без ``window`` окно ей открывает телеграм - новым топиком."""
conv = await conversations.spawn(
kind="branch",
agent=agent,
title=TITLE,
origin=FRONTEND,
binding=(TELEGRAM, window) if window else None,
)
await conversations.bind(conv, frontend=FRONTEND, external_id=KEY)
_log.info("голосовая ветка %s (%s)", conv.external_id, agent)
return conv
async def window_of(conversations: Conversations, conv: Conversation) -> str | None:
for binding in await conversations.bindings(conv):
if binding.frontend == TELEGRAM and binding.visible:
return binding.external_id
return None
async def rotate(conversations: Conversations) -> Conversation | None:
"""Ночью - новая ветка в том же топике; молчавшую за сутки не трогаем."""
old = await current(conversations)
if old is None or old.last_user_activity_at is None:
return None
new = await open_thread(
conversations, agent=old.agent_name, window=await window_of(conversations, old)
)
await conversations.close(old)
await conversations.say(new, ROTATED)
_log.info("голосовая ветка: %s -> %s", old.external_id, new.external_id)
return new