aip-agents-binary 0.5.20__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.
Files changed (280) hide show
  1. aip_agents/__init__.py +65 -0
  2. aip_agents/a2a/__init__.py +19 -0
  3. aip_agents/a2a/server/__init__.py +10 -0
  4. aip_agents/a2a/server/base_executor.py +1086 -0
  5. aip_agents/a2a/server/google_adk_executor.py +198 -0
  6. aip_agents/a2a/server/langflow_executor.py +180 -0
  7. aip_agents/a2a/server/langgraph_executor.py +270 -0
  8. aip_agents/a2a/types.py +232 -0
  9. aip_agents/agent/__init__.py +27 -0
  10. aip_agents/agent/base_agent.py +970 -0
  11. aip_agents/agent/base_langgraph_agent.py +2942 -0
  12. aip_agents/agent/google_adk_agent.py +926 -0
  13. aip_agents/agent/google_adk_constants.py +6 -0
  14. aip_agents/agent/hitl/__init__.py +24 -0
  15. aip_agents/agent/hitl/config.py +28 -0
  16. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  17. aip_agents/agent/hitl/manager.py +532 -0
  18. aip_agents/agent/hitl/models.py +18 -0
  19. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  20. aip_agents/agent/hitl/prompt/base.py +42 -0
  21. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  22. aip_agents/agent/hitl/registry.py +149 -0
  23. aip_agents/agent/interface.py +138 -0
  24. aip_agents/agent/interfaces.py +65 -0
  25. aip_agents/agent/langflow_agent.py +464 -0
  26. aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
  27. aip_agents/agent/langgraph_react_agent.py +2514 -0
  28. aip_agents/agent/system_instruction_context.py +34 -0
  29. aip_agents/clients/__init__.py +10 -0
  30. aip_agents/clients/langflow/__init__.py +10 -0
  31. aip_agents/clients/langflow/client.py +477 -0
  32. aip_agents/clients/langflow/types.py +18 -0
  33. aip_agents/constants.py +23 -0
  34. aip_agents/credentials/manager.py +132 -0
  35. aip_agents/examples/__init__.py +5 -0
  36. aip_agents/examples/compare_streaming_client.py +783 -0
  37. aip_agents/examples/compare_streaming_server.py +142 -0
  38. aip_agents/examples/demo_memory_recall.py +401 -0
  39. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  40. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  41. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  42. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  43. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  44. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  45. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  46. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  47. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  48. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  49. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  50. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  51. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  52. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  53. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  54. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  55. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  56. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  57. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  58. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  59. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  60. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  61. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  62. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  63. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  64. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  65. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  66. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  67. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  68. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  69. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  70. aip_agents/examples/hello_world_google_adk.py +41 -0
  71. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  72. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  73. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  74. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  75. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  76. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  77. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  78. aip_agents/examples/hello_world_langchain.py +28 -0
  79. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  80. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  81. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  82. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  83. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  84. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  85. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  86. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  87. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  88. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  89. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  90. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  91. aip_agents/examples/hello_world_langgraph.py +39 -0
  92. aip_agents/examples/hello_world_langgraph_bosa_twitter.py +41 -0
  93. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  94. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  95. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  96. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  97. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  98. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  99. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  100. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  101. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  102. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  103. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  104. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  105. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  106. aip_agents/examples/hello_world_pii_logger.py +21 -0
  107. aip_agents/examples/hello_world_sentry.py +133 -0
  108. aip_agents/examples/hello_world_step_limits.py +273 -0
  109. aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
  110. aip_agents/examples/hello_world_tool_output_client.py +46 -0
  111. aip_agents/examples/hello_world_tool_output_server.py +114 -0
  112. aip_agents/examples/hitl_demo.py +724 -0
  113. aip_agents/examples/mcp_configs/configs.py +63 -0
  114. aip_agents/examples/mcp_servers/common.py +76 -0
  115. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  116. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  117. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  118. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  119. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  120. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  121. aip_agents/examples/pii_demo_langgraph_server.py +126 -0
  122. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  123. aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
  124. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  125. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
  126. aip_agents/examples/tools/__init__.py +27 -0
  127. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  128. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  129. aip_agents/examples/tools/data_generator_tool.py +103 -0
  130. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  131. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  132. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  133. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  134. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  135. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  136. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  137. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  138. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  139. aip_agents/examples/tools/random_chart_tool.py +142 -0
  140. aip_agents/examples/tools/serper_tool.py +202 -0
  141. aip_agents/examples/tools/stock_tools.py +82 -0
  142. aip_agents/examples/tools/table_generator_tool.py +167 -0
  143. aip_agents/examples/tools/time_tool.py +82 -0
  144. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  145. aip_agents/executor/agent_executor.py +473 -0
  146. aip_agents/executor/base.py +48 -0
  147. aip_agents/mcp/__init__.py +1 -0
  148. aip_agents/mcp/client/__init__.py +14 -0
  149. aip_agents/mcp/client/base_mcp_client.py +369 -0
  150. aip_agents/mcp/client/connection_manager.py +193 -0
  151. aip_agents/mcp/client/google_adk/__init__.py +11 -0
  152. aip_agents/mcp/client/google_adk/client.py +381 -0
  153. aip_agents/mcp/client/langchain/__init__.py +11 -0
  154. aip_agents/mcp/client/langchain/client.py +265 -0
  155. aip_agents/mcp/client/persistent_session.py +359 -0
  156. aip_agents/mcp/client/session_pool.py +351 -0
  157. aip_agents/mcp/client/transports.py +215 -0
  158. aip_agents/mcp/utils/__init__.py +7 -0
  159. aip_agents/mcp/utils/config_validator.py +139 -0
  160. aip_agents/memory/__init__.py +14 -0
  161. aip_agents/memory/adapters/__init__.py +10 -0
  162. aip_agents/memory/adapters/base_adapter.py +717 -0
  163. aip_agents/memory/adapters/mem0.py +84 -0
  164. aip_agents/memory/base.py +84 -0
  165. aip_agents/memory/constants.py +49 -0
  166. aip_agents/memory/factory.py +86 -0
  167. aip_agents/memory/guidance.py +20 -0
  168. aip_agents/memory/simple_memory.py +47 -0
  169. aip_agents/middleware/__init__.py +17 -0
  170. aip_agents/middleware/base.py +88 -0
  171. aip_agents/middleware/manager.py +128 -0
  172. aip_agents/middleware/todolist.py +274 -0
  173. aip_agents/schema/__init__.py +69 -0
  174. aip_agents/schema/a2a.py +56 -0
  175. aip_agents/schema/agent.py +111 -0
  176. aip_agents/schema/hitl.py +157 -0
  177. aip_agents/schema/langgraph.py +37 -0
  178. aip_agents/schema/model_id.py +97 -0
  179. aip_agents/schema/step_limit.py +108 -0
  180. aip_agents/schema/storage.py +40 -0
  181. aip_agents/sentry/__init__.py +11 -0
  182. aip_agents/sentry/sentry.py +151 -0
  183. aip_agents/storage/__init__.py +41 -0
  184. aip_agents/storage/base.py +85 -0
  185. aip_agents/storage/clients/__init__.py +12 -0
  186. aip_agents/storage/clients/minio_client.py +318 -0
  187. aip_agents/storage/config.py +62 -0
  188. aip_agents/storage/providers/__init__.py +15 -0
  189. aip_agents/storage/providers/base.py +106 -0
  190. aip_agents/storage/providers/memory.py +114 -0
  191. aip_agents/storage/providers/object_storage.py +214 -0
  192. aip_agents/tools/__init__.py +33 -0
  193. aip_agents/tools/bosa_tools.py +105 -0
  194. aip_agents/tools/browser_use/__init__.py +82 -0
  195. aip_agents/tools/browser_use/action_parser.py +103 -0
  196. aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
  197. aip_agents/tools/browser_use/llm_config.py +120 -0
  198. aip_agents/tools/browser_use/minio_storage.py +198 -0
  199. aip_agents/tools/browser_use/schemas.py +119 -0
  200. aip_agents/tools/browser_use/session.py +76 -0
  201. aip_agents/tools/browser_use/session_errors.py +132 -0
  202. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  203. aip_agents/tools/browser_use/streaming.py +813 -0
  204. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  205. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  206. aip_agents/tools/browser_use/types.py +78 -0
  207. aip_agents/tools/code_sandbox/__init__.py +26 -0
  208. aip_agents/tools/code_sandbox/constant.py +13 -0
  209. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +257 -0
  210. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  211. aip_agents/tools/constants.py +165 -0
  212. aip_agents/tools/document_loader/__init__.py +44 -0
  213. aip_agents/tools/document_loader/base_reader.py +302 -0
  214. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  215. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  216. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  217. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  218. aip_agents/tools/gl_connector/__init__.py +5 -0
  219. aip_agents/tools/gl_connector/tool.py +351 -0
  220. aip_agents/tools/memory_search/__init__.py +22 -0
  221. aip_agents/tools/memory_search/base.py +200 -0
  222. aip_agents/tools/memory_search/mem0.py +258 -0
  223. aip_agents/tools/memory_search/schema.py +48 -0
  224. aip_agents/tools/memory_search_tool.py +26 -0
  225. aip_agents/tools/time_tool.py +117 -0
  226. aip_agents/tools/tool_config_injector.py +300 -0
  227. aip_agents/tools/web_search/__init__.py +15 -0
  228. aip_agents/tools/web_search/serper_tool.py +187 -0
  229. aip_agents/types/__init__.py +70 -0
  230. aip_agents/types/a2a_events.py +13 -0
  231. aip_agents/utils/__init__.py +79 -0
  232. aip_agents/utils/a2a_connector.py +1757 -0
  233. aip_agents/utils/artifact_helpers.py +502 -0
  234. aip_agents/utils/constants.py +22 -0
  235. aip_agents/utils/datetime/__init__.py +34 -0
  236. aip_agents/utils/datetime/normalization.py +231 -0
  237. aip_agents/utils/datetime/timezone.py +206 -0
  238. aip_agents/utils/env_loader.py +27 -0
  239. aip_agents/utils/event_handler_registry.py +58 -0
  240. aip_agents/utils/file_prompt_utils.py +176 -0
  241. aip_agents/utils/final_response_builder.py +211 -0
  242. aip_agents/utils/formatter_llm_client.py +231 -0
  243. aip_agents/utils/langgraph/__init__.py +19 -0
  244. aip_agents/utils/langgraph/converter.py +128 -0
  245. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  246. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  247. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  248. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
  249. aip_agents/utils/langgraph/tool_output_management.py +967 -0
  250. aip_agents/utils/logger.py +195 -0
  251. aip_agents/utils/metadata/__init__.py +27 -0
  252. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  253. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  254. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  255. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  256. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  257. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  258. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  259. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  260. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  261. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  262. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  263. aip_agents/utils/metadata_helper.py +358 -0
  264. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  265. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  266. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  267. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  268. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  269. aip_agents/utils/pii/__init__.py +25 -0
  270. aip_agents/utils/pii/pii_handler.py +397 -0
  271. aip_agents/utils/pii/pii_helper.py +207 -0
  272. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  273. aip_agents/utils/reference_helper.py +273 -0
  274. aip_agents/utils/sse_chunk_transformer.py +831 -0
  275. aip_agents/utils/step_limit_manager.py +265 -0
  276. aip_agents/utils/token_usage_helper.py +156 -0
  277. aip_agents_binary-0.5.20.dist-info/METADATA +681 -0
  278. aip_agents_binary-0.5.20.dist-info/RECORD +280 -0
  279. aip_agents_binary-0.5.20.dist-info/WHEEL +5 -0
  280. aip_agents_binary-0.5.20.dist-info/top_level.txt +1 -0
@@ -0,0 +1,257 @@
1
+ """High-level helpers for parsing and validating structured data extractor output.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from collections.abc import Callable, Sequence
11
+ from typing import Any
12
+
13
+ from aip_agents.tools.browser_use.structured_data_recovery import (
14
+ recover_concatenated_json_objects,
15
+ repair_json_blob,
16
+ )
17
+ from aip_agents.tools.browser_use.types import ToolCallInfo
18
+ from aip_agents.utils.logger import get_logger
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ def detect_structured_data_failure(
24
+ tool_calls: Sequence[ToolCallInfo],
25
+ summarize_error: Callable[[str], str],
26
+ ) -> str | None:
27
+ """Return a descriptive error when structured data extraction yields no results.
28
+
29
+ Args:
30
+ tool_calls: Tool call descriptors extracted from the latest agent step.
31
+ summarize_error: Function to summarize error messages.
32
+
33
+ Returns:
34
+ str | None: Failure reason when extraction yielded nothing, otherwise None.
35
+ """
36
+ for call in tool_calls:
37
+ failure = structured_data_failure_for_call(call, summarize_error)
38
+ if failure:
39
+ return failure
40
+ return None
41
+
42
+
43
+ def structured_data_failure_for_call(
44
+ call: ToolCallInfo,
45
+ summarize_error: Callable[[str], str],
46
+ ) -> str | None:
47
+ """Evaluate a single tool call for extractor failures.
48
+
49
+ Args:
50
+ call: Tool call descriptor encapsulating name/args/output.
51
+ summarize_error: Function to summarize error messages.
52
+
53
+ Returns:
54
+ str | None: Failure message when the call represents a bad extraction.
55
+ """
56
+ if call.name != "extract_structured_data":
57
+ return None
58
+
59
+ payload, parse_error = parse_structured_data(call.output or "", summarize_error)
60
+ if parse_error:
61
+ summary = summarize_error(parse_error)
62
+ return f"Structured data extraction emitted invalid JSON. Decoder reported: {summary}"
63
+ if payload is None:
64
+ return None
65
+
66
+ raw_output = call.output or ""
67
+ if isinstance(payload, dict):
68
+ error_message = payload_reports_error(payload, raw_output, summarize_error)
69
+ if error_message:
70
+ return error_message
71
+
72
+ empty_message = payload_reports_empty(payload, raw_output, summarize_error)
73
+ if empty_message:
74
+ return empty_message
75
+
76
+ return None
77
+
78
+
79
+ def payload_reports_error(
80
+ payload: dict[str, Any],
81
+ raw_output: str,
82
+ summarize_error: Callable[[str], str],
83
+ ) -> str | None:
84
+ """Return a formatted error message when extractor status indicates failure.
85
+
86
+ Args:
87
+ payload: Parsed JSON payload emitted by the extractor.
88
+ raw_output: Original extractor output (used for fallback summaries).
89
+ summarize_error: Function to summarize error messages.
90
+
91
+ Returns:
92
+ str | None: Human-readable failure string when extraction failed.
93
+ """
94
+ status = payload.get("status")
95
+ if isinstance(status, str) and status.lower() == "error":
96
+ message = payload.get("message") or payload.get("error") or raw_output
97
+ summary = summarize_error(message)
98
+ return f"Structured data extraction failed: {summary}"
99
+ return None
100
+
101
+
102
+ def payload_reports_empty(
103
+ payload: dict[str, Any],
104
+ raw_output: str,
105
+ summarize_error: Callable[[str], str],
106
+ ) -> str | None:
107
+ """Return a formatted message when the extractor returned no usable data.
108
+
109
+ Args:
110
+ payload: Parsed JSON payload emitted by the extractor.
111
+ raw_output: Original extractor output string.
112
+ summarize_error: Function to summarize error messages.
113
+
114
+ Returns:
115
+ str | None: Human-readable failure message when no content was extracted.
116
+ """
117
+ products = payload.get("products")
118
+ count = payload.get("count")
119
+ available = payload.get("available")
120
+ products_found = payload.get("products_found")
121
+
122
+ no_products = isinstance(products, list) and len(products) == 0
123
+ explicit_zero = count == 0 or products_found == 0
124
+ explicitly_unavailable = available is False
125
+
126
+ if no_products or explicit_zero or explicitly_unavailable:
127
+ summary = summarize_error(raw_output)
128
+ return f"Structured data extraction returned no usable entries. Extractor response: {summary}"
129
+ return None
130
+
131
+
132
+ def parse_structured_data(
133
+ output: str,
134
+ summarize_error: Callable[[str], str],
135
+ ) -> tuple[dict[str, Any] | list[Any] | None, str | None]:
136
+ """Extract the JSON blob emitted by extract_structured_data if present.
137
+
138
+ Args:
139
+ output: Raw string payload returned by extract_structured_data.
140
+ summarize_error: Function to summarize error messages.
141
+
142
+ Returns:
143
+ tuple[dict[str, Any] | list[Any] | None, str | None]: Parsed JSON payload when extraction succeeds,
144
+ otherwise a tuple containing None and a diagnostic string on failure.
145
+ """
146
+ content = extract_content_after_marker(output)
147
+ if not content:
148
+ return None, None
149
+
150
+ json_blob = extract_json_blob(content)
151
+ if not json_blob:
152
+ return None, None
153
+
154
+ parsed_data = attempt_json_recovery(json_blob)
155
+ if parsed_data is not None:
156
+ return parsed_data, None
157
+
158
+ snippet = summarize_error(json_blob)
159
+ error_msg = "JSON parsing failed after all recovery attempts"
160
+ logger.warning(
161
+ "Structured data extractor emitted malformed JSON: %s. snippet=%s",
162
+ error_msg,
163
+ snippet,
164
+ )
165
+ return None, f"{error_msg}: {snippet}"
166
+
167
+
168
+ def extract_content_after_marker(output: str) -> str | None:
169
+ """Extract content after the 'Extracted Content:' marker and clean trailing metadata.
170
+
171
+ Args:
172
+ output: Raw string payload returned by extract_structured_data.
173
+
174
+ Returns:
175
+ str | None: Cleaned content after the marker, or None if marker not found.
176
+ """
177
+ if "Extracted Content:" not in output:
178
+ return None
179
+
180
+ _, remainder = output.split("Extracted Content:", 1)
181
+
182
+ for marker in ("</extracted_content>", "<file_system>", "</file_system>"):
183
+ marker_index = remainder.find(marker)
184
+ if marker_index != -1:
185
+ remainder = remainder[:marker_index]
186
+ break
187
+
188
+ remainder = remainder.strip()
189
+ return remainder if remainder else None
190
+
191
+
192
+ def extract_json_blob(content: str) -> str | None:
193
+ """Extract the JSON blob from content by finding delimiters and trimming trailing content.
194
+
195
+ Args:
196
+ content: Content string potentially containing JSON.
197
+
198
+ Returns:
199
+ str | None: Extracted JSON blob, or None if no valid JSON delimiters found.
200
+ """
201
+ first_bracket = content.find("[")
202
+ first_brace = content.find("{")
203
+ candidates = [index for index in (first_bracket, first_brace) if index != -1]
204
+ if not candidates:
205
+ return None
206
+
207
+ start = min(candidates)
208
+ json_blob = content[start:]
209
+
210
+ closing_char = "]" if json_blob.startswith("[") else "}"
211
+ end = json_blob.rfind(closing_char)
212
+ if end == -1:
213
+ return None
214
+ return json_blob[: end + 1].strip()
215
+
216
+
217
+ def attempt_json_recovery(json_blob: str) -> dict[str, Any] | list[Any] | None:
218
+ """Attempt to parse JSON blob, with recovery strategies for common issues.
219
+
220
+ Args:
221
+ json_blob: JSON string to attempt parsing.
222
+
223
+ Returns:
224
+ dict[str, Any] | list[Any] | None: Parsed JSON data if successful, None if all recovery attempts fail.
225
+ """
226
+ try:
227
+ return json.loads(json_blob)
228
+ except json.JSONDecodeError as error:
229
+ recovered_payload = recover_concatenated_json_objects(json_blob)
230
+ if recovered_payload is not None:
231
+ return recovered_payload
232
+
233
+ repaired = repair_json_blob(json_blob)
234
+ if repaired:
235
+ try:
236
+ payload = json.loads(repaired)
237
+ except json.JSONDecodeError:
238
+ logger.debug("json_repair returned unrecoverable payload for structured data output.")
239
+ else:
240
+ logger.info(
241
+ "Structured data extractor output repaired via json_repair and parsed successfully. original_error=%s",
242
+ error.msg,
243
+ )
244
+ return payload
245
+ return None
246
+
247
+
248
+ __all__ = [
249
+ "attempt_json_recovery",
250
+ "detect_structured_data_failure",
251
+ "extract_content_after_marker",
252
+ "extract_json_blob",
253
+ "parse_structured_data",
254
+ "payload_reports_empty",
255
+ "payload_reports_error",
256
+ "structured_data_failure_for_call",
257
+ ]
@@ -0,0 +1,204 @@
1
+ """Utilities for recovering malformed structured-data payloads emitted by browser-use.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from typing import Any
11
+
12
+ from json_repair import repair_json
13
+
14
+ from aip_agents.utils.logger import get_logger
15
+
16
+ logger = get_logger(__name__)
17
+
18
+
19
+ def recover_concatenated_json_objects(json_blob: str) -> dict[str, Any] | None:
20
+ """Normalize concatenated JSON object strings into a structured payload.
21
+
22
+ Args:
23
+ json_blob: Raw JSON-like string returned by the structured data extractor.
24
+
25
+ Returns:
26
+ dict[str, Any] | None: Standardized payload when multiple objects are recovered,
27
+ otherwise None.
28
+ """
29
+ segments = _split_top_level_json_objects(json_blob)
30
+ if len(segments) <= 1:
31
+ return None
32
+
33
+ try:
34
+ records = [json.loads(segment) for segment in segments]
35
+ except json.JSONDecodeError:
36
+ return None
37
+
38
+ count = len(records)
39
+ logger.info("Structured data extractor returned concatenated JSON objects. recovered=%s", count)
40
+ return {
41
+ "status": "ok",
42
+ "items": records,
43
+ "products": records,
44
+ "count": count,
45
+ "products_found": count,
46
+ "available": bool(records),
47
+ }
48
+
49
+
50
+ def repair_json_blob(json_blob: str) -> str | None:
51
+ """Apply json_repair to malformed JSON strings and return the mutated payload.
52
+
53
+ Args:
54
+ json_blob: Raw JSON string that may contain syntax mistakes.
55
+
56
+ Returns:
57
+ str | None: Repaired JSON string when modifications were applied, otherwise None.
58
+ """
59
+ try:
60
+ repaired = repair_json(json_blob)
61
+ except Exception:
62
+ logger.debug("json_repair failed to repair structured data output.", exc_info=True)
63
+ return None
64
+
65
+ if not repaired or repaired == json_blob:
66
+ return None
67
+
68
+ return repaired
69
+
70
+
71
+ def _split_top_level_json_objects(json_blob: str) -> list[str]:
72
+ """Split concatenated JSON objects while respecting string literals.
73
+
74
+ Args:
75
+ json_blob: The JSON string containing concatenated objects to split.
76
+ """
77
+ splitter = _JsonObjectSplitter(json_blob)
78
+ return splitter.split_objects()
79
+
80
+
81
+ def _has_only_separators(value: str) -> bool:
82
+ """Return True when the substring only contains whitespace or commas.
83
+
84
+ Args:
85
+ value: The string to check for separators only.
86
+ """
87
+ return value.strip(" \t\r\n,") == ""
88
+
89
+
90
+ class _JsonObjectSplitter:
91
+ """Helper class to split JSON objects with reduced cognitive complexity."""
92
+
93
+ def __init__(self, json_blob: str) -> None:
94
+ """Initialize the splitter with the raw JSON string.
95
+
96
+ Args:
97
+ json_blob: Raw string potentially containing concatenated JSON objects.
98
+ """
99
+ self.json_blob = json_blob
100
+ self.segments: list[str] = []
101
+ self.depth = 0
102
+ self.start: int | None = None
103
+ self.last_end = 0
104
+ self.in_string = False
105
+ self.escaping = False
106
+
107
+ def split_objects(self) -> list[str]:
108
+ """Main method to split JSON objects."""
109
+ if not self._parse_characters():
110
+ return []
111
+
112
+ if not self._validate_final_state():
113
+ return []
114
+
115
+ return self.segments
116
+
117
+ def _parse_characters(self) -> bool:
118
+ """Parse each character and build segments. Returns False if invalid."""
119
+ for index, char in enumerate(self.json_blob):
120
+ if not self._process_character(char, index):
121
+ return False
122
+ return True
123
+
124
+ def _process_character(self, char: str, index: int) -> bool:
125
+ """Process a single character. Returns False if parsing should stop.
126
+
127
+ Args:
128
+ char: The character to process.
129
+ index: The position of the character in the JSON blob.
130
+
131
+ Returns:
132
+ True if processing should continue, False if parsing should stop.
133
+ """
134
+ if self.escaping:
135
+ self.escaping = False
136
+ return True
137
+
138
+ if char == "\\":
139
+ self.escaping = True
140
+ return True
141
+
142
+ if char == '"':
143
+ self.in_string = not self.in_string
144
+ return True
145
+
146
+ if self.in_string:
147
+ return True
148
+
149
+ return self._process_non_string_char(char, index)
150
+
151
+ def _process_non_string_char(self, char: str, index: int) -> bool:
152
+ """Process characters outside of strings.
153
+
154
+ Args:
155
+ char: The character to process (not in a string).
156
+ index: The position of the character in the JSON blob.
157
+
158
+ Returns:
159
+ True if processing should continue, False if invalid structure found.
160
+ """
161
+ if char == "{":
162
+ return self._handle_open_brace(index)
163
+ if char == "}":
164
+ return self._handle_close_brace(index)
165
+ return True
166
+
167
+ def _handle_open_brace(self, index: int) -> bool:
168
+ """Handle opening brace character.
169
+
170
+ Args:
171
+ index: The position of the opening brace in the JSON blob.
172
+
173
+ Returns:
174
+ True if brace should be processed, False if invalid separators found.
175
+ """
176
+ if self.depth == 0:
177
+ if not _has_only_separators(self.json_blob[self.last_end : index]):
178
+ return False
179
+ self.start = index
180
+ self.depth += 1
181
+ return True
182
+
183
+ def _handle_close_brace(self, index: int) -> bool:
184
+ """Handle closing brace character.
185
+
186
+ Args:
187
+ index: The position of the closing brace in the JSON blob.
188
+
189
+ Returns:
190
+ Always True as closing braces don't cause parsing failures.
191
+ """
192
+ self.depth -= 1
193
+ if self.depth == 0 and self.start is not None:
194
+ end = index + 1
195
+ self.segments.append(self.json_blob[self.start : end])
196
+ self.last_end = end
197
+ return True
198
+
199
+ def _validate_final_state(self) -> bool:
200
+ """Validate that parsing completed successfully."""
201
+ if self.depth != 0 or self.in_string:
202
+ return False
203
+
204
+ return _has_only_separators(self.json_blob[self.last_end :])
@@ -0,0 +1,78 @@
1
+ """Shared data structures for the browser-use tool.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Any
11
+
12
+
13
+ class BrowserUseFatalError(RuntimeError):
14
+ """Raised when the Browser Use session must terminate immediately."""
15
+
16
+
17
+ @dataclass
18
+ class ToolCallInfo:
19
+ """Structured information for a single tool call."""
20
+
21
+ name: str
22
+ args: dict[str, Any]
23
+ output: str
24
+
25
+
26
+ @dataclass
27
+ class StreamingResponse:
28
+ """Standardized streaming response structure."""
29
+
30
+ event_type: str
31
+ content: str
32
+ thinking_and_activity_info: dict
33
+ is_final: bool
34
+ tool_info: dict[str, Any] | None = None
35
+ metadata: dict[str, Any] | None = None
36
+
37
+ def to_dict(self) -> dict[str, Any]:
38
+ """Convert to dictionary format for yielding."""
39
+ return {
40
+ "event_type": self.event_type,
41
+ "content": self.content,
42
+ "thinking_and_activity_info": self.thinking_and_activity_info,
43
+ "tool_info": self.tool_info,
44
+ "is_final": self.is_final,
45
+ "metadata": self.metadata,
46
+ }
47
+
48
+
49
+ @dataclass
50
+ class StreamingState:
51
+ """State management for streaming operations."""
52
+
53
+ debug_url: str
54
+ recording_url: str
55
+ step_count: int = 0
56
+ is_complete: bool = False
57
+ session_id: str | None = None
58
+ terminal_error: str | None = None
59
+ recording_started: bool = False
60
+
61
+
62
+ @dataclass
63
+ class RetryDecision:
64
+ """Encapsulate retry metadata when Steel sessions need to be restarted."""
65
+
66
+ retries_remaining: int
67
+ attempted_retries: int
68
+ message: str
69
+ delay: float
70
+
71
+
72
+ __all__ = [
73
+ "BrowserUseFatalError",
74
+ "RetryDecision",
75
+ "StreamingResponse",
76
+ "StreamingState",
77
+ "ToolCallInfo",
78
+ ]
@@ -0,0 +1,26 @@
1
+ # flake8: noqa: F401
2
+ """Code Sandbox Tools for AI Agents.
3
+
4
+ This package provides code execution capabilities for AI agents through integration
5
+ with E2B Cloud Sandbox environment.
6
+
7
+ Authors:
8
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
9
+ """
10
+
11
+ import warnings
12
+
13
+ try:
14
+ import e2b
15
+
16
+ from aip_agents.tools.code_sandbox.e2b_sandbox_tool import E2BCodeSandboxTool
17
+
18
+ __all__ = ["E2BCodeSandboxTool"]
19
+
20
+ except ImportError:
21
+ warnings.warn(
22
+ "Code sandbox tools not available. Install with: pip install aip-agents[local]",
23
+ ImportWarning,
24
+ stacklevel=2,
25
+ )
26
+ __all__ = []
@@ -0,0 +1,13 @@
1
+ """Constants for Code Sandbox tool.
2
+
3
+ Authors:
4
+ Komang Elang Surya Prawira (komang.e.s.prawira@gdplabs.id)
5
+ """
6
+
7
+ # The filename `data.csv` is used as a hard-coded value because we currently lack a mechanism
8
+ # to dynamically determine the actual filename. This static filename is used for saving files
9
+ # inside the sandbox, and any pre-population steps will need to read from this filename.
10
+ # Code interacting with the sandbox can directly access the content of the file using the pre-defined variable,
11
+ # eliminating the need to load the file again. Agents or LLMs do not need to be aware of this filename.
12
+ DATA_FILE_NAME = "data.csv"
13
+ DATA_FILE_PATH = f"/files/{DATA_FILE_NAME}"