feat(deploy): finalize MVP deployment and file transfer approach
This commit is contained in:
parent
6369721876
commit
0f79494fbe
43 changed files with 3078 additions and 645 deletions
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
|
@ -24,21 +25,26 @@ from nio import (
|
|||
)
|
||||
from nio.responses import SyncResponse
|
||||
|
||||
from adapter.matrix.agent_registry import AgentRegistry, AgentRegistryError, load_agent_registry
|
||||
from adapter.matrix.converter import from_room_event
|
||||
from adapter.matrix.files import (
|
||||
download_matrix_attachment,
|
||||
matrix_msgtype_for_attachment,
|
||||
resolve_workspace_attachment_path,
|
||||
)
|
||||
from adapter.matrix.agent_registry import AgentRegistry, AgentRegistryError, load_agent_registry
|
||||
from adapter.matrix.handlers import register_matrix_handlers
|
||||
from adapter.matrix.handlers.auth import handle_invite, provision_workspace_chat
|
||||
from adapter.matrix.handlers.auth import (
|
||||
default_agent_notice,
|
||||
handle_invite,
|
||||
provision_workspace_chat,
|
||||
restore_workspace_access,
|
||||
)
|
||||
from adapter.matrix.handlers.context_commands import (
|
||||
LOAD_PROMPT,
|
||||
)
|
||||
from adapter.matrix.routed_platform import RoutedPlatformClient
|
||||
from adapter.matrix.reconciliation import reconcile_startup_state
|
||||
from adapter.matrix.room_router import resolve_chat_id
|
||||
from adapter.matrix.routed_platform import RoutedPlatformClient
|
||||
from adapter.matrix.store import (
|
||||
add_staged_attachment,
|
||||
clear_load_pending,
|
||||
|
|
@ -50,7 +56,6 @@ from adapter.matrix.store import (
|
|||
remove_staged_attachment_at,
|
||||
set_pending_confirm,
|
||||
set_platform_chat_id,
|
||||
set_room_agent_id,
|
||||
set_room_meta,
|
||||
)
|
||||
from core.auth import AuthManager
|
||||
|
|
@ -118,6 +123,26 @@ def _normalize_agent_base_url(url: str) -> str:
|
|||
return urlunsplit((parsed.scheme, parsed.netloc, path, "", ""))
|
||||
|
||||
|
||||
def _ws_debug_enabled() -> bool:
|
||||
value = os.environ.get("SURFACES_DEBUG_WS", "")
|
||||
return value.strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
def _configure_debug_logging() -> None:
|
||||
if not _ws_debug_enabled():
|
||||
return
|
||||
root_logger = logging.getLogger()
|
||||
if not root_logger.handlers:
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)-8s] %(name)s %(message)s",
|
||||
)
|
||||
elif root_logger.level > logging.INFO:
|
||||
root_logger.setLevel(logging.INFO)
|
||||
logging.getLogger("lambda_agent_api").setLevel(logging.INFO)
|
||||
logging.getLogger("lambda_agent_api.agent_api").setLevel(logging.INFO)
|
||||
|
||||
|
||||
def _agent_base_url_from_env() -> str:
|
||||
if base_url := os.environ.get("AGENT_BASE_URL"):
|
||||
return base_url
|
||||
|
|
@ -135,13 +160,39 @@ def _load_agent_registry_from_env(required: bool = False) -> AgentRegistry | Non
|
|||
)
|
||||
return None
|
||||
try:
|
||||
return load_agent_registry(registry_path)
|
||||
registry = load_agent_registry(registry_path)
|
||||
except (AgentRegistryError, OSError) as exc:
|
||||
raise RuntimeError(f"failed to load matrix agent registry: {registry_path}") from exc
|
||||
if _ws_debug_enabled():
|
||||
logger.warning(
|
||||
"matrix_agent_registry_loaded",
|
||||
registry_path=registry_path,
|
||||
agent_count=len(registry.agents),
|
||||
)
|
||||
for agent in registry.agents:
|
||||
logger.warning(
|
||||
"matrix_agent_registry_entry",
|
||||
registry_path=registry_path,
|
||||
agent_id=agent.agent_id,
|
||||
label=agent.label,
|
||||
configured_base_url=agent.base_url,
|
||||
normalized_base_url=_normalize_agent_base_url(agent.base_url)
|
||||
if agent.base_url
|
||||
else "",
|
||||
workspace_path=agent.workspace_path,
|
||||
)
|
||||
return registry
|
||||
|
||||
|
||||
def _build_platform_from_env(*, store: StateStore, chat_mgr: ChatManager) -> PlatformClient:
|
||||
backend = os.environ.get("MATRIX_PLATFORM_BACKEND", "mock").strip().lower()
|
||||
if _ws_debug_enabled():
|
||||
logger.warning(
|
||||
"matrix_platform_backend_selected",
|
||||
backend=backend,
|
||||
global_agent_base_url=_agent_base_url_from_env(),
|
||||
registry_path=os.environ.get("MATRIX_AGENT_REGISTRY_PATH", "").strip(),
|
||||
)
|
||||
if backend == "real":
|
||||
prototype_state = PrototypeStateStore()
|
||||
registry = _load_agent_registry_from_env(required=True)
|
||||
|
|
@ -220,6 +271,36 @@ class MatrixBot:
|
|||
await next_platform_chat_id(self.runtime.store),
|
||||
)
|
||||
|
||||
async def _refresh_room_agent_assignment(
|
||||
self, room_id: str, matrix_user_id: str, room_meta: dict | None
|
||||
) -> tuple[dict | None, bool]:
|
||||
if not room_meta or room_meta.get("redirect_room_id") or self.runtime.registry is None:
|
||||
return room_meta, False
|
||||
|
||||
assignment = self.runtime.registry.resolve_agent_for_user(matrix_user_id)
|
||||
updated = dict(room_meta)
|
||||
should_warn_default = False
|
||||
|
||||
if assignment.source == "configured" and (
|
||||
updated.get("agent_id") != assignment.agent_id
|
||||
or updated.get("agent_assignment") != "configured"
|
||||
):
|
||||
updated["agent_id"] = assignment.agent_id
|
||||
updated["agent_assignment"] = "configured"
|
||||
updated.pop("default_agent_notice_sent", None)
|
||||
elif assignment.source == "default":
|
||||
if not updated.get("agent_id"):
|
||||
updated["agent_id"] = assignment.agent_id
|
||||
if updated.get("agent_id") == assignment.agent_id:
|
||||
updated["agent_assignment"] = "default"
|
||||
should_warn_default = not updated.get("default_agent_notice_sent")
|
||||
updated["default_agent_notice_sent"] = True
|
||||
|
||||
if updated != room_meta:
|
||||
await set_room_meta(self.runtime.store, room_id, updated)
|
||||
return updated, should_warn_default
|
||||
return room_meta, should_warn_default
|
||||
|
||||
async def on_room_message(self, room: MatrixRoom, event: RoomMessageText) -> None:
|
||||
if getattr(event, "sender", None) == self.client.user_id:
|
||||
return
|
||||
|
|
@ -228,6 +309,14 @@ class MatrixBot:
|
|||
room_meta = await get_room_meta(self.runtime.store, room.room_id)
|
||||
if room_meta is not None and not room_meta.get("redirect_room_id"):
|
||||
await self._ensure_platform_chat_id(room.room_id, room_meta)
|
||||
room_meta, warn_default_agent = await self._refresh_room_agent_assignment(
|
||||
room.room_id, sender, room_meta
|
||||
)
|
||||
if warn_default_agent and not body.startswith("!"):
|
||||
await self._send_all(
|
||||
room.room_id,
|
||||
[OutgoingMessage(chat_id=room.room_id, text=default_agent_notice())],
|
||||
)
|
||||
|
||||
load_pending = await get_load_pending(self.runtime.store, sender, room.room_id)
|
||||
if load_pending is not None and (body.isdigit() or body == "!cancel"):
|
||||
|
|
@ -241,17 +330,97 @@ class MatrixBot:
|
|||
await self._send_all(room.room_id, outgoing)
|
||||
return
|
||||
elif room_meta.get("redirect_room_id"):
|
||||
display_name = getattr(room, "display_name", None) or sender
|
||||
if body == "!new":
|
||||
try:
|
||||
created = await provision_workspace_chat(
|
||||
self.client,
|
||||
sender,
|
||||
display_name,
|
||||
self.runtime.platform,
|
||||
self.runtime.store,
|
||||
self.runtime.auth_mgr,
|
||||
self.runtime.chat_mgr,
|
||||
registry=self.runtime.registry,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"matrix_entry_room_new_chat_failed",
|
||||
room_id=room.room_id,
|
||||
sender=sender,
|
||||
error=str(exc),
|
||||
)
|
||||
await self._send_all(
|
||||
room.room_id,
|
||||
[
|
||||
OutgoingMessage(
|
||||
chat_id=room.room_id,
|
||||
text="Не удалось создать новый рабочий чат.",
|
||||
)
|
||||
],
|
||||
)
|
||||
return
|
||||
|
||||
welcome = f"Создал новый рабочий чат {created['room_name']}."
|
||||
if created.get("agent_assignment") == "default":
|
||||
welcome = f"{welcome}\n\n{default_agent_notice()}"
|
||||
await self.client.room_send(
|
||||
created["chat_room_id"],
|
||||
"m.room.message",
|
||||
{"msgtype": "m.text", "body": welcome},
|
||||
)
|
||||
await set_room_meta(
|
||||
self.runtime.store,
|
||||
room.room_id,
|
||||
{
|
||||
**room_meta,
|
||||
"redirect_room_id": created["chat_room_id"],
|
||||
"redirect_chat_id": created["chat_id"],
|
||||
},
|
||||
)
|
||||
await self._send_all(
|
||||
room.room_id,
|
||||
[
|
||||
OutgoingMessage(
|
||||
chat_id=room.room_id,
|
||||
text=(
|
||||
f"Создал рабочий чат {created['room_name']} "
|
||||
f"({created['chat_id']}) и отправил приглашение."
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
return
|
||||
|
||||
restored = await restore_workspace_access(
|
||||
self.client,
|
||||
sender,
|
||||
display_name,
|
||||
self.runtime.platform,
|
||||
self.runtime.store,
|
||||
self.runtime.auth_mgr,
|
||||
self.runtime.chat_mgr,
|
||||
registry=self.runtime.registry,
|
||||
)
|
||||
redirect_room_id = room_meta["redirect_room_id"]
|
||||
redirect_chat_id = room_meta.get("redirect_chat_id", "рабочий чат")
|
||||
if restored.get("created_new_chat"):
|
||||
text = (
|
||||
f"Создал новый рабочий чат {restored['room_name']} "
|
||||
f"({restored['chat_id']}) и отправил приглашение."
|
||||
)
|
||||
else:
|
||||
text = (
|
||||
f"Рабочий чат уже создан: {redirect_chat_id}. "
|
||||
"Я повторно отправил приглашения в пространство Lambda и рабочие чаты. "
|
||||
"Чтобы создать новый чат, напишите !new здесь."
|
||||
)
|
||||
await self._send_all(
|
||||
room.room_id,
|
||||
[
|
||||
OutgoingMessage(
|
||||
chat_id=room.room_id,
|
||||
text=(
|
||||
f"Рабочий чат уже создан: {redirect_chat_id}. "
|
||||
"Открой приглашённую комнату для продолжения."
|
||||
),
|
||||
text=text,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
|
@ -302,6 +471,15 @@ class MatrixBot:
|
|||
incoming,
|
||||
)
|
||||
agent_id = (room_meta or {}).get("agent_id")
|
||||
if _ws_debug_enabled() and not body.startswith("!"):
|
||||
logger.warning(
|
||||
"matrix_incoming_message_route",
|
||||
room_id=room.room_id,
|
||||
sender=sender,
|
||||
local_chat_id=local_chat_id,
|
||||
agent_id=agent_id,
|
||||
platform_chat_id=(room_meta or {}).get("platform_chat_id"),
|
||||
)
|
||||
workspace_root = self._agent_workspace_root(agent_id)
|
||||
try:
|
||||
outgoing = await self.runtime.dispatcher.dispatch(incoming)
|
||||
|
|
@ -520,6 +698,8 @@ class MatrixBot:
|
|||
f"Привет, {created['user'].display_name or sender}! Пиши — я здесь.\n\n"
|
||||
"Команды: !new · !chats · !rename · !archive · !context · !save · !load · !help"
|
||||
)
|
||||
if created.get("agent_assignment") == "default":
|
||||
welcome = f"{welcome}\n\n{default_agent_notice()}"
|
||||
await set_room_meta(
|
||||
self.runtime.store,
|
||||
room.room_id,
|
||||
|
|
@ -715,6 +895,7 @@ async def send_outgoing(
|
|||
|
||||
|
||||
async def main() -> None:
|
||||
_configure_debug_logging()
|
||||
homeserver = os.environ.get("MATRIX_HOMESERVER")
|
||||
user_id = os.environ.get("MATRIX_USER_ID")
|
||||
device_id = os.environ.get("MATRIX_DEVICE_ID", "")
|
||||
|
|
@ -768,6 +949,15 @@ async def main() -> None:
|
|||
store_path=store_path,
|
||||
request_timeout=client_config.request_timeout,
|
||||
)
|
||||
if _ws_debug_enabled():
|
||||
logger.warning(
|
||||
"matrix_ws_debug_enabled",
|
||||
homeserver=homeserver,
|
||||
user_id=user_id,
|
||||
backend=os.environ.get("MATRIX_PLATFORM_BACKEND", "mock").strip().lower(),
|
||||
global_agent_base_url=_agent_base_url_from_env(),
|
||||
registry_path=os.environ.get("MATRIX_AGENT_REGISTRY_PATH", "").strip(),
|
||||
)
|
||||
try:
|
||||
await client.sync_forever(timeout=30000, since=since_token)
|
||||
finally:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue