Команда /context
Показывает текущий промпт, базовую сцену, последний запрос и время обновления контекста.
This commit is contained in:
parent
bb096dec47
commit
6bbd9cc04a
|
|
@ -204,6 +204,7 @@ TELEGRAM_HEALTH_INTERVAL=60
|
||||||
- `/balance` — текущий баланс.
|
- `/balance` — текущий баланс.
|
||||||
- `/invite` — реферальная ссылка.
|
- `/invite` — реферальная ссылка.
|
||||||
- `/restart` — сбросить контекст и начать новую сцену (алиас `/reset`).
|
- `/restart` — сбросить контекст и начать новую сцену (алиас `/reset`).
|
||||||
|
- `/context` — показать текущий контекст: промпт, базовую сцену и последний запрос.
|
||||||
- `/settings` — выбор модели Krea2: стандартная Q4 или улучшенная Q6.
|
- `/settings` — выбор модели Krea2: стандартная Q4 или улучшенная Q6.
|
||||||
- При старте бот проверяет `setMyCommands` и включает кнопку меню с командами.
|
- При старте бот проверяет `setMyCommands` и включает кнопку меню с командами.
|
||||||
- За первую оплату приглашённого друга начисляется +3 генерации, реферер получает уведомление.
|
- За первую оплату приглашённого друга начисляется +3 генерации, реферер получает уведомление.
|
||||||
|
|
@ -215,6 +216,7 @@ TELEGRAM_HEALTH_INTERVAL=60
|
||||||
- Первый текстовый запрос создаёт контекст: базовую сцену и текущий улучшенный промпт.
|
- Первый текстовый запрос создаёт контекст: базовую сцену и текущий улучшенный промпт.
|
||||||
- Следующий текст обрабатывается вместе с предыдущим промптом: бот сохраняет сцену и применяет только запрошенное изменение.
|
- Следующий текст обрабатывается вместе с предыдущим промптом: бот сохраняет сцену и применяет только запрошенное изменение.
|
||||||
- `/restart` очищает контекст; следующий запрос начнёт новую сцену.
|
- `/restart` очищает контекст; следующий запрос начнёт новую сцену.
|
||||||
|
- `/context` показывает текущий промпт, базовую сцену и последний запрос.
|
||||||
- Новое фото с подписью автоматически начинает новую цепочку.
|
- Новое фото с подписью автоматически начинает новую цепочку.
|
||||||
- Для точного редактирования конкретной картинки ответь на сообщение бота с изображением.
|
- Для точного редактирования конкретной картинки ответь на сообщение бота с изображением.
|
||||||
- `/restart` не отменяет уже запущенную генерацию: она завершится со своим снимком контекста.
|
- `/restart` не отменяет уже запущенную генерацию: она завершится со своим снимком контекста.
|
||||||
|
|
|
||||||
49
bot.py
49
bot.py
|
|
@ -35,7 +35,7 @@ from telegram.ext import (
|
||||||
|
|
||||||
from comfy_client import ComfyClient
|
from comfy_client import ComfyClient
|
||||||
from config import Settings, setup_logging
|
from config import Settings, setup_logging
|
||||||
from db import Database, Request, UserAlreadyQueued
|
from db import Database, Request, UserAlreadyQueued, UserContext
|
||||||
from moderation import PromptBlocked, detect_person, is_prompt_allowed
|
from moderation import PromptBlocked, detect_person, is_prompt_allowed
|
||||||
from prompt_enhancer import PromptEnhancer
|
from prompt_enhancer import PromptEnhancer
|
||||||
from queue_manager import QueueEvent, QueueFull, QueueManager
|
from queue_manager import QueueEvent, QueueFull, QueueManager
|
||||||
|
|
@ -62,6 +62,34 @@ def commands_need_update(
|
||||||
return current_pairs != desired_pairs
|
return current_pairs != desired_pairs
|
||||||
|
|
||||||
|
|
||||||
|
def format_context(context: UserContext | None) -> str:
|
||||||
|
if context is None:
|
||||||
|
return "Контекст пуст. Следующий запрос начнёт новую сцену."
|
||||||
|
|
||||||
|
parts = ["Текущий контекст:", "", f"Промпт: {context.current_prompt}"]
|
||||||
|
if (
|
||||||
|
context.base_prompt
|
||||||
|
and context.base_prompt != context.current_prompt
|
||||||
|
):
|
||||||
|
parts.extend(["", f"Базовая сцена: {context.base_prompt}"])
|
||||||
|
if context.last_request_id is not None:
|
||||||
|
mode = f" ({context.last_mode})" if context.last_mode else ""
|
||||||
|
parts.append(
|
||||||
|
f"Последний запрос: #{context.last_request_id}{mode}"
|
||||||
|
)
|
||||||
|
if context.updated_at:
|
||||||
|
try:
|
||||||
|
updated = datetime.fromisoformat(
|
||||||
|
context.updated_at
|
||||||
|
).astimezone(MSK)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
parts.append(f"Обновлён: {updated.strftime('%d.%m.%Y %H:%M')}")
|
||||||
|
parts.extend(["", "Сбросить: /restart"])
|
||||||
|
return "\n".join(parts)
|
||||||
|
|
||||||
|
|
||||||
def result_caption(prompt: str, *, limit: int = CAPTION_LIMIT) -> str:
|
def result_caption(prompt: str, *, limit: int = CAPTION_LIMIT) -> str:
|
||||||
value = (prompt or "").strip()
|
value = (prompt or "").strip()
|
||||||
suffix = f"\n\n{RESTART_HINT}"
|
suffix = f"\n\n{RESTART_HINT}"
|
||||||
|
|
@ -93,6 +121,7 @@ START_TEXT = (
|
||||||
"/invite — пригласить друга.\n"
|
"/invite — пригласить друга.\n"
|
||||||
"/status — состояние текущей задачи.\n"
|
"/status — состояние текущей задачи.\n"
|
||||||
"/restart — сброс контекста.\n"
|
"/restart — сброс контекста.\n"
|
||||||
|
"/context — текущий контекст.\n"
|
||||||
"/settings — качество Krea2."
|
"/settings — качество Krea2."
|
||||||
)
|
)
|
||||||
BOT_COMMANDS = (
|
BOT_COMMANDS = (
|
||||||
|
|
@ -101,6 +130,7 @@ BOT_COMMANDS = (
|
||||||
BotCommand("balance", "Баланс генераций"),
|
BotCommand("balance", "Баланс генераций"),
|
||||||
BotCommand("invite", "Пригласить друга"),
|
BotCommand("invite", "Пригласить друга"),
|
||||||
BotCommand("status", "Статус текущей задачи"),
|
BotCommand("status", "Статус текущей задачи"),
|
||||||
|
BotCommand("context", "Текущий контекст"),
|
||||||
BotCommand("settings", "Качество Krea2"),
|
BotCommand("settings", "Качество Krea2"),
|
||||||
BotCommand("restart", "Сброс контекста"),
|
BotCommand("restart", "Сброс контекста"),
|
||||||
)
|
)
|
||||||
|
|
@ -163,6 +193,9 @@ class MpakoBot:
|
||||||
application.add_handler(CommandHandler("status", self._status))
|
application.add_handler(CommandHandler("status", self._status))
|
||||||
application.add_handler(CommandHandler("balance", self._balance))
|
application.add_handler(CommandHandler("balance", self._balance))
|
||||||
application.add_handler(CommandHandler("invite", self._invite))
|
application.add_handler(CommandHandler("invite", self._invite))
|
||||||
|
application.add_handler(
|
||||||
|
CommandHandler("context", self._context_command)
|
||||||
|
)
|
||||||
application.add_handler(
|
application.add_handler(
|
||||||
CommandHandler("settings", self._settings_command)
|
CommandHandler("settings", self._settings_command)
|
||||||
)
|
)
|
||||||
|
|
@ -583,6 +616,20 @@ class MpakoBot:
|
||||||
)
|
)
|
||||||
await self._retry(message.reply_text, text)
|
await self._retry(message.reply_text, text)
|
||||||
|
|
||||||
|
async def _context_command(
|
||||||
|
self, update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||||
|
) -> None:
|
||||||
|
if not await self._check_access(update):
|
||||||
|
return
|
||||||
|
message = update.effective_message
|
||||||
|
user = update.effective_user
|
||||||
|
if message is None or user is None:
|
||||||
|
return
|
||||||
|
user_context = await self._db.get_user_context(user.id)
|
||||||
|
await self._retry(
|
||||||
|
message.reply_text, format_context(user_context)
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _quality_keyboard(quality: str) -> InlineKeyboardMarkup:
|
def _quality_keyboard(quality: str) -> InlineKeyboardMarkup:
|
||||||
def label(name: str, value: str) -> str:
|
def label(name: str, value: str) -> str:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
- Реализованы блоки 2–7, базовое хранилище (9), файлы Docker (10), тесты (11), README (12), веб-история (13), улучшение промптов (14), модерация (15), оплата Stars (16), баланс/рефералы (17), управление пользователями (18), возрастной gate (19) и надёжность Telegram (20).
|
- Реализованы блоки 2–7, базовое хранилище (9), файлы Docker (10), тесты (11), README (12), веб-история (13), улучшение промптов (14), модерация (15), оплата Stars (16), баланс/рефералы (17), управление пользователями (18), возрастной gate (19) и надёжность Telegram (20).
|
||||||
- Вживую проверены: T2I (Z-Image), I2I (Krea2), WebSocket-прогресс, улучшение промптов, Docker, CI и веб-история.
|
- Вживую проверены: T2I (Z-Image), I2I (Krea2), WebSocket-прогресс, улучшение промптов, Docker, CI и веб-история.
|
||||||
- 78 тестов: 50 проходят локально без БД, 28 тестов БД выполняются в CI на PostgreSQL через `TEST_DATABASE_URL`. Покрыты workflows, очередь, веб-история, enhancer, модерация, платежи, премиум-очередь, баланс/рефералы, управление пользователями, retry, миграция, watchdog, контекст, админ-панель, скрытие истории, настройки Krea2.
|
- 80 тестов: 52 проходят локально без БД, 28 тестов БД выполняются в CI на PostgreSQL через `TEST_DATABASE_URL`. Покрыты workflows, очередь, веб-история, enhancer, модерация, платежи, премиум-очередь, баланс/рефералы, управление пользователями, retry, миграция, watchdog, контекст, админ-панель, скрытие истории, настройки Krea2.
|
||||||
- Не проверено: реальная оплата Stars и реферальная выплата в Telegram, очередь из 3+ пользователей, падение ComfyUI, рестарт с `running`-задачей, детекция лица на реальном фото. PostgreSQL-миграция и тесты БД ещё не запускались вживую — это сделает CI и первый деплой.
|
- Не проверено: реальная оплата Stars и реферальная выплата в Telegram, очередь из 3+ пользователей, падение ComfyUI, рестарт с `running`-задачей, детекция лица на реальном фото. PostgreSQL-миграция и тесты БД ещё не запускались вживую — это сделает CI и первый деплой.
|
||||||
|
|
||||||
## 0. Допущения и решения по умолчанию
|
## 0. Допущения и решения по умолчанию
|
||||||
|
|
@ -388,6 +388,7 @@ ON requests(user_id) WHERE status IN ('queued', 'running');
|
||||||
- [x] Следующий текстовый запрос обрабатывается вместе с предыдущим промптом.
|
- [x] Следующий текстовый запрос обрабатывается вместе с предыдущим промптом.
|
||||||
- [x] Контекст обновляется только после успешной отправки результата.
|
- [x] Контекст обновляется только после успешной отправки результата.
|
||||||
- [x] `/restart` очищает контекст; новое фото начинает новую цепочку.
|
- [x] `/restart` очищает контекст; новое фото начинает новую цепочку.
|
||||||
|
- [x] `/context` показывает текущий промпт, базовую сцену и последний запрос.
|
||||||
- [x] В подписи результата итоговый промпт и подсказка `/restart`.
|
- [x] В подписи результата итоговый промпт и подсказка `/restart`.
|
||||||
- [x] Тесты БД, enhancer, очереди и подписи.
|
- [x] Тесты БД, enhancer, очереди и подписи.
|
||||||
|
|
||||||
|
|
@ -419,7 +420,7 @@ ON requests(user_id) WHERE status IN ('queued', 'running');
|
||||||
## 24. Команды и меню
|
## 24. Команды и меню
|
||||||
|
|
||||||
- [x] При старте бот получает `getMyCommands` и обновляет список при расхождении.
|
- [x] При старте бот получает `getMyCommands` и обновляет список при расхождении.
|
||||||
- [x] `setMyCommands` для `/start`, `/help`, `/balance`, `/invite`, `/status`, `/settings`, `/restart`.
|
- [x] `setMyCommands` для `/start`, `/help`, `/balance`, `/invite`, `/status`, `/context`, `/settings`, `/restart`.
|
||||||
- [x] `setChatMenuButton` включает `MenuButtonCommands` — кнопка меню слева снизу.
|
- [x] `setChatMenuButton` включает `MenuButtonCommands` — кнопка меню слева снизу.
|
||||||
- [x] Ошибки настройки команд не блокируют запуск бота.
|
- [x] Ошибки настройки команд не блокируют запуск бота.
|
||||||
- [x] Тесты сравнения команд и идемпотентной настройки.
|
- [x] Тесты сравнения команд и идемпотентной настройки.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from bot import format_context
|
||||||
|
from db import UserContext
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_context_empty() -> None:
|
||||||
|
text = format_context(None)
|
||||||
|
assert "Контекст пуст" in text
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_context_shows_prompt_and_reset_hint() -> None:
|
||||||
|
context = UserContext(
|
||||||
|
user_id=1,
|
||||||
|
base_prompt="A red apple on a table",
|
||||||
|
current_prompt="A blue apple on a table",
|
||||||
|
last_request_id=42,
|
||||||
|
last_mode="t2i",
|
||||||
|
updated_at="2026-10-19T12:00:00+00:00",
|
||||||
|
)
|
||||||
|
|
||||||
|
text = format_context(context)
|
||||||
|
|
||||||
|
assert "A blue apple on a table" in text
|
||||||
|
assert "A red apple on a table" in text
|
||||||
|
assert "Последний запрос: #42 (t2i)" in text
|
||||||
|
assert "Обновлён: 19.10.2026 15:00" in text
|
||||||
|
assert "/restart" in text
|
||||||
Loading…
Reference in New Issue