aip-agents-binary 0.5.20__py3-none-manylinux_2_31_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aip_agents/__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 +2942 -0
- aip_agents/agent/base_langgraph_agent.pyi +232 -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 +433 -0
- aip_agents/agent/langgraph_memory_enhancer_agent.pyi +49 -0
- aip_agents/agent/langgraph_react_agent.py +2514 -0
- aip_agents/agent/langgraph_react_agent.pyi +126 -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/demo_memory_recall.py +401 -0
- aip_agents/examples/demo_memory_recall.pyi +58 -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_bosa_twitter.py +41 -0
- aip_agents/examples/hello_world_langgraph_bosa_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_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 +46 -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/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 +193 -0
- aip_agents/mcp/client/connection_manager.pyi +48 -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 +359 -0
- aip_agents/mcp/client/persistent_session.pyi +113 -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 +215 -0
- aip_agents/mcp/client/transports.pyi +123 -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 +717 -0
- aip_agents/memory/adapters/base_adapter.pyi +150 -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 +88 -0
- aip_agents/middleware/base.pyi +71 -0
- aip_agents/middleware/manager.py +128 -0
- aip_agents/middleware/manager.pyi +80 -0
- aip_agents/middleware/todolist.py +274 -0
- aip_agents/middleware/todolist.pyi +125 -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 +33 -0
- aip_agents/tools/__init__.pyi +13 -0
- aip_agents/tools/bosa_tools.py +105 -0
- aip_agents/tools/bosa_tools.pyi +37 -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 +1112 -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 +813 -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 +257 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +86 -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 +165 -0
- aip_agents/tools/constants.pyi +135 -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/gl_connector/__init__.py +5 -0
- aip_agents/tools/gl_connector/__init__.pyi +3 -0
- aip_agents/tools/gl_connector/tool.py +351 -0
- aip_agents/tools/gl_connector/tool.pyi +74 -0
- aip_agents/tools/memory_search/__init__.py +22 -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 +258 -0
- aip_agents/tools/memory_search/mem0.pyi +19 -0
- aip_agents/tools/memory_search/schema.py +48 -0
- aip_agents/tools/memory_search/schema.pyi +15 -0
- aip_agents/tools/memory_search_tool.py +26 -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 +1071 -0
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +56 -0
- aip_agents/utils/langgraph/tool_output_management.py +967 -0
- aip_agents/utils/langgraph/tool_output_management.pyi +292 -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.5.20.dist-info/METADATA +681 -0
- aip_agents_binary-0.5.20.dist-info/RECORD +546 -0
- aip_agents_binary-0.5.20.dist-info/WHEEL +5 -0
- aip_agents_binary-0.5.20.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1071 @@
|
|
|
1
|
+
"""Delegation tool manager for LangGraph agents.
|
|
2
|
+
|
|
3
|
+
This module provides the DelegationToolManager class that converts internal
|
|
4
|
+
agent instances into LangChain tools for delegation within LangGraph agents.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import uuid
|
|
8
|
+
from contextvars import ContextVar
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from langchain_core.runnables import RunnableConfig
|
|
12
|
+
from langchain_core.tools import BaseTool, tool
|
|
13
|
+
from langgraph.config import get_stream_writer
|
|
14
|
+
from langgraph.types import Command, StreamWriter
|
|
15
|
+
|
|
16
|
+
import aip_agents.agent.base_langgraph_agent as bla
|
|
17
|
+
from aip_agents.agent.base_agent import BaseAgent
|
|
18
|
+
from aip_agents.schema.step_limit import MaxDelegationDepthExceededError
|
|
19
|
+
from aip_agents.types import A2AEvent, A2AStreamEventType
|
|
20
|
+
from aip_agents.utils.artifact_helpers import extract_artifacts_from_agent_response
|
|
21
|
+
from aip_agents.utils.langgraph.tool_managers.base_tool_manager import BaseLangGraphToolManager
|
|
22
|
+
from aip_agents.utils.logger import get_logger
|
|
23
|
+
from aip_agents.utils.metadata_helper import MetadataFieldKeys, get_next_step_number
|
|
24
|
+
from aip_agents.utils.pii.pii_helper import (
|
|
25
|
+
anonymize_final_response_content,
|
|
26
|
+
extract_pii_mapping_from_agent_response,
|
|
27
|
+
)
|
|
28
|
+
from aip_agents.utils.reference_helper import extract_references_from_agent_response
|
|
29
|
+
from aip_agents.utils.step_limit_manager import (
|
|
30
|
+
_DELEGATION_CHAIN_CVAR,
|
|
31
|
+
_DELEGATION_DEPTH_CVAR,
|
|
32
|
+
_REMAINING_STEP_BUDGET_CVAR,
|
|
33
|
+
_STEP_LIMIT_CONFIG_CVAR,
|
|
34
|
+
StepLimitManager,
|
|
35
|
+
)
|
|
36
|
+
from aip_agents.utils.token_usage_helper import (
|
|
37
|
+
STEP_USAGE_KEY,
|
|
38
|
+
TOTAL_USAGE_KEY,
|
|
39
|
+
USAGE_METADATA_KEY,
|
|
40
|
+
extract_token_usage_from_agent_response,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Context variable to carry parent step id from coordinator to sub-agent as a fallback
|
|
44
|
+
_DELEGATION_PARENT_STEP_ID_CVAR: ContextVar[str | None] = ContextVar("delegation_parent_step_id", default=None)
|
|
45
|
+
# Track the last sub-agent TOOL_CALL step_id so sub-agent TOOL_RESULT can link to it
|
|
46
|
+
_DELEGATION_SUB_START_STEP_CVAR: ContextVar[dict[str, str] | None] = ContextVar(
|
|
47
|
+
"delegation_sub_start_step", default=None
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
logger = get_logger(__name__)
|
|
51
|
+
|
|
52
|
+
# Constants for response keys
|
|
53
|
+
OUTPUT_KEY = "output"
|
|
54
|
+
RESULT_KEY = "result"
|
|
55
|
+
ARTIFACTS_KEY = "artifacts"
|
|
56
|
+
METADATA_KEY = "metadata"
|
|
57
|
+
|
|
58
|
+
# Internal metadata keys to filter out
|
|
59
|
+
METADATA_INTERNAL_PREFIXES = ["__", "langgraph_", "langchain_"]
|
|
60
|
+
METADATA_INTERNAL_KEYS = {"step_id", "previous_step_ids", "agent_name"}
|
|
61
|
+
AGENT_RUN_A2A_STREAMING_METHOD = "arun_a2a_stream"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class DelegationToolManager(BaseLangGraphToolManager):
|
|
65
|
+
"""Manages internal agent delegation tools for LangGraph agents.
|
|
66
|
+
|
|
67
|
+
This tool manager converts internal agent instances into LangChain tools
|
|
68
|
+
that can be used for task delegation within a unified ToolNode. Each
|
|
69
|
+
delegated agent becomes a tool that the coordinator can call.
|
|
70
|
+
|
|
71
|
+
Simplified version following legacy BaseLangChainAgent patterns.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, parent_agent: BaseAgent | None = None):
|
|
75
|
+
"""Initialize the delegation tool manager.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
parent_agent: The parent agent that creates delegation tools, used for parent step lookup.
|
|
79
|
+
"""
|
|
80
|
+
super().__init__()
|
|
81
|
+
self.registered_agents: list[BaseAgent] = []
|
|
82
|
+
self.parent_agent = parent_agent
|
|
83
|
+
|
|
84
|
+
def register_resources(self, agents: list[BaseAgent]) -> list[BaseTool]:
|
|
85
|
+
"""Register internal agents for delegation and convert them to tools.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
agents: List of BaseAgent instances for internal task delegation.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
List of created delegation tools.
|
|
92
|
+
"""
|
|
93
|
+
self.registered_agents = list(agents)
|
|
94
|
+
self.created_tools = []
|
|
95
|
+
|
|
96
|
+
for agent in agents:
|
|
97
|
+
delegation_tool = self._create_delegation_tool_streaming(agent)
|
|
98
|
+
self.created_tools.append(delegation_tool)
|
|
99
|
+
|
|
100
|
+
logger.info(
|
|
101
|
+
f"DelegationToolManager: Created {len(self.created_tools)} streaming delegation tools "
|
|
102
|
+
f"for {len(agents)} agents"
|
|
103
|
+
)
|
|
104
|
+
return self.created_tools
|
|
105
|
+
|
|
106
|
+
def get_resource_names(self) -> list[str]:
|
|
107
|
+
"""Get names of all registered delegation agents.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
list[str]: A list of names of all registered delegation agents.
|
|
111
|
+
"""
|
|
112
|
+
return [agent.name for agent in self.registered_agents]
|
|
113
|
+
|
|
114
|
+
def _create_delegation_tool_streaming(self, agent: BaseAgent) -> BaseTool:
|
|
115
|
+
"""Create a LangChain tool for agent delegation with real-time streaming support.
|
|
116
|
+
|
|
117
|
+
This version uses async streaming to provide real-time tool call and artifact visibility.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
agent: The agent to create a delegation tool for.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
BaseTool: An async LangChain tool for agent delegation with streaming.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
@tool
|
|
127
|
+
async def delegate_to_agent(query: str, config: RunnableConfig) -> str | dict[str, Any]:
|
|
128
|
+
"""Delegate task to internal agent with real-time streaming.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
query: The task to delegate to the internal agent.
|
|
132
|
+
config: The runtime configuration for the agent.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
The result from the delegated agent, including artifacts if any.
|
|
136
|
+
"""
|
|
137
|
+
writer: StreamWriter = get_stream_writer()
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
# Check delegation depth limit before executing
|
|
141
|
+
try:
|
|
142
|
+
current_depth = _DELEGATION_DEPTH_CVAR.get() or 0
|
|
143
|
+
parent_config = _STEP_LIMIT_CONFIG_CVAR.get()
|
|
144
|
+
|
|
145
|
+
temp_state = {
|
|
146
|
+
"delegation_depth": current_depth,
|
|
147
|
+
"delegation_chain": list(_DELEGATION_CHAIN_CVAR.get() or []),
|
|
148
|
+
}
|
|
149
|
+
manager = StepLimitManager.from_state(temp_state, config=parent_config)
|
|
150
|
+
manager.check_delegation_depth(target_agent_name=agent.name)
|
|
151
|
+
|
|
152
|
+
except MaxDelegationDepthExceededError as depth_error:
|
|
153
|
+
logger.warning(
|
|
154
|
+
f"DelegationToolManager: Delegation depth check failed for '{agent.name}': {depth_error}"
|
|
155
|
+
)
|
|
156
|
+
self._notify_error_via_writer(writer, agent.name, depth_error)
|
|
157
|
+
return self._handle_delegation_error(agent.name, depth_error)
|
|
158
|
+
|
|
159
|
+
logger.debug(f"DelegationToolManager: Delegating to '{agent.name}'")
|
|
160
|
+
configurable_kwargs = self._get_configurable_kwargs(agent, config)
|
|
161
|
+
result = await self._execute_delegated_agent(agent, query, configurable_kwargs, config, writer)
|
|
162
|
+
return self._handle_delegation_response_with_extras(agent.name, result)
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
self._notify_error_via_writer(writer, agent.name, e)
|
|
166
|
+
return self._handle_delegation_error(agent.name, e)
|
|
167
|
+
|
|
168
|
+
delegate_to_agent.name = f"delegate_to_{agent.name}"
|
|
169
|
+
delegate_to_agent.description = (
|
|
170
|
+
f"Delegate tasks to internal agent '{agent.name}'. "
|
|
171
|
+
f"Use this when you need to: {agent.description or 'coordinate with this agent'}"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
delegate_to_agent.metadata = self._build_delegation_tool_metadata(agent)
|
|
175
|
+
|
|
176
|
+
return delegate_to_agent
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def _build_delegation_tool_metadata(agent: BaseAgent) -> dict[str, Any]:
|
|
180
|
+
"""Create metadata payload used to mark tools as delegation-capable.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
agent (BaseAgent): The agent to create metadata for.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
dict[str, Any]: The metadata payload for the delegation tool.
|
|
187
|
+
"""
|
|
188
|
+
return {
|
|
189
|
+
"is_delegation_tool": True,
|
|
190
|
+
"delegated_agent_name": agent.name,
|
|
191
|
+
"tool_type": "delegation",
|
|
192
|
+
"delegation_manager": DelegationToolManager.__name__,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async def _execute_delegated_agent(
|
|
196
|
+
self,
|
|
197
|
+
agent: BaseAgent,
|
|
198
|
+
query: str,
|
|
199
|
+
configurable_kwargs: dict[str, Any],
|
|
200
|
+
config: RunnableConfig | None,
|
|
201
|
+
writer: StreamWriter,
|
|
202
|
+
) -> Any:
|
|
203
|
+
"""Execute delegated agent call with streaming or synchronous fallback.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
agent: The delegated agent to execute.
|
|
207
|
+
query: The query string to pass to the agent.
|
|
208
|
+
configurable_kwargs: Additional configuration arguments for the agent.
|
|
209
|
+
config: Runnable configuration for the execution context.
|
|
210
|
+
writer: Stream writer to emit events during execution.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
The result of the agent execution.
|
|
214
|
+
"""
|
|
215
|
+
# T021: Propagate delegation depth and step budget to sub-agent
|
|
216
|
+
# Import here to avoid circular dependencies if any
|
|
217
|
+
from aip_agents.utils.step_limit_manager import (
|
|
218
|
+
_DELEGATION_DEPTH_CVAR,
|
|
219
|
+
_STEP_LIMIT_CONFIG_CVAR,
|
|
220
|
+
StepLimitManager,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Context tokens for reset
|
|
224
|
+
depth_token = None
|
|
225
|
+
chain_token = None
|
|
226
|
+
budget_token = None
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
# Get current delegation context from ContextVars
|
|
230
|
+
current_depth = _DELEGATION_DEPTH_CVAR.get() or 0
|
|
231
|
+
current_chain = _DELEGATION_CHAIN_CVAR.get() or []
|
|
232
|
+
remaining_budget = _REMAINING_STEP_BUDGET_CVAR.get()
|
|
233
|
+
parent_config = _STEP_LIMIT_CONFIG_CVAR.get()
|
|
234
|
+
|
|
235
|
+
# Increment depth for sub-agent
|
|
236
|
+
new_depth = current_depth + 1
|
|
237
|
+
new_chain = list(current_chain) + [agent.name]
|
|
238
|
+
|
|
239
|
+
# Calculate child budget using parent logic
|
|
240
|
+
# Use transient manager to apply calculation rules
|
|
241
|
+
manager = StepLimitManager(
|
|
242
|
+
config=parent_config,
|
|
243
|
+
initial_delegation_depth=current_depth,
|
|
244
|
+
parent_step_budget=remaining_budget,
|
|
245
|
+
)
|
|
246
|
+
child_max_steps = None
|
|
247
|
+
child_config = getattr(agent, "step_limit_config", None)
|
|
248
|
+
if child_config is not None and hasattr(child_config, "max_steps"):
|
|
249
|
+
child_max_steps = child_config.max_steps
|
|
250
|
+
child_budget = manager.get_child_budget(child_max_steps=child_max_steps)
|
|
251
|
+
|
|
252
|
+
# Set context for sub-agent
|
|
253
|
+
depth_token = _DELEGATION_DEPTH_CVAR.set(new_depth)
|
|
254
|
+
chain_token = _DELEGATION_CHAIN_CVAR.set(tuple(new_chain))
|
|
255
|
+
budget_token = _REMAINING_STEP_BUDGET_CVAR.set(child_budget)
|
|
256
|
+
|
|
257
|
+
logger.debug(
|
|
258
|
+
f"DelegationToolManager: Delegating to '{agent.name}' at depth {new_depth}, "
|
|
259
|
+
f"chain: {new_chain}, child_budget: {child_budget} (from parent: {remaining_budget})"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Execute
|
|
263
|
+
if not hasattr(agent, AGENT_RUN_A2A_STREAMING_METHOD):
|
|
264
|
+
return agent.run(query, **configurable_kwargs)
|
|
265
|
+
|
|
266
|
+
self._set_parent_step_context(config)
|
|
267
|
+
final_chunk = await self._handle_delegation_streaming(agent, query, configurable_kwargs, writer)
|
|
268
|
+
return self._format_final_chunk_sub_agent_output(final_chunk)
|
|
269
|
+
|
|
270
|
+
except Exception as e:
|
|
271
|
+
logger.warning(f"DelegationToolManager: Error in delegation execution: {e}")
|
|
272
|
+
raise e
|
|
273
|
+
finally:
|
|
274
|
+
# Restore context and propagate child usage back to parent (Spec-1)
|
|
275
|
+
# Parent context is restored by reset, then we set it to the final child budget
|
|
276
|
+
if budget_token:
|
|
277
|
+
final_child_budget = _REMAINING_STEP_BUDGET_CVAR.get()
|
|
278
|
+
_REMAINING_STEP_BUDGET_CVAR.reset(budget_token)
|
|
279
|
+
_REMAINING_STEP_BUDGET_CVAR.set(final_child_budget)
|
|
280
|
+
|
|
281
|
+
if depth_token:
|
|
282
|
+
_DELEGATION_DEPTH_CVAR.reset(depth_token)
|
|
283
|
+
if chain_token:
|
|
284
|
+
_DELEGATION_CHAIN_CVAR.reset(chain_token)
|
|
285
|
+
|
|
286
|
+
def _set_parent_step_context(self, config: RunnableConfig | None) -> None:
|
|
287
|
+
"""Bridge parent step ID from tool configuration into context variables.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
config: Runnable configuration containing parent step ID information.
|
|
291
|
+
"""
|
|
292
|
+
try:
|
|
293
|
+
parent_step_id = None
|
|
294
|
+
cfg = config or {}
|
|
295
|
+
cfg_conf = cfg.get("configurable") if isinstance(cfg, dict) else None
|
|
296
|
+
if isinstance(cfg_conf, dict):
|
|
297
|
+
parent_step_id = cfg_conf.get("parent_step_id")
|
|
298
|
+
_DELEGATION_PARENT_STEP_ID_CVAR.set(parent_step_id)
|
|
299
|
+
except Exception:
|
|
300
|
+
_DELEGATION_PARENT_STEP_ID_CVAR.set(None)
|
|
301
|
+
|
|
302
|
+
def _notify_error_via_writer(self, writer: StreamWriter, agent_name: str, exception: Exception) -> None:
|
|
303
|
+
"""Safely notify error via writer using A2AEvent structure.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
writer: Stream writer for sending updates
|
|
307
|
+
agent_name: Name of the agent that encountered the error
|
|
308
|
+
exception: The exception that occurred
|
|
309
|
+
"""
|
|
310
|
+
try:
|
|
311
|
+
a2a_event: A2AEvent = {
|
|
312
|
+
"event_type": A2AStreamEventType.ERROR,
|
|
313
|
+
"content": f"Error in {agent_name}: {str(exception)}",
|
|
314
|
+
"metadata": {},
|
|
315
|
+
"is_final": False,
|
|
316
|
+
}
|
|
317
|
+
writer(a2a_event)
|
|
318
|
+
except Exception:
|
|
319
|
+
pass
|
|
320
|
+
|
|
321
|
+
def _create_delegation_tool(self, agent: BaseAgent) -> BaseTool:
|
|
322
|
+
"""Create a LangChain tool for agent delegation (non-streaming version).
|
|
323
|
+
|
|
324
|
+
Simplified version following legacy BaseLangChainAgent._create_delegation_func pattern.
|
|
325
|
+
This is the original non-streaming version for backward compatibility.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
agent: The agent to create a delegation tool for.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
BaseTool: A LangChain tool for agent delegation.
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
@tool
|
|
335
|
+
def delegate_to_agent(query: str, config: RunnableConfig) -> str | dict[str, Any] | Command:
|
|
336
|
+
"""Delegate task to internal agent.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
query: The task to delegate to the internal agent.
|
|
340
|
+
config: The runtime configuration for the agent.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
The result from the delegated agent, including artifacts and metadata if any.
|
|
344
|
+
"""
|
|
345
|
+
try:
|
|
346
|
+
logger.debug(f"DelegationToolManager: Delegating to '{agent.name}'")
|
|
347
|
+
|
|
348
|
+
# Use simple delegation kwargs (following legacy pattern)
|
|
349
|
+
configurable_kwargs = self._get_configurable_kwargs(agent, config)
|
|
350
|
+
result = agent.run(query, **configurable_kwargs)
|
|
351
|
+
|
|
352
|
+
# Handle response with artifact, metadata, references and token usage support
|
|
353
|
+
return self._handle_delegation_response_with_extras(agent.name, result)
|
|
354
|
+
|
|
355
|
+
except Exception as e:
|
|
356
|
+
return self._handle_delegation_error(agent.name, e)
|
|
357
|
+
|
|
358
|
+
# Set tool metadata
|
|
359
|
+
delegate_to_agent.name = f"delegate_to_{agent.name}"
|
|
360
|
+
delegate_to_agent.description = (
|
|
361
|
+
f"Delegate tasks to internal agent '{agent.name}'. "
|
|
362
|
+
f"Use this when you need to: {agent.description or 'coordinate with this agent'}"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
delegate_to_agent.metadata = self._build_delegation_tool_metadata(agent)
|
|
366
|
+
|
|
367
|
+
return delegate_to_agent
|
|
368
|
+
|
|
369
|
+
def _get_configurable_kwargs(self, agent: BaseAgent, config: RunnableConfig | None = None) -> dict[str, Any]:
|
|
370
|
+
"""Get configurable kwargs for agent delegation.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
agent: The agent to get configurable kwargs for.
|
|
374
|
+
config: The parent agent's config containing thread_id to inherit.
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
dict[str, Any]: A dictionary with 'configurable' key if the agent
|
|
378
|
+
has 'thread_id_key', otherwise an empty dictionary.
|
|
379
|
+
"""
|
|
380
|
+
if hasattr(agent, "thread_id_key"):
|
|
381
|
+
# Try to use parent thread ID from config first
|
|
382
|
+
parent_thread_id = None
|
|
383
|
+
if config and config.get("configurable"):
|
|
384
|
+
parent_thread_id = config["configurable"].get("thread_id")
|
|
385
|
+
|
|
386
|
+
if parent_thread_id:
|
|
387
|
+
# Use parent's thread ID to maintain conversation continuity
|
|
388
|
+
return {"configurable": {agent.thread_id_key: parent_thread_id}}
|
|
389
|
+
else:
|
|
390
|
+
delegation_thread_id = f"delegation_to_{agent.name}_{uuid.uuid4().hex[:8]}"
|
|
391
|
+
return {"configurable": {agent.thread_id_key: delegation_thread_id}}
|
|
392
|
+
return {}
|
|
393
|
+
|
|
394
|
+
def _handle_delegation_response(self, agent_name: str, result: Any) -> str:
|
|
395
|
+
"""Handle delegation response (following legacy pattern).
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
agent_name (str): The name of the agent that was delegated to.
|
|
399
|
+
result (Any): The result from the delegated agent.
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
str: The formatted response string.
|
|
403
|
+
"""
|
|
404
|
+
try:
|
|
405
|
+
if isinstance(result, dict):
|
|
406
|
+
response_content = result.get(
|
|
407
|
+
OUTPUT_KEY, f"No '{OUTPUT_KEY}' key found in response from agent {agent_name}."
|
|
408
|
+
)
|
|
409
|
+
logger.info(f"DelegationToolManager: Agent '{agent_name}' responded: {response_content}")
|
|
410
|
+
return str(response_content)
|
|
411
|
+
else:
|
|
412
|
+
return str(result)
|
|
413
|
+
except Exception as e:
|
|
414
|
+
logger.warning(f"DelegationToolManager: Error formatting delegation response from '{agent_name}': {e}")
|
|
415
|
+
return str(result)
|
|
416
|
+
|
|
417
|
+
async def _handle_delegation_streaming(
|
|
418
|
+
self, agent: BaseAgent, query: str, configurable_kwargs: dict, writer: StreamWriter
|
|
419
|
+
) -> dict | None:
|
|
420
|
+
"""Handle streaming communication with a delegated agent.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
agent: The agent to stream from
|
|
424
|
+
query: The query to send to the agent
|
|
425
|
+
configurable_kwargs: Configuration parameters for the agent
|
|
426
|
+
writer: Stream writer for sending updates
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
The final result chunk from the agent
|
|
430
|
+
"""
|
|
431
|
+
final_result = None
|
|
432
|
+
|
|
433
|
+
async for chunk in agent.arun_a2a_stream(query, **configurable_kwargs):
|
|
434
|
+
if isinstance(chunk, dict):
|
|
435
|
+
self._anonymize_final_chunk(chunk)
|
|
436
|
+
self._forward_sub_agent_chunk(chunk, writer)
|
|
437
|
+
|
|
438
|
+
if self._is_delegation_chunk_final(chunk):
|
|
439
|
+
final_result = chunk
|
|
440
|
+
return final_result
|
|
441
|
+
|
|
442
|
+
def _is_delegation_chunk_final(self, chunk: dict) -> bool:
|
|
443
|
+
"""Check if a delegation chunk represents the final result.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
chunk: The chunk to check
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
True if this is the final chunk
|
|
450
|
+
"""
|
|
451
|
+
return chunk.get("is_final") or chunk.get("status") == "completed"
|
|
452
|
+
|
|
453
|
+
def _anonymize_final_chunk(self, chunk: dict) -> None:
|
|
454
|
+
"""Mask sub-agent final responses using available PII mapping.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
chunk: Streamed response chunk from the delegated agent.
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
None: This method mutates the provided chunk in place when masking is applied.
|
|
461
|
+
"""
|
|
462
|
+
if not self._is_delegation_chunk_final(chunk):
|
|
463
|
+
return
|
|
464
|
+
|
|
465
|
+
content = chunk.get("content")
|
|
466
|
+
if not isinstance(content, str) or not content:
|
|
467
|
+
return
|
|
468
|
+
|
|
469
|
+
chunk["content"] = anonymize_final_response_content(content, chunk)
|
|
470
|
+
|
|
471
|
+
def _handle_delegation_response_with_extras(self, agent_name: str, result: Any) -> str | dict[str, Any] | Command:
|
|
472
|
+
"""Handle delegation response with full support for artifacts, metadata, references, and token usage.
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
agent_name: The name of the agent that provided the response.
|
|
476
|
+
result: The result from the delegated agent.
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
Either a string (when no additional data), dict with 'result' and other keys,
|
|
480
|
+
or Command with comprehensive updates.
|
|
481
|
+
"""
|
|
482
|
+
try:
|
|
483
|
+
text_response, artifacts = extract_artifacts_from_agent_response(result)
|
|
484
|
+
metadata_update = self._extract_metadata_from_agent_response(result)
|
|
485
|
+
token_usage = extract_token_usage_from_agent_response(result)
|
|
486
|
+
references = extract_references_from_agent_response(result)
|
|
487
|
+
pii_mapping = extract_pii_mapping_from_agent_response(result)
|
|
488
|
+
|
|
489
|
+
if artifacts:
|
|
490
|
+
logger.info(f"DelegationToolManager: Agent '{agent_name}' responded with {len(artifacts)} artifacts")
|
|
491
|
+
if metadata_update:
|
|
492
|
+
logger.info(
|
|
493
|
+
f"DelegationToolManager: Agent '{agent_name}' responded with metadata updates: {metadata_update}"
|
|
494
|
+
)
|
|
495
|
+
if token_usage:
|
|
496
|
+
logger.info(f"DelegationToolManager: Agent '{agent_name}' responded with token usage: {token_usage}")
|
|
497
|
+
if references:
|
|
498
|
+
logger.info(f"DelegationToolManager: Agent '{agent_name}' responded with {len(references)} references")
|
|
499
|
+
if pii_mapping:
|
|
500
|
+
logger.info(
|
|
501
|
+
f"DelegationToolManager: Agent '{agent_name}' responded with PII mapping: {len(pii_mapping)} entries"
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# Prepare response with any additional data
|
|
505
|
+
has_extras = self._has_response_extras(artifacts, metadata_update, token_usage, references, pii_mapping)
|
|
506
|
+
|
|
507
|
+
if has_extras:
|
|
508
|
+
update_dict = self._build_response_update_dict(
|
|
509
|
+
agent_name, text_response, artifacts, metadata_update, token_usage, references, pii_mapping
|
|
510
|
+
)
|
|
511
|
+
return Command(update=update_dict)
|
|
512
|
+
else:
|
|
513
|
+
return f"[{agent_name}] {text_response}"
|
|
514
|
+
|
|
515
|
+
except Exception as e:
|
|
516
|
+
logger.warning(f"DelegationToolManager: Error formatting delegation response from '{agent_name}': {e}")
|
|
517
|
+
return str(result)
|
|
518
|
+
|
|
519
|
+
def _has_response_extras(
|
|
520
|
+
self,
|
|
521
|
+
artifacts: list,
|
|
522
|
+
metadata_update: dict | None,
|
|
523
|
+
token_usage: dict | None,
|
|
524
|
+
references: list,
|
|
525
|
+
pii_mapping: dict[str, str] | None = None,
|
|
526
|
+
) -> bool:
|
|
527
|
+
"""Check if the response has any extra data beyond the text response.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
artifacts (list): List of artifacts from the response.
|
|
531
|
+
metadata_update (dict | None): Optional metadata update.
|
|
532
|
+
token_usage (dict | None): Optional token usage information.
|
|
533
|
+
references (list): List of references from the response.
|
|
534
|
+
pii_mapping (dict[str, str] | None): Optional PII mapping from the response.
|
|
535
|
+
|
|
536
|
+
Returns:
|
|
537
|
+
bool: True if any extra data is present, False otherwise.
|
|
538
|
+
"""
|
|
539
|
+
return any([artifacts, metadata_update, token_usage, references, pii_mapping])
|
|
540
|
+
|
|
541
|
+
def _build_response_update_dict( # noqa: PLR0913
|
|
542
|
+
self,
|
|
543
|
+
agent_name: str,
|
|
544
|
+
text_response: str,
|
|
545
|
+
artifacts: list,
|
|
546
|
+
metadata_update: dict | None,
|
|
547
|
+
token_usage: dict | None,
|
|
548
|
+
references: list,
|
|
549
|
+
pii_mapping: dict[str, str] | None = None,
|
|
550
|
+
) -> dict[str, Any]: # noqa: PLR0913
|
|
551
|
+
"""Build the update dictionary for responses with extra data.
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
agent_name: Name of the agent that generated the response.
|
|
555
|
+
text_response: The main text response from the agent.
|
|
556
|
+
artifacts: List of artifacts associated with the response.
|
|
557
|
+
metadata_update: Optional metadata update dictionary.
|
|
558
|
+
token_usage: Optional token usage information.
|
|
559
|
+
references: List of references associated with the response.
|
|
560
|
+
pii_mapping: Optional PII mapping from the response.
|
|
561
|
+
|
|
562
|
+
Returns:
|
|
563
|
+
Dictionary containing the formatted response with all extra data included.
|
|
564
|
+
"""
|
|
565
|
+
update_dict = {RESULT_KEY: f"[{agent_name}] {text_response}"}
|
|
566
|
+
|
|
567
|
+
# Add each type of extra data if available
|
|
568
|
+
if artifacts:
|
|
569
|
+
update_dict[ARTIFACTS_KEY] = artifacts
|
|
570
|
+
if metadata_update:
|
|
571
|
+
update_dict[METADATA_KEY] = metadata_update
|
|
572
|
+
if token_usage:
|
|
573
|
+
update_dict[USAGE_METADATA_KEY] = token_usage
|
|
574
|
+
if references:
|
|
575
|
+
update_dict[MetadataFieldKeys.REFERENCES] = references
|
|
576
|
+
if pii_mapping:
|
|
577
|
+
update_dict[MetadataFieldKeys.PII_MAPPING] = pii_mapping
|
|
578
|
+
|
|
579
|
+
return update_dict
|
|
580
|
+
|
|
581
|
+
def _extract_metadata_from_agent_response(self, result: Any) -> dict[str, Any] | None:
|
|
582
|
+
"""Extract metadata from agent response for delegation tools.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
result: The result returned by the delegated agent.
|
|
586
|
+
|
|
587
|
+
Returns:
|
|
588
|
+
Metadata dict if found, None otherwise.
|
|
589
|
+
"""
|
|
590
|
+
if not isinstance(result, dict):
|
|
591
|
+
return None
|
|
592
|
+
|
|
593
|
+
full_state = result.get("full_final_state", {})
|
|
594
|
+
if not isinstance(full_state, dict):
|
|
595
|
+
return None
|
|
596
|
+
|
|
597
|
+
metadata = full_state.get("metadata")
|
|
598
|
+
if not isinstance(metadata, dict):
|
|
599
|
+
return None
|
|
600
|
+
|
|
601
|
+
# Keep filtered metadata but also preserve linkage fields
|
|
602
|
+
filtered = self._filter_metadata(metadata)
|
|
603
|
+
prev_ids = metadata.get("previous_step_ids")
|
|
604
|
+
if isinstance(prev_ids, list) and prev_ids:
|
|
605
|
+
# Do not drop linkage information
|
|
606
|
+
filtered["previous_step_ids"] = list(prev_ids)
|
|
607
|
+
# Optionally keep step_id if present (useful for advanced tracing)
|
|
608
|
+
if "step_id" in metadata and metadata["step_id"]:
|
|
609
|
+
filtered.setdefault("step_id", metadata["step_id"]) # don't overwrite if user explicitly set
|
|
610
|
+
|
|
611
|
+
return filtered
|
|
612
|
+
|
|
613
|
+
def _filter_metadata(self, metadata: dict[str, Any]) -> dict[str, Any]:
|
|
614
|
+
"""Filter out internal LangGraph keys to avoid pollution.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
metadata: Raw metadata dict
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
Filtered metadata dict
|
|
621
|
+
"""
|
|
622
|
+
filtered_metadata = {
|
|
623
|
+
k: v
|
|
624
|
+
for k, v in metadata.items()
|
|
625
|
+
if not any(k.startswith(prefix) for prefix in METADATA_INTERNAL_PREFIXES)
|
|
626
|
+
and k not in METADATA_INTERNAL_KEYS
|
|
627
|
+
}
|
|
628
|
+
return filtered_metadata if filtered_metadata else {}
|
|
629
|
+
|
|
630
|
+
def _handle_delegation_error(self, agent_name: str, exception: Exception) -> str:
|
|
631
|
+
"""Handle delegation errors (following legacy pattern).
|
|
632
|
+
|
|
633
|
+
Args:
|
|
634
|
+
agent_name: The name of the agent that caused the error.
|
|
635
|
+
exception: The exception that occurred.
|
|
636
|
+
|
|
637
|
+
Returns:
|
|
638
|
+
str: A string containing the error message.
|
|
639
|
+
"""
|
|
640
|
+
error_msg = f"Error calling agent {agent_name}: {str(exception)}"
|
|
641
|
+
logger.error(f"DelegationToolManager: Error delegating to '{agent_name}': {exception}", exc_info=True)
|
|
642
|
+
return error_msg
|
|
643
|
+
|
|
644
|
+
def _forward_sub_agent_chunk(self, chunk: dict, writer: StreamWriter) -> None:
|
|
645
|
+
"""Forward sub-agent streaming chunks in real-time.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
chunk: Streaming chunk from the sub-agent
|
|
649
|
+
writer: Stream writer to emit events
|
|
650
|
+
"""
|
|
651
|
+
event_type = self._extract_event_type(chunk)
|
|
652
|
+
|
|
653
|
+
if event_type == A2AStreamEventType.TOOL_CALL:
|
|
654
|
+
self._forward_tool_call_event(chunk, writer)
|
|
655
|
+
elif event_type == A2AStreamEventType.TOOL_RESULT:
|
|
656
|
+
self._forward_tool_result_event(chunk, writer)
|
|
657
|
+
|
|
658
|
+
def _extract_event_type(self, chunk: dict) -> A2AStreamEventType | None:
|
|
659
|
+
"""Extract event type from chunk, converting to A2AStreamEventType enum.
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
chunk: Streaming chunk from the sub-agent
|
|
663
|
+
|
|
664
|
+
Returns:
|
|
665
|
+
Event type as A2AStreamEventType enum, or None if not found/invalid
|
|
666
|
+
"""
|
|
667
|
+
event_type = chunk.get("event_type")
|
|
668
|
+
|
|
669
|
+
if isinstance(event_type, A2AStreamEventType):
|
|
670
|
+
return event_type
|
|
671
|
+
|
|
672
|
+
if isinstance(event_type, str):
|
|
673
|
+
try:
|
|
674
|
+
return A2AStreamEventType(event_type)
|
|
675
|
+
except ValueError:
|
|
676
|
+
return None
|
|
677
|
+
|
|
678
|
+
return None
|
|
679
|
+
|
|
680
|
+
def _extract_delegation_tool_name_prefix(self, tool_name: str) -> str:
|
|
681
|
+
"""Extract meaningful prefix from delegation tool name.
|
|
682
|
+
|
|
683
|
+
Args:
|
|
684
|
+
tool_name: The delegation tool name (e.g., "delegate_to_TableAgent")
|
|
685
|
+
|
|
686
|
+
Returns:
|
|
687
|
+
The extracted prefix (e.g., "table" from "delegate_to_TableAgent")
|
|
688
|
+
"""
|
|
689
|
+
if tool_name.startswith("delegate_to_"):
|
|
690
|
+
# Extract the agent name after "delegate_to_"
|
|
691
|
+
agent_name = tool_name[12:] # Remove "delegate_to_"
|
|
692
|
+
# Remove "Agent" suffix if present
|
|
693
|
+
if agent_name.endswith("Agent"):
|
|
694
|
+
agent_name = agent_name[:-5] # Remove "Agent"
|
|
695
|
+
# Convert to lowercase and take first 4 characters
|
|
696
|
+
return agent_name.lower()[:4]
|
|
697
|
+
else:
|
|
698
|
+
# Fallback to first 4 characters
|
|
699
|
+
return tool_name[:4]
|
|
700
|
+
|
|
701
|
+
def _generate_delegation_tool_call_step_id(self, tool_info: dict[str, Any], counter: int) -> str:
|
|
702
|
+
"""Generate step_id for delegation tool call events.
|
|
703
|
+
|
|
704
|
+
Args:
|
|
705
|
+
tool_info: Tool information
|
|
706
|
+
counter: Step counter
|
|
707
|
+
|
|
708
|
+
Returns:
|
|
709
|
+
Generated step_id
|
|
710
|
+
"""
|
|
711
|
+
if not tool_info or not tool_info.get("tool_calls"):
|
|
712
|
+
return f"delegate_call_{counter:03d}"
|
|
713
|
+
|
|
714
|
+
tool_calls = tool_info["tool_calls"]
|
|
715
|
+
if len(tool_calls) == 1:
|
|
716
|
+
# Single tool call
|
|
717
|
+
tool_name = tool_calls[0].get("name", "unknown")
|
|
718
|
+
prefix = self._extract_delegation_tool_name_prefix(tool_name)
|
|
719
|
+
return f"{prefix}_call_{counter:03d}"
|
|
720
|
+
else:
|
|
721
|
+
# Multiple tool calls (parallel execution)
|
|
722
|
+
tool_names = [self._extract_delegation_tool_name_prefix(tc.get("name", "unknown")) for tc in tool_calls]
|
|
723
|
+
combined_name = "".join(tool_names)[:6] # Limit length
|
|
724
|
+
return f"{combined_name}_parent_{counter:03d}"
|
|
725
|
+
|
|
726
|
+
def _generate_delegation_tool_result_step_id(self, tool_info: dict[str, Any] | None, counter: int) -> str:
|
|
727
|
+
"""Generate step_id for delegation tool result events.
|
|
728
|
+
|
|
729
|
+
Args:
|
|
730
|
+
tool_info: Tool information
|
|
731
|
+
counter: Step counter
|
|
732
|
+
|
|
733
|
+
Returns:
|
|
734
|
+
Generated step_id
|
|
735
|
+
"""
|
|
736
|
+
if not tool_info:
|
|
737
|
+
return f"delegate_done_{counter:03d}"
|
|
738
|
+
|
|
739
|
+
tool_name = tool_info.get("name", "unknown")
|
|
740
|
+
prefix = self._extract_delegation_tool_name_prefix(tool_name)
|
|
741
|
+
return f"{prefix}_done_{counter:03d}"
|
|
742
|
+
|
|
743
|
+
def _generate_delegation_step_id(
|
|
744
|
+
self, event_type: A2AStreamEventType, agent_name: str, tool_info: dict[str, Any] | None = None
|
|
745
|
+
) -> str:
|
|
746
|
+
"""Generate a meaningful step_id for delegation events.
|
|
747
|
+
|
|
748
|
+
Args:
|
|
749
|
+
event_type: The type of event (tool_call, tool_result, etc.)
|
|
750
|
+
agent_name: The name of the delegated agent
|
|
751
|
+
tool_info: Tool information containing tool names and IDs
|
|
752
|
+
|
|
753
|
+
Returns:
|
|
754
|
+
A meaningful step_id string
|
|
755
|
+
"""
|
|
756
|
+
try:
|
|
757
|
+
counter = get_next_step_number()
|
|
758
|
+
|
|
759
|
+
# Use mapping to reduce branches
|
|
760
|
+
step_id_generators = {
|
|
761
|
+
A2AStreamEventType.TOOL_CALL: lambda: self._generate_delegation_tool_call_step_id(tool_info, counter),
|
|
762
|
+
A2AStreamEventType.TOOL_RESULT: lambda: self._generate_delegation_tool_result_step_id(
|
|
763
|
+
tool_info, counter
|
|
764
|
+
),
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
generator = step_id_generators.get(event_type)
|
|
768
|
+
if generator:
|
|
769
|
+
return generator()
|
|
770
|
+
|
|
771
|
+
# Handle both enum and string event types
|
|
772
|
+
event_type_value = event_type.value if hasattr(event_type, "value") else str(event_type)
|
|
773
|
+
fallback_prefix = self._build_fallback_prefix(agent_name)
|
|
774
|
+
return f"{fallback_prefix}_{event_type_value}_{counter:03d}"
|
|
775
|
+
|
|
776
|
+
except Exception:
|
|
777
|
+
# Fallback to random generation if anything goes wrong
|
|
778
|
+
return f"delegate_{uuid.uuid4().hex[:8]}"
|
|
779
|
+
|
|
780
|
+
@staticmethod
|
|
781
|
+
def _build_fallback_prefix(agent_name: str) -> str:
|
|
782
|
+
"""Create a fallback prefix that incorporates the agent name when available.
|
|
783
|
+
|
|
784
|
+
Args:
|
|
785
|
+
agent_name: The name of the agent to create a prefix for.
|
|
786
|
+
|
|
787
|
+
Returns:
|
|
788
|
+
A fallback prefix string incorporating the agent name.
|
|
789
|
+
"""
|
|
790
|
+
if not agent_name:
|
|
791
|
+
return "delegate"
|
|
792
|
+
|
|
793
|
+
sanitized = "".join(ch for ch in agent_name.lower() if ch.isalnum())[:8]
|
|
794
|
+
return f"delegate_{sanitized}" if sanitized else "delegate"
|
|
795
|
+
|
|
796
|
+
def _forward_tool_call_event(self, chunk: dict, writer: StreamWriter) -> None:
|
|
797
|
+
"""Forward tool call events with coordinator-style format using A2AEvent structure.
|
|
798
|
+
|
|
799
|
+
Args:
|
|
800
|
+
chunk: Streaming chunk containing tool call info
|
|
801
|
+
writer: Stream writer to emit events
|
|
802
|
+
"""
|
|
803
|
+
tool_info = chunk.get("tool_info", {})
|
|
804
|
+
message = self._create_tool_call_message(tool_info)
|
|
805
|
+
metadata = self._prepare_tool_call_metadata(chunk, tool_info)
|
|
806
|
+
|
|
807
|
+
a2a_event: A2AEvent = {
|
|
808
|
+
"event_type": A2AStreamEventType.TOOL_CALL,
|
|
809
|
+
"content": message,
|
|
810
|
+
"metadata": metadata,
|
|
811
|
+
"tool_info": tool_info,
|
|
812
|
+
"is_final": False,
|
|
813
|
+
"artifacts": chunk.get("artifacts"),
|
|
814
|
+
MetadataFieldKeys.REFERENCES: chunk.get(MetadataFieldKeys.REFERENCES),
|
|
815
|
+
STEP_USAGE_KEY: chunk.get(STEP_USAGE_KEY),
|
|
816
|
+
TOTAL_USAGE_KEY: chunk.get(TOTAL_USAGE_KEY),
|
|
817
|
+
}
|
|
818
|
+
writer(a2a_event)
|
|
819
|
+
|
|
820
|
+
def _prepare_tool_call_metadata(self, chunk: dict, tool_info: dict) -> dict:
|
|
821
|
+
"""Prepare metadata for tool call events with step ID generation and linkage.
|
|
822
|
+
|
|
823
|
+
Args:
|
|
824
|
+
chunk: Streaming chunk containing metadata
|
|
825
|
+
tool_info: Tool information for step ID generation
|
|
826
|
+
|
|
827
|
+
Returns:
|
|
828
|
+
Prepared metadata dictionary
|
|
829
|
+
"""
|
|
830
|
+
metadata = chunk.get("metadata") or {}
|
|
831
|
+
self._ensure_step_id_in_metadata(metadata, tool_info)
|
|
832
|
+
|
|
833
|
+
agent_name = metadata.get("agent_name")
|
|
834
|
+
if agent_name:
|
|
835
|
+
self._setup_agent_linkage(metadata, agent_name, tool_info)
|
|
836
|
+
else:
|
|
837
|
+
self._handle_missing_agent_name(metadata)
|
|
838
|
+
|
|
839
|
+
return metadata
|
|
840
|
+
|
|
841
|
+
def _ensure_step_id_in_metadata(self, metadata: dict, tool_info: dict) -> None:
|
|
842
|
+
"""Ensure step_id is present in metadata, generating one if missing.
|
|
843
|
+
|
|
844
|
+
Args:
|
|
845
|
+
metadata: Metadata dictionary to update
|
|
846
|
+
tool_info: Tool information for step ID generation
|
|
847
|
+
"""
|
|
848
|
+
if "step_id" not in metadata:
|
|
849
|
+
agent_name_for_id = metadata.get("agent_name") or "anon_agent"
|
|
850
|
+
metadata["step_id"] = self._generate_delegation_step_id(
|
|
851
|
+
A2AStreamEventType.TOOL_CALL, agent_name_for_id, tool_info
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
def _setup_agent_linkage(self, metadata: dict, agent_name: str, tool_info: dict) -> None:
|
|
855
|
+
"""Setup linkage between parent and sub-agent for tool call events.
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
metadata: Metadata dictionary to update
|
|
859
|
+
agent_name: Name of the agent
|
|
860
|
+
tool_info: Tool information for parent lookup
|
|
861
|
+
"""
|
|
862
|
+
parent_step_id = self._get_parent_step_id(tool_info)
|
|
863
|
+
metadata["previous_step_ids"] = [parent_step_id] if parent_step_id else []
|
|
864
|
+
|
|
865
|
+
# Record this sub-agent start step_id so its result can link back to it
|
|
866
|
+
sub_start_map = _DELEGATION_SUB_START_STEP_CVAR.get() or {}
|
|
867
|
+
sub_start_map[agent_name] = metadata["step_id"]
|
|
868
|
+
_DELEGATION_SUB_START_STEP_CVAR.set(sub_start_map)
|
|
869
|
+
|
|
870
|
+
def _get_parent_step_id(self, tool_info: dict) -> str | None:
|
|
871
|
+
"""Get parent step ID from context or parent agent lookup.
|
|
872
|
+
|
|
873
|
+
Args:
|
|
874
|
+
tool_info: Tool information for parent lookup
|
|
875
|
+
|
|
876
|
+
Returns:
|
|
877
|
+
Parent step ID if found, None otherwise
|
|
878
|
+
"""
|
|
879
|
+
parent_step_id = _DELEGATION_PARENT_STEP_ID_CVAR.get()
|
|
880
|
+
|
|
881
|
+
if not parent_step_id and self._can_lookup_parent_step():
|
|
882
|
+
parent_step_id = self._lookup_parent_step_from_agent(tool_info)
|
|
883
|
+
|
|
884
|
+
return parent_step_id
|
|
885
|
+
|
|
886
|
+
def _can_lookup_parent_step(self) -> bool:
|
|
887
|
+
"""Check if parent step lookup is possible.
|
|
888
|
+
|
|
889
|
+
Returns:
|
|
890
|
+
True if parent agent has the required mapping
|
|
891
|
+
"""
|
|
892
|
+
return self.parent_agent is not None and hasattr(self.parent_agent, "_tool_parent_map_by_thread")
|
|
893
|
+
|
|
894
|
+
def _lookup_parent_step_from_agent(self, tool_info: dict) -> str | None:
|
|
895
|
+
"""Lookup parent step ID from parent agent's mapping.
|
|
896
|
+
|
|
897
|
+
Args:
|
|
898
|
+
tool_info: Tool information containing tool call ID
|
|
899
|
+
|
|
900
|
+
Returns:
|
|
901
|
+
Parent step ID if found, None otherwise
|
|
902
|
+
"""
|
|
903
|
+
try:
|
|
904
|
+
thread_id = bla._THREAD_ID_CVAR.get()
|
|
905
|
+
if not thread_id:
|
|
906
|
+
return None
|
|
907
|
+
|
|
908
|
+
parent_map = self.parent_agent._tool_parent_map_by_thread.get(thread_id, {})
|
|
909
|
+
tool_call_id = tool_info.get("id") if tool_info else None
|
|
910
|
+
|
|
911
|
+
if tool_call_id:
|
|
912
|
+
return parent_map.get(str(tool_call_id))
|
|
913
|
+
|
|
914
|
+
except Exception as e:
|
|
915
|
+
logger.debug(f"Failed to look up parent step ID from parent agent: {e}")
|
|
916
|
+
|
|
917
|
+
return None
|
|
918
|
+
|
|
919
|
+
def _handle_missing_agent_name(self, metadata: dict) -> None:
|
|
920
|
+
"""Handle case where agent_name is missing from metadata.
|
|
921
|
+
|
|
922
|
+
Args:
|
|
923
|
+
metadata: Metadata dictionary to update
|
|
924
|
+
"""
|
|
925
|
+
logger.warning("Delegation tool call missing 'agent_name'; skipping linkage")
|
|
926
|
+
metadata["previous_step_ids"] = []
|
|
927
|
+
metadata["agent_name_missing"] = True
|
|
928
|
+
|
|
929
|
+
def _forward_tool_result_event(self, chunk: dict, writer: StreamWriter) -> None:
|
|
930
|
+
"""Forward tool result events with coordinator-style format using A2AEvent structure.
|
|
931
|
+
|
|
932
|
+
Args:
|
|
933
|
+
chunk: Streaming chunk containing tool result info
|
|
934
|
+
writer: Stream writer to emit events
|
|
935
|
+
"""
|
|
936
|
+
tool_info = chunk.get("tool_info", {})
|
|
937
|
+
tool_names: list[str] = []
|
|
938
|
+
|
|
939
|
+
primary_name = tool_info.get("name")
|
|
940
|
+
if isinstance(primary_name, str) and primary_name:
|
|
941
|
+
tool_names.append(primary_name)
|
|
942
|
+
elif isinstance(tool_info.get("tool_calls"), list):
|
|
943
|
+
tool_names.extend(
|
|
944
|
+
call.get("name")
|
|
945
|
+
for call in tool_info["tool_calls"]
|
|
946
|
+
if isinstance(call, dict) and isinstance(call.get("name"), str)
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
tool_names = [name for name in tool_names if name] or ["unknown_tool"]
|
|
950
|
+
|
|
951
|
+
# Preserve sub-agent metadata
|
|
952
|
+
metadata = chunk.get("metadata") or {}
|
|
953
|
+
|
|
954
|
+
# Link result to sub-agent start step_id only if agent_name present
|
|
955
|
+
agent_name = metadata.get("agent_name")
|
|
956
|
+
if agent_name:
|
|
957
|
+
sub_start_map = _DELEGATION_SUB_START_STEP_CVAR.get() or {}
|
|
958
|
+
start_step_id = sub_start_map.get(agent_name)
|
|
959
|
+
metadata["previous_step_ids"] = [start_step_id] if start_step_id else []
|
|
960
|
+
else:
|
|
961
|
+
logger.warning("Delegation tool result missing 'agent_name'; skipping linkage")
|
|
962
|
+
metadata["previous_step_ids"] = []
|
|
963
|
+
metadata["agent_name_missing"] = True
|
|
964
|
+
|
|
965
|
+
content = self._build_completion_content(tool_names)
|
|
966
|
+
|
|
967
|
+
a2a_event: A2AEvent = {
|
|
968
|
+
"event_type": A2AStreamEventType.TOOL_RESULT,
|
|
969
|
+
"content": content,
|
|
970
|
+
"metadata": metadata,
|
|
971
|
+
"tool_info": tool_info,
|
|
972
|
+
"is_final": False,
|
|
973
|
+
"artifacts": chunk.get("artifacts"),
|
|
974
|
+
MetadataFieldKeys.REFERENCES: chunk.get(MetadataFieldKeys.REFERENCES),
|
|
975
|
+
STEP_USAGE_KEY: chunk.get(STEP_USAGE_KEY),
|
|
976
|
+
TOTAL_USAGE_KEY: chunk.get(TOTAL_USAGE_KEY),
|
|
977
|
+
}
|
|
978
|
+
writer(a2a_event)
|
|
979
|
+
|
|
980
|
+
def _build_completion_content(self, tool_names: list[str]) -> str:
|
|
981
|
+
"""Create completion message consistent with coordinator formatting.
|
|
982
|
+
|
|
983
|
+
Args:
|
|
984
|
+
tool_names: List of tool names that were executed.
|
|
985
|
+
|
|
986
|
+
Returns:
|
|
987
|
+
Formatted completion message string.
|
|
988
|
+
"""
|
|
989
|
+
deduped_names = list(dict.fromkeys(name for name in tool_names if name))
|
|
990
|
+
if not deduped_names:
|
|
991
|
+
deduped_names = ["unknown_tool"]
|
|
992
|
+
|
|
993
|
+
if self.parent_agent and hasattr(self.parent_agent, "_get_tool_completion_content"):
|
|
994
|
+
try:
|
|
995
|
+
return self.parent_agent._get_tool_completion_content(deduped_names)
|
|
996
|
+
except Exception: # pragma: no cover - defensive fallback
|
|
997
|
+
logger.debug("DelegationToolManager: parent agent completion formatting failed", exc_info=True)
|
|
998
|
+
|
|
999
|
+
has_delegation = any(name.startswith("delegate_to") for name in deduped_names)
|
|
1000
|
+
prefix = "Completed sub-agents:" if has_delegation else "Completed tools:"
|
|
1001
|
+
return f"{prefix} {', '.join(deduped_names)}"
|
|
1002
|
+
|
|
1003
|
+
def _create_tool_call_message(self, tool_info: dict) -> str:
|
|
1004
|
+
"""Create a consistent message for tool call events.
|
|
1005
|
+
|
|
1006
|
+
Args:
|
|
1007
|
+
tool_info: Tool information from the chunk
|
|
1008
|
+
|
|
1009
|
+
Returns:
|
|
1010
|
+
Formatted message string
|
|
1011
|
+
"""
|
|
1012
|
+
tool_calls = tool_info.get("tool_calls", [])
|
|
1013
|
+
tool_names = [tc.get("name", "unknown") for tc in tool_calls]
|
|
1014
|
+
return f"Processing with tools: {', '.join(tool_names)}"
|
|
1015
|
+
|
|
1016
|
+
def _format_final_chunk_sub_agent_output(self, final_result: dict | str | Any) -> dict[str, Any]:
|
|
1017
|
+
"""Format the final chunk from a sub-agent result to match the .arun() output.
|
|
1018
|
+
|
|
1019
|
+
Args:
|
|
1020
|
+
final_result: The result from agent execution
|
|
1021
|
+
|
|
1022
|
+
Returns:
|
|
1023
|
+
A dictionary with keys:
|
|
1024
|
+
{
|
|
1025
|
+
"output": <output string>,
|
|
1026
|
+
"full_final_state": {
|
|
1027
|
+
"artifacts": <artifacts>,
|
|
1028
|
+
"references": <references>,
|
|
1029
|
+
"metadata": <metadata>,
|
|
1030
|
+
"total_usage": <total_usage>,
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
Note:
|
|
1035
|
+
- To preserve pattern of .arun():
|
|
1036
|
+
- the output will be stored in "output" key and extras will be stored in "full_final_state" key
|
|
1037
|
+
- Those extras being: "artifacts", "references", "metadata", and "total_usage"
|
|
1038
|
+
"""
|
|
1039
|
+
result: dict[str, Any] = {
|
|
1040
|
+
"output": "",
|
|
1041
|
+
"full_final_state": {
|
|
1042
|
+
"artifacts": [],
|
|
1043
|
+
MetadataFieldKeys.REFERENCES: [],
|
|
1044
|
+
"metadata": {},
|
|
1045
|
+
TOTAL_USAGE_KEY: {},
|
|
1046
|
+
},
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
if not isinstance(final_result, dict):
|
|
1050
|
+
result["output"] = str(final_result)
|
|
1051
|
+
else:
|
|
1052
|
+
result["output"] = final_result.get("content", str(final_result))
|
|
1053
|
+
result["full_final_state"] = {
|
|
1054
|
+
"artifacts": final_result.get("artifacts", []),
|
|
1055
|
+
MetadataFieldKeys.REFERENCES: final_result.get(MetadataFieldKeys.REFERENCES, []),
|
|
1056
|
+
"metadata": final_result.get("metadata", {}),
|
|
1057
|
+
TOTAL_USAGE_KEY: final_result.get(TOTAL_USAGE_KEY, {}),
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
# Propagate sub-agent final step id to coordinator via metadata.previous_step_ids
|
|
1061
|
+
try:
|
|
1062
|
+
metadata = final_result.get("metadata") or {}
|
|
1063
|
+
final_step_id = metadata.get("step_id")
|
|
1064
|
+
if final_step_id:
|
|
1065
|
+
result["metadata"] = {"previous_step_ids": [final_step_id]}
|
|
1066
|
+
except Exception:
|
|
1067
|
+
# If metadata access fails, ensure we have a metadata key
|
|
1068
|
+
result["metadata"] = {}
|
|
1069
|
+
|
|
1070
|
+
# Single return point
|
|
1071
|
+
return result
|