187 lines
8.3 KiB
Python
187 lines
8.3 KiB
Python
"""Всё, что gateway говорит модели и Бобру, по-русски."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import TYPE_CHECKING
|
||
|
||
from beaver_gateway.conversations.texts import ConversationTexts, EnvelopeTexts
|
||
from beaver_gateway.frontends.telegram import TelegramTexts
|
||
|
||
from beaver_agent.memory.handouts import (
|
||
distill_prompt,
|
||
handout_prompt,
|
||
new_day,
|
||
seed_body,
|
||
)
|
||
from beaver_agent.vault import GRANULES, read
|
||
|
||
if TYPE_CHECKING:
|
||
from beaver_gateway.conversations.injects import InjectContext
|
||
|
||
BEAVER_ORIGINS = frozenset({"panel", "api"})
|
||
"""Панель и API - это сам Бобёр; крон, watch, komodo, ротация - нет."""
|
||
|
||
ORIGINS = {
|
||
"merge": "слив",
|
||
"digest": "выжимка",
|
||
"rotation": "ротация",
|
||
"cap": "потолок",
|
||
"system": "система",
|
||
"agent": "агент",
|
||
}
|
||
|
||
INTERRUPTED_TURN = (
|
||
"[этот инжект прервал предыдущий тёрн: «Request interrupted» выше - "
|
||
"прерывание, не отказ от тулзы]"
|
||
)
|
||
|
||
|
||
def inject_header(ctx: InjectContext) -> str:
|
||
if ctx.origin in BEAVER_ORIGINS:
|
||
head = (
|
||
"[инжект из панели: это Бобёр, ответ он видит в панели; "
|
||
"в телегу - только через say]"
|
||
)
|
||
else:
|
||
origin = ORIGINS.get(ctx.origin, ctx.origin)
|
||
head = (
|
||
f"[инжект: {origin} - это не Бобёр, отвечать не нужно, голос не обязателен]"
|
||
)
|
||
return f"{head}\n{INTERRUPTED_TURN}" if ctx.interrupted_turn else head
|
||
|
||
|
||
ENVELOPE = EnvelopeTexts(
|
||
header=(
|
||
"[конверт - фоновый сигнал, не обращение; "
|
||
"реагируй, только если относится к вопросу]"
|
||
),
|
||
time="время: {stamp} ({zone})",
|
||
changed="vault, изменено {since}: {names}",
|
||
since="с {time}",
|
||
since_start="со старта",
|
||
truncated="… (потолок конверта)",
|
||
file_header="--- {path}, только добавленное ---",
|
||
more_lines="+ … ещё {count}",
|
||
)
|
||
|
||
MERGE_PROMPT = read(GRANULES / "слив.md") or (
|
||
"Ветка закрывается. Напиши слив для мастера: что решили, что сделано, "
|
||
"что не сделано и почему, открытые вопросы. Идентификаторы и ссылки - "
|
||
"дословно. Коротко, прошедшее время."
|
||
)
|
||
|
||
CONVERSATIONS = ConversationTexts(
|
||
merge_prompt=MERGE_PROMPT,
|
||
inject_header=inject_header,
|
||
bundle_header="[инжекты, накопившиеся с {since}; это не Бобёр]",
|
||
task_prompt="[субагент отчитался: {note}]",
|
||
interrupted="прервано",
|
||
answered="Пользователь ответил: {answer}",
|
||
unanswered=(
|
||
"Пользователь не ответил за {minutes} мин. Вопрос ему показан текстом; "
|
||
"заверши тёрн сейчас, ответ придёт следующим сообщением."
|
||
),
|
||
seed=seed_body,
|
||
seed_head="[сид: {seed}] {kind}{title}, {stamp}.",
|
||
seed_copy="История родителя скопирована ({scope}); продолжай в ней.",
|
||
seed_copy_window="последние {window} тёрнов",
|
||
seed_copy_all="вся история",
|
||
seed_morning_missing="Хендаут не приехал.",
|
||
handout=handout_prompt,
|
||
new_day=new_day,
|
||
moved_injects=" Инжектов переехало из старого мастера: {moved}.",
|
||
distill=distill_prompt,
|
||
closed="Закрыт глубокий чат [[{chat}]]{digest}.\n{text}",
|
||
closed_digest=", выжимка [[{digest}]]",
|
||
digest_missing="файл выжимки не появился",
|
||
too_long=(
|
||
"`{name}`: {lines} строк при потолке {max_lines}. Запись отбита, файл "
|
||
"возвращён к прежней версии. Сократи и перепиши."
|
||
),
|
||
cut_by_restart="тёрн {turn_id} оборван рестартом gateway",
|
||
repaired_tools=(
|
||
"; {fixed} незакрытых тул-коллов получили tool_result «{interrupted}»"
|
||
),
|
||
envelope=ENVELOPE,
|
||
)
|
||
|
||
TELEGRAM = TelegramTexts(
|
||
commands=(
|
||
("merge", "слить ветку в мастер"),
|
||
("new", "новая ветка: в General - новый топик, в топике - заново на нём"),
|
||
("chat", "открыть глубокий чат в vault: /chat тема"),
|
||
("status", "что с этим разговором"),
|
||
("help", "команды"),
|
||
),
|
||
help_head="General - мастер, любой другой топик - ветка.",
|
||
branch="ветка",
|
||
waiting="⏳ думаю",
|
||
thinking="🤔 думаю",
|
||
writing="✍️ пишу",
|
||
tool_labels={
|
||
"Read": "читаю vault",
|
||
"Glob": "ищу файлы",
|
||
"Grep": "ищу в vault",
|
||
"Edit": "правлю файл",
|
||
"Write": "пишу файл",
|
||
"MultiEdit": "правлю файлы",
|
||
"Bash": "выполняю команду",
|
||
"WebSearch": "ищу в сети",
|
||
"WebFetch": "читаю страницу",
|
||
"Task": "запустил сабагента",
|
||
"Agent": "запустил сабагента",
|
||
"AskUserQuestion": "спрашиваю",
|
||
"TodoWrite": "планирую",
|
||
"Skill": "открываю скилл",
|
||
"mcp__gateway__spawn": "открываю разговор",
|
||
"mcp__gateway__read_conversation": "читаю разговор",
|
||
"mcp__gateway__say": "говорю",
|
||
"mcp__gateway__schedule": "ставлю напоминание",
|
||
"mcp__gateway__inject": "передаю в другой разговор",
|
||
},
|
||
attachment_sticker="[вложение: стикер - не поддерживается]",
|
||
attachment_animation="[вложение: анимация - не поддерживается]",
|
||
attachment_kinds={
|
||
"photo": "фото",
|
||
"document": "файл",
|
||
"voice": "голосовое",
|
||
"audio": "аудио",
|
||
"video": "видео",
|
||
"video_note": "кружок",
|
||
},
|
||
attachment_too_big=(
|
||
"[вложение: {kind} {name}, {mb} МБ - больше 20 МБ, Telegram не отдаёт ботам]"
|
||
),
|
||
attachment_failed="[вложение: {kind} {name} - не скачалось: {error}]",
|
||
attachment_size=", {kb} КБ",
|
||
attachment="[вложение: {kind} {path}{size}]",
|
||
attachment_kept=(
|
||
"[вложение: {kind} {path}{size}; хранится {days} дн., "
|
||
"перенеси в vault, если нужно]"
|
||
),
|
||
merge_nothing="сливать нечего: это не открытая ветка",
|
||
merging="🔀 сливаю в мастер",
|
||
merged="✅ слито в мастер",
|
||
merge_failed="⚠️ слив не удался: {error}",
|
||
new_branch_topic="🌿 ветка «{title}» - в новом топике",
|
||
new_branch_here="🌿 новая ветка на этом топике",
|
||
chat_usage="/chat <тема>",
|
||
chat_failed="не вышло: {error}",
|
||
chat_opened="💬 глубокий чат: {where}",
|
||
status_unbound="этот топик ни к чему не привязан - напиши, и откроется ветка",
|
||
status_line="сессия: {live} · тёрн: {turn} · в очереди: {queued}",
|
||
status_live="живая",
|
||
status_dead="нет",
|
||
status_running="идёт",
|
||
status_idle="нет",
|
||
status_question="❓ ждёт ответа на вопрос",
|
||
status_pool="пул: {sessions} сессий, rss {mb} МБ",
|
||
status_outbox="outbox: {pending} в очереди",
|
||
turn_failed="⚠️ тёрн упал, смотри логи gateway",
|
||
background_failed="⚠️ фоновый тёрн ({origin}) упал, смотри логи gateway",
|
||
question_closed="вопрос уже закрыт",
|
||
question_timeout="⌛ время вышло - ответь текстом",
|
||
done_button="✅ готово",
|
||
mirrored="📝 из панели:\n{text}",
|
||
)
|