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.
Files changed (306) hide show
  1. aip_agents/agent/langgraph_react_agent.py +194 -2
  2. aip_agents/examples/hello_world_ptc.py +49 -0
  3. aip_agents/ptc/__init__.py +48 -0
  4. aip_agents/ptc/doc_gen.py +122 -0
  5. aip_agents/ptc/exceptions.py +39 -0
  6. aip_agents/ptc/executor.py +143 -0
  7. aip_agents/ptc/mcp/__init__.py +45 -0
  8. aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
  9. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  10. aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
  11. aip_agents/ptc/naming.py +184 -0
  12. aip_agents/ptc/payload.py +26 -0
  13. aip_agents/ptc/prompt_builder.py +571 -0
  14. aip_agents/ptc/ptc_helper.py +16 -0
  15. aip_agents/ptc/sandbox_bridge.py +58 -0
  16. aip_agents/ptc/template_utils.py +33 -0
  17. aip_agents/ptc/templates/__init__.py +1 -0
  18. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  19. aip_agents/sandbox/__init__.py +43 -0
  20. aip_agents/sandbox/defaults.py +9 -0
  21. aip_agents/sandbox/e2b_runtime.py +267 -0
  22. aip_agents/sandbox/template_builder.py +131 -0
  23. aip_agents/sandbox/types.py +24 -0
  24. aip_agents/sandbox/validation.py +50 -0
  25. aip_agents/tools/__init__.py +2 -0
  26. aip_agents/tools/execute_ptc_code.py +308 -0
  27. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/METADATA +1 -1
  28. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/RECORD +30 -282
  29. aip_agents/__init__.pyi +0 -19
  30. aip_agents/a2a/__init__.pyi +0 -3
  31. aip_agents/a2a/server/__init__.pyi +0 -4
  32. aip_agents/a2a/server/base_executor.pyi +0 -73
  33. aip_agents/a2a/server/google_adk_executor.pyi +0 -51
  34. aip_agents/a2a/server/langflow_executor.pyi +0 -43
  35. aip_agents/a2a/server/langgraph_executor.pyi +0 -47
  36. aip_agents/a2a/types.pyi +0 -132
  37. aip_agents/agent/__init__.pyi +0 -9
  38. aip_agents/agent/base_agent.pyi +0 -221
  39. aip_agents/agent/base_langgraph_agent.pyi +0 -233
  40. aip_agents/agent/google_adk_agent.pyi +0 -141
  41. aip_agents/agent/google_adk_constants.pyi +0 -3
  42. aip_agents/agent/hitl/__init__.pyi +0 -6
  43. aip_agents/agent/hitl/config.pyi +0 -15
  44. aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +0 -42
  45. aip_agents/agent/hitl/manager.pyi +0 -200
  46. aip_agents/agent/hitl/models.pyi +0 -3
  47. aip_agents/agent/hitl/prompt/__init__.pyi +0 -4
  48. aip_agents/agent/hitl/prompt/base.pyi +0 -24
  49. aip_agents/agent/hitl/prompt/deferred.pyi +0 -30
  50. aip_agents/agent/hitl/registry.pyi +0 -101
  51. aip_agents/agent/interface.pyi +0 -81
  52. aip_agents/agent/interfaces.pyi +0 -44
  53. aip_agents/agent/langflow_agent.pyi +0 -133
  54. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +0 -49
  55. aip_agents/agent/langgraph_react_agent.pyi +0 -131
  56. aip_agents/agent/system_instruction_context.pyi +0 -13
  57. aip_agents/clients/__init__.pyi +0 -4
  58. aip_agents/clients/langflow/__init__.pyi +0 -4
  59. aip_agents/clients/langflow/client.pyi +0 -140
  60. aip_agents/clients/langflow/types.pyi +0 -7
  61. aip_agents/constants.pyi +0 -7
  62. aip_agents/examples/__init__.pyi +0 -0
  63. aip_agents/examples/compare_streaming_client.pyi +0 -48
  64. aip_agents/examples/compare_streaming_server.pyi +0 -18
  65. aip_agents/examples/demo_memory_recall.pyi +0 -58
  66. aip_agents/examples/hello_world_a2a_google_adk_client.pyi +0 -9
  67. aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +0 -9
  68. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +0 -9
  69. aip_agents/examples/hello_world_a2a_google_adk_server.pyi +0 -15
  70. aip_agents/examples/hello_world_a2a_langchain_client.pyi +0 -5
  71. aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +0 -5
  72. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +0 -5
  73. aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +0 -5
  74. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +0 -5
  75. aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +0 -15
  76. aip_agents/examples/hello_world_a2a_langchain_server.pyi +0 -15
  77. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +0 -15
  78. aip_agents/examples/hello_world_a2a_langflow_client.pyi +0 -9
  79. aip_agents/examples/hello_world_a2a_langflow_server.pyi +0 -14
  80. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +0 -5
  81. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +0 -5
  82. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +0 -16
  83. aip_agents/examples/hello_world_a2a_langgraph_client.pyi +0 -9
  84. aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +0 -9
  85. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +0 -2
  86. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +0 -9
  87. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +0 -5
  88. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +0 -5
  89. aip_agents/examples/hello_world_a2a_langgraph_server.pyi +0 -14
  90. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +0 -15
  91. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +0 -15
  92. aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +0 -48
  93. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +0 -48
  94. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +0 -45
  95. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +0 -5
  96. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +0 -15
  97. aip_agents/examples/hello_world_google_adk.pyi +0 -5
  98. aip_agents/examples/hello_world_google_adk_mcp_http.pyi +0 -5
  99. aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +0 -5
  100. aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +0 -5
  101. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +0 -5
  102. aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +0 -5
  103. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +0 -5
  104. aip_agents/examples/hello_world_google_adk_stream.pyi +0 -5
  105. aip_agents/examples/hello_world_langchain.pyi +0 -5
  106. aip_agents/examples/hello_world_langchain_lm_invoker.pyi +0 -2
  107. aip_agents/examples/hello_world_langchain_mcp_http.pyi +0 -5
  108. aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +0 -16
  109. aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +0 -5
  110. aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +0 -18
  111. aip_agents/examples/hello_world_langchain_mcp_sse.pyi +0 -5
  112. aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +0 -5
  113. aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +0 -5
  114. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +0 -5
  115. aip_agents/examples/hello_world_langchain_stream.pyi +0 -5
  116. aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +0 -5
  117. aip_agents/examples/hello_world_langflow_agent.pyi +0 -35
  118. aip_agents/examples/hello_world_langgraph.pyi +0 -5
  119. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +0 -5
  120. aip_agents/examples/hello_world_langgraph_mcp_http.pyi +0 -5
  121. aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +0 -5
  122. aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +0 -5
  123. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +0 -5
  124. aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +0 -5
  125. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +0 -5
  126. aip_agents/examples/hello_world_langgraph_stream.pyi +0 -5
  127. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +0 -5
  128. aip_agents/examples/hello_world_model_switch_cli.pyi +0 -30
  129. aip_agents/examples/hello_world_multi_agent_adk.pyi +0 -6
  130. aip_agents/examples/hello_world_multi_agent_langchain.pyi +0 -5
  131. aip_agents/examples/hello_world_multi_agent_langgraph.pyi +0 -5
  132. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +0 -5
  133. aip_agents/examples/hello_world_pii_logger.pyi +0 -5
  134. aip_agents/examples/hello_world_sentry.pyi +0 -21
  135. aip_agents/examples/hello_world_step_limits.pyi +0 -17
  136. aip_agents/examples/hello_world_stock_a2a_server.pyi +0 -17
  137. aip_agents/examples/hello_world_tool_output_client.pyi +0 -5
  138. aip_agents/examples/hello_world_tool_output_server.pyi +0 -19
  139. aip_agents/examples/hitl_demo.pyi +0 -67
  140. aip_agents/examples/pii_demo_langgraph_client.pyi +0 -5
  141. aip_agents/examples/pii_demo_langgraph_server.pyi +0 -20
  142. aip_agents/examples/pii_demo_multi_agent_client.pyi +0 -5
  143. aip_agents/examples/pii_demo_multi_agent_server.pyi +0 -40
  144. aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +0 -5
  145. aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +0 -19
  146. aip_agents/examples/tools/__init__.pyi +0 -9
  147. aip_agents/examples/tools/adk_arithmetic_tools.pyi +0 -24
  148. aip_agents/examples/tools/adk_weather_tool.pyi +0 -18
  149. aip_agents/examples/tools/data_generator_tool.pyi +0 -15
  150. aip_agents/examples/tools/data_visualization_tool.pyi +0 -19
  151. aip_agents/examples/tools/image_artifact_tool.pyi +0 -26
  152. aip_agents/examples/tools/langchain_arithmetic_tools.pyi +0 -17
  153. aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +0 -20
  154. aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +0 -25
  155. aip_agents/examples/tools/langchain_weather_tool.pyi +0 -19
  156. aip_agents/examples/tools/langgraph_streaming_tool.pyi +0 -43
  157. aip_agents/examples/tools/mock_retrieval_tool.pyi +0 -13
  158. aip_agents/examples/tools/pii_demo_tools.pyi +0 -54
  159. aip_agents/examples/tools/random_chart_tool.pyi +0 -20
  160. aip_agents/examples/tools/serper_tool.pyi +0 -16
  161. aip_agents/examples/tools/stock_tools.pyi +0 -36
  162. aip_agents/examples/tools/table_generator_tool.pyi +0 -22
  163. aip_agents/examples/tools/time_tool.pyi +0 -15
  164. aip_agents/examples/tools/weather_forecast_tool.pyi +0 -14
  165. aip_agents/guardrails/__init__.pyi +0 -6
  166. aip_agents/guardrails/engines/__init__.pyi +0 -4
  167. aip_agents/guardrails/engines/base.pyi +0 -61
  168. aip_agents/guardrails/engines/nemo.pyi +0 -46
  169. aip_agents/guardrails/engines/phrase_matcher.pyi +0 -48
  170. aip_agents/guardrails/exceptions.pyi +0 -23
  171. aip_agents/guardrails/manager.pyi +0 -42
  172. aip_agents/guardrails/middleware.pyi +0 -87
  173. aip_agents/guardrails/schemas.pyi +0 -43
  174. aip_agents/guardrails/utils.pyi +0 -19
  175. aip_agents/mcp/__init__.pyi +0 -0
  176. aip_agents/mcp/client/__init__.pyi +0 -5
  177. aip_agents/mcp/client/base_mcp_client.pyi +0 -148
  178. aip_agents/mcp/client/connection_manager.pyi +0 -51
  179. aip_agents/mcp/client/google_adk/__init__.pyi +0 -3
  180. aip_agents/mcp/client/google_adk/client.pyi +0 -75
  181. aip_agents/mcp/client/langchain/__init__.pyi +0 -3
  182. aip_agents/mcp/client/langchain/client.pyi +0 -48
  183. aip_agents/mcp/client/persistent_session.pyi +0 -122
  184. aip_agents/mcp/client/session_pool.pyi +0 -101
  185. aip_agents/mcp/client/transports.pyi +0 -132
  186. aip_agents/mcp/utils/__init__.pyi +0 -0
  187. aip_agents/mcp/utils/config_validator.pyi +0 -82
  188. aip_agents/memory/__init__.pyi +0 -5
  189. aip_agents/memory/adapters/__init__.pyi +0 -4
  190. aip_agents/memory/adapters/base_adapter.pyi +0 -150
  191. aip_agents/memory/adapters/mem0.pyi +0 -22
  192. aip_agents/memory/base.pyi +0 -60
  193. aip_agents/memory/constants.pyi +0 -25
  194. aip_agents/memory/factory.pyi +0 -24
  195. aip_agents/memory/guidance.pyi +0 -3
  196. aip_agents/memory/simple_memory.pyi +0 -23
  197. aip_agents/middleware/__init__.pyi +0 -5
  198. aip_agents/middleware/base.pyi +0 -75
  199. aip_agents/middleware/manager.pyi +0 -84
  200. aip_agents/middleware/todolist.pyi +0 -125
  201. aip_agents/schema/__init__.pyi +0 -9
  202. aip_agents/schema/a2a.pyi +0 -40
  203. aip_agents/schema/agent.pyi +0 -65
  204. aip_agents/schema/hitl.pyi +0 -89
  205. aip_agents/schema/langgraph.pyi +0 -28
  206. aip_agents/schema/model_id.pyi +0 -54
  207. aip_agents/schema/step_limit.pyi +0 -63
  208. aip_agents/schema/storage.pyi +0 -21
  209. aip_agents/sentry/__init__.pyi +0 -3
  210. aip_agents/sentry/sentry.pyi +0 -48
  211. aip_agents/storage/__init__.pyi +0 -8
  212. aip_agents/storage/base.pyi +0 -58
  213. aip_agents/storage/clients/__init__.pyi +0 -3
  214. aip_agents/storage/clients/minio_client.pyi +0 -137
  215. aip_agents/storage/config.pyi +0 -29
  216. aip_agents/storage/providers/__init__.pyi +0 -5
  217. aip_agents/storage/providers/base.pyi +0 -88
  218. aip_agents/storage/providers/memory.pyi +0 -79
  219. aip_agents/storage/providers/object_storage.pyi +0 -98
  220. aip_agents/tools/__init__.pyi +0 -9
  221. aip_agents/tools/browser_use/__init__.pyi +0 -14
  222. aip_agents/tools/browser_use/action_parser.pyi +0 -18
  223. aip_agents/tools/browser_use/browser_use_tool.pyi +0 -50
  224. aip_agents/tools/browser_use/llm_config.pyi +0 -52
  225. aip_agents/tools/browser_use/minio_storage.pyi +0 -109
  226. aip_agents/tools/browser_use/schemas.pyi +0 -32
  227. aip_agents/tools/browser_use/session.pyi +0 -4
  228. aip_agents/tools/browser_use/session_errors.pyi +0 -53
  229. aip_agents/tools/browser_use/steel_session_recording.pyi +0 -63
  230. aip_agents/tools/browser_use/streaming.pyi +0 -81
  231. aip_agents/tools/browser_use/structured_data_parser.pyi +0 -86
  232. aip_agents/tools/browser_use/structured_data_recovery.pyi +0 -43
  233. aip_agents/tools/browser_use/types.pyi +0 -45
  234. aip_agents/tools/code_sandbox/__init__.pyi +0 -3
  235. aip_agents/tools/code_sandbox/constant.pyi +0 -4
  236. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +0 -102
  237. aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +0 -29
  238. aip_agents/tools/constants.pyi +0 -138
  239. aip_agents/tools/document_loader/__init__.pyi +0 -7
  240. aip_agents/tools/document_loader/base_reader.pyi +0 -75
  241. aip_agents/tools/document_loader/docx_reader_tool.pyi +0 -10
  242. aip_agents/tools/document_loader/excel_reader_tool.pyi +0 -26
  243. aip_agents/tools/document_loader/pdf_reader_tool.pyi +0 -11
  244. aip_agents/tools/document_loader/pdf_splitter.pyi +0 -18
  245. aip_agents/tools/gl_connector/__init__.pyi +0 -3
  246. aip_agents/tools/gl_connector/tool.pyi +0 -74
  247. aip_agents/tools/gl_connector_tools.pyi +0 -39
  248. aip_agents/tools/memory_search/__init__.pyi +0 -5
  249. aip_agents/tools/memory_search/base.pyi +0 -69
  250. aip_agents/tools/memory_search/mem0.pyi +0 -19
  251. aip_agents/tools/memory_search/schema.pyi +0 -15
  252. aip_agents/tools/memory_search_tool.pyi +0 -3
  253. aip_agents/tools/time_tool.pyi +0 -16
  254. aip_agents/tools/tool_config_injector.pyi +0 -26
  255. aip_agents/tools/web_search/__init__.pyi +0 -3
  256. aip_agents/tools/web_search/serper_tool.pyi +0 -19
  257. aip_agents/types/__init__.pyi +0 -36
  258. aip_agents/types/a2a_events.pyi +0 -3
  259. aip_agents/utils/__init__.pyi +0 -11
  260. aip_agents/utils/a2a_connector.pyi +0 -146
  261. aip_agents/utils/artifact_helpers.pyi +0 -203
  262. aip_agents/utils/constants.pyi +0 -10
  263. aip_agents/utils/datetime/__init__.pyi +0 -4
  264. aip_agents/utils/datetime/normalization.pyi +0 -95
  265. aip_agents/utils/datetime/timezone.pyi +0 -48
  266. aip_agents/utils/env_loader.pyi +0 -10
  267. aip_agents/utils/event_handler_registry.pyi +0 -23
  268. aip_agents/utils/file_prompt_utils.pyi +0 -21
  269. aip_agents/utils/final_response_builder.pyi +0 -34
  270. aip_agents/utils/formatter_llm_client.pyi +0 -71
  271. aip_agents/utils/langgraph/__init__.pyi +0 -3
  272. aip_agents/utils/langgraph/converter.pyi +0 -49
  273. aip_agents/utils/langgraph/tool_managers/__init__.pyi +0 -5
  274. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +0 -35
  275. aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +0 -48
  276. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +0 -56
  277. aip_agents/utils/langgraph/tool_output_management.pyi +0 -329
  278. aip_agents/utils/logger.pyi +0 -60
  279. aip_agents/utils/metadata/__init__.pyi +0 -5
  280. aip_agents/utils/metadata/activity_metadata_helper.pyi +0 -25
  281. aip_agents/utils/metadata/activity_narrative/__init__.pyi +0 -7
  282. aip_agents/utils/metadata/activity_narrative/builder.pyi +0 -35
  283. aip_agents/utils/metadata/activity_narrative/constants.pyi +0 -10
  284. aip_agents/utils/metadata/activity_narrative/context.pyi +0 -32
  285. aip_agents/utils/metadata/activity_narrative/formatters.pyi +0 -48
  286. aip_agents/utils/metadata/activity_narrative/utils.pyi +0 -12
  287. aip_agents/utils/metadata/schemas/__init__.pyi +0 -4
  288. aip_agents/utils/metadata/schemas/activity_schema.pyi +0 -18
  289. aip_agents/utils/metadata/schemas/thinking_schema.pyi +0 -20
  290. aip_agents/utils/metadata/thinking_metadata_helper.pyi +0 -4
  291. aip_agents/utils/metadata_helper.pyi +0 -117
  292. aip_agents/utils/name_preprocessor/__init__.pyi +0 -6
  293. aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +0 -52
  294. aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +0 -38
  295. aip_agents/utils/name_preprocessor/name_preprocessor.pyi +0 -41
  296. aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +0 -34
  297. aip_agents/utils/pii/__init__.pyi +0 -5
  298. aip_agents/utils/pii/pii_handler.pyi +0 -96
  299. aip_agents/utils/pii/pii_helper.pyi +0 -78
  300. aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +0 -73
  301. aip_agents/utils/reference_helper.pyi +0 -81
  302. aip_agents/utils/sse_chunk_transformer.pyi +0 -166
  303. aip_agents/utils/step_limit_manager.pyi +0 -112
  304. aip_agents/utils/token_usage_helper.pyi +0 -60
  305. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/WHEEL +0 -0
  306. {aip_agents_binary-0.6.0.dist-info → aip_agents_binary-0.6.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,668 @@
1
+ """Sandbox Bridge for PTC.
2
+
3
+ This module generates the sandbox payload (config + tool modules) that allows
4
+ LLM-generated code to call MCP tools inside an E2B sandbox.
5
+
6
+ The payload includes:
7
+ - ptc_config.json: MCP server configs for the sandbox MCP client
8
+ - tools/__init__.py: Package init with server imports
9
+ - tools/<server>.py: Per-server module with sync tool functions
10
+ - tools/mcp_client.py: HTTP JSON-RPC client for MCP calls
11
+
12
+ Authors:
13
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
14
+ """
15
+
16
+ import json
17
+ from dataclasses import dataclass, field
18
+ from typing import Any
19
+
20
+ from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
21
+ from aip_agents.ptc.doc_gen import (
22
+ render_tool_doc,
23
+ )
24
+ from aip_agents.ptc.naming import (
25
+ DEFAULT_EXAMPLE_PLACEHOLDER,
26
+ example_value_from_schema,
27
+ sanitize_function_name,
28
+ sanitize_module_name_with_reserved,
29
+ schema_to_params,
30
+ )
31
+ from aip_agents.ptc.payload import SandboxPayload
32
+ from aip_agents.ptc.ptc_helper import _generate_ptc_helper_module
33
+ from aip_agents.ptc.template_utils import render_template
34
+ from aip_agents.utils.logger import get_logger
35
+
36
+ logger = get_logger(__name__)
37
+
38
+ # Transport types supported in sandbox (HTTP-based only)
39
+ # Using normalized hyphenated format to align with connection_manager
40
+ SUPPORTED_TRANSPORTS = {"sse", "streamable-http"}
41
+
42
+ _TEMPLATE_PACKAGE = "aip_agents.ptc.mcp.templates"
43
+
44
+
45
+ @dataclass
46
+ class ServerConfig:
47
+ """Extracted server configuration for sandbox payload.
48
+
49
+ Attributes:
50
+ name: Server name identifier.
51
+ transport: Transport type (sse or streamable_http).
52
+ url: Server URL.
53
+ headers: HTTP headers for authentication.
54
+ allowed_tools: List of allowed tool names, or None for all.
55
+ tools: List of tool definitions from the server.
56
+ timeout: Request timeout in seconds.
57
+ """
58
+
59
+ name: str
60
+ transport: str
61
+ url: str
62
+ headers: dict[str, str] = field(default_factory=dict)
63
+ allowed_tools: list[str] | None = None
64
+ tools: list[dict[str, Any]] = field(default_factory=list)
65
+ timeout: float = 60.0
66
+
67
+
68
+ async def build_mcp_payload(
69
+ mcp_client: BaseMCPClient,
70
+ default_tool_timeout: float = 60.0,
71
+ ) -> SandboxPayload:
72
+ """Build MCP sandbox payload from MCP client configuration.
73
+
74
+ Extracts server configs, tools, and generates the necessary files
75
+ for the sandbox to execute PTC code.
76
+
77
+ Args:
78
+ mcp_client: The MCP client with configured servers.
79
+ default_tool_timeout: Default timeout for tool calls in seconds.
80
+
81
+ Returns:
82
+ SandboxPayload containing files and env vars for the sandbox.
83
+ """
84
+ payload = SandboxPayload()
85
+
86
+ # Extract server configs
87
+ server_configs = await _extract_server_configs(mcp_client, default_tool_timeout)
88
+
89
+ if not server_configs:
90
+ logger.warning("No supported MCP servers found for sandbox payload")
91
+ return payload
92
+
93
+ # Generate ptc_config.json
94
+ config_json = _generate_config_json(server_configs)
95
+ payload.files["ptc_config.json"] = config_json
96
+
97
+ # Generate tools/mcp_client.py
98
+ payload.files["tools/mcp_client.py"] = _generate_mcp_client_module()
99
+
100
+ # Generate tools/__init__.py
101
+ server_names = [cfg.name for cfg in server_configs]
102
+ payload.files["tools/__init__.py"] = _generate_tools_init(server_names)
103
+
104
+ # Generate tools/<server>.py for each server (using reserved-aware sanitization)
105
+ for server_cfg in server_configs:
106
+ module_content = _generate_server_module(server_cfg)
107
+ safe_name = sanitize_module_name_with_reserved(server_cfg.name)
108
+ payload.files[f"tools/{safe_name}.py"] = module_content
109
+
110
+ # Generate tools/ptc_helper.py for discovery
111
+ payload.files["tools/ptc_helper.py"] = _generate_ptc_helper_module()
112
+
113
+ # Generate tools/ptc_index.json for tool index
114
+ payload.files["tools/ptc_index.json"] = _generate_ptc_index(server_configs)
115
+
116
+ # Generate tools/docs/<package>/<tool>.md for each tool
117
+ docs = _generate_all_docs(server_configs)
118
+ payload.files.update(docs)
119
+
120
+ logger.info(f"Built sandbox payload with {len(server_configs)} servers, {len(payload.files)} files")
121
+
122
+ return payload
123
+
124
+
125
+ def _get_config_value(config: Any, key: str, default: Any = None) -> Any:
126
+ """Extract value from config dict or object.
127
+
128
+ Args:
129
+ config: Configuration dict or object.
130
+ key: Key to extract.
131
+ default: Default value if key not found.
132
+
133
+ Returns:
134
+ Extracted value or default.
135
+ """
136
+ if isinstance(config, dict):
137
+ return config.get(key, default)
138
+ return getattr(config, key, default)
139
+
140
+
141
+ def _normalize_transport(transport: Any) -> str | None:
142
+ """Normalize transport format.
143
+
144
+ Args:
145
+ transport: Raw transport value.
146
+
147
+ Returns:
148
+ Normalized transport string or None.
149
+ """
150
+ if not transport:
151
+ return None
152
+ return str(transport).lower().replace("_", "-")
153
+
154
+
155
+ def _extract_headers(config: Any) -> dict[str, str]:
156
+ """Extract headers from config.
157
+
158
+ Args:
159
+ config: Configuration dict or object.
160
+
161
+ Returns:
162
+ Headers dictionary.
163
+ """
164
+ raw_headers = _get_config_value(config, "headers")
165
+ if raw_headers and isinstance(raw_headers, dict):
166
+ return dict(raw_headers)
167
+ return {}
168
+
169
+
170
+ def _extract_timeout(config: Any, default_timeout: float) -> float:
171
+ """Extract timeout from config.
172
+
173
+ Args:
174
+ config: Configuration dict or object.
175
+ default_timeout: Default timeout value.
176
+
177
+ Returns:
178
+ Timeout in seconds.
179
+ """
180
+ raw_timeout = _get_config_value(config, "timeout")
181
+ if raw_timeout is not None:
182
+ return float(raw_timeout)
183
+ return default_timeout
184
+
185
+
186
+ def _extract_allowed_tools(mcp_client: BaseMCPClient, server_name: str, config: Any) -> list[str] | None:
187
+ """Extract allowed tools from session or config.
188
+
189
+ Args:
190
+ mcp_client: MCP client instance.
191
+ server_name: Name of the server.
192
+ config: Configuration dict or object.
193
+
194
+ Returns:
195
+ List of allowed tool names or None.
196
+ """
197
+ try:
198
+ session = mcp_client.session_pool.get_session(server_name)
199
+ if session.allowed_tools:
200
+ return list(session.allowed_tools)
201
+ except (KeyError, AttributeError):
202
+ pass
203
+
204
+ raw_allowed = _get_config_value(config, "allowed_tools")
205
+ if raw_allowed and isinstance(raw_allowed, list):
206
+ return list(raw_allowed)
207
+ return None
208
+
209
+
210
+ async def _extract_tools(
211
+ mcp_client: BaseMCPClient,
212
+ server_name: str,
213
+ allowed_tools: list[str] | None,
214
+ ) -> list[dict[str, Any]]:
215
+ """Extract tools from MCP client.
216
+
217
+ When tools are not loaded but allowed_tools exists, returns stub tool entries.
218
+
219
+ Args:
220
+ mcp_client: MCP client instance.
221
+ server_name: Name of the server.
222
+ allowed_tools: List of allowed tool names or None.
223
+
224
+ Returns:
225
+ List of tool definitions. Stubs have minimal schema when tools not loaded.
226
+ """
227
+ tools: list[dict[str, Any]] = []
228
+ try:
229
+ raw_tools = await mcp_client.get_raw_mcp_tools(server_name)
230
+ for tool in raw_tools:
231
+ if allowed_tools and tool.name not in allowed_tools:
232
+ continue
233
+ tools.append(
234
+ {
235
+ "name": tool.name,
236
+ "description": tool.description or "",
237
+ "input_schema": tool.inputSchema,
238
+ "stub": False,
239
+ }
240
+ )
241
+ except Exception as e:
242
+ logger.warning(f"Failed to get tools from server '{server_name}': {e}")
243
+ # If tools not loaded but allowlist exists, create stub entries
244
+ if allowed_tools:
245
+ for tool_name in sorted(allowed_tools):
246
+ tools.append(
247
+ {
248
+ "name": tool_name,
249
+ "description": "",
250
+ "input_schema": {"type": "object", "properties": {}},
251
+ "stub": True,
252
+ }
253
+ )
254
+ return tools
255
+
256
+
257
+ async def _extract_server_configs(
258
+ mcp_client: BaseMCPClient,
259
+ default_tool_timeout: float,
260
+ ) -> list[ServerConfig]:
261
+ """Extract server configurations from MCP client.
262
+
263
+ Args:
264
+ mcp_client: The MCP client with configured servers.
265
+ default_tool_timeout: Default timeout for tool calls.
266
+
267
+ Returns:
268
+ List of ServerConfig objects for supported servers.
269
+ """
270
+ server_configs: list[ServerConfig] = []
271
+
272
+ for server_name, config in mcp_client.servers.items():
273
+ transport = _normalize_transport(_get_config_value(config, "transport"))
274
+
275
+ if transport not in SUPPORTED_TRANSPORTS:
276
+ logger.warning(
277
+ f"Skipping server '{server_name}': transport '{transport}' not supported in sandbox "
278
+ f"(supported: {SUPPORTED_TRANSPORTS})"
279
+ )
280
+ continue
281
+
282
+ url = _get_config_value(config, "url")
283
+ if not url:
284
+ logger.warning(f"Skipping server '{server_name}': no URL configured")
285
+ continue
286
+
287
+ headers = _extract_headers(config)
288
+ timeout = _extract_timeout(config, default_tool_timeout)
289
+ allowed_tools = _extract_allowed_tools(mcp_client, server_name, config)
290
+ tools = await _extract_tools(mcp_client, server_name, allowed_tools)
291
+
292
+ server_configs.append(
293
+ ServerConfig(
294
+ name=server_name,
295
+ transport=transport,
296
+ url=url,
297
+ headers=headers,
298
+ allowed_tools=allowed_tools,
299
+ tools=tools,
300
+ timeout=timeout,
301
+ )
302
+ )
303
+
304
+ logger.debug(f"Extracted config for server '{server_name}': {len(tools)} tools")
305
+
306
+ return server_configs
307
+
308
+
309
+ def _generate_config_json(server_configs: list[ServerConfig]) -> str:
310
+ """Generate ptc_config.json content.
311
+
312
+ Args:
313
+ server_configs: List of server configurations.
314
+
315
+ Returns:
316
+ JSON string of the config.
317
+ """
318
+ config = {
319
+ "servers": {
320
+ cfg.name: {
321
+ "transport": cfg.transport,
322
+ "url": cfg.url,
323
+ "headers": cfg.headers,
324
+ "timeout": cfg.timeout,
325
+ "allowed_tools": cfg.allowed_tools,
326
+ }
327
+ for cfg in server_configs
328
+ }
329
+ }
330
+ return json.dumps(config, indent=2)
331
+
332
+
333
+ def _generate_mcp_client_module() -> str:
334
+ """Generate the tools/mcp_client.py module.
335
+
336
+ This module uses the official MCP Python SDK to call MCP tools from the sandbox.
337
+ It provides sync wrappers around the async MCP SDK for simpler LLM-generated code.
338
+
339
+ Returns:
340
+ Python source code for the MCP client module.
341
+ """
342
+ return render_template(_TEMPLATE_PACKAGE, "mcp_client.py.template")
343
+
344
+
345
+ def _generate_tools_init(server_names: list[str]) -> str:
346
+ """Generate tools/__init__.py content.
347
+
348
+ Args:
349
+ server_names: List of server names.
350
+
351
+ Returns:
352
+ Python source code for the __init__.py module.
353
+ """
354
+ # Use reserved-safe sanitization and sort for deterministic output
355
+ safe_names = sorted(sanitize_module_name_with_reserved(name) for name in server_names)
356
+
357
+ imports = "\n".join(f"from tools import {name}" for name in safe_names)
358
+ all_list = ", ".join(f'"{name}"' for name in safe_names)
359
+
360
+ return f'''"""Generated tools package for PTC sandbox execution.
361
+
362
+ This package provides access to MCP tools configured for this agent.
363
+ Import tools from specific server modules:
364
+
365
+ from tools.server_name import tool_name
366
+ """
367
+
368
+ {imports}
369
+
370
+ __all__ = [{all_list}]
371
+ '''
372
+
373
+
374
+ def _generate_server_module(server_cfg: ServerConfig) -> str:
375
+ """Generate tools/<server>.py module content.
376
+
377
+ Args:
378
+ server_cfg: Server configuration with tools.
379
+
380
+ Returns:
381
+ Python source code for the server module.
382
+ """
383
+ functions: list[str] = []
384
+ function_names: list[str] = []
385
+
386
+ for tool in server_cfg.tools:
387
+ func_name = sanitize_function_name(tool["name"])
388
+ function_names.append(func_name)
389
+
390
+ # Build function signature from input_schema
391
+ schema = tool.get("input_schema", {})
392
+ params = schema_to_params(schema)
393
+ doc = _build_docstring(tool)
394
+
395
+ func_code = f'''
396
+ def {func_name}({params}) -> Any:
397
+ """{doc}"""
398
+ arguments = {_build_arguments_dict(schema)}
399
+ return call_tool("{server_cfg.name}", "{tool["name"]}", arguments)
400
+ '''
401
+ functions.append(func_code)
402
+
403
+ all_list = ", ".join(f'"{name}"' for name in function_names)
404
+ functions_code = "\n".join(functions)
405
+
406
+ return f'''"""Generated module for MCP server: {server_cfg.name}
407
+
408
+ This module provides Python functions for each tool exposed by the MCP server.
409
+ """
410
+
411
+ from typing import Any
412
+
413
+ from tools.mcp_client import call_tool
414
+
415
+ __all__ = [{all_list}]
416
+
417
+ {functions_code}
418
+ '''
419
+
420
+
421
+ # Note: sanitize_module_name, sanitize_function_name, schema_to_params, and
422
+ # json_type_to_python are imported from aip_agents.ptc.naming
423
+
424
+
425
+ def _build_arguments_dict(schema: dict[str, Any]) -> str:
426
+ """Build arguments dict code from schema.
427
+
428
+ Args:
429
+ schema: JSON schema for tool input.
430
+
431
+ Returns:
432
+ Python code for building arguments dict.
433
+ """
434
+ properties = schema.get("properties", {})
435
+
436
+ if not properties:
437
+ return "kwargs"
438
+
439
+ items: list[str] = []
440
+ for prop_name in properties:
441
+ safe_name = sanitize_function_name(prop_name)
442
+ items.append(f'"{prop_name}": {safe_name}')
443
+
444
+ return "{" + ", ".join(items) + "}"
445
+
446
+
447
+ def _build_docstring(tool: dict[str, Any]) -> str:
448
+ """Build docstring for a tool function.
449
+
450
+ Args:
451
+ tool: Tool definition with name, description, input_schema.
452
+
453
+ Returns:
454
+ Docstring content.
455
+ """
456
+ desc = tool.get("description", f"Call {tool['name']} tool.")
457
+ # Escape triple quotes in description
458
+ desc = desc.replace('"""', '\\"\\"\\"')
459
+ return desc
460
+
461
+
462
+ def _generate_tool_doc(
463
+ tool: dict[str, Any],
464
+ ) -> str:
465
+ """Generate markdown documentation for a single tool.
466
+
467
+ Args:
468
+ tool: Tool definition with name, description, input_schema, optional stub flag.
469
+
470
+ Returns:
471
+ Markdown documentation string.
472
+ """
473
+ func_name = sanitize_function_name(tool["name"])
474
+ schema = tool.get("input_schema", {})
475
+
476
+ # Use schema_to_params for consistent signatures (stubs and loaded tools)
477
+ params = schema_to_params(schema)
478
+ signature = f"{func_name}({params})"
479
+
480
+ # Add example section
481
+ example_args, uses_placeholder = _build_example_args_with_placeholder(schema)
482
+ if tool.get("stub"):
483
+ uses_placeholder = True
484
+ example_heading = "## Example (placeholder)" if uses_placeholder else "## Example"
485
+ example_code = f"{func_name}({example_args})"
486
+
487
+ return render_tool_doc(
488
+ func_name=func_name,
489
+ signature=signature,
490
+ description=tool.get("description", ""),
491
+ schema=schema,
492
+ is_stub=tool.get("stub", False),
493
+ example_code=example_code,
494
+ example_heading=example_heading,
495
+ )
496
+
497
+
498
+ def _build_example_args(schema: dict[str, Any]) -> str:
499
+ """Build example argument string for a tool call.
500
+
501
+ Uses schema defaults, enums, or examples when available.
502
+ Falls back to neutral placeholders. Required params come first.
503
+
504
+ Args:
505
+ schema: Tool input schema.
506
+
507
+ Returns:
508
+ Example arguments string.
509
+ """
510
+ args, _ = _build_example_args_with_placeholder(schema)
511
+ return args
512
+
513
+
514
+ def _is_placeholder(prop_schema: dict[str, Any]) -> bool:
515
+ """Check if a property schema uses a placeholder value.
516
+
517
+ Args:
518
+ prop_schema: Property schema dict.
519
+
520
+ Returns:
521
+ True if the schema doesn't have examples, default, or enum values.
522
+ """
523
+ return not (prop_schema.get("examples") or "default" in prop_schema or prop_schema.get("enum"))
524
+
525
+
526
+ def _process_property_args(
527
+ properties: dict[str, Any],
528
+ required: set[str],
529
+ filter_required: bool,
530
+ ) -> tuple[list[str], bool]:
531
+ """Process properties and generate argument strings.
532
+
533
+ Args:
534
+ properties: Schema properties dict.
535
+ required: Set of required property names.
536
+ filter_required: If True, process only required params; if False, only optional.
537
+
538
+ Returns:
539
+ Tuple of (list of arg strings, uses_placeholder flag).
540
+ """
541
+ args: list[str] = []
542
+ uses_placeholder = False
543
+
544
+ for prop_name in sorted(properties.keys()):
545
+ is_required = prop_name in required
546
+ if (filter_required and not is_required) or (not filter_required and is_required):
547
+ continue
548
+
549
+ prop_schema = properties[prop_name]
550
+ safe_name = sanitize_function_name(prop_name)
551
+ example_value = _get_doc_example_value(prop_schema, prop_name)
552
+ args.append(f"{safe_name}={example_value}")
553
+
554
+ if _is_placeholder(prop_schema):
555
+ uses_placeholder = True
556
+
557
+ return args, uses_placeholder
558
+
559
+
560
+ def _build_example_args_with_placeholder(schema: dict[str, Any]) -> tuple[str, bool]:
561
+ """Build example arguments with placeholder tracking.
562
+
563
+ Args:
564
+ schema: Tool input schema.
565
+
566
+ Returns:
567
+ Tuple of (arguments string, uses_placeholder flag).
568
+ """
569
+ properties = schema.get("properties", {})
570
+ required = set(schema.get("required", []))
571
+
572
+ if not properties:
573
+ return "", False
574
+
575
+ # Process required params first
576
+ required_args, required_has_placeholder = _process_property_args(properties, required, filter_required=True)
577
+
578
+ # Then add optional params
579
+ optional_args, optional_has_placeholder = _process_property_args(properties, required, filter_required=False)
580
+
581
+ all_args = required_args + optional_args
582
+ uses_placeholder = required_has_placeholder or optional_has_placeholder
583
+
584
+ return ", ".join(all_args), uses_placeholder
585
+
586
+
587
+ def _get_doc_example_value(prop_schema: dict[str, Any], prop_name: str) -> str:
588
+ """Get example value for documentation.
589
+
590
+ Prefers schema-provided examples, defaults, or enums.
591
+ Falls back to neutral placeholders.
592
+
593
+ Args:
594
+ prop_schema: Property schema.
595
+ prop_name: Property name.
596
+
597
+ Returns:
598
+ Example value as Python literal string.
599
+ """
600
+ return example_value_from_schema(prop_schema, default_placeholder=DEFAULT_EXAMPLE_PLACEHOLDER)
601
+
602
+
603
+ def _generate_all_docs(server_configs: list[ServerConfig]) -> dict[str, str]:
604
+ """Generate all tool documentation files.
605
+
606
+ Args:
607
+ server_configs: List of server configurations.
608
+
609
+ Returns:
610
+ Dict mapping file path to content.
611
+ """
612
+ docs: dict[str, str] = {}
613
+
614
+ sorted_configs = sorted(server_configs, key=lambda cfg: sanitize_module_name_with_reserved(cfg.name))
615
+
616
+ for cfg in sorted_configs:
617
+ safe_name = sanitize_module_name_with_reserved(cfg.name)
618
+ sorted_tools = sorted(cfg.tools, key=lambda tool: sanitize_function_name(tool["name"]))
619
+
620
+ for tool in sorted_tools:
621
+ func_name = sanitize_function_name(tool["name"])
622
+ doc_path = f"tools/docs/{safe_name}/{func_name}.md"
623
+ doc_content = _generate_tool_doc(tool)
624
+ docs[doc_path] = doc_content
625
+
626
+ return docs
627
+
628
+
629
+ def _generate_ptc_index(server_configs: list[ServerConfig]) -> str:
630
+ """Generate the tools/ptc_index.json tool index.
631
+
632
+ Args:
633
+ server_configs: List of server configurations with tools.
634
+
635
+ Returns:
636
+ JSON string of the tool index.
637
+ """
638
+ packages: dict[str, Any] = {}
639
+
640
+ # Sort server configs by sanitized name for deterministic output
641
+ sorted_configs = sorted(server_configs, key=lambda c: sanitize_module_name_with_reserved(c.name))
642
+
643
+ for cfg in sorted_configs:
644
+ safe_name = sanitize_module_name_with_reserved(cfg.name)
645
+
646
+ # Sort tools by sanitized name
647
+ sorted_tools = sorted(cfg.tools, key=lambda t: sanitize_function_name(t["name"]))
648
+
649
+ tool_entries = []
650
+ for tool in sorted_tools:
651
+ func_name = sanitize_function_name(tool["name"])
652
+ schema = tool.get("input_schema", {})
653
+
654
+ # Use schema_to_params for consistent signatures (stubs and loaded tools)
655
+ signature = f"{func_name}({schema_to_params(schema)})"
656
+
657
+ tool_entries.append(
658
+ {
659
+ "name": func_name,
660
+ "signature": signature,
661
+ "doc_path": f"tools/docs/{safe_name}/{func_name}.md",
662
+ }
663
+ )
664
+
665
+ packages[safe_name] = {"tools": tool_entries}
666
+
667
+ index = {"packages": packages}
668
+ return json.dumps(index, indent=2, sort_keys=True)
@@ -0,0 +1 @@
1
+ """Templates package for PTC sandbox code generation."""