feat: add Memory Gateway integration with async support for memory snapshots and user management
This commit is contained in:
150
app-instance/backend/beaver/memory/gateway/client.py
Normal file
150
app-instance/backend/beaver/memory/gateway/client.py
Normal file
@ -0,0 +1,150 @@
|
||||
"""HTTP client for Memory Gateway's `/memory-system` API."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from .store import MemoryGatewayUserStore
|
||||
|
||||
|
||||
class MemoryGatewayClient:
|
||||
"""Small async client for the Memory Gateway business API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
base_url: str,
|
||||
store: MemoryGatewayUserStore,
|
||||
api_key: str = "",
|
||||
timeout_seconds: float = 15.0,
|
||||
transport: httpx.AsyncBaseTransport | None = None,
|
||||
) -> None:
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.api_key = api_key
|
||||
self.timeout_seconds = timeout_seconds
|
||||
self.store = store
|
||||
self.transport = transport
|
||||
|
||||
async def ensure_user(self, user_id: str) -> str:
|
||||
cached = self.store.get_user_key(user_id)
|
||||
if cached:
|
||||
return cached
|
||||
|
||||
data = await self._post("/memory-system/users", {"user_id": user_id})
|
||||
user_key = self._extract_user_key(data)
|
||||
if not user_key:
|
||||
raise RuntimeError("Memory Gateway user creation response missing user_key")
|
||||
self.store.save_user_key(user_id, user_key)
|
||||
return user_key
|
||||
|
||||
async def get_profile(
|
||||
self,
|
||||
*,
|
||||
user_id: str,
|
||||
user_key: str,
|
||||
query: str = "用户画像",
|
||||
limit: int = 5,
|
||||
) -> dict[str, Any]:
|
||||
return await self._get(
|
||||
f"/memory-system/users/{user_id}/profile",
|
||||
{"user_key": user_key, "query": query, "limit": limit},
|
||||
)
|
||||
|
||||
async def get_session_context(
|
||||
self,
|
||||
*,
|
||||
user_id: str,
|
||||
user_key: str,
|
||||
session_id: str,
|
||||
query: str,
|
||||
limit: int = 5,
|
||||
) -> dict[str, Any]:
|
||||
return await self._post(
|
||||
f"/memory-system/sessions/{session_id}/context",
|
||||
{"user_id": user_id, "user_key": user_key, "query": query, "limit": limit},
|
||||
)
|
||||
|
||||
async def search(
|
||||
self,
|
||||
*,
|
||||
user_id: str,
|
||||
user_key: str,
|
||||
session_id: str,
|
||||
query: str,
|
||||
limit: int = 5,
|
||||
) -> dict[str, Any]:
|
||||
return await self._post(
|
||||
"/memory-system/search",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"user_key": user_key,
|
||||
"session_id": session_id,
|
||||
"query": query,
|
||||
"use_llm": False,
|
||||
"limit": limit,
|
||||
},
|
||||
)
|
||||
|
||||
async def ingest_messages(
|
||||
self,
|
||||
*,
|
||||
user_id: str,
|
||||
user_key: str,
|
||||
session_id: str,
|
||||
user_message: str | None,
|
||||
assistant_message: str | None,
|
||||
) -> dict[str, Any]:
|
||||
return await self._post(
|
||||
"/memory-system/messages",
|
||||
{
|
||||
"user_id": user_id,
|
||||
"user_key": user_key,
|
||||
"session_id": session_id,
|
||||
"user_message": user_message,
|
||||
"assistant_message": assistant_message,
|
||||
},
|
||||
)
|
||||
|
||||
async def commit_session(
|
||||
self,
|
||||
*,
|
||||
user_id: str,
|
||||
user_key: str,
|
||||
session_id: str,
|
||||
) -> dict[str, Any]:
|
||||
return await self._post(
|
||||
f"/memory-system/sessions/{session_id}/commit",
|
||||
{"user_id": user_id, "user_key": user_key},
|
||||
)
|
||||
|
||||
async def _get(self, path: str, params: dict[str, Any]) -> dict[str, Any]:
|
||||
async with self._client() as client:
|
||||
response = await client.get(path, params=params)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
async def _post(self, path: str, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
async with self._client() as client:
|
||||
response = await client.post(path, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def _client(self) -> httpx.AsyncClient:
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if self.api_key:
|
||||
headers["X-API-Key"] = self.api_key
|
||||
return httpx.AsyncClient(
|
||||
base_url=self.base_url,
|
||||
headers=headers,
|
||||
timeout=self.timeout_seconds,
|
||||
transport=self.transport,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _extract_user_key(data: dict[str, Any]) -> str | None:
|
||||
account = data.get("account")
|
||||
result = account.get("result") if isinstance(account, dict) else None
|
||||
user_key = result.get("user_key") if isinstance(result, dict) else None
|
||||
return str(user_key) if user_key else None
|
||||
Reference in New Issue
Block a user