aip-agents-binary 0.0.0__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 +66 -0
- aip_agents/a2a/__init__.py +19 -0
- aip_agents/a2a/server/__init__.py +10 -0
- aip_agents/a2a/server/base_executor.py +1125 -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/a2ui/__init__.py +26 -0
- aip_agents/a2ui/catalog_provider.py +109 -0
- aip_agents/a2ui/examples/0.8/booking_form.json +1 -0
- aip_agents/a2ui/examples/0.8/confirmation.json +1 -0
- aip_agents/a2ui/examples/0.8/single_column_list.json +1 -0
- aip_agents/a2ui/examples/0.9/booking_form.json +1 -0
- aip_agents/a2ui/examples/0.9/confirmation.json +1 -0
- aip_agents/a2ui/examples/0.9/single_column_list.json +1 -0
- aip_agents/a2ui/middleware.py +754 -0
- aip_agents/a2ui/models.py +153 -0
- aip_agents/a2ui/public_url.py +138 -0
- aip_agents/a2ui/stream_filter.py +67 -0
- aip_agents/agent/__init__.py +72 -0
- aip_agents/agent/base_agent.py +1437 -0
- aip_agents/agent/base_langgraph_agent.py +4048 -0
- aip_agents/agent/google_adk_agent.py +932 -0
- aip_agents/agent/google_adk_constants.py +11 -0
- aip_agents/agent/graph_runtime.py +185 -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_react_agent.py +3741 -0
- aip_agents/agent/model_invocation_adapter.py +346 -0
- aip_agents/agent/system_instruction_context.py +34 -0
- aip_agents/agent/tool_execution_engine.py +587 -0
- aip_agents/audio_interface/__init__.py +30 -0
- aip_agents/audio_interface/audio_agent_adapter.py +254 -0
- aip_agents/audio_interface/config.py +146 -0
- aip_agents/audio_interface/errors.py +15 -0
- aip_agents/audio_interface/livekit_audio_session.py +504 -0
- aip_agents/audio_interface/livekit_realtime_audio_session.py +142 -0
- aip_agents/audio_interface/session_factory.py +40 -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/common/__init__.py +5 -0
- aip_agents/common/callable_introspection.py +17 -0
- aip_agents/constants.py +23 -0
- aip_agents/context/__init__.py +5 -0
- aip_agents/context/budget.py +237 -0
- aip_agents/context/compaction.py +339 -0
- aip_agents/context/metadata.py +169 -0
- aip_agents/context/offload.py +449 -0
- aip_agents/context/preparation.py +196 -0
- aip_agents/context/prompt.py +61 -0
- aip_agents/context/runtime.py +149 -0
- aip_agents/context/session_recall.py +69 -0
- aip_agents/context/state.py +48 -0
- aip_agents/context/utils.py +44 -0
- aip_agents/credentials/manager.py +132 -0
- aip_agents/examples/__init__.py +5 -0
- aip_agents/examples/audio_livekit_session.py +116 -0
- aip_agents/examples/compare_streaming_client.py +783 -0
- aip_agents/examples/compare_streaming_server.py +141 -0
- aip_agents/examples/gl_connectors_smoke.py +148 -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 +78 -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 +104 -0
- aip_agents/examples/hello_world_a2a_langchain_server.py +78 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +77 -0
- aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
- aip_agents/examples/hello_world_a2a_langflow_server.py +80 -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 +91 -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 +83 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +78 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +131 -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 +250 -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 +79 -0
- aip_agents/examples/hello_world_filesystem.py +162 -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_gl_connector_twitter.py +44 -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_skills.py +62 -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_multi_agent_tool_output_client.py +57 -0
- aip_agents/examples/hello_world_multi_agent_tool_output_server.py +131 -0
- aip_agents/examples/hello_world_pii_logger.py +21 -0
- aip_agents/examples/hello_world_ptc.py +49 -0
- aip_agents/examples/hello_world_ptc_custom_tools.py +83 -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 +100 -0
- aip_agents/examples/hello_world_tool_output_client.py +55 -0
- aip_agents/examples/hello_world_tool_output_server.py +113 -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 +125 -0
- aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
- aip_agents/examples/pii_demo_multi_agent_server.py +246 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.py +87 -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/multiply_tool.py +43 -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/guardrails/__init__.py +83 -0
- aip_agents/guardrails/engines/__init__.py +69 -0
- aip_agents/guardrails/engines/base.py +90 -0
- aip_agents/guardrails/engines/nemo.py +101 -0
- aip_agents/guardrails/engines/phrase_matcher.py +113 -0
- aip_agents/guardrails/exceptions.py +39 -0
- aip_agents/guardrails/manager.py +163 -0
- aip_agents/guardrails/middleware.py +199 -0
- aip_agents/guardrails/schemas.py +63 -0
- aip_agents/guardrails/utils.py +45 -0
- aip_agents/integration/__init__.py +43 -0
- aip_agents/integration/agent.py +58 -0
- aip_agents/integration/guardrails.py +52 -0
- aip_agents/integration/hitl.py +71 -0
- aip_agents/integration/ptc.py +95 -0
- aip_agents/integration/skills.py +37 -0
- aip_agents/integration/storage.py +81 -0
- aip_agents/integration/version.py +103 -0
- aip_agents/mcp/__init__.py +1 -0
- aip_agents/mcp/client/__init__.py +60 -0
- aip_agents/mcp/client/base_mcp_client.py +377 -0
- aip_agents/mcp/client/connection_manager.py +231 -0
- aip_agents/mcp/client/errors.py +119 -0
- aip_agents/mcp/client/google_adk/__init__.py +45 -0
- aip_agents/mcp/client/google_adk/client.py +383 -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 +704 -0
- aip_agents/mcp/client/session_pool.py +358 -0
- aip_agents/mcp/client/transports.py +288 -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 +815 -0
- aip_agents/memory/adapters/mem0.py +286 -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 +65 -0
- aip_agents/middleware/backends/__init__.py +72 -0
- aip_agents/middleware/backends/in_memory.py +578 -0
- aip_agents/middleware/backends/local_disk.py +1203 -0
- aip_agents/middleware/backends/protocol.py +353 -0
- aip_agents/middleware/backends/sandbox.py +1431 -0
- aip_agents/middleware/backends/sandbox_files.py +113 -0
- aip_agents/middleware/backends/utils.py +293 -0
- aip_agents/middleware/base.py +239 -0
- aip_agents/middleware/filesystem.py +690 -0
- aip_agents/middleware/manager.py +291 -0
- aip_agents/middleware/memory.py +532 -0
- aip_agents/middleware/pii_middleware.py +192 -0
- aip_agents/middleware/schema.py +19 -0
- aip_agents/middleware/skills.py +872 -0
- aip_agents/middleware/task_delegation.py +1656 -0
- aip_agents/middleware/todolist.py +276 -0
- aip_agents/middleware/tools/__init__.py +23 -0
- aip_agents/middleware/tools/edit_file.py +70 -0
- aip_agents/middleware/tools/execute.py +428 -0
- aip_agents/middleware/tools/grep_file.py +79 -0
- aip_agents/middleware/tools/ls.py +71 -0
- aip_agents/middleware/tools/read_file.py +239 -0
- aip_agents/middleware/tools/skill_load.py +193 -0
- aip_agents/middleware/tools/skill_resource.py +338 -0
- aip_agents/middleware/tools/write_file.py +114 -0
- aip_agents/middleware/vision.py +322 -0
- aip_agents/ptc/__init__.py +87 -0
- aip_agents/ptc/custom_tools.py +511 -0
- aip_agents/ptc/custom_tools_payload.py +499 -0
- aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
- aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +24 -0
- aip_agents/ptc/custom_tools_templates/custom_init.py.template +26 -0
- aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +149 -0
- aip_agents/ptc/custom_tools_templates/custom_registry.py.template +112 -0
- aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
- aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +87 -0
- aip_agents/ptc/doc_gen.py +122 -0
- aip_agents/ptc/exceptions.py +57 -0
- aip_agents/ptc/executor.py +627 -0
- aip_agents/ptc/mcp/__init__.py +45 -0
- aip_agents/ptc/mcp/sandbox_bridge.py +679 -0
- aip_agents/ptc/mcp/templates/__init__.py +1 -0
- aip_agents/ptc/mcp/templates/mcp_client.py.template +240 -0
- aip_agents/ptc/naming.py +430 -0
- aip_agents/ptc/payload.py +26 -0
- aip_agents/ptc/prompt_builder.py +674 -0
- aip_agents/ptc/ptc_helper.py +16 -0
- aip_agents/ptc/sandbox_bridge.py +297 -0
- aip_agents/ptc/template_utils.py +33 -0
- aip_agents/ptc/templates/__init__.py +1 -0
- aip_agents/ptc/templates/ptc_helper.py.template +134 -0
- aip_agents/ptc/tool_def_helpers.py +101 -0
- aip_agents/ptc/tool_enrichment.py +163 -0
- aip_agents/resilience/__init__.py +10 -0
- aip_agents/resilience/base.py +46 -0
- aip_agents/resilience/tool.py +1661 -0
- aip_agents/sandbox/__init__.py +62 -0
- aip_agents/sandbox/defaults.py +220 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1-datasaur.Dockerfile +120 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1.Dockerfile +100 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2-datasaur.Dockerfile +143 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2.Dockerfile +119 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3-datasaur.Dockerfile +173 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3.Dockerfile +130 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.Dockerfile +164 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.patch_js_context.py +41 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.requirements.txt +29 -0
- aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2.Dockerfile +87 -0
- aip_agents/sandbox/e2b_runtime.py +27 -0
- aip_agents/sandbox/image_registry.py +98 -0
- aip_agents/sandbox/providers/__init__.py +76 -0
- aip_agents/sandbox/providers/base.py +392 -0
- aip_agents/sandbox/providers/bedrock.py +28 -0
- aip_agents/sandbox/providers/e2b.py +230 -0
- aip_agents/sandbox/providers/opensandbox.py +163 -0
- aip_agents/sandbox/sandbox_runtime.py +967 -0
- aip_agents/sandbox/template_builder.py +240 -0
- aip_agents/sandbox/types.py +141 -0
- aip_agents/sandbox/validation.py +50 -0
- aip_agents/schema/__init__.py +106 -0
- aip_agents/schema/a2a.py +58 -0
- aip_agents/schema/agent.py +117 -0
- aip_agents/schema/artifacts.py +75 -0
- aip_agents/schema/attachments.py +425 -0
- aip_agents/schema/hitl.py +157 -0
- aip_agents/schema/langgraph.py +47 -0
- aip_agents/schema/model_id.py +103 -0
- aip_agents/schema/model_usage.py +50 -0
- aip_agents/schema/runtime_metadata.py +78 -0
- aip_agents/schema/step_limit.py +108 -0
- aip_agents/schema/storage.py +40 -0
- aip_agents/schema/tool_payloads.py +68 -0
- aip_agents/sentry/__init__.py +11 -0
- aip_agents/sentry/sentry.py +267 -0
- aip_agents/skills/__init__.py +30 -0
- aip_agents/skills/errors.py +110 -0
- aip_agents/skills/installer.py +479 -0
- aip_agents/skills/models.py +769 -0
- aip_agents/skills/stager.py +224 -0
- aip_agents/skills/validation.py +53 -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 +65 -0
- aip_agents/tools/agent_schedule_tool.py +392 -0
- aip_agents/tools/browser_use/__init__.py +83 -0
- aip_agents/tools/browser_use/action_parser.py +104 -0
- aip_agents/tools/browser_use/browser_use_tool.py +1118 -0
- aip_agents/tools/browser_use/llm_config.py +120 -0
- aip_agents/tools/browser_use/minio_storage.py +203 -0
- aip_agents/tools/browser_use/schemas.py +121 -0
- aip_agents/tools/browser_use/session.py +13 -0
- aip_agents/tools/browser_use/session_errors.py +132 -0
- aip_agents/tools/browser_use/steel_session_recording.py +321 -0
- aip_agents/tools/browser_use/streaming.py +263 -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 +37 -0
- aip_agents/tools/code_sandbox/__init__.py +48 -0
- aip_agents/tools/code_sandbox/code_sandbox_tool.py +350 -0
- aip_agents/tools/code_sandbox/constant.py +13 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +346 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +65 -0
- aip_agents/tools/computer_use/__init__.py +75 -0
- aip_agents/tools/computer_use/computer_use_tool.py +910 -0
- aip_agents/tools/computer_use/schemas.py +78 -0
- aip_agents/tools/computer_use/streaming.py +198 -0
- aip_agents/tools/computer_use/types.py +32 -0
- aip_agents/tools/constants.py +177 -0
- aip_agents/tools/date_range_tool.py +554 -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/execute_ptc_code.py +582 -0
- aip_agents/tools/gl_connector/__init__.py +5 -0
- aip_agents/tools/gl_connector/tool.py +518 -0
- aip_agents/tools/gl_connector_tools.py +170 -0
- aip_agents/tools/memory_search/__init__.py +29 -0
- aip_agents/tools/memory_search/base.py +213 -0
- aip_agents/tools/memory_search/mem0.py +424 -0
- aip_agents/tools/memory_search/schema.py +81 -0
- aip_agents/tools/memory_search_tool.py +34 -0
- aip_agents/tools/streaming_common.py +577 -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 +73 -0
- aip_agents/types/a2a_events.py +13 -0
- aip_agents/utils/__init__.py +91 -0
- aip_agents/utils/a2a_connector.py +1758 -0
- aip_agents/utils/artifact_helpers.py +542 -0
- aip_agents/utils/artifacts/__init__.py +20 -0
- aip_agents/utils/artifacts/tracker.py +367 -0
- aip_agents/utils/attachment_helpers.py +178 -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 +276 -0
- aip_agents/utils/file_watchers/__init__.py +33 -0
- aip_agents/utils/file_watchers/base.py +46 -0
- aip_agents/utils/file_watchers/e2b.py +128 -0
- aip_agents/utils/file_watchers/factory.py +39 -0
- aip_agents/utils/file_watchers/ls_diff.py +88 -0
- aip_agents/utils/file_watchers/sandbox.py +66 -0
- aip_agents/utils/file_watchers/watchdog.py +97 -0
- aip_agents/utils/final_response_builder.py +211 -0
- aip_agents/utils/formatter_llm_client.py +258 -0
- aip_agents/utils/langgraph/__init__.py +19 -0
- aip_agents/utils/langgraph/converter.py +399 -0
- aip_agents/utils/langgraph/delegation_context.py +22 -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 +1329 -0
- aip_agents/utils/langgraph/tool_output_management.py +1047 -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 +822 -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 +363 -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 +29 -0
- aip_agents/utils/pii/pii_handler.py +555 -0
- aip_agents/utils/pii/pii_helper.py +225 -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 +836 -0
- aip_agents/utils/step_limit_manager.py +265 -0
- aip_agents/utils/token_usage_helper.py +531 -0
- aip_agents_binary-0.0.0.dist-info/METADATA +825 -0
- aip_agents_binary-0.0.0.dist-info/RECORD +447 -0
- aip_agents_binary-0.0.0.dist-info/WHEEL +5 -0
- aip_agents_binary-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
"""Execute tool for running shell commands with artifact tracking.
|
|
2
|
+
|
|
3
|
+
This module provides the ExecuteTool class that wraps backend command execution
|
|
4
|
+
with a LangChain tool interface, including timeout handling, output formatting,
|
|
5
|
+
and artifact tracking for files created during execution.
|
|
6
|
+
|
|
7
|
+
Authors:
|
|
8
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
from collections.abc import AsyncIterator
|
|
15
|
+
from contextlib import AsyncExitStack, asynccontextmanager
|
|
16
|
+
from functools import lru_cache
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Annotated, Any, cast
|
|
19
|
+
|
|
20
|
+
from langchain_core.tools import BaseTool
|
|
21
|
+
from langgraph.types import Command
|
|
22
|
+
from pydantic import BaseModel, Field, SkipValidation
|
|
23
|
+
|
|
24
|
+
from aip_agents.common.callable_introspection import callable_accepts_keyword
|
|
25
|
+
from aip_agents.middleware.backends.protocol import BackendProtocol, ExecuteResult
|
|
26
|
+
from aip_agents.middleware.schema import MiddlewareToolConfig
|
|
27
|
+
from aip_agents.utils.artifacts import ArtifactTracker
|
|
28
|
+
from aip_agents.utils.file_watchers import FileWatcherStrategy, SandboxArtifactWatcher, create_local_strategy
|
|
29
|
+
from aip_agents.utils.logger import get_logger
|
|
30
|
+
|
|
31
|
+
logger = get_logger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ExecuteInput(BaseModel):
|
|
35
|
+
"""Input schema for the execute tool.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
command: The shell command to execute.
|
|
39
|
+
timeout: Timeout in seconds (optional, defaults to 30).
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
command: str = Field(description="Shell command to execute")
|
|
43
|
+
timeout: int = Field(default=30, description="Timeout in seconds", ge=1)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ExecuteTool(BaseTool):
|
|
47
|
+
"""Tool for executing shell commands via backend with artifact tracking.
|
|
48
|
+
|
|
49
|
+
This tool wraps backend command execution with proper timeout handling,
|
|
50
|
+
output formatting, and artifact tracking for files created during execution.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
name: Tool name ('execute').
|
|
54
|
+
description: Tool description for LLM.
|
|
55
|
+
args_schema: Input schema class.
|
|
56
|
+
backend: BackendProtocol instance for execution.
|
|
57
|
+
max_execute_timeout: Maximum allowed timeout in seconds.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
name: str = "execute"
|
|
61
|
+
description: str = (
|
|
62
|
+
"Execute a shell command. Returns stdout/stderr with exit code. "
|
|
63
|
+
"Use timeout parameter for long-running commands. "
|
|
64
|
+
"Creates artifacts for any files generated during execution."
|
|
65
|
+
)
|
|
66
|
+
args_schema: type[BaseModel] = ExecuteInput # type: ignore[assignment]
|
|
67
|
+
tool_config_schema: type[BaseModel] = MiddlewareToolConfig
|
|
68
|
+
backend: Annotated[BackendProtocol, SkipValidation()] # BackendProtocol type with validation skipped for Protocol
|
|
69
|
+
max_execute_timeout: int = 300
|
|
70
|
+
|
|
71
|
+
async def _arun(self, command: str, timeout: int = 30) -> Command | str:
|
|
72
|
+
"""Execute a command with artifact tracking (async).
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
command: Shell command to execute.
|
|
76
|
+
timeout: Timeout in seconds.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Command with artifacts if files created,
|
|
80
|
+
otherwise formatted result string.
|
|
81
|
+
"""
|
|
82
|
+
timeout = self._clamp_timeout(timeout)
|
|
83
|
+
|
|
84
|
+
if not self._backend_supports_execution():
|
|
85
|
+
return (
|
|
86
|
+
"Error: Execution not available. This agent's backend "
|
|
87
|
+
"does not support command execution. "
|
|
88
|
+
"To use the execute tool, provide a backend that implements "
|
|
89
|
+
"the execute() method."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
backend_class = type(self.backend)
|
|
93
|
+
execute_fn = getattr(self.backend, "execute", None)
|
|
94
|
+
supports_timeout = ExecuteTool._execute_accepts_timeout(backend_class) or callable_accepts_keyword(
|
|
95
|
+
execute_fn, "timeout"
|
|
96
|
+
)
|
|
97
|
+
if not supports_timeout:
|
|
98
|
+
return (
|
|
99
|
+
"Error: This backend does not support per-command timeout "
|
|
100
|
+
"overrides. Update your backend package or omit the timeout parameter."
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
return await self._arun_with_tracking(command, timeout)
|
|
105
|
+
except NotImplementedError as e:
|
|
106
|
+
return f"Error: Execution not available. {e}"
|
|
107
|
+
except ValueError as e:
|
|
108
|
+
return f"Error: Invalid parameter. {e}"
|
|
109
|
+
except Exception as e:
|
|
110
|
+
return f"Error: Execution failed. {str(e)}"
|
|
111
|
+
|
|
112
|
+
def _run(self, command: str, timeout: int = 30) -> Command | str:
|
|
113
|
+
"""Execute a command synchronously with artifact tracking.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
command: Shell command to execute.
|
|
117
|
+
timeout: Timeout in seconds.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Command with artifacts (if files created) or error message string.
|
|
121
|
+
"""
|
|
122
|
+
backend = cast(Any, self.backend)
|
|
123
|
+
run_sync = getattr(backend, "_run_sync", None)
|
|
124
|
+
if callable(run_sync):
|
|
125
|
+
return run_sync(self._arun(command, timeout=timeout))
|
|
126
|
+
return asyncio.run(self._arun(command, timeout=timeout))
|
|
127
|
+
|
|
128
|
+
def _clamp_timeout(self, timeout: int) -> int:
|
|
129
|
+
"""Clamp timeout to valid range.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
timeout: Requested timeout in seconds.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Timeout clamped between 1 and max_execute_timeout.
|
|
136
|
+
"""
|
|
137
|
+
return max(1, min(timeout, self.max_execute_timeout))
|
|
138
|
+
|
|
139
|
+
def _backend_supports_execution(self) -> bool:
|
|
140
|
+
"""Check if backend supports execution.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if backend has callable execute method.
|
|
144
|
+
"""
|
|
145
|
+
return hasattr(self.backend, "execute") and callable(getattr(self.backend, "execute"))
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
@lru_cache(maxsize=128)
|
|
149
|
+
def _execute_accepts_timeout(backend_class: type) -> bool:
|
|
150
|
+
"""Check if backend class execute accepts timeout parameter."""
|
|
151
|
+
execute_fn = getattr(backend_class, "execute", None)
|
|
152
|
+
return callable_accepts_keyword(execute_fn, "timeout")
|
|
153
|
+
|
|
154
|
+
async def _arun_with_tracking(self, command: str, timeout: int) -> Command:
|
|
155
|
+
"""Execute with artifact tracking using context manager.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
command: Shell command to execute.
|
|
159
|
+
timeout: Timeout in seconds.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Command with execution result and artifacts.
|
|
163
|
+
"""
|
|
164
|
+
# Initialize the runtime first so a lazily-resolved base_dir (e.g. the
|
|
165
|
+
# sandbox backend's $HOME/workspace resolution) is settled before we read
|
|
166
|
+
# it; otherwise the tracker would watch the stale default directory.
|
|
167
|
+
if hasattr(self.backend, "ensure_runtime"):
|
|
168
|
+
await self.backend.ensure_runtime()
|
|
169
|
+
|
|
170
|
+
# Get base directory from backend
|
|
171
|
+
base_dir = self._get_backend_base_dir()
|
|
172
|
+
if not base_dir:
|
|
173
|
+
# Fallback to execute without tracking
|
|
174
|
+
result = await self._aexecute_backend(command, timeout)
|
|
175
|
+
return self._format_result_as_command(result, [])
|
|
176
|
+
|
|
177
|
+
# Create watcher strategy
|
|
178
|
+
strategy = await self._create_watcher_strategy()
|
|
179
|
+
|
|
180
|
+
async with self._tracker_with_fallback(base_dir, strategy) as tracker:
|
|
181
|
+
return await self._execute_with_tracker(command, timeout, tracker)
|
|
182
|
+
|
|
183
|
+
@asynccontextmanager
|
|
184
|
+
async def _tracker_with_fallback(
|
|
185
|
+
self, base_dir: str, strategy: FileWatcherStrategy
|
|
186
|
+
) -> AsyncIterator[ArtifactTracker]:
|
|
187
|
+
"""Context manager that yields an ArtifactTracker using the given strategy.
|
|
188
|
+
|
|
189
|
+
Falls back to a non-watchdog strategy if the given strategy fails to initialize
|
|
190
|
+
(e.g. due to missing dependencies or OS limitations).
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
base_dir: Base directory to watch for artifacts.
|
|
194
|
+
strategy: Primary file watcher strategy to use.
|
|
195
|
+
|
|
196
|
+
Yields:
|
|
197
|
+
An initialized ArtifactTracker instance.
|
|
198
|
+
"""
|
|
199
|
+
stack = AsyncExitStack()
|
|
200
|
+
tracker = self._make_tracker(base_dir, strategy)
|
|
201
|
+
try:
|
|
202
|
+
active_tracker = await stack.enter_async_context(tracker)
|
|
203
|
+
except (ImportError, OSError) as exc:
|
|
204
|
+
await stack.aclose()
|
|
205
|
+
logger.warning(
|
|
206
|
+
"Watchdog watcher unavailable (%s), falling back to LsDiffWatcher",
|
|
207
|
+
exc,
|
|
208
|
+
)
|
|
209
|
+
file_lister = self._get_file_lister()
|
|
210
|
+
fallback = create_local_strategy(file_lister, use_watchdog=False)
|
|
211
|
+
fallback_tracker = self._make_tracker(base_dir, fallback)
|
|
212
|
+
stack = AsyncExitStack()
|
|
213
|
+
active_tracker = await stack.enter_async_context(fallback_tracker)
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
yield active_tracker
|
|
217
|
+
finally:
|
|
218
|
+
await stack.aclose()
|
|
219
|
+
|
|
220
|
+
def _make_tracker(self, base_dir: str, strategy: FileWatcherStrategy) -> ArtifactTracker:
|
|
221
|
+
"""Helper to create an ArtifactTracker with the given strategy."""
|
|
222
|
+
return ArtifactTracker(
|
|
223
|
+
base_dir=base_dir,
|
|
224
|
+
file_reader=self._read_file_bytes,
|
|
225
|
+
watcher_strategy=strategy,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
async def _execute_with_tracker(self, command: str, timeout: int, tracker: ArtifactTracker) -> Command:
|
|
229
|
+
"""Execute command and track artifacts using the given tracker.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
command: Shell command to execute.
|
|
233
|
+
timeout: Timeout in seconds.
|
|
234
|
+
tracker: Initialized ArtifactTracker instance.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Command with execution result and artifacts.
|
|
238
|
+
"""
|
|
239
|
+
result = await self._aexecute_backend(command, timeout)
|
|
240
|
+
created_files = await tracker.collect()
|
|
241
|
+
|
|
242
|
+
if created_files:
|
|
243
|
+
file_cache = await self._aread_file_cache(created_files)
|
|
244
|
+
tracker._file_reader = lambda p, _c=file_cache: _c[p] # type: ignore[method-assign]
|
|
245
|
+
|
|
246
|
+
artifacts, errors = tracker.build_artifacts(created_files)
|
|
247
|
+
return self._format_result_as_command(result, artifacts, errors)
|
|
248
|
+
|
|
249
|
+
def _read_file_bytes(self, path: str) -> bytes:
|
|
250
|
+
"""Read file as bytes using backend (synchronous fallback).
|
|
251
|
+
|
|
252
|
+
Used as the initial ``file_reader`` passed to ``ArtifactTracker``. For
|
|
253
|
+
backends that only expose an async API (e.g. ``SandboxBackend``), the
|
|
254
|
+
caller should replace ``tracker._file_reader`` with a cache-backed
|
|
255
|
+
synchronous lambda after pre-reading the files asynchronously via
|
|
256
|
+
:meth:`_aread_file_cache`. That avoids running async E2B client calls
|
|
257
|
+
from a sync callback that runs in a different event loop.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
path: File path to read.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
File content as bytes.
|
|
264
|
+
|
|
265
|
+
Raises:
|
|
266
|
+
FileNotFoundError: If file not found.
|
|
267
|
+
PermissionError: If file not readable.
|
|
268
|
+
IsADirectoryError: If path is a directory.
|
|
269
|
+
"""
|
|
270
|
+
backend = cast(Any, self.backend)
|
|
271
|
+
|
|
272
|
+
if hasattr(backend, "read_bytes") and callable(getattr(backend, "read_bytes")):
|
|
273
|
+
return backend.read_bytes(path)
|
|
274
|
+
|
|
275
|
+
content = backend.read(path)
|
|
276
|
+
return content.encode("utf-8")
|
|
277
|
+
|
|
278
|
+
async def _aread_file_bytes(self, path: str) -> bytes:
|
|
279
|
+
"""Read file as bytes using the async backend API.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
path: File path to read.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
File content as bytes.
|
|
286
|
+
"""
|
|
287
|
+
backend = cast(Any, self.backend)
|
|
288
|
+
|
|
289
|
+
if hasattr(backend, "aread_bytes") and callable(getattr(backend, "aread_bytes")):
|
|
290
|
+
return await backend.aread_bytes(path)
|
|
291
|
+
|
|
292
|
+
if hasattr(backend, "read_bytes") and callable(getattr(backend, "read_bytes")):
|
|
293
|
+
return await asyncio.to_thread(backend.read_bytes, path)
|
|
294
|
+
|
|
295
|
+
content = await asyncio.to_thread(backend.read, path)
|
|
296
|
+
return content.encode("utf-8")
|
|
297
|
+
|
|
298
|
+
async def _aread_file_cache(self, paths: list[str]) -> dict[str, bytes]:
|
|
299
|
+
"""Pre-read multiple files asynchronously into an in-memory cache.
|
|
300
|
+
|
|
301
|
+
Files that cannot be read are omitted from the returned dict; the
|
|
302
|
+
caller's ``file_reader`` lambda will raise ``KeyError`` for those paths
|
|
303
|
+
which ``ArtifactTracker.build_artifacts`` catches and converts to error
|
|
304
|
+
entries.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
paths: Absolute file paths to read.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Mapping of path → raw bytes for every file that was read
|
|
311
|
+
successfully.
|
|
312
|
+
"""
|
|
313
|
+
cache: dict[str, bytes] = {}
|
|
314
|
+
for path in paths:
|
|
315
|
+
try:
|
|
316
|
+
cache[path] = await self._aread_file_bytes(path)
|
|
317
|
+
except Exception as exc:
|
|
318
|
+
logger.warning("Failed to read file %s: %s: %s", path, type(exc).__name__, exc)
|
|
319
|
+
return cache
|
|
320
|
+
|
|
321
|
+
async def _aexecute_backend(self, command: str, timeout: int) -> ExecuteResult:
|
|
322
|
+
"""Execute command using the backend's async API if available, otherwise run sync execute in a thread."""
|
|
323
|
+
backend = cast(Any, self.backend)
|
|
324
|
+
if hasattr(backend, "aexecute") and callable(getattr(backend, "aexecute")):
|
|
325
|
+
return await backend.aexecute(command, timeout=timeout)
|
|
326
|
+
|
|
327
|
+
execute_fn = getattr(backend, "execute")
|
|
328
|
+
return await asyncio.to_thread(execute_fn, command, timeout=timeout)
|
|
329
|
+
|
|
330
|
+
async def _create_watcher_strategy(self) -> FileWatcherStrategy:
|
|
331
|
+
"""Create and return the appropriate file watcher strategy based on the backend."""
|
|
332
|
+
try:
|
|
333
|
+
from aip_agents.middleware.backends.sandbox import SandboxBackend as sandbox_backend_type
|
|
334
|
+
except ImportError:
|
|
335
|
+
sandbox_backend_type = None
|
|
336
|
+
|
|
337
|
+
backend = cast(Any, self.backend)
|
|
338
|
+
if sandbox_backend_type is not None and isinstance(backend, sandbox_backend_type):
|
|
339
|
+
# Provider-neutral: watch via the runtime's execute_command (find -newer),
|
|
340
|
+
# so this works on any sandbox backend, not just E2B. _create_watcher_strategy
|
|
341
|
+
# runs before _aexecute_backend, so initialize the runtime first.
|
|
342
|
+
runtime = await backend._ensure_runtime()
|
|
343
|
+
return SandboxArtifactWatcher(runtime)
|
|
344
|
+
|
|
345
|
+
file_lister = self._get_file_lister()
|
|
346
|
+
return create_local_strategy(file_lister, use_watchdog=True)
|
|
347
|
+
|
|
348
|
+
def _format_result_as_command(
|
|
349
|
+
self,
|
|
350
|
+
result: ExecuteResult,
|
|
351
|
+
artifacts: list[dict[str, Any]],
|
|
352
|
+
errors: list[dict[str, str]] | None = None,
|
|
353
|
+
) -> Command:
|
|
354
|
+
"""Format execution result as Command with artifacts.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
result: ExecuteResult from backend.
|
|
358
|
+
artifacts: List of artifact dicts.
|
|
359
|
+
errors: List of artifact error dicts.
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
Command with result and artifacts.
|
|
363
|
+
"""
|
|
364
|
+
execution_summary = self._format_result(result)
|
|
365
|
+
|
|
366
|
+
# Build response
|
|
367
|
+
response_data: dict[str, Any] = {
|
|
368
|
+
"result": execution_summary,
|
|
369
|
+
"artifacts": artifacts,
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if errors:
|
|
373
|
+
response_data["artifact_errors"] = errors
|
|
374
|
+
|
|
375
|
+
# Return Command directly (not via helper to support empty artifacts)
|
|
376
|
+
return Command(update=response_data)
|
|
377
|
+
|
|
378
|
+
def _get_backend_base_dir(self) -> str | None:
|
|
379
|
+
"""Get base directory from backend for artifact tracking.
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
Base directory path or None if not available.
|
|
383
|
+
"""
|
|
384
|
+
# Try common attribute names
|
|
385
|
+
for attr in ["base_path", "base_directory", "base_dir", "working_dir"]:
|
|
386
|
+
if hasattr(self.backend, attr):
|
|
387
|
+
val = getattr(self.backend, attr)
|
|
388
|
+
if isinstance(val, str | Path):
|
|
389
|
+
return str(val)
|
|
390
|
+
return None
|
|
391
|
+
|
|
392
|
+
def _get_file_lister(self) -> Any:
|
|
393
|
+
"""Get file lister callable from backend.
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Callable that lists files in a directory.
|
|
397
|
+
|
|
398
|
+
Raises:
|
|
399
|
+
AttributeError: If backend does not have ls_info method.
|
|
400
|
+
"""
|
|
401
|
+
if hasattr(self.backend, "ls_info"):
|
|
402
|
+
return self.backend.ls_info
|
|
403
|
+
|
|
404
|
+
raise AttributeError("Backend does not support file listing. Please provide a backend with ls_info method.")
|
|
405
|
+
|
|
406
|
+
def _format_result(self, result: ExecuteResult) -> str:
|
|
407
|
+
"""Format execution result for LLM consumption.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
result: ExecuteResult from backend.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
Formatted output string with stdout, stderr, exit code.
|
|
414
|
+
"""
|
|
415
|
+
parts: list[str] = []
|
|
416
|
+
|
|
417
|
+
if result.stdout:
|
|
418
|
+
parts.append(f"STDOUT:\n{result.stdout}")
|
|
419
|
+
if result.stderr:
|
|
420
|
+
parts.append(f"STDERR:\n{result.stderr}")
|
|
421
|
+
|
|
422
|
+
status = "succeeded" if result.exit_code == 0 else "failed"
|
|
423
|
+
parts.append(f"\n[Command {status} with exit code {result.exit_code}]")
|
|
424
|
+
|
|
425
|
+
if result.truncated:
|
|
426
|
+
parts.append("\n[Output was truncated due to size limits]")
|
|
427
|
+
|
|
428
|
+
return "\n".join(parts)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Grep tool for agents.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
5
|
+
|
|
6
|
+
Notes:
|
|
7
|
+
TODO: After migrating to deepagents:
|
|
8
|
+
- Reuse from deepagents: BackendProtocol, GrepMatch, format_grep_matches()
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Annotated, Any
|
|
12
|
+
|
|
13
|
+
from langchain_core.tools import BaseTool
|
|
14
|
+
from pydantic import BaseModel, Field, SkipValidation
|
|
15
|
+
|
|
16
|
+
from aip_agents.middleware.backends.protocol import GrepMatch
|
|
17
|
+
from aip_agents.middleware.backends.utils import ensure_absolute_path, format_grep_matches
|
|
18
|
+
from aip_agents.middleware.schema import MiddlewareToolConfig
|
|
19
|
+
|
|
20
|
+
GREP_TOOL_DESCRIPTION = """Search for a text pattern across files.
|
|
21
|
+
|
|
22
|
+
Searches for literal text (not regex) and returns matching files or content based on output_mode.
|
|
23
|
+
Special characters like parentheses, brackets, pipes, etc. are treated as literal characters, not regex operators.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
- Search all files: `grep(pattern="TODO")`
|
|
27
|
+
- Search Python files only: `grep(pattern="import", glob="*.py")`
|
|
28
|
+
- Show matching lines: `grep(pattern="error", output_mode="content")`
|
|
29
|
+
- Search for code with special chars: `grep(pattern="def __init__(self):")`"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class GrepInput(BaseModel):
|
|
33
|
+
"""Input schema for grep tool."""
|
|
34
|
+
|
|
35
|
+
path: str = Field(description="Directory path to search")
|
|
36
|
+
pattern: str = Field(description="Text pattern to search for (literal string)")
|
|
37
|
+
max_matches: int = Field(default=50, description="Maximum number of matches to return")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class GrepTool(BaseTool):
|
|
41
|
+
"""Tool for searching file contents across directories."""
|
|
42
|
+
|
|
43
|
+
name: str = "grep"
|
|
44
|
+
description: str = GREP_TOOL_DESCRIPTION
|
|
45
|
+
args_schema: type[BaseModel] = GrepInput
|
|
46
|
+
tool_config_schema: type[BaseModel] = MiddlewareToolConfig
|
|
47
|
+
backend: Annotated[Any, SkipValidation()]
|
|
48
|
+
|
|
49
|
+
def _run(self, path: str, pattern: str, max_matches: int = 50) -> str:
|
|
50
|
+
"""Execute the grep operation.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
path (str): Directory path to search.
|
|
54
|
+
pattern (str): Text pattern to search for.
|
|
55
|
+
max_matches (int, optional): Maximum matches to return. Defaults to 50.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str: Search results or error message.
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
matches = self.backend.grep(pattern=pattern, path=path)
|
|
62
|
+
if len(matches) > max_matches:
|
|
63
|
+
matches = matches[:max_matches]
|
|
64
|
+
# Normalize paths to absolute so read_file receives consistent input
|
|
65
|
+
normalized = [
|
|
66
|
+
GrepMatch(
|
|
67
|
+
path=ensure_absolute_path(m.path),
|
|
68
|
+
line_number=m.line_number,
|
|
69
|
+
content=m.content,
|
|
70
|
+
)
|
|
71
|
+
for m in matches
|
|
72
|
+
]
|
|
73
|
+
return format_grep_matches(normalized, output_mode="content")
|
|
74
|
+
except FileNotFoundError:
|
|
75
|
+
return f"Error: Path not found: {path}"
|
|
76
|
+
except ValueError as e:
|
|
77
|
+
return f"Error: {str(e)}"
|
|
78
|
+
except Exception as e:
|
|
79
|
+
return f"Error searching: {str(e)}"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""List files tool for agents.
|
|
2
|
+
|
|
3
|
+
Authors:
|
|
4
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
5
|
+
|
|
6
|
+
TODO: When migrating to deepagents, replace this entire file with:
|
|
7
|
+
from deepagents.middleware.tools.ls import LsTool, LsInput, truncate_if_too_long
|
|
8
|
+
|
|
9
|
+
Note: deepagents LsTool uses ToolRuntime pattern instead of BaseTool.
|
|
10
|
+
We'll need to adapt our middleware to use deepagents' tool registration.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Annotated, Any
|
|
14
|
+
|
|
15
|
+
from langchain_core.tools import BaseTool
|
|
16
|
+
from pydantic import BaseModel, Field, SkipValidation
|
|
17
|
+
|
|
18
|
+
from aip_agents.middleware.backends.utils import (
|
|
19
|
+
ensure_absolute_path,
|
|
20
|
+
truncate_if_too_long,
|
|
21
|
+
)
|
|
22
|
+
from aip_agents.middleware.schema import MiddlewareToolConfig
|
|
23
|
+
|
|
24
|
+
LIST_FILES_TOOL_DESCRIPTION = """Lists all files in a directory.
|
|
25
|
+
|
|
26
|
+
This is useful for exploring the filesystem and finding the right file to read or edit.
|
|
27
|
+
You should almost ALWAYS use this tool before using the read_file tool."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LsInput(BaseModel):
|
|
31
|
+
"""Input schema for ls tool."""
|
|
32
|
+
|
|
33
|
+
path: str = Field(description="Absolute path to the directory to list. Must be absolute, not relative.")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LsTool(BaseTool):
|
|
37
|
+
"""Tool for listing directory contents.
|
|
38
|
+
|
|
39
|
+
Returns a Python list representation (as string) of absolute paths for files and directories.
|
|
40
|
+
Large results are automatically truncated with guidance.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
name: str = "ls"
|
|
44
|
+
description: str = LIST_FILES_TOOL_DESCRIPTION
|
|
45
|
+
args_schema: type[BaseModel] = LsInput
|
|
46
|
+
tool_config_schema: type[BaseModel] = MiddlewareToolConfig
|
|
47
|
+
backend: Annotated[Any, SkipValidation()]
|
|
48
|
+
|
|
49
|
+
def _run(self, path: str) -> str:
|
|
50
|
+
"""Execute the ls operation.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
path (str): Directory path to list.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
str: Python list representation (as string) of absolute paths,
|
|
57
|
+
or an error message if directory not found or path is not a directory.
|
|
58
|
+
Example: "['/workspace/file1.txt', '/workspace/file2.txt']"
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
entries = self.backend.ls_info(path)
|
|
62
|
+
# Extract full paths like deepagents does
|
|
63
|
+
paths = [ensure_absolute_path(str(entry["path"])) for entry in entries]
|
|
64
|
+
truncated = truncate_if_too_long(paths)
|
|
65
|
+
return str(truncated)
|
|
66
|
+
except FileNotFoundError:
|
|
67
|
+
return f"No directory found: {path}"
|
|
68
|
+
except NotADirectoryError:
|
|
69
|
+
return f"Not a directory: {path}"
|
|
70
|
+
except Exception as e:
|
|
71
|
+
return f"Error: {str(e)}"
|