mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
1b39062ecd
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
142 lines
4.2 KiB
Python
142 lines
4.2 KiB
Python
"""Typed client for memory/context API operations.
|
|
|
|
Encapsulates all /v2/projects/{project_id}/memory/* endpoints.
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
from httpx import AsyncClient
|
|
|
|
import logfire
|
|
from basic_memory.mcp.tools.utils import call_get
|
|
from basic_memory.schemas.memory import GraphContext
|
|
|
|
|
|
class MemoryClient:
|
|
"""Typed client for memory context operations.
|
|
|
|
Centralizes:
|
|
- API path construction for /v2/projects/{project_id}/memory/*
|
|
- Response validation via Pydantic models
|
|
- Consistent error handling through call_* utilities
|
|
|
|
Usage:
|
|
async with get_client() as http_client:
|
|
client = MemoryClient(http_client, project_id)
|
|
context = await client.build_context("memory://specs/search")
|
|
"""
|
|
|
|
def __init__(self, http_client: AsyncClient, project_id: str):
|
|
"""Initialize the memory client.
|
|
|
|
Args:
|
|
http_client: HTTPX AsyncClient for making requests
|
|
project_id: Project external_id (UUID) for API calls
|
|
"""
|
|
self.http_client = http_client
|
|
self.project_id = project_id
|
|
self._base_path = f"/v2/projects/{project_id}/memory"
|
|
|
|
async def build_context(
|
|
self,
|
|
path: str,
|
|
*,
|
|
depth: int = 1,
|
|
timeframe: Optional[str] = None,
|
|
page: int = 1,
|
|
page_size: int = 10,
|
|
max_related: int = 10,
|
|
) -> GraphContext:
|
|
"""Build context from a memory path.
|
|
|
|
Args:
|
|
path: The path to build context for (without memory:// prefix)
|
|
depth: How deep to traverse relations
|
|
timeframe: Time filter (e.g., "7d", "1 week")
|
|
page: Page number (1-indexed)
|
|
page_size: Results per page
|
|
max_related: Maximum related items per result
|
|
|
|
Returns:
|
|
GraphContext with hierarchical results
|
|
|
|
Raises:
|
|
ToolError: If the request fails
|
|
"""
|
|
params: dict = {
|
|
"depth": depth,
|
|
"page": page,
|
|
"page_size": page_size,
|
|
"max_related": max_related,
|
|
}
|
|
if timeframe:
|
|
params["timeframe"] = timeframe
|
|
|
|
with logfire.span(
|
|
"mcp.client.memory.build_context",
|
|
client_name="memory",
|
|
operation="build_context",
|
|
page=page,
|
|
page_size=page_size,
|
|
):
|
|
response = await call_get(
|
|
self.http_client,
|
|
f"{self._base_path}/{path}",
|
|
params=params,
|
|
client_name="memory",
|
|
operation="build_context",
|
|
path_template="/v2/projects/{project_id}/memory/{path}",
|
|
)
|
|
return GraphContext.model_validate(response.json())
|
|
|
|
async def recent(
|
|
self,
|
|
*,
|
|
timeframe: str = "7d",
|
|
depth: int = 1,
|
|
types: Optional[list[str]] = None,
|
|
page: int = 1,
|
|
page_size: int = 10,
|
|
) -> GraphContext:
|
|
"""Get recent activity.
|
|
|
|
Args:
|
|
timeframe: Time filter (e.g., "7d", "1 week", "2 days ago")
|
|
depth: How deep to traverse relations
|
|
types: Filter by item types
|
|
page: Page number (1-indexed)
|
|
page_size: Results per page
|
|
|
|
Returns:
|
|
GraphContext with recent activity
|
|
|
|
Raises:
|
|
ToolError: If the request fails
|
|
"""
|
|
params: dict = {
|
|
"timeframe": timeframe,
|
|
"depth": depth,
|
|
"page": page,
|
|
"page_size": page_size,
|
|
}
|
|
if types:
|
|
# Join types as comma-separated string if provided
|
|
params["type"] = ",".join(types) if isinstance(types, list) else types
|
|
|
|
with logfire.span(
|
|
"mcp.client.memory.recent_activity",
|
|
client_name="memory",
|
|
operation="recent_activity",
|
|
page=page,
|
|
page_size=page_size,
|
|
):
|
|
response = await call_get(
|
|
self.http_client,
|
|
f"{self._base_path}/recent",
|
|
params=params,
|
|
client_name="memory",
|
|
operation="recent_activity",
|
|
path_template="/v2/projects/{project_id}/memory/recent",
|
|
)
|
|
return GraphContext.model_validate(response.json())
|