feat: add Memory Gateway integration with async support for memory snapshots and user management
This commit is contained in:
@ -14,6 +14,8 @@ from beaver.engine.session import SessionManager
|
||||
from beaver.foundation.config import BeaverConfig, load_config
|
||||
from beaver.integrations.mcp import MCPConnectionManager
|
||||
from beaver.memory.curated.store import MemoryStore
|
||||
from beaver.memory.gateway import MemoryGatewayClient, MemoryGatewayUserStore
|
||||
from beaver.memory.gateway.service import GatewayAugmentedMemoryService
|
||||
from beaver.memory.runs import RunMemoryStore
|
||||
from beaver.memory.skills import SkillLearningStore
|
||||
from beaver.services.memory_service import MemoryService
|
||||
@ -206,7 +208,26 @@ class EngineLoader:
|
||||
|
||||
curated_root = workspace / "memory" / "curated"
|
||||
curated_memory_store = self._curated_memory_store or MemoryStore(curated_root)
|
||||
memory_service = self._memory_service or MemoryService(curated_root, store=curated_memory_store)
|
||||
if self._memory_service is not None:
|
||||
memory_service = self._memory_service
|
||||
else:
|
||||
local_memory_service = MemoryService(curated_root, store=curated_memory_store)
|
||||
memory_cfg = self.config.memory
|
||||
if memory_cfg.mode == "gateway" and memory_cfg.gateway.base_url:
|
||||
gateway_store = MemoryGatewayUserStore(workspace / "memory" / "gateway" / "state.db")
|
||||
gateway_client = MemoryGatewayClient(
|
||||
base_url=memory_cfg.gateway.base_url,
|
||||
api_key=memory_cfg.gateway.api_key,
|
||||
timeout_seconds=memory_cfg.gateway.timeout_seconds,
|
||||
store=gateway_store,
|
||||
)
|
||||
memory_service = GatewayAugmentedMemoryService(
|
||||
local_service=local_memory_service,
|
||||
client=gateway_client,
|
||||
config=memory_cfg.gateway,
|
||||
)
|
||||
else:
|
||||
memory_service = local_memory_service
|
||||
memory_service.initialize()
|
||||
run_memory_store = self._run_memory_store or RunMemoryStore(workspace / "memory" / "runs")
|
||||
skill_learning_store = self._skill_learning_store or SkillLearningStore(workspace / "memory" / "skills")
|
||||
|
||||
@ -380,10 +380,15 @@ class AgentLoop:
|
||||
resolved_max_tool_iterations = (
|
||||
self.profile.max_tool_iterations if max_tool_iterations is None else max_tool_iterations
|
||||
)
|
||||
resolved_memory_user_id = user_id or config.memory.gateway.default_user_id or None
|
||||
|
||||
# 每个 run 都捕获自己的 frozen snapshot,不能依赖 MemoryService
|
||||
# 上的共享 `_snapshot`,否则 parallel team runs 会互相覆盖。
|
||||
memory_snapshot = memory_service.capture_snapshot_for_run()
|
||||
memory_snapshot = await memory_service.capture_snapshot_for_run_async(
|
||||
user_id=resolved_memory_user_id,
|
||||
session_id=resolved_session_id,
|
||||
query=task,
|
||||
)
|
||||
|
||||
if parent_session_id:
|
||||
session_manager.ensure_session(
|
||||
@ -834,6 +839,22 @@ class AgentLoop:
|
||||
model=final_model,
|
||||
user_id=user_id,
|
||||
)
|
||||
archive_fn = getattr(memory_service, "archive_run_async", None)
|
||||
if archive_fn is not None and resolved_memory_user_id:
|
||||
asyncio.create_task(
|
||||
self._archive_memory_gateway_run(
|
||||
archive_fn=archive_fn,
|
||||
session_manager=session_manager,
|
||||
session_id=resolved_session_id,
|
||||
run_id=resolved_run_id,
|
||||
user_id=resolved_memory_user_id,
|
||||
user_message=task,
|
||||
assistant_message=final_text,
|
||||
source=source,
|
||||
title=title,
|
||||
model=final_model,
|
||||
)
|
||||
)
|
||||
self._record_run_receipts(
|
||||
skill_learning_service=skill_learning_service,
|
||||
session_manager=session_manager,
|
||||
@ -1191,6 +1212,57 @@ class AgentLoop:
|
||||
context_visible=False,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
async def _archive_memory_gateway_run(
|
||||
*,
|
||||
archive_fn: Any,
|
||||
session_manager: Any,
|
||||
session_id: str,
|
||||
run_id: str,
|
||||
user_id: str | None,
|
||||
user_message: str,
|
||||
assistant_message: str,
|
||||
source: str,
|
||||
title: str | None,
|
||||
model: str | None,
|
||||
) -> None:
|
||||
try:
|
||||
result = await archive_fn(
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
user_message=user_message,
|
||||
assistant_message=assistant_message,
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001 - archive must not change completed run result
|
||||
session_manager.append_message(
|
||||
session_id,
|
||||
run_id=run_id,
|
||||
role="system",
|
||||
event_type="memory_gateway_archive_failed",
|
||||
event_payload={"error": str(exc)},
|
||||
content=f"Memory Gateway archive failed: {exc}",
|
||||
context_visible=False,
|
||||
source=source,
|
||||
title=title,
|
||||
model=model,
|
||||
user_id=user_id,
|
||||
)
|
||||
return
|
||||
|
||||
session_manager.append_message(
|
||||
session_id,
|
||||
run_id=run_id,
|
||||
role="system",
|
||||
event_type="memory_gateway_archive_completed",
|
||||
event_payload={"result": result},
|
||||
content="Memory Gateway archive completed.",
|
||||
context_visible=False,
|
||||
source=source,
|
||||
title=title,
|
||||
model=model,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _utc_now() -> str:
|
||||
return datetime.now(timezone.utc).isoformat()
|
||||
|
||||
Reference in New Issue
Block a user