aip-agents-binary 0.6.0__py3-none-any.whl → 0.6.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aip-agents-binary might be problematic. Click here for more details.
- aip_agents/agent/langgraph_react_agent.py +194 -2
- aip_agents/examples/hello_world_ptc.py +49 -0
- aip_agents/ptc/__init__.py +48 -0
- aip_agents/ptc/doc_gen.py +122 -0
- aip_agents/ptc/exceptions.py +39 -0
- aip_agents/ptc/executor.py +143 -0
- aip_agents/ptc/mcp/__init__.py +45 -0
- aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
- aip_agents/ptc/mcp/templates/__init__.py +1 -0
- aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
- aip_agents/ptc/naming.py +184 -0
- aip_agents/ptc/payload.py +26 -0
- aip_agents/ptc/prompt_builder.py +571 -0
- aip_agents/ptc/ptc_helper.py +16 -0
- aip_agents/ptc/sandbox_bridge.py +58 -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/sandbox/__init__.py +43 -0
- aip_agents/sandbox/defaults.py +9 -0
- aip_agents/sandbox/e2b_runtime.py +267 -0
- aip_agents/sandbox/template_builder.py +131 -0
- aip_agents/sandbox/types.py +24 -0
- aip_agents/sandbox/validation.py +50 -0
- aip_agents/tools/__init__.py +2 -0
- aip_agents/tools/execute_ptc_code.py +308 -0
- {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/METADATA +1 -1
- {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/RECORD +30 -282
- aip_agents/__init__.pyi +0 -19
- aip_agents/a2a/__init__.pyi +0 -3
- aip_agents/a2a/server/__init__.pyi +0 -4
- aip_agents/a2a/server/base_executor.pyi +0 -73
- aip_agents/a2a/server/google_adk_executor.pyi +0 -51
- aip_agents/a2a/server/langflow_executor.pyi +0 -43
- aip_agents/a2a/server/langgraph_executor.pyi +0 -47
- aip_agents/a2a/types.pyi +0 -132
- aip_agents/agent/__init__.pyi +0 -9
- aip_agents/agent/base_agent.pyi +0 -221
- aip_agents/agent/base_langgraph_agent.pyi +0 -233
- aip_agents/agent/google_adk_agent.pyi +0 -141
- aip_agents/agent/google_adk_constants.pyi +0 -3
- aip_agents/agent/hitl/__init__.pyi +0 -6
- aip_agents/agent/hitl/config.pyi +0 -15
- aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +0 -42
- aip_agents/agent/hitl/manager.pyi +0 -200
- aip_agents/agent/hitl/models.pyi +0 -3
- aip_agents/agent/hitl/prompt/__init__.pyi +0 -4
- aip_agents/agent/hitl/prompt/base.pyi +0 -24
- aip_agents/agent/hitl/prompt/deferred.pyi +0 -30
- aip_agents/agent/hitl/registry.pyi +0 -101
- aip_agents/agent/interface.pyi +0 -81
- aip_agents/agent/interfaces.pyi +0 -44
- aip_agents/agent/langflow_agent.pyi +0 -133
- aip_agents/agent/langgraph_memory_enhancer_agent.pyi +0 -49
- aip_agents/agent/langgraph_react_agent.pyi +0 -131
- aip_agents/agent/system_instruction_context.pyi +0 -13
- aip_agents/clients/__init__.pyi +0 -4
- aip_agents/clients/langflow/__init__.pyi +0 -4
- aip_agents/clients/langflow/client.pyi +0 -140
- aip_agents/clients/langflow/types.pyi +0 -7
- aip_agents/constants.pyi +0 -7
- aip_agents/examples/__init__.pyi +0 -0
- aip_agents/examples/compare_streaming_client.pyi +0 -48
- aip_agents/examples/compare_streaming_server.pyi +0 -18
- aip_agents/examples/demo_memory_recall.pyi +0 -58
- aip_agents/examples/hello_world_a2a_google_adk_client.pyi +0 -9
- aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +0 -9
- aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +0 -9
- aip_agents/examples/hello_world_a2a_google_adk_server.pyi +0 -15
- aip_agents/examples/hello_world_a2a_langchain_client.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +0 -15
- aip_agents/examples/hello_world_a2a_langchain_server.pyi +0 -15
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +0 -15
- aip_agents/examples/hello_world_a2a_langflow_client.pyi +0 -9
- aip_agents/examples/hello_world_a2a_langflow_server.pyi +0 -14
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +0 -16
- aip_agents/examples/hello_world_a2a_langgraph_client.pyi +0 -9
- aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +0 -9
- aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +0 -2
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +0 -9
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +0 -5
- aip_agents/examples/hello_world_a2a_langgraph_server.pyi +0 -14
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +0 -15
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +0 -15
- aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +0 -48
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +0 -48
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +0 -45
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +0 -5
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +0 -15
- aip_agents/examples/hello_world_google_adk.pyi +0 -5
- aip_agents/examples/hello_world_google_adk_mcp_http.pyi +0 -5
- aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +0 -5
- aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +0 -5
- aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +0 -5
- aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +0 -5
- aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +0 -5
- aip_agents/examples/hello_world_google_adk_stream.pyi +0 -5
- aip_agents/examples/hello_world_langchain.pyi +0 -5
- aip_agents/examples/hello_world_langchain_lm_invoker.pyi +0 -2
- aip_agents/examples/hello_world_langchain_mcp_http.pyi +0 -5
- aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +0 -16
- aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +0 -5
- aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +0 -18
- aip_agents/examples/hello_world_langchain_mcp_sse.pyi +0 -5
- aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +0 -5
- aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +0 -5
- aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +0 -5
- aip_agents/examples/hello_world_langchain_stream.pyi +0 -5
- aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +0 -5
- aip_agents/examples/hello_world_langflow_agent.pyi +0 -35
- aip_agents/examples/hello_world_langgraph.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_mcp_http.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_stream.pyi +0 -5
- aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +0 -5
- aip_agents/examples/hello_world_model_switch_cli.pyi +0 -30
- aip_agents/examples/hello_world_multi_agent_adk.pyi +0 -6
- aip_agents/examples/hello_world_multi_agent_langchain.pyi +0 -5
- aip_agents/examples/hello_world_multi_agent_langgraph.pyi +0 -5
- aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +0 -5
- aip_agents/examples/hello_world_pii_logger.pyi +0 -5
- aip_agents/examples/hello_world_sentry.pyi +0 -21
- aip_agents/examples/hello_world_step_limits.pyi +0 -17
- aip_agents/examples/hello_world_stock_a2a_server.pyi +0 -17
- aip_agents/examples/hello_world_tool_output_client.pyi +0 -5
- aip_agents/examples/hello_world_tool_output_server.pyi +0 -19
- aip_agents/examples/hitl_demo.pyi +0 -67
- aip_agents/examples/pii_demo_langgraph_client.pyi +0 -5
- aip_agents/examples/pii_demo_langgraph_server.pyi +0 -20
- aip_agents/examples/pii_demo_multi_agent_client.pyi +0 -5
- aip_agents/examples/pii_demo_multi_agent_server.pyi +0 -40
- aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +0 -5
- aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +0 -19
- aip_agents/examples/tools/__init__.pyi +0 -9
- aip_agents/examples/tools/adk_arithmetic_tools.pyi +0 -24
- aip_agents/examples/tools/adk_weather_tool.pyi +0 -18
- aip_agents/examples/tools/data_generator_tool.pyi +0 -15
- aip_agents/examples/tools/data_visualization_tool.pyi +0 -19
- aip_agents/examples/tools/image_artifact_tool.pyi +0 -26
- aip_agents/examples/tools/langchain_arithmetic_tools.pyi +0 -17
- aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +0 -20
- aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +0 -25
- aip_agents/examples/tools/langchain_weather_tool.pyi +0 -19
- aip_agents/examples/tools/langgraph_streaming_tool.pyi +0 -43
- aip_agents/examples/tools/mock_retrieval_tool.pyi +0 -13
- aip_agents/examples/tools/pii_demo_tools.pyi +0 -54
- aip_agents/examples/tools/random_chart_tool.pyi +0 -20
- aip_agents/examples/tools/serper_tool.pyi +0 -16
- aip_agents/examples/tools/stock_tools.pyi +0 -36
- aip_agents/examples/tools/table_generator_tool.pyi +0 -22
- aip_agents/examples/tools/time_tool.pyi +0 -15
- aip_agents/examples/tools/weather_forecast_tool.pyi +0 -14
- aip_agents/guardrails/__init__.pyi +0 -6
- aip_agents/guardrails/engines/__init__.pyi +0 -4
- aip_agents/guardrails/engines/base.pyi +0 -61
- aip_agents/guardrails/engines/nemo.pyi +0 -46
- aip_agents/guardrails/engines/phrase_matcher.pyi +0 -48
- aip_agents/guardrails/exceptions.pyi +0 -23
- aip_agents/guardrails/manager.pyi +0 -42
- aip_agents/guardrails/middleware.pyi +0 -87
- aip_agents/guardrails/schemas.pyi +0 -43
- aip_agents/guardrails/utils.pyi +0 -19
- aip_agents/mcp/__init__.pyi +0 -0
- aip_agents/mcp/client/__init__.pyi +0 -5
- aip_agents/mcp/client/base_mcp_client.pyi +0 -148
- aip_agents/mcp/client/connection_manager.pyi +0 -51
- aip_agents/mcp/client/google_adk/__init__.pyi +0 -3
- aip_agents/mcp/client/google_adk/client.pyi +0 -75
- aip_agents/mcp/client/langchain/__init__.pyi +0 -3
- aip_agents/mcp/client/langchain/client.pyi +0 -48
- aip_agents/mcp/client/persistent_session.pyi +0 -122
- aip_agents/mcp/client/session_pool.pyi +0 -101
- aip_agents/mcp/client/transports.pyi +0 -132
- aip_agents/mcp/utils/__init__.pyi +0 -0
- aip_agents/mcp/utils/config_validator.pyi +0 -82
- aip_agents/memory/__init__.pyi +0 -5
- aip_agents/memory/adapters/__init__.pyi +0 -4
- aip_agents/memory/adapters/base_adapter.pyi +0 -150
- aip_agents/memory/adapters/mem0.pyi +0 -22
- aip_agents/memory/base.pyi +0 -60
- aip_agents/memory/constants.pyi +0 -25
- aip_agents/memory/factory.pyi +0 -24
- aip_agents/memory/guidance.pyi +0 -3
- aip_agents/memory/simple_memory.pyi +0 -23
- aip_agents/middleware/__init__.pyi +0 -5
- aip_agents/middleware/base.pyi +0 -75
- aip_agents/middleware/manager.pyi +0 -84
- aip_agents/middleware/todolist.pyi +0 -125
- aip_agents/schema/__init__.pyi +0 -9
- aip_agents/schema/a2a.pyi +0 -40
- aip_agents/schema/agent.pyi +0 -65
- aip_agents/schema/hitl.pyi +0 -89
- aip_agents/schema/langgraph.pyi +0 -28
- aip_agents/schema/model_id.pyi +0 -54
- aip_agents/schema/step_limit.pyi +0 -63
- aip_agents/schema/storage.pyi +0 -21
- aip_agents/sentry/__init__.pyi +0 -3
- aip_agents/sentry/sentry.pyi +0 -48
- aip_agents/storage/__init__.pyi +0 -8
- aip_agents/storage/base.pyi +0 -58
- aip_agents/storage/clients/__init__.pyi +0 -3
- aip_agents/storage/clients/minio_client.pyi +0 -137
- aip_agents/storage/config.pyi +0 -29
- aip_agents/storage/providers/__init__.pyi +0 -5
- aip_agents/storage/providers/base.pyi +0 -88
- aip_agents/storage/providers/memory.pyi +0 -79
- aip_agents/storage/providers/object_storage.pyi +0 -98
- aip_agents/tools/__init__.pyi +0 -9
- aip_agents/tools/browser_use/__init__.pyi +0 -14
- aip_agents/tools/browser_use/action_parser.pyi +0 -18
- aip_agents/tools/browser_use/browser_use_tool.pyi +0 -50
- aip_agents/tools/browser_use/llm_config.pyi +0 -52
- aip_agents/tools/browser_use/minio_storage.pyi +0 -109
- aip_agents/tools/browser_use/schemas.pyi +0 -32
- aip_agents/tools/browser_use/session.pyi +0 -4
- aip_agents/tools/browser_use/session_errors.pyi +0 -53
- aip_agents/tools/browser_use/steel_session_recording.pyi +0 -63
- aip_agents/tools/browser_use/streaming.pyi +0 -81
- aip_agents/tools/browser_use/structured_data_parser.pyi +0 -86
- aip_agents/tools/browser_use/structured_data_recovery.pyi +0 -43
- aip_agents/tools/browser_use/types.pyi +0 -45
- aip_agents/tools/code_sandbox/__init__.pyi +0 -3
- aip_agents/tools/code_sandbox/constant.pyi +0 -4
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +0 -102
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +0 -29
- aip_agents/tools/constants.pyi +0 -138
- aip_agents/tools/document_loader/__init__.pyi +0 -7
- aip_agents/tools/document_loader/base_reader.pyi +0 -75
- aip_agents/tools/document_loader/docx_reader_tool.pyi +0 -10
- aip_agents/tools/document_loader/excel_reader_tool.pyi +0 -26
- aip_agents/tools/document_loader/pdf_reader_tool.pyi +0 -11
- aip_agents/tools/document_loader/pdf_splitter.pyi +0 -18
- aip_agents/tools/gl_connector/__init__.pyi +0 -3
- aip_agents/tools/gl_connector/tool.pyi +0 -74
- aip_agents/tools/gl_connector_tools.pyi +0 -39
- aip_agents/tools/memory_search/__init__.pyi +0 -5
- aip_agents/tools/memory_search/base.pyi +0 -69
- aip_agents/tools/memory_search/mem0.pyi +0 -19
- aip_agents/tools/memory_search/schema.pyi +0 -15
- aip_agents/tools/memory_search_tool.pyi +0 -3
- aip_agents/tools/time_tool.pyi +0 -16
- aip_agents/tools/tool_config_injector.pyi +0 -26
- aip_agents/tools/web_search/__init__.pyi +0 -3
- aip_agents/tools/web_search/serper_tool.pyi +0 -19
- aip_agents/types/__init__.pyi +0 -36
- aip_agents/types/a2a_events.pyi +0 -3
- aip_agents/utils/__init__.pyi +0 -11
- aip_agents/utils/a2a_connector.pyi +0 -146
- aip_agents/utils/artifact_helpers.pyi +0 -203
- aip_agents/utils/constants.pyi +0 -10
- aip_agents/utils/datetime/__init__.pyi +0 -4
- aip_agents/utils/datetime/normalization.pyi +0 -95
- aip_agents/utils/datetime/timezone.pyi +0 -48
- aip_agents/utils/env_loader.pyi +0 -10
- aip_agents/utils/event_handler_registry.pyi +0 -23
- aip_agents/utils/file_prompt_utils.pyi +0 -21
- aip_agents/utils/final_response_builder.pyi +0 -34
- aip_agents/utils/formatter_llm_client.pyi +0 -71
- aip_agents/utils/langgraph/__init__.pyi +0 -3
- aip_agents/utils/langgraph/converter.pyi +0 -49
- aip_agents/utils/langgraph/tool_managers/__init__.pyi +0 -5
- aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +0 -35
- aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +0 -48
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +0 -56
- aip_agents/utils/langgraph/tool_output_management.pyi +0 -329
- aip_agents/utils/logger.pyi +0 -60
- aip_agents/utils/metadata/__init__.pyi +0 -5
- aip_agents/utils/metadata/activity_metadata_helper.pyi +0 -25
- aip_agents/utils/metadata/activity_narrative/__init__.pyi +0 -7
- aip_agents/utils/metadata/activity_narrative/builder.pyi +0 -35
- aip_agents/utils/metadata/activity_narrative/constants.pyi +0 -10
- aip_agents/utils/metadata/activity_narrative/context.pyi +0 -32
- aip_agents/utils/metadata/activity_narrative/formatters.pyi +0 -48
- aip_agents/utils/metadata/activity_narrative/utils.pyi +0 -12
- aip_agents/utils/metadata/schemas/__init__.pyi +0 -4
- aip_agents/utils/metadata/schemas/activity_schema.pyi +0 -18
- aip_agents/utils/metadata/schemas/thinking_schema.pyi +0 -20
- aip_agents/utils/metadata/thinking_metadata_helper.pyi +0 -4
- aip_agents/utils/metadata_helper.pyi +0 -117
- aip_agents/utils/name_preprocessor/__init__.pyi +0 -6
- aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +0 -52
- aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +0 -38
- aip_agents/utils/name_preprocessor/name_preprocessor.pyi +0 -41
- aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +0 -34
- aip_agents/utils/pii/__init__.pyi +0 -5
- aip_agents/utils/pii/pii_handler.pyi +0 -96
- aip_agents/utils/pii/pii_helper.pyi +0 -78
- aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +0 -73
- aip_agents/utils/reference_helper.pyi +0 -81
- aip_agents/utils/sse_chunk_transformer.pyi +0 -166
- aip_agents/utils/step_limit_manager.pyi +0 -112
- aip_agents/utils/token_usage_helper.pyi +0 -60
- {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/WHEEL +0 -0
- {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/top_level.txt +0 -0
|
@@ -87,6 +87,9 @@ from aip_agents.utils.token_usage_helper import (
|
|
|
87
87
|
extract_token_usage_from_tool_output,
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
+
if TYPE_CHECKING:
|
|
91
|
+
from aip_agents.ptc import PTCSandboxConfig
|
|
92
|
+
|
|
90
93
|
logger = get_logger(__name__)
|
|
91
94
|
|
|
92
95
|
# Default instruction for ReAct agents
|
|
@@ -165,6 +168,7 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
|
|
|
165
168
|
middlewares: Sequence[AgentMiddleware] | None = None,
|
|
166
169
|
guardrail: GuardrailManager | None = None,
|
|
167
170
|
step_limit_config: StepLimitConfig | None = None,
|
|
171
|
+
ptc_config: PTCSandboxConfig | None = None,
|
|
168
172
|
**kwargs: Any,
|
|
169
173
|
):
|
|
170
174
|
"""Initialize the LangGraph ReAct Agent.
|
|
@@ -193,6 +197,11 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
|
|
|
193
197
|
input/output filtering during agent execution.
|
|
194
198
|
enable_pii: Optional toggle to enable PII handling for tool inputs and outputs.
|
|
195
199
|
step_limit_config: Optional configuration for step limits and delegation depth.
|
|
200
|
+
ptc_config: Optional configuration for PTC sandbox execution. See PTCSandboxConfig
|
|
201
|
+
for available options including enabled flag, sandbox timeout, and template settings.
|
|
202
|
+
PTC is enabled when ptc_config is not None and ptc_config.enabled is True.
|
|
203
|
+
When enabled, prompt guidance is automatically injected into the agent's instruction.
|
|
204
|
+
PTC runs in a sandbox only; there is no in-process trusted PTC path.
|
|
196
205
|
**kwargs: Additional keyword arguments passed to BaseLangGraphAgent.
|
|
197
206
|
"""
|
|
198
207
|
# Use LangGraph's standard AgentState for ReAct
|
|
@@ -239,6 +248,18 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
|
|
|
239
248
|
|
|
240
249
|
self.step_limit_config = step_limit_config
|
|
241
250
|
|
|
251
|
+
# Initialize PTC state (Programmatic Tool Calling)
|
|
252
|
+
self._ptc_config: PTCSandboxConfig | None = None
|
|
253
|
+
self._ptc_tool_synced = False
|
|
254
|
+
self._ptc_tool: BaseTool | None = None
|
|
255
|
+
self._ptc_prompt_hash: str = ""
|
|
256
|
+
# Capture instruction after middleware setup so middleware prompts are preserved
|
|
257
|
+
self._original_instruction: str = self.instruction
|
|
258
|
+
|
|
259
|
+
# Enable PTC if requested via constructor
|
|
260
|
+
if ptc_config is not None and ptc_config.enabled:
|
|
261
|
+
self.enable_ptc(ptc_config)
|
|
262
|
+
|
|
242
263
|
def _setup_middleware(
|
|
243
264
|
self,
|
|
244
265
|
planning: bool,
|
|
@@ -425,9 +446,9 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
|
|
|
425
446
|
)
|
|
426
447
|
|
|
427
448
|
def _rebuild_resolved_tools(self) -> None:
|
|
428
|
-
"""Rebuild resolved tools including middleware tools.
|
|
449
|
+
"""Rebuild resolved tools including middleware and PTC tools.
|
|
429
450
|
|
|
430
|
-
Overrides base class to ensure middleware tools are preserved
|
|
451
|
+
Overrides base class to ensure middleware tools and the PTC tool are preserved
|
|
431
452
|
when tools are rebuilt (e.g., after update_regular_tools).
|
|
432
453
|
"""
|
|
433
454
|
# Call base class to rebuild with regular, a2a, delegation, and mcp tools
|
|
@@ -437,6 +458,10 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
|
|
|
437
458
|
if hasattr(self, "_middleware_tools") and self._middleware_tools:
|
|
438
459
|
self.resolved_tools.extend(self._middleware_tools)
|
|
439
460
|
|
|
461
|
+
# Add PTC tool if synced
|
|
462
|
+
if hasattr(self, "_ptc_tool") and self._ptc_tool is not None:
|
|
463
|
+
self.resolved_tools.append(self._ptc_tool)
|
|
464
|
+
|
|
440
465
|
def _handle_tool_artifacts(
|
|
441
466
|
self, tool_output: Any, pending_artifacts: list[dict[str, Any]]
|
|
442
467
|
) -> tuple[str, list[dict[str, Any]]]:
|
|
@@ -2620,6 +2645,173 @@ class LangGraphReactAgent(LangGraphHitLMixin, BaseLangGraphAgent):
|
|
|
2620
2645
|
if current_thread_id:
|
|
2621
2646
|
self._pii_handlers_by_thread.pop(current_thread_id, None)
|
|
2622
2647
|
|
|
2648
|
+
# ==========================================================================
|
|
2649
|
+
# Programmatic Tool Calling (PTC) Methods
|
|
2650
|
+
# ==========================================================================
|
|
2651
|
+
|
|
2652
|
+
def add_mcp_server(self, mcp_config: dict[str, dict[str, Any]]) -> None:
|
|
2653
|
+
"""Add MCP servers and refresh PTC tool state if needed."""
|
|
2654
|
+
super().add_mcp_server(mcp_config)
|
|
2655
|
+
|
|
2656
|
+
if not self._ptc_config or not self._ptc_config.enabled:
|
|
2657
|
+
return
|
|
2658
|
+
|
|
2659
|
+
if self._ptc_tool is not None:
|
|
2660
|
+
self._ptc_tool = None
|
|
2661
|
+
|
|
2662
|
+
self._ptc_tool_synced = False
|
|
2663
|
+
logger.debug(f"Agent '{self.name}': PTC tool will resync after MCP changes")
|
|
2664
|
+
|
|
2665
|
+
def enable_ptc(self, config: PTCSandboxConfig | None = None) -> None:
|
|
2666
|
+
"""Enable Programmatic Tool Calling (PTC) for this agent.
|
|
2667
|
+
|
|
2668
|
+
PTC allows the LLM to execute Python code that calls MCP tools
|
|
2669
|
+
programmatically inside a sandboxed environment. This is useful for
|
|
2670
|
+
chaining multiple tool calls with local data processing.
|
|
2671
|
+
|
|
2672
|
+
The execute_ptc_code tool is automatically added to the agent's tools
|
|
2673
|
+
after MCP servers are configured. If no MCP servers are configured,
|
|
2674
|
+
the tool sync is deferred until servers are added.
|
|
2675
|
+
|
|
2676
|
+
Args:
|
|
2677
|
+
config: Optional configuration for PTC sandbox execution.
|
|
2678
|
+
See PTCSandboxConfig for options like enabled flag and sandbox_timeout.
|
|
2679
|
+
If None is passed, a default config with enabled=True will be created.
|
|
2680
|
+
|
|
2681
|
+
Example:
|
|
2682
|
+
agent.enable_ptc(PTCSandboxConfig(enabled=True))
|
|
2683
|
+
agent.add_mcp_server({"yfinance": {...}})
|
|
2684
|
+
# execute_ptc_code tool is now available
|
|
2685
|
+
|
|
2686
|
+
Note:
|
|
2687
|
+
PTC can also be enabled via the constructor by passing
|
|
2688
|
+
ptc_config=PTCSandboxConfig(enabled=True, ...).
|
|
2689
|
+
"""
|
|
2690
|
+
# Lazy import to avoid circular dependencies
|
|
2691
|
+
from aip_agents.ptc.executor import PTCSandboxConfig
|
|
2692
|
+
|
|
2693
|
+
self._ptc_config = config or PTCSandboxConfig()
|
|
2694
|
+
self._ptc_config.enabled = True
|
|
2695
|
+
self._ptc_tool_synced = False
|
|
2696
|
+
|
|
2697
|
+
logger.info(f"Agent '{self.name}': PTC enabled")
|
|
2698
|
+
|
|
2699
|
+
# Attempt to sync PTC tool if MCP client is available
|
|
2700
|
+
self._sync_ptc_tool()
|
|
2701
|
+
|
|
2702
|
+
def _sync_ptc_tool(self) -> None:
|
|
2703
|
+
"""Build and register the execute_ptc_code tool when MCP servers are available.
|
|
2704
|
+
|
|
2705
|
+
This method is called after enable_ptc() and after MCP servers are added.
|
|
2706
|
+
It creates the execute_ptc_code tool using the current MCP client
|
|
2707
|
+
configuration and adds it to the agent's resolved tools.
|
|
2708
|
+
|
|
2709
|
+
The tool is only created once. Subsequent calls are no-ops if the tool
|
|
2710
|
+
has already been synced.
|
|
2711
|
+
"""
|
|
2712
|
+
if not self._ptc_config or not self._ptc_config.enabled:
|
|
2713
|
+
return
|
|
2714
|
+
|
|
2715
|
+
if self._ptc_tool_synced:
|
|
2716
|
+
return
|
|
2717
|
+
|
|
2718
|
+
# Check if we have MCP servers configured
|
|
2719
|
+
if not self.mcp_config:
|
|
2720
|
+
logger.debug(f"Agent '{self.name}': PTC tool sync deferred - no MCP servers configured")
|
|
2721
|
+
return
|
|
2722
|
+
|
|
2723
|
+
if not self.mcp_client:
|
|
2724
|
+
logger.debug(f"Agent '{self.name}': PTC tool sync deferred - no MCP client yet")
|
|
2725
|
+
return
|
|
2726
|
+
|
|
2727
|
+
if not self.mcp_client.is_initialized:
|
|
2728
|
+
logger.debug(f"Agent '{self.name}': PTC tool sync deferred - MCP client not initialized")
|
|
2729
|
+
return
|
|
2730
|
+
|
|
2731
|
+
# Lazy import to avoid circular dependencies
|
|
2732
|
+
from aip_agents.tools.execute_ptc_code import create_execute_ptc_code_tool
|
|
2733
|
+
|
|
2734
|
+
logger.info(f"Agent '{self.name}': Syncing PTC tool with MCP client")
|
|
2735
|
+
|
|
2736
|
+
# Create the execute_ptc_code tool with agent's tool configs
|
|
2737
|
+
self._ptc_tool = create_execute_ptc_code_tool(
|
|
2738
|
+
self.mcp_client, self._ptc_config, agent_tool_configs=self.tool_configs
|
|
2739
|
+
)
|
|
2740
|
+
|
|
2741
|
+
# Rebuild graph to include PTC tool
|
|
2742
|
+
self._rebuild_graph()
|
|
2743
|
+
|
|
2744
|
+
self._ptc_tool_synced = True
|
|
2745
|
+
logger.info(f"Agent '{self.name}': PTC tool synced successfully")
|
|
2746
|
+
|
|
2747
|
+
# Sync PTC prompt guidance
|
|
2748
|
+
self._sync_ptc_prompt()
|
|
2749
|
+
|
|
2750
|
+
def _sync_ptc_prompt(self) -> None:
|
|
2751
|
+
"""Sync PTC usage guidance into the agent instruction.
|
|
2752
|
+
|
|
2753
|
+
This method builds and injects a PTC usage block into the agent's
|
|
2754
|
+
instruction when PTC is enabled. The prompt is refreshed when MCP
|
|
2755
|
+
configuration changes (detected via hash).
|
|
2756
|
+
"""
|
|
2757
|
+
if not self._ptc_config or not self._ptc_config.enabled:
|
|
2758
|
+
return
|
|
2759
|
+
|
|
2760
|
+
if not self.mcp_client:
|
|
2761
|
+
return
|
|
2762
|
+
|
|
2763
|
+
# Lazy import to avoid circular dependencies
|
|
2764
|
+
from aip_agents.ptc.prompt_builder import build_ptc_prompt, compute_ptc_prompt_hash
|
|
2765
|
+
|
|
2766
|
+
# Get prompt config from PTC sandbox config
|
|
2767
|
+
prompt_config = self._ptc_config.prompt if self._ptc_config else None
|
|
2768
|
+
|
|
2769
|
+
# Check if MCP config has changed
|
|
2770
|
+
current_hash = compute_ptc_prompt_hash(self.mcp_client, config=prompt_config)
|
|
2771
|
+
if current_hash == self._ptc_prompt_hash:
|
|
2772
|
+
logger.debug(f"Agent '{self.name}': PTC prompt unchanged, skipping refresh")
|
|
2773
|
+
return
|
|
2774
|
+
|
|
2775
|
+
# Build and inject the prompt
|
|
2776
|
+
ptc_prompt = build_ptc_prompt(self.mcp_client, config=prompt_config)
|
|
2777
|
+
|
|
2778
|
+
# Rebuild instruction from original + PTC guidance
|
|
2779
|
+
self.instruction = f"{self._original_instruction}\n\n{ptc_prompt}"
|
|
2780
|
+
self._ptc_prompt_hash = current_hash
|
|
2781
|
+
|
|
2782
|
+
logger.info(f"Agent '{self.name}': PTC prompt guidance injected")
|
|
2783
|
+
|
|
2784
|
+
async def _register_mcp_tools(self) -> None:
|
|
2785
|
+
"""Override to sync PTC tool after MCP tools are registered.
|
|
2786
|
+
|
|
2787
|
+
This extends the base implementation to ensure the execute_ptc_code
|
|
2788
|
+
tool is added after MCP servers are initialized.
|
|
2789
|
+
"""
|
|
2790
|
+
await super()._register_mcp_tools()
|
|
2791
|
+
|
|
2792
|
+
# Sync PTC tool after MCP tools are registered
|
|
2793
|
+
if self._ptc_config and self._ptc_config.enabled and not self._ptc_tool_synced:
|
|
2794
|
+
self._sync_ptc_tool()
|
|
2795
|
+
|
|
2796
|
+
async def cleanup(self) -> None:
|
|
2797
|
+
"""Cleanup agent resources including PTC sandbox.
|
|
2798
|
+
|
|
2799
|
+
Extends base cleanup to also cleanup the PTC sandbox runtime if
|
|
2800
|
+
execute_ptc_code tool was created.
|
|
2801
|
+
"""
|
|
2802
|
+
# Cleanup PTC tool's sandbox runtime if present
|
|
2803
|
+
if self._ptc_tool is not None:
|
|
2804
|
+
try:
|
|
2805
|
+
cleanup_method = getattr(self._ptc_tool, "cleanup", None)
|
|
2806
|
+
if cleanup_method and callable(cleanup_method):
|
|
2807
|
+
await cleanup_method()
|
|
2808
|
+
logger.debug(f"Agent '{self.name}': PTC sandbox cleanup completed")
|
|
2809
|
+
except Exception as e:
|
|
2810
|
+
logger.warning(f"Agent '{self.name}': Error during PTC sandbox cleanup: {e}")
|
|
2811
|
+
|
|
2812
|
+
# Call parent cleanup for MCP client
|
|
2813
|
+
await super().cleanup()
|
|
2814
|
+
|
|
2623
2815
|
def _format_graph_output(self, final_state_result: dict[str, Any]) -> Any:
|
|
2624
2816
|
"""Convert final graph state to user-friendly output.
|
|
2625
2817
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Minimal PTC hello world example.
|
|
2
|
+
|
|
3
|
+
Required environment variables:
|
|
4
|
+
- OPENAI_API_KEY
|
|
5
|
+
- E2B_API_KEY
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
|
|
10
|
+
from aip_agents.agent import LangGraphReactAgent
|
|
11
|
+
from aip_agents.ptc import PromptConfig, PTCSandboxConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def main() -> None:
|
|
15
|
+
"""Run a hello-world PTC flow."""
|
|
16
|
+
instruction = (
|
|
17
|
+
"You are a helpful assistant with access to execute_ptc_code. "
|
|
18
|
+
"Use execute_ptc_code to run Python and print output. "
|
|
19
|
+
"The tool returns JSON with ok/stdout/stderr/exit_code."
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
agent = LangGraphReactAgent(
|
|
23
|
+
name="ptc_hello_world",
|
|
24
|
+
instruction=instruction,
|
|
25
|
+
model="openai/gpt-5.2",
|
|
26
|
+
ptc_config=PTCSandboxConfig(enabled=True, sandbox_timeout=180.0, prompt=PromptConfig(mode="index")),
|
|
27
|
+
)
|
|
28
|
+
agent.add_mcp_server(
|
|
29
|
+
{
|
|
30
|
+
"deepwiki": {
|
|
31
|
+
"transport": "streamable-http",
|
|
32
|
+
"url": "https://mcp.deepwiki.com/mcp",
|
|
33
|
+
"headers": {},
|
|
34
|
+
"timeout": 60.0,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
response = await agent.arun(
|
|
41
|
+
query="Use execute_ptc_code to print 'Hello, world!' and count the number of words in the output of deepwiki.read_wiki_structure('anthropics/claude-code')."
|
|
42
|
+
)
|
|
43
|
+
print("execute_ptc_code output:", response["output"])
|
|
44
|
+
finally:
|
|
45
|
+
await agent.cleanup()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == "__main__":
|
|
49
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""PTC (Programmatic Tool Calling) core module (MCP-only).
|
|
2
|
+
|
|
3
|
+
This module provides the core PTC functionality for MCP tools, including
|
|
4
|
+
executor, prompt builder, and sandbox bridge.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from aip_agents.ptc.exceptions import PTCError, PTCToolError
|
|
11
|
+
from aip_agents.ptc.prompt_builder import PromptConfig, build_ptc_prompt, compute_ptc_prompt_hash
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
# Exceptions
|
|
15
|
+
"PTCError",
|
|
16
|
+
"PTCToolError",
|
|
17
|
+
# Executor
|
|
18
|
+
"PTCSandboxConfig",
|
|
19
|
+
"PTCSandboxExecutor",
|
|
20
|
+
# Prompt builder
|
|
21
|
+
"PromptConfig",
|
|
22
|
+
"build_ptc_prompt",
|
|
23
|
+
"compute_ptc_prompt_hash",
|
|
24
|
+
# Sandbox bridge
|
|
25
|
+
"build_sandbox_payload",
|
|
26
|
+
"wrap_ptc_code",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def __getattr__(name: str):
|
|
31
|
+
"""Lazy import to avoid circular dependencies."""
|
|
32
|
+
if name == "PTCSandboxConfig":
|
|
33
|
+
from aip_agents.ptc.executor import PTCSandboxConfig
|
|
34
|
+
|
|
35
|
+
return PTCSandboxConfig
|
|
36
|
+
elif name == "PTCSandboxExecutor":
|
|
37
|
+
from aip_agents.ptc.executor import PTCSandboxExecutor
|
|
38
|
+
|
|
39
|
+
return PTCSandboxExecutor
|
|
40
|
+
elif name == "build_sandbox_payload":
|
|
41
|
+
from aip_agents.ptc.sandbox_bridge import build_sandbox_payload
|
|
42
|
+
|
|
43
|
+
return build_sandbox_payload
|
|
44
|
+
elif name == "wrap_ptc_code":
|
|
45
|
+
from aip_agents.ptc.sandbox_bridge import wrap_ptc_code
|
|
46
|
+
|
|
47
|
+
return wrap_ptc_code
|
|
48
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Documentation generation utilities for PTC.
|
|
2
|
+
|
|
3
|
+
Shared constants and helpers for generating tool documentation in sandbox payloads.
|
|
4
|
+
|
|
5
|
+
Authors:
|
|
6
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from aip_agents.ptc.naming import sanitize_function_name
|
|
12
|
+
|
|
13
|
+
# Documentation limits (fixed constants per plan)
|
|
14
|
+
DOC_DESC_LIMIT = 120 # Tool description trim limit
|
|
15
|
+
DOC_PARAM_DESC_LIMIT = 80 # Parameter description trim limit
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def json_type_to_display(json_type: Any) -> str:
|
|
19
|
+
"""Convert JSON type to display string.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
json_type: JSON schema type.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Human-readable type string.
|
|
26
|
+
"""
|
|
27
|
+
if isinstance(json_type, list):
|
|
28
|
+
return "any"
|
|
29
|
+
type_map = {
|
|
30
|
+
"string": "str",
|
|
31
|
+
"integer": "int",
|
|
32
|
+
"number": "float",
|
|
33
|
+
"boolean": "bool",
|
|
34
|
+
"array": "list",
|
|
35
|
+
"object": "dict",
|
|
36
|
+
"null": "None",
|
|
37
|
+
}
|
|
38
|
+
return type_map.get(str(json_type), "any")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def trim_text(text: str | None, limit: int) -> str:
|
|
42
|
+
"""Trim text to limit with ellipsis if needed.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
text: Text to trim.
|
|
46
|
+
limit: Maximum length before trimming.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Trimmed text.
|
|
50
|
+
"""
|
|
51
|
+
if not text:
|
|
52
|
+
return ""
|
|
53
|
+
if len(text) > limit:
|
|
54
|
+
return text[:limit] + "..."
|
|
55
|
+
return text
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def render_tool_doc(
|
|
59
|
+
func_name: str,
|
|
60
|
+
signature: str,
|
|
61
|
+
description: str,
|
|
62
|
+
schema: dict[str, Any],
|
|
63
|
+
is_stub: bool = False,
|
|
64
|
+
example_code: str | None = None,
|
|
65
|
+
example_heading: str = "## Example",
|
|
66
|
+
) -> str:
|
|
67
|
+
"""Render markdown documentation for a tool.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
func_name: Sanitized function name.
|
|
71
|
+
signature: Full function signature.
|
|
72
|
+
description: Tool description.
|
|
73
|
+
schema: Input schema for parameters.
|
|
74
|
+
is_stub: Whether this is a stub documentation.
|
|
75
|
+
example_code: Optional example code block content (without ```python).
|
|
76
|
+
example_heading: Heading for the example section.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Markdown documentation string.
|
|
80
|
+
"""
|
|
81
|
+
if is_stub:
|
|
82
|
+
desc = "Details unavailable because tool definitions are not loaded yet."
|
|
83
|
+
else:
|
|
84
|
+
desc = trim_text(description, DOC_DESC_LIMIT) or "No description available."
|
|
85
|
+
|
|
86
|
+
lines = [
|
|
87
|
+
f"# {func_name}",
|
|
88
|
+
"",
|
|
89
|
+
f"**Description:** {desc}",
|
|
90
|
+
"",
|
|
91
|
+
f"**Signature:** `{signature}`",
|
|
92
|
+
"",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
# Add parameters section
|
|
96
|
+
properties = schema.get("properties", {})
|
|
97
|
+
required = set(schema.get("required", []))
|
|
98
|
+
|
|
99
|
+
if properties:
|
|
100
|
+
lines.append("## Parameters")
|
|
101
|
+
lines.append("")
|
|
102
|
+
for prop_name, prop_schema in sorted(properties.items()):
|
|
103
|
+
safe_param = sanitize_function_name(prop_name)
|
|
104
|
+
prop_type = json_type_to_display(prop_schema.get("type", "any"))
|
|
105
|
+
is_required = "required" if prop_name in required else "optional"
|
|
106
|
+
|
|
107
|
+
# Trim param description
|
|
108
|
+
raw_param_desc = prop_schema.get("description", "")
|
|
109
|
+
param_desc = trim_text(raw_param_desc, DOC_PARAM_DESC_LIMIT)
|
|
110
|
+
|
|
111
|
+
lines.append(f"- **{safe_param}** ({prop_type}, {is_required}): {param_desc}")
|
|
112
|
+
lines.append("")
|
|
113
|
+
|
|
114
|
+
# Add example section
|
|
115
|
+
if example_code:
|
|
116
|
+
lines.append(example_heading)
|
|
117
|
+
lines.append("")
|
|
118
|
+
lines.append("```python")
|
|
119
|
+
lines.append(example_code)
|
|
120
|
+
lines.append("```")
|
|
121
|
+
|
|
122
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""PTC-specific exceptions.
|
|
2
|
+
|
|
3
|
+
This module defines exceptions for Programmatic Tool Calling operations.
|
|
4
|
+
|
|
5
|
+
Authors:
|
|
6
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PTCError(Exception):
|
|
11
|
+
"""Base exception for PTC errors."""
|
|
12
|
+
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PTCToolError(PTCError):
|
|
17
|
+
"""Error during tool execution.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
server_name: The MCP server where the error occurred.
|
|
21
|
+
tool_name: The tool that failed.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
message: str,
|
|
27
|
+
server_name: str | None = None,
|
|
28
|
+
tool_name: str | None = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Initialize PTCToolError.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
message: Error message.
|
|
34
|
+
server_name: The MCP server name (optional).
|
|
35
|
+
tool_name: The tool name (optional).
|
|
36
|
+
"""
|
|
37
|
+
super().__init__(message)
|
|
38
|
+
self.server_name = server_name
|
|
39
|
+
self.tool_name = tool_name
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""PTC Executor implementations (MCP-only).
|
|
2
|
+
|
|
3
|
+
This module provides the sandboxed executor for Programmatic Tool Calling
|
|
4
|
+
with MCP tools.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
|
|
14
|
+
from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
|
|
15
|
+
from aip_agents.ptc.exceptions import PTCToolError
|
|
16
|
+
from aip_agents.ptc.prompt_builder import PromptConfig
|
|
17
|
+
from aip_agents.sandbox.defaults import DEFAULT_PTC_PACKAGES, DEFAULT_PTC_TEMPLATE
|
|
18
|
+
from aip_agents.utils.logger import get_logger
|
|
19
|
+
|
|
20
|
+
# Lazy import to avoid circular dependencies
|
|
21
|
+
# These are only needed for PTCSandboxExecutor
|
|
22
|
+
try:
|
|
23
|
+
from aip_agents.ptc.sandbox_bridge import build_sandbox_payload, wrap_ptc_code
|
|
24
|
+
from aip_agents.sandbox.e2b_runtime import E2BSandboxRuntime
|
|
25
|
+
from aip_agents.sandbox.types import SandboxExecutionResult
|
|
26
|
+
|
|
27
|
+
_SANDBOX_DEPS_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
_SANDBOX_DEPS_AVAILABLE = False
|
|
30
|
+
|
|
31
|
+
logger = get_logger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class PTCSandboxConfig:
|
|
36
|
+
"""Configuration for PTC sandbox executor (MCP-only).
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
enabled: Whether PTC is enabled. When False, PTC is disabled.
|
|
40
|
+
default_tool_timeout: Default timeout per tool call in seconds.
|
|
41
|
+
sandbox_template: Optional E2B sandbox template ID.
|
|
42
|
+
sandbox_timeout: Sandbox execution timeout in seconds (hard cap/TTL).
|
|
43
|
+
ptc_packages: List of packages to install in sandbox. None or empty list skips install.
|
|
44
|
+
prompt: Prompt configuration for PTC usage guidance.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
enabled: bool = False
|
|
48
|
+
default_tool_timeout: float = 60.0
|
|
49
|
+
sandbox_template: str | None = DEFAULT_PTC_TEMPLATE
|
|
50
|
+
sandbox_timeout: float = 300.0
|
|
51
|
+
ptc_packages: list[str] | None = field(default_factory=lambda: list(DEFAULT_PTC_PACKAGES))
|
|
52
|
+
prompt: PromptConfig = field(default_factory=PromptConfig)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class PTCSandboxExecutor:
|
|
56
|
+
r"""Executes PTC code inside an E2B sandbox (MCP-only).
|
|
57
|
+
|
|
58
|
+
This executor is used for LLM-generated code that requires sandboxing.
|
|
59
|
+
It builds a sandbox payload (MCP server config + generated tool modules)
|
|
60
|
+
and executes the code using the E2B runtime.
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
runtime = E2BSandboxRuntime()
|
|
64
|
+
executor = PTCSandboxExecutor(mcp_client, runtime)
|
|
65
|
+
result = await executor.execute_code("from tools.yfinance import get_stock\nprint(get_stock('AAPL'))")
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
mcp_client: BaseMCPClient,
|
|
71
|
+
runtime: E2BSandboxRuntime,
|
|
72
|
+
config: PTCSandboxConfig | None = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""Initialize PTCSandboxExecutor.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
mcp_client: The MCP client with configured servers.
|
|
78
|
+
runtime: The E2B sandbox runtime instance.
|
|
79
|
+
config: Optional sandbox executor configuration.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
ImportError: If sandbox dependencies are not available.
|
|
83
|
+
"""
|
|
84
|
+
if not _SANDBOX_DEPS_AVAILABLE:
|
|
85
|
+
raise ImportError(
|
|
86
|
+
"Sandbox dependencies not available. "
|
|
87
|
+
"PTCSandboxExecutor requires sandbox_bridge and e2b_runtime modules."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
self._mcp_client = mcp_client
|
|
91
|
+
self._runtime = runtime
|
|
92
|
+
self._config = config or PTCSandboxConfig()
|
|
93
|
+
|
|
94
|
+
async def execute_code(
|
|
95
|
+
self,
|
|
96
|
+
code: str,
|
|
97
|
+
) -> SandboxExecutionResult:
|
|
98
|
+
"""Execute code inside the sandbox with MCP access.
|
|
99
|
+
|
|
100
|
+
This method:
|
|
101
|
+
1. Builds the sandbox payload (MCP config + generated tool modules)
|
|
102
|
+
2. Wraps the user code with necessary imports and setup
|
|
103
|
+
3. Executes the code in the E2B sandbox
|
|
104
|
+
4. Returns the execution result (stdout/stderr/exit_code)
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
code: Python code to execute in the sandbox.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
SandboxExecutionResult with stdout, stderr, and exit_code.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
PTCToolError: If sandbox execution fails.
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
logger.info("Building sandbox payload")
|
|
117
|
+
payload = await build_sandbox_payload(
|
|
118
|
+
self._mcp_client,
|
|
119
|
+
self._config.default_tool_timeout,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
logger.info("Wrapping PTC code")
|
|
123
|
+
wrapped_code = wrap_ptc_code(code)
|
|
124
|
+
|
|
125
|
+
logger.info(f"Executing code in sandbox (timeout: {self._config.sandbox_timeout}s)")
|
|
126
|
+
result = await self._runtime.execute(
|
|
127
|
+
code=wrapped_code,
|
|
128
|
+
timeout=self._config.sandbox_timeout,
|
|
129
|
+
files=payload.files if payload.files else None,
|
|
130
|
+
env=payload.env,
|
|
131
|
+
template=self._config.sandbox_template,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if result.exit_code == 0:
|
|
135
|
+
logger.info("Sandbox execution completed successfully")
|
|
136
|
+
else:
|
|
137
|
+
logger.warning(f"Sandbox execution failed with exit code {result.exit_code}")
|
|
138
|
+
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
except Exception as exc:
|
|
142
|
+
logger.error(f"Sandbox execution failed: {exc}")
|
|
143
|
+
raise PTCToolError(f"Sandbox execution failed: {exc}") from exc
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Programmatic Tool Calling (PTC) module for MCP tools.
|
|
2
|
+
|
|
3
|
+
This module provides programmatic tool calling capabilities for MCP tools,
|
|
4
|
+
allowing code-based tool invocation instead of JSON tool calls.
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from aip_agents.ptc.exceptions import PTCError, PTCToolError
|
|
11
|
+
from aip_agents.ptc.mcp.sandbox_bridge import (
|
|
12
|
+
ServerConfig,
|
|
13
|
+
build_mcp_payload,
|
|
14
|
+
)
|
|
15
|
+
from aip_agents.ptc.naming import (
|
|
16
|
+
json_type_to_python,
|
|
17
|
+
sanitize_function_name,
|
|
18
|
+
sanitize_module_name,
|
|
19
|
+
sanitize_param_name,
|
|
20
|
+
schema_to_params,
|
|
21
|
+
)
|
|
22
|
+
from aip_agents.ptc.payload import SandboxPayload
|
|
23
|
+
from aip_agents.ptc.prompt_builder import (
|
|
24
|
+
build_ptc_prompt,
|
|
25
|
+
compute_ptc_prompt_hash,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Exceptions
|
|
30
|
+
"PTCError",
|
|
31
|
+
"PTCToolError",
|
|
32
|
+
# Naming utilities
|
|
33
|
+
"json_type_to_python",
|
|
34
|
+
"sanitize_function_name",
|
|
35
|
+
"sanitize_module_name",
|
|
36
|
+
"sanitize_param_name",
|
|
37
|
+
"schema_to_params",
|
|
38
|
+
# Prompt builder
|
|
39
|
+
"build_ptc_prompt",
|
|
40
|
+
"compute_ptc_prompt_hash",
|
|
41
|
+
# Sandbox Bridge
|
|
42
|
+
"SandboxPayload",
|
|
43
|
+
"ServerConfig",
|
|
44
|
+
"build_mcp_payload",
|
|
45
|
+
]
|