refactor: split flat core into capability packages, layer the conversations service, English defaults for every model-facing text

This commit is contained in:
hh
2026-09-02 00:13:20 +02:00
parent 253bde1b11
commit 9aaddbed75
77 changed files with 2987 additions and 2944 deletions
+9 -9
View File
@@ -10,8 +10,8 @@ from aiogram.methods import SendMessage
from aiogram.types import Update
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
from beaver_gateway.core.registry import McpRegistry
from beaver_gateway.core.transcript import build_entries
from beaver_gateway.app import McpRegistry
from beaver_gateway.backends.transcript import build_entries
from beaver_gateway.frontends.base import GatewayRuntime
from beaver_gateway.frontends.telegram import Attachments, TelegramFrontend
from beaver_gateway.frontends.telegram.render import (
@@ -279,7 +279,7 @@ async def stack() -> Stack:
async def test_general_is_master_and_reply_has_no_thread(stack: Stack) -> None:
stack.bot.message("hi")
reply = await stack.until(lambda: stack.sent_with("hi"), what="reply")
assert reply["text"].startswith("ok:[сид: clean] master")
assert reply["text"].startswith("ok:[seed: clean] master")
assert reply["thread"] == 901
assert reply["parse_mode"] == "HTML"
assert stack.bot.topics == ["🦫 General"]
@@ -316,8 +316,8 @@ async def test_new_topic_becomes_morning_branch_and_replies_in_thread(
)
assert branch.parent_id == master.id
prompts = [p for c in ScriptedClient.instances for p in c.prompts]
seed = next(p for p in prompts if p.startswith("[сид: morning] branch «план»"))
assert "Хендаут не приехал." in seed
seed = next(p for p in prompts if p.startswith("[seed: morning] branch «план»"))
assert "No handout arrived." in seed
assert seed.endswith("hello topic")
assert stack.bot.drafts[-1]["message_thread_id"] == 7
@@ -334,7 +334,7 @@ async def test_first_message_without_service_message_seeds_with_text(
client = next(
c for c in ScriptedClient.instances if c.prompts and "сразу" in c.prompts[0]
)
assert client.prompts[0].startswith("[сид: morning]")
assert client.prompts[0].startswith("[seed: morning]")
assert client.prompts[0].endswith("сразу текстом")
assert len(client.prompts) == 1
@@ -432,7 +432,7 @@ async def test_question_becomes_buttons_and_callback_answers(stack: Stack) -> No
stack.bot.callback(f"q:{pending[0]}:0:1", question["message_id"])
assert await asyncio.wait_for(asking, 5) == "Красный"
assert stack.world.conversations.answer_text("Красный") == (
"Пользователь ответил: Красный"
"The user answered: Красный"
)
await stack.until(
lambda: any("✅ Красный" in e.get("text", "") for e in stack.bot.edits),
@@ -454,7 +454,7 @@ async def test_question_timeout_renders_text_and_free_text_answers(
payload = {"questions": [{"header": "Q", "question": "Сколько?", "options": []}]}
result = await stack.world.conversations.ask(master.external_id, payload)
assert result is None
assert "не ответил" in stack.world.conversations.answer_text(None)
assert "did not answer" in stack.world.conversations.answer_text(None)
await stack.until(
lambda: any("время вышло" in e.get("text", "") for e in stack.bot.edits),
what="timeout edit",
@@ -510,7 +510,7 @@ async def test_commands_status_merge_and_new(stack: Stack) -> None:
stack.bot.message("первое в новый топик", thread=902)
reply = await stack.until(lambda: stack.sent_with("первое в новый топик"), what="r")
assert reply["thread"] == 902
assert reply["text"].startswith("ok:[сид: morning] branch «отчёт»")
assert reply["text"].startswith("ok:[seed: morning] branch «отчёт»")
assert await stack.tg.mark_topic(child)
assert stack.bot.topics[-1] == "edit:902:✅ отчёт"