aip-agents-binary 0.6.4__py3-none-any.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.
Potentially problematic release.
This version of aip-agents-binary might be problematic. Click here for more details.
- aip_agents/__init__.py +65 -0
- aip_agents/__init__.pyi +19 -0
- aip_agents/a2a/__init__.py +19 -0
- aip_agents/a2a/__init__.pyi +3 -0
- aip_agents/a2a/server/__init__.py +10 -0
- aip_agents/a2a/server/__init__.pyi +4 -0
- aip_agents/a2a/server/base_executor.py +1086 -0
- aip_agents/a2a/server/base_executor.pyi +73 -0
- aip_agents/a2a/server/google_adk_executor.py +198 -0
- aip_agents/a2a/server/google_adk_executor.pyi +51 -0
- aip_agents/a2a/server/langflow_executor.py +180 -0
- aip_agents/a2a/server/langflow_executor.pyi +43 -0
- aip_agents/a2a/server/langgraph_executor.py +270 -0
- aip_agents/a2a/server/langgraph_executor.pyi +47 -0
- aip_agents/a2a/types.py +232 -0
- aip_agents/a2a/types.pyi +132 -0
- aip_agents/agent/__init__.py +27 -0
- aip_agents/agent/__init__.pyi +9 -0
- aip_agents/agent/base_agent.py +970 -0
- aip_agents/agent/base_agent.pyi +221 -0
- aip_agents/agent/base_langgraph_agent.py +3037 -0
- aip_agents/agent/base_langgraph_agent.pyi +233 -0
- aip_agents/agent/google_adk_agent.py +926 -0
- aip_agents/agent/google_adk_agent.pyi +141 -0
- aip_agents/agent/google_adk_constants.py +6 -0
- aip_agents/agent/google_adk_constants.pyi +3 -0
- aip_agents/agent/hitl/__init__.py +24 -0
- aip_agents/agent/hitl/__init__.pyi +6 -0
- aip_agents/agent/hitl/config.py +28 -0
- aip_agents/agent/hitl/config.pyi +15 -0
- aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
- aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +42 -0
- aip_agents/agent/hitl/manager.py +532 -0
- aip_agents/agent/hitl/manager.pyi +200 -0
- aip_agents/agent/hitl/models.py +18 -0
- aip_agents/agent/hitl/models.pyi +3 -0
- aip_agents/agent/hitl/prompt/__init__.py +9 -0
- aip_agents/agent/hitl/prompt/__init__.pyi +4 -0
- aip_agents/agent/hitl/prompt/base.py +42 -0
- aip_agents/agent/hitl/prompt/base.pyi +24 -0
- aip_agents/agent/hitl/prompt/deferred.py +73 -0
- aip_agents/agent/hitl/prompt/deferred.pyi +30 -0
- aip_agents/agent/hitl/registry.py +149 -0
- aip_agents/agent/hitl/registry.pyi +101 -0
- aip_agents/agent/interface.py +138 -0
- aip_agents/agent/interface.pyi +81 -0
- aip_agents/agent/interfaces.py +65 -0
- aip_agents/agent/interfaces.pyi +44 -0
- aip_agents/agent/langflow_agent.py +464 -0
- aip_agents/agent/langflow_agent.pyi +133 -0
- aip_agents/agent/langgraph_memory_enhancer_agent.py +767 -0
- aip_agents/agent/langgraph_memory_enhancer_agent.pyi +50 -0
- aip_agents/agent/langgraph_react_agent.py +2856 -0
- aip_agents/agent/langgraph_react_agent.pyi +170 -0
- aip_agents/agent/system_instruction_context.py +34 -0
- aip_agents/agent/system_instruction_context.pyi +13 -0
- aip_agents/clients/__init__.py +10 -0
- aip_agents/clients/__init__.pyi +4 -0
- aip_agents/clients/langflow/__init__.py +10 -0
- aip_agents/clients/langflow/__init__.pyi +4 -0
- aip_agents/clients/langflow/client.py +477 -0
- aip_agents/clients/langflow/client.pyi +140 -0
- aip_agents/clients/langflow/types.py +18 -0
- aip_agents/clients/langflow/types.pyi +7 -0
- aip_agents/constants.py +23 -0
- aip_agents/constants.pyi +7 -0
- aip_agents/credentials/manager.py +132 -0
- aip_agents/examples/__init__.py +5 -0
- aip_agents/examples/__init__.pyi +0 -0
- aip_agents/examples/compare_streaming_client.py +783 -0
- aip_agents/examples/compare_streaming_client.pyi +48 -0
- aip_agents/examples/compare_streaming_server.py +142 -0
- aip_agents/examples/compare_streaming_server.pyi +18 -0
- aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
- aip_agents/examples/hello_world_a2a_google_adk_client.pyi +9 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +9 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +9 -0
- aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
- aip_agents/examples/hello_world_a2a_google_adk_server.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
- aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
- aip_agents/examples/hello_world_a2a_langchain_server.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
- aip_agents/examples/hello_world_a2a_langflow_client.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
- aip_agents/examples/hello_world_a2a_langflow_server.pyi +14 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +16 -0
- aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +2 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
- aip_agents/examples/hello_world_a2a_langgraph_server.pyi +14 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +15 -0
- aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
- aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +48 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +48 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +45 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +5 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +15 -0
- aip_agents/examples/hello_world_google_adk.py +41 -0
- aip_agents/examples/hello_world_google_adk.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
- aip_agents/examples/hello_world_google_adk_mcp_http.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
- aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_stream.py +44 -0
- aip_agents/examples/hello_world_google_adk_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain.py +28 -0
- aip_agents/examples/hello_world_langchain.pyi +5 -0
- aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
- aip_agents/examples/hello_world_langchain_lm_invoker.pyi +2 -0
- aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_http.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
- aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +16 -0
- aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
- aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
- aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +18 -0
- aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_sse.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
- aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_stream.py +36 -0
- aip_agents/examples/hello_world_langchain_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
- aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_langflow_agent.py +163 -0
- aip_agents/examples/hello_world_langflow_agent.pyi +35 -0
- aip_agents/examples/hello_world_langgraph.py +39 -0
- aip_agents/examples/hello_world_langgraph.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
- aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
- aip_agents/examples/hello_world_langgraph_mcp_http.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
- aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_stream.py +43 -0
- aip_agents/examples/hello_world_langgraph_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_model_switch_cli.py +210 -0
- aip_agents/examples/hello_world_model_switch_cli.pyi +30 -0
- aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
- aip_agents/examples/hello_world_multi_agent_adk.pyi +6 -0
- aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
- aip_agents/examples/hello_world_multi_agent_langchain.pyi +5 -0
- aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
- aip_agents/examples/hello_world_multi_agent_langgraph.pyi +5 -0
- aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
- aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_pii_logger.py +21 -0
- aip_agents/examples/hello_world_pii_logger.pyi +5 -0
- aip_agents/examples/hello_world_ptc.py +49 -0
- aip_agents/examples/hello_world_ptc.pyi +5 -0
- aip_agents/examples/hello_world_sentry.py +133 -0
- aip_agents/examples/hello_world_sentry.pyi +21 -0
- aip_agents/examples/hello_world_step_limits.py +273 -0
- aip_agents/examples/hello_world_step_limits.pyi +17 -0
- aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
- aip_agents/examples/hello_world_stock_a2a_server.pyi +17 -0
- aip_agents/examples/hello_world_tool_output_client.py +55 -0
- aip_agents/examples/hello_world_tool_output_client.pyi +5 -0
- aip_agents/examples/hello_world_tool_output_server.py +114 -0
- aip_agents/examples/hello_world_tool_output_server.pyi +19 -0
- aip_agents/examples/hitl_demo.py +724 -0
- aip_agents/examples/hitl_demo.pyi +67 -0
- aip_agents/examples/mcp_configs/configs.py +63 -0
- aip_agents/examples/mcp_servers/common.py +76 -0
- aip_agents/examples/mcp_servers/mcp_name.py +29 -0
- aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
- aip_agents/examples/mcp_servers/mcp_time.py +10 -0
- aip_agents/examples/pii_demo_langgraph_client.py +69 -0
- aip_agents/examples/pii_demo_langgraph_client.pyi +5 -0
- aip_agents/examples/pii_demo_langgraph_server.py +126 -0
- aip_agents/examples/pii_demo_langgraph_server.pyi +20 -0
- aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
- aip_agents/examples/pii_demo_multi_agent_client.pyi +5 -0
- aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
- aip_agents/examples/pii_demo_multi_agent_server.pyi +40 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +5 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +19 -0
- aip_agents/examples/tools/__init__.py +27 -0
- aip_agents/examples/tools/__init__.pyi +9 -0
- aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
- aip_agents/examples/tools/adk_arithmetic_tools.pyi +24 -0
- aip_agents/examples/tools/adk_weather_tool.py +60 -0
- aip_agents/examples/tools/adk_weather_tool.pyi +18 -0
- aip_agents/examples/tools/data_generator_tool.py +103 -0
- aip_agents/examples/tools/data_generator_tool.pyi +15 -0
- aip_agents/examples/tools/data_visualization_tool.py +312 -0
- aip_agents/examples/tools/data_visualization_tool.pyi +19 -0
- aip_agents/examples/tools/image_artifact_tool.py +136 -0
- aip_agents/examples/tools/image_artifact_tool.pyi +26 -0
- aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
- aip_agents/examples/tools/langchain_arithmetic_tools.pyi +17 -0
- aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
- aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +20 -0
- aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
- aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +25 -0
- aip_agents/examples/tools/langchain_weather_tool.py +48 -0
- aip_agents/examples/tools/langchain_weather_tool.pyi +19 -0
- aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
- aip_agents/examples/tools/langgraph_streaming_tool.pyi +43 -0
- aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
- aip_agents/examples/tools/mock_retrieval_tool.pyi +13 -0
- aip_agents/examples/tools/pii_demo_tools.py +189 -0
- aip_agents/examples/tools/pii_demo_tools.pyi +54 -0
- aip_agents/examples/tools/random_chart_tool.py +142 -0
- aip_agents/examples/tools/random_chart_tool.pyi +20 -0
- aip_agents/examples/tools/serper_tool.py +202 -0
- aip_agents/examples/tools/serper_tool.pyi +16 -0
- aip_agents/examples/tools/stock_tools.py +82 -0
- aip_agents/examples/tools/stock_tools.pyi +36 -0
- aip_agents/examples/tools/table_generator_tool.py +167 -0
- aip_agents/examples/tools/table_generator_tool.pyi +22 -0
- aip_agents/examples/tools/time_tool.py +82 -0
- aip_agents/examples/tools/time_tool.pyi +15 -0
- aip_agents/examples/tools/weather_forecast_tool.py +38 -0
- aip_agents/examples/tools/weather_forecast_tool.pyi +14 -0
- aip_agents/executor/agent_executor.py +473 -0
- aip_agents/executor/base.py +48 -0
- aip_agents/guardrails/__init__.py +83 -0
- aip_agents/guardrails/__init__.pyi +6 -0
- aip_agents/guardrails/engines/__init__.py +69 -0
- aip_agents/guardrails/engines/__init__.pyi +4 -0
- aip_agents/guardrails/engines/base.py +90 -0
- aip_agents/guardrails/engines/base.pyi +61 -0
- aip_agents/guardrails/engines/nemo.py +101 -0
- aip_agents/guardrails/engines/nemo.pyi +46 -0
- aip_agents/guardrails/engines/phrase_matcher.py +113 -0
- aip_agents/guardrails/engines/phrase_matcher.pyi +48 -0
- aip_agents/guardrails/exceptions.py +39 -0
- aip_agents/guardrails/exceptions.pyi +23 -0
- aip_agents/guardrails/manager.py +163 -0
- aip_agents/guardrails/manager.pyi +42 -0
- aip_agents/guardrails/middleware.py +199 -0
- aip_agents/guardrails/middleware.pyi +87 -0
- aip_agents/guardrails/schemas.py +63 -0
- aip_agents/guardrails/schemas.pyi +43 -0
- aip_agents/guardrails/utils.py +45 -0
- aip_agents/guardrails/utils.pyi +19 -0
- aip_agents/mcp/__init__.py +1 -0
- aip_agents/mcp/__init__.pyi +0 -0
- aip_agents/mcp/client/__init__.py +14 -0
- aip_agents/mcp/client/__init__.pyi +5 -0
- aip_agents/mcp/client/base_mcp_client.py +369 -0
- aip_agents/mcp/client/base_mcp_client.pyi +148 -0
- aip_agents/mcp/client/connection_manager.py +228 -0
- aip_agents/mcp/client/connection_manager.pyi +51 -0
- aip_agents/mcp/client/google_adk/__init__.py +11 -0
- aip_agents/mcp/client/google_adk/__init__.pyi +3 -0
- aip_agents/mcp/client/google_adk/client.py +381 -0
- aip_agents/mcp/client/google_adk/client.pyi +75 -0
- aip_agents/mcp/client/langchain/__init__.py +11 -0
- aip_agents/mcp/client/langchain/__init__.pyi +3 -0
- aip_agents/mcp/client/langchain/client.py +265 -0
- aip_agents/mcp/client/langchain/client.pyi +48 -0
- aip_agents/mcp/client/persistent_session.py +612 -0
- aip_agents/mcp/client/persistent_session.pyi +122 -0
- aip_agents/mcp/client/session_pool.py +351 -0
- aip_agents/mcp/client/session_pool.pyi +101 -0
- aip_agents/mcp/client/transports.py +263 -0
- aip_agents/mcp/client/transports.pyi +132 -0
- aip_agents/mcp/utils/__init__.py +7 -0
- aip_agents/mcp/utils/__init__.pyi +0 -0
- aip_agents/mcp/utils/config_validator.py +139 -0
- aip_agents/mcp/utils/config_validator.pyi +82 -0
- aip_agents/memory/__init__.py +14 -0
- aip_agents/memory/__init__.pyi +5 -0
- aip_agents/memory/adapters/__init__.py +10 -0
- aip_agents/memory/adapters/__init__.pyi +4 -0
- aip_agents/memory/adapters/base_adapter.py +811 -0
- aip_agents/memory/adapters/base_adapter.pyi +176 -0
- aip_agents/memory/adapters/mem0.py +84 -0
- aip_agents/memory/adapters/mem0.pyi +22 -0
- aip_agents/memory/base.py +84 -0
- aip_agents/memory/base.pyi +60 -0
- aip_agents/memory/constants.py +49 -0
- aip_agents/memory/constants.pyi +25 -0
- aip_agents/memory/factory.py +86 -0
- aip_agents/memory/factory.pyi +24 -0
- aip_agents/memory/guidance.py +20 -0
- aip_agents/memory/guidance.pyi +3 -0
- aip_agents/memory/simple_memory.py +47 -0
- aip_agents/memory/simple_memory.pyi +23 -0
- aip_agents/middleware/__init__.py +17 -0
- aip_agents/middleware/__init__.pyi +5 -0
- aip_agents/middleware/base.py +96 -0
- aip_agents/middleware/base.pyi +75 -0
- aip_agents/middleware/manager.py +150 -0
- aip_agents/middleware/manager.pyi +84 -0
- aip_agents/middleware/todolist.py +274 -0
- aip_agents/middleware/todolist.pyi +125 -0
- aip_agents/ptc/__init__.py +48 -0
- aip_agents/ptc/__init__.pyi +10 -0
- aip_agents/ptc/doc_gen.py +122 -0
- aip_agents/ptc/doc_gen.pyi +40 -0
- aip_agents/ptc/exceptions.py +39 -0
- aip_agents/ptc/exceptions.pyi +22 -0
- aip_agents/ptc/executor.py +143 -0
- aip_agents/ptc/executor.pyi +73 -0
- aip_agents/ptc/mcp/__init__.py +45 -0
- aip_agents/ptc/mcp/__init__.pyi +7 -0
- aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
- aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
- aip_agents/ptc/mcp/templates/__init__.py +1 -0
- aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
- aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
- aip_agents/ptc/naming.py +184 -0
- aip_agents/ptc/naming.pyi +76 -0
- aip_agents/ptc/payload.py +26 -0
- aip_agents/ptc/payload.pyi +15 -0
- aip_agents/ptc/prompt_builder.py +571 -0
- aip_agents/ptc/prompt_builder.pyi +55 -0
- aip_agents/ptc/ptc_helper.py +16 -0
- aip_agents/ptc/ptc_helper.pyi +1 -0
- aip_agents/ptc/sandbox_bridge.py +58 -0
- aip_agents/ptc/sandbox_bridge.pyi +25 -0
- aip_agents/ptc/template_utils.py +33 -0
- aip_agents/ptc/template_utils.pyi +13 -0
- aip_agents/ptc/templates/__init__.py +1 -0
- aip_agents/ptc/templates/__init__.pyi +0 -0
- aip_agents/ptc/templates/ptc_helper.py.template +134 -0
- aip_agents/sandbox/__init__.py +43 -0
- aip_agents/sandbox/__init__.pyi +5 -0
- aip_agents/sandbox/defaults.py +9 -0
- aip_agents/sandbox/defaults.pyi +2 -0
- aip_agents/sandbox/e2b_runtime.py +267 -0
- aip_agents/sandbox/e2b_runtime.pyi +51 -0
- aip_agents/sandbox/template_builder.py +131 -0
- aip_agents/sandbox/template_builder.pyi +36 -0
- aip_agents/sandbox/types.py +24 -0
- aip_agents/sandbox/types.pyi +14 -0
- aip_agents/sandbox/validation.py +50 -0
- aip_agents/sandbox/validation.pyi +20 -0
- aip_agents/schema/__init__.py +69 -0
- aip_agents/schema/__init__.pyi +9 -0
- aip_agents/schema/a2a.py +56 -0
- aip_agents/schema/a2a.pyi +40 -0
- aip_agents/schema/agent.py +111 -0
- aip_agents/schema/agent.pyi +65 -0
- aip_agents/schema/hitl.py +157 -0
- aip_agents/schema/hitl.pyi +89 -0
- aip_agents/schema/langgraph.py +37 -0
- aip_agents/schema/langgraph.pyi +28 -0
- aip_agents/schema/model_id.py +97 -0
- aip_agents/schema/model_id.pyi +54 -0
- aip_agents/schema/step_limit.py +108 -0
- aip_agents/schema/step_limit.pyi +63 -0
- aip_agents/schema/storage.py +40 -0
- aip_agents/schema/storage.pyi +21 -0
- aip_agents/sentry/__init__.py +11 -0
- aip_agents/sentry/__init__.pyi +3 -0
- aip_agents/sentry/sentry.py +151 -0
- aip_agents/sentry/sentry.pyi +48 -0
- aip_agents/storage/__init__.py +41 -0
- aip_agents/storage/__init__.pyi +8 -0
- aip_agents/storage/base.py +85 -0
- aip_agents/storage/base.pyi +58 -0
- aip_agents/storage/clients/__init__.py +12 -0
- aip_agents/storage/clients/__init__.pyi +3 -0
- aip_agents/storage/clients/minio_client.py +318 -0
- aip_agents/storage/clients/minio_client.pyi +137 -0
- aip_agents/storage/config.py +62 -0
- aip_agents/storage/config.pyi +29 -0
- aip_agents/storage/providers/__init__.py +15 -0
- aip_agents/storage/providers/__init__.pyi +5 -0
- aip_agents/storage/providers/base.py +106 -0
- aip_agents/storage/providers/base.pyi +88 -0
- aip_agents/storage/providers/memory.py +114 -0
- aip_agents/storage/providers/memory.pyi +79 -0
- aip_agents/storage/providers/object_storage.py +214 -0
- aip_agents/storage/providers/object_storage.pyi +98 -0
- aip_agents/tools/__init__.py +64 -0
- aip_agents/tools/__init__.pyi +11 -0
- aip_agents/tools/browser_use/__init__.py +82 -0
- aip_agents/tools/browser_use/__init__.pyi +14 -0
- aip_agents/tools/browser_use/action_parser.py +103 -0
- aip_agents/tools/browser_use/action_parser.pyi +18 -0
- aip_agents/tools/browser_use/browser_use_tool.py +1120 -0
- aip_agents/tools/browser_use/browser_use_tool.pyi +50 -0
- aip_agents/tools/browser_use/llm_config.py +120 -0
- aip_agents/tools/browser_use/llm_config.pyi +52 -0
- aip_agents/tools/browser_use/minio_storage.py +198 -0
- aip_agents/tools/browser_use/minio_storage.pyi +109 -0
- aip_agents/tools/browser_use/schemas.py +119 -0
- aip_agents/tools/browser_use/schemas.pyi +32 -0
- aip_agents/tools/browser_use/session.py +76 -0
- aip_agents/tools/browser_use/session.pyi +4 -0
- aip_agents/tools/browser_use/session_errors.py +132 -0
- aip_agents/tools/browser_use/session_errors.pyi +53 -0
- aip_agents/tools/browser_use/steel_session_recording.py +317 -0
- aip_agents/tools/browser_use/steel_session_recording.pyi +63 -0
- aip_agents/tools/browser_use/streaming.py +815 -0
- aip_agents/tools/browser_use/streaming.pyi +81 -0
- aip_agents/tools/browser_use/structured_data_parser.py +257 -0
- aip_agents/tools/browser_use/structured_data_parser.pyi +86 -0
- aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
- aip_agents/tools/browser_use/structured_data_recovery.pyi +43 -0
- aip_agents/tools/browser_use/types.py +78 -0
- aip_agents/tools/browser_use/types.pyi +45 -0
- aip_agents/tools/code_sandbox/__init__.py +26 -0
- aip_agents/tools/code_sandbox/__init__.pyi +3 -0
- aip_agents/tools/code_sandbox/constant.py +13 -0
- aip_agents/tools/code_sandbox/constant.pyi +4 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +306 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +102 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +29 -0
- aip_agents/tools/constants.py +177 -0
- aip_agents/tools/constants.pyi +138 -0
- aip_agents/tools/date_range_tool.py +554 -0
- aip_agents/tools/date_range_tool.pyi +21 -0
- aip_agents/tools/document_loader/__init__.py +44 -0
- aip_agents/tools/document_loader/__init__.pyi +7 -0
- aip_agents/tools/document_loader/base_reader.py +302 -0
- aip_agents/tools/document_loader/base_reader.pyi +75 -0
- aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
- aip_agents/tools/document_loader/docx_reader_tool.pyi +10 -0
- aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
- aip_agents/tools/document_loader/excel_reader_tool.pyi +26 -0
- aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
- aip_agents/tools/document_loader/pdf_reader_tool.pyi +11 -0
- aip_agents/tools/document_loader/pdf_splitter.py +169 -0
- aip_agents/tools/document_loader/pdf_splitter.pyi +18 -0
- aip_agents/tools/execute_ptc_code.py +308 -0
- aip_agents/tools/execute_ptc_code.pyi +90 -0
- aip_agents/tools/gl_connector/__init__.py +5 -0
- aip_agents/tools/gl_connector/__init__.pyi +3 -0
- aip_agents/tools/gl_connector/tool.py +383 -0
- aip_agents/tools/gl_connector/tool.pyi +74 -0
- aip_agents/tools/gl_connector_tools.py +119 -0
- aip_agents/tools/gl_connector_tools.pyi +39 -0
- aip_agents/tools/memory_search/__init__.py +29 -0
- aip_agents/tools/memory_search/__init__.pyi +5 -0
- aip_agents/tools/memory_search/base.py +200 -0
- aip_agents/tools/memory_search/base.pyi +69 -0
- aip_agents/tools/memory_search/mem0.py +365 -0
- aip_agents/tools/memory_search/mem0.pyi +29 -0
- aip_agents/tools/memory_search/schema.py +81 -0
- aip_agents/tools/memory_search/schema.pyi +25 -0
- aip_agents/tools/memory_search_tool.py +34 -0
- aip_agents/tools/memory_search_tool.pyi +3 -0
- aip_agents/tools/time_tool.py +117 -0
- aip_agents/tools/time_tool.pyi +16 -0
- aip_agents/tools/tool_config_injector.py +300 -0
- aip_agents/tools/tool_config_injector.pyi +26 -0
- aip_agents/tools/web_search/__init__.py +15 -0
- aip_agents/tools/web_search/__init__.pyi +3 -0
- aip_agents/tools/web_search/serper_tool.py +187 -0
- aip_agents/tools/web_search/serper_tool.pyi +19 -0
- aip_agents/types/__init__.py +70 -0
- aip_agents/types/__init__.pyi +36 -0
- aip_agents/types/a2a_events.py +13 -0
- aip_agents/types/a2a_events.pyi +3 -0
- aip_agents/utils/__init__.py +79 -0
- aip_agents/utils/__init__.pyi +11 -0
- aip_agents/utils/a2a_connector.py +1757 -0
- aip_agents/utils/a2a_connector.pyi +146 -0
- aip_agents/utils/artifact_helpers.py +502 -0
- aip_agents/utils/artifact_helpers.pyi +203 -0
- aip_agents/utils/constants.py +22 -0
- aip_agents/utils/constants.pyi +10 -0
- aip_agents/utils/datetime/__init__.py +34 -0
- aip_agents/utils/datetime/__init__.pyi +4 -0
- aip_agents/utils/datetime/normalization.py +231 -0
- aip_agents/utils/datetime/normalization.pyi +95 -0
- aip_agents/utils/datetime/timezone.py +206 -0
- aip_agents/utils/datetime/timezone.pyi +48 -0
- aip_agents/utils/env_loader.py +27 -0
- aip_agents/utils/env_loader.pyi +10 -0
- aip_agents/utils/event_handler_registry.py +58 -0
- aip_agents/utils/event_handler_registry.pyi +23 -0
- aip_agents/utils/file_prompt_utils.py +176 -0
- aip_agents/utils/file_prompt_utils.pyi +21 -0
- aip_agents/utils/final_response_builder.py +211 -0
- aip_agents/utils/final_response_builder.pyi +34 -0
- aip_agents/utils/formatter_llm_client.py +231 -0
- aip_agents/utils/formatter_llm_client.pyi +71 -0
- aip_agents/utils/langgraph/__init__.py +19 -0
- aip_agents/utils/langgraph/__init__.pyi +3 -0
- aip_agents/utils/langgraph/converter.py +128 -0
- aip_agents/utils/langgraph/converter.pyi +49 -0
- aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
- aip_agents/utils/langgraph/tool_managers/__init__.pyi +5 -0
- aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
- aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +35 -0
- aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
- aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +48 -0
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1096 -0
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +56 -0
- aip_agents/utils/langgraph/tool_output_management.py +1047 -0
- aip_agents/utils/langgraph/tool_output_management.pyi +329 -0
- aip_agents/utils/logger.py +195 -0
- aip_agents/utils/logger.pyi +60 -0
- aip_agents/utils/metadata/__init__.py +27 -0
- aip_agents/utils/metadata/__init__.pyi +5 -0
- aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
- aip_agents/utils/metadata/activity_metadata_helper.pyi +25 -0
- aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
- aip_agents/utils/metadata/activity_narrative/__init__.pyi +7 -0
- aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
- aip_agents/utils/metadata/activity_narrative/builder.pyi +35 -0
- aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
- aip_agents/utils/metadata/activity_narrative/constants.pyi +10 -0
- aip_agents/utils/metadata/activity_narrative/context.py +49 -0
- aip_agents/utils/metadata/activity_narrative/context.pyi +32 -0
- aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
- aip_agents/utils/metadata/activity_narrative/formatters.pyi +48 -0
- aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
- aip_agents/utils/metadata/activity_narrative/utils.pyi +12 -0
- aip_agents/utils/metadata/schemas/__init__.py +16 -0
- aip_agents/utils/metadata/schemas/__init__.pyi +4 -0
- aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
- aip_agents/utils/metadata/schemas/activity_schema.pyi +18 -0
- aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
- aip_agents/utils/metadata/schemas/thinking_schema.pyi +20 -0
- aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
- aip_agents/utils/metadata/thinking_metadata_helper.pyi +4 -0
- aip_agents/utils/metadata_helper.py +358 -0
- aip_agents/utils/metadata_helper.pyi +117 -0
- aip_agents/utils/name_preprocessor/__init__.py +17 -0
- aip_agents/utils/name_preprocessor/__init__.pyi +6 -0
- aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
- aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +52 -0
- aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
- aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +38 -0
- aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
- aip_agents/utils/name_preprocessor/name_preprocessor.pyi +41 -0
- aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
- aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +34 -0
- aip_agents/utils/pii/__init__.py +25 -0
- aip_agents/utils/pii/__init__.pyi +5 -0
- aip_agents/utils/pii/pii_handler.py +397 -0
- aip_agents/utils/pii/pii_handler.pyi +96 -0
- aip_agents/utils/pii/pii_helper.py +207 -0
- aip_agents/utils/pii/pii_helper.pyi +78 -0
- aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
- aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +73 -0
- aip_agents/utils/reference_helper.py +273 -0
- aip_agents/utils/reference_helper.pyi +81 -0
- aip_agents/utils/sse_chunk_transformer.py +831 -0
- aip_agents/utils/sse_chunk_transformer.pyi +166 -0
- aip_agents/utils/step_limit_manager.py +265 -0
- aip_agents/utils/step_limit_manager.pyi +112 -0
- aip_agents/utils/token_usage_helper.py +156 -0
- aip_agents/utils/token_usage_helper.pyi +60 -0
- aip_agents_binary-0.6.4.dist-info/METADATA +673 -0
- aip_agents_binary-0.6.4.dist-info/RECORD +612 -0
- aip_agents_binary-0.6.4.dist-info/WHEEL +5 -0
- aip_agents_binary-0.6.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
"""LangGraph Memory Enhancer Agent.
|
|
2
|
+
|
|
3
|
+
This module implements the ``LangGraphMemoryEnhancerAgent``, a dedicated LangGraph helper agent
|
|
4
|
+
that automatically augments user queries with relevant memories before the primary agent runs.
|
|
5
|
+
It replaces manual memory tool invocation with a consistent preprocessing layer.
|
|
6
|
+
|
|
7
|
+
Authors:
|
|
8
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import textwrap
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from langchain_core.language_models import BaseChatModel
|
|
16
|
+
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
|
|
17
|
+
from langgraph.graph import END, StateGraph
|
|
18
|
+
from langgraph.graph.state import CompiledStateGraph
|
|
19
|
+
|
|
20
|
+
from aip_agents.agent.langgraph_react_agent import LangGraphReactAgent
|
|
21
|
+
from aip_agents.agent.system_instruction_context import get_current_date_context
|
|
22
|
+
from aip_agents.memory.guidance import MEM0_MEMORY_RECALL_GUIDANCE
|
|
23
|
+
from aip_agents.tools.memory_search_tool import (
|
|
24
|
+
MEMORY_DELETE_TOOL_NAME,
|
|
25
|
+
MEMORY_SEARCH_TOOL_NAME,
|
|
26
|
+
LongTermMemorySearchTool,
|
|
27
|
+
Mem0DeleteTool,
|
|
28
|
+
Mem0SearchTool,
|
|
29
|
+
)
|
|
30
|
+
from aip_agents.utils.langgraph import (
|
|
31
|
+
convert_langchain_messages_to_gllm_messages,
|
|
32
|
+
convert_lm_output_to_langchain_message,
|
|
33
|
+
)
|
|
34
|
+
from aip_agents.utils.logger import get_logger
|
|
35
|
+
|
|
36
|
+
logger = get_logger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LangGraphMemoryEnhancerAgent(LangGraphReactAgent):
|
|
40
|
+
"""Simplified mini-agent for automatic memory retrieval or deletion and query enhancement.
|
|
41
|
+
|
|
42
|
+
This agent has a simple 2-node LangGraph (agent + tools) and uses existing memory
|
|
43
|
+
infrastructure to enhance user queries with relevant context. It acts as a
|
|
44
|
+
preprocessing layer that automatically attempts memory retrieval for every query.
|
|
45
|
+
|
|
46
|
+
Key features:
|
|
47
|
+
- Uses runtime `memory_user_id` provided via call arguments (no static storage)
|
|
48
|
+
- Uses simplified instruction reusing existing guidance
|
|
49
|
+
- Standard 2-node LangGraph pattern (agent -> tools -> agent)
|
|
50
|
+
- Automatically enhances queries with memory context when available
|
|
51
|
+
- Returns original query unchanged if no relevant memories found
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, memory, **kwargs) -> None:
|
|
55
|
+
"""Initialize the LangGraphMemoryEnhancerAgent with memory backend and configuration.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
memory: Memory backend instance (Mem0Memory or compatible)
|
|
59
|
+
**kwargs: Additional arguments passed to BaseLangGraphAgent, including:
|
|
60
|
+
- memory_agent_id: Fallback user ID for memory operations
|
|
61
|
+
- model: LLM model to use for memory decisions
|
|
62
|
+
- Other BaseLangGraphAgent parameters
|
|
63
|
+
"""
|
|
64
|
+
memory_search_tool: LongTermMemorySearchTool = Mem0SearchTool(
|
|
65
|
+
memory=memory,
|
|
66
|
+
default_user_id=kwargs.get("memory_agent_id"),
|
|
67
|
+
user_id_provider=None,
|
|
68
|
+
)
|
|
69
|
+
memory_delete_tool: LongTermMemorySearchTool = Mem0DeleteTool(
|
|
70
|
+
memory=memory,
|
|
71
|
+
default_user_id=kwargs.get("memory_agent_id"),
|
|
72
|
+
user_id_provider=None,
|
|
73
|
+
)
|
|
74
|
+
kwargs["save_interaction_to_memory"] = False
|
|
75
|
+
super().__init__(
|
|
76
|
+
name="LangGraphMemoryEnhancerAgent",
|
|
77
|
+
instruction=self._build_simple_instruction(),
|
|
78
|
+
tools=[memory_search_tool, memory_delete_tool],
|
|
79
|
+
**kwargs,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def _build_simple_instruction(self) -> str:
|
|
83
|
+
"""Build simplified memory recall instruction reusing existing components.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
str: Complete instruction including date context and memory guidance
|
|
87
|
+
"""
|
|
88
|
+
date_context = get_current_date_context()
|
|
89
|
+
|
|
90
|
+
instruction = textwrap.dedent(f"""
|
|
91
|
+
{date_context}
|
|
92
|
+
|
|
93
|
+
You are a Memory Controller Agent that decides whether to retrieve or delete memory.
|
|
94
|
+
|
|
95
|
+
Important: You WILL NOT see the tool results. The system will either append retrieved memory
|
|
96
|
+
to the user input or return a memory action summary after your turn. Your sole responsibility
|
|
97
|
+
is to trigger the correct tool calls with concise arguments based on the user's message.
|
|
98
|
+
|
|
99
|
+
What to do:
|
|
100
|
+
1. Read the user's message as-is (do not rephrase it).
|
|
101
|
+
2. Decide which tool to call:
|
|
102
|
+
- Use `built_in_mem0_search` to retrieve memory for answering questions.
|
|
103
|
+
- Use `built_in_mem0_delete` when the user asks to forget/delete memories.
|
|
104
|
+
Prefer a single call, but you MAY make multiple calls when clearly needed.
|
|
105
|
+
- If the user implies a time frame (e.g., "yesterday", "last week"), set `start_date`/`end_date`.
|
|
106
|
+
- If the user implies a precise range, set `start_date`/`end_date` (YYYY-MM-DD).
|
|
107
|
+
- If the user mentions a topic, set a concise `query` (few words or at most a sentence).
|
|
108
|
+
- Adjust `limit` to higher number to allow more memory to be retrieved if needed.
|
|
109
|
+
- Default when uncertain: omit dates, set a concise `query` derived from the message,
|
|
110
|
+
and set `limit=10`.
|
|
111
|
+
3. Do NOT answer the user's question. Do NOT summarize. Do NOT format output. The system will handle it.
|
|
112
|
+
|
|
113
|
+
Constraints:
|
|
114
|
+
- Keep tool arguments succinct and precise; avoid verbose or speculative queries.
|
|
115
|
+
- Never invent facts. If unsure about time ranges, prefer omitting dates rather than fabricating.
|
|
116
|
+
- Do not include any preambles or explanations in your messages.
|
|
117
|
+
- Make one or more tool calls as needed; avoid duplicates or redundant calls.
|
|
118
|
+
|
|
119
|
+
Reference guidance:
|
|
120
|
+
{MEM0_MEMORY_RECALL_GUIDANCE}
|
|
121
|
+
""").strip()
|
|
122
|
+
|
|
123
|
+
return instruction
|
|
124
|
+
|
|
125
|
+
async def _memory_retrieval_node(self, state: dict, config: dict | None = None) -> dict:
|
|
126
|
+
"""Execute memory retrieval or deletion using explicit tool calls or synthesized defaults.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
state: LangGraph state containing the conversation `messages` history.
|
|
130
|
+
config: Optional LangGraph configuration forwarded to the memory tool.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
dict: State update whose `messages` list contains `ToolMessage` outputs.
|
|
134
|
+
"""
|
|
135
|
+
messages = state.get("messages", [])
|
|
136
|
+
tool_calls = self._extract_mem0_tool_calls(messages)
|
|
137
|
+
|
|
138
|
+
if tool_calls:
|
|
139
|
+
tool_messages = await self._execute_mem0_tool_calls(tool_calls, state, config)
|
|
140
|
+
return {"messages": tool_messages}
|
|
141
|
+
|
|
142
|
+
default_query = self._extract_last_human_query(messages)
|
|
143
|
+
tool_messages = await self._execute_default_retrieval(default_query, state, config)
|
|
144
|
+
return {"messages": tool_messages}
|
|
145
|
+
|
|
146
|
+
def _extract_mem0_tool_calls(self, messages: list) -> list[dict[str, Any]]:
|
|
147
|
+
"""Return all Mem0 tool calls from the last message if present.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
messages: Ordered list of LangChain message objects representing the state.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
List of tool call dictionaries filtered for the Mem0 search tool.
|
|
154
|
+
"""
|
|
155
|
+
if not messages:
|
|
156
|
+
return []
|
|
157
|
+
|
|
158
|
+
last_message = messages[-1]
|
|
159
|
+
tool_calls = getattr(last_message, "tool_calls", None)
|
|
160
|
+
if not tool_calls:
|
|
161
|
+
return []
|
|
162
|
+
|
|
163
|
+
return [tc for tc in tool_calls if tc.get("name") in {MEMORY_SEARCH_TOOL_NAME, MEMORY_DELETE_TOOL_NAME}]
|
|
164
|
+
|
|
165
|
+
async def _execute_mem0_tool_calls(
|
|
166
|
+
self,
|
|
167
|
+
tool_calls: list[dict[str, Any]],
|
|
168
|
+
state: dict,
|
|
169
|
+
config: dict | None,
|
|
170
|
+
) -> list[ToolMessage]:
|
|
171
|
+
"""Execute the provided Mem0 tool calls and return their messages.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
tool_calls: Tool call dictionaries emitted by the LLM.
|
|
175
|
+
state: LangGraph state containing messages and metadata.
|
|
176
|
+
config: Optional runnable configuration forwarded to the tool.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
List of `ToolMessage` objects describing each execution result.
|
|
180
|
+
"""
|
|
181
|
+
tool_messages: list[ToolMessage] = []
|
|
182
|
+
delete_intent: dict[str, Any] | None = None
|
|
183
|
+
user_query = self._extract_last_human_query(state.get("messages", [])) or self._fallback_query(
|
|
184
|
+
state.get("messages", [])
|
|
185
|
+
)
|
|
186
|
+
for index, tool_call in enumerate(tool_calls):
|
|
187
|
+
tool_name = tool_call.get("name") or MEMORY_SEARCH_TOOL_NAME
|
|
188
|
+
args = dict(tool_call.get("args") or {})
|
|
189
|
+
if "id" not in args and "id" in tool_call:
|
|
190
|
+
args["id"] = tool_call["id"]
|
|
191
|
+
log_args = self._redact_mem0_args(tool_name, args)
|
|
192
|
+
logger.info("Executing memory tool call #%s name=%s args=%s", index, tool_name, log_args)
|
|
193
|
+
if tool_name == MEMORY_DELETE_TOOL_NAME:
|
|
194
|
+
delete_intent = delete_intent or await self._preprocess_delete_intent(user_query, state, config)
|
|
195
|
+
if not self._is_delete_intent_confirmed(delete_intent):
|
|
196
|
+
tool_messages.append(self._build_delete_confirmation_message(tool_call, user_query))
|
|
197
|
+
continue
|
|
198
|
+
tool_messages.append(await self._execute_mem0_call(tool_name, args, state, config))
|
|
199
|
+
return tool_messages
|
|
200
|
+
|
|
201
|
+
def _redact_mem0_args(self, tool_name: str, args: dict[str, Any]) -> dict[str, Any]:
|
|
202
|
+
"""Redact sensitive fields from Mem0 tool args before logging."""
|
|
203
|
+
if tool_name != MEMORY_DELETE_TOOL_NAME:
|
|
204
|
+
return args
|
|
205
|
+
|
|
206
|
+
redacted_args = dict(args)
|
|
207
|
+
if "memory_ids" in redacted_args:
|
|
208
|
+
memory_ids = redacted_args.pop("memory_ids")
|
|
209
|
+
if isinstance(memory_ids, list):
|
|
210
|
+
redacted_args["memory_ids_count"] = len(memory_ids)
|
|
211
|
+
else:
|
|
212
|
+
redacted_args["memory_ids_count"] = 0
|
|
213
|
+
return redacted_args
|
|
214
|
+
|
|
215
|
+
async def _execute_default_retrieval(
|
|
216
|
+
self,
|
|
217
|
+
default_query: str | None,
|
|
218
|
+
state: dict,
|
|
219
|
+
config: dict | None,
|
|
220
|
+
) -> list[ToolMessage]:
|
|
221
|
+
"""Perform a default retrieval when the LLM does not request tools.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
default_query: Latest human utterance content or ``None`` if unavailable.
|
|
225
|
+
state: LangGraph state with message history and metadata.
|
|
226
|
+
config: Optional runnable configuration forwarded to the tool.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Single-item list containing the resulting `ToolMessage`.
|
|
230
|
+
"""
|
|
231
|
+
args = self._build_default_mem0_args(default_query)
|
|
232
|
+
tool_message = await self._execute_mem0_call(MEMORY_SEARCH_TOOL_NAME, args, state, config)
|
|
233
|
+
return [tool_message]
|
|
234
|
+
|
|
235
|
+
def _build_default_mem0_args(self, query: str | None) -> dict[str, Any]:
|
|
236
|
+
"""Create safe default arguments for the Mem0 search tool.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
query: Latest human utterance used to derive the search query.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Dictionary of keyword arguments passed to the Mem0 search tool.
|
|
243
|
+
"""
|
|
244
|
+
if query:
|
|
245
|
+
trimmed_query = query[:128]
|
|
246
|
+
else:
|
|
247
|
+
trimmed_query = None
|
|
248
|
+
|
|
249
|
+
return {"query": trimmed_query, "limit": 10}
|
|
250
|
+
|
|
251
|
+
async def _execute_mem0_call(
|
|
252
|
+
self,
|
|
253
|
+
tool_name: str,
|
|
254
|
+
args: dict[str, Any],
|
|
255
|
+
state: dict,
|
|
256
|
+
config: dict | None,
|
|
257
|
+
) -> ToolMessage:
|
|
258
|
+
"""Execute a single Mem0 tool call with metadata resolution.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
tool_name: Name of the memory tool to invoke.
|
|
262
|
+
args: Base arguments supplied by the LLM or synthesized defaults.
|
|
263
|
+
state: LangGraph state that may include additional metadata.
|
|
264
|
+
config: Optional runnable configuration forwarded to the tool.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
`ToolMessage` containing raw tool output or an error description.
|
|
268
|
+
"""
|
|
269
|
+
args_with_metadata = self._merge_metadata(args, state, tool_name)
|
|
270
|
+
tool_config = self._create_tool_config(config, state, tool_name=tool_name)
|
|
271
|
+
try:
|
|
272
|
+
mem0_tool = self._get_tool_by_name(tool_name)
|
|
273
|
+
result = await mem0_tool.ainvoke(args_with_metadata, config=tool_config)
|
|
274
|
+
content = str(result)
|
|
275
|
+
except Exception as exc:
|
|
276
|
+
content = f"Error executing memory tool '{tool_name}': {exc}"
|
|
277
|
+
|
|
278
|
+
return ToolMessage(content=content, tool_call_id=args.get("id", ""))
|
|
279
|
+
|
|
280
|
+
def _merge_metadata(self, args: dict[str, Any], state: dict, tool_name: str) -> dict[str, Any]:
|
|
281
|
+
"""Merge resolved metadata into tool arguments.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
args: Tool arguments that may already include metadata.
|
|
285
|
+
state: LangGraph state providing globally resolved metadata values.
|
|
286
|
+
tool_name: Name of the tool requesting metadata (used to resolve tool-specific metadata).
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Copy of ``args`` containing merged metadata entries.
|
|
290
|
+
"""
|
|
291
|
+
args_with_metadata = dict(args)
|
|
292
|
+
effective_metadata = self._resolve_effective_metadata(state, tool_name)
|
|
293
|
+
if not effective_metadata:
|
|
294
|
+
return args_with_metadata
|
|
295
|
+
|
|
296
|
+
existing_metadata = args_with_metadata.get("metadata")
|
|
297
|
+
if isinstance(existing_metadata, dict):
|
|
298
|
+
merged_metadata = {**effective_metadata, **existing_metadata}
|
|
299
|
+
else:
|
|
300
|
+
merged_metadata = effective_metadata
|
|
301
|
+
|
|
302
|
+
args_with_metadata["metadata"] = merged_metadata
|
|
303
|
+
return args_with_metadata
|
|
304
|
+
|
|
305
|
+
def _resolve_effective_metadata(self, state: dict, tool_name: str) -> dict[str, Any] | None:
|
|
306
|
+
"""Resolve metadata for the Mem0 tool, swallowing resolution errors.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
state: LangGraph state whose ``metadata`` key may include overrides.
|
|
310
|
+
tool_name: Name of the tool whose metadata resolution strategy should be used.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Resolved metadata dictionary or ``None`` if not available.
|
|
314
|
+
"""
|
|
315
|
+
raw_metadata = state.get("metadata")
|
|
316
|
+
if not isinstance(raw_metadata, dict):
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
return self._resolve_tool_metadata(tool_name, raw_metadata)
|
|
321
|
+
except Exception:
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
def _extract_last_human_query(self, messages: list) -> str | None:
|
|
325
|
+
"""Return the content of the most recent `HumanMessage` if available.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
messages: Ordered message history produced during the graph run.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
Text content of the last human message or ``None``.
|
|
332
|
+
"""
|
|
333
|
+
for message in reversed(messages):
|
|
334
|
+
if isinstance(message, HumanMessage):
|
|
335
|
+
if isinstance(message.content, str):
|
|
336
|
+
return message.content
|
|
337
|
+
return str(message.content)
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
def _finalize_node(self, state: dict) -> dict:
|
|
341
|
+
"""Assemble the enhanced query returned by the memory recall agent.
|
|
342
|
+
|
|
343
|
+
Collects raw memory results from all tool calls, deduplicates by memory ID,
|
|
344
|
+
formats the unique memories, and combines with the original user query.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
state: LangGraph state containing the original conversation messages and the
|
|
348
|
+
tool outputs generated by `_memory_retrieval_node`.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
dict: State update with a single `AIMessage` that concatenates the original user
|
|
352
|
+
query and any deduplicated memory context.
|
|
353
|
+
"""
|
|
354
|
+
messages = state.get("messages", [])
|
|
355
|
+
original_query = self._extract_last_human_query(messages) or self._fallback_query(messages)
|
|
356
|
+
delete_action = self._extract_delete_action(messages)
|
|
357
|
+
if delete_action:
|
|
358
|
+
action_block = self._format_memory_action(delete_action)
|
|
359
|
+
return {"messages": [AIMessage(content=action_block)]}
|
|
360
|
+
|
|
361
|
+
delete_error = self._extract_delete_error(messages)
|
|
362
|
+
if delete_error:
|
|
363
|
+
action_block = self._format_memory_action_error(delete_error)
|
|
364
|
+
return {"messages": [AIMessage(content=action_block)]}
|
|
365
|
+
|
|
366
|
+
memories = self._collect_unique_memories(messages)
|
|
367
|
+
tagged_memory = self._format_memories(memories)
|
|
368
|
+
|
|
369
|
+
final_text = (f"{original_query}\n\n" + tagged_memory).strip()
|
|
370
|
+
return {"messages": [AIMessage(content=final_text)]}
|
|
371
|
+
|
|
372
|
+
def _fallback_query(self, messages: list) -> str:
|
|
373
|
+
"""Fallback to the last message content when no human message is present.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
messages: Ordered message history produced during the graph run.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
The string representation of the last message content.
|
|
380
|
+
"""
|
|
381
|
+
if not messages:
|
|
382
|
+
return ""
|
|
383
|
+
last_message = messages[-1]
|
|
384
|
+
content = getattr(last_message, "content", "")
|
|
385
|
+
return content if isinstance(content, str) else str(content)
|
|
386
|
+
|
|
387
|
+
def _collect_unique_memories(self, messages: list) -> list[dict[str, Any]]:
|
|
388
|
+
"""Collect and deduplicate memory hits from tool messages.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
messages: Ordered message history produced during the graph run.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
List of memory dictionaries with unique memory identifiers.
|
|
395
|
+
"""
|
|
396
|
+
unique_memories: list[dict[str, Any]] = []
|
|
397
|
+
seen_ids: set[str] = set()
|
|
398
|
+
|
|
399
|
+
for message in messages:
|
|
400
|
+
for memory in self._extract_memories_from_message(message):
|
|
401
|
+
memory_id = memory.get("id")
|
|
402
|
+
if not memory_id or memory_id in seen_ids:
|
|
403
|
+
continue
|
|
404
|
+
|
|
405
|
+
seen_ids.add(memory_id)
|
|
406
|
+
unique_memories.append(memory)
|
|
407
|
+
|
|
408
|
+
return unique_memories
|
|
409
|
+
|
|
410
|
+
def _extract_memories_from_message(self, message: Any) -> list[dict[str, Any]]:
|
|
411
|
+
"""Return parsed memory dictionaries contained in a tool message.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
message: Message instance that may contain memory tool output.
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
List of memory dictionaries or an empty list when no memories are present.
|
|
418
|
+
"""
|
|
419
|
+
if not isinstance(message, ToolMessage):
|
|
420
|
+
return []
|
|
421
|
+
|
|
422
|
+
raw_results = self._parse_tool_message_json(message)
|
|
423
|
+
if isinstance(raw_results, list):
|
|
424
|
+
return [memory for memory in raw_results if isinstance(memory, dict)]
|
|
425
|
+
return []
|
|
426
|
+
|
|
427
|
+
def _parse_tool_message_json(self, message: ToolMessage) -> Any:
|
|
428
|
+
"""Parse the JSON content of a tool message.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
message: Tool message emitted by the memory search tool.
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
List extracted from the tool message content or an empty list on failure.
|
|
435
|
+
"""
|
|
436
|
+
try:
|
|
437
|
+
raw_results = json.loads(message.content)
|
|
438
|
+
except (json.JSONDecodeError, TypeError) as exc:
|
|
439
|
+
logger.warning(
|
|
440
|
+
"Failed to parse tool result as JSON: %s, content: %s...",
|
|
441
|
+
exc,
|
|
442
|
+
message.content[:200],
|
|
443
|
+
)
|
|
444
|
+
return None
|
|
445
|
+
|
|
446
|
+
return raw_results
|
|
447
|
+
|
|
448
|
+
def _extract_delete_action(self, messages: list) -> dict[str, Any] | None:
|
|
449
|
+
"""Return delete action details if a delete tool message is present.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
messages: Ordered message history produced during the graph run.
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Action dict or None when no delete action is detected.
|
|
456
|
+
"""
|
|
457
|
+
for message in messages:
|
|
458
|
+
if not isinstance(message, ToolMessage):
|
|
459
|
+
continue
|
|
460
|
+
raw_payload = self._parse_tool_message_json(message)
|
|
461
|
+
if not isinstance(raw_payload, dict):
|
|
462
|
+
continue
|
|
463
|
+
status = raw_payload.get("status")
|
|
464
|
+
if status == "success" and raw_payload.get("mode"):
|
|
465
|
+
return raw_payload
|
|
466
|
+
if status == "needs_confirmation":
|
|
467
|
+
return raw_payload
|
|
468
|
+
return None
|
|
469
|
+
|
|
470
|
+
def _format_memory_action(self, action: dict[str, Any]) -> str:
|
|
471
|
+
"""Format a memory action block for delete results.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
action: Parsed action payload from the delete tool.
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
Formatted action block string.
|
|
478
|
+
"""
|
|
479
|
+
status = action.get("status", "success")
|
|
480
|
+
summary = action.get("summary")
|
|
481
|
+
if status == "needs_confirmation":
|
|
482
|
+
summary = summary or "Do you want me to delete the related memories?"
|
|
483
|
+
else:
|
|
484
|
+
mode = action.get("mode", "unknown")
|
|
485
|
+
result = action.get("result")
|
|
486
|
+
summary = summary or f"Deleted memories (mode: {mode})."
|
|
487
|
+
if isinstance(result, dict):
|
|
488
|
+
count = result.get("count") or result.get("deleted") or result.get("total")
|
|
489
|
+
if count is not None:
|
|
490
|
+
summary = f"Deleted {count} memories (mode: {mode})."
|
|
491
|
+
return "\n".join(
|
|
492
|
+
[
|
|
493
|
+
"<MEMORY_ACTION>",
|
|
494
|
+
"action=delete",
|
|
495
|
+
f"status={status}",
|
|
496
|
+
f"summary={summary}",
|
|
497
|
+
"</MEMORY_ACTION>",
|
|
498
|
+
]
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
def _extract_delete_error(self, messages: list) -> str | None:
|
|
502
|
+
"""Return delete error summary if delete tool failed.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
messages: Ordered message history produced during the graph run.
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
Error summary string or None when no delete error is detected.
|
|
509
|
+
"""
|
|
510
|
+
for message in messages:
|
|
511
|
+
if not isinstance(message, ToolMessage):
|
|
512
|
+
continue
|
|
513
|
+
content = message.content if isinstance(message.content, str) else str(message.content)
|
|
514
|
+
if MEMORY_DELETE_TOOL_NAME in content and "Error" in content:
|
|
515
|
+
return content[:200]
|
|
516
|
+
return None
|
|
517
|
+
|
|
518
|
+
def _format_memory_action_error(self, error_summary: str) -> str:
|
|
519
|
+
"""Format a memory action block for delete errors."""
|
|
520
|
+
safe_summary = error_summary.replace("\n", " ").strip()
|
|
521
|
+
return "\n".join(
|
|
522
|
+
[
|
|
523
|
+
"<MEMORY_ACTION>",
|
|
524
|
+
"action=delete",
|
|
525
|
+
"status=error",
|
|
526
|
+
f"summary={safe_summary}",
|
|
527
|
+
"</MEMORY_ACTION>",
|
|
528
|
+
]
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
def _get_tool_by_name(self, tool_name: str) -> Any:
|
|
532
|
+
"""Return the resolved tool instance by name."""
|
|
533
|
+
for tool in self.resolved_tools:
|
|
534
|
+
if tool.name == tool_name:
|
|
535
|
+
return tool
|
|
536
|
+
raise ValueError(f"Tool '{tool_name}' not found in resolved tools.")
|
|
537
|
+
|
|
538
|
+
async def _preprocess_delete_intent(
|
|
539
|
+
self,
|
|
540
|
+
query: str | None,
|
|
541
|
+
state: dict,
|
|
542
|
+
config: dict | None,
|
|
543
|
+
) -> dict[str, Any]:
|
|
544
|
+
"""Run a pre-processing intent check for delete requests.
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
query: Latest user query.
|
|
548
|
+
state: LangGraph state containing metadata for the request.
|
|
549
|
+
config: Optional runnable configuration forwarded to the model.
|
|
550
|
+
|
|
551
|
+
Returns:
|
|
552
|
+
Normalized intent payload with intent/confidence/reason keys.
|
|
553
|
+
"""
|
|
554
|
+
if not isinstance(query, str) or not query.strip():
|
|
555
|
+
return {"intent": "unknown", "confidence": "low", "reason": "empty_query"}
|
|
556
|
+
|
|
557
|
+
raw_response = await self._invoke_delete_intent_model(query, state, config)
|
|
558
|
+
return self._parse_delete_intent_payload(raw_response)
|
|
559
|
+
|
|
560
|
+
async def _invoke_delete_intent_model(
|
|
561
|
+
self,
|
|
562
|
+
query: str,
|
|
563
|
+
state: dict,
|
|
564
|
+
config: dict | None,
|
|
565
|
+
) -> str:
|
|
566
|
+
"""Invoke the configured model to classify delete intent.
|
|
567
|
+
|
|
568
|
+
Args:
|
|
569
|
+
query: User query to classify.
|
|
570
|
+
state: LangGraph state containing request metadata.
|
|
571
|
+
config: Optional runnable configuration forwarded to the model.
|
|
572
|
+
|
|
573
|
+
Returns:
|
|
574
|
+
Raw model output string, or empty string on failure.
|
|
575
|
+
"""
|
|
576
|
+
instruction = self._build_delete_intent_instruction()
|
|
577
|
+
effective_event_emitter = state.get("event_emitter") or self.event_emitter
|
|
578
|
+
if self.lm_invoker is not None:
|
|
579
|
+
return await self._invoke_delete_intent_with_invoker(query, instruction, effective_event_emitter)
|
|
580
|
+
|
|
581
|
+
if isinstance(self.model, BaseChatModel):
|
|
582
|
+
return await self._invoke_delete_intent_with_chat_model(query, instruction, config)
|
|
583
|
+
|
|
584
|
+
logger.warning("Delete intent check skipped; no model configured.")
|
|
585
|
+
return ""
|
|
586
|
+
|
|
587
|
+
async def _invoke_delete_intent_with_invoker(
|
|
588
|
+
self,
|
|
589
|
+
query: str,
|
|
590
|
+
instruction: str,
|
|
591
|
+
event_emitter: Any,
|
|
592
|
+
) -> str:
|
|
593
|
+
"""Invoke delete intent check using an LM invoker."""
|
|
594
|
+
messages = convert_langchain_messages_to_gllm_messages([HumanMessage(content=query)], instruction)
|
|
595
|
+
restore_tools = self.resolved_tools if self.resolved_tools else None
|
|
596
|
+
if restore_tools is not None:
|
|
597
|
+
self.lm_invoker.set_tools([])
|
|
598
|
+
try:
|
|
599
|
+
lm_output = await self.lm_invoker.invoke(messages=messages, event_emitter=event_emitter)
|
|
600
|
+
except Exception as exc:
|
|
601
|
+
logger.warning("Delete intent check failed: %s", exc)
|
|
602
|
+
return ""
|
|
603
|
+
finally:
|
|
604
|
+
if restore_tools is not None:
|
|
605
|
+
self.lm_invoker.set_tools(restore_tools)
|
|
606
|
+
|
|
607
|
+
ai_message = convert_lm_output_to_langchain_message(lm_output)
|
|
608
|
+
return self._coerce_message_content(ai_message)
|
|
609
|
+
|
|
610
|
+
async def _invoke_delete_intent_with_chat_model(
|
|
611
|
+
self,
|
|
612
|
+
query: str,
|
|
613
|
+
instruction: str,
|
|
614
|
+
config: dict | None,
|
|
615
|
+
) -> str:
|
|
616
|
+
"""Invoke delete intent check using a LangChain chat model."""
|
|
617
|
+
prompt = [SystemMessage(content=instruction), HumanMessage(content=query)]
|
|
618
|
+
try:
|
|
619
|
+
ai_message = await self.model.ainvoke(prompt, config)
|
|
620
|
+
except Exception as exc:
|
|
621
|
+
logger.warning("Delete intent check failed: %s", exc)
|
|
622
|
+
return ""
|
|
623
|
+
return self._coerce_message_content(ai_message)
|
|
624
|
+
|
|
625
|
+
def _parse_delete_intent_payload(self, content: str) -> dict[str, Any]:
|
|
626
|
+
"""Parse delete intent payload from model output."""
|
|
627
|
+
default_payload = {"intent": "unknown", "confidence": "low", "reason": "unparsed"}
|
|
628
|
+
if not isinstance(content, str) or not content.strip():
|
|
629
|
+
return default_payload
|
|
630
|
+
|
|
631
|
+
payload = self._extract_json_payload(content)
|
|
632
|
+
if not isinstance(payload, dict):
|
|
633
|
+
return default_payload
|
|
634
|
+
|
|
635
|
+
return self._normalize_delete_intent_payload(payload, default_payload)
|
|
636
|
+
|
|
637
|
+
def _extract_json_payload(self, content: str) -> dict[str, Any] | None:
|
|
638
|
+
"""Extract a JSON payload from a raw string."""
|
|
639
|
+
raw_text = content.strip()
|
|
640
|
+
if raw_text.startswith("```"):
|
|
641
|
+
raw_text = raw_text.strip("`")
|
|
642
|
+
if raw_text.lower().startswith("json"):
|
|
643
|
+
raw_text = raw_text[4:].strip()
|
|
644
|
+
|
|
645
|
+
try:
|
|
646
|
+
return json.loads(raw_text)
|
|
647
|
+
except json.JSONDecodeError:
|
|
648
|
+
start = raw_text.find("{")
|
|
649
|
+
end = raw_text.rfind("}")
|
|
650
|
+
if start == -1 or end == -1 or end <= start:
|
|
651
|
+
return None
|
|
652
|
+
try:
|
|
653
|
+
return json.loads(raw_text[start : end + 1])
|
|
654
|
+
except json.JSONDecodeError:
|
|
655
|
+
return None
|
|
656
|
+
|
|
657
|
+
def _normalize_delete_intent_payload(
|
|
658
|
+
self,
|
|
659
|
+
payload: dict[str, Any],
|
|
660
|
+
default_payload: dict[str, str],
|
|
661
|
+
) -> dict[str, Any]:
|
|
662
|
+
"""Normalize payload keys and guard against invalid values."""
|
|
663
|
+
intent = str(payload.get("intent", "")).lower()
|
|
664
|
+
confidence = str(payload.get("confidence", "")).lower()
|
|
665
|
+
if intent not in {"delete", "retrieve", "unknown"}:
|
|
666
|
+
intent = "unknown"
|
|
667
|
+
if confidence not in {"high", "medium", "low"}:
|
|
668
|
+
confidence = "low"
|
|
669
|
+
|
|
670
|
+
reason = payload.get("reason")
|
|
671
|
+
if not isinstance(reason, str):
|
|
672
|
+
reason = default_payload["reason"]
|
|
673
|
+
|
|
674
|
+
return {"intent": intent, "confidence": confidence, "reason": reason}
|
|
675
|
+
|
|
676
|
+
@staticmethod
|
|
677
|
+
def _coerce_message_content(message: AIMessage) -> str:
|
|
678
|
+
"""Normalize AI message content into a string."""
|
|
679
|
+
content = message.content
|
|
680
|
+
return content if isinstance(content, str) else str(content)
|
|
681
|
+
|
|
682
|
+
def _build_delete_intent_instruction(self) -> str:
|
|
683
|
+
"""Return the system prompt for delete intent classification.
|
|
684
|
+
|
|
685
|
+
Design rationale:
|
|
686
|
+
- Require JSON-only output for deterministic parsing.
|
|
687
|
+
- Use intent labels (delete|retrieve|unknown) to avoid keyword false positives.
|
|
688
|
+
- Gate deletion on high confidence to keep ambiguous requests safe.
|
|
689
|
+
|
|
690
|
+
Tuning guidance:
|
|
691
|
+
- Add examples if delete intents are missed.
|
|
692
|
+
- Adjust confidence thresholds if false negatives become frequent.
|
|
693
|
+
"""
|
|
694
|
+
return (
|
|
695
|
+
"You are a memory deletion intent checker. Determine whether the user is asking to "
|
|
696
|
+
"delete/forget memories stored about them. Reply with JSON only: "
|
|
697
|
+
'{"intent": "delete|retrieve|unknown", "confidence": "high|medium|low", '
|
|
698
|
+
'"reason": "short"}. '
|
|
699
|
+
"If unsure, respond with intent unknown and low confidence."
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
def _is_delete_intent_confirmed(self, decision: dict[str, Any] | None) -> bool:
|
|
703
|
+
"""Return True when delete intent is confirmed by pre-processing."""
|
|
704
|
+
if not isinstance(decision, dict):
|
|
705
|
+
logger.warning("Delete intent check failed: decision is not a dict.")
|
|
706
|
+
return False
|
|
707
|
+
intent = decision.get("intent")
|
|
708
|
+
confidence = decision.get("confidence")
|
|
709
|
+
reason = decision.get("reason", "unknown")
|
|
710
|
+
if intent != "delete":
|
|
711
|
+
logger.info("Delete intent not confirmed: intent=%s reason=%s.", intent, reason)
|
|
712
|
+
return False
|
|
713
|
+
if confidence != "high":
|
|
714
|
+
logger.info("Delete intent not confirmed: confidence=%s reason=%s.", confidence, reason)
|
|
715
|
+
return False
|
|
716
|
+
return True
|
|
717
|
+
|
|
718
|
+
def _build_delete_confirmation_message(self, tool_call: dict[str, Any], query: str | None) -> ToolMessage:
|
|
719
|
+
"""Return a ToolMessage asking for delete confirmation."""
|
|
720
|
+
summary = "Do you want me to delete the related memories?"
|
|
721
|
+
if isinstance(query, str) and query.strip():
|
|
722
|
+
trimmed = query.strip()
|
|
723
|
+
if len(trimmed) > 160:
|
|
724
|
+
trimmed = f"{trimmed[:157]}..."
|
|
725
|
+
summary = f"Do you want me to delete memories related to: '{trimmed}'?"
|
|
726
|
+
payload = {"status": "needs_confirmation", "summary": summary}
|
|
727
|
+
return ToolMessage(content=json.dumps(payload), tool_call_id=tool_call.get("id", ""))
|
|
728
|
+
|
|
729
|
+
def _format_memories(self, memories: list[dict[str, Any]]) -> str:
|
|
730
|
+
"""Format memory hits using the underlying tool formatter.
|
|
731
|
+
|
|
732
|
+
Args:
|
|
733
|
+
memories: Deduplicated list of memory dictionaries.
|
|
734
|
+
|
|
735
|
+
Returns:
|
|
736
|
+
Tagged string representation of the relevant memories.
|
|
737
|
+
"""
|
|
738
|
+
if not memories:
|
|
739
|
+
return ""
|
|
740
|
+
return self.resolved_tools[0].format_hits(memories, with_tag=True)
|
|
741
|
+
|
|
742
|
+
def define_graph(self, graph_builder: StateGraph) -> CompiledStateGraph:
|
|
743
|
+
"""Define the 3-node memory recall LangGraph for this agent.
|
|
744
|
+
|
|
745
|
+
This creates a streamlined ReAct-inspired structure that reuses
|
|
746
|
+
`LangGraphReactAgent` helpers for robust LM invocation, token usage tracking,
|
|
747
|
+
error handling, and tool execution.
|
|
748
|
+
|
|
749
|
+
Args:
|
|
750
|
+
graph_builder: LangGraph `StateGraph` builder instance used to register nodes and
|
|
751
|
+
edges for compilation.
|
|
752
|
+
|
|
753
|
+
Returns:
|
|
754
|
+
CompiledStateGraph: The compiled memory recall graph ready for execution.
|
|
755
|
+
"""
|
|
756
|
+
# Reuse parent's robust node implementations
|
|
757
|
+
# Simple 3-step structure for single pass: agent -> memory_retrieval -> finalize -> END
|
|
758
|
+
agent_node = self._create_agent_node() # Handles LM invoker + LangChain + token usage
|
|
759
|
+
graph_builder.add_node("agent", agent_node)
|
|
760
|
+
graph_builder.add_node("memory_retrieval", self._memory_retrieval_node)
|
|
761
|
+
graph_builder.add_node("finalize", self._finalize_node)
|
|
762
|
+
graph_builder.add_edge("agent", "memory_retrieval")
|
|
763
|
+
graph_builder.add_edge("memory_retrieval", "finalize")
|
|
764
|
+
graph_builder.add_edge("finalize", END)
|
|
765
|
+
graph_builder.set_entry_point("agent")
|
|
766
|
+
|
|
767
|
+
return graph_builder.compile(checkpointer=self.checkpointer)
|