diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1996568 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +.git +.gitignore +.DS_Store +__pycache__/ +.pytest_cache/ +.ruff_cache/ +.venv/ +.worktrees/ + +# Local runtime state must not be baked into the image. +lambda_matrix.db +matrix_store/ +lambda_bot.db + +# Local environment and editor state +.env +.idea/ diff --git a/adapter/matrix/handlers/__init__.py b/adapter/matrix/handlers/__init__.py index 52ee545..d3635bf 100644 --- a/adapter/matrix/handlers/__init__.py +++ b/adapter/matrix/handlers/__init__.py @@ -10,13 +10,13 @@ from adapter.matrix.handlers.confirm import make_handle_cancel, make_handle_conf from adapter.matrix.handlers.context_commands import ( make_handle_context, make_handle_load, - make_handle_reset, make_handle_save, ) from adapter.matrix.handlers.settings import ( handle_help, handle_settings, handle_settings_connectors, + handle_unknown_command, handle_settings_plan, handle_settings_safety, handle_settings_skills, @@ -43,6 +43,7 @@ def register_matrix_handlers( dispatcher.register(IncomingCommand, "archive", make_handle_archive(client, store)) dispatcher.register(IncomingCommand, "help", handle_help) dispatcher.register(IncomingCommand, "settings", handle_settings) + dispatcher.register(IncomingCommand, "reset", handle_settings) dispatcher.register(IncomingCommand, "settings_skills", handle_settings_skills) dispatcher.register(IncomingCommand, "settings_connectors", handle_settings_connectors) dispatcher.register(IncomingCommand, "settings_soul", handle_settings_soul) @@ -54,9 +55,9 @@ def register_matrix_handlers( dispatcher.register(IncomingCallback, "confirm", make_handle_confirm(store)) dispatcher.register(IncomingCallback, "cancel", make_handle_cancel(store)) dispatcher.register(IncomingCallback, "toggle_skill", handle_toggle_skill) + dispatcher.register(IncomingCommand, "*", handle_unknown_command) if agent_api is not None and prototype_state is not None: dispatcher.register(IncomingCommand, "save", make_handle_save(agent_api, store, prototype_state)) dispatcher.register(IncomingCommand, "load", make_handle_load(store, prototype_state)) - dispatcher.register(IncomingCommand, "reset", make_handle_reset(store, agent_base_url)) dispatcher.register(IncomingCommand, "context", make_handle_context(store, prototype_state)) diff --git a/adapter/matrix/handlers/settings.py b/adapter/matrix/handlers/settings.py index a63df02..d0ff8a4 100644 --- a/adapter/matrix/handlers/settings.py +++ b/adapter/matrix/handlers/settings.py @@ -1,7 +1,6 @@ from __future__ import annotations -from adapter.matrix.reactions import build_skills_text -from core.protocol import IncomingCommand, OutgoingMessage, SettingsAction +from core.protocol import IncomingCommand, OutgoingMessage HELP_TEXT = "\n".join( @@ -12,77 +11,25 @@ HELP_TEXT = "\n".join( "!chats список активных чатов", "!rename <название> переименовать текущий чат", "!archive архивировать текущий чат", - "!settings общий обзор настроек", - "!skills список навыков", - "!soul [поле значение] показать или изменить личность", - "!safety [триггер on/off] показать или изменить безопасность", - "!status краткий статус", - "!whoami показать ваш id", - "!yes / !no подтвердить или отменить действие", + "!context показать текущее состояние контекста", + "!save [имя] сохранить текущий контекст", + "!load показать сохранённые контексты", + "", + "Остальные команды и настройки скрыты в MVP, чтобы не вводить в заблуждение.", ] ) -def _render_mapping(title: str, data: dict | None) -> str: - data = data or {} - lines = [title] - if not data: - lines.append("Нет данных.") - else: - for key, value in data.items(): - lines.append(f"• {key}: {value}") - return "\n".join(lines) - - -def _parse_bool(value: str) -> bool: - return value.lower() in {"1", "true", "yes", "on", "enable", "enabled"} +MVP_UNAVAILABLE_TEXT = ( + "Эта команда скрыта в MVP и сейчас недоступна. " + "Используй !help для списка поддерживаемых команд." +) async def handle_settings( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - settings = await settings_mgr.get(event.user_id) - chats = await chat_mgr.list_active(event.user_id) - - skills_lines = [] - for name, enabled in settings.skills.items(): - state = "on" if enabled else "off" - skills_lines.append(f" {state} {name}") - skills_text = "\n".join(skills_lines) if skills_lines else " нет навыков" - - soul_lines = [] - for key, value in (settings.soul or {}).items(): - soul_lines.append(f" {key}: {value}") - soul_text = "\n".join(soul_lines) if soul_lines else " по умолчанию" - - safety_lines = [] - for key, value in (settings.safety or {}).items(): - state = "on" if value else "off" - safety_lines.append(f" {state} {key}") - safety_text = "\n".join(safety_lines) if safety_lines else " по умолчанию" - - chat_lines = [f" {chat.display_name} ({chat.chat_id})" for chat in chats] - chats_text = "\n".join(chat_lines) if chat_lines else " нет активных чатов" - - dashboard = "\n".join( - [ - "Настройки", - "", - "Скиллы:", - skills_text, - "", - "Личность:", - soul_text, - "", - "Безопасность:", - safety_text, - "", - f"Активные чаты ({len(chats)}):", - chats_text, - ] - ) - - return [OutgoingMessage(chat_id=event.chat_id, text=dashboard)] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_help( @@ -94,104 +41,55 @@ async def handle_help( async def handle_settings_skills( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - settings = await settings_mgr.get(event.user_id) - return [OutgoingMessage(chat_id=event.chat_id, text=build_skills_text(settings))] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_settings_connectors( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - settings = await settings_mgr.get(event.user_id) - return [ - OutgoingMessage( - chat_id=event.chat_id, text=_render_mapping("🔗 Коннекторы", settings.connectors) - ) - ] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_settings_soul( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - if len(event.args) >= 2: - field = event.args[0] - value = " ".join(event.args[1:]) - await settings_mgr.apply( - event.user_id, - SettingsAction(action="set_soul", payload={"field": field, "value": value}), - ) - return [ - OutgoingMessage(chat_id=event.chat_id, text=f"Личность обновлена: {field} = {value}") - ] - settings = await settings_mgr.get(event.user_id) - return [ - OutgoingMessage(chat_id=event.chat_id, text=_render_mapping("🧠 Личность", settings.soul)) - ] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_settings_safety( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - if len(event.args) >= 2: - trigger = event.args[0] - enabled = _parse_bool(event.args[1]) - await settings_mgr.apply( - event.user_id, - SettingsAction(action="set_safety", payload={"trigger": trigger, "enabled": enabled}), - ) - state = "включена" if enabled else "выключена" - return [OutgoingMessage(chat_id=event.chat_id, text=f"Безопасность {trigger} {state}")] - settings = await settings_mgr.get(event.user_id) - return [ - OutgoingMessage( - chat_id=event.chat_id, text=_render_mapping("🔒 Безопасность", settings.safety) - ) - ] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_settings_plan( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - settings = await settings_mgr.get(event.user_id) - return [OutgoingMessage(chat_id=event.chat_id, text=_render_mapping("💳 План", settings.plan))] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_settings_status( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - chats = await chat_mgr.list_active(event.user_id) - settings = await settings_mgr.get(event.user_id) - text = "\n".join( - [ - "📊 Статус", - f"Активных чатов: {len(chats)}", - f"Скиллов: {len(settings.skills)}", - f"Коннекторов: {len(settings.connectors)}", - ] - ) - return [OutgoingMessage(chat_id=event.chat_id, text=text)] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_settings_whoami( event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr ) -> list: - return [OutgoingMessage(chat_id=event.chat_id, text=f"👤 {event.platform}:{event.user_id}")] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] async def handle_toggle_skill(event, auth_mgr, platform, chat_mgr, settings_mgr) -> list: - settings = await settings_mgr.get(event.user_id) - keys = list(settings.skills.keys()) - skill = event.payload.get("skill") - if not skill: - idx = event.payload.get("skill_index") - if isinstance(idx, int) and 1 <= idx <= len(keys): - skill = keys[idx - 1] - if not skill: - return [OutgoingMessage(chat_id=event.chat_id, text="Ошибка: не удалось определить навык.")] + return [OutgoingMessage(chat_id=event.chat_id, text=MVP_UNAVAILABLE_TEXT)] - enabled = not bool(settings.skills.get(skill, False)) - await settings_mgr.apply( - event.user_id, - SettingsAction(action="toggle_skill", payload={"skill": skill, "enabled": enabled}), - ) - state = "включён" if enabled else "выключен" - return [OutgoingMessage(chat_id=event.chat_id, text=f"Навык {skill} {state}.")] + +async def handle_unknown_command( + event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr +) -> list: + return [ + OutgoingMessage( + chat_id=event.chat_id, + text="Неизвестная команда. Используй !help для списка поддерживаемых команд.", + ) + ] diff --git a/sdk/agent_api_wrapper.py b/sdk/agent_api_wrapper.py index 3e400f7..32f126d 100644 --- a/sdk/agent_api_wrapper.py +++ b/sdk/agent_api_wrapper.py @@ -76,7 +76,7 @@ class AgentApiWrapper(AgentApi): @staticmethod def _build_ws_url(base_url: str, chat_id: int | str) -> str: - return base_url.rstrip("/") + f"/v1/agent_ws/{chat_id}/" + return base_url.rstrip("/") + f"/agent_ws/?thread_id={chat_id}" def for_chat(self, chat_id: int | str) -> "AgentApiWrapper": return type(self)( diff --git a/tests/platform/test_real.py b/tests/platform/test_real.py index 2a36a99..6edecbd 100644 --- a/tests/platform/test_real.py +++ b/tests/platform/test_real.py @@ -159,7 +159,7 @@ def test_agent_api_wrapper_falls_back_to_legacy_url_constructor(monkeypatch): wrapper = AgentApiWrapper( agent_id="agent-2", - url="https://agent.example.com/v1/agent_ws/chat-9/", + url="https://agent.example.com/agent_ws/", chat_id="chat-9", callback="cb", ) @@ -167,7 +167,7 @@ def test_agent_api_wrapper_falls_back_to_legacy_url_constructor(monkeypatch): assert calls == [ { "agent_id": "agent-2", - "url": "https://agent.example.com/v1/agent_ws/chat-9/", + "url": "https://agent.example.com/agent_ws/?thread_id=chat-9", "callback": "cb", "on_disconnect": None, }