fix max-bot, add tests
This commit is contained in:
parent
7abbaf7e7a
commit
2ad1438e1c
17 changed files with 1621 additions and 494 deletions
|
|
@ -1 +1,5 @@
|
|||
"""MAX surface handlers."""
|
||||
"""MAX surface handlers."""
|
||||
|
||||
from adapter.max.handlers.commands import register_max_handlers
|
||||
|
||||
__all__ = ["register_max_handlers"]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class AttachmentHandler:
|
|||
def handle_list(self, max_chat_id: str) -> str:
|
||||
attachments = self.store.get_attachments(max_chat_id)
|
||||
if not attachments:
|
||||
return "Attachment queue is empty."
|
||||
return "Очередь вложений пуста."
|
||||
lines = [f" {i+1}. {name}" for i, (_, name) in enumerate(attachments)]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
|
@ -17,13 +17,13 @@ class AttachmentHandler:
|
|||
attachments = self.store.staged_attachments.get(max_chat_id, [])
|
||||
if index.lower() == "all":
|
||||
self.store.staged_attachments[max_chat_id] = []
|
||||
return "All attachments removed from queue."
|
||||
return "Все вложения удалены из очереди."
|
||||
|
||||
try:
|
||||
idx = int(index) - 1
|
||||
if 0 <= idx < len(attachments):
|
||||
removed = attachments.pop(idx)
|
||||
return f"Removed: {removed[1]}"
|
||||
return "Invalid index."
|
||||
return f"Удалено: {removed[1]}"
|
||||
return "Неверный номер."
|
||||
except ValueError:
|
||||
return "Usage: !remove <number> or !remove all"
|
||||
return "Использование: /remove <номер> или /remove all"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,15 @@ class ChatHandler:
|
|||
def __init__(self, store: ChatStore):
|
||||
self.store = store
|
||||
|
||||
def handle_new(self, max_chat_id: str, user_id: str, agent_id: str, name: str = None) -> str:
|
||||
def handle_new(
|
||||
self,
|
||||
max_chat_id: str,
|
||||
user_id: str,
|
||||
agent_id: str,
|
||||
name: str | None = None,
|
||||
*,
|
||||
workspace_path: str = "",
|
||||
) -> str:
|
||||
platform_chat_id = str(uuid.uuid4())
|
||||
room = RoomMeta(
|
||||
platform_chat_id=platform_chat_id,
|
||||
|
|
@ -15,6 +23,7 @@ class ChatHandler:
|
|||
name=name or "New Chat",
|
||||
user_id=user_id,
|
||||
agent_id=agent_id,
|
||||
workspace_path=workspace_path,
|
||||
)
|
||||
self.store.add_room(room)
|
||||
return platform_chat_id
|
||||
|
|
@ -22,27 +31,27 @@ class ChatHandler:
|
|||
def handle_chats(self, user_id: str) -> str:
|
||||
rooms = self.store.list_rooms_for_user(user_id)
|
||||
if not rooms:
|
||||
return "No active chats."
|
||||
return "Нет активных чатов."
|
||||
lines = [f" {i+1}. {r.name}" for i, r in enumerate(rooms)]
|
||||
return "\n".join(lines)
|
||||
|
||||
def handle_rename(self, max_chat_id: str, new_name: str) -> str:
|
||||
room = self.store.get_room_by_max_chat_id(max_chat_id)
|
||||
if not room:
|
||||
return "Chat not found."
|
||||
return "Чат не найден."
|
||||
room.name = new_name
|
||||
return f"Chat renamed to: {new_name}"
|
||||
return f"Чат переименован в: {new_name}"
|
||||
|
||||
def handle_archive(self, max_chat_id: str) -> str:
|
||||
room = self.store.get_room_by_max_chat_id(max_chat_id)
|
||||
if not room:
|
||||
return "Chat not found."
|
||||
return "Чат не найден."
|
||||
self.store.remove_room(max_chat_id)
|
||||
return "Chat archived."
|
||||
return "Чат архивирован."
|
||||
|
||||
def handle_clear(self, max_chat_id: str) -> str:
|
||||
room = self.store.get_room_by_max_chat_id(max_chat_id)
|
||||
if not room:
|
||||
return "Chat not found."
|
||||
return "Чат не найден."
|
||||
room.platform_chat_id = str(uuid.uuid4())
|
||||
return "Chat context cleared."
|
||||
return "Контекст чата очищен."
|
||||
112
adapter/max/handlers/commands.py
Normal file
112
adapter/max/handlers/commands.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from adapter.max.handlers.chat import ChatHandler as MaxChatHandler
|
||||
from adapter.max.handlers.help import get_help
|
||||
from adapter.max.store import ChatStore
|
||||
from sdk.prototype_state import PrototypeStateStore
|
||||
|
||||
from core.handler import EventDispatcher
|
||||
from core.protocol import IncomingCallback, IncomingCommand, OutgoingMessage
|
||||
|
||||
_SINGLE_DIALOG_HINT = (
|
||||
"В MAX один диалог с ботом. Чтобы сбросить контекст агента, используйте /clear или /reset."
|
||||
)
|
||||
|
||||
|
||||
def register_max_handlers(
|
||||
dispatcher: EventDispatcher,
|
||||
*,
|
||||
chat_store: ChatStore,
|
||||
max_chat_handler: MaxChatHandler,
|
||||
prototype_state: PrototypeStateStore,
|
||||
) -> None:
|
||||
async def _room_or_error(
|
||||
event: IncomingCommand,
|
||||
) -> tuple[str, str] | list[OutgoingMessage]:
|
||||
room = chat_store.get_room_by_platform_chat_id(event.chat_id)
|
||||
if room is None:
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id,
|
||||
text="Состояние ещё не готово. Напишите сообщение ещё раз.",
|
||||
)
|
||||
]
|
||||
return room.max_chat_id, room.platform_chat_id
|
||||
|
||||
async def handle_max_help(event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr): # noqa: ARG001
|
||||
if not await auth_mgr.is_authenticated(event.user_id):
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id,
|
||||
text="Введите /start чтобы начать.",
|
||||
)
|
||||
]
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=get_help())]
|
||||
|
||||
async def handle_max_no_multichat(event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr): # noqa: ARG001
|
||||
if not await auth_mgr.is_authenticated(event.user_id):
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id,
|
||||
text="Введите /start чтобы начать.",
|
||||
)
|
||||
]
|
||||
_ = event
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=_SINGLE_DIALOG_HINT)]
|
||||
|
||||
async def handle_max_clear(event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr): # noqa: ARG001
|
||||
if not await auth_mgr.is_authenticated(event.user_id):
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id,
|
||||
text="Введите /start чтобы начать.",
|
||||
)
|
||||
]
|
||||
routed = await _room_or_error(event)
|
||||
if isinstance(routed, list):
|
||||
return routed
|
||||
max_chat_id, platform_chat_old = routed
|
||||
|
||||
await prototype_state.clear_current_session(platform_chat_old)
|
||||
await chat_mgr.archive(platform_chat_old, event.user_id)
|
||||
max_chat_handler.handle_clear(max_chat_id)
|
||||
room = chat_store.get_room_by_max_chat_id(max_chat_id)
|
||||
if room is None:
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=platform_chat_old,
|
||||
text="Не удалось сбросить контекст.",
|
||||
)
|
||||
]
|
||||
platform_chat_new = room.platform_chat_id
|
||||
await chat_mgr.get_or_create(
|
||||
user_id=event.user_id,
|
||||
chat_id=platform_chat_new,
|
||||
platform="max",
|
||||
surface_ref=max_chat_id,
|
||||
name=room.name,
|
||||
)
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=platform_chat_new,
|
||||
text="Контекст агента сброшен.",
|
||||
)
|
||||
]
|
||||
|
||||
for cmd in ("new", "rename", "archive", "chats"):
|
||||
dispatcher.register(IncomingCommand, cmd, handle_max_no_multichat)
|
||||
|
||||
dispatcher.register(IncomingCommand, "reset", handle_max_clear)
|
||||
dispatcher.register(IncomingCommand, "clear", handle_max_clear)
|
||||
dispatcher.register(IncomingCommand, "help", handle_max_help)
|
||||
|
||||
async def handle_max_plain_callback(event: IncomingCallback, auth_mgr, platform, chat_mgr, settings_mgr): # noqa: ARG001
|
||||
payload = str(event.payload.get("payload", ""))
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id,
|
||||
text=f"Неизвестное действие кнопки: {payload}",
|
||||
)
|
||||
]
|
||||
|
||||
dispatcher.register(IncomingCallback, "max_callback", handle_max_plain_callback)
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
"""Help handler for MAX surface."""
|
||||
"""Help text for MAX surface (single dialog, slash commands)."""
|
||||
|
||||
HELP_TEXT = """
|
||||
Available commands:
|
||||
Команды (/ как в Telegram):
|
||||
|
||||
Chat management:
|
||||
!new [name] — Create a new chat
|
||||
!chats — List active chats
|
||||
!rename <name> — Rename current chat
|
||||
!archive — Archive current chat
|
||||
!clear / !reset — Reset chat context
|
||||
/start — начать
|
||||
/help — эта справка
|
||||
/clear или /reset — сбросить контекст агента
|
||||
|
||||
Attachments:
|
||||
!list — Show attachment queue
|
||||
!remove <n> — Remove attachment from queue
|
||||
!remove all — Clear attachment queue
|
||||
Вложения (файл без текста ставится в очередь):
|
||||
|
||||
Actions:
|
||||
!yes — Confirm agent action
|
||||
!no — Cancel agent action
|
||||
!help — Show this help
|
||||
/list — очередь вложений
|
||||
/remove n — убрать из очереди
|
||||
/remove all — очистить очередь
|
||||
|
||||
Подтверждения агента:
|
||||
|
||||
/yes / /no
|
||||
|
||||
Команды вида /new, /chats, /rename, /archive в MAX не нужны —
|
||||
у вас один диалог с ботом; контекст сбрасывайте через /clear.
|
||||
"""
|
||||
|
||||
|
||||
def get_help() -> str:
|
||||
return HELP_TEXT.strip()
|
||||
return HELP_TEXT.strip()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue