aip-agents-binary 0.6.3__py3-none-macosx_13_0_arm64.whl → 0.6.5__py3-none-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aip_agents/agent/__init__.py +44 -4
- aip_agents/agent/base_langgraph_agent.py +26 -6
- aip_agents/agent/langgraph_memory_enhancer_agent.py +368 -34
- aip_agents/agent/langgraph_memory_enhancer_agent.pyi +3 -2
- aip_agents/agent/langgraph_react_agent.py +30 -6
- aip_agents/agent/langgraph_react_agent.pyi +1 -1
- aip_agents/mcp/client/__init__.py +38 -2
- aip_agents/mcp/client/transports.py +5 -1
- aip_agents/memory/adapters/base_adapter.py +94 -0
- aip_agents/memory/adapters/base_adapter.pyi +26 -0
- aip_agents/sentry/sentry.py +29 -8
- aip_agents/sentry/sentry.pyi +3 -2
- aip_agents/tools/memory_search/__init__.py +8 -1
- aip_agents/tools/memory_search/__init__.pyi +3 -3
- aip_agents/tools/memory_search/mem0.py +108 -1
- aip_agents/tools/memory_search/mem0.pyi +11 -1
- aip_agents/tools/memory_search/schema.py +33 -0
- aip_agents/tools/memory_search/schema.pyi +10 -0
- aip_agents/tools/memory_search_tool.py +8 -0
- aip_agents/tools/memory_search_tool.pyi +2 -2
- {aip_agents_binary-0.6.3.dist-info → aip_agents_binary-0.6.5.dist-info}/METADATA +4 -16
- {aip_agents_binary-0.6.3.dist-info → aip_agents_binary-0.6.5.dist-info}/RECORD +24 -26
- aip_agents/examples/demo_memory_recall.py +0 -401
- aip_agents/examples/demo_memory_recall.pyi +0 -58
- {aip_agents_binary-0.6.3.dist-info → aip_agents_binary-0.6.5.dist-info}/WHEEL +0 -0
- {aip_agents_binary-0.6.3.dist-info → aip_agents_binary-0.6.5.dist-info}/top_level.txt +0 -0
|
@@ -7,8 +7,44 @@ Authors:
|
|
|
7
7
|
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import TYPE_CHECKING, Any
|
|
13
|
+
|
|
10
14
|
from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from aip_agents.mcp.client.google_adk.client import GoogleADKMCPClient
|
|
18
|
+
from aip_agents.mcp.client.langchain.client import LangchainMCPClient
|
|
13
19
|
|
|
14
20
|
__all__ = ["GoogleADKMCPClient", "LangchainMCPClient", "BaseMCPClient"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def __getattr__(name: str) -> Any:
|
|
24
|
+
"""Lazy import of MCP client implementations.
|
|
25
|
+
|
|
26
|
+
This avoids importing heavy dependencies (Google ADK, Vertex AI, etc.)
|
|
27
|
+
when they are not needed.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
name: Attribute name to import.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The requested class.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
AttributeError: If attribute is not found.
|
|
37
|
+
"""
|
|
38
|
+
if name == "GoogleADKMCPClient":
|
|
39
|
+
from aip_agents.mcp.client.google_adk.client import (
|
|
40
|
+
GoogleADKMCPClient as _GoogleADKMCPClient,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return _GoogleADKMCPClient
|
|
44
|
+
elif name == "LangchainMCPClient":
|
|
45
|
+
from aip_agents.mcp.client.langchain.client import (
|
|
46
|
+
LangchainMCPClient as _LangchainMCPClient,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return _LangchainMCPClient
|
|
50
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
@@ -185,7 +185,11 @@ class HTTPTransport(Transport):
|
|
|
185
185
|
headers = _sanitize_headers(self.config)
|
|
186
186
|
logger.debug(f"Attempting streamable HTTP connection to {url} with headers: {list(headers.keys())}")
|
|
187
187
|
try:
|
|
188
|
-
http_client = httpx.AsyncClient(
|
|
188
|
+
http_client = httpx.AsyncClient(
|
|
189
|
+
timeout=httpx.Timeout(timeout),
|
|
190
|
+
headers=headers,
|
|
191
|
+
follow_redirects=True,
|
|
192
|
+
)
|
|
189
193
|
self._http_client = http_client
|
|
190
194
|
self.ctx = streamable_http_client(url=url, http_client=http_client)
|
|
191
195
|
read_stream, write_stream, _ = await self.ctx.__aenter__()
|
|
@@ -256,6 +256,100 @@ class BaseMemoryAdapter(BaseMemory):
|
|
|
256
256
|
|
|
257
257
|
return [self._chunk_to_hit(chunk) for chunk in chunks]
|
|
258
258
|
|
|
259
|
+
def delete_by_query(
|
|
260
|
+
self,
|
|
261
|
+
*,
|
|
262
|
+
query: str,
|
|
263
|
+
user_id: str,
|
|
264
|
+
metadata: dict[str, Any] | None = None,
|
|
265
|
+
threshold: float | None = None,
|
|
266
|
+
top_k: int | None = None,
|
|
267
|
+
categories: list[str] | None = None,
|
|
268
|
+
) -> Any:
|
|
269
|
+
"""Delete memories by semantic query.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
query: Semantic query describing memories to delete.
|
|
273
|
+
user_id: User identifier for the deletion scope.
|
|
274
|
+
metadata: Optional metadata filters to constrain deletion.
|
|
275
|
+
threshold: Optional semantic threshold (if supported by backend).
|
|
276
|
+
top_k: Optional max number of memories to delete by query.
|
|
277
|
+
categories: Optional categories to filter by (best-effort).
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Backend-specific delete result or None on failure.
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
start = perf_counter()
|
|
284
|
+
result = self._runner.run(
|
|
285
|
+
self._call_manager_with_optional_categories(
|
|
286
|
+
self._manager.delete_by_user_query,
|
|
287
|
+
categories=categories,
|
|
288
|
+
query=query,
|
|
289
|
+
user_id=user_id or self.agent_id,
|
|
290
|
+
agent_id=self.agent_id,
|
|
291
|
+
scopes=DEFAULT_SCOPE,
|
|
292
|
+
metadata=metadata,
|
|
293
|
+
threshold=threshold,
|
|
294
|
+
top_k=top_k,
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
duration = perf_counter() - start
|
|
298
|
+
logger.info(
|
|
299
|
+
"BaseMemoryAdapter: delete_by_query user_id='%s' query='%s' completed in %.2fs",
|
|
300
|
+
user_id,
|
|
301
|
+
query,
|
|
302
|
+
duration,
|
|
303
|
+
)
|
|
304
|
+
return result
|
|
305
|
+
except Exception as exc: # noqa: BLE001
|
|
306
|
+
logger.debug("BaseMemoryAdapter.delete_by_query ignored error: %s", exc)
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
def delete(
|
|
310
|
+
self,
|
|
311
|
+
*,
|
|
312
|
+
memory_ids: list[str] | None,
|
|
313
|
+
user_id: str,
|
|
314
|
+
metadata: dict[str, Any] | None = None,
|
|
315
|
+
categories: list[str] | None = None,
|
|
316
|
+
) -> Any:
|
|
317
|
+
"""Delete memories by IDs or by user scope when IDs are None.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
memory_ids: Optional list of memory IDs to delete.
|
|
321
|
+
user_id: User identifier for the deletion scope.
|
|
322
|
+
metadata: Optional metadata filters to constrain deletion.
|
|
323
|
+
categories: Optional categories to filter by (best-effort).
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Backend-specific delete result or None on failure.
|
|
327
|
+
"""
|
|
328
|
+
try:
|
|
329
|
+
start = perf_counter()
|
|
330
|
+
result = self._runner.run(
|
|
331
|
+
self._call_manager_with_optional_categories(
|
|
332
|
+
self._manager.delete,
|
|
333
|
+
categories=categories,
|
|
334
|
+
memory_ids=memory_ids,
|
|
335
|
+
user_id=user_id or self.agent_id,
|
|
336
|
+
agent_id=self.agent_id,
|
|
337
|
+
scopes=DEFAULT_SCOPE,
|
|
338
|
+
metadata=metadata,
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
duration = perf_counter() - start
|
|
342
|
+
logger.info(
|
|
343
|
+
"BaseMemoryAdapter: delete user_id='%s' memory_ids=%s completed in %.2fs",
|
|
344
|
+
user_id,
|
|
345
|
+
"None" if memory_ids is None else len(memory_ids),
|
|
346
|
+
duration,
|
|
347
|
+
)
|
|
348
|
+
return result
|
|
349
|
+
except Exception as exc: # noqa: BLE001
|
|
350
|
+
logger.debug("BaseMemoryAdapter.delete ignored error: %s", exc)
|
|
351
|
+
return None
|
|
352
|
+
|
|
259
353
|
def save_interaction(self, *, user_text: str, ai_text: str, user_id: str) -> None:
|
|
260
354
|
"""Save a user-AI interaction as memories.
|
|
261
355
|
|
|
@@ -106,6 +106,32 @@ class BaseMemoryAdapter(BaseMemory):
|
|
|
106
106
|
Returns:
|
|
107
107
|
List of memory hits matching the criteria.
|
|
108
108
|
"""
|
|
109
|
+
def delete_by_query(self, *, query: str, user_id: str, metadata: dict[str, Any] | None = None, threshold: float | None = None, top_k: int | None = None, categories: list[str] | None = None) -> Any:
|
|
110
|
+
"""Delete memories by semantic query.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
query: Semantic query describing memories to delete.
|
|
114
|
+
user_id: User identifier for the deletion scope.
|
|
115
|
+
metadata: Optional metadata filters to constrain deletion.
|
|
116
|
+
threshold: Optional semantic threshold (if supported by backend).
|
|
117
|
+
top_k: Optional max number of memories to delete by query.
|
|
118
|
+
categories: Optional categories to filter by (best-effort).
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Backend-specific delete result or None on failure.
|
|
122
|
+
"""
|
|
123
|
+
def delete(self, *, memory_ids: list[str] | None, user_id: str, metadata: dict[str, Any] | None = None, categories: list[str] | None = None) -> Any:
|
|
124
|
+
"""Delete memories by IDs or by user scope when IDs are None.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
memory_ids: Optional list of memory IDs to delete.
|
|
128
|
+
user_id: User identifier for the deletion scope.
|
|
129
|
+
metadata: Optional metadata filters to constrain deletion.
|
|
130
|
+
categories: Optional categories to filter by (best-effort).
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Backend-specific delete result or None on failure.
|
|
134
|
+
"""
|
|
109
135
|
def save_interaction(self, *, user_text: str, ai_text: str, user_id: str) -> None:
|
|
110
136
|
"""Save a user-AI interaction as memories.
|
|
111
137
|
|
aip_agents/sentry/sentry.py
CHANGED
|
@@ -6,6 +6,7 @@ Authors:
|
|
|
6
6
|
|
|
7
7
|
import inspect
|
|
8
8
|
import os
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
10
11
|
from bosa_core.telemetry import (
|
|
11
12
|
FastAPIConfig,
|
|
@@ -20,7 +21,7 @@ from bosa_core.telemetry.opentelemetry.instrument.functions import (
|
|
|
20
21
|
from dotenv import load_dotenv
|
|
21
22
|
from fastapi import FastAPI
|
|
22
23
|
|
|
23
|
-
from aip_agents.agent import BaseAgent,
|
|
24
|
+
from aip_agents.agent import BaseAgent, LangChainAgent, LangGraphAgent
|
|
24
25
|
from aip_agents.utils.logger import get_logger
|
|
25
26
|
|
|
26
27
|
load_dotenv()
|
|
@@ -35,12 +36,32 @@ VERSION_NUMBER = os.getenv("VERSION_NUMBER", "0.0.0")
|
|
|
35
36
|
BUILD_NUMBER = os.getenv("BUILD_NUMBER", "0")
|
|
36
37
|
USE_OPENTELEMETRY = os.getenv("USE_OPENTELEMETRY", "true").lower() == "true"
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
# Lazy import of GoogleADKAgent to avoid heavy dependencies when not needed.
|
|
40
|
+
# This is initialized lazily by _get_classes_to_instrument() and can be
|
|
41
|
+
# patched by tests for mocking purposes.
|
|
42
|
+
CLASSES_TO_INSTRUMENT: list[type[Any]] | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get_classes_to_instrument() -> list[type[Any]]:
|
|
46
|
+
"""Get the list of classes to instrument.
|
|
47
|
+
|
|
48
|
+
This lazily imports GoogleADKAgent only when telemetry is being set up,
|
|
49
|
+
avoiding the heavy Google ADK dependencies during module import.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
List of agent classes to instrument.
|
|
53
|
+
"""
|
|
54
|
+
global CLASSES_TO_INSTRUMENT
|
|
55
|
+
if CLASSES_TO_INSTRUMENT is None:
|
|
56
|
+
from aip_agents.agent import GoogleADKAgent
|
|
57
|
+
|
|
58
|
+
CLASSES_TO_INSTRUMENT = [
|
|
59
|
+
BaseAgent,
|
|
60
|
+
LangGraphAgent,
|
|
61
|
+
LangChainAgent,
|
|
62
|
+
GoogleADKAgent,
|
|
63
|
+
]
|
|
64
|
+
return CLASSES_TO_INSTRUMENT
|
|
44
65
|
|
|
45
66
|
|
|
46
67
|
def get_all_methods(cls: type) -> list:
|
|
@@ -66,7 +87,7 @@ def instrument_gl_functions() -> None:
|
|
|
66
87
|
if BOSAFunctionsInstrumentor is None:
|
|
67
88
|
return
|
|
68
89
|
agent_methods = []
|
|
69
|
-
for cls in
|
|
90
|
+
for cls in _get_classes_to_instrument():
|
|
70
91
|
agent_methods.extend(get_all_methods(cls))
|
|
71
92
|
BOSAFunctionsInstrumentor().instrument(methods=agent_methods)
|
|
72
93
|
|
aip_agents/sentry/sentry.pyi
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from _typeshed import Incomplete
|
|
2
|
-
from aip_agents.agent import BaseAgent as BaseAgent,
|
|
2
|
+
from aip_agents.agent import BaseAgent as BaseAgent, LangChainAgent as LangChainAgent, LangGraphAgent as LangGraphAgent
|
|
3
3
|
from aip_agents.utils.logger import get_logger as get_logger
|
|
4
4
|
from fastapi import FastAPI
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
logger: Incomplete
|
|
7
8
|
SENTRY_DSN: Incomplete
|
|
@@ -10,7 +11,7 @@ SENTRY_PROJECT: Incomplete
|
|
|
10
11
|
VERSION_NUMBER: Incomplete
|
|
11
12
|
BUILD_NUMBER: Incomplete
|
|
12
13
|
USE_OPENTELEMETRY: Incomplete
|
|
13
|
-
CLASSES_TO_INSTRUMENT:
|
|
14
|
+
CLASSES_TO_INSTRUMENT: list[type[Any]] | None
|
|
14
15
|
|
|
15
16
|
def get_all_methods(cls) -> list:
|
|
16
17
|
"""Get all methods from a class.
|
|
@@ -6,17 +6,24 @@ Authors:
|
|
|
6
6
|
|
|
7
7
|
from aip_agents.tools.memory_search.base import LongTermMemorySearchTool
|
|
8
8
|
from aip_agents.tools.memory_search.mem0 import (
|
|
9
|
+
MEMORY_DELETE_TOOL_NAME,
|
|
9
10
|
MEMORY_SEARCH_TOOL_NAME,
|
|
11
|
+
Mem0DeleteInput,
|
|
12
|
+
Mem0DeleteTool,
|
|
10
13
|
Mem0SearchInput,
|
|
11
14
|
Mem0SearchTool,
|
|
12
15
|
)
|
|
13
|
-
from aip_agents.tools.memory_search.schema import LongTermMemorySearchInput, MemoryConfig
|
|
16
|
+
from aip_agents.tools.memory_search.schema import LongTermMemoryDeleteInput, LongTermMemorySearchInput, MemoryConfig
|
|
14
17
|
|
|
15
18
|
__all__ = [
|
|
16
19
|
"MemoryConfig",
|
|
20
|
+
"LongTermMemoryDeleteInput",
|
|
17
21
|
"LongTermMemorySearchInput",
|
|
18
22
|
"LongTermMemorySearchTool",
|
|
23
|
+
"Mem0DeleteInput",
|
|
24
|
+
"Mem0DeleteTool",
|
|
19
25
|
"Mem0SearchInput",
|
|
20
26
|
"Mem0SearchTool",
|
|
27
|
+
"MEMORY_DELETE_TOOL_NAME",
|
|
21
28
|
"MEMORY_SEARCH_TOOL_NAME",
|
|
22
29
|
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from aip_agents.tools.memory_search.base import LongTermMemorySearchTool as LongTermMemorySearchTool
|
|
2
|
-
from aip_agents.tools.memory_search.mem0 import MEMORY_SEARCH_TOOL_NAME as MEMORY_SEARCH_TOOL_NAME, Mem0SearchInput as Mem0SearchInput, Mem0SearchTool as Mem0SearchTool
|
|
3
|
-
from aip_agents.tools.memory_search.schema import LongTermMemorySearchInput as LongTermMemorySearchInput, MemoryConfig as MemoryConfig
|
|
2
|
+
from aip_agents.tools.memory_search.mem0 import MEMORY_DELETE_TOOL_NAME as MEMORY_DELETE_TOOL_NAME, MEMORY_SEARCH_TOOL_NAME as MEMORY_SEARCH_TOOL_NAME, Mem0DeleteInput as Mem0DeleteInput, Mem0DeleteTool as Mem0DeleteTool, Mem0SearchInput as Mem0SearchInput, Mem0SearchTool as Mem0SearchTool
|
|
3
|
+
from aip_agents.tools.memory_search.schema import LongTermMemoryDeleteInput as LongTermMemoryDeleteInput, LongTermMemorySearchInput as LongTermMemorySearchInput, MemoryConfig as MemoryConfig
|
|
4
4
|
|
|
5
|
-
__all__ = ['MemoryConfig', 'LongTermMemorySearchInput', 'LongTermMemorySearchTool', 'Mem0SearchInput', 'Mem0SearchTool', 'MEMORY_SEARCH_TOOL_NAME']
|
|
5
|
+
__all__ = ['MemoryConfig', 'LongTermMemoryDeleteInput', 'LongTermMemorySearchInput', 'LongTermMemorySearchTool', 'Mem0DeleteInput', 'Mem0DeleteTool', 'Mem0SearchInput', 'Mem0SearchTool', 'MEMORY_DELETE_TOOL_NAME', 'MEMORY_SEARCH_TOOL_NAME']
|
|
@@ -13,13 +13,14 @@ from langchain_core.runnables import RunnableConfig
|
|
|
13
13
|
|
|
14
14
|
from aip_agents.memory.constants import MemoryDefaults
|
|
15
15
|
from aip_agents.tools.memory_search.base import LongTermMemorySearchTool
|
|
16
|
-
from aip_agents.tools.memory_search.schema import LongTermMemorySearchInput
|
|
16
|
+
from aip_agents.tools.memory_search.schema import LongTermMemoryDeleteInput, LongTermMemorySearchInput
|
|
17
17
|
from aip_agents.utils.datetime import is_valid_date_string, next_day_iso
|
|
18
18
|
from aip_agents.utils.logger import get_logger
|
|
19
19
|
|
|
20
20
|
logger = get_logger(__name__)
|
|
21
21
|
|
|
22
22
|
MEMORY_SEARCH_TOOL_NAME = "built_in_mem0_search"
|
|
23
|
+
MEMORY_DELETE_TOOL_NAME = "built_in_mem0_delete"
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class Mem0SearchTool(LongTermMemorySearchTool):
|
|
@@ -256,3 +257,109 @@ class Mem0SearchTool(LongTermMemorySearchTool):
|
|
|
256
257
|
|
|
257
258
|
|
|
258
259
|
Mem0SearchInput = LongTermMemorySearchInput
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class Mem0DeleteTool(LongTermMemorySearchTool):
|
|
263
|
+
"""Mem0-specific implementation of the long-term memory delete tool."""
|
|
264
|
+
|
|
265
|
+
name: str = MEMORY_DELETE_TOOL_NAME
|
|
266
|
+
description: str = (
|
|
267
|
+
"Delete memories from long-term mem0 storage. Supports three modes:\n"
|
|
268
|
+
"1. DELETE BY IDS: Provide 'memory_ids'\n"
|
|
269
|
+
"2. DELETE BY QUERY: Provide 'query'\n"
|
|
270
|
+
"3. DELETE ALL: Provide 'delete_all=true' with no query/IDs\n"
|
|
271
|
+
)
|
|
272
|
+
args_schema: type[LongTermMemoryDeleteInput] = LongTermMemoryDeleteInput
|
|
273
|
+
LOG_PREFIX: ClassVar[str] = "Mem0DeleteTool"
|
|
274
|
+
METADATA_FILTER_BLOCKLIST: ClassVar[set[str]] = {"user_id", "memory_user_id"}
|
|
275
|
+
|
|
276
|
+
async def _arun(
|
|
277
|
+
self,
|
|
278
|
+
query: str | None = None,
|
|
279
|
+
config: RunnableConfig | None = None,
|
|
280
|
+
run_manager: Any | None = None,
|
|
281
|
+
**kwargs: Any,
|
|
282
|
+
) -> str:
|
|
283
|
+
"""Execute the memory delete asynchronously for LangChain.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
query: Semantic delete query when provided.
|
|
287
|
+
config: Runnable configuration containing LangChain metadata.
|
|
288
|
+
run_manager: LangChain callbacks (unused).
|
|
289
|
+
**kwargs: Additional arguments such as ``memory_ids``, ``delete_all``, ``metadata``.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
str: JSON-encoded delete result or an error message.
|
|
293
|
+
"""
|
|
294
|
+
logger.info("%s: Received config: %s", self.LOG_PREFIX, config)
|
|
295
|
+
|
|
296
|
+
memory_ids: list[str] | None = kwargs.get("memory_ids")
|
|
297
|
+
delete_all: bool | None = kwargs.get("delete_all")
|
|
298
|
+
threshold: float | None = kwargs.get("threshold")
|
|
299
|
+
top_k: int | None = kwargs.get("top_k")
|
|
300
|
+
categories: list[str] | None = kwargs.get("categories")
|
|
301
|
+
metadata: dict[str, Any] | None = kwargs.get("metadata")
|
|
302
|
+
|
|
303
|
+
user_id = self._resolve_user_id(metadata=metadata, config=config)
|
|
304
|
+
|
|
305
|
+
metadata_filter = None
|
|
306
|
+
if isinstance(metadata, dict):
|
|
307
|
+
metadata_filter = {k: v for k, v in metadata.items() if k not in self.METADATA_FILTER_BLOCKLIST} or None
|
|
308
|
+
|
|
309
|
+
if memory_ids:
|
|
310
|
+
if not hasattr(self.memory, "delete"):
|
|
311
|
+
return f"Error executing memory tool '{self.name}': backend does not support delete()"
|
|
312
|
+
mode = "ids"
|
|
313
|
+
result = self.memory.delete( # type: ignore[attr-defined]
|
|
314
|
+
memory_ids=memory_ids,
|
|
315
|
+
user_id=user_id,
|
|
316
|
+
metadata=metadata_filter,
|
|
317
|
+
categories=categories,
|
|
318
|
+
)
|
|
319
|
+
elif query:
|
|
320
|
+
if not hasattr(self.memory, "delete_by_query"):
|
|
321
|
+
return f"Error executing memory tool '{self.name}': backend does not support delete_by_query()"
|
|
322
|
+
mode = "query"
|
|
323
|
+
result = self.memory.delete_by_query( # type: ignore[attr-defined]
|
|
324
|
+
query=query,
|
|
325
|
+
user_id=user_id,
|
|
326
|
+
metadata=metadata_filter,
|
|
327
|
+
threshold=threshold,
|
|
328
|
+
top_k=top_k,
|
|
329
|
+
categories=categories,
|
|
330
|
+
)
|
|
331
|
+
elif delete_all:
|
|
332
|
+
if not hasattr(self.memory, "delete"):
|
|
333
|
+
return f"Error executing memory tool '{self.name}': backend does not support delete()"
|
|
334
|
+
mode = "all"
|
|
335
|
+
result = self.memory.delete( # type: ignore[attr-defined]
|
|
336
|
+
memory_ids=None,
|
|
337
|
+
user_id=user_id,
|
|
338
|
+
metadata=metadata_filter,
|
|
339
|
+
categories=categories,
|
|
340
|
+
)
|
|
341
|
+
else:
|
|
342
|
+
return f"Error executing memory tool '{self.name}': provide memory_ids, query, or delete_all=true."
|
|
343
|
+
|
|
344
|
+
count = None
|
|
345
|
+
if isinstance(result, dict):
|
|
346
|
+
count = result.get("count") or result.get("deleted") or result.get("total")
|
|
347
|
+
|
|
348
|
+
logger.info(
|
|
349
|
+
"%s: delete mode=%s user_id='%s' count=%s",
|
|
350
|
+
self.LOG_PREFIX,
|
|
351
|
+
mode,
|
|
352
|
+
user_id,
|
|
353
|
+
count if count is not None else "unknown",
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
payload = {"status": "success", "mode": mode}
|
|
357
|
+
try:
|
|
358
|
+
json.dumps(result)
|
|
359
|
+
payload["result"] = result
|
|
360
|
+
except TypeError:
|
|
361
|
+
payload["result"] = str(result)
|
|
362
|
+
return json.dumps(payload)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
Mem0DeleteInput = LongTermMemoryDeleteInput
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
from _typeshed import Incomplete
|
|
2
2
|
from aip_agents.memory.constants import MemoryDefaults as MemoryDefaults
|
|
3
3
|
from aip_agents.tools.memory_search.base import LongTermMemorySearchTool as LongTermMemorySearchTool
|
|
4
|
-
from aip_agents.tools.memory_search.schema import LongTermMemorySearchInput as LongTermMemorySearchInput
|
|
4
|
+
from aip_agents.tools.memory_search.schema import LongTermMemoryDeleteInput as LongTermMemoryDeleteInput, LongTermMemorySearchInput as LongTermMemorySearchInput
|
|
5
5
|
from aip_agents.utils.datetime import is_valid_date_string as is_valid_date_string, next_day_iso as next_day_iso
|
|
6
6
|
from aip_agents.utils.logger import get_logger as get_logger
|
|
7
7
|
from typing import ClassVar
|
|
8
8
|
|
|
9
9
|
logger: Incomplete
|
|
10
10
|
MEMORY_SEARCH_TOOL_NAME: str
|
|
11
|
+
MEMORY_DELETE_TOOL_NAME: str
|
|
11
12
|
|
|
12
13
|
class Mem0SearchTool(LongTermMemorySearchTool):
|
|
13
14
|
"""Mem0-specific implementation of the long-term memory search tool."""
|
|
@@ -17,3 +18,12 @@ class Mem0SearchTool(LongTermMemorySearchTool):
|
|
|
17
18
|
LOG_PREFIX: ClassVar[str]
|
|
18
19
|
METADATA_FILTER_BLOCKLIST: ClassVar[set[str]]
|
|
19
20
|
Mem0SearchInput = LongTermMemorySearchInput
|
|
21
|
+
|
|
22
|
+
class Mem0DeleteTool(LongTermMemorySearchTool):
|
|
23
|
+
"""Mem0-specific implementation of the long-term memory delete tool."""
|
|
24
|
+
name: str
|
|
25
|
+
description: str
|
|
26
|
+
args_schema: type[LongTermMemoryDeleteInput]
|
|
27
|
+
LOG_PREFIX: ClassVar[str]
|
|
28
|
+
METADATA_FILTER_BLOCKLIST: ClassVar[set[str]]
|
|
29
|
+
Mem0DeleteInput = LongTermMemoryDeleteInput
|
|
@@ -46,3 +46,36 @@ class LongTermMemorySearchInput(BaseModel):
|
|
|
46
46
|
None,
|
|
47
47
|
description="Optional metadata dict to filter by (exact key-value match).",
|
|
48
48
|
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class LongTermMemoryDeleteInput(BaseModel):
|
|
52
|
+
"""Input schema for unified long-term memory deletion."""
|
|
53
|
+
|
|
54
|
+
query: str | None = Field(
|
|
55
|
+
None,
|
|
56
|
+
description="Semantic query describing memories to delete. If provided, delete_by_user_query is used.",
|
|
57
|
+
)
|
|
58
|
+
memory_ids: list[str] | None = Field(
|
|
59
|
+
None,
|
|
60
|
+
description="Optional list of memory IDs to delete directly.",
|
|
61
|
+
)
|
|
62
|
+
delete_all: bool | None = Field(
|
|
63
|
+
None,
|
|
64
|
+
description="When True and no query/IDs are provided, delete all memories for the user scope.",
|
|
65
|
+
)
|
|
66
|
+
top_k: int | None = Field(
|
|
67
|
+
None,
|
|
68
|
+
description="Optional maximum number of memories to delete by query.",
|
|
69
|
+
)
|
|
70
|
+
threshold: float | None = Field(
|
|
71
|
+
None,
|
|
72
|
+
description="Optional semantic threshold for delete_by_user_query.",
|
|
73
|
+
)
|
|
74
|
+
categories: list[str] | None = Field(
|
|
75
|
+
None,
|
|
76
|
+
description="Optional categories to filter by (uses 'in' operator).",
|
|
77
|
+
)
|
|
78
|
+
metadata: dict[str, Any] | None = Field(
|
|
79
|
+
None,
|
|
80
|
+
description="Optional metadata dict to filter by (exact key-value match).",
|
|
81
|
+
)
|
|
@@ -13,3 +13,13 @@ class LongTermMemorySearchInput(BaseModel):
|
|
|
13
13
|
limit: int | None
|
|
14
14
|
categories: list[str] | None
|
|
15
15
|
metadata: dict[str, Any] | None
|
|
16
|
+
|
|
17
|
+
class LongTermMemoryDeleteInput(BaseModel):
|
|
18
|
+
"""Input schema for unified long-term memory deletion."""
|
|
19
|
+
query: str | None
|
|
20
|
+
memory_ids: list[str] | None
|
|
21
|
+
delete_all: bool | None
|
|
22
|
+
top_k: int | None
|
|
23
|
+
threshold: float | None
|
|
24
|
+
categories: list[str] | None
|
|
25
|
+
metadata: dict[str, Any] | None
|
|
@@ -8,9 +8,13 @@ Authors:
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from aip_agents.tools.memory_search import (
|
|
11
|
+
MEMORY_DELETE_TOOL_NAME,
|
|
11
12
|
MEMORY_SEARCH_TOOL_NAME,
|
|
13
|
+
LongTermMemoryDeleteInput,
|
|
12
14
|
LongTermMemorySearchInput,
|
|
13
15
|
LongTermMemorySearchTool,
|
|
16
|
+
Mem0DeleteInput,
|
|
17
|
+
Mem0DeleteTool,
|
|
14
18
|
Mem0SearchInput,
|
|
15
19
|
Mem0SearchTool,
|
|
16
20
|
MemoryConfig,
|
|
@@ -18,9 +22,13 @@ from aip_agents.tools.memory_search import (
|
|
|
18
22
|
|
|
19
23
|
__all__ = [
|
|
20
24
|
"MemoryConfig",
|
|
25
|
+
"LongTermMemoryDeleteInput",
|
|
21
26
|
"LongTermMemorySearchInput",
|
|
22
27
|
"LongTermMemorySearchTool",
|
|
28
|
+
"Mem0DeleteInput",
|
|
29
|
+
"Mem0DeleteTool",
|
|
23
30
|
"Mem0SearchInput",
|
|
24
31
|
"Mem0SearchTool",
|
|
32
|
+
"MEMORY_DELETE_TOOL_NAME",
|
|
25
33
|
"MEMORY_SEARCH_TOOL_NAME",
|
|
26
34
|
]
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from aip_agents.tools.memory_search import LongTermMemorySearchInput as LongTermMemorySearchInput, LongTermMemorySearchTool as LongTermMemorySearchTool, MEMORY_SEARCH_TOOL_NAME as MEMORY_SEARCH_TOOL_NAME, Mem0SearchInput as Mem0SearchInput, Mem0SearchTool as Mem0SearchTool, MemoryConfig as MemoryConfig
|
|
1
|
+
from aip_agents.tools.memory_search import LongTermMemoryDeleteInput as LongTermMemoryDeleteInput, LongTermMemorySearchInput as LongTermMemorySearchInput, LongTermMemorySearchTool as LongTermMemorySearchTool, MEMORY_DELETE_TOOL_NAME as MEMORY_DELETE_TOOL_NAME, MEMORY_SEARCH_TOOL_NAME as MEMORY_SEARCH_TOOL_NAME, Mem0DeleteInput as Mem0DeleteInput, Mem0DeleteTool as Mem0DeleteTool, Mem0SearchInput as Mem0SearchInput, Mem0SearchTool as Mem0SearchTool, MemoryConfig as MemoryConfig
|
|
2
2
|
|
|
3
|
-
__all__ = ['MemoryConfig', 'LongTermMemorySearchInput', 'LongTermMemorySearchTool', 'Mem0SearchInput', 'Mem0SearchTool', 'MEMORY_SEARCH_TOOL_NAME']
|
|
3
|
+
__all__ = ['MemoryConfig', 'LongTermMemoryDeleteInput', 'LongTermMemorySearchInput', 'LongTermMemorySearchTool', 'Mem0DeleteInput', 'Mem0DeleteTool', 'Mem0SearchInput', 'Mem0SearchTool', 'MEMORY_DELETE_TOOL_NAME', 'MEMORY_SEARCH_TOOL_NAME']
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aip-agents-binary
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: A library for managing agents in Gen AI applications.
|
|
5
5
|
Author-email: Raymond Christopher <raymond.christopher@gdplabs.id>
|
|
6
6
|
Requires-Python: <3.13,>=3.11
|
|
@@ -476,26 +476,14 @@ The library supports Mem0 as a memory backend for long-term conversation recall.
|
|
|
476
476
|
- Date range parsing for natural language time filters using `dateparser`.
|
|
477
477
|
- Conditional auto-augmentation (disabled by default to reduce noise; enable with `memory_auto_augment=True`).
|
|
478
478
|
|
|
479
|
-
#### Mem0
|
|
479
|
+
#### Mem0 Integration Tests
|
|
480
480
|
|
|
481
|
-
Use the
|
|
481
|
+
Use the Mem0 integration tests to validate memory persistence, recall, and deletion:
|
|
482
482
|
|
|
483
483
|
```bash
|
|
484
|
-
poetry run
|
|
484
|
+
cd python/aip-agents && poetry run pytest tests/integration_tests/test_mem0_coordinator.py -q
|
|
485
485
|
```
|
|
486
486
|
|
|
487
|
-
In client:
|
|
488
|
-
```python
|
|
489
|
-
agent = LangGraphAgent(
|
|
490
|
-
name="client",
|
|
491
|
-
instruction="...",
|
|
492
|
-
model="gpt-4o-mini",
|
|
493
|
-
memory_backend="mem0",
|
|
494
|
-
)
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
Test recall: After some interactions, query "What did we discuss yesterday?" – agent uses tool to filter by created_at.
|
|
498
|
-
|
|
499
487
|
## Deep Agents Middleware
|
|
500
488
|
|
|
501
489
|
The Deep Agents Middleware system provides composable components for enhancing agent capabilities with planning, context management, and custom lifecycle hooks.
|