feat(builtin_tools/memory): add optional namespace param to commit_memory and search_memory
Adds optional namespace parameter so agents can organize memories into named buckets (e.g. "facts", "procedures", "blockers"). Defaults to "general". - commit_memory(content, scope, *, namespace=None): namespace normalised to "general" when None or whitespace-only, forwarded to awareness client and included in httpx POST body. - search_memory(query, scope, *, namespace=None): namespace forwarded as ?namespace= query param (omitted when None), matching the existing behaviour for the scope param. - AwarenessClient.commit() and .search() updated to accept namespace kwarg. Fixes #908.
This commit is contained in:
parent
2bb0f97085
commit
ecc0a231bf
@ -51,21 +51,24 @@ class AwarenessClient:
|
||||
# be adjusted later without touching the agent-facing tools.
|
||||
return f"{self.base_url}/api/v1/namespaces/{self.namespace}/memories"
|
||||
|
||||
async def commit(self, content: str, scope: str) -> dict[str, Any]:
|
||||
async def commit(self, content: str, scope: str, *, namespace: str | None = None) -> dict[str, Any]:
|
||||
_ns = _normalise_namespace(namespace)
|
||||
client_cls = _resolve_async_client()
|
||||
async with client_cls(timeout=self.timeout) as client:
|
||||
resp = await client.post(
|
||||
self._memories_url(),
|
||||
json={"content": content, "scope": scope},
|
||||
json={"content": content, "scope": scope, "namespace": _ns},
|
||||
)
|
||||
return _parse_commit_response(resp, scope)
|
||||
|
||||
async def search(self, query: str = "", scope: str = "") -> dict[str, Any]:
|
||||
async def search(self, query: str = "", scope: str = "", *, namespace: str | None = None) -> dict[str, Any]:
|
||||
params: dict[str, str] = {}
|
||||
if query:
|
||||
params["q"] = query
|
||||
if scope:
|
||||
params["scope"] = scope
|
||||
if namespace is not None:
|
||||
params["namespace"] = namespace.strip()
|
||||
|
||||
client_cls = _resolve_async_client()
|
||||
async with client_cls(timeout=self.timeout) as client:
|
||||
@ -120,3 +123,17 @@ def _resolve_async_client():
|
||||
return client_cls
|
||||
|
||||
raise RuntimeError("httpx.AsyncClient is unavailable")
|
||||
|
||||
|
||||
def _normalise_namespace(namespace: str | None) -> str:
|
||||
"""Normalise a namespace value to 'general' when None or empty.
|
||||
|
||||
Whitespace is stripped before the empty check so that strings
|
||||
containing only spaces are also treated as 'general'.
|
||||
"""
|
||||
if namespace is None:
|
||||
return "general"
|
||||
stripped = namespace.strip()
|
||||
if not stripped:
|
||||
return "general"
|
||||
return stripped
|
||||
|
||||
@ -32,7 +32,7 @@ from types import SimpleNamespace
|
||||
from typing import Any
|
||||
|
||||
from langchain_core.tools import tool
|
||||
from builtin_tools.awareness_client import build_awareness_client
|
||||
from builtin_tools.awareness_client import _normalise_namespace, build_awareness_client
|
||||
from builtin_tools.audit import check_permission, get_workspace_roles, log_event
|
||||
from builtin_tools.telemetry import MEMORY_QUERY, MEMORY_SCOPE, WORKSPACE_ID_ATTR, get_tracer
|
||||
|
||||
@ -46,12 +46,13 @@ WORKSPACE_ID = os.environ.get("WORKSPACE_ID", "")
|
||||
|
||||
|
||||
@tool
|
||||
async def commit_memory(content: str, scope: str = "LOCAL") -> dict:
|
||||
async def commit_memory(content: str, scope: str = "LOCAL", *, namespace: str | None = None) -> dict:
|
||||
"""Store a fact in memory with a specific scope.
|
||||
|
||||
Args:
|
||||
content: The fact or knowledge to remember.
|
||||
scope: Memory scope — LOCAL (private), TEAM (shared with team), or GLOBAL (company-wide, root only).
|
||||
namespace: Optional namespace bucket (e.g. "facts", "procedures", "blockers"). Defaults to "general".
|
||||
"""
|
||||
trace_id = str(uuid.uuid4())
|
||||
scope = scope.upper()
|
||||
@ -99,7 +100,7 @@ async def commit_memory(content: str, scope: str = "LOCAL") -> dict:
|
||||
awareness_client = build_awareness_client()
|
||||
if awareness_client is not None:
|
||||
try:
|
||||
result = await awareness_client.commit(content, scope)
|
||||
result = await awareness_client.commit(content, scope, namespace=namespace)
|
||||
except Exception as e:
|
||||
log_event(
|
||||
event_type="memory",
|
||||
@ -129,7 +130,7 @@ async def commit_memory(content: str, scope: str = "LOCAL") -> dict:
|
||||
try:
|
||||
resp = await client.post(
|
||||
f"{PLATFORM_URL}/workspaces/{WORKSPACE_ID}/memories",
|
||||
json={"content": content, "scope": scope},
|
||||
json={"content": content, "scope": scope, "namespace": _normalise_namespace(namespace)},
|
||||
headers=_headers,
|
||||
)
|
||||
if resp.status_code == 201:
|
||||
@ -186,12 +187,13 @@ async def commit_memory(content: str, scope: str = "LOCAL") -> dict:
|
||||
|
||||
|
||||
@tool
|
||||
async def search_memory(query: str = "", scope: str = "") -> dict:
|
||||
async def search_memory(query: str = "", scope: str = "", *, namespace: str | None = None) -> dict:
|
||||
"""Search stored memories.
|
||||
|
||||
Args:
|
||||
query: Text to search for (empty returns all).
|
||||
scope: Filter by scope — LOCAL, TEAM, GLOBAL, or empty for all accessible.
|
||||
namespace: Optional namespace bucket to search within. When None, searches all namespaces.
|
||||
"""
|
||||
trace_id = str(uuid.uuid4())
|
||||
scope = scope.upper()
|
||||
@ -239,7 +241,7 @@ async def search_memory(query: str = "", scope: str = "") -> dict:
|
||||
awareness_client = build_awareness_client()
|
||||
if awareness_client is not None:
|
||||
try:
|
||||
result = await awareness_client.search(query, scope)
|
||||
result = await awareness_client.search(query, scope, namespace=namespace)
|
||||
mem_span.set_attribute("memory.result_count", result.get("count", 0))
|
||||
mem_span.set_attribute("memory.success", result.get("success", False))
|
||||
log_event(
|
||||
@ -273,6 +275,8 @@ async def search_memory(query: str = "", scope: str = "") -> dict:
|
||||
params["q"] = query
|
||||
if scope:
|
||||
params["scope"] = scope.upper()
|
||||
if namespace is not None:
|
||||
params["namespace"] = namespace.strip()
|
||||
|
||||
# #215-class bug (search path): same fix as commit_memory above —
|
||||
# the platform gates GET /workspaces/:id/memories behind workspace
|
||||
|
||||
Loading…
Reference in New Issue
Block a user