refactor: update MemoryClient to use entity options instead of filters

This commit is contained in:
kartik-mem0
2026-04-14 17:13:52 +05:30
parent 28cc8fcee4
commit c3f48d093e
21 changed files with 201 additions and 249 deletions
+9 -32
View File
@@ -142,7 +142,8 @@ class MemoryClient:
or a string. If a string is provided, it will be converted to
a user message.
options: Typed options for the add operation (AddMemoryOptions).
**kwargs: Additional parameters such as metadata, filters.
**kwargs: Additional parameters such as user_id, agent_id, app_id,
metadata, filters.
Returns:
A dictionary containing the API response in v1.1 format.
@@ -165,11 +166,7 @@ class MemoryClient:
raise ValueError(f"messages must be str, dict, or list[dict], got {type(messages).__name__}")
kwargs = self._prepare_params(kwargs)
# Extract filters and spread entity IDs into payload (API expects top-level entity IDs)
filters = kwargs.pop("filters", None)
payload = self._prepare_payload(messages, kwargs)
if filters:
payload.update(filters)
response = self.client.post("/v3/memories/", json=payload)
response.raise_for_status()
if "metadata" in kwargs:
@@ -370,7 +367,8 @@ class MemoryClient:
Args:
options: Typed options for the delete_all operation (DeleteAllMemoryOptions).
**kwargs: Optional parameters for filtering (filters).
**kwargs: Optional parameters for filtering (user_id, agent_id,
app_id).
Returns:
A dictionary containing the API response.
@@ -385,16 +383,7 @@ class MemoryClient:
"""
kwargs = {**(options.model_dump(exclude_unset=True) if options else {}), **kwargs}
params = self._prepare_params(kwargs)
# Extract filters and pass as query params (snake_case keys)
query_params = {}
if "filters" in params:
filters = params.pop("filters")
if isinstance(filters, dict):
query_params.update(filters)
query_params.update(params)
response = self.client.delete("/v1/memories/", params=query_params)
response = self.client.delete("/v1/memories/", params=params)
response.raise_for_status()
capture_client_event(
"client.delete_all",
@@ -1081,7 +1070,8 @@ class AsyncMemoryClient:
or a string. If a string is provided, it will be converted to
a user message.
options: Typed options for the add operation (AddMemoryOptions).
**kwargs: Additional parameters such as metadata, filters.
**kwargs: Additional parameters such as user_id, agent_id, app_id,
metadata, filters.
Returns:
A dictionary containing the API response in v1.1 format.
@@ -1104,11 +1094,7 @@ class AsyncMemoryClient:
raise ValueError(f"messages must be str, dict, or list[dict], got {type(messages).__name__}")
kwargs = self._prepare_params(kwargs)
# Extract filters and spread entity IDs into payload (API expects top-level entity IDs)
filters = kwargs.pop("filters", None)
payload = self._prepare_payload(messages, kwargs)
if filters:
payload.update(filters)
response = await self.async_client.post("/v3/memories/", json=payload)
response.raise_for_status()
if "metadata" in kwargs:
@@ -1293,7 +1279,7 @@ class AsyncMemoryClient:
Args:
options: Typed options for the delete_all operation (DeleteAllMemoryOptions).
**kwargs: Optional parameters for filtering (filters).
**kwargs: Optional parameters for filtering (user_id, agent_id, app_id).
Returns:
A dictionary containing the API response.
@@ -1308,16 +1294,7 @@ class AsyncMemoryClient:
"""
kwargs = {**(options.model_dump(exclude_unset=True) if options else {}), **kwargs}
params = self._prepare_params(kwargs)
# Extract filters and pass as query params (snake_case keys)
query_params = {}
if "filters" in params:
filters = params.pop("filters")
if isinstance(filters, dict):
query_params.update(filters)
query_params.update(params)
response = await self.async_client.delete("/v1/memories/", params=query_params)
response = await self.async_client.delete("/v1/memories/", params=params)
response.raise_for_status()
capture_client_event("client.delete_all", self, {"keys": list(kwargs.keys()), "sync_type": "async"})
return response.json()
+45 -61
View File
@@ -425,27 +425,26 @@ class Memory(MemoryBase):
self,
messages,
*,
filters: Optional[Dict[str, Any]] = None,
user_id: Optional[str] = None,
agent_id: Optional[str] = None,
run_id: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
infer: bool = True,
memory_type: Optional[str] = None,
prompt: Optional[str] = None,
**kwargs,
):
"""
Create a new memory.
Adds new memories scoped to a single session id (e.g. `user_id`, `agent_id`, or `run_id`). One of those ids is required in filters.
Adds new memories scoped to a single session id (e.g. `user_id`, `agent_id`, or `run_id`). One of those ids is required.
Args:
messages (str or List[Dict[str, str]]): The message content or list of messages
(e.g., `[{"role": "user", "content": "Hello"}, {"role": "assistant", "content": "Hi"}]`)
to be processed and stored.
filters (dict, optional): Dictionary containing entity identifiers. Must contain at least one of:
- user_id: ID of the user creating the memory
- agent_id: ID of the agent creating the memory
- run_id: ID of the run creating the memory
Example: `{"user_id": "user123"}` or `{"agent_id": "agent456"}`
user_id (str, optional): ID of the user creating the memory. Defaults to None.
agent_id (str, optional): ID of the agent creating the memory. Defaults to None.
run_id (str, optional): ID of the run creating the memory. Defaults to None.
metadata (dict, optional): Metadata to store with the memory. Defaults to None.
infer (bool, optional): If True (default), an LLM is used to extract key facts from
'messages' and decide whether to add, update, or delete related memories.
@@ -469,19 +468,11 @@ class Memory(MemoryBase):
LLMError: If LLM operations fail.
DatabaseError: If database operations fail.
"""
filters = filters or {}
# Validate filters contains at least one entity ID
if not any(key in filters for key in ("user_id", "agent_id", "run_id")):
raise ValueError(
"filters must contain at least one of: user_id, agent_id, run_id. "
"Example: filters={'user_id': 'u1'}"
)
processed_metadata, effective_filters = _build_filters_and_metadata(
user_id=filters.get("user_id"),
agent_id=filters.get("agent_id"),
run_id=filters.get("run_id"),
user_id=user_id,
agent_id=agent_id,
run_id=run_id,
input_metadata=metadata,
)
@@ -507,7 +498,7 @@ class Memory(MemoryBase):
suggestion="Convert your input to a string, dictionary, or list of dictionaries."
)
if filters.get("agent_id") is not None and memory_type == MemoryType.PROCEDURAL.value:
if agent_id is not None and memory_type == MemoryType.PROCEDURAL.value:
results = self._create_procedural_memory(messages, metadata=processed_metadata, prompt=prompt)
return results
@@ -1360,23 +1351,26 @@ class Memory(MemoryBase):
self._delete_memory(memory_id, existing_memory)
return {"message": "Memory deleted successfully!"}
def delete_all(self, *, filters: Optional[Dict[str, Any]] = None, **kwargs):
def delete_all(self, user_id: Optional[str] = None, agent_id: Optional[str] = None, run_id: Optional[str] = None):
"""
Delete all memories.
Args:
filters (dict, optional): Dictionary containing entity identifiers. Must contain at least one of:
- user_id: ID of the user to delete memories for
- agent_id: ID of the agent to delete memories for
- run_id: ID of the run to delete memories for
Example: `{"user_id": "user123"}`
user_id (str, optional): ID of the user to delete memories for. Defaults to None.
agent_id (str, optional): ID of the agent to delete memories for. Defaults to None.
run_id (str, optional): ID of the run to delete memories for. Defaults to None.
"""
filters = filters or {}
filters: Dict[str, Any] = {}
if user_id:
filters["user_id"] = user_id
if agent_id:
filters["agent_id"] = agent_id
if run_id:
filters["run_id"] = run_id
if not any(key in filters for key in ("user_id", "agent_id", "run_id")):
if not filters:
raise ValueError(
"filters must contain at least one of: user_id, agent_id, run_id. "
"If you want to delete all memories, use the `reset()` method."
"At least one filter is required to delete all memories. If you want to delete all memories, use the `reset()` method."
)
keys, encoded_ids = process_telemetry_filters(filters)
@@ -1694,24 +1688,23 @@ class AsyncMemory(MemoryBase):
self,
messages,
*,
filters: Optional[Dict[str, Any]] = None,
user_id: Optional[str] = None,
agent_id: Optional[str] = None,
run_id: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
infer: bool = True,
memory_type: Optional[str] = None,
prompt: Optional[str] = None,
llm=None,
**kwargs,
):
"""
Create a new memory asynchronously.
Args:
messages (str or List[Dict[str, str]]): Messages to store in the memory.
filters (dict, optional): Dictionary containing entity identifiers. Must contain at least one of:
- user_id: ID of the user creating the memory
- agent_id: ID of the agent creating the memory
- run_id: ID of the run creating the memory
Example: `{"user_id": "user123"}` or `{"agent_id": "agent456"}`
user_id (str, optional): ID of the user creating the memory.
agent_id (str, optional): ID of the agent creating the memory. Defaults to None.
run_id (str, optional): ID of the run creating the memory. Defaults to None.
metadata (dict, optional): Metadata to store with the memory. Defaults to None.
infer (bool, optional): Whether to infer the memories. Defaults to True.
memory_type (str, optional): Type of memory to create. Defaults to None.
@@ -1721,20 +1714,8 @@ class AsyncMemory(MemoryBase):
Returns:
dict: A dictionary containing the result of the memory addition operation.
"""
filters = filters or {}
# Validate filters contains at least one entity ID
if not any(key in filters for key in ("user_id", "agent_id", "run_id")):
raise ValueError(
"filters must contain at least one of: user_id, agent_id, run_id. "
"Example: filters={'user_id': 'u1'}"
)
processed_metadata, effective_filters = _build_filters_and_metadata(
user_id=filters.get("user_id"),
agent_id=filters.get("agent_id"),
run_id=filters.get("run_id"),
input_metadata=metadata,
user_id=user_id, agent_id=agent_id, run_id=run_id, input_metadata=metadata
)
if memory_type is not None and memory_type != MemoryType.PROCEDURAL.value:
@@ -1756,7 +1737,7 @@ class AsyncMemory(MemoryBase):
suggestion="Convert your input to a string, dictionary, or list of dictionaries."
)
if filters.get("agent_id") is not None and memory_type == MemoryType.PROCEDURAL.value:
if agent_id is not None and memory_type == MemoryType.PROCEDURAL.value:
results = await self._create_procedural_memory(
messages, metadata=processed_metadata, prompt=prompt, llm=llm
)
@@ -2606,23 +2587,26 @@ class AsyncMemory(MemoryBase):
await self._delete_memory(memory_id, existing_memory)
return {"message": "Memory deleted successfully!"}
async def delete_all(self, *, filters: Optional[Dict[str, Any]] = None, **kwargs):
async def delete_all(self, user_id=None, agent_id=None, run_id=None):
"""
Delete all memories asynchronously.
Args:
filters (dict, optional): Dictionary containing entity identifiers. Must contain at least one of:
- user_id: ID of the user to delete memories for
- agent_id: ID of the agent to delete memories for
- run_id: ID of the run to delete memories for
Example: `{"user_id": "user123"}`
user_id (str, optional): ID of the user to delete memories for. Defaults to None.
agent_id (str, optional): ID of the agent to delete memories for. Defaults to None.
run_id (str, optional): ID of the run to delete memories for. Defaults to None.
"""
filters = filters or {}
filters = {}
if user_id:
filters["user_id"] = user_id
if agent_id:
filters["agent_id"] = agent_id
if run_id:
filters["run_id"] = run_id
if not any(key in filters for key in ("user_id", "agent_id", "run_id")):
if not filters:
raise ValueError(
"filters must contain at least one of: user_id, agent_id, run_id. "
"If you want to delete all memories, use the `reset()` method."
"At least one filter is required to delete all memories. If you want to delete all memories, use the `reset()` method."
)
keys, encoded_ids = process_telemetry_filters(filters)