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,1120 @@
1
+ """Tool to execute web automation tasks using browser-use framework.
2
+
3
+ This tool provides browser automation capabilities using the browser-use framework
4
+ with Steel session management and streaming support.
5
+
6
+ Streaming Contract
7
+
8
+ The tool emits streaming events with the following structure:
9
+
10
+ Thinking Markers
11
+ - **thinking_start**: Indicates beginning of a thinking phase
12
+ ```json
13
+ {
14
+ "event_type": "status_update",
15
+ "content": "Thinking start",
16
+ "thinking_and_activity_info": {
17
+ "data_type": "thinking_start",
18
+ "data_value": "",
19
+ "id": "default_thinking_id"
20
+ }
21
+ }
22
+ ```
23
+
24
+ - thinking: Contains actual thinking content
25
+ ```json
26
+ {
27
+ "event_type": "tool_result",
28
+ "content": "Completed go_to_url, extract_structured_data",
29
+ "thinking_and_activity_info": {
30
+ "data_type": "thinking",
31
+ "data_value": "**Starting fresh with the task...**",
32
+ "id": "default_thinking_id"
33
+ }
34
+ }
35
+ ```
36
+
37
+ - thinking_end: Marks end of thinking phase
38
+ ```json
39
+ {
40
+ "event_type": "status_update",
41
+ "content": "Thinking end",
42
+ "thinking_and_activity_info": {
43
+ "data_type": "thinking_end",
44
+ "data_value": "",
45
+ "id": "default_thinking_id"
46
+ }
47
+ }
48
+ ```
49
+
50
+ Iframe Activities
51
+
52
+ - Streaming URL: Live browser session URL
53
+ ```json
54
+ {
55
+ "event_type": "status_update",
56
+ "content": "Receive streaming URL",
57
+ "thinking_and_activity_info": {
58
+ "data_type": "activity",
59
+ "data_value": "{'type': 'iframe', 'message': 'https://steel.dev/...'}"
60
+ }
61
+ }
62
+ ```
63
+
64
+ - Recording URL: Session recording URL
65
+ ```json
66
+ {
67
+ "event_type": "status_update",
68
+ "content": "Receive recording URL",
69
+ "thinking_and_activity_info": {
70
+ "data_type": "activity",
71
+ "data_value": "{'type': 'iframe', 'message': 'http://minio/...'}"
72
+ }
73
+ }
74
+ ```
75
+
76
+ Event Flow
77
+
78
+ 1. Initialize session → Yield streaming URL
79
+ 2. For each agent step:
80
+ - thinking_start
81
+ - tool_result with thinking content (emitted between the markers)
82
+ - thinking_end
83
+ 3. Complete → Yield recording URL
84
+
85
+ Authors:
86
+ Reinhart Linanda (reinhart.linanda@gdplabs.id)
87
+ Fachriza Adhiatma (fachriza.d.adhiatma@gdplabs.id)
88
+ Saul Sayerz (saul.sayerz@gdplabs.id)
89
+ Raymond Christopher (raymond.christopher@gdplabs.id)
90
+
91
+ References:
92
+ https://github.com/GDP-ADMIN/glair-rnd/tree/main/browser-use/steeldev
93
+ """
94
+
95
+ import asyncio
96
+ import copy
97
+ import json
98
+ from collections.abc import Callable
99
+ from typing import Any, Literal
100
+
101
+ from browser_use import Agent
102
+ from langchain_core.runnables import RunnableConfig
103
+ from langchain_core.tools import BaseTool
104
+ from pydantic import BaseModel
105
+ from steel import Steel
106
+ from steel.types import Session
107
+
108
+ from aip_agents.tools.browser_use import session_errors
109
+ from aip_agents.tools.browser_use.action_parser import ActionParser
110
+ from aip_agents.tools.browser_use.llm_config import build_browser_use_llm, configure_browser_use_environment
111
+ from aip_agents.tools.browser_use.schemas import BrowserUseToolConfig, BrowserUseToolInput
112
+ from aip_agents.tools.browser_use.session import BrowserSession
113
+ from aip_agents.tools.browser_use.steel_session_recording import SteelSessionRecorder
114
+ from aip_agents.tools.browser_use.streaming import (
115
+ create_error_response,
116
+ create_step_response,
117
+ generate_step_content,
118
+ generate_thinking_message,
119
+ yield_iframe_activity,
120
+ yield_status_message,
121
+ yield_thinking_marker,
122
+ )
123
+ from aip_agents.tools.browser_use.structured_data_parser import detect_structured_data_failure
124
+ from aip_agents.tools.browser_use.types import (
125
+ BrowserUseFatalError,
126
+ RetryDecision,
127
+ StreamingResponse,
128
+ StreamingState,
129
+ ToolCallInfo,
130
+ )
131
+ from aip_agents.utils.logger import get_logger
132
+
133
+ logger = get_logger(__name__)
134
+
135
+
136
+ class BrowserUseTool(BaseTool):
137
+ """Tool to execute web automation tasks using browser-use framework.
138
+
139
+ This tool provides step-by-step execution of browser automation tasks with detailed
140
+ logging of intermediate steps, including the agent's thinking process, goals, and
141
+ results at each step.
142
+ """
143
+
144
+ name: str = "browser_use_tool"
145
+ description: str = (
146
+ "Execute web automation tasks using browser-use framework. "
147
+ "Connects browser-use agent, and runs the specified task step by step. "
148
+ "Provides detailed logging of intermediate steps including agent thinking and goals. "
149
+ "Returns the task execution results with step-by-step information."
150
+ )
151
+ args_schema: type[BaseModel] = BrowserUseToolInput
152
+ tool_config_schema: type[BaseModel] = BrowserUseToolConfig
153
+
154
+ MAX_SESSION_RELEASE_RETRIES: int = 3
155
+ SESSION_RELEASE_SLEEP_TIME_IN_S: int = 10
156
+
157
+ def _run(self, task: str, config: RunnableConfig | None = None) -> str:
158
+ """Run the tool synchronously.
159
+
160
+ Args:
161
+ task (str): The task prompt for the AI agent to execute in the browser.
162
+ config (RunnableConfig): RunnableConfig containing tool configuration.
163
+
164
+ Returns:
165
+ str: The result of the task execution or an error message if the task fails.
166
+ """
167
+ return asyncio.run(self._arun(task, config))
168
+
169
+ async def _arun(self, task: str, config: RunnableConfig | None = None) -> str:
170
+ """Execute a web automation task using browser-use asynchronously.
171
+
172
+ This method creates a Steel browser session, initializes a browser-use agent with
173
+ the specified task, and executes the automation. It handles session cleanup and
174
+ error handling.
175
+
176
+ Args:
177
+ task (str): The task prompt for the AI agent to execute in the browser.
178
+ config (RunnableConfig): RunnableConfig containing tool configuration.
179
+
180
+ Returns:
181
+ str: A success message with the task result, or an error message if the task
182
+ execution fails. The result includes the final output from the browser-use
183
+ agent's execution.
184
+
185
+ Raises:
186
+ Exception: Any exception that occurs during task execution will be caught
187
+ and returned as an error message string.
188
+ """
189
+ tool_config = self._get_tool_config(config)
190
+
191
+ if not tool_config.browser_use_llm_openai_api_key:
192
+ return self._log_and_return("warning", "Browser-use LLM OpenAI API key is required.")
193
+ if not tool_config.browser_use_page_extraction_llm_openai_api_key:
194
+ return self._log_and_return("warning", "Browser-use Page Extraction OpenAI API key is required.")
195
+ if not tool_config.steel_api_key:
196
+ return self._log_and_return("warning", "Steel API key is required.")
197
+
198
+ client = self._init_steel_client(tool_config)
199
+
200
+ session = None
201
+ try:
202
+ session = self._create_steel_session(client, tool_config)
203
+ cdp_url = self._construct_cdp_url(session.id, tool_config)
204
+ agent = self._create_browser_use_agent(task, cdp_url, tool_config)
205
+ result = await agent.run()
206
+ return self._log_and_return("info", f"Task completed successfully!\nResult: {result.final_result()}")
207
+
208
+ except Exception as e:
209
+ return self._log_and_return("warning", f"Error during task execution: {str(e)}")
210
+ finally:
211
+ await self._release_session(session, client)
212
+
213
+ def _get_tool_config(self, config: RunnableConfig | None) -> BrowserUseToolConfig:
214
+ """Get the tool configuration.
215
+
216
+ Args:
217
+ config (RunnableConfig): RunnableConfig containing tool configuration.
218
+
219
+ Returns:
220
+ BrowserUseToolConfig: The tool configuration.
221
+ """
222
+ tool_config: BrowserUseToolConfig | None = None
223
+ if hasattr(self, "get_tool_config") and callable(self.get_tool_config):
224
+ tool_config = self.get_tool_config(config)
225
+ if not tool_config:
226
+ tool_config = BrowserUseToolConfig()
227
+ return tool_config
228
+
229
+ def _log_and_return(self, level: Literal["info", "warning"], message: str) -> str:
230
+ """Log a message and return it.
231
+
232
+ Args:
233
+ level (Literal["info", "warning"]): The log level.
234
+ message (str): The message to log and return.
235
+
236
+ Returns:
237
+ str: The logged message.
238
+ """
239
+ if level == "info":
240
+ logger.info(message)
241
+ elif level == "warning":
242
+ logger.warning(message)
243
+ return message
244
+
245
+ def _init_steel_client(self, tool_config: BrowserUseToolConfig) -> Steel:
246
+ """Initialize the Steel client.
247
+
248
+ Args:
249
+ tool_config: Tool configuration containing Steel settings.
250
+
251
+ Returns:
252
+ Steel: The Steel client.
253
+ """
254
+ return Steel(steel_api_key=tool_config.steel_api_key, base_url=tool_config.steel_base_url)
255
+
256
+ def _create_steel_session(self, client: Steel, tool_config: BrowserUseToolConfig) -> Session:
257
+ """Create the Steel session.
258
+
259
+ Args:
260
+ client: The Steel client.
261
+ tool_config: Tool configuration containing Steel session settings.
262
+
263
+ Returns:
264
+ Session: The Steel session.
265
+ """
266
+ return client.sessions.create(use_proxy=False, api_timeout=tool_config.steel_timeout_in_ms)
267
+
268
+ def _construct_cdp_url(self, session_id: str, tool_config: BrowserUseToolConfig) -> str:
269
+ """Construct the CDP URL for browser connection.
270
+
271
+ Args:
272
+ session_id: The Steel session ID.
273
+ tool_config: Tool configuration containing Steel WebSocket URL.
274
+
275
+ Returns:
276
+ str: The CDP URL.
277
+ """
278
+ return f"{tool_config.steel_ws_url}?apiKey={tool_config.steel_api_key}&sessionId={session_id}"
279
+
280
+ def _create_browser_use_agent(self, task: str, cdp_url: str, tool_config: BrowserUseToolConfig) -> Agent:
281
+ """Create OpenAI LLM and browser-use agent bound to the provided task and CDP URL.
282
+
283
+ Args:
284
+ task: The task prompt for the AI agent
285
+ cdp_url: The CDP URL for the browser session
286
+ tool_config: Tool configuration containing OpenAI model settings
287
+
288
+ Returns:
289
+ Agent: The browser-use agent
290
+ """
291
+ llm = build_browser_use_llm(
292
+ model=tool_config.browser_use_llm_openai_model,
293
+ reasoning_effort=tool_config.browser_use_llm_openai_reasoning_effort,
294
+ temperature=tool_config.browser_use_llm_openai_temperature,
295
+ api_key=tool_config.browser_use_llm_openai_api_key,
296
+ base_url=tool_config.browser_use_llm_openai_base_url,
297
+ )
298
+
299
+ page_extraction_llm = build_browser_use_llm(
300
+ model=tool_config.browser_use_page_extraction_llm_openai_model,
301
+ reasoning_effort=tool_config.browser_use_page_extraction_llm_openai_reasoning_effort,
302
+ temperature=tool_config.browser_use_page_extraction_llm_openai_temperature,
303
+ api_key=tool_config.browser_use_page_extraction_llm_openai_api_key,
304
+ base_url=tool_config.browser_use_page_extraction_llm_openai_base_url,
305
+ )
306
+
307
+ browser_session = BrowserSession(cdp_url=cdp_url)
308
+ # Mark the proxy-created session as owned so browser-use does not clone it with warnings.
309
+ setattr(browser_session, "_owns_browser_resources", True)
310
+
311
+ configure_browser_use_environment(
312
+ enable_cloud_sync=tool_config.browser_use_enable_cloud_sync,
313
+ logging_level=tool_config.browser_use_logging_level,
314
+ )
315
+
316
+ return Agent(
317
+ task=task,
318
+ llm=llm,
319
+ page_extraction_llm=page_extraction_llm,
320
+ browser_session=browser_session,
321
+ extend_system_message=tool_config.browser_use_extend_system_message,
322
+ vision_detail_level=tool_config.browser_use_vision_detail_level,
323
+ llm_timeout=tool_config.browser_use_llm_timeout_in_s,
324
+ step_timeout=tool_config.browser_use_step_timeout_in_s,
325
+ )
326
+
327
+ def _create_agent_session(
328
+ self,
329
+ client: Steel,
330
+ recorder: SteelSessionRecorder,
331
+ tool_config: BrowserUseToolConfig,
332
+ task: str,
333
+ ) -> tuple[Session, StreamingState, Agent]:
334
+ """Provision a Steel session, streaming state, and browser-use agent.
335
+
336
+ Args:
337
+ client: Initialized Steel SDK client.
338
+ recorder: Recorder responsible for generating session recordings.
339
+ tool_config: Tool configuration containing browser-use settings.
340
+ task: The textual task prompt the agent must execute.
341
+
342
+ Returns:
343
+ tuple[Session, StreamingState, Agent]: Provisioned Steel session,
344
+ streaming state metadata, and browser-use agent instance.
345
+ """
346
+ session = self._create_steel_session(client, tool_config)
347
+ streaming_state = self._create_streaming_state(session, recorder)
348
+ cdp_url = self._construct_cdp_url(session.id, tool_config)
349
+ agent = self._create_browser_use_agent(task, cdp_url, tool_config)
350
+ return session, streaming_state, agent
351
+
352
+ async def _retry_stream_events(self, task: str, tool_config: BrowserUseToolConfig):
353
+ """Yield streaming events while automatically retrying Steel sessions.
354
+
355
+ Args:
356
+ task: Textual task prompt.
357
+ tool_config: Tool configuration containing Steel/OpenAI settings.
358
+
359
+ Yields:
360
+ dict: Streaming events produced by the browser-use agent.
361
+ """
362
+ client = self._init_steel_client(tool_config)
363
+ recorder = SteelSessionRecorder(tool_config.steel_base_url, tool_config.steel_api_key)
364
+
365
+ retry_state = self._init_retry_state(tool_config)
366
+
367
+ while True:
368
+ state_holder: dict[str, StreamingState | None] = {"state": None}
369
+ store_state = self._make_state_store(state_holder)
370
+
371
+ try:
372
+ async for event in self._session_event_stream(
373
+ client,
374
+ recorder,
375
+ tool_config,
376
+ task,
377
+ store_state,
378
+ ):
379
+ yield event
380
+ return
381
+
382
+ except Exception as exc: # pragma: no cover - defensive orchestration
383
+ async for event in self._emit_retry_exception(
384
+ exc,
385
+ state_holder,
386
+ recorder,
387
+ tool_config,
388
+ retry_state,
389
+ ):
390
+ yield event
391
+
392
+ def _init_retry_state(self, tool_config: BrowserUseToolConfig) -> dict[str, int]:
393
+ """Initialize retry state tracking.
394
+
395
+ Args:
396
+ tool_config: Tool configuration containing retry settings.
397
+
398
+ Returns:
399
+ Dictionary with retry state counters.
400
+ """
401
+ return {
402
+ "retries_remaining": max(0, tool_config.browser_use_max_session_retries),
403
+ "attempted_retries": 0,
404
+ }
405
+
406
+ async def _handle_fatal_error_during_retry(
407
+ self,
408
+ fatal_error: BrowserUseFatalError,
409
+ state_holder: dict[str, StreamingState | None],
410
+ tool_config: BrowserUseToolConfig,
411
+ retry_state: dict[str, int],
412
+ ):
413
+ """Handle fatal errors during retry attempts.
414
+
415
+ Args:
416
+ fatal_error: The fatal error that occurred.
417
+ state_holder: Container for current streaming state.
418
+ tool_config: Tool configuration for retry logic.
419
+ retry_state: Current retry counters and state.
420
+ """
421
+ streaming_state = state_holder["state"]
422
+ decision, pending_event = await self._handle_retryable_fatal_error(
423
+ fatal_error,
424
+ streaming_state,
425
+ retry_state["retries_remaining"],
426
+ retry_state["attempted_retries"],
427
+ tool_config,
428
+ )
429
+ if pending_event:
430
+ yield pending_event
431
+ retry_state["retries_remaining"], retry_state["attempted_retries"] = self._apply_retry_decision(
432
+ fatal_error, decision
433
+ )
434
+
435
+ async def _handle_cancellation_during_retry(
436
+ self,
437
+ state_holder: dict[str, StreamingState | None],
438
+ recorder: SteelSessionRecorder,
439
+ ):
440
+ """Handle cancellation during retry attempts.
441
+
442
+ Args:
443
+ state_holder: Container for current streaming state.
444
+ recorder: Session recorder for cleanup operations.
445
+ """
446
+ streaming_state = state_holder["state"]
447
+ if streaming_state:
448
+ self._finalize_streaming_on_cancel(streaming_state, recorder)
449
+ if streaming_state.recording_url:
450
+ yield yield_iframe_activity(streaming_state.recording_url, "Receive recording URL")
451
+
452
+ async def _handle_unexpected_error_during_retry(
453
+ self,
454
+ exc: Exception,
455
+ state_holder: dict[str, StreamingState | None],
456
+ ):
457
+ """Handle unexpected errors during retry attempts.
458
+
459
+ Args:
460
+ exc: The unexpected exception that occurred.
461
+ state_holder: Container for current streaming state.
462
+ """
463
+ streaming_state = state_holder["state"]
464
+ error_url = streaming_state.recording_url if streaming_state else ""
465
+ yield create_error_response(f"Error during task execution: {str(exc)}", error_url)
466
+
467
+ @staticmethod
468
+ def _make_state_store(state_holder: dict[str, StreamingState | None]) -> Callable[[StreamingState], None]:
469
+ """Return a callback that records the latest streaming state.
470
+
471
+ Args:
472
+ state_holder: Mutable container storing the latest streaming state.
473
+ """
474
+
475
+ def store(streaming_state: StreamingState) -> None:
476
+ """Persist the latest streaming state in the shared holder.
477
+
478
+ Args:
479
+ streaming_state: Streaming state emitted by browser-use callbacks.
480
+ """
481
+ state_holder["state"] = streaming_state
482
+
483
+ return store
484
+
485
+ def _apply_retry_decision(
486
+ self, fatal_error: BrowserUseFatalError, decision: RetryDecision | None
487
+ ) -> tuple[int, int]:
488
+ """Update retry counters or re-raise when no retries remain.
489
+
490
+ Args:
491
+ fatal_error: The fatal error that triggered the retry decision.
492
+ decision: The retry decision containing retry counters, or None if no retries available.
493
+
494
+ Returns:
495
+ tuple[int, int]: A tuple containing (retries_remaining, attempted_retries).
496
+ """
497
+ if not decision:
498
+ raise fatal_error
499
+ return decision.retries_remaining, decision.attempted_retries
500
+
501
+ async def _emit_retry_exception(
502
+ self,
503
+ exc: Exception,
504
+ state_holder: dict[str, StreamingState | None],
505
+ recorder: SteelSessionRecorder,
506
+ tool_config: BrowserUseToolConfig,
507
+ retry_state: dict[str, int],
508
+ ):
509
+ """Normalize retry exception handling and yield resulting events.
510
+
511
+ Args:
512
+ exc: Exception raised during the streaming attempt.
513
+ state_holder: Container for current streaming state.
514
+ recorder: Session recorder for cleanup operations.
515
+ tool_config: Tool configuration containing retry settings.
516
+ retry_state: Mutable retry counters.
517
+ """
518
+ if isinstance(exc, asyncio.CancelledError):
519
+ async for event in self._handle_cancellation_during_retry(state_holder, recorder):
520
+ yield event
521
+ raise exc
522
+
523
+ if isinstance(exc, BrowserUseFatalError):
524
+ async for event in self._handle_fatal_error_during_retry(exc, state_holder, tool_config, retry_state):
525
+ yield event
526
+ return
527
+
528
+ async for event in self._handle_unexpected_error_during_retry(exc, state_holder):
529
+ yield event
530
+ raise BrowserUseFatalError(str(exc)) from exc
531
+
532
+ async def _session_event_stream(
533
+ self,
534
+ client: Steel,
535
+ recorder: SteelSessionRecorder,
536
+ tool_config: BrowserUseToolConfig,
537
+ task: str,
538
+ on_state_ready: Callable[[StreamingState], None],
539
+ ):
540
+ """Yield streaming events for a single Steel session attempt.
541
+
542
+ Args:
543
+ client: Steel SDK client instance.
544
+ recorder: Steel session recorder.
545
+ tool_config: Tool configuration for the agent.
546
+ task: Task prompt assigned to the agent.
547
+ on_state_ready: Callback invoked when streaming state is initialized.
548
+
549
+ Yields:
550
+ dict: Streaming events describing agent progress.
551
+ """
552
+ session: Session | None = None
553
+ streaming_state: StreamingState | None = None
554
+ try:
555
+ session, streaming_state, agent = self._create_agent_session(client, recorder, tool_config, task)
556
+ on_state_ready(streaming_state)
557
+ iframe_event = yield_iframe_activity(streaming_state.debug_url, "Receive streaming URL")
558
+ self._log_stream_event("iframe_start", iframe_event)
559
+ yield iframe_event
560
+ last_done_event: dict | None = None
561
+ async for event in self._stream_agent_with_markers(agent, streaming_state, recorder):
562
+ self._log_stream_event("agent_event", event)
563
+ tool_info = event.get("tool_info") if isinstance(event, dict) else None
564
+ tool_calls = tool_info.get("tool_calls", []) if isinstance(tool_info, dict) else []
565
+ if any(call.get("name") == "done" for call in tool_calls if isinstance(call, dict)):
566
+ last_done_event = event
567
+ yield event
568
+ recording_event = self._recording_event(streaming_state)
569
+ if recording_event:
570
+ self._log_stream_event("recording_event", recording_event)
571
+ yield recording_event
572
+ if last_done_event:
573
+ yield copy.deepcopy(last_done_event)
574
+ finally:
575
+ await self._release_session(session, client)
576
+
577
+ async def _release_session(self, session: Session | None, client: Steel) -> None:
578
+ """Release a Steel browser session.
579
+
580
+ This method attempts to release the provided Steel session.
581
+ It retries the release operation up to MAX_SESSION_RELEASE_RETRIES times, waiting
582
+ SESSION_RELEASE_SLEEP_TIME_IN_S seconds between attempts. If the session is None, the method
583
+ returns immediately.
584
+
585
+ Args:
586
+ session (Session | None): The Steel session to release. If None, no action is taken.
587
+ client (Steel): The Steel client used to release the session.
588
+ """
589
+ if not session:
590
+ return
591
+
592
+ for attempt in range(self.MAX_SESSION_RELEASE_RETRIES):
593
+ try:
594
+ await asyncio.sleep(self.SESSION_RELEASE_SLEEP_TIME_IN_S)
595
+ client.sessions.release(session.id)
596
+ logger.info(f"Session {session.id} released")
597
+ break
598
+ except Exception as e:
599
+ if attempt == self.MAX_SESSION_RELEASE_RETRIES - 1:
600
+ logger.warning(f"Failed to release session after {self.MAX_SESSION_RELEASE_RETRIES} attempts: {e}")
601
+
602
+ async def arun_streaming(self, task: str = None, config: RunnableConfig | None = None, **kwargs):
603
+ """Execute a web automation task using browser-use asynchronously with streaming output.
604
+
605
+ This method creates a Steel browser session, initializes a browser-use agent with
606
+ the specified task, and executes the automation step by step, yielding results
607
+ in streaming fashion. Starts background recording after completion.
608
+
609
+ Args:
610
+ task (str, optional): The task prompt for the AI agent to execute in the browser.
611
+ If not provided, will attempt to extract from kwargs.
612
+ config (RunnableConfig): RunnableConfig containing tool configuration.
613
+ **kwargs: Additional parameters that may contain the task or other tool-specific arguments.
614
+
615
+ Yields:
616
+ dict: Step-by-step results in standardized StreamingResponse format.
617
+
618
+ Raises:
619
+ Exception: Any exception that occurs during task execution will be caught
620
+ and yielded as an error message.
621
+ """
622
+ if task is None:
623
+ task = kwargs.get("task")
624
+
625
+ tool_config = self._get_tool_config(config)
626
+
627
+ missing_key_error = self._missing_api_key_error(tool_config)
628
+ if missing_key_error:
629
+ yield create_error_response(missing_key_error)
630
+ return
631
+
632
+ async for event in self._retry_stream_events(task, tool_config):
633
+ yield event
634
+
635
+ @staticmethod
636
+ def _missing_api_key_error(tool_config: BrowserUseToolConfig) -> str | None:
637
+ """Return a descriptive error when required API keys are missing.
638
+
639
+ Args:
640
+ tool_config: The tool configuration containing API key settings.
641
+
642
+ Returns:
643
+ str | None: Error message if API keys are missing, otherwise None.
644
+ """
645
+ if not tool_config.browser_use_llm_openai_api_key:
646
+ return "Browser-use LLM OpenAI API key is required."
647
+ if not tool_config.browser_use_page_extraction_llm_openai_api_key:
648
+ return "Browser-use Page Extraction OpenAI API key is required."
649
+ if not tool_config.steel_api_key:
650
+ return "Steel API key is required."
651
+ return None
652
+
653
+ def _create_streaming_state(self, session: Session, recorder: SteelSessionRecorder) -> StreamingState:
654
+ """Build initial streaming state for a Steel session.
655
+
656
+ Args:
657
+ session: The Steel browser session to create streaming state for.
658
+ recorder: The Steel session recorder for video capture.
659
+
660
+ Returns:
661
+ StreamingState: The initialized streaming state with debug URL, recording URL, and session ID.
662
+ """
663
+ video_filename = recorder.generate_video_filename(session.id)
664
+ recording_url = recorder.minio_storage.get_file_url(video_filename) if recorder.minio_storage else ""
665
+ return StreamingState(debug_url=session.debug_url, recording_url=recording_url, session_id=session.id)
666
+
667
+ def _should_retry_session(self, error_message: str, retries_remaining: int) -> bool:
668
+ """Return True when the failure qualifies for an automatic Steel session retry.
669
+
670
+ Args:
671
+ error_message: The error message from the failed session.
672
+ retries_remaining: The number of retry attempts still available.
673
+
674
+ Returns:
675
+ bool: True if the error is recoverable and retries are available, False otherwise.
676
+ """
677
+ if retries_remaining <= 0:
678
+ return False
679
+ return session_errors.is_recoverable_message(error_message)
680
+
681
+ def _build_retry_decision(
682
+ self,
683
+ error_message: str,
684
+ retries_remaining: int,
685
+ attempted_retries: int,
686
+ tool_config: BrowserUseToolConfig,
687
+ ) -> RetryDecision | None:
688
+ """Create a retry decision payload when a Steel session disconnects.
689
+
690
+ Args:
691
+ error_message: Message describing the fatal error.
692
+ retries_remaining: Number of retries still allowed.
693
+ attempted_retries: Number of retries already attempted.
694
+ tool_config: Tool configuration containing retry settings.
695
+
696
+ Returns:
697
+ RetryDecision | None: A retry decision when the error is recoverable,
698
+ otherwise None.
699
+ """
700
+ if not self._should_retry_session(error_message, retries_remaining):
701
+ return None
702
+
703
+ new_retries = retries_remaining - 1
704
+ new_attempts = attempted_retries + 1
705
+ retry_total = tool_config.browser_use_max_session_retries or 1
706
+ retry_message = f"Steel session disconnected. Attempting automatic recovery ({new_attempts}/{retry_total})."
707
+ delay = max(0.0, tool_config.browser_use_session_retry_delay_in_s)
708
+ return RetryDecision(
709
+ retries_remaining=new_retries,
710
+ attempted_retries=new_attempts,
711
+ message=retry_message,
712
+ delay=delay,
713
+ )
714
+
715
+ def _recording_event(self, streaming_state: StreamingState | None) -> dict | None:
716
+ """Return an iframe activity event for successful runs.
717
+
718
+ Args:
719
+ streaming_state: Current streaming metadata, if available.
720
+
721
+ Returns:
722
+ dict | None: Iframe activity event when a recording is available.
723
+ """
724
+ if not streaming_state or streaming_state.terminal_error or not streaming_state.recording_url:
725
+ return None
726
+ return yield_iframe_activity(streaming_state.recording_url, "Receive recording URL")
727
+
728
+ async def _handle_retryable_fatal_error(
729
+ self,
730
+ fatal_error: BrowserUseFatalError,
731
+ streaming_state: StreamingState | None,
732
+ retries_remaining: int,
733
+ attempted_retries: int,
734
+ tool_config: BrowserUseToolConfig,
735
+ ) -> tuple[RetryDecision | None, dict | None]:
736
+ """Handle a fatal error by either yielding an error response or scheduling a retry.
737
+
738
+ Args:
739
+ fatal_error: The raised fatal exception.
740
+ streaming_state: Current streaming metadata, if any.
741
+ retries_remaining: Number of retries still allowed.
742
+ attempted_retries: Number of retries already attempted.
743
+ tool_config: Tool configuration containing retry settings.
744
+
745
+ Returns:
746
+ tuple[RetryDecision | None, dict | None]: Retry decision (None when unrecoverable)
747
+ and a streaming event to emit (error or status).
748
+ """
749
+ decision = self._build_retry_decision(
750
+ str(fatal_error),
751
+ retries_remaining,
752
+ attempted_retries,
753
+ tool_config,
754
+ )
755
+ if not decision:
756
+ error_url = streaming_state.recording_url if streaming_state else ""
757
+ return None, create_error_response(str(fatal_error), error_url)
758
+
759
+ status_event = yield_status_message(decision.message)
760
+ self._log_stream_event("retry_status", status_event)
761
+ if decision.delay:
762
+ await asyncio.sleep(decision.delay)
763
+ return decision, status_event
764
+
765
+ async def _stream_agent_with_markers(
766
+ self, agent: Agent, streaming_state: StreamingState, recorder: SteelSessionRecorder
767
+ ):
768
+ """Yield agent progress events surrounded by thinking markers.
769
+
770
+ Args:
771
+ self: The BrowserUseTool instance.
772
+ agent: The browser-use agent to execute steps with.
773
+ streaming_state: State management for the streaming operation.
774
+ recorder: The Steel session recorder for video capture.
775
+
776
+ Yields:
777
+ dict: Streaming events including thinking start/end markers and step results.
778
+ """
779
+ async for stream_item in self._execute_agent_steps_streaming(agent, streaming_state, recorder):
780
+ payload = stream_item["payload"]
781
+ wrap_markers = stream_item.get("wrap_markers", False)
782
+ label = stream_item.get("label", "step_result")
783
+
784
+ if wrap_markers:
785
+ start_marker = yield_thinking_marker("start")
786
+ self._log_stream_event(f"{label}_thinking_start", start_marker)
787
+ yield start_marker
788
+
789
+ self._log_stream_event(label, payload)
790
+ yield payload
791
+
792
+ end_marker = yield_thinking_marker("end")
793
+ self._log_stream_event(f"{label}_thinking_end", end_marker)
794
+ yield end_marker
795
+ else:
796
+ self._log_stream_event(label, payload)
797
+ yield payload
798
+
799
+ async def _execute_agent_steps_streaming(
800
+ self, agent: Agent, streaming_state: StreamingState, recorder: SteelSessionRecorder
801
+ ):
802
+ """Execute agent steps one by one and yield intermediate progress in streaming fashion.
803
+
804
+ This method uses the take_step approach to execute the agent's task step by step,
805
+ yielding each step's information in a standardized format. Starts background recording after completion.
806
+
807
+ Args:
808
+ agent (Agent): The browser-use agent to execute.
809
+ streaming_state (StreamingState): State management for the streaming operation.
810
+ recorder (SteelSessionRecorder): The Steel session recorder.
811
+
812
+ Yields:
813
+ dict: Step information in standardized StreamingResponse format.
814
+ """
815
+ while not streaming_state.is_complete:
816
+ events = await self._next_step_events(agent, streaming_state, recorder)
817
+ for event in events:
818
+ yield event
819
+ if streaming_state.is_complete:
820
+ break
821
+
822
+ async def _perform_agent_step(
823
+ self, agent: Agent, streaming_state: StreamingState, recorder: SteelSessionRecorder
824
+ ) -> StreamingResponse:
825
+ """Execute a single agent step and return its streaming payload.
826
+
827
+ Args:
828
+ agent: The browser-use agent currently executing the plan.
829
+ streaming_state: Mutable state tracking streaming progress and errors.
830
+ recorder: Steel recorder used to schedule background video exports.
831
+
832
+ Returns:
833
+ StreamingResponse: Serialized streaming payload for the current step.
834
+ """
835
+ is_done, _ = await agent.take_step()
836
+ streaming_state.step_count += 1
837
+
838
+ tool_calls = ActionParser.extract_actions(agent.state.last_model_output, agent.state.last_result)
839
+ logger.info("Browser-use raw tool calls: %s", [call.__dict__ for call in tool_calls])
840
+
841
+ error_message = self._resolve_step_error(agent, streaming_state, recorder, tool_calls)
842
+ if error_message:
843
+ raise BrowserUseFatalError(error_message)
844
+
845
+ if is_done:
846
+ self._update_completion_state(streaming_state, recorder)
847
+
848
+ tool_calls_dict = [{"name": tc.name, "args": tc.args, "output": tc.output} for tc in tool_calls]
849
+ content = generate_step_content(tool_calls, is_done)
850
+ thinking_message = await generate_thinking_message(content, tool_calls_dict, is_final=is_done)
851
+
852
+ step_response = create_step_response(agent, tool_calls, is_done, content, thinking_message)
853
+ logger.info("Browser-use step event: %s", step_response.to_dict())
854
+
855
+ self._log_stream_event("step_streaming_response", step_response)
856
+
857
+ return step_response
858
+
859
+ def _prepare_step_events(
860
+ self,
861
+ response: StreamingResponse,
862
+ streaming_state: StreamingState,
863
+ ) -> list[dict[str, Any]]:
864
+ """Construct serialized events for a streaming step.
865
+
866
+ Args:
867
+ response: Primary streaming response generated for the current step.
868
+ streaming_state: Streaming state describing overall progress/completion.
869
+
870
+ Returns:
871
+ List of serialized event dictionaries ready to be emitted.
872
+ """
873
+ wrap_markers = not streaming_state.is_complete
874
+ return [
875
+ {
876
+ "payload": response.to_dict(),
877
+ "wrap_markers": wrap_markers,
878
+ "label": "step_result",
879
+ }
880
+ ]
881
+
882
+ async def _next_step_events(
883
+ self,
884
+ agent: Agent,
885
+ streaming_state: StreamingState,
886
+ recorder: SteelSessionRecorder,
887
+ ) -> list[dict[str, Any]]:
888
+ """Execute a step and return serialized events, handling errors consistently.
889
+
890
+ Args:
891
+ agent: Browser-use agent executing the step.
892
+ streaming_state: Mutable streaming state metadata.
893
+ recorder: Recorder used for background capture scheduling.
894
+
895
+ Returns:
896
+ List of serialized event dictionaries produced by the step.
897
+ """
898
+ try:
899
+ response = await self._perform_agent_step(agent, streaming_state, recorder)
900
+ return self._prepare_step_events(response, streaming_state)
901
+ except asyncio.CancelledError as cancel:
902
+ self._finalize_streaming_on_cancel(streaming_state, recorder)
903
+ raise cancel
904
+ except BrowserUseFatalError:
905
+ raise
906
+ except Exception as error: # pragma: no cover - defensive path
907
+ error_message = f"Error in step execution: {error}"
908
+ raise BrowserUseFatalError(error_message) from error
909
+
910
+ def _resolve_step_error(
911
+ self,
912
+ agent: Agent,
913
+ streaming_state: StreamingState,
914
+ recorder: SteelSessionRecorder,
915
+ tool_calls: list[ToolCallInfo],
916
+ ) -> str | None:
917
+ """Handle terminal or extraction errors detected during a step.
918
+
919
+ Args:
920
+ agent: Browser-use agent with latest results.
921
+ streaming_state: Current streaming state object.
922
+ recorder: Recorder used to schedule background exports.
923
+ tool_calls: Tool calls extracted from the agent step.
924
+
925
+ Returns:
926
+ Error message when the step must terminate, otherwise ``None``.
927
+ """
928
+ terminal_error = self._detect_terminal_session_error(agent, tool_calls)
929
+ if terminal_error:
930
+ message = f"Browser session disconnected unexpectedly. Steel reported: {terminal_error}"
931
+ self._finalize_stream_with_error(streaming_state, recorder, message)
932
+ return message
933
+
934
+ extraction_error = detect_structured_data_failure(tool_calls, self._summarize_terminal_error)
935
+ if extraction_error:
936
+ self._finalize_stream_with_error(streaming_state, recorder, extraction_error)
937
+ return extraction_error
938
+
939
+ return None
940
+
941
+ def _finalize_stream_with_error(
942
+ self,
943
+ streaming_state: StreamingState,
944
+ recorder: SteelSessionRecorder,
945
+ message: str,
946
+ ) -> None:
947
+ """Mark the stream as complete due to an unrecoverable error.
948
+
949
+ Args:
950
+ streaming_state: Current streaming state to update.
951
+ recorder: Recorder used to schedule background exports.
952
+ message: Human-readable error message to persist.
953
+ """
954
+ streaming_state.terminal_error = message
955
+ streaming_state.is_complete = True
956
+ if streaming_state.session_id:
957
+ self._start_background_recording(recorder, streaming_state.session_id)
958
+ streaming_state.recording_started = True
959
+
960
+ def _update_completion_state(
961
+ self,
962
+ streaming_state: StreamingState,
963
+ recorder: SteelSessionRecorder,
964
+ ) -> None:
965
+ """Mark the streaming state as complete and schedule recording when needed.
966
+
967
+ Args:
968
+ streaming_state: Current streaming state to mutate.
969
+ recorder: Recorder used to kick off background exports.
970
+ """
971
+ streaming_state.is_complete = True
972
+ if streaming_state.session_id:
973
+ self._start_background_recording(recorder, streaming_state.session_id)
974
+ streaming_state.recording_started = True
975
+
976
+ def _detect_terminal_session_error(self, agent: Agent, tool_calls: list[ToolCallInfo]) -> str | None:
977
+ """Inspect tool outputs and agent results for terminal Steel session failures.
978
+
979
+ Args:
980
+ agent: The browser-use agent providing result context.
981
+ tool_calls: Tool call descriptors extracted for the current step.
982
+
983
+ Returns:
984
+ str | None: A concise terminal error summary when detected, otherwise None.
985
+ """
986
+ candidates = self._collect_session_messages(agent, tool_calls)
987
+
988
+ fatal_match = session_errors.find_fatal_message(candidates)
989
+ if fatal_match:
990
+ fatal_message, _ = fatal_match
991
+ return self._summarize_terminal_error(fatal_message)
992
+
993
+ self._log_session_warnings(candidates)
994
+
995
+ return None
996
+
997
+ def _collect_session_messages(self, agent: Agent, tool_calls: list[ToolCallInfo]) -> list[str]:
998
+ """Gather outputs and errors emitted during a step for analysis.
999
+
1000
+ Args:
1001
+ agent: Browser-use agent providing result context.
1002
+ tool_calls: Tool calls extracted from the step execution.
1003
+
1004
+ Returns:
1005
+ List of raw output/error messages produced during the step.
1006
+ """
1007
+ tool_outputs = [call.output for call in tool_calls if call.output]
1008
+ result_errors = []
1009
+ if agent.state.last_result:
1010
+ result_errors = [result.error for result in agent.state.last_result if result and result.error]
1011
+ return [message for message in (*tool_outputs, *result_errors) if message]
1012
+
1013
+ def _log_session_warnings(self, candidates: list[str]) -> None:
1014
+ """Emit debug logs for non-fatal Steel session warnings.
1015
+
1016
+ Args:
1017
+ candidates: Messages captured during the step to inspect for warnings.
1018
+ """
1019
+ logged_warnings: set[str] = set()
1020
+ for candidate in candidates:
1021
+ warning_name = session_errors.categorize_warning_message(candidate)
1022
+ if warning_name and warning_name not in logged_warnings:
1023
+ logger.debug("Detected session warning=%s; continuing stream.", warning_name)
1024
+ logged_warnings.add(warning_name)
1025
+
1026
+ def _detect_terminal_error_from_exception(self, error_message: str) -> str | None:
1027
+ """Detect terminal Steel session failures from raised exceptions.
1028
+
1029
+ Args:
1030
+ error_message: The exception message captured during step execution.
1031
+
1032
+ Returns:
1033
+ str | None: Terminal error summary when matched, otherwise None.
1034
+ """
1035
+ fatal_match = session_errors.find_fatal_message([error_message])
1036
+ if fatal_match:
1037
+ fatal_message, _ = fatal_match
1038
+ return self._summarize_terminal_error(fatal_message)
1039
+ warning_name = session_errors.categorize_warning_message(error_message)
1040
+ if warning_name:
1041
+ logger.debug(
1042
+ "Detected session warning=%s from exception; continuing stream.",
1043
+ warning_name,
1044
+ )
1045
+ return None
1046
+
1047
+ @staticmethod
1048
+ def _summarize_terminal_error(error_message: str, max_length: int = 200) -> str:
1049
+ """Produce a concise summary for terminal error messages.
1050
+
1051
+ Args:
1052
+ error_message: The original terminal error message.
1053
+ max_length: Maximum length for the summary text.
1054
+
1055
+ Returns:
1056
+ str: Sanitized and truncated error summary.
1057
+ """
1058
+ sanitized = " ".join(error_message.split())
1059
+ if len(sanitized) <= max_length:
1060
+ return sanitized
1061
+ return sanitized[: max_length - 1] + "…"
1062
+
1063
+ def _log_stream_event(self, label: str, event: dict[str, Any] | StreamingResponse) -> None:
1064
+ """Log streaming events to trace emission order during debugging.
1065
+
1066
+ Args:
1067
+ label: Descriptive label for the event source.
1068
+ event: Streaming payload (dict or StreamingResponse).
1069
+ """
1070
+ try:
1071
+ payload = event.to_dict() if isinstance(event, StreamingResponse) else event
1072
+ if isinstance(payload, dict):
1073
+ message = json.dumps(payload, default=str)
1074
+ else:
1075
+ message = repr(payload)
1076
+ logger.info("Streaming event (%s): %s", label, message)
1077
+ except Exception as error: # pragma: no cover - defensive logging
1078
+ logger.info("Streaming event (%s): logging failed: %s", label, error)
1079
+
1080
+ def _start_background_recording(self, recorder: SteelSessionRecorder, session_id: str) -> None:
1081
+ """Start background process to record and convert Steel session to video.
1082
+
1083
+ This method starts a background task to fetch rrweb events and convert them to video
1084
+ after the browser-use agent has completed its task. The recording happens asynchronously
1085
+ to avoid blocking the main execution flow.
1086
+
1087
+ Args:
1088
+ recorder: The Steel session recorder instance.
1089
+ session_id: The Steel session ID to record.
1090
+
1091
+ Note:
1092
+ The recording task is created using asyncio.create_task() to run in the
1093
+ background. This allows the main execution to continue while video
1094
+ generation happens asynchronously.
1095
+ """
1096
+ recording_task = asyncio.create_task(recorder.record_session_to_video(session_id=session_id))
1097
+ background_tasks: set[asyncio.Task[Any]] = getattr(self, "_background_tasks", set())
1098
+ background_tasks.add(recording_task)
1099
+ recording_task.add_done_callback(background_tasks.discard)
1100
+ self._background_tasks = background_tasks
1101
+
1102
+ def _finalize_streaming_on_cancel(
1103
+ self, streaming_state: StreamingState | None, recorder: SteelSessionRecorder
1104
+ ) -> None:
1105
+ """Mark streaming as complete and trigger recording when execution is cancelled.
1106
+
1107
+ Args:
1108
+ streaming_state: Current state container for streaming progress.
1109
+ recorder: Recorder instance responsible for exporting session recordings.
1110
+ """
1111
+ if not streaming_state or streaming_state.is_complete:
1112
+ return
1113
+
1114
+ streaming_state.is_complete = True
1115
+ if not streaming_state.terminal_error:
1116
+ streaming_state.terminal_error = "Execution cancelled by upstream request."
1117
+
1118
+ if streaming_state.session_id and not streaming_state.recording_started:
1119
+ self._start_background_recording(recorder, streaming_state.session_id)
1120
+ streaming_state.recording_started = True