feat: add matrix routed platform facade
This commit is contained in:
parent
7627012f24
commit
242f4aadd3
3 changed files with 376 additions and 8 deletions
|
|
@ -30,11 +30,13 @@ from adapter.matrix.files import (
|
|||
matrix_msgtype_for_attachment,
|
||||
resolve_workspace_attachment_path,
|
||||
)
|
||||
from adapter.matrix.agent_registry import 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.context_commands import (
|
||||
LOAD_PROMPT,
|
||||
)
|
||||
from adapter.matrix.routed_platform import RoutedPlatformClient
|
||||
from adapter.matrix.room_router import resolve_chat_id
|
||||
from adapter.matrix.store import (
|
||||
add_staged_attachment,
|
||||
|
|
@ -118,13 +120,31 @@ def _agent_base_url_from_env() -> str:
|
|||
return "http://127.0.0.1:8000"
|
||||
|
||||
|
||||
def _build_platform_from_env() -> PlatformClient:
|
||||
def _build_platform_from_env(*, store: StateStore, chat_mgr: ChatManager) -> PlatformClient:
|
||||
backend = os.environ.get("MATRIX_PLATFORM_BACKEND", "mock").strip().lower()
|
||||
if backend == "real":
|
||||
prototype_state = PrototypeStateStore()
|
||||
registry_path = os.environ.get("MATRIX_AGENT_REGISTRY_PATH", "").strip()
|
||||
if registry_path:
|
||||
registry = load_agent_registry(registry_path)
|
||||
delegates = {
|
||||
agent.agent_id: RealPlatformClient(
|
||||
agent_id=agent.agent_id,
|
||||
agent_base_url=_agent_base_url_from_env(),
|
||||
prototype_state=prototype_state,
|
||||
platform="matrix",
|
||||
)
|
||||
for agent in registry.agents
|
||||
}
|
||||
return RoutedPlatformClient(
|
||||
chat_mgr=chat_mgr,
|
||||
store=store,
|
||||
delegates=delegates,
|
||||
)
|
||||
return RealPlatformClient(
|
||||
agent_id="matrix-bot",
|
||||
agent_base_url=_agent_base_url_from_env(),
|
||||
prototype_state=PrototypeStateStore(),
|
||||
prototype_state=prototype_state,
|
||||
platform="matrix",
|
||||
)
|
||||
return MockPlatformClient()
|
||||
|
|
@ -135,9 +155,10 @@ def build_runtime(
|
|||
store: StateStore | None = None,
|
||||
client: AsyncClient | None = None,
|
||||
) -> MatrixRuntime:
|
||||
platform = platform or _build_platform_from_env()
|
||||
store = store or InMemoryStore()
|
||||
chat_mgr = ChatManager(platform, store)
|
||||
platform = platform or _build_platform_from_env(store=store, chat_mgr=chat_mgr)
|
||||
chat_mgr = ChatManager(platform, store)
|
||||
auth_mgr = AuthManager(platform, store)
|
||||
settings_mgr = SettingsManager(platform, store)
|
||||
prototype_state = getattr(platform, "_prototype_state", None)
|
||||
|
|
@ -224,10 +245,7 @@ class MatrixBot:
|
|||
)
|
||||
return
|
||||
local_chat_id = await resolve_chat_id(self.runtime.store, room.room_id, sender)
|
||||
dispatch_chat_id = local_chat_id
|
||||
if not body.startswith("!"):
|
||||
dispatch_chat_id = (room_meta or {}).get("platform_chat_id") or local_chat_id
|
||||
incoming = from_room_event(event, room_id=room.room_id, chat_id=dispatch_chat_id)
|
||||
incoming = from_room_event(event, room_id=room.room_id, chat_id=local_chat_id)
|
||||
if incoming is None:
|
||||
return
|
||||
if isinstance(incoming, IncomingCommand) and incoming.command in {
|
||||
|
|
@ -274,7 +292,7 @@ class MatrixBot:
|
|||
)
|
||||
outgoing = [
|
||||
OutgoingMessage(
|
||||
chat_id=dispatch_chat_id,
|
||||
chat_id=local_chat_id,
|
||||
text="Сервис временно недоступен. Попробуйте ещё раз позже.",
|
||||
)
|
||||
]
|
||||
|
|
|
|||
110
adapter/matrix/routed_platform.py
Normal file
110
adapter/matrix/routed_platform.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator, Mapping
|
||||
|
||||
from adapter.matrix.store import get_room_meta
|
||||
from core.chat import ChatManager
|
||||
from core.store import StateStore
|
||||
from sdk.interface import (
|
||||
Attachment,
|
||||
MessageChunk,
|
||||
MessageResponse,
|
||||
PlatformClient,
|
||||
PlatformError,
|
||||
User,
|
||||
UserSettings,
|
||||
)
|
||||
|
||||
|
||||
class RoutedPlatformClient(PlatformClient):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
chat_mgr: ChatManager,
|
||||
store: StateStore,
|
||||
delegates: Mapping[str, PlatformClient],
|
||||
) -> None:
|
||||
if not delegates:
|
||||
raise ValueError("RoutedPlatformClient requires at least one delegate")
|
||||
self._chat_mgr = chat_mgr
|
||||
self._store = store
|
||||
self._delegates = dict(delegates)
|
||||
self._default_client = next(iter(self._delegates.values()))
|
||||
self._prototype_state = getattr(self._default_client, "_prototype_state", None)
|
||||
|
||||
async def get_or_create_user(
|
||||
self,
|
||||
external_id: str,
|
||||
platform: str,
|
||||
display_name: str | None = None,
|
||||
) -> User:
|
||||
return await self._default_client.get_or_create_user(
|
||||
external_id=external_id,
|
||||
platform=platform,
|
||||
display_name=display_name,
|
||||
)
|
||||
|
||||
async def send_message(
|
||||
self,
|
||||
user_id: str,
|
||||
chat_id: str,
|
||||
text: str,
|
||||
attachments: list[Attachment] | None = None,
|
||||
) -> MessageResponse:
|
||||
delegate, platform_chat_id = await self._resolve_delegate(user_id, chat_id)
|
||||
return await delegate.send_message(user_id, platform_chat_id, text, attachments)
|
||||
|
||||
async def stream_message(
|
||||
self,
|
||||
user_id: str,
|
||||
chat_id: str,
|
||||
text: str,
|
||||
attachments: list[Attachment] | None = None,
|
||||
) -> AsyncIterator[MessageChunk]:
|
||||
delegate, platform_chat_id = await self._resolve_delegate(user_id, chat_id)
|
||||
async for chunk in delegate.stream_message(user_id, platform_chat_id, text, attachments):
|
||||
yield chunk
|
||||
|
||||
async def get_settings(self, user_id: str) -> UserSettings:
|
||||
return await self._default_client.get_settings(user_id)
|
||||
|
||||
async def update_settings(self, user_id: str, action) -> None:
|
||||
await self._default_client.update_settings(user_id, action)
|
||||
|
||||
async def close(self) -> None:
|
||||
for delegate in self._delegates.values():
|
||||
close = getattr(delegate, "close", None)
|
||||
if callable(close):
|
||||
await close()
|
||||
|
||||
async def _resolve_delegate(self, user_id: str, local_chat_id: str) -> tuple[PlatformClient, str]:
|
||||
chat = await self._chat_mgr.get(local_chat_id, user_id)
|
||||
if chat is None:
|
||||
raise PlatformError(
|
||||
f"unknown matrix chat id: {local_chat_id}",
|
||||
code="MATRIX_CHAT_NOT_FOUND",
|
||||
)
|
||||
|
||||
room_meta = await get_room_meta(self._store, chat.surface_ref)
|
||||
if room_meta is None:
|
||||
raise PlatformError(
|
||||
f"matrix room is not bound: {chat.surface_ref}",
|
||||
code="MATRIX_ROOM_NOT_BOUND",
|
||||
)
|
||||
|
||||
agent_id = room_meta.get("agent_id")
|
||||
platform_chat_id = room_meta.get("platform_chat_id")
|
||||
if not agent_id or not platform_chat_id:
|
||||
raise PlatformError(
|
||||
f"matrix room routing is incomplete: {chat.surface_ref}",
|
||||
code="MATRIX_ROUTE_INCOMPLETE",
|
||||
)
|
||||
|
||||
delegate = self._delegates.get(str(agent_id))
|
||||
if delegate is None:
|
||||
raise PlatformError(
|
||||
f"unknown matrix agent id: {agent_id}",
|
||||
code="MATRIX_AGENT_NOT_FOUND",
|
||||
)
|
||||
|
||||
return delegate, str(platform_chat_id)
|
||||
Loading…
Add table
Add a link
Reference in a new issue