aip-agents-binary 0.6.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aip-agents-binary might be problematic. Click here for more details.

Files changed (612) 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 +3037 -0
  22. aip_agents/agent/base_langgraph_agent.pyi +233 -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 +767 -0
  52. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +50 -0
  53. aip_agents/agent/langgraph_react_agent.py +2856 -0
  54. aip_agents/agent/langgraph_react_agent.pyi +170 -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/hello_world_a2a_google_adk_client.py +49 -0
  75. aip_agents/examples/hello_world_a2a_google_adk_client.pyi +9 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +9 -0
  78. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  79. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +9 -0
  80. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  81. aip_agents/examples/hello_world_a2a_google_adk_server.pyi +15 -0
  82. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  83. aip_agents/examples/hello_world_a2a_langchain_client.pyi +5 -0
  84. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  85. aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +5 -0
  86. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  87. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +5 -0
  88. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  89. aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +5 -0
  90. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  91. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +5 -0
  92. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  93. aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +15 -0
  94. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  95. aip_agents/examples/hello_world_a2a_langchain_server.pyi +15 -0
  96. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  97. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +15 -0
  98. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  99. aip_agents/examples/hello_world_a2a_langflow_client.pyi +9 -0
  100. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  101. aip_agents/examples/hello_world_a2a_langflow_server.pyi +14 -0
  102. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  103. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +5 -0
  104. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  105. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +5 -0
  106. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  107. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +16 -0
  108. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  109. aip_agents/examples/hello_world_a2a_langgraph_client.pyi +9 -0
  110. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  111. aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +9 -0
  112. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  113. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +2 -0
  114. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  115. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +9 -0
  116. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  117. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +5 -0
  118. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  119. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +5 -0
  120. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  121. aip_agents/examples/hello_world_a2a_langgraph_server.pyi +14 -0
  122. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  123. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +15 -0
  124. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  125. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +15 -0
  126. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  127. aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +48 -0
  128. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  129. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +48 -0
  130. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  131. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +45 -0
  132. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  133. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +5 -0
  134. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  135. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +15 -0
  136. aip_agents/examples/hello_world_google_adk.py +41 -0
  137. aip_agents/examples/hello_world_google_adk.pyi +5 -0
  138. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  139. aip_agents/examples/hello_world_google_adk_mcp_http.pyi +5 -0
  140. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  141. aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +5 -0
  142. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  143. aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +5 -0
  144. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  145. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +5 -0
  146. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  147. aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +5 -0
  148. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  149. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +5 -0
  150. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  151. aip_agents/examples/hello_world_google_adk_stream.pyi +5 -0
  152. aip_agents/examples/hello_world_langchain.py +28 -0
  153. aip_agents/examples/hello_world_langchain.pyi +5 -0
  154. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  155. aip_agents/examples/hello_world_langchain_lm_invoker.pyi +2 -0
  156. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  157. aip_agents/examples/hello_world_langchain_mcp_http.pyi +5 -0
  158. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  159. aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +16 -0
  160. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  161. aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +5 -0
  162. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  163. aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +18 -0
  164. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  165. aip_agents/examples/hello_world_langchain_mcp_sse.pyi +5 -0
  166. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  167. aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +5 -0
  168. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  169. aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +5 -0
  170. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  171. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +5 -0
  172. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  173. aip_agents/examples/hello_world_langchain_stream.pyi +5 -0
  174. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  175. aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +5 -0
  176. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  177. aip_agents/examples/hello_world_langflow_agent.pyi +35 -0
  178. aip_agents/examples/hello_world_langgraph.py +39 -0
  179. aip_agents/examples/hello_world_langgraph.pyi +5 -0
  180. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  181. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +5 -0
  182. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  183. aip_agents/examples/hello_world_langgraph_mcp_http.pyi +5 -0
  184. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  185. aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +5 -0
  186. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  187. aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +5 -0
  188. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  189. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +5 -0
  190. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  191. aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +5 -0
  192. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  193. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +5 -0
  194. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  195. aip_agents/examples/hello_world_langgraph_stream.pyi +5 -0
  196. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  197. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +5 -0
  198. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  199. aip_agents/examples/hello_world_model_switch_cli.pyi +30 -0
  200. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  201. aip_agents/examples/hello_world_multi_agent_adk.pyi +6 -0
  202. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  203. aip_agents/examples/hello_world_multi_agent_langchain.pyi +5 -0
  204. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  205. aip_agents/examples/hello_world_multi_agent_langgraph.pyi +5 -0
  206. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  207. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +5 -0
  208. aip_agents/examples/hello_world_pii_logger.py +21 -0
  209. aip_agents/examples/hello_world_pii_logger.pyi +5 -0
  210. aip_agents/examples/hello_world_ptc.py +49 -0
  211. aip_agents/examples/hello_world_ptc.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 +55 -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 +228 -0
  310. aip_agents/mcp/client/connection_manager.pyi +51 -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 +612 -0
  320. aip_agents/mcp/client/persistent_session.pyi +122 -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 +263 -0
  324. aip_agents/mcp/client/transports.pyi +132 -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 +811 -0
  334. aip_agents/memory/adapters/base_adapter.pyi +176 -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/ptc/__init__.py +48 -0
  356. aip_agents/ptc/__init__.pyi +10 -0
  357. aip_agents/ptc/doc_gen.py +122 -0
  358. aip_agents/ptc/doc_gen.pyi +40 -0
  359. aip_agents/ptc/exceptions.py +39 -0
  360. aip_agents/ptc/exceptions.pyi +22 -0
  361. aip_agents/ptc/executor.py +143 -0
  362. aip_agents/ptc/executor.pyi +73 -0
  363. aip_agents/ptc/mcp/__init__.py +45 -0
  364. aip_agents/ptc/mcp/__init__.pyi +7 -0
  365. aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
  366. aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
  367. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  368. aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
  369. aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
  370. aip_agents/ptc/naming.py +184 -0
  371. aip_agents/ptc/naming.pyi +76 -0
  372. aip_agents/ptc/payload.py +26 -0
  373. aip_agents/ptc/payload.pyi +15 -0
  374. aip_agents/ptc/prompt_builder.py +571 -0
  375. aip_agents/ptc/prompt_builder.pyi +55 -0
  376. aip_agents/ptc/ptc_helper.py +16 -0
  377. aip_agents/ptc/ptc_helper.pyi +1 -0
  378. aip_agents/ptc/sandbox_bridge.py +58 -0
  379. aip_agents/ptc/sandbox_bridge.pyi +25 -0
  380. aip_agents/ptc/template_utils.py +33 -0
  381. aip_agents/ptc/template_utils.pyi +13 -0
  382. aip_agents/ptc/templates/__init__.py +1 -0
  383. aip_agents/ptc/templates/__init__.pyi +0 -0
  384. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  385. aip_agents/sandbox/__init__.py +43 -0
  386. aip_agents/sandbox/__init__.pyi +5 -0
  387. aip_agents/sandbox/defaults.py +9 -0
  388. aip_agents/sandbox/defaults.pyi +2 -0
  389. aip_agents/sandbox/e2b_runtime.py +267 -0
  390. aip_agents/sandbox/e2b_runtime.pyi +51 -0
  391. aip_agents/sandbox/template_builder.py +131 -0
  392. aip_agents/sandbox/template_builder.pyi +36 -0
  393. aip_agents/sandbox/types.py +24 -0
  394. aip_agents/sandbox/types.pyi +14 -0
  395. aip_agents/sandbox/validation.py +50 -0
  396. aip_agents/sandbox/validation.pyi +20 -0
  397. aip_agents/schema/__init__.py +69 -0
  398. aip_agents/schema/__init__.pyi +9 -0
  399. aip_agents/schema/a2a.py +56 -0
  400. aip_agents/schema/a2a.pyi +40 -0
  401. aip_agents/schema/agent.py +111 -0
  402. aip_agents/schema/agent.pyi +65 -0
  403. aip_agents/schema/hitl.py +157 -0
  404. aip_agents/schema/hitl.pyi +89 -0
  405. aip_agents/schema/langgraph.py +37 -0
  406. aip_agents/schema/langgraph.pyi +28 -0
  407. aip_agents/schema/model_id.py +97 -0
  408. aip_agents/schema/model_id.pyi +54 -0
  409. aip_agents/schema/step_limit.py +108 -0
  410. aip_agents/schema/step_limit.pyi +63 -0
  411. aip_agents/schema/storage.py +40 -0
  412. aip_agents/schema/storage.pyi +21 -0
  413. aip_agents/sentry/__init__.py +11 -0
  414. aip_agents/sentry/__init__.pyi +3 -0
  415. aip_agents/sentry/sentry.py +151 -0
  416. aip_agents/sentry/sentry.pyi +48 -0
  417. aip_agents/storage/__init__.py +41 -0
  418. aip_agents/storage/__init__.pyi +8 -0
  419. aip_agents/storage/base.py +85 -0
  420. aip_agents/storage/base.pyi +58 -0
  421. aip_agents/storage/clients/__init__.py +12 -0
  422. aip_agents/storage/clients/__init__.pyi +3 -0
  423. aip_agents/storage/clients/minio_client.py +318 -0
  424. aip_agents/storage/clients/minio_client.pyi +137 -0
  425. aip_agents/storage/config.py +62 -0
  426. aip_agents/storage/config.pyi +29 -0
  427. aip_agents/storage/providers/__init__.py +15 -0
  428. aip_agents/storage/providers/__init__.pyi +5 -0
  429. aip_agents/storage/providers/base.py +106 -0
  430. aip_agents/storage/providers/base.pyi +88 -0
  431. aip_agents/storage/providers/memory.py +114 -0
  432. aip_agents/storage/providers/memory.pyi +79 -0
  433. aip_agents/storage/providers/object_storage.py +214 -0
  434. aip_agents/storage/providers/object_storage.pyi +98 -0
  435. aip_agents/tools/__init__.py +64 -0
  436. aip_agents/tools/__init__.pyi +11 -0
  437. aip_agents/tools/browser_use/__init__.py +82 -0
  438. aip_agents/tools/browser_use/__init__.pyi +14 -0
  439. aip_agents/tools/browser_use/action_parser.py +103 -0
  440. aip_agents/tools/browser_use/action_parser.pyi +18 -0
  441. aip_agents/tools/browser_use/browser_use_tool.py +1120 -0
  442. aip_agents/tools/browser_use/browser_use_tool.pyi +50 -0
  443. aip_agents/tools/browser_use/llm_config.py +120 -0
  444. aip_agents/tools/browser_use/llm_config.pyi +52 -0
  445. aip_agents/tools/browser_use/minio_storage.py +198 -0
  446. aip_agents/tools/browser_use/minio_storage.pyi +109 -0
  447. aip_agents/tools/browser_use/schemas.py +119 -0
  448. aip_agents/tools/browser_use/schemas.pyi +32 -0
  449. aip_agents/tools/browser_use/session.py +76 -0
  450. aip_agents/tools/browser_use/session.pyi +4 -0
  451. aip_agents/tools/browser_use/session_errors.py +132 -0
  452. aip_agents/tools/browser_use/session_errors.pyi +53 -0
  453. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  454. aip_agents/tools/browser_use/steel_session_recording.pyi +63 -0
  455. aip_agents/tools/browser_use/streaming.py +815 -0
  456. aip_agents/tools/browser_use/streaming.pyi +81 -0
  457. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  458. aip_agents/tools/browser_use/structured_data_parser.pyi +86 -0
  459. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  460. aip_agents/tools/browser_use/structured_data_recovery.pyi +43 -0
  461. aip_agents/tools/browser_use/types.py +78 -0
  462. aip_agents/tools/browser_use/types.pyi +45 -0
  463. aip_agents/tools/code_sandbox/__init__.py +26 -0
  464. aip_agents/tools/code_sandbox/__init__.pyi +3 -0
  465. aip_agents/tools/code_sandbox/constant.py +13 -0
  466. aip_agents/tools/code_sandbox/constant.pyi +4 -0
  467. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +306 -0
  468. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +102 -0
  469. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  470. aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +29 -0
  471. aip_agents/tools/constants.py +177 -0
  472. aip_agents/tools/constants.pyi +138 -0
  473. aip_agents/tools/date_range_tool.py +554 -0
  474. aip_agents/tools/date_range_tool.pyi +21 -0
  475. aip_agents/tools/document_loader/__init__.py +44 -0
  476. aip_agents/tools/document_loader/__init__.pyi +7 -0
  477. aip_agents/tools/document_loader/base_reader.py +302 -0
  478. aip_agents/tools/document_loader/base_reader.pyi +75 -0
  479. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  480. aip_agents/tools/document_loader/docx_reader_tool.pyi +10 -0
  481. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  482. aip_agents/tools/document_loader/excel_reader_tool.pyi +26 -0
  483. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  484. aip_agents/tools/document_loader/pdf_reader_tool.pyi +11 -0
  485. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  486. aip_agents/tools/document_loader/pdf_splitter.pyi +18 -0
  487. aip_agents/tools/execute_ptc_code.py +308 -0
  488. aip_agents/tools/execute_ptc_code.pyi +90 -0
  489. aip_agents/tools/gl_connector/__init__.py +5 -0
  490. aip_agents/tools/gl_connector/__init__.pyi +3 -0
  491. aip_agents/tools/gl_connector/tool.py +383 -0
  492. aip_agents/tools/gl_connector/tool.pyi +74 -0
  493. aip_agents/tools/gl_connector_tools.py +119 -0
  494. aip_agents/tools/gl_connector_tools.pyi +39 -0
  495. aip_agents/tools/memory_search/__init__.py +29 -0
  496. aip_agents/tools/memory_search/__init__.pyi +5 -0
  497. aip_agents/tools/memory_search/base.py +200 -0
  498. aip_agents/tools/memory_search/base.pyi +69 -0
  499. aip_agents/tools/memory_search/mem0.py +365 -0
  500. aip_agents/tools/memory_search/mem0.pyi +29 -0
  501. aip_agents/tools/memory_search/schema.py +81 -0
  502. aip_agents/tools/memory_search/schema.pyi +25 -0
  503. aip_agents/tools/memory_search_tool.py +34 -0
  504. aip_agents/tools/memory_search_tool.pyi +3 -0
  505. aip_agents/tools/time_tool.py +117 -0
  506. aip_agents/tools/time_tool.pyi +16 -0
  507. aip_agents/tools/tool_config_injector.py +300 -0
  508. aip_agents/tools/tool_config_injector.pyi +26 -0
  509. aip_agents/tools/web_search/__init__.py +15 -0
  510. aip_agents/tools/web_search/__init__.pyi +3 -0
  511. aip_agents/tools/web_search/serper_tool.py +187 -0
  512. aip_agents/tools/web_search/serper_tool.pyi +19 -0
  513. aip_agents/types/__init__.py +70 -0
  514. aip_agents/types/__init__.pyi +36 -0
  515. aip_agents/types/a2a_events.py +13 -0
  516. aip_agents/types/a2a_events.pyi +3 -0
  517. aip_agents/utils/__init__.py +79 -0
  518. aip_agents/utils/__init__.pyi +11 -0
  519. aip_agents/utils/a2a_connector.py +1757 -0
  520. aip_agents/utils/a2a_connector.pyi +146 -0
  521. aip_agents/utils/artifact_helpers.py +502 -0
  522. aip_agents/utils/artifact_helpers.pyi +203 -0
  523. aip_agents/utils/constants.py +22 -0
  524. aip_agents/utils/constants.pyi +10 -0
  525. aip_agents/utils/datetime/__init__.py +34 -0
  526. aip_agents/utils/datetime/__init__.pyi +4 -0
  527. aip_agents/utils/datetime/normalization.py +231 -0
  528. aip_agents/utils/datetime/normalization.pyi +95 -0
  529. aip_agents/utils/datetime/timezone.py +206 -0
  530. aip_agents/utils/datetime/timezone.pyi +48 -0
  531. aip_agents/utils/env_loader.py +27 -0
  532. aip_agents/utils/env_loader.pyi +10 -0
  533. aip_agents/utils/event_handler_registry.py +58 -0
  534. aip_agents/utils/event_handler_registry.pyi +23 -0
  535. aip_agents/utils/file_prompt_utils.py +176 -0
  536. aip_agents/utils/file_prompt_utils.pyi +21 -0
  537. aip_agents/utils/final_response_builder.py +211 -0
  538. aip_agents/utils/final_response_builder.pyi +34 -0
  539. aip_agents/utils/formatter_llm_client.py +231 -0
  540. aip_agents/utils/formatter_llm_client.pyi +71 -0
  541. aip_agents/utils/langgraph/__init__.py +19 -0
  542. aip_agents/utils/langgraph/__init__.pyi +3 -0
  543. aip_agents/utils/langgraph/converter.py +128 -0
  544. aip_agents/utils/langgraph/converter.pyi +49 -0
  545. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  546. aip_agents/utils/langgraph/tool_managers/__init__.pyi +5 -0
  547. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  548. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +35 -0
  549. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  550. aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +48 -0
  551. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1096 -0
  552. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +56 -0
  553. aip_agents/utils/langgraph/tool_output_management.py +1047 -0
  554. aip_agents/utils/langgraph/tool_output_management.pyi +329 -0
  555. aip_agents/utils/logger.py +195 -0
  556. aip_agents/utils/logger.pyi +60 -0
  557. aip_agents/utils/metadata/__init__.py +27 -0
  558. aip_agents/utils/metadata/__init__.pyi +5 -0
  559. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  560. aip_agents/utils/metadata/activity_metadata_helper.pyi +25 -0
  561. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  562. aip_agents/utils/metadata/activity_narrative/__init__.pyi +7 -0
  563. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  564. aip_agents/utils/metadata/activity_narrative/builder.pyi +35 -0
  565. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  566. aip_agents/utils/metadata/activity_narrative/constants.pyi +10 -0
  567. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  568. aip_agents/utils/metadata/activity_narrative/context.pyi +32 -0
  569. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  570. aip_agents/utils/metadata/activity_narrative/formatters.pyi +48 -0
  571. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  572. aip_agents/utils/metadata/activity_narrative/utils.pyi +12 -0
  573. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  574. aip_agents/utils/metadata/schemas/__init__.pyi +4 -0
  575. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  576. aip_agents/utils/metadata/schemas/activity_schema.pyi +18 -0
  577. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  578. aip_agents/utils/metadata/schemas/thinking_schema.pyi +20 -0
  579. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  580. aip_agents/utils/metadata/thinking_metadata_helper.pyi +4 -0
  581. aip_agents/utils/metadata_helper.py +358 -0
  582. aip_agents/utils/metadata_helper.pyi +117 -0
  583. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  584. aip_agents/utils/name_preprocessor/__init__.pyi +6 -0
  585. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  586. aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +52 -0
  587. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  588. aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +38 -0
  589. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  590. aip_agents/utils/name_preprocessor/name_preprocessor.pyi +41 -0
  591. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  592. aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +34 -0
  593. aip_agents/utils/pii/__init__.py +25 -0
  594. aip_agents/utils/pii/__init__.pyi +5 -0
  595. aip_agents/utils/pii/pii_handler.py +397 -0
  596. aip_agents/utils/pii/pii_handler.pyi +96 -0
  597. aip_agents/utils/pii/pii_helper.py +207 -0
  598. aip_agents/utils/pii/pii_helper.pyi +78 -0
  599. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  600. aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +73 -0
  601. aip_agents/utils/reference_helper.py +273 -0
  602. aip_agents/utils/reference_helper.pyi +81 -0
  603. aip_agents/utils/sse_chunk_transformer.py +831 -0
  604. aip_agents/utils/sse_chunk_transformer.pyi +166 -0
  605. aip_agents/utils/step_limit_manager.py +265 -0
  606. aip_agents/utils/step_limit_manager.pyi +112 -0
  607. aip_agents/utils/token_usage_helper.py +156 -0
  608. aip_agents/utils/token_usage_helper.pyi +60 -0
  609. aip_agents_binary-0.6.4.dist-info/METADATA +673 -0
  610. aip_agents_binary-0.6.4.dist-info/RECORD +612 -0
  611. aip_agents_binary-0.6.4.dist-info/WHEEL +5 -0
  612. aip_agents_binary-0.6.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,970 @@
1
+ """Base class for concrete agent implementations.
2
+
3
+ This class provides common functionalities like A2A client capabilities.
4
+
5
+ Authors:
6
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
7
+ Raymond Christopher (raymond.christopher@gdplabs.id)
8
+ """
9
+
10
+ import asyncio
11
+ from collections.abc import AsyncGenerator
12
+ from importlib import import_module
13
+ from pathlib import Path
14
+ from typing import Any
15
+ from warnings import warn
16
+
17
+ import httpx
18
+ from a2a.server.apps import A2AStarletteApplication
19
+ from a2a.server.request_handlers import DefaultRequestHandler
20
+ from a2a.server.tasks import InMemoryTaskStore
21
+ from a2a.types import AgentCard
22
+ from gllm_core.utils.retry import RetryConfig
23
+ from gllm_inference.builder import build_lm_invoker
24
+ from gllm_inference.lm_invoker.lm_invoker import BaseLMInvoker
25
+ from langchain_core.language_models import BaseChatModel
26
+ from starlette.applications import Starlette
27
+
28
+ from aip_agents.agent.interface import AgentInterface
29
+ from aip_agents.credentials.manager import CredentialsManager
30
+ from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
31
+ from aip_agents.schema.agent import A2AClientConfig, AgentConfig, BaseAgentConfig, CredentialType
32
+ from aip_agents.schema.model_id import ModelId, ModelProvider
33
+ from aip_agents.utils.a2a_connector import A2AConnector
34
+ from aip_agents.utils.logger import get_logger
35
+ from aip_agents.utils.name_preprocessor.name_preprocessor import NamePreprocessor
36
+
37
+ logger = get_logger(__name__)
38
+
39
+
40
+ def _get_agent_executor_mapping() -> dict[str, str]:
41
+ """Map agent class names to their executor import paths."""
42
+ return {
43
+ "LangGraphReactAgent": "aip_agents.a2a.server.langgraph_executor.LangGraphA2AExecutor",
44
+ "LangGraphAgent": "aip_agents.a2a.server.langgraph_executor.LangGraphA2AExecutor",
45
+ "LangChainAgent": "aip_agents.a2a.server.langgraph_executor.LangGraphA2AExecutor",
46
+ "LangflowAgent": "aip_agents.a2a.server.langflow_executor.LangflowA2AExecutor",
47
+ "GoogleADKAgent": "aip_agents.a2a.server.google_adk_executor.GoogleADKExecutor",
48
+ }
49
+
50
+
51
+ AGENT_EXECUTOR_MAPPING = _get_agent_executor_mapping()
52
+
53
+
54
+ def _load_executor_class(candidate: Any) -> type[Any]:
55
+ """Resolve an executor class from a dotted path or a direct reference.
56
+
57
+ Args:
58
+ candidate (Any): Either a string path (e.g., "module.Class") or a class/type object.
59
+
60
+ Returns:
61
+ type[Any]: The resolved executor class.
62
+ """
63
+ if isinstance(candidate, str):
64
+ module_name, class_name = candidate.rsplit(".", 1)
65
+ module = import_module(module_name)
66
+ return getattr(module, class_name)
67
+
68
+ if isinstance(candidate, type): # Already a class reference
69
+ return candidate
70
+
71
+ if callable(candidate):
72
+ return candidate # type: ignore[return-value]
73
+
74
+ raise TypeError(f"Unsupported executor mapping entry: {candidate!r}")
75
+
76
+
77
+ def _get_executor_class_for_agent(agent: "BaseAgent") -> type[Any]:
78
+ """Resolve the appropriate executor class for the given agent instance.
79
+
80
+ Args:
81
+ agent (BaseAgent): The agent instance to find an executor for.
82
+
83
+ Returns:
84
+ type[Any]: The appropriate executor class for the agent.
85
+ """
86
+ for cls in agent.__class__.__mro__:
87
+ executor_path = AGENT_EXECUTOR_MAPPING.get(cls.__name__)
88
+ if executor_path:
89
+ return _load_executor_class(executor_path)
90
+ raise KeyError(f"No A2A executor registered for agent class '{agent.__class__.__name__}'")
91
+
92
+
93
+ DEFAULT_RETRY_CONFIG = RetryConfig(max_retries=5, timeout=240.0)
94
+
95
+ LM_EXCLUDE_FIELDS = {
96
+ "lm_base_url",
97
+ "lm_api_key",
98
+ "lm_name",
99
+ "lm_provider",
100
+ "lm_hyperparameters",
101
+ "lm_retry_config",
102
+ "lm_credentials",
103
+ }
104
+
105
+ CUSTOM_PROVIDERS = {
106
+ "openai-compatible/": ModelProvider.OPENAI_COMPATIBLE,
107
+ "azure-openai/": ModelProvider.AZURE_OPENAI,
108
+ }
109
+
110
+ OUTPUT_ANALYTICS_KEY = "output_analytics"
111
+
112
+
113
+ class BaseAgent(AgentInterface):
114
+ """Base class for agents, providing common A2A client method implementations.
115
+
116
+ Concrete agent implementations (e.g., LangGraphAgent, GoogleADKAgent)
117
+ should inherit from this class if they need to utilize the shared A2A
118
+ client functionalities.
119
+
120
+ This class now supports flexible model handling:
121
+ - model: Optional[Any] - can be an lm_invoker, string/ModelId, LangChain BaseChatModel, or other types
122
+ - Automatically sets self.lm_invoker if an lm_invoker is provided or can be built
123
+ - Stores the original model in self.model for subclass use
124
+ - Enhanced credential support with automatic type detection
125
+ """
126
+
127
+ def __init__( # noqa: PLR0913
128
+ self,
129
+ name: str,
130
+ instruction: str,
131
+ description: str | None = None,
132
+ model: Any | None = None,
133
+ tools: list[Any] | None = None,
134
+ config: BaseAgentConfig | dict[str, Any] | None = None,
135
+ tool_configs: dict[str, Any] | None = None,
136
+ **kwargs: Any,
137
+ ):
138
+ """Initializes the BaseAgent.
139
+
140
+ Args:
141
+ name: The name of the agent.
142
+ instruction: The core directive or system prompt for the agent.
143
+ description: Human-readable description. Defaults to instruction if not provided.
144
+ model: The model to use. Can be:
145
+ - BaseLMInvoker instance (will be set as self.lm_invoker)
146
+ - String or ModelId (will build an lm_invoker)
147
+ - LangChain BaseChatModel (will be stored in self.model)
148
+ - Any other type (will be stored in self.model)
149
+ tools: List of tools available to the agent.
150
+ config: Additional configuration for the agent. Can be a BaseAgentConfig instance or dict.
151
+ tool_configs: Default tool configurations applied to all tool calls from this agent.
152
+ **kwargs: Additional keyword arguments for AgentInterface.
153
+ """
154
+ # Convert config to BaseAgentConfig if it's a dict (backward compatibility)
155
+ processed_config = self._process_config(config, tools)
156
+
157
+ # Process model parameter to set up lm_invoker and model attributes
158
+ processed_lm_invoker, processed_model = self._process_model_parameter(
159
+ name, model, tools or [], processed_config
160
+ )
161
+
162
+ # Pass the lm_invoker to the parent class
163
+ super().__init__(
164
+ name=name,
165
+ instruction=instruction,
166
+ description=description,
167
+ lm_invoker=processed_lm_invoker,
168
+ config=processed_config,
169
+ **kwargs,
170
+ )
171
+
172
+ # Store processed model and other attributes
173
+ self.model = processed_model
174
+ self.tools = tools or []
175
+ # Private MCP configuration to prevent tampering and maintain sync
176
+ self._mcp_config: dict[str, dict[str, Any]] = {}
177
+ self.tool_configs = tool_configs or {}
178
+
179
+ self._mcp_tools_initialized: bool = False
180
+ self.mcp_client: BaseMCPClient | None = None
181
+ self._mcp_init_lock: asyncio.Lock = asyncio.Lock()
182
+
183
+ self.name_preprocessor = self.get_name_preprocessor()
184
+
185
+ def get_name_preprocessor(self) -> NamePreprocessor:
186
+ """Get the name preprocessor based on the provider.
187
+
188
+ This will be used to correct the agent name and tool name. (mostly tool name)
189
+
190
+ Returns:
191
+ NamePreprocessor: The name preprocessor for the model.
192
+ """
193
+ return NamePreprocessor(self.model_provider)
194
+
195
+ @property
196
+ def model_provider(self) -> str:
197
+ """Get the provider of the model with simplified logic.
198
+
199
+ Returns:
200
+ str: The provider of the model.
201
+ """
202
+ if hasattr(self, "lm_invoker") and self.lm_invoker is not None:
203
+ return self.lm_invoker.model_provider
204
+
205
+ if hasattr(self, "model") and self.model is not None:
206
+ return self._detect_provider_from_model(self.model)
207
+
208
+ return "unknown"
209
+
210
+ def _detect_provider_from_model(self, model: Any) -> str:
211
+ """Detect provider from model object.
212
+
213
+ Args:
214
+ model: The model object.
215
+
216
+ Returns:
217
+ str: The provider of the model.
218
+ """
219
+ if isinstance(model, str):
220
+ return self._detect_provider_from_string(model)
221
+
222
+ if hasattr(model, "__class__"):
223
+ return self._detect_provider_from_class(model.__class__.__name__)
224
+
225
+ return "unknown"
226
+
227
+ def _detect_provider_from_string(self, model_str: str) -> str:
228
+ """Detect provider from model string.
229
+
230
+ Args:
231
+ model_str: The model string.
232
+
233
+ Returns:
234
+ str: The provider of the model.
235
+ """
236
+ model_lower = model_str.lower()
237
+
238
+ if model_lower.startswith(("gemini", "google")):
239
+ return "google"
240
+
241
+ return model_str.split("/")[0] if "/" in model_str else model_str
242
+
243
+ def _detect_provider_from_class(self, class_name: str) -> str:
244
+ """Detect provider from class name.
245
+
246
+ Args:
247
+ class_name: The class name.
248
+
249
+ Returns:
250
+ str: The provider of the model.
251
+ """
252
+ class_name_lower = class_name.lower()
253
+
254
+ provider_mappings = {"openai": "openai", "google": "google", "vertex": "google", "anthropic": "anthropic"}
255
+
256
+ for keyword, provider in provider_mappings.items():
257
+ if keyword in class_name_lower:
258
+ return provider
259
+
260
+ return "unknown"
261
+
262
+ def _process_config(
263
+ self, config: BaseAgentConfig | dict[str, Any] | None, tools: list[Any] | None = None
264
+ ) -> BaseAgentConfig | None:
265
+ """Process config parameter to ensure it's a BaseAgentConfig instance.
266
+
267
+ Args:
268
+ config: Configuration parameter that can be dict, BaseAgentConfig, or None.
269
+ tools: List of tools to include in config if not already present.
270
+
271
+ Returns:
272
+ BaseAgentConfig instance or None.
273
+ """
274
+ if config is None:
275
+ if tools:
276
+ return AgentConfig(tools=tools)
277
+ return None
278
+
279
+ if isinstance(config, BaseAgentConfig):
280
+ if tools and not config.tools:
281
+ config.tools = tools
282
+ return config
283
+
284
+ if isinstance(config, dict):
285
+ config_dict = config.copy()
286
+
287
+ if tools and "tools" not in config_dict:
288
+ config_dict["tools"] = tools
289
+
290
+ if "lm_hyperparameters" in config_dict and "default_hyperparameters" not in config_dict:
291
+ config_dict["default_hyperparameters"] = config_dict.pop("lm_hyperparameters")
292
+
293
+ return AgentConfig(**config_dict)
294
+
295
+ raise TypeError(f"Config must be BaseAgentConfig, dict, or None, got {type(config)}")
296
+
297
+ def _extract_credentials_from_config(
298
+ self, config: BaseAgentConfig | None
299
+ ) -> tuple[CredentialType, str | dict[str, Any] | None]:
300
+ """Extract and auto-detect credentials from config with ultra-simple logic.
301
+
302
+ This method supports multiple credential formats with automatic type detection:
303
+ - New lm_credentials field: Auto-detects type based on content
304
+ - Legacy lm_api_key field: For backward compatibility
305
+
306
+ Auto-detection logic:
307
+ - Dict: Passed through as-is (CredentialType.DICT)
308
+ - String + file exists: Treated as file path (CredentialType.FILE)
309
+ - String + file doesn't exist: Treated as API key (CredentialType.API_KEY)
310
+
311
+ Args:
312
+ config: Configuration object.
313
+
314
+ Returns:
315
+ Tuple containing:
316
+ - credential_type: CredentialType enum value
317
+ - credentials: The extracted credentials or None if not found
318
+ """
319
+ if not config:
320
+ return CredentialType.API_KEY, None
321
+
322
+ if hasattr(config, "lm_credentials") and config.lm_credentials is not None:
323
+ detected_type, formatted_creds = self._auto_detect_credential_type(config.lm_credentials)
324
+ return detected_type, formatted_creds
325
+
326
+ if hasattr(config, "lm_api_key") and config.lm_api_key:
327
+ warn(
328
+ (
329
+ "The lm_api_key is deprecated as of version 0.5.0. "
330
+ "Use lm_credentials instead which supports auto-detection of API keys, "
331
+ "file paths, and dictionary credentials."
332
+ ),
333
+ DeprecationWarning,
334
+ stacklevel=2,
335
+ )
336
+ return CredentialType.API_KEY, config.lm_api_key
337
+
338
+ return CredentialType.API_KEY, None
339
+
340
+ def _auto_detect_credential_type(self, credentials: Any) -> tuple[CredentialType, Any]:
341
+ """Automatically detect credential type using simple file existence rules.
342
+
343
+ This method uses ultra-simple detection logic:
344
+ 1. If credentials is dict -> CredentialType.DICT (Bedrock, LangChain credentials)
345
+ 2. If credentials is string and exists on disk -> CredentialType.FILE
346
+ 3. Everything else -> CredentialType.API_KEY (simple fallback)
347
+
348
+ Args:
349
+ credentials: Raw credentials from config.
350
+
351
+ Returns:
352
+ Tuple containing:
353
+ - credential_type: CredentialType enum value
354
+ - formatted_credentials: The credentials in the detected format
355
+ """
356
+ if isinstance(credentials, dict):
357
+ return CredentialType.DICT, credentials
358
+
359
+ if isinstance(credentials, str):
360
+ if not credentials.strip():
361
+ return CredentialType.API_KEY, credentials
362
+
363
+ try:
364
+ path = Path(credentials)
365
+ if path.exists():
366
+ return CredentialType.FILE, credentials
367
+ except (ValueError, OSError):
368
+ pass
369
+
370
+ return CredentialType.API_KEY, credentials
371
+
372
+ return CredentialType.API_KEY, str(credentials)
373
+
374
+ @property
375
+ def mcp_config(self) -> dict[str, dict[str, Any]]:
376
+ """Read-only view of MCP configuration.
377
+
378
+ Returns a copy to prevent direct mutation; use add_mcp_server() for changes.
379
+ """
380
+ return self._mcp_config.copy()
381
+
382
+ @mcp_config.setter
383
+ def mcp_config(self, value: dict[str, dict[str, Any]]) -> None:
384
+ """Set MCP configuration and maintain synchronization.
385
+
386
+ Automatically resets initialization flag and recreates client to ensure consistency.
387
+ Prefer using add_mcp_server() for proper validation.
388
+
389
+ Args:
390
+ value (dict[str, dict[str, Any]]): The MCP configuration to set.
391
+ """
392
+ if not isinstance(value, dict):
393
+ raise ValueError("mcp_config must be a dict[str, dict[str, Any]]")
394
+ self._mcp_config = value.copy()
395
+ # Reset flag and recreate client object to maintain sync. This is lightweight
396
+ # (no connections created) and safe to perform synchronously. Actual session
397
+ # initialization remains lazy in the event loop via _ensure_mcp_tools_initialized().
398
+ self._mcp_tools_initialized = False
399
+ if self._mcp_config:
400
+ self._initialize_mcp_client()
401
+ else:
402
+ # Clear client for empty config
403
+ self.mcp_client = None
404
+
405
+ def _get_credentials(self, model: str | ModelId, config: BaseAgentConfig | None) -> str | dict[str, Any] | None:
406
+ """Get credentials for the model with enhanced type support.
407
+
408
+ This method now supports multiple credential formats through the new
409
+ _extract_credentials_from_config method while maintaining backward compatibility.
410
+
411
+ Args:
412
+ model: Model identifier.
413
+ config: Configuration object.
414
+
415
+ Returns:
416
+ Credentials if found, None otherwise. Can be:
417
+ - str: For API keys or file paths
418
+ - dict: For structured credentials (Bedrock, LangChain)
419
+ """
420
+ credentials = None
421
+
422
+ if config:
423
+ _, credentials = self._extract_credentials_from_config(config)
424
+
425
+ if not credentials:
426
+ credentials = CredentialsManager.get_credentials(model)
427
+
428
+ return credentials
429
+
430
+ def _extract_retry_config(self, config: BaseAgentConfig | None, use_default: bool = True) -> RetryConfig | None:
431
+ """Extract and process retry config from agent config.
432
+
433
+ Args:
434
+ config: Configuration object.
435
+ use_default: If True, return a default RetryConfig when none is found.
436
+
437
+ Returns:
438
+ RetryConfig instance if found, default RetryConfig if use_default=True and none found, None otherwise.
439
+ """
440
+ if config and isinstance(config, AgentConfig) and config.lm_retry_config:
441
+ if isinstance(config.lm_retry_config, dict):
442
+ return RetryConfig(**config.lm_retry_config)
443
+ return config.lm_retry_config
444
+
445
+ if use_default:
446
+ return DEFAULT_RETRY_CONFIG
447
+
448
+ return None
449
+
450
+ def _update_config_with_tools(
451
+ self, tools: list[Any], config: BaseAgentConfig | None = None
452
+ ) -> dict[str, Any] | None:
453
+ """Update config with tools if not already present and convert to dict for lm_invoker.
454
+
455
+ Args:
456
+ tools: List of tools.
457
+ config: Configuration object.
458
+
459
+ Returns:
460
+ Configuration dictionary for lm_invoker.
461
+ """
462
+ if config is None:
463
+ config_dict = {"tools": tools} if tools else {}
464
+ config_dict["retry_config"] = self._extract_retry_config(config)
465
+ return config_dict
466
+
467
+ # Convert BaseAgentConfig to dict, excluding LM-specific fields
468
+ config_dict = config.model_dump(
469
+ exclude_none=True,
470
+ exclude=LM_EXCLUDE_FIELDS,
471
+ )
472
+ if isinstance(config, AgentConfig) and config.lm_hyperparameters:
473
+ config_dict["default_hyperparameters"] = config.lm_hyperparameters
474
+
475
+ config_dict["retry_config"] = self._extract_retry_config(config)
476
+
477
+ if tools:
478
+ config_dict["tools"] = tools
479
+
480
+ return config_dict if config_dict else None
481
+
482
+ def _finalize_lm_invoker_config(self, tools: list[Any], config: BaseAgentConfig | None) -> dict[str, Any]:
483
+ """Finalize lm_invoker config by adding tools and output analytics.
484
+
485
+ Args:
486
+ tools: List of tools.
487
+ config: Configuration object for lm_invoker.
488
+
489
+ Returns:
490
+ Configuration dictionary for lm_invoker.
491
+ """
492
+ processed_config = self._update_config_with_tools(tools, config)
493
+ if processed_config is None:
494
+ processed_config = {}
495
+
496
+ if OUTPUT_ANALYTICS_KEY not in processed_config:
497
+ processed_config[OUTPUT_ANALYTICS_KEY] = True
498
+
499
+ return processed_config
500
+
501
+ def _setup_lm_invoker_param(
502
+ self, model: str | ModelId, tools: list[Any], config: BaseAgentConfig | None = None
503
+ ) -> tuple[ModelId | str, str | None, dict[str, Any] | None]:
504
+ """Setup parameter for build_lm_invoker.
505
+
506
+ Args:
507
+ model (str | ModelId): The model identifier.
508
+ tools (list[Any]): List of tools.
509
+ config (BaseAgentConfig | None): Configuration object.
510
+
511
+ Returns:
512
+ - model_id: ModelId | str
513
+ - credentials: str | None
514
+ - config: dict[str, Any] | None
515
+ as tuple
516
+ """
517
+ model_id: str | ModelId = model
518
+ credentials: str | None = None
519
+ processed_as_custom = False
520
+
521
+ if isinstance(model, str):
522
+ for prefix, provider in CUSTOM_PROVIDERS.items():
523
+ if model.startswith(prefix):
524
+ processed_as_custom = True
525
+
526
+ if not config or not isinstance(config, AgentConfig):
527
+ raise ValueError(f"AgentConfig is required for model '{model}'")
528
+
529
+ try:
530
+ model_id = ModelId.from_string(model)
531
+ except ValueError as e:
532
+ base_url = config.lm_base_url
533
+ if not base_url:
534
+ raise ValueError(f"lm_base_url in AgentConfig is required for model '{model}'") from e
535
+
536
+ model_name = model.removeprefix(prefix)
537
+ model_id = ModelId(provider=provider, name=model_name, path=base_url)
538
+
539
+ # Extract credentials using new enhanced method
540
+ _, credentials = self._extract_credentials_from_config(config)
541
+ break
542
+
543
+ if not processed_as_custom or credentials is None:
544
+ credentials = self._get_credentials(model_id, config)
545
+
546
+ processed_config = self._finalize_lm_invoker_config(tools, config)
547
+ return model_id, credentials, processed_config
548
+
549
+ def _process_model_parameter(
550
+ self, agent_name: str, model: Any | None, tools: list[Any], config: BaseAgentConfig | None = None
551
+ ) -> tuple[Any | None, Any | None]:
552
+ """Process the model parameter and determine lm_invoker and model attributes.
553
+
554
+ Args:
555
+ agent_name: The name of the agent (for logging).
556
+ model: The model parameter from initialization.
557
+ tools: List of tools for lm_invoker configuration.
558
+ config: Configuration object.
559
+
560
+ Returns:
561
+ Tuple of (lm_invoker, processed_model) where:
562
+ - lm_invoker: Built LM Invoker if model is string/ModelId or existing BaseLMInvoker, None otherwise
563
+ - processed_model: LangChain model if it's a BaseChatModel,
564
+ original model for other types, None if lm_invoker was created
565
+ or if model is None
566
+ """
567
+ # If model is already an lm_invoker instance
568
+ if BaseLMInvoker and isinstance(model, BaseLMInvoker):
569
+ logger.debug(f"Agent '{agent_name}': Using provided LM Invoker: {model.__class__.__name__}")
570
+ return model, None
571
+
572
+ # Check if model is a string or ModelId - build lm_invoker
573
+ if isinstance(model, str) or (ModelId and isinstance(model, ModelId)):
574
+ if not build_lm_invoker:
575
+ logger.warning(
576
+ f"Agent '{agent_name}': gllm-inference not available, cannot build LM Invoker from {model}"
577
+ )
578
+ return None, model
579
+
580
+ logger.info(f"Agent '{agent_name}': Building LM Invoker from model identifier: {model}")
581
+ try:
582
+ model_id, credentials, preprocessed_config = self._setup_lm_invoker_param(model, tools, config)
583
+
584
+ lm_invoker = build_lm_invoker(
585
+ model_id=model_id,
586
+ credentials=credentials,
587
+ config=preprocessed_config,
588
+ )
589
+ return lm_invoker, None
590
+ except Exception as e:
591
+ logger.error(f"Agent '{agent_name}': Failed to build LM Invoker from {model}: {e}")
592
+ raise RuntimeError(f"Failed to build LM Invoker from model '{model}': {e}") from e
593
+
594
+ # If it's a LangChain model, use it directly
595
+ elif BaseChatModel and hasattr(model, "__class__") and issubclass(model.__class__, BaseChatModel):
596
+ logger.debug(f"Agent '{agent_name}': Using provided LangChain model: {model.__class__.__name__}")
597
+ return None, model
598
+
599
+ # If model is None, that's acceptable for some use cases
600
+ elif model is None:
601
+ logger.debug(f"Agent '{agent_name}': No model provided")
602
+ return None, None
603
+
604
+ # For any other type, store as model (e.g., Google ADK agent instance)
605
+ else:
606
+ logger.debug(f"Agent '{agent_name}': Using provided model of type: {type(model)}")
607
+ return None, model
608
+
609
+ def to_a2a(self, agent_card: AgentCard, **kwargs: Any) -> Starlette:
610
+ """Converts the agent to an A2A-compatible ASGI application.
611
+
612
+ This implementation provides a base setup for A2A server components.
613
+ Subclasses can override this method if they need custom executor
614
+ or task store implementations.
615
+
616
+ Args:
617
+ agent_card: The agent card to use for the A2A application.
618
+ **kwargs: Additional keyword arguments for ASGI application configuration.
619
+
620
+ Returns:
621
+ A Starlette ASGI application that can be used with any ASGI server.
622
+ """
623
+ # Use provided task store or create default in-memory store
624
+ task_store = kwargs.get("task_store", InMemoryTaskStore())
625
+
626
+ # Create default request handler if not provided
627
+ try:
628
+ executor_cls = _get_executor_class_for_agent(self)
629
+ except KeyError as exc:
630
+ raise ValueError(f"No A2A executor registered for agent type '{self.__class__.__name__}'.") from exc
631
+
632
+ agent_executor = executor_cls(self)
633
+ request_handler = kwargs.get(
634
+ "request_handler",
635
+ DefaultRequestHandler(
636
+ agent_executor=agent_executor,
637
+ task_store=task_store,
638
+ ),
639
+ )
640
+
641
+ # Create A2A application
642
+ a2a_app = A2AStarletteApplication(agent_card=agent_card, http_handler=request_handler)
643
+
644
+ # Get base routes from A2A app
645
+ routes = a2a_app.routes()
646
+
647
+ # Add any additional routes if provided
648
+ if "routes" in kwargs:
649
+ routes.extend(kwargs["routes"])
650
+
651
+ # Create and return Starlette application
652
+ return Starlette(routes=routes)
653
+
654
+ @classmethod
655
+ def discover_agents(cls, a2a_config: A2AClientConfig, **kwargs: Any) -> list[AgentCard]:
656
+ """Discover agents from the URLs specified in a2a_config.discovery_urls.
657
+
658
+ This concrete implementation fetches and parses .well-known/agent.json
659
+ from each discovery URL to build a list of available agents.
660
+
661
+ Args:
662
+ a2a_config: Configuration containing discovery URLs and other A2A settings.
663
+ **kwargs: Additional keyword arguments (unused in this implementation).
664
+
665
+ Returns:
666
+ A list of AgentCard objects representing discovered agents.
667
+ """
668
+ discovered_cards: list[AgentCard] = []
669
+
670
+ if not a2a_config or not a2a_config.discovery_urls:
671
+ logger.debug("No discovery URLs configured")
672
+ return discovered_cards
673
+
674
+ httpx_client_options = {}
675
+ if a2a_config.httpx_client_options:
676
+ httpx_client_options = a2a_config.httpx_client_options.model_dump(exclude_none=True)
677
+
678
+ with httpx.Client(**httpx_client_options) as client:
679
+ for base_url in a2a_config.discovery_urls:
680
+ try:
681
+ agent_json_url = f"{base_url.rstrip('/')}/.well-known/agent.json"
682
+
683
+ response = client.get(agent_json_url)
684
+ response.raise_for_status()
685
+
686
+ try:
687
+ agent_card = AgentCard.model_validate(response.json())
688
+ discovered_cards.append(agent_card)
689
+ logger.info(f"Successfully discovered agent '{agent_card.name}' at {base_url}")
690
+ except Exception as parse_error:
691
+ logger.error(f"Error parsing agent card from {agent_json_url}: {parse_error}")
692
+ continue
693
+
694
+ except Exception as e:
695
+ logger.error(f"Unexpected error discovering agents from {base_url}: {e}")
696
+ continue
697
+
698
+ agent_list = "\n".join([f"{i + 1}. {agent.name}" for i, agent in enumerate(discovered_cards)])
699
+ logger.info(f"Discovered agents ({len(discovered_cards)} Agents): \n{agent_list}")
700
+ return discovered_cards
701
+
702
+ def send_to_agent(
703
+ self,
704
+ agent_card: AgentCard,
705
+ message: str | dict[str, Any],
706
+ **kwargs: Any,
707
+ ) -> dict[str, Any]:
708
+ """Synchronously sends a message to another agent using the A2A protocol.
709
+
710
+ This method is a synchronous wrapper around asend_to_agent. It handles the creation
711
+ of an event loop if one doesn't exist, and manages the asynchronous call internally.
712
+
713
+ Args:
714
+ agent_card: The AgentCard instance containing the target agent's details including
715
+ URL, authentication requirements, and capabilities.
716
+ message: The message to send to the agent. Can be either a string for simple text
717
+ messages or a dictionary for structured data.
718
+ **kwargs: Additional keyword arguments passed to asend_to_agent.
719
+
720
+ Returns:
721
+ A dictionary containing the response details:
722
+ - status (str): 'success' or 'error'
723
+ - content (str): Extracted text content from the response
724
+ - task_id (str, optional): ID of the created/updated task
725
+ - task_state (str, optional): Current state of the task
726
+ - raw_response (str): Complete JSON response from the A2A client
727
+ - error_type (str, optional): Type of error if status is 'error'
728
+ - message (str, optional): Error message if status is 'error'
729
+
730
+ Raises:
731
+ RuntimeError: If called from within an existing event loop or if asend_to_agent
732
+ encounters an unhandled exception.
733
+ """
734
+ try:
735
+ return A2AConnector.send_to_agent(agent_card, message, **kwargs)
736
+ except RuntimeError as e:
737
+ raise RuntimeError(f"Agent '{self.name}': Error in sync 'send_to_agent'. Original error: {e}") from e
738
+
739
+ async def asend_to_agent(
740
+ self,
741
+ agent_card: AgentCard,
742
+ message: str | dict[str, Any],
743
+ **kwargs: Any,
744
+ ) -> dict[str, Any]:
745
+ """Asynchronously sends a message to another agent using the A2A protocol.
746
+
747
+ This method handles the core A2A communication logic, creating and sending properly
748
+ formatted A2A messages and processing the responses.
749
+
750
+ Args:
751
+ agent_card: The AgentCard instance containing the target agent's details including
752
+ URL, authentication requirements, and capabilities.
753
+ message: The message to send to the agent. Can be either a string for simple text
754
+ messages or a dictionary for structured data.
755
+ **kwargs: Additional keyword arguments.
756
+
757
+ Returns:
758
+ A dictionary containing the response details:
759
+ - status (str): 'success' or 'error'
760
+ - content (str): Extracted text content from the response
761
+ - task_id (str, optional): ID of the created/updated task
762
+ - task_state (str, optional): Current state of the task
763
+ - raw_response (str): Complete JSON response from the A2A client
764
+ - error_type (str, optional): Type of error if status is 'error'
765
+ - message (str, optional): Error message if status is 'error'
766
+
767
+ Raises:
768
+ httpx.HTTPError: If there's an HTTP-related error during the request.
769
+ Exception: For any other unexpected errors during message sending or processing.
770
+ """
771
+ return await A2AConnector.asend_to_agent(agent_card, message, **kwargs)
772
+
773
+ async def astream_to_agent(
774
+ self,
775
+ agent_card: AgentCard,
776
+ message: str | dict[str, Any],
777
+ **kwargs: Any,
778
+ ) -> AsyncGenerator[dict[str, Any], None]:
779
+ """Asynchronously sends a streaming message to another agent using the A2A protocol.
780
+
781
+ This method supports streaming responses from the target agent, yielding chunks of
782
+ the response as they become available. It handles various types of streaming events
783
+ including task status updates, artifact updates, and message parts.
784
+
785
+ Args:
786
+ agent_card: The AgentCard instance containing the target agent's details including
787
+ URL, authentication requirements, and capabilities.
788
+ message: The message to send to the agent. Can be either a string for simple text
789
+ messages or a dictionary for structured data.
790
+ **kwargs: Additional keyword arguments.
791
+
792
+ Yields:
793
+ Dictionaries containing streaming response chunks:
794
+ For successful chunks:
795
+ - status (str): 'success'
796
+ - content (str): Extracted text content from the chunk
797
+ - task_id (str): ID of the associated task
798
+ - task_state (str): Current state of the task
799
+ - final (bool): Whether this is the final chunk
800
+ - artifact_name (str, optional): Name of the artifact if chunk is an artifact update
801
+ For error chunks:
802
+ - status (str): 'error'
803
+ - error_type (str): Type of error encountered
804
+ - message (str): Error description
805
+
806
+ Raises:
807
+ httpx.HTTPError: If there's an HTTP-related error during the streaming request.
808
+ Exception: For any other unexpected errors during message streaming or processing.
809
+ """
810
+ # Default to the richer A2A event payload so integrations (e.g. HITL streaming) receive
811
+ async for chunk in A2AConnector.astream_to_agent(agent_card, message, **kwargs):
812
+ yield chunk
813
+
814
+ @staticmethod
815
+ def format_agent_description(agent_card: AgentCard) -> str:
816
+ """Format the description of an agent card including skills information.
817
+
818
+ Args:
819
+ agent_card (AgentCard): The agent card to format.
820
+
821
+ Returns:
822
+ str: The formatted description including skills.
823
+ """
824
+ # Start with the base description
825
+ formatted_description = agent_card.description or ""
826
+
827
+ if agent_card.skills:
828
+ formatted_description += "\n\nSkills:"
829
+ for skill in agent_card.skills:
830
+ formatted_description += f"\n• {skill.name}: {skill.description}"
831
+
832
+ if skill.tags:
833
+ tags_str = ", ".join(skill.tags)
834
+ formatted_description += f" (Tags: {tags_str})"
835
+
836
+ if skill.examples:
837
+ formatted_description += "\n Examples:"
838
+ for example in skill.examples:
839
+ formatted_description += f"\n - {example}"
840
+
841
+ return formatted_description
842
+
843
+ def add_mcp_server(self, mcp_config: dict[str, dict[str, Any]]) -> None:
844
+ """Adds MCP servers to the agent.
845
+
846
+ Args:
847
+ mcp_config: A dictionary containing MCP server configurations.
848
+
849
+ Raises:
850
+ ValueError: If the MCP configuration is empty or None.
851
+ KeyError: If a server with the same name already exists in the MCP configuration.
852
+ """
853
+ if not mcp_config:
854
+ raise ValueError("MCP configuration must not be empty or None")
855
+
856
+ for server_name, config in mcp_config.items():
857
+ if server_name in self.mcp_config:
858
+ raise KeyError(f"Server '{server_name}' already exists in MCP configuration")
859
+ if not isinstance(config, dict):
860
+ raise ValueError(f"Configuration for server '{server_name}' must be a dictionary")
861
+ if not config:
862
+ raise ValueError(f"Configuration for server '{server_name}' must not be empty")
863
+
864
+ # Validate that either URL or command is present
865
+ required_keys = ["url"] if "url" in config else ["command"]
866
+ if not any(key in config for key in required_keys):
867
+ raise ValueError(
868
+ f"Server '{server_name}' missing required configuration: must have either 'url' or 'command'"
869
+ )
870
+
871
+ self._mcp_config.update(mcp_config)
872
+ # Initialize/recreate MCP client object (lightweight, no sessions yet)
873
+ # This satisfies existing unit tests and keeps lazy async session init intact.
874
+ self._initialize_mcp_client()
875
+ # Mark that we need to initialize MCP tools (lazy initialization)
876
+ self._mcp_tools_initialized = False
877
+
878
+ def _initialize_mcp_client(self) -> None:
879
+ """Initialize/recreate MCP client with current config.
880
+
881
+ To be implemented by child agents as each agent type has its own MCP client.
882
+ For agents that don't support MCP (e.g., LangflowAgent), this can be a no-op.
883
+ """
884
+ # Default implementation is no-op for agents that don't support MCP
885
+ pass
886
+
887
+ def _set_mcp_client_safely(self, new_client: BaseMCPClient | None) -> None:
888
+ """Replace current MCP client with cleanup of the previous instance.
889
+
890
+ This helper ensures we don't leak persistent sessions when recreating the client.
891
+ It attempts asynchronous cleanup when an event loop is running, otherwise performs
892
+ a synchronous cleanup using asyncio.run.
893
+
894
+ Args:
895
+ new_client (BaseMCPClient | None): The new MCP client to set, or None to clear the current client.
896
+ """
897
+ prev_client = self.mcp_client
898
+ self.mcp_client = new_client
899
+ if prev_client is not None and prev_client is not new_client:
900
+ try:
901
+ loop = asyncio.get_running_loop()
902
+ loop.create_task(prev_client.cleanup())
903
+ except RuntimeError:
904
+ try:
905
+ asyncio.run(prev_client.cleanup())
906
+ except Exception:
907
+ # Best-effort cleanup; ignore failures
908
+ pass
909
+
910
+ async def _ensure_mcp_tools_initialized(self) -> None:
911
+ """Ensure MCP tools are initialized lazily (one-time operation).
912
+
913
+ This method ensures MCP tools are initialized only once during the first run,
914
+ avoiding event loop issues by doing initialization in the correct event loop.
915
+ """
916
+ if self._mcp_tools_initialized:
917
+ return
918
+
919
+ # Prevent concurrent initialization across simultaneous runs
920
+ async with self._mcp_init_lock:
921
+ if self._mcp_tools_initialized:
922
+ return
923
+
924
+ await self._handle_mcp_client_initialization()
925
+
926
+ # If we have a client at this point, proceed with tool registration
927
+ if self.mcp_client is not None:
928
+ await self._perform_mcp_tool_registration()
929
+
930
+ async def _handle_mcp_client_initialization(self) -> None:
931
+ """Handle MCP client initialization based on current state.
932
+
933
+ Sets the initialization flag appropriately for different scenarios.
934
+ """
935
+ if self.mcp_client is not None:
936
+ return # Client already exists
937
+
938
+ if not self._mcp_config:
939
+ # No config at all - skip and mark as initialized to avoid repeated logs
940
+ logger.debug(f"Agent '{self.name}': MCP client not configured; skipping MCP tool registration")
941
+ self._mcp_tools_initialized = True
942
+ return
943
+
944
+ # Config exists but no client - try to initialize
945
+ self._initialize_mcp_client()
946
+ if self.mcp_client is None:
947
+ # Still no client after init - agent type doesn't support MCP
948
+ logger.warning(f"Agent '{self.name}': MCP config present but agent type doesn't support MCP")
949
+
950
+ async def _perform_mcp_tool_registration(self) -> None:
951
+ """Perform the actual MCP tool registration.
952
+
953
+ Raises:
954
+ RuntimeError: If tool registration fails
955
+ """
956
+ try:
957
+ await self._register_mcp_tools()
958
+ self._mcp_tools_initialized = True
959
+ except Exception as e:
960
+ logger.error(f"Agent '{self.name}': Failed to initialize MCP tools: {e}", exc_info=True)
961
+ raise RuntimeError(f"Agent '{self.name}': MCP tool initialization failed: {e}") from e
962
+
963
+ async def _register_mcp_tools(self) -> None:
964
+ """Register MCP tools with the agent.
965
+
966
+ To be implemented by child agents as each agent type has its own way
967
+ of registering tools. For agents that don't support MCP, this can be a no-op.
968
+ """
969
+ # Default implementation is no-op for agents that don't support MCP
970
+ pass