aip-agents-binary 0.5.21__py3-none-macosx_13_0_arm64.whl → 0.6.8__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.
Files changed (149) hide show
  1. aip_agents/agent/__init__.py +44 -4
  2. aip_agents/agent/base_langgraph_agent.py +169 -74
  3. aip_agents/agent/base_langgraph_agent.pyi +3 -2
  4. aip_agents/agent/langgraph_memory_enhancer_agent.py +368 -34
  5. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +3 -2
  6. aip_agents/agent/langgraph_react_agent.py +424 -35
  7. aip_agents/agent/langgraph_react_agent.pyi +46 -2
  8. aip_agents/examples/{hello_world_langgraph_bosa_twitter.py → hello_world_langgraph_gl_connector_twitter.py} +10 -7
  9. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +5 -0
  10. aip_agents/examples/hello_world_ptc.py +49 -0
  11. aip_agents/examples/hello_world_ptc.pyi +5 -0
  12. aip_agents/examples/hello_world_ptc_custom_tools.py +83 -0
  13. aip_agents/examples/hello_world_ptc_custom_tools.pyi +7 -0
  14. aip_agents/examples/hello_world_sentry.py +2 -2
  15. aip_agents/examples/hello_world_tool_output_client.py +9 -0
  16. aip_agents/examples/tools/multiply_tool.py +43 -0
  17. aip_agents/examples/tools/multiply_tool.pyi +18 -0
  18. aip_agents/guardrails/__init__.py +83 -0
  19. aip_agents/guardrails/__init__.pyi +6 -0
  20. aip_agents/guardrails/engines/__init__.py +69 -0
  21. aip_agents/guardrails/engines/__init__.pyi +4 -0
  22. aip_agents/guardrails/engines/base.py +90 -0
  23. aip_agents/guardrails/engines/base.pyi +61 -0
  24. aip_agents/guardrails/engines/nemo.py +101 -0
  25. aip_agents/guardrails/engines/nemo.pyi +46 -0
  26. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  27. aip_agents/guardrails/engines/phrase_matcher.pyi +48 -0
  28. aip_agents/guardrails/exceptions.py +39 -0
  29. aip_agents/guardrails/exceptions.pyi +23 -0
  30. aip_agents/guardrails/manager.py +163 -0
  31. aip_agents/guardrails/manager.pyi +42 -0
  32. aip_agents/guardrails/middleware.py +199 -0
  33. aip_agents/guardrails/middleware.pyi +87 -0
  34. aip_agents/guardrails/schemas.py +63 -0
  35. aip_agents/guardrails/schemas.pyi +43 -0
  36. aip_agents/guardrails/utils.py +45 -0
  37. aip_agents/guardrails/utils.pyi +19 -0
  38. aip_agents/mcp/client/__init__.py +38 -2
  39. aip_agents/mcp/client/connection_manager.py +36 -1
  40. aip_agents/mcp/client/connection_manager.pyi +3 -0
  41. aip_agents/mcp/client/persistent_session.py +318 -65
  42. aip_agents/mcp/client/persistent_session.pyi +9 -0
  43. aip_agents/mcp/client/transports.py +52 -4
  44. aip_agents/mcp/client/transports.pyi +9 -0
  45. aip_agents/memory/adapters/base_adapter.py +98 -0
  46. aip_agents/memory/adapters/base_adapter.pyi +25 -0
  47. aip_agents/middleware/base.py +8 -0
  48. aip_agents/middleware/base.pyi +4 -0
  49. aip_agents/middleware/manager.py +22 -0
  50. aip_agents/middleware/manager.pyi +4 -0
  51. aip_agents/ptc/__init__.py +87 -0
  52. aip_agents/ptc/__init__.pyi +14 -0
  53. aip_agents/ptc/custom_tools.py +473 -0
  54. aip_agents/ptc/custom_tools.pyi +184 -0
  55. aip_agents/ptc/custom_tools_payload.py +400 -0
  56. aip_agents/ptc/custom_tools_payload.pyi +31 -0
  57. aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
  58. aip_agents/ptc/custom_tools_templates/__init__.pyi +0 -0
  59. aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +23 -0
  60. aip_agents/ptc/custom_tools_templates/custom_init.py.template +15 -0
  61. aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +60 -0
  62. aip_agents/ptc/custom_tools_templates/custom_registry.py.template +87 -0
  63. aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
  64. aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +19 -0
  65. aip_agents/ptc/doc_gen.py +122 -0
  66. aip_agents/ptc/doc_gen.pyi +40 -0
  67. aip_agents/ptc/exceptions.py +57 -0
  68. aip_agents/ptc/exceptions.pyi +37 -0
  69. aip_agents/ptc/executor.py +261 -0
  70. aip_agents/ptc/executor.pyi +99 -0
  71. aip_agents/ptc/mcp/__init__.py +45 -0
  72. aip_agents/ptc/mcp/__init__.pyi +7 -0
  73. aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
  74. aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
  75. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  76. aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
  77. aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
  78. aip_agents/ptc/naming.py +196 -0
  79. aip_agents/ptc/naming.pyi +85 -0
  80. aip_agents/ptc/payload.py +26 -0
  81. aip_agents/ptc/payload.pyi +15 -0
  82. aip_agents/ptc/prompt_builder.py +673 -0
  83. aip_agents/ptc/prompt_builder.pyi +59 -0
  84. aip_agents/ptc/ptc_helper.py +16 -0
  85. aip_agents/ptc/ptc_helper.pyi +1 -0
  86. aip_agents/ptc/sandbox_bridge.py +256 -0
  87. aip_agents/ptc/sandbox_bridge.pyi +38 -0
  88. aip_agents/ptc/template_utils.py +33 -0
  89. aip_agents/ptc/template_utils.pyi +13 -0
  90. aip_agents/ptc/templates/__init__.py +1 -0
  91. aip_agents/ptc/templates/__init__.pyi +0 -0
  92. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  93. aip_agents/ptc/tool_def_helpers.py +101 -0
  94. aip_agents/ptc/tool_def_helpers.pyi +38 -0
  95. aip_agents/ptc/tool_enrichment.py +163 -0
  96. aip_agents/ptc/tool_enrichment.pyi +60 -0
  97. aip_agents/sandbox/__init__.py +43 -0
  98. aip_agents/sandbox/__init__.pyi +5 -0
  99. aip_agents/sandbox/defaults.py +205 -0
  100. aip_agents/sandbox/defaults.pyi +30 -0
  101. aip_agents/sandbox/e2b_runtime.py +295 -0
  102. aip_agents/sandbox/e2b_runtime.pyi +57 -0
  103. aip_agents/sandbox/template_builder.py +131 -0
  104. aip_agents/sandbox/template_builder.pyi +36 -0
  105. aip_agents/sandbox/types.py +24 -0
  106. aip_agents/sandbox/types.pyi +14 -0
  107. aip_agents/sandbox/validation.py +50 -0
  108. aip_agents/sandbox/validation.pyi +20 -0
  109. aip_agents/sentry/__init__.py +1 -1
  110. aip_agents/sentry/sentry.py +33 -12
  111. aip_agents/sentry/sentry.pyi +5 -4
  112. aip_agents/tools/__init__.py +20 -3
  113. aip_agents/tools/__init__.pyi +4 -2
  114. aip_agents/tools/browser_use/browser_use_tool.py +8 -0
  115. aip_agents/tools/browser_use/streaming.py +2 -0
  116. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +80 -31
  117. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +25 -9
  118. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +6 -6
  119. aip_agents/tools/constants.py +24 -12
  120. aip_agents/tools/constants.pyi +14 -11
  121. aip_agents/tools/date_range_tool.py +554 -0
  122. aip_agents/tools/date_range_tool.pyi +21 -0
  123. aip_agents/tools/execute_ptc_code.py +357 -0
  124. aip_agents/tools/execute_ptc_code.pyi +90 -0
  125. aip_agents/tools/gl_connector/__init__.py +1 -1
  126. aip_agents/tools/gl_connector/tool.py +62 -30
  127. aip_agents/tools/gl_connector/tool.pyi +3 -3
  128. aip_agents/tools/gl_connector_tools.py +119 -0
  129. aip_agents/tools/gl_connector_tools.pyi +39 -0
  130. aip_agents/tools/memory_search/__init__.py +8 -1
  131. aip_agents/tools/memory_search/__init__.pyi +3 -3
  132. aip_agents/tools/memory_search/mem0.py +114 -1
  133. aip_agents/tools/memory_search/mem0.pyi +11 -1
  134. aip_agents/tools/memory_search/schema.py +33 -0
  135. aip_agents/tools/memory_search/schema.pyi +10 -0
  136. aip_agents/tools/memory_search_tool.py +8 -0
  137. aip_agents/tools/memory_search_tool.pyi +2 -2
  138. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +26 -1
  139. aip_agents/utils/langgraph/tool_output_management.py +80 -0
  140. aip_agents/utils/langgraph/tool_output_management.pyi +37 -0
  141. {aip_agents_binary-0.5.21.dist-info → aip_agents_binary-0.6.8.dist-info}/METADATA +14 -22
  142. {aip_agents_binary-0.5.21.dist-info → aip_agents_binary-0.6.8.dist-info}/RECORD +144 -58
  143. {aip_agents_binary-0.5.21.dist-info → aip_agents_binary-0.6.8.dist-info}/WHEEL +1 -1
  144. aip_agents/examples/demo_memory_recall.py +0 -401
  145. aip_agents/examples/demo_memory_recall.pyi +0 -58
  146. aip_agents/examples/hello_world_langgraph_bosa_twitter.pyi +0 -5
  147. aip_agents/tools/bosa_tools.py +0 -105
  148. aip_agents/tools/bosa_tools.pyi +0 -37
  149. {aip_agents_binary-0.5.21.dist-info → aip_agents_binary-0.6.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,119 @@
1
+ """Auto-generated tools from GL Connectors.
2
+
3
+ Authors:
4
+ Saul Sayers (saul.sayers@gdplabs.id)
5
+ """
6
+
7
+ from bosa_connectors import BosaConnector, BOSAConnectorToolGenerator
8
+ from langchain_core.tools import BaseTool
9
+
10
+ from aip_agents.tools.constants import (
11
+ GL_CONNECTORS_API_KEY,
12
+ GL_CONNECTORS_BASE_URL,
13
+ GL_CONNECTORS_FETCH_MAX_RETRIES,
14
+ ToolType,
15
+ )
16
+ from aip_agents.utils.logger import get_logger
17
+
18
+ logger = get_logger(__name__)
19
+
20
+
21
+ def get_gl_connector_modules_with_retry() -> list[str]:
22
+ """Try to get available modules with retries.
23
+
24
+ Returns:
25
+ List of available modules.
26
+ """
27
+ if not GL_CONNECTORS_BASE_URL or not GL_CONNECTORS_API_KEY:
28
+ logger.warning("GL Connectors credentials missing (base_url or api_key); returning empty modules list")
29
+ return []
30
+
31
+ connector = BosaConnector(api_base_url=GL_CONNECTORS_BASE_URL, api_key=GL_CONNECTORS_API_KEY)
32
+ modules = []
33
+ for attempt in range(GL_CONNECTORS_FETCH_MAX_RETRIES):
34
+ try:
35
+ modules = list(connector.get_available_modules())
36
+ if modules:
37
+ return modules
38
+ logger.warning(
39
+ f"Failed to get GL Connectors available modules, retrying... (attempt {attempt + 1} / {GL_CONNECTORS_FETCH_MAX_RETRIES})"
40
+ )
41
+ except Exception as e:
42
+ logger.exception(
43
+ f"Exception when getting GL Connectors available modules (attempt {attempt + 1} / {GL_CONNECTORS_FETCH_MAX_RETRIES}): {e}"
44
+ )
45
+ logger.error("Failed to get GL Connectors available modules after maximum retries")
46
+ return modules
47
+
48
+
49
+ class LazyGLConnectorToolsDict(dict):
50
+ """Lazy dictionary for GL Connectors."""
51
+
52
+ def __missing__(self, app):
53
+ """When a key is missing, create the tools and store them in the dictionary.
54
+
55
+ Args:
56
+ app: Name of the GL Connectors.
57
+
58
+ Returns:
59
+ List of tools generated by the GL Connectors tool generator.
60
+ """
61
+ if app not in get_gl_connector_modules():
62
+ return []
63
+ tools = []
64
+ for attempt in range(GL_CONNECTORS_FETCH_MAX_RETRIES):
65
+ try:
66
+ tools = BOSAConnectorToolGenerator(
67
+ api_base_url=GL_CONNECTORS_BASE_URL,
68
+ api_key=GL_CONNECTORS_API_KEY,
69
+ app_name=app,
70
+ ).generate_tools(tool_type=ToolType.LANGCHAIN)
71
+ if tools:
72
+ self[app] = tools
73
+ return tools
74
+ logger.warning(
75
+ f"Failed to create GL Connectors, retrying... (attempt {attempt + 1} / {GL_CONNECTORS_FETCH_MAX_RETRIES})"
76
+ )
77
+ except Exception as e:
78
+ logger.exception(
79
+ f"Exception when creating GL Connectors for app '{app}' "
80
+ f"(attempt {attempt + 1} / {GL_CONNECTORS_FETCH_MAX_RETRIES}): {e}"
81
+ )
82
+ logger.error("Failed to create GL Connectors after maximum retries")
83
+ return tools
84
+
85
+
86
+ # Supported modules (dynamic)
87
+ def get_gl_connector_modules() -> list[str]:
88
+ """Lazily fetch and cache GL Connectors modules.
89
+
90
+ Returns:
91
+ List of GL Connectors modules.
92
+ """
93
+ if not hasattr(get_gl_connector_modules, "_cache"):
94
+ get_gl_connector_modules._cache = get_gl_connector_modules_with_retry()
95
+ return get_gl_connector_modules._cache
96
+
97
+
98
+ # FOR BACKWARDS COMPATIBILITY
99
+ def get_bosa_modules() -> list[str]:
100
+ """Backward-compatible alias for get_gl_connector_modules."""
101
+ return get_gl_connector_modules()
102
+
103
+
104
+ GL_CONNECTORS_MODULES = [
105
+ "github",
106
+ "twitter",
107
+ "google",
108
+ "google_drive",
109
+ "google_mail",
110
+ "google_docs",
111
+ ]
112
+
113
+ # FOR BACKWARDS COMPATIBILITY
114
+ BOSA_MODULES = GL_CONNECTORS_MODULES
115
+
116
+ GL_CONNECTORS_AUTOMATED_TOOLS: dict[str, list[BaseTool]] = LazyGLConnectorToolsDict()
117
+
118
+ # FOR BACKWARDS COMPATIBILITY
119
+ BOSA_AUTOMATED_TOOLS = GL_CONNECTORS_AUTOMATED_TOOLS
@@ -0,0 +1,39 @@
1
+ from _typeshed import Incomplete
2
+ from aip_agents.tools.constants import GL_CONNECTORS_API_KEY as GL_CONNECTORS_API_KEY, GL_CONNECTORS_BASE_URL as GL_CONNECTORS_BASE_URL, GL_CONNECTORS_FETCH_MAX_RETRIES as GL_CONNECTORS_FETCH_MAX_RETRIES, ToolType as ToolType
3
+ from aip_agents.utils.logger import get_logger as get_logger
4
+ from langchain_core.tools import BaseTool as BaseTool
5
+
6
+ logger: Incomplete
7
+
8
+ def get_gl_connector_modules_with_retry() -> list[str]:
9
+ """Try to get available modules with retries.
10
+
11
+ Returns:
12
+ List of available modules.
13
+ """
14
+
15
+ class LazyGLConnectorToolsDict(dict):
16
+ """Lazy dictionary for GL Connectors."""
17
+ def __missing__(self, app):
18
+ """When a key is missing, create the tools and store them in the dictionary.
19
+
20
+ Args:
21
+ app: Name of the GL Connectors.
22
+
23
+ Returns:
24
+ List of tools generated by the GL Connectors tool generator.
25
+ """
26
+
27
+ def get_gl_connector_modules() -> list[str]:
28
+ """Lazily fetch and cache GL Connectors modules.
29
+
30
+ Returns:
31
+ List of GL Connectors modules.
32
+ """
33
+ def get_bosa_modules() -> list[str]:
34
+ """Backward-compatible alias for get_gl_connector_modules."""
35
+
36
+ GL_CONNECTORS_MODULES: Incomplete
37
+ BOSA_MODULES = GL_CONNECTORS_MODULES
38
+ GL_CONNECTORS_AUTOMATED_TOOLS: dict[str, list[BaseTool]]
39
+ BOSA_AUTOMATED_TOOLS = GL_CONNECTORS_AUTOMATED_TOOLS
@@ -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,115 @@ 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
+ filters: dict[str, Any] | None = None
324
+ if metadata_filter or categories:
325
+ filters = {}
326
+ if metadata_filter:
327
+ filters["metadata"] = metadata_filter
328
+ if categories:
329
+ filters["categories"] = categories
330
+ result = self.memory.delete_by_query( # type: ignore[attr-defined]
331
+ query=query,
332
+ user_id=user_id,
333
+ threshold=threshold,
334
+ top_k=top_k,
335
+ filters=filters,
336
+ )
337
+ elif delete_all:
338
+ if not hasattr(self.memory, "delete"):
339
+ return f"Error executing memory tool '{self.name}': backend does not support delete()"
340
+ mode = "all"
341
+ result = self.memory.delete( # type: ignore[attr-defined]
342
+ memory_ids=None,
343
+ user_id=user_id,
344
+ metadata=metadata_filter,
345
+ categories=categories,
346
+ )
347
+ else:
348
+ return f"Error executing memory tool '{self.name}': provide memory_ids, query, or delete_all=true."
349
+
350
+ count = None
351
+ if isinstance(result, dict):
352
+ count = result.get("count") or result.get("deleted") or result.get("total")
353
+
354
+ logger.info(
355
+ "%s: delete mode=%s user_id='%s' count=%s",
356
+ self.LOG_PREFIX,
357
+ mode,
358
+ user_id,
359
+ count if count is not None else "unknown",
360
+ )
361
+
362
+ payload = {"status": "success", "mode": mode}
363
+ try:
364
+ json.dumps(result)
365
+ payload["result"] = result
366
+ except TypeError:
367
+ payload["result"] = str(result)
368
+ return json.dumps(payload)
369
+
370
+
371
+ 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']
@@ -134,7 +134,19 @@ class DelegationToolManager(BaseLangGraphToolManager):
134
134
  Returns:
135
135
  The result from the delegated agent, including artifacts if any.
136
136
  """
137
- writer: StreamWriter = get_stream_writer()
137
+ try:
138
+ writer: StreamWriter = get_stream_writer()
139
+ except Exception as exc:
140
+ logger.warning(
141
+ "DelegationToolManager: Stream writer unavailable; delegation streaming disabled.",
142
+ extra={"error": str(exc), "error_type": type(exc).__name__},
143
+ )
144
+
145
+ def _noop_writer(_: Any) -> None:
146
+ """No-op writer for non-graph execution contexts."""
147
+ return None
148
+
149
+ writer = _noop_writer
138
150
 
139
151
  try:
140
152
  # Check delegation depth limit before executing
@@ -295,6 +307,19 @@ class DelegationToolManager(BaseLangGraphToolManager):
295
307
  cfg_conf = cfg.get("configurable") if isinstance(cfg, dict) else None
296
308
  if isinstance(cfg_conf, dict):
297
309
  parent_step_id = cfg_conf.get("parent_step_id")
310
+ if not parent_step_id:
311
+ metadata = cfg.get("metadata") or {}
312
+ tool_call_id = (
313
+ cfg_conf.get("tool_call_id")
314
+ or metadata.get("tool_call_id")
315
+ or metadata.get("id")
316
+ or (metadata.get("tool_call") or {}).get("id")
317
+ )
318
+ if tool_call_id and self.parent_agent is not None:
319
+ thread_key = getattr(self.parent_agent, "thread_id_key", "thread_id")
320
+ thread_id = cfg_conf.get(thread_key)
321
+ parent_map = self.parent_agent._tool_parent_map_by_thread.get(str(thread_id), {})
322
+ parent_step_id = parent_map.get(str(tool_call_id))
298
323
  _DELEGATION_PARENT_STEP_ID_CVAR.set(parent_step_id)
299
324
  except Exception:
300
325
  _DELEGATION_PARENT_STEP_ID_CVAR.set(None)
@@ -471,6 +471,86 @@ class ToolOutputManager:
471
471
  """
472
472
  return self._generate_json_summary(thread_id, max_entries)
473
473
 
474
+ def get_latest_reference(self, thread_id: str) -> str | None:
475
+ """Return the most recent tool output reference for a thread.
476
+
477
+ Args:
478
+ thread_id: Thread ID to retrieve the latest output reference for.
479
+
480
+ Returns:
481
+ Latest tool output reference string or None when unavailable.
482
+ """
483
+ try:
484
+ summary = json.loads(self.generate_summary(thread_id, max_entries=1))
485
+ except Exception as exc:
486
+ logger.debug("Failed to parse tool output summary: %s", exc)
487
+ return None
488
+
489
+ if not summary:
490
+ return None
491
+ latest = summary[0].get("reference")
492
+ return latest if isinstance(latest, str) and latest else None
493
+
494
+ def has_reference(self, value: Any) -> bool:
495
+ """Check whether a value contains a tool output reference.
496
+
497
+ Args:
498
+ value: Value to inspect for tool output references.
499
+
500
+ Returns:
501
+ True if any tool output reference is present.
502
+ """
503
+ if isinstance(value, str):
504
+ return value.startswith(TOOL_OUTPUT_REFERENCE_PREFIX)
505
+ if isinstance(value, dict):
506
+ return any(self.has_reference(item) for item in value.values())
507
+ if isinstance(value, list):
508
+ return any(self.has_reference(item) for item in value)
509
+ return False
510
+
511
+ def should_replace_with_reference(self, value: Any) -> bool:
512
+ """Check whether a tool argument value should use a tool output reference.
513
+
514
+ Args:
515
+ value: Value to evaluate for replacement.
516
+
517
+ Returns:
518
+ True if the value should be replaced with a reference.
519
+ """
520
+ if isinstance(value, dict | list | tuple):
521
+ return True
522
+ if isinstance(value, str):
523
+ return len(value) > DATA_PREVIEW_TRUNCATION_LENGTH
524
+ return False
525
+
526
+ def rewrite_args_with_latest_reference(self, args: dict[str, Any], thread_id: str) -> dict[str, Any]:
527
+ """Rewrite tool args to use the latest tool output reference when appropriate.
528
+
529
+ Args:
530
+ args: Tool arguments to rewrite.
531
+ thread_id: Thread ID used for resolving stored outputs.
532
+
533
+ Returns:
534
+ Updated args dictionary with references substituted when needed.
535
+ """
536
+ if not self.has_outputs(thread_id):
537
+ return args
538
+ if self.has_reference(args):
539
+ return args
540
+
541
+ latest_reference = self.get_latest_reference(thread_id)
542
+ if not latest_reference:
543
+ return args
544
+
545
+ updated_args = dict(args)
546
+ replaced_any = False
547
+ for key, value in args.items():
548
+ if self.should_replace_with_reference(value):
549
+ updated_args[key] = latest_reference
550
+ replaced_any = True
551
+
552
+ return updated_args if replaced_any else args
553
+
474
554
  def _generate_json_summary(self, thread_id: str, max_entries: int) -> str:
475
555
  """Generate simplified JSON summary optimized for LLM prompts.
476
556
 
@@ -232,6 +232,43 @@ class ToolOutputManager:
232
232
  A JSON string containing structured data about tool outputs. Always returns
233
233
  valid JSON, even when no outputs are stored (empty entries list).
234
234
  """
235
+ def get_latest_reference(self, thread_id: str) -> str | None:
236
+ """Return the most recent tool output reference for a thread.
237
+
238
+ Args:
239
+ thread_id: Thread ID to retrieve the latest output reference for.
240
+
241
+ Returns:
242
+ Latest tool output reference string or None when unavailable.
243
+ """
244
+ def has_reference(self, value: Any) -> bool:
245
+ """Check whether a value contains a tool output reference.
246
+
247
+ Args:
248
+ value: Value to inspect for tool output references.
249
+
250
+ Returns:
251
+ True if any tool output reference is present.
252
+ """
253
+ def should_replace_with_reference(self, value: Any) -> bool:
254
+ """Check whether a tool argument value should use a tool output reference.
255
+
256
+ Args:
257
+ value: Value to evaluate for replacement.
258
+
259
+ Returns:
260
+ True if the value should be replaced with a reference.
261
+ """
262
+ def rewrite_args_with_latest_reference(self, args: dict[str, Any], thread_id: str) -> dict[str, Any]:
263
+ """Rewrite tool args to use the latest tool output reference when appropriate.
264
+
265
+ Args:
266
+ args: Tool arguments to rewrite.
267
+ thread_id: Thread ID used for resolving stored outputs.
268
+
269
+ Returns:
270
+ Updated args dictionary with references substituted when needed.
271
+ """
235
272
  def clear_all(self) -> None:
236
273
  """Clear all stored outputs from both metadata and storage.
237
274