aip-agents-binary 0.5.25b1__py3-none-macosx_13_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (566) hide show
  1. aip_agents/__init__.py +65 -0
  2. aip_agents/__init__.pyi +19 -0
  3. aip_agents/a2a/__init__.py +19 -0
  4. aip_agents/a2a/__init__.pyi +3 -0
  5. aip_agents/a2a/server/__init__.py +10 -0
  6. aip_agents/a2a/server/__init__.pyi +4 -0
  7. aip_agents/a2a/server/base_executor.py +1086 -0
  8. aip_agents/a2a/server/base_executor.pyi +73 -0
  9. aip_agents/a2a/server/google_adk_executor.py +198 -0
  10. aip_agents/a2a/server/google_adk_executor.pyi +51 -0
  11. aip_agents/a2a/server/langflow_executor.py +180 -0
  12. aip_agents/a2a/server/langflow_executor.pyi +43 -0
  13. aip_agents/a2a/server/langgraph_executor.py +270 -0
  14. aip_agents/a2a/server/langgraph_executor.pyi +47 -0
  15. aip_agents/a2a/types.py +232 -0
  16. aip_agents/a2a/types.pyi +132 -0
  17. aip_agents/agent/__init__.py +27 -0
  18. aip_agents/agent/__init__.pyi +9 -0
  19. aip_agents/agent/base_agent.py +970 -0
  20. aip_agents/agent/base_agent.pyi +221 -0
  21. aip_agents/agent/base_langgraph_agent.py +2948 -0
  22. aip_agents/agent/base_langgraph_agent.pyi +232 -0
  23. aip_agents/agent/google_adk_agent.py +926 -0
  24. aip_agents/agent/google_adk_agent.pyi +141 -0
  25. aip_agents/agent/google_adk_constants.py +6 -0
  26. aip_agents/agent/google_adk_constants.pyi +3 -0
  27. aip_agents/agent/hitl/__init__.py +24 -0
  28. aip_agents/agent/hitl/__init__.pyi +6 -0
  29. aip_agents/agent/hitl/config.py +28 -0
  30. aip_agents/agent/hitl/config.pyi +15 -0
  31. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  32. aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +42 -0
  33. aip_agents/agent/hitl/manager.py +532 -0
  34. aip_agents/agent/hitl/manager.pyi +200 -0
  35. aip_agents/agent/hitl/models.py +18 -0
  36. aip_agents/agent/hitl/models.pyi +3 -0
  37. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  38. aip_agents/agent/hitl/prompt/__init__.pyi +4 -0
  39. aip_agents/agent/hitl/prompt/base.py +42 -0
  40. aip_agents/agent/hitl/prompt/base.pyi +24 -0
  41. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  42. aip_agents/agent/hitl/prompt/deferred.pyi +30 -0
  43. aip_agents/agent/hitl/registry.py +149 -0
  44. aip_agents/agent/hitl/registry.pyi +101 -0
  45. aip_agents/agent/interface.py +138 -0
  46. aip_agents/agent/interface.pyi +81 -0
  47. aip_agents/agent/interfaces.py +65 -0
  48. aip_agents/agent/interfaces.pyi +44 -0
  49. aip_agents/agent/langflow_agent.py +464 -0
  50. aip_agents/agent/langflow_agent.pyi +133 -0
  51. aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
  52. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +49 -0
  53. aip_agents/agent/langgraph_react_agent.py +2596 -0
  54. aip_agents/agent/langgraph_react_agent.pyi +131 -0
  55. aip_agents/agent/system_instruction_context.py +34 -0
  56. aip_agents/agent/system_instruction_context.pyi +13 -0
  57. aip_agents/clients/__init__.py +10 -0
  58. aip_agents/clients/__init__.pyi +4 -0
  59. aip_agents/clients/langflow/__init__.py +10 -0
  60. aip_agents/clients/langflow/__init__.pyi +4 -0
  61. aip_agents/clients/langflow/client.py +477 -0
  62. aip_agents/clients/langflow/client.pyi +140 -0
  63. aip_agents/clients/langflow/types.py +18 -0
  64. aip_agents/clients/langflow/types.pyi +7 -0
  65. aip_agents/constants.py +23 -0
  66. aip_agents/constants.pyi +7 -0
  67. aip_agents/credentials/manager.py +132 -0
  68. aip_agents/examples/__init__.py +5 -0
  69. aip_agents/examples/__init__.pyi +0 -0
  70. aip_agents/examples/compare_streaming_client.py +783 -0
  71. aip_agents/examples/compare_streaming_client.pyi +48 -0
  72. aip_agents/examples/compare_streaming_server.py +142 -0
  73. aip_agents/examples/compare_streaming_server.pyi +18 -0
  74. aip_agents/examples/demo_memory_recall.py +401 -0
  75. aip_agents/examples/demo_memory_recall.pyi +58 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_client.pyi +9 -0
  78. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  79. aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +9 -0
  80. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  81. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +9 -0
  82. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  83. aip_agents/examples/hello_world_a2a_google_adk_server.pyi +15 -0
  84. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  85. aip_agents/examples/hello_world_a2a_langchain_client.pyi +5 -0
  86. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  87. aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +5 -0
  88. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  89. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +5 -0
  90. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  91. aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +5 -0
  92. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  93. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +5 -0
  94. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  95. aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +15 -0
  96. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  97. aip_agents/examples/hello_world_a2a_langchain_server.pyi +15 -0
  98. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  99. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +15 -0
  100. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  101. aip_agents/examples/hello_world_a2a_langflow_client.pyi +9 -0
  102. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  103. aip_agents/examples/hello_world_a2a_langflow_server.pyi +14 -0
  104. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  105. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +5 -0
  106. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  107. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +5 -0
  108. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  109. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +16 -0
  110. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  111. aip_agents/examples/hello_world_a2a_langgraph_client.pyi +9 -0
  112. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  113. aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +9 -0
  114. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  115. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +2 -0
  116. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  117. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +9 -0
  118. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  119. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +5 -0
  120. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  121. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +5 -0
  122. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  123. aip_agents/examples/hello_world_a2a_langgraph_server.pyi +14 -0
  124. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  125. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +15 -0
  126. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  127. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +15 -0
  128. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  129. aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +48 -0
  130. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  131. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +48 -0
  132. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  133. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +45 -0
  134. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  135. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +5 -0
  136. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  137. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +15 -0
  138. aip_agents/examples/hello_world_google_adk.py +41 -0
  139. aip_agents/examples/hello_world_google_adk.pyi +5 -0
  140. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  141. aip_agents/examples/hello_world_google_adk_mcp_http.pyi +5 -0
  142. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  143. aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +5 -0
  144. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  145. aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +5 -0
  146. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  147. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +5 -0
  148. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  149. aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +5 -0
  150. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  151. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +5 -0
  152. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  153. aip_agents/examples/hello_world_google_adk_stream.pyi +5 -0
  154. aip_agents/examples/hello_world_langchain.py +28 -0
  155. aip_agents/examples/hello_world_langchain.pyi +5 -0
  156. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  157. aip_agents/examples/hello_world_langchain_lm_invoker.pyi +2 -0
  158. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  159. aip_agents/examples/hello_world_langchain_mcp_http.pyi +5 -0
  160. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  161. aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +16 -0
  162. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  163. aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +5 -0
  164. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  165. aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +18 -0
  166. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  167. aip_agents/examples/hello_world_langchain_mcp_sse.pyi +5 -0
  168. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  169. aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +5 -0
  170. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  171. aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +5 -0
  172. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  173. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +5 -0
  174. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  175. aip_agents/examples/hello_world_langchain_stream.pyi +5 -0
  176. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  177. aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +5 -0
  178. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  179. aip_agents/examples/hello_world_langflow_agent.pyi +35 -0
  180. aip_agents/examples/hello_world_langgraph.py +39 -0
  181. aip_agents/examples/hello_world_langgraph.pyi +5 -0
  182. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  183. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +5 -0
  184. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  185. aip_agents/examples/hello_world_langgraph_mcp_http.pyi +5 -0
  186. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  187. aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +5 -0
  188. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  189. aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +5 -0
  190. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  191. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +5 -0
  192. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  193. aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +5 -0
  194. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  195. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +5 -0
  196. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  197. aip_agents/examples/hello_world_langgraph_stream.pyi +5 -0
  198. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  199. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +5 -0
  200. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  201. aip_agents/examples/hello_world_model_switch_cli.pyi +30 -0
  202. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  203. aip_agents/examples/hello_world_multi_agent_adk.pyi +6 -0
  204. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  205. aip_agents/examples/hello_world_multi_agent_langchain.pyi +5 -0
  206. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  207. aip_agents/examples/hello_world_multi_agent_langgraph.pyi +5 -0
  208. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  209. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +5 -0
  210. aip_agents/examples/hello_world_pii_logger.py +21 -0
  211. aip_agents/examples/hello_world_pii_logger.pyi +5 -0
  212. aip_agents/examples/hello_world_sentry.py +133 -0
  213. aip_agents/examples/hello_world_sentry.pyi +21 -0
  214. aip_agents/examples/hello_world_step_limits.py +273 -0
  215. aip_agents/examples/hello_world_step_limits.pyi +17 -0
  216. aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
  217. aip_agents/examples/hello_world_stock_a2a_server.pyi +17 -0
  218. aip_agents/examples/hello_world_tool_output_client.py +46 -0
  219. aip_agents/examples/hello_world_tool_output_client.pyi +5 -0
  220. aip_agents/examples/hello_world_tool_output_server.py +114 -0
  221. aip_agents/examples/hello_world_tool_output_server.pyi +19 -0
  222. aip_agents/examples/hitl_demo.py +724 -0
  223. aip_agents/examples/hitl_demo.pyi +67 -0
  224. aip_agents/examples/mcp_configs/configs.py +63 -0
  225. aip_agents/examples/mcp_servers/common.py +76 -0
  226. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  227. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  228. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  229. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  230. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  231. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  232. aip_agents/examples/pii_demo_langgraph_client.pyi +5 -0
  233. aip_agents/examples/pii_demo_langgraph_server.py +126 -0
  234. aip_agents/examples/pii_demo_langgraph_server.pyi +20 -0
  235. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  236. aip_agents/examples/pii_demo_multi_agent_client.pyi +5 -0
  237. aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
  238. aip_agents/examples/pii_demo_multi_agent_server.pyi +40 -0
  239. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  240. aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +5 -0
  241. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
  242. aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +19 -0
  243. aip_agents/examples/tools/__init__.py +27 -0
  244. aip_agents/examples/tools/__init__.pyi +9 -0
  245. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  246. aip_agents/examples/tools/adk_arithmetic_tools.pyi +24 -0
  247. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  248. aip_agents/examples/tools/adk_weather_tool.pyi +18 -0
  249. aip_agents/examples/tools/data_generator_tool.py +103 -0
  250. aip_agents/examples/tools/data_generator_tool.pyi +15 -0
  251. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  252. aip_agents/examples/tools/data_visualization_tool.pyi +19 -0
  253. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  254. aip_agents/examples/tools/image_artifact_tool.pyi +26 -0
  255. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  256. aip_agents/examples/tools/langchain_arithmetic_tools.pyi +17 -0
  257. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  258. aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +20 -0
  259. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  260. aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +25 -0
  261. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  262. aip_agents/examples/tools/langchain_weather_tool.pyi +19 -0
  263. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  264. aip_agents/examples/tools/langgraph_streaming_tool.pyi +43 -0
  265. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  266. aip_agents/examples/tools/mock_retrieval_tool.pyi +13 -0
  267. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  268. aip_agents/examples/tools/pii_demo_tools.pyi +54 -0
  269. aip_agents/examples/tools/random_chart_tool.py +142 -0
  270. aip_agents/examples/tools/random_chart_tool.pyi +20 -0
  271. aip_agents/examples/tools/serper_tool.py +202 -0
  272. aip_agents/examples/tools/serper_tool.pyi +16 -0
  273. aip_agents/examples/tools/stock_tools.py +82 -0
  274. aip_agents/examples/tools/stock_tools.pyi +36 -0
  275. aip_agents/examples/tools/table_generator_tool.py +167 -0
  276. aip_agents/examples/tools/table_generator_tool.pyi +22 -0
  277. aip_agents/examples/tools/time_tool.py +82 -0
  278. aip_agents/examples/tools/time_tool.pyi +15 -0
  279. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  280. aip_agents/examples/tools/weather_forecast_tool.pyi +14 -0
  281. aip_agents/executor/agent_executor.py +473 -0
  282. aip_agents/executor/base.py +48 -0
  283. aip_agents/guardrails/__init__.py +83 -0
  284. aip_agents/guardrails/__init__.pyi +6 -0
  285. aip_agents/guardrails/engines/__init__.py +69 -0
  286. aip_agents/guardrails/engines/__init__.pyi +4 -0
  287. aip_agents/guardrails/engines/base.py +90 -0
  288. aip_agents/guardrails/engines/base.pyi +61 -0
  289. aip_agents/guardrails/engines/nemo.py +101 -0
  290. aip_agents/guardrails/engines/nemo.pyi +46 -0
  291. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  292. aip_agents/guardrails/engines/phrase_matcher.pyi +48 -0
  293. aip_agents/guardrails/exceptions.py +39 -0
  294. aip_agents/guardrails/exceptions.pyi +23 -0
  295. aip_agents/guardrails/manager.py +163 -0
  296. aip_agents/guardrails/manager.pyi +42 -0
  297. aip_agents/guardrails/middleware.py +199 -0
  298. aip_agents/guardrails/middleware.pyi +87 -0
  299. aip_agents/guardrails/schemas.py +63 -0
  300. aip_agents/guardrails/schemas.pyi +43 -0
  301. aip_agents/guardrails/utils.py +45 -0
  302. aip_agents/guardrails/utils.pyi +19 -0
  303. aip_agents/mcp/__init__.py +1 -0
  304. aip_agents/mcp/__init__.pyi +0 -0
  305. aip_agents/mcp/client/__init__.py +14 -0
  306. aip_agents/mcp/client/__init__.pyi +5 -0
  307. aip_agents/mcp/client/base_mcp_client.py +369 -0
  308. aip_agents/mcp/client/base_mcp_client.pyi +148 -0
  309. aip_agents/mcp/client/connection_manager.py +193 -0
  310. aip_agents/mcp/client/connection_manager.pyi +48 -0
  311. aip_agents/mcp/client/google_adk/__init__.py +11 -0
  312. aip_agents/mcp/client/google_adk/__init__.pyi +3 -0
  313. aip_agents/mcp/client/google_adk/client.py +381 -0
  314. aip_agents/mcp/client/google_adk/client.pyi +75 -0
  315. aip_agents/mcp/client/langchain/__init__.py +11 -0
  316. aip_agents/mcp/client/langchain/__init__.pyi +3 -0
  317. aip_agents/mcp/client/langchain/client.py +265 -0
  318. aip_agents/mcp/client/langchain/client.pyi +48 -0
  319. aip_agents/mcp/client/persistent_session.py +362 -0
  320. aip_agents/mcp/client/persistent_session.pyi +113 -0
  321. aip_agents/mcp/client/session_pool.py +351 -0
  322. aip_agents/mcp/client/session_pool.pyi +101 -0
  323. aip_agents/mcp/client/transports.py +228 -0
  324. aip_agents/mcp/client/transports.pyi +123 -0
  325. aip_agents/mcp/utils/__init__.py +7 -0
  326. aip_agents/mcp/utils/__init__.pyi +0 -0
  327. aip_agents/mcp/utils/config_validator.py +139 -0
  328. aip_agents/mcp/utils/config_validator.pyi +82 -0
  329. aip_agents/memory/__init__.py +14 -0
  330. aip_agents/memory/__init__.pyi +5 -0
  331. aip_agents/memory/adapters/__init__.py +10 -0
  332. aip_agents/memory/adapters/__init__.pyi +4 -0
  333. aip_agents/memory/adapters/base_adapter.py +717 -0
  334. aip_agents/memory/adapters/base_adapter.pyi +150 -0
  335. aip_agents/memory/adapters/mem0.py +84 -0
  336. aip_agents/memory/adapters/mem0.pyi +22 -0
  337. aip_agents/memory/base.py +84 -0
  338. aip_agents/memory/base.pyi +60 -0
  339. aip_agents/memory/constants.py +49 -0
  340. aip_agents/memory/constants.pyi +25 -0
  341. aip_agents/memory/factory.py +86 -0
  342. aip_agents/memory/factory.pyi +24 -0
  343. aip_agents/memory/guidance.py +20 -0
  344. aip_agents/memory/guidance.pyi +3 -0
  345. aip_agents/memory/simple_memory.py +47 -0
  346. aip_agents/memory/simple_memory.pyi +23 -0
  347. aip_agents/middleware/__init__.py +17 -0
  348. aip_agents/middleware/__init__.pyi +5 -0
  349. aip_agents/middleware/base.py +96 -0
  350. aip_agents/middleware/base.pyi +75 -0
  351. aip_agents/middleware/manager.py +150 -0
  352. aip_agents/middleware/manager.pyi +84 -0
  353. aip_agents/middleware/todolist.py +274 -0
  354. aip_agents/middleware/todolist.pyi +125 -0
  355. aip_agents/schema/__init__.py +69 -0
  356. aip_agents/schema/__init__.pyi +9 -0
  357. aip_agents/schema/a2a.py +56 -0
  358. aip_agents/schema/a2a.pyi +40 -0
  359. aip_agents/schema/agent.py +111 -0
  360. aip_agents/schema/agent.pyi +65 -0
  361. aip_agents/schema/hitl.py +157 -0
  362. aip_agents/schema/hitl.pyi +89 -0
  363. aip_agents/schema/langgraph.py +37 -0
  364. aip_agents/schema/langgraph.pyi +28 -0
  365. aip_agents/schema/model_id.py +97 -0
  366. aip_agents/schema/model_id.pyi +54 -0
  367. aip_agents/schema/step_limit.py +108 -0
  368. aip_agents/schema/step_limit.pyi +63 -0
  369. aip_agents/schema/storage.py +40 -0
  370. aip_agents/schema/storage.pyi +21 -0
  371. aip_agents/sentry/__init__.py +11 -0
  372. aip_agents/sentry/__init__.pyi +3 -0
  373. aip_agents/sentry/sentry.py +151 -0
  374. aip_agents/sentry/sentry.pyi +48 -0
  375. aip_agents/storage/__init__.py +41 -0
  376. aip_agents/storage/__init__.pyi +8 -0
  377. aip_agents/storage/base.py +85 -0
  378. aip_agents/storage/base.pyi +58 -0
  379. aip_agents/storage/clients/__init__.py +12 -0
  380. aip_agents/storage/clients/__init__.pyi +3 -0
  381. aip_agents/storage/clients/minio_client.py +318 -0
  382. aip_agents/storage/clients/minio_client.pyi +137 -0
  383. aip_agents/storage/config.py +62 -0
  384. aip_agents/storage/config.pyi +29 -0
  385. aip_agents/storage/providers/__init__.py +15 -0
  386. aip_agents/storage/providers/__init__.pyi +5 -0
  387. aip_agents/storage/providers/base.py +106 -0
  388. aip_agents/storage/providers/base.pyi +88 -0
  389. aip_agents/storage/providers/memory.py +114 -0
  390. aip_agents/storage/providers/memory.pyi +79 -0
  391. aip_agents/storage/providers/object_storage.py +214 -0
  392. aip_agents/storage/providers/object_storage.pyi +98 -0
  393. aip_agents/tools/__init__.py +53 -0
  394. aip_agents/tools/__init__.pyi +9 -0
  395. aip_agents/tools/browser_use/__init__.py +82 -0
  396. aip_agents/tools/browser_use/__init__.pyi +14 -0
  397. aip_agents/tools/browser_use/action_parser.py +103 -0
  398. aip_agents/tools/browser_use/action_parser.pyi +18 -0
  399. aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
  400. aip_agents/tools/browser_use/browser_use_tool.pyi +50 -0
  401. aip_agents/tools/browser_use/llm_config.py +120 -0
  402. aip_agents/tools/browser_use/llm_config.pyi +52 -0
  403. aip_agents/tools/browser_use/minio_storage.py +198 -0
  404. aip_agents/tools/browser_use/minio_storage.pyi +109 -0
  405. aip_agents/tools/browser_use/schemas.py +119 -0
  406. aip_agents/tools/browser_use/schemas.pyi +32 -0
  407. aip_agents/tools/browser_use/session.py +76 -0
  408. aip_agents/tools/browser_use/session.pyi +4 -0
  409. aip_agents/tools/browser_use/session_errors.py +132 -0
  410. aip_agents/tools/browser_use/session_errors.pyi +53 -0
  411. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  412. aip_agents/tools/browser_use/steel_session_recording.pyi +63 -0
  413. aip_agents/tools/browser_use/streaming.py +813 -0
  414. aip_agents/tools/browser_use/streaming.pyi +81 -0
  415. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  416. aip_agents/tools/browser_use/structured_data_parser.pyi +86 -0
  417. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  418. aip_agents/tools/browser_use/structured_data_recovery.pyi +43 -0
  419. aip_agents/tools/browser_use/types.py +78 -0
  420. aip_agents/tools/browser_use/types.pyi +45 -0
  421. aip_agents/tools/code_sandbox/__init__.py +26 -0
  422. aip_agents/tools/code_sandbox/__init__.pyi +3 -0
  423. aip_agents/tools/code_sandbox/constant.py +13 -0
  424. aip_agents/tools/code_sandbox/constant.pyi +4 -0
  425. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +306 -0
  426. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +102 -0
  427. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  428. aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +29 -0
  429. aip_agents/tools/constants.py +177 -0
  430. aip_agents/tools/constants.pyi +138 -0
  431. aip_agents/tools/document_loader/__init__.py +44 -0
  432. aip_agents/tools/document_loader/__init__.pyi +7 -0
  433. aip_agents/tools/document_loader/base_reader.py +302 -0
  434. aip_agents/tools/document_loader/base_reader.pyi +75 -0
  435. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  436. aip_agents/tools/document_loader/docx_reader_tool.pyi +10 -0
  437. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  438. aip_agents/tools/document_loader/excel_reader_tool.pyi +26 -0
  439. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  440. aip_agents/tools/document_loader/pdf_reader_tool.pyi +11 -0
  441. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  442. aip_agents/tools/document_loader/pdf_splitter.pyi +18 -0
  443. aip_agents/tools/gl_connector/__init__.py +5 -0
  444. aip_agents/tools/gl_connector/__init__.pyi +3 -0
  445. aip_agents/tools/gl_connector/tool.py +383 -0
  446. aip_agents/tools/gl_connector/tool.pyi +74 -0
  447. aip_agents/tools/gl_connector_tools.py +119 -0
  448. aip_agents/tools/gl_connector_tools.pyi +39 -0
  449. aip_agents/tools/memory_search/__init__.py +22 -0
  450. aip_agents/tools/memory_search/__init__.pyi +5 -0
  451. aip_agents/tools/memory_search/base.py +200 -0
  452. aip_agents/tools/memory_search/base.pyi +69 -0
  453. aip_agents/tools/memory_search/mem0.py +258 -0
  454. aip_agents/tools/memory_search/mem0.pyi +19 -0
  455. aip_agents/tools/memory_search/schema.py +48 -0
  456. aip_agents/tools/memory_search/schema.pyi +15 -0
  457. aip_agents/tools/memory_search_tool.py +26 -0
  458. aip_agents/tools/memory_search_tool.pyi +3 -0
  459. aip_agents/tools/time_tool.py +117 -0
  460. aip_agents/tools/time_tool.pyi +16 -0
  461. aip_agents/tools/tool_config_injector.py +300 -0
  462. aip_agents/tools/tool_config_injector.pyi +26 -0
  463. aip_agents/tools/web_search/__init__.py +15 -0
  464. aip_agents/tools/web_search/__init__.pyi +3 -0
  465. aip_agents/tools/web_search/serper_tool.py +187 -0
  466. aip_agents/tools/web_search/serper_tool.pyi +19 -0
  467. aip_agents/types/__init__.py +70 -0
  468. aip_agents/types/__init__.pyi +36 -0
  469. aip_agents/types/a2a_events.py +13 -0
  470. aip_agents/types/a2a_events.pyi +3 -0
  471. aip_agents/utils/__init__.py +79 -0
  472. aip_agents/utils/__init__.pyi +11 -0
  473. aip_agents/utils/a2a_connector.py +1757 -0
  474. aip_agents/utils/a2a_connector.pyi +146 -0
  475. aip_agents/utils/artifact_helpers.py +502 -0
  476. aip_agents/utils/artifact_helpers.pyi +203 -0
  477. aip_agents/utils/constants.py +22 -0
  478. aip_agents/utils/constants.pyi +10 -0
  479. aip_agents/utils/datetime/__init__.py +34 -0
  480. aip_agents/utils/datetime/__init__.pyi +4 -0
  481. aip_agents/utils/datetime/normalization.py +231 -0
  482. aip_agents/utils/datetime/normalization.pyi +95 -0
  483. aip_agents/utils/datetime/timezone.py +206 -0
  484. aip_agents/utils/datetime/timezone.pyi +48 -0
  485. aip_agents/utils/env_loader.py +27 -0
  486. aip_agents/utils/env_loader.pyi +10 -0
  487. aip_agents/utils/event_handler_registry.py +58 -0
  488. aip_agents/utils/event_handler_registry.pyi +23 -0
  489. aip_agents/utils/file_prompt_utils.py +176 -0
  490. aip_agents/utils/file_prompt_utils.pyi +21 -0
  491. aip_agents/utils/final_response_builder.py +211 -0
  492. aip_agents/utils/final_response_builder.pyi +34 -0
  493. aip_agents/utils/formatter_llm_client.py +231 -0
  494. aip_agents/utils/formatter_llm_client.pyi +71 -0
  495. aip_agents/utils/langgraph/__init__.py +19 -0
  496. aip_agents/utils/langgraph/__init__.pyi +3 -0
  497. aip_agents/utils/langgraph/converter.py +128 -0
  498. aip_agents/utils/langgraph/converter.pyi +49 -0
  499. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  500. aip_agents/utils/langgraph/tool_managers/__init__.pyi +5 -0
  501. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  502. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +35 -0
  503. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  504. aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +48 -0
  505. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
  506. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +56 -0
  507. aip_agents/utils/langgraph/tool_output_management.py +967 -0
  508. aip_agents/utils/langgraph/tool_output_management.pyi +292 -0
  509. aip_agents/utils/logger.py +195 -0
  510. aip_agents/utils/logger.pyi +60 -0
  511. aip_agents/utils/metadata/__init__.py +27 -0
  512. aip_agents/utils/metadata/__init__.pyi +5 -0
  513. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  514. aip_agents/utils/metadata/activity_metadata_helper.pyi +25 -0
  515. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  516. aip_agents/utils/metadata/activity_narrative/__init__.pyi +7 -0
  517. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  518. aip_agents/utils/metadata/activity_narrative/builder.pyi +35 -0
  519. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  520. aip_agents/utils/metadata/activity_narrative/constants.pyi +10 -0
  521. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  522. aip_agents/utils/metadata/activity_narrative/context.pyi +32 -0
  523. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  524. aip_agents/utils/metadata/activity_narrative/formatters.pyi +48 -0
  525. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  526. aip_agents/utils/metadata/activity_narrative/utils.pyi +12 -0
  527. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  528. aip_agents/utils/metadata/schemas/__init__.pyi +4 -0
  529. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  530. aip_agents/utils/metadata/schemas/activity_schema.pyi +18 -0
  531. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  532. aip_agents/utils/metadata/schemas/thinking_schema.pyi +20 -0
  533. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  534. aip_agents/utils/metadata/thinking_metadata_helper.pyi +4 -0
  535. aip_agents/utils/metadata_helper.py +358 -0
  536. aip_agents/utils/metadata_helper.pyi +117 -0
  537. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  538. aip_agents/utils/name_preprocessor/__init__.pyi +6 -0
  539. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  540. aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +52 -0
  541. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  542. aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +38 -0
  543. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  544. aip_agents/utils/name_preprocessor/name_preprocessor.pyi +41 -0
  545. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  546. aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +34 -0
  547. aip_agents/utils/pii/__init__.py +25 -0
  548. aip_agents/utils/pii/__init__.pyi +5 -0
  549. aip_agents/utils/pii/pii_handler.py +397 -0
  550. aip_agents/utils/pii/pii_handler.pyi +96 -0
  551. aip_agents/utils/pii/pii_helper.py +207 -0
  552. aip_agents/utils/pii/pii_helper.pyi +78 -0
  553. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  554. aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +73 -0
  555. aip_agents/utils/reference_helper.py +273 -0
  556. aip_agents/utils/reference_helper.pyi +81 -0
  557. aip_agents/utils/sse_chunk_transformer.py +831 -0
  558. aip_agents/utils/sse_chunk_transformer.pyi +166 -0
  559. aip_agents/utils/step_limit_manager.py +265 -0
  560. aip_agents/utils/step_limit_manager.pyi +112 -0
  561. aip_agents/utils/token_usage_helper.py +156 -0
  562. aip_agents/utils/token_usage_helper.pyi +60 -0
  563. aip_agents_binary-0.5.25b1.dist-info/METADATA +681 -0
  564. aip_agents_binary-0.5.25b1.dist-info/RECORD +566 -0
  565. aip_agents_binary-0.5.25b1.dist-info/WHEEL +5 -0
  566. aip_agents_binary-0.5.25b1.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