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,571 @@
1
+ """PTC Prompt Builder.
2
+
3
+ Generates usage guidance prompts for PTC that help the LLM correctly use
4
+ the execute_ptc_code tool with proper import patterns and parameter naming.
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
13
+ from typing import TYPE_CHECKING, Any, Literal
14
+
15
+ from aip_agents.ptc.naming import (
16
+ example_value_from_schema,
17
+ sanitize_function_name,
18
+ sanitize_module_name_with_reserved,
19
+ sanitize_param_name,
20
+ schema_to_params,
21
+ )
22
+ from aip_agents.utils.logger import get_logger
23
+
24
+ if TYPE_CHECKING:
25
+ from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
26
+
27
+ logger = get_logger(__name__)
28
+
29
+ # Prompt mode type alias
30
+ PromptMode = Literal["minimal", "index", "full", "auto"]
31
+
32
+ # Markdown constants
33
+ PYTHON_BLOCK_START = "```python"
34
+
35
+
36
+ @dataclass
37
+ class PromptConfig:
38
+ """Configuration for PTC prompt generation.
39
+
40
+ Attributes:
41
+ mode: Prompt mode - minimal, index, full, or auto.
42
+ auto_threshold: Total tool count threshold for auto mode (default 10).
43
+ include_example: Whether to include example code in prompt.
44
+ """
45
+
46
+ mode: PromptMode = "auto"
47
+ auto_threshold: int = 10
48
+ include_example: bool = True
49
+
50
+
51
+ # Shared PTC usage rules block (DRY: used in both placeholder and full prompts)
52
+ PTC_USAGE_RULES = """## PTC (Programmatic Tool Calling) Usage
53
+
54
+ When using `execute_ptc_code`, follow these rules:
55
+
56
+ 1. **Import pattern**: `from tools.<server> import <tool_name>`
57
+ 2. **Output**: Only `print()` output is returned to you. Always print results.
58
+ 3. **Parameter names**: All parameters are lowercase with underscores.
59
+ - Example: `userId` becomes `userid`, `user-id` becomes `user_id`
60
+ """
61
+
62
+
63
+ def build_ptc_prompt(
64
+ mcp_client: BaseMCPClient | None = None,
65
+ config: PromptConfig | None = None,
66
+ ) -> str:
67
+ """Build PTC usage guidance prompt from MCP configuration.
68
+
69
+ Generates a short usage block that includes:
70
+ - The import pattern: MCP (`from tools.<server> import <tool>`)
71
+ - Rule: use `print()`; only printed output returns
72
+ - Rule: parameter names are sanitized to lowercase/underscored
73
+ - Prompt mode content (minimal/index/full)
74
+ - Examples based on the resolved prompt mode
75
+
76
+ Args:
77
+ mcp_client: The MCP client with configured servers.
78
+ config: Prompt configuration. If None, uses default PromptConfig.
79
+
80
+ Returns:
81
+ PTC usage guidance prompt string.
82
+ """
83
+ if config is None:
84
+ config = PromptConfig()
85
+
86
+ # Collect MCP server info (sorted for deterministic output)
87
+ server_infos: list[dict[str, Any]] = []
88
+ if mcp_client and mcp_client.servers:
89
+ for server_name in sorted(mcp_client.servers.keys()):
90
+ tools = _get_server_tools(mcp_client, server_name)
91
+ server_infos.append({"name": server_name, "tools": tools})
92
+
93
+ # Check if we have any tools
94
+ if not server_infos:
95
+ return _build_placeholder_prompt()
96
+
97
+ # Resolve mode and build appropriate prompt
98
+ resolved_mode = _resolve_mode(config, server_infos)
99
+
100
+ if resolved_mode == "minimal":
101
+ return _build_minimal_prompt(server_infos, config.include_example)
102
+ elif resolved_mode == "index":
103
+ return _build_index_prompt(server_infos, config.include_example)
104
+ else: # full
105
+ return _build_full_prompt(server_infos, config.include_example)
106
+
107
+
108
+ def _get_server_tools(
109
+ mcp_client: BaseMCPClient,
110
+ server_name: str,
111
+ ) -> list[dict[str, Any]]:
112
+ """Get tool definitions for a server.
113
+
114
+ When tools are not loaded but allowed_tools exists, returns stub tool entries.
115
+
116
+ Args:
117
+ mcp_client: MCP client instance.
118
+ server_name: Name of the server.
119
+
120
+ Returns:
121
+ List of tool definitions with name, description, and input_schema.
122
+ Stubs have empty description and minimal schema when tools not loaded.
123
+ """
124
+ tools: list[dict[str, Any]] = []
125
+ allowed_tools: list[str] | None = None
126
+ raw_tools: list[Any] = []
127
+ try:
128
+ # Try to get cached tools from session pool
129
+ session = mcp_client.session_pool.get_session(server_name)
130
+ allowed_tools = session.allowed_tools if session.allowed_tools else None
131
+
132
+ # Get tools from session (public attribute on PersistentMCPSession)
133
+ raw_tools = list(getattr(session, "tools", []))
134
+ except (KeyError, AttributeError) as e:
135
+ logger.debug(f"Could not get tools for server '{server_name}': {e}")
136
+
137
+ if allowed_tools is None:
138
+ allowed_tools = _get_allowed_tools_from_config(mcp_client, server_name)
139
+
140
+ if not raw_tools and allowed_tools:
141
+ # Tools not loaded but allowlist exists - return stub entries
142
+ for tool_name in sorted(allowed_tools):
143
+ tools.append(
144
+ {
145
+ "name": tool_name,
146
+ "description": "",
147
+ "input_schema": {"type": "object", "properties": {}},
148
+ "stub": True,
149
+ }
150
+ )
151
+ elif raw_tools:
152
+ # Tools loaded - return actual tool definitions
153
+ for tool in raw_tools:
154
+ if allowed_tools and tool.name not in allowed_tools:
155
+ continue
156
+ tools.append(
157
+ {
158
+ "name": tool.name,
159
+ "description": tool.description or "",
160
+ "input_schema": tool.inputSchema,
161
+ "stub": False,
162
+ }
163
+ )
164
+ return tools
165
+
166
+
167
+ def _get_allowed_tools_from_config(mcp_client: BaseMCPClient, server_name: str) -> list[str] | None:
168
+ """Extract allowed_tools from MCP client server config.
169
+
170
+ Args:
171
+ mcp_client: MCP client instance.
172
+ server_name: Server name to look up.
173
+
174
+ Returns:
175
+ List of allowed tools or None.
176
+ """
177
+ if not mcp_client or not mcp_client.servers:
178
+ return None
179
+
180
+ config = mcp_client.servers.get(server_name)
181
+ if not config:
182
+ return None
183
+
184
+ raw_allowed = config.get("allowed_tools") if isinstance(config, dict) else getattr(config, "allowed_tools", None)
185
+ if raw_allowed and isinstance(raw_allowed, list):
186
+ return list(raw_allowed)
187
+ return None
188
+
189
+
190
+ def _count_total_tools(
191
+ server_infos: list[dict[str, Any]],
192
+ ) -> int:
193
+ """Count total tools across all servers.
194
+
195
+ Args:
196
+ server_infos: List of server info dicts with name and tools.
197
+
198
+ Returns:
199
+ Total tool count.
200
+ """
201
+ return sum(len(info.get("tools", [])) for info in server_infos)
202
+
203
+
204
+ def _resolve_mode(
205
+ config: PromptConfig,
206
+ server_infos: list[dict[str, Any]],
207
+ ) -> PromptMode:
208
+ """Resolve auto mode to concrete mode based on tool count.
209
+
210
+ Args:
211
+ config: Prompt configuration.
212
+ server_infos: List of server info dicts.
213
+
214
+ Returns:
215
+ Resolved mode (minimal, index, or full).
216
+ """
217
+ if config.mode != "auto":
218
+ return config.mode
219
+
220
+ total_tools = _count_total_tools(server_infos)
221
+ if total_tools == 0 or total_tools > config.auto_threshold:
222
+ return "minimal"
223
+ return "full"
224
+
225
+
226
+ def _build_discovery_example() -> str:
227
+ """Build discovery example using ptc_helper module.
228
+
229
+ Returns:
230
+ Discovery example code string.
231
+ """
232
+ return """from tools.ptc_helper import list_tools, describe_tool
233
+
234
+ # List available tools in a package
235
+ tools = list_tools("package_name")
236
+ print([tool["name"] for tool in tools])
237
+
238
+ # Get details for a specific tool
239
+ doc = describe_tool("package_name", tools[0]["name"])
240
+ print(doc["doc"])"""
241
+
242
+
243
+ def _build_minimal_prompt(
244
+ server_infos: list[dict[str, Any]],
245
+ include_example: bool,
246
+ ) -> str:
247
+ """Build minimal prompt with rules and package list only.
248
+
249
+ Args:
250
+ server_infos: List of server info dicts with name and tools.
251
+ include_example: Whether to include discovery example.
252
+
253
+ Returns:
254
+ Minimal PTC usage prompt.
255
+ """
256
+ lines = [
257
+ PTC_USAGE_RULES.rstrip(),
258
+ "",
259
+ "### Available Packages",
260
+ "",
261
+ ]
262
+
263
+ # List MCP packages (sorted reserved-safe sanitized names)
264
+ package_names = sorted(sanitize_module_name_with_reserved(info["name"]) for info in server_infos)
265
+ for pkg in package_names:
266
+ lines.append(f"- `tools.{pkg}`")
267
+
268
+ lines.append("")
269
+ lines.append("Use `tools.ptc_helper` to discover available tools and their signatures.")
270
+
271
+ if include_example:
272
+ lines.extend(
273
+ [
274
+ "",
275
+ "### Discovery Example",
276
+ "",
277
+ PYTHON_BLOCK_START,
278
+ _build_discovery_example(),
279
+ "```",
280
+ ]
281
+ )
282
+
283
+ return "\n".join(lines)
284
+
285
+
286
+ def _build_index_prompt(
287
+ server_infos: list[dict[str, Any]],
288
+ include_example: bool,
289
+ ) -> str:
290
+ """Build index prompt with rules, package list, and tool names.
291
+
292
+ Args:
293
+ server_infos: List of server info dicts with name and tools.
294
+ include_example: Whether to include discovery example.
295
+
296
+ Returns:
297
+ Index PTC usage prompt.
298
+ """
299
+ lines = [
300
+ PTC_USAGE_RULES.rstrip(),
301
+ "",
302
+ "### Available Tools",
303
+ "",
304
+ ]
305
+
306
+ # Sort server infos by reserved-safe sanitized name for deterministic output
307
+ sorted_infos = sorted(server_infos, key=lambda x: sanitize_module_name_with_reserved(x["name"]))
308
+
309
+ for server_info in sorted_infos:
310
+ safe_server = sanitize_module_name_with_reserved(server_info["name"])
311
+ lines.append(f"**`tools.{safe_server}`**")
312
+
313
+ # Sort tools by sanitized name
314
+ sorted_tools = sorted(server_info["tools"], key=lambda t: sanitize_function_name(t["name"]))
315
+ tool_names = [sanitize_function_name(t["name"]) for t in sorted_tools]
316
+ lines.append(f" Tools: {', '.join(tool_names)}")
317
+ lines.append("")
318
+
319
+ lines.append("Use `tools.ptc_helper` to get tool signatures and descriptions.")
320
+
321
+ if include_example:
322
+ lines.extend(
323
+ [
324
+ "",
325
+ "### Discovery Example",
326
+ "",
327
+ PYTHON_BLOCK_START,
328
+ _build_discovery_example(),
329
+ "```",
330
+ ]
331
+ )
332
+
333
+ return "\n".join(lines)
334
+
335
+
336
+ def _build_full_prompt(
337
+ server_infos: list[dict[str, Any]],
338
+ include_example: bool,
339
+ ) -> str:
340
+ """Build full prompt with rules, signatures, and descriptions.
341
+
342
+ Args:
343
+ server_infos: List of server info dicts with name and tools.
344
+ include_example: Whether to include real tool example.
345
+
346
+ Returns:
347
+ Full PTC usage prompt.
348
+ """
349
+ lines = [
350
+ PTC_USAGE_RULES.rstrip(),
351
+ "",
352
+ "### Available Tools",
353
+ "",
354
+ ]
355
+
356
+ # Sort server infos by reserved-safe sanitized name for deterministic output
357
+ sorted_infos = sorted(server_infos, key=lambda x: sanitize_module_name_with_reserved(x["name"]))
358
+
359
+ for server_info in sorted_infos:
360
+ safe_server = sanitize_module_name_with_reserved(server_info["name"])
361
+ lines.append(f"**Server: `{safe_server}`** (from `tools.{safe_server}`)")
362
+ lines.append("")
363
+
364
+ # Sort tools by sanitized name
365
+ sorted_tools = sorted(server_info["tools"], key=lambda t: sanitize_function_name(t["name"]))
366
+
367
+ for tool in sorted_tools:
368
+ func_name = sanitize_function_name(tool["name"])
369
+ schema = tool.get("input_schema", {})
370
+ params = schema_to_params(schema)
371
+ raw_desc = tool.get("description", "")
372
+ desc = raw_desc[:120]
373
+ if raw_desc and len(raw_desc) > 120:
374
+ desc += "..."
375
+
376
+ lines.append(f"- `{func_name}({params})`: {desc}")
377
+
378
+ lines.append("")
379
+
380
+ if include_example:
381
+ example = _build_example(server_infos)
382
+ lines.extend(
383
+ [
384
+ "### Example",
385
+ "",
386
+ PYTHON_BLOCK_START,
387
+ example,
388
+ "```",
389
+ ]
390
+ )
391
+
392
+ return "\n".join(lines)
393
+
394
+
395
+ def _build_prompt_from_servers(server_infos: list[dict[str, Any]]) -> str:
396
+ """Build prompt from collected server information (legacy, uses full mode).
397
+
398
+ Args:
399
+ server_infos: List of server info dicts with name and tools.
400
+
401
+ Returns:
402
+ Formatted PTC usage prompt.
403
+ """
404
+ return _build_full_prompt(server_infos, include_example=True)
405
+
406
+
407
+ def _build_example(
408
+ server_infos: list[dict[str, Any]],
409
+ ) -> str:
410
+ """Build an example code snippet using the first available tool.
411
+
412
+ Args:
413
+ server_infos: List of server info dicts.
414
+
415
+ Returns:
416
+ Example code string.
417
+ """
418
+ if server_infos:
419
+ sorted_servers = sorted(server_infos, key=lambda info: sanitize_module_name_with_reserved(info["name"]))
420
+ for server in sorted_servers:
421
+ tools = server.get("tools", [])
422
+ if tools:
423
+ sorted_tools = sorted(tools, key=lambda t: sanitize_function_name(t["name"]))
424
+ tool = sorted_tools[0]
425
+ safe_server = sanitize_module_name_with_reserved(server["name"])
426
+ func_name = sanitize_function_name(tool["name"])
427
+ args_str = _build_example_args_from_schema(tool.get("input_schema", {}))
428
+ return f"""from tools.{safe_server} import {func_name}
429
+
430
+ result = {func_name}({args_str})
431
+ print(result)"""
432
+
433
+ return _build_generic_example()
434
+
435
+
436
+ def _build_example_args_from_schema(schema: dict[str, Any]) -> str:
437
+ """Build example arguments string from a JSON schema.
438
+
439
+ Args:
440
+ schema: JSON schema for tool input.
441
+
442
+ Returns:
443
+ Example arguments string.
444
+ """
445
+ properties = schema.get("properties", {})
446
+ required = set(schema.get("required", []))
447
+
448
+ args: list[str] = []
449
+ for prop_name in sorted(required):
450
+ if prop_name not in properties:
451
+ continue
452
+ safe_name = sanitize_param_name(prop_name)
453
+ prop_schema = properties[prop_name]
454
+ example_value = _get_example_value(prop_schema, prop_name)
455
+ args.append(f"{safe_name}={example_value}")
456
+
457
+ for prop_name in sorted(properties.keys()):
458
+ if prop_name in required:
459
+ continue
460
+ if len(args) >= 2:
461
+ break
462
+ safe_name = sanitize_param_name(prop_name)
463
+ prop_schema = properties[prop_name]
464
+ example_value = _get_example_value(prop_schema, prop_name)
465
+ args.append(f"{safe_name}={example_value}")
466
+
467
+ return ", ".join(args) if args else ""
468
+
469
+
470
+ def _get_example_value(prop_schema: dict[str, Any], prop_name: str) -> str:
471
+ """Generate an example value for a parameter.
472
+
473
+ Prefers schema-provided examples, defaults, or enums.
474
+ Falls back to type-based placeholders.
475
+
476
+ Args:
477
+ prop_schema: Property schema from JSON schema.
478
+ prop_name: Original property name.
479
+
480
+ Returns:
481
+ Example value as a Python literal string.
482
+ """
483
+ return example_value_from_schema(prop_schema)
484
+
485
+
486
+ def _build_generic_example() -> str:
487
+ """Build a generic example when no tools are available.
488
+
489
+ Returns:
490
+ Generic example code string.
491
+ """
492
+ return """from tools.server_name import tool_name
493
+
494
+ result = tool_name(param="value")
495
+ print(result)"""
496
+
497
+
498
+ def _build_placeholder_prompt() -> str:
499
+ """Build a placeholder prompt when no MCP servers are configured.
500
+
501
+ Returns:
502
+ Placeholder PTC usage prompt.
503
+ """
504
+ return PTC_USAGE_RULES + "\n*No MCP servers configured yet. Tools will be available after MCP setup.*\n"
505
+
506
+
507
+ def _build_server_hash_part(mcp_client: BaseMCPClient, server_name: str) -> str:
508
+ """Build hash part for a single MCP server.
509
+
510
+ Args:
511
+ mcp_client: MCP client instance.
512
+ server_name: Name of the server.
513
+
514
+ Returns:
515
+ Hash part string for the server.
516
+ """
517
+ try:
518
+ session = mcp_client.session_pool.get_session(server_name)
519
+ tools = list(getattr(session, "tools", []))
520
+ tool_names = sorted(t.name for t in tools)
521
+
522
+ allowed = session.allowed_tools if hasattr(session, "allowed_tools") else None
523
+ if not allowed:
524
+ allowed = _get_allowed_tools_from_config(mcp_client, server_name)
525
+ allowed_str = ",".join(sorted(allowed)) if allowed else "*"
526
+
527
+ return f"{server_name}:{','.join(tool_names)}|allowed={allowed_str}"
528
+ except (KeyError, AttributeError):
529
+ allowed = _get_allowed_tools_from_config(mcp_client, server_name)
530
+ allowed_str = ",".join(sorted(allowed)) if allowed else "*"
531
+ return f"{server_name}:|allowed={allowed_str}"
532
+
533
+
534
+ def compute_ptc_prompt_hash(
535
+ mcp_client: BaseMCPClient | None = None,
536
+ config: PromptConfig | None = None,
537
+ ) -> str:
538
+ """Compute a hash of the MCP configuration for change detection.
539
+
540
+ Includes PromptConfig fields and allowed_tools in hash computation
541
+ so prompt updates re-sync correctly when configuration changes.
542
+
543
+ Args:
544
+ mcp_client: MCP client instance.
545
+ config: Prompt configuration. If None, uses default PromptConfig.
546
+
547
+ Returns:
548
+ Hash string representing current configuration.
549
+ """
550
+ import hashlib
551
+
552
+ if config is None:
553
+ config = PromptConfig()
554
+
555
+ # Include config fields in hash
556
+ config_part = f"mode={config.mode}|threshold={config.auto_threshold}|example={config.include_example}"
557
+
558
+ # Create hash from server names, tool names, and allowed_tools
559
+ parts: list[str] = [config_part]
560
+
561
+ # Add MCP server parts
562
+ if mcp_client and mcp_client.servers:
563
+ for server_name in sorted(mcp_client.servers.keys()):
564
+ parts.append(_build_server_hash_part(mcp_client, server_name))
565
+
566
+ # Return empty hash if no tools configured
567
+ if len(parts) == 1:
568
+ return ""
569
+
570
+ content = "|".join(parts)
571
+ return hashlib.sha256(content.encode()).hexdigest()[:16]
@@ -0,0 +1,16 @@
1
+ """PTC helper module generation utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from aip_agents.ptc.template_utils import render_template
6
+
7
+ _TEMPLATE_PACKAGE = "aip_agents.ptc.templates"
8
+
9
+
10
+ def _generate_ptc_helper_module() -> str:
11
+ """Generate the tools/ptc_helper.py discovery module.
12
+
13
+ Returns:
14
+ Python source code for the PTC helper module.
15
+ """
16
+ return render_template(_TEMPLATE_PACKAGE, "ptc_helper.py.template")
@@ -0,0 +1,58 @@
1
+ """Top-level PTC Sandbox Bridge (MCP-only).
2
+
3
+ This module provides the unified entry point for building sandbox payloads
4
+ for MCP tools.
5
+
6
+ Authors:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
13
+ from aip_agents.ptc.mcp.sandbox_bridge import build_mcp_payload
14
+ from aip_agents.ptc.payload import SandboxPayload
15
+
16
+
17
+ async def build_sandbox_payload(
18
+ mcp_client: BaseMCPClient | None = None,
19
+ default_tool_timeout: float = 60.0,
20
+ ) -> SandboxPayload:
21
+ """Build sandbox payload from MCP client configuration (MCP-only).
22
+
23
+ Args:
24
+ mcp_client: The MCP client with configured servers.
25
+ default_tool_timeout: Default timeout for tool calls in seconds.
26
+
27
+ Returns:
28
+ SandboxPayload containing files and env vars for the sandbox.
29
+ """
30
+ # Build MCP payload
31
+ if mcp_client:
32
+ return await build_mcp_payload(mcp_client, default_tool_timeout)
33
+ return SandboxPayload()
34
+
35
+
36
+ def wrap_ptc_code(code: str) -> str:
37
+ """Wrap user PTC code with necessary imports and setup (MCP-only).
38
+
39
+ This prepends sys.path setup to ensure the tools package is importable.
40
+
41
+ Args:
42
+ code: User-provided Python code.
43
+
44
+ Returns:
45
+ Wrapped code ready for sandbox execution.
46
+ """
47
+ preamble = """# PTC Code Wrapper - Auto-generated
48
+ import sys
49
+ import os
50
+
51
+ # Add tools package to path
52
+ _tools_dir = os.path.dirname(os.path.abspath(__file__)) if "__file__" in dir() else os.getcwd()
53
+ if _tools_dir not in sys.path:
54
+ sys.path.insert(0, _tools_dir)
55
+
56
+ # User code below
57
+ """
58
+ return preamble + code
@@ -0,0 +1,33 @@
1
+ """Template rendering helpers for PTC payloads.
2
+
3
+ Authors:
4
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Mapping
10
+ from importlib import resources
11
+ from string import Template
12
+
13
+
14
+ def render_template(
15
+ package: str,
16
+ template_name: str,
17
+ values: Mapping[str, str] | None = None,
18
+ ) -> str:
19
+ """Render a template from package resources with optional substitutions.
20
+
21
+ Args:
22
+ package: Package path containing the template.
23
+ template_name: Template filename.
24
+ values: Optional mapping of template variables.
25
+
26
+ Returns:
27
+ Rendered template content.
28
+ """
29
+ template_file = resources.files(package).joinpath(template_name)
30
+ template_text = template_file.read_text(encoding="utf-8")
31
+ if not values:
32
+ return template_text
33
+ return Template(template_text).substitute(values)
@@ -0,0 +1 @@
1
+ """Shared templates for PTC sandbox code generation."""