feat(task-5): scope matrix context state per room

This commit is contained in:
Mikhail Putilovskij 2026-04-19 17:41:04 +03:00
parent 03160a3b37
commit c11c8ecfbf
7 changed files with 189 additions and 72 deletions

View file

@ -297,7 +297,19 @@ class MatrixBot:
await clear_load_pending(self.runtime.store, user_id, room_id)
prototype_state = getattr(self.runtime.platform, "_prototype_state", None)
if prototype_state is not None:
await prototype_state.set_current_session(user_id, name)
room_meta = await get_room_meta(self.runtime.store, room_id)
context_keys = []
if room_meta is not None:
platform_chat_id = room_meta.get("platform_chat_id")
if platform_chat_id:
context_keys.append(platform_chat_id)
chat_id = room_meta.get("chat_id")
if chat_id:
context_keys.append(chat_id)
if not context_keys:
context_keys.append(room_id)
for context_key in dict.fromkeys(context_keys):
await prototype_state.set_current_session(context_key, name)
try:
await self.runtime.platform.send_message(

View file

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
import httpx
import structlog
from adapter.matrix.store import set_load_pending, set_reset_pending
from adapter.matrix.store import get_room_meta, set_load_pending, set_reset_pending
from core.protocol import IncomingCommand, OutgoingEvent, OutgoingMessage
if TYPE_CHECKING:
@ -43,6 +43,17 @@ async def _resolve_room_id(event: IncomingCommand, chat_mgr) -> str:
return event.chat_id
async def _resolve_context_scope(
event: IncomingCommand,
store: "StateStore",
chat_mgr,
) -> tuple[str, str | None]:
room_id = await _resolve_room_id(event, chat_mgr)
room_meta = await get_room_meta(store, room_id)
platform_chat_id = room_meta.get("platform_chat_id") if room_meta else None
return room_id, platform_chat_id
def make_handle_save(agent_api, store: "StateStore", prototype_state: "PrototypeStateStore"):
async def handle_save(
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
@ -69,8 +80,18 @@ def make_handle_save(agent_api, store: "StateStore", prototype_state: "Prototype
logger.warning("save_agent_call_failed", error=str(exc))
return [OutgoingMessage(chat_id=event.chat_id, text=f"Ошибка при сохранении: {exc}")]
await prototype_state.add_saved_session(event.user_id, name)
return [OutgoingMessage(chat_id=event.chat_id, text=f"Сохранение запущено: {name}")]
_, platform_chat_id = await _resolve_context_scope(event, store, chat_mgr)
await prototype_state.add_saved_session(
event.user_id,
name,
source_context_id=platform_chat_id or event.chat_id,
)
return [
OutgoingMessage(
chat_id=event.chat_id,
text=f"Запрос на сохранение отправлен агенту: {name}",
)
]
return handle_save
@ -88,7 +109,7 @@ def make_handle_load(store: "StateStore", prototype_state: "PrototypeStateStore"
)
]
room_id = await _resolve_room_id(event, chat_mgr)
room_id, _ = await _resolve_context_scope(event, store, chat_mgr)
lines = ["Сохранённые сессии:"]
for index, session in enumerate(sessions, start=1):
created = session.get("created_at", "")[:10]
@ -150,12 +171,20 @@ def make_handle_context(store: "StateStore", prototype_state: "PrototypeStateSto
async def handle_context(
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
) -> list[OutgoingEvent]:
current_session = await prototype_state.get_current_session(event.user_id)
tokens_used = await prototype_state.get_last_tokens_used(event.user_id)
_, platform_chat_id = await _resolve_context_scope(event, store, chat_mgr)
context_key = platform_chat_id or event.chat_id
current_session = await prototype_state.get_current_session(context_key)
tokens_used = await prototype_state.get_last_tokens_used(context_key)
if platform_chat_id is not None and event.chat_id != platform_chat_id:
if current_session is None:
current_session = await prototype_state.get_current_session(event.chat_id)
if tokens_used == 0:
tokens_used = await prototype_state.get_last_tokens_used(event.chat_id)
sessions = await prototype_state.list_saved_sessions(event.user_id)
lines = [
"Контекст:",
f" Контекст чата: {platform_chat_id or event.chat_id}",
f" Сессия: {current_session or 'не загружена'}",
f" Токены (последний ответ): {tokens_used}",
f" Сохранения ({len(sessions)}):",