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.
- aip_agents/__init__.py +65 -0
- aip_agents/a2a/__init__.py +19 -0
- aip_agents/a2a/server/__init__.py +10 -0
- aip_agents/a2a/server/base_executor.py +1086 -0
- aip_agents/a2a/server/google_adk_executor.py +198 -0
- aip_agents/a2a/server/langflow_executor.py +180 -0
- aip_agents/a2a/server/langgraph_executor.py +270 -0
- aip_agents/a2a/types.py +232 -0
- aip_agents/agent/__init__.py +27 -0
- aip_agents/agent/base_agent.py +970 -0
- aip_agents/agent/base_langgraph_agent.py +2942 -0
- aip_agents/agent/google_adk_agent.py +926 -0
- aip_agents/agent/google_adk_constants.py +6 -0
- aip_agents/agent/hitl/__init__.py +24 -0
- aip_agents/agent/hitl/config.py +28 -0
- aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
- aip_agents/agent/hitl/manager.py +532 -0
- aip_agents/agent/hitl/models.py +18 -0
- aip_agents/agent/hitl/prompt/__init__.py +9 -0
- aip_agents/agent/hitl/prompt/base.py +42 -0
- aip_agents/agent/hitl/prompt/deferred.py +73 -0
- aip_agents/agent/hitl/registry.py +149 -0
- aip_agents/agent/interface.py +138 -0
- aip_agents/agent/interfaces.py +65 -0
- aip_agents/agent/langflow_agent.py +464 -0
- aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
- aip_agents/agent/langgraph_react_agent.py +2514 -0
- aip_agents/agent/system_instruction_context.py +34 -0
- aip_agents/clients/__init__.py +10 -0
- aip_agents/clients/langflow/__init__.py +10 -0
- aip_agents/clients/langflow/client.py +477 -0
- aip_agents/clients/langflow/types.py +18 -0
- aip_agents/constants.py +23 -0
- aip_agents/credentials/manager.py +132 -0
- aip_agents/examples/__init__.py +5 -0
- aip_agents/examples/compare_streaming_client.py +783 -0
- aip_agents/examples/compare_streaming_server.py +142 -0
- aip_agents/examples/demo_memory_recall.py +401 -0
- aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
- aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
- aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
- aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
- aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
- aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
- aip_agents/examples/hello_world_google_adk.py +41 -0
- aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
- aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_stream.py +44 -0
- aip_agents/examples/hello_world_langchain.py +28 -0
- aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
- aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
- aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
- aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
- aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
- aip_agents/examples/hello_world_langchain_stream.py +36 -0
- aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
- aip_agents/examples/hello_world_langflow_agent.py +163 -0
- aip_agents/examples/hello_world_langgraph.py +39 -0
- aip_agents/examples/hello_world_langgraph_bosa_twitter.py +41 -0
- aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
- aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_stream.py +43 -0
- aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_model_switch_cli.py +210 -0
- aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
- aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
- aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
- aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
- aip_agents/examples/hello_world_pii_logger.py +21 -0
- aip_agents/examples/hello_world_sentry.py +133 -0
- aip_agents/examples/hello_world_step_limits.py +273 -0
- aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
- aip_agents/examples/hello_world_tool_output_client.py +46 -0
- aip_agents/examples/hello_world_tool_output_server.py +114 -0
- aip_agents/examples/hitl_demo.py +724 -0
- aip_agents/examples/mcp_configs/configs.py +63 -0
- aip_agents/examples/mcp_servers/common.py +76 -0
- aip_agents/examples/mcp_servers/mcp_name.py +29 -0
- aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
- aip_agents/examples/mcp_servers/mcp_time.py +10 -0
- aip_agents/examples/pii_demo_langgraph_client.py +69 -0
- aip_agents/examples/pii_demo_langgraph_server.py +126 -0
- aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
- aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
- aip_agents/examples/tools/__init__.py +27 -0
- aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
- aip_agents/examples/tools/adk_weather_tool.py +60 -0
- aip_agents/examples/tools/data_generator_tool.py +103 -0
- aip_agents/examples/tools/data_visualization_tool.py +312 -0
- aip_agents/examples/tools/image_artifact_tool.py +136 -0
- aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
- aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
- aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
- aip_agents/examples/tools/langchain_weather_tool.py +48 -0
- aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
- aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
- aip_agents/examples/tools/pii_demo_tools.py +189 -0
- aip_agents/examples/tools/random_chart_tool.py +142 -0
- aip_agents/examples/tools/serper_tool.py +202 -0
- aip_agents/examples/tools/stock_tools.py +82 -0
- aip_agents/examples/tools/table_generator_tool.py +167 -0
- aip_agents/examples/tools/time_tool.py +82 -0
- aip_agents/examples/tools/weather_forecast_tool.py +38 -0
- aip_agents/executor/agent_executor.py +473 -0
- aip_agents/executor/base.py +48 -0
- aip_agents/mcp/__init__.py +1 -0
- aip_agents/mcp/client/__init__.py +14 -0
- aip_agents/mcp/client/base_mcp_client.py +369 -0
- aip_agents/mcp/client/connection_manager.py +193 -0
- aip_agents/mcp/client/google_adk/__init__.py +11 -0
- aip_agents/mcp/client/google_adk/client.py +381 -0
- aip_agents/mcp/client/langchain/__init__.py +11 -0
- aip_agents/mcp/client/langchain/client.py +265 -0
- aip_agents/mcp/client/persistent_session.py +359 -0
- aip_agents/mcp/client/session_pool.py +351 -0
- aip_agents/mcp/client/transports.py +215 -0
- aip_agents/mcp/utils/__init__.py +7 -0
- aip_agents/mcp/utils/config_validator.py +139 -0
- aip_agents/memory/__init__.py +14 -0
- aip_agents/memory/adapters/__init__.py +10 -0
- aip_agents/memory/adapters/base_adapter.py +717 -0
- aip_agents/memory/adapters/mem0.py +84 -0
- aip_agents/memory/base.py +84 -0
- aip_agents/memory/constants.py +49 -0
- aip_agents/memory/factory.py +86 -0
- aip_agents/memory/guidance.py +20 -0
- aip_agents/memory/simple_memory.py +47 -0
- aip_agents/middleware/__init__.py +17 -0
- aip_agents/middleware/base.py +88 -0
- aip_agents/middleware/manager.py +128 -0
- aip_agents/middleware/todolist.py +274 -0
- aip_agents/schema/__init__.py +69 -0
- aip_agents/schema/a2a.py +56 -0
- aip_agents/schema/agent.py +111 -0
- aip_agents/schema/hitl.py +157 -0
- aip_agents/schema/langgraph.py +37 -0
- aip_agents/schema/model_id.py +97 -0
- aip_agents/schema/step_limit.py +108 -0
- aip_agents/schema/storage.py +40 -0
- aip_agents/sentry/__init__.py +11 -0
- aip_agents/sentry/sentry.py +151 -0
- aip_agents/storage/__init__.py +41 -0
- aip_agents/storage/base.py +85 -0
- aip_agents/storage/clients/__init__.py +12 -0
- aip_agents/storage/clients/minio_client.py +318 -0
- aip_agents/storage/config.py +62 -0
- aip_agents/storage/providers/__init__.py +15 -0
- aip_agents/storage/providers/base.py +106 -0
- aip_agents/storage/providers/memory.py +114 -0
- aip_agents/storage/providers/object_storage.py +214 -0
- aip_agents/tools/__init__.py +33 -0
- aip_agents/tools/bosa_tools.py +105 -0
- aip_agents/tools/browser_use/__init__.py +82 -0
- aip_agents/tools/browser_use/action_parser.py +103 -0
- aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
- aip_agents/tools/browser_use/llm_config.py +120 -0
- aip_agents/tools/browser_use/minio_storage.py +198 -0
- aip_agents/tools/browser_use/schemas.py +119 -0
- aip_agents/tools/browser_use/session.py +76 -0
- aip_agents/tools/browser_use/session_errors.py +132 -0
- aip_agents/tools/browser_use/steel_session_recording.py +317 -0
- aip_agents/tools/browser_use/streaming.py +813 -0
- aip_agents/tools/browser_use/structured_data_parser.py +257 -0
- aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
- aip_agents/tools/browser_use/types.py +78 -0
- aip_agents/tools/code_sandbox/__init__.py +26 -0
- aip_agents/tools/code_sandbox/constant.py +13 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +257 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
- aip_agents/tools/constants.py +165 -0
- aip_agents/tools/document_loader/__init__.py +44 -0
- aip_agents/tools/document_loader/base_reader.py +302 -0
- aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
- aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
- aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
- aip_agents/tools/document_loader/pdf_splitter.py +169 -0
- aip_agents/tools/gl_connector/__init__.py +5 -0
- aip_agents/tools/gl_connector/tool.py +351 -0
- aip_agents/tools/memory_search/__init__.py +22 -0
- aip_agents/tools/memory_search/base.py +200 -0
- aip_agents/tools/memory_search/mem0.py +258 -0
- aip_agents/tools/memory_search/schema.py +48 -0
- aip_agents/tools/memory_search_tool.py +26 -0
- aip_agents/tools/time_tool.py +117 -0
- aip_agents/tools/tool_config_injector.py +300 -0
- aip_agents/tools/web_search/__init__.py +15 -0
- aip_agents/tools/web_search/serper_tool.py +187 -0
- aip_agents/types/__init__.py +70 -0
- aip_agents/types/a2a_events.py +13 -0
- aip_agents/utils/__init__.py +79 -0
- aip_agents/utils/a2a_connector.py +1757 -0
- aip_agents/utils/artifact_helpers.py +502 -0
- aip_agents/utils/constants.py +22 -0
- aip_agents/utils/datetime/__init__.py +34 -0
- aip_agents/utils/datetime/normalization.py +231 -0
- aip_agents/utils/datetime/timezone.py +206 -0
- aip_agents/utils/env_loader.py +27 -0
- aip_agents/utils/event_handler_registry.py +58 -0
- aip_agents/utils/file_prompt_utils.py +176 -0
- aip_agents/utils/final_response_builder.py +211 -0
- aip_agents/utils/formatter_llm_client.py +231 -0
- aip_agents/utils/langgraph/__init__.py +19 -0
- aip_agents/utils/langgraph/converter.py +128 -0
- aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
- aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
- aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
- aip_agents/utils/langgraph/tool_output_management.py +967 -0
- aip_agents/utils/logger.py +195 -0
- aip_agents/utils/metadata/__init__.py +27 -0
- aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
- aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
- aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
- aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
- aip_agents/utils/metadata/activity_narrative/context.py +49 -0
- aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
- aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
- aip_agents/utils/metadata/schemas/__init__.py +16 -0
- aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
- aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
- aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
- aip_agents/utils/metadata_helper.py +358 -0
- aip_agents/utils/name_preprocessor/__init__.py +17 -0
- aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
- aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
- aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
- aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
- aip_agents/utils/pii/__init__.py +25 -0
- aip_agents/utils/pii/pii_handler.py +397 -0
- aip_agents/utils/pii/pii_helper.py +207 -0
- aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
- aip_agents/utils/reference_helper.py +273 -0
- aip_agents/utils/sse_chunk_transformer.py +831 -0
- aip_agents/utils/step_limit_manager.py +265 -0
- aip_agents/utils/token_usage_helper.py +156 -0
- aip_agents_binary-0.5.20.dist-info/METADATA +681 -0
- aip_agents_binary-0.5.20.dist-info/RECORD +280 -0
- aip_agents_binary-0.5.20.dist-info/WHEEL +5 -0
- aip_agents_binary-0.5.20.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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}"
|