aip-agents-binary 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. aip_agents/__init__.py +66 -0
  2. aip_agents/a2a/__init__.py +19 -0
  3. aip_agents/a2a/server/__init__.py +10 -0
  4. aip_agents/a2a/server/base_executor.py +1125 -0
  5. aip_agents/a2a/server/google_adk_executor.py +198 -0
  6. aip_agents/a2a/server/langflow_executor.py +180 -0
  7. aip_agents/a2a/server/langgraph_executor.py +270 -0
  8. aip_agents/a2a/types.py +232 -0
  9. aip_agents/a2ui/__init__.py +26 -0
  10. aip_agents/a2ui/catalog_provider.py +109 -0
  11. aip_agents/a2ui/examples/0.8/booking_form.json +1 -0
  12. aip_agents/a2ui/examples/0.8/confirmation.json +1 -0
  13. aip_agents/a2ui/examples/0.8/single_column_list.json +1 -0
  14. aip_agents/a2ui/examples/0.9/booking_form.json +1 -0
  15. aip_agents/a2ui/examples/0.9/confirmation.json +1 -0
  16. aip_agents/a2ui/examples/0.9/single_column_list.json +1 -0
  17. aip_agents/a2ui/middleware.py +754 -0
  18. aip_agents/a2ui/models.py +153 -0
  19. aip_agents/a2ui/public_url.py +138 -0
  20. aip_agents/a2ui/stream_filter.py +67 -0
  21. aip_agents/agent/__init__.py +72 -0
  22. aip_agents/agent/base_agent.py +1437 -0
  23. aip_agents/agent/base_langgraph_agent.py +4048 -0
  24. aip_agents/agent/google_adk_agent.py +932 -0
  25. aip_agents/agent/google_adk_constants.py +11 -0
  26. aip_agents/agent/graph_runtime.py +185 -0
  27. aip_agents/agent/hitl/__init__.py +24 -0
  28. aip_agents/agent/hitl/config.py +28 -0
  29. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  30. aip_agents/agent/hitl/manager.py +532 -0
  31. aip_agents/agent/hitl/models.py +18 -0
  32. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  33. aip_agents/agent/hitl/prompt/base.py +42 -0
  34. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  35. aip_agents/agent/hitl/registry.py +149 -0
  36. aip_agents/agent/interface.py +138 -0
  37. aip_agents/agent/interfaces.py +65 -0
  38. aip_agents/agent/langflow_agent.py +464 -0
  39. aip_agents/agent/langgraph_react_agent.py +3741 -0
  40. aip_agents/agent/model_invocation_adapter.py +346 -0
  41. aip_agents/agent/system_instruction_context.py +34 -0
  42. aip_agents/agent/tool_execution_engine.py +587 -0
  43. aip_agents/audio_interface/__init__.py +30 -0
  44. aip_agents/audio_interface/audio_agent_adapter.py +254 -0
  45. aip_agents/audio_interface/config.py +146 -0
  46. aip_agents/audio_interface/errors.py +15 -0
  47. aip_agents/audio_interface/livekit_audio_session.py +504 -0
  48. aip_agents/audio_interface/livekit_realtime_audio_session.py +142 -0
  49. aip_agents/audio_interface/session_factory.py +40 -0
  50. aip_agents/clients/__init__.py +10 -0
  51. aip_agents/clients/langflow/__init__.py +10 -0
  52. aip_agents/clients/langflow/client.py +477 -0
  53. aip_agents/clients/langflow/types.py +18 -0
  54. aip_agents/common/__init__.py +5 -0
  55. aip_agents/common/callable_introspection.py +17 -0
  56. aip_agents/constants.py +23 -0
  57. aip_agents/context/__init__.py +5 -0
  58. aip_agents/context/budget.py +237 -0
  59. aip_agents/context/compaction.py +339 -0
  60. aip_agents/context/metadata.py +169 -0
  61. aip_agents/context/offload.py +449 -0
  62. aip_agents/context/preparation.py +196 -0
  63. aip_agents/context/prompt.py +61 -0
  64. aip_agents/context/runtime.py +149 -0
  65. aip_agents/context/session_recall.py +69 -0
  66. aip_agents/context/state.py +48 -0
  67. aip_agents/context/utils.py +44 -0
  68. aip_agents/credentials/manager.py +132 -0
  69. aip_agents/examples/__init__.py +5 -0
  70. aip_agents/examples/audio_livekit_session.py +116 -0
  71. aip_agents/examples/compare_streaming_client.py +783 -0
  72. aip_agents/examples/compare_streaming_server.py +141 -0
  73. aip_agents/examples/gl_connectors_smoke.py +148 -0
  74. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  75. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_server.py +78 -0
  78. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  79. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  80. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  81. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  82. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  83. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +104 -0
  84. aip_agents/examples/hello_world_a2a_langchain_server.py +78 -0
  85. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +77 -0
  86. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  87. aip_agents/examples/hello_world_a2a_langflow_server.py +80 -0
  88. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  89. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  90. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +91 -0
  91. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  92. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  93. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  94. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  95. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  96. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  97. aip_agents/examples/hello_world_a2a_langgraph_server.py +83 -0
  98. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +78 -0
  99. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +131 -0
  100. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  101. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  102. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +250 -0
  103. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  104. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +79 -0
  105. aip_agents/examples/hello_world_filesystem.py +162 -0
  106. aip_agents/examples/hello_world_google_adk.py +41 -0
  107. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  108. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  109. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  110. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  111. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  112. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  113. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  114. aip_agents/examples/hello_world_langchain.py +28 -0
  115. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  116. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  117. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  118. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  119. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  120. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  121. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  122. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  123. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  124. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  125. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  126. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  127. aip_agents/examples/hello_world_langgraph.py +39 -0
  128. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  129. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  130. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  131. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  132. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  133. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  134. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  135. aip_agents/examples/hello_world_langgraph_skills.py +62 -0
  136. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  137. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  138. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  139. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  140. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  141. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  142. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  143. aip_agents/examples/hello_world_multi_agent_tool_output_client.py +57 -0
  144. aip_agents/examples/hello_world_multi_agent_tool_output_server.py +131 -0
  145. aip_agents/examples/hello_world_pii_logger.py +21 -0
  146. aip_agents/examples/hello_world_ptc.py +49 -0
  147. aip_agents/examples/hello_world_ptc_custom_tools.py +83 -0
  148. aip_agents/examples/hello_world_sentry.py +133 -0
  149. aip_agents/examples/hello_world_step_limits.py +273 -0
  150. aip_agents/examples/hello_world_stock_a2a_server.py +100 -0
  151. aip_agents/examples/hello_world_tool_output_client.py +55 -0
  152. aip_agents/examples/hello_world_tool_output_server.py +113 -0
  153. aip_agents/examples/hitl_demo.py +724 -0
  154. aip_agents/examples/mcp_configs/configs.py +63 -0
  155. aip_agents/examples/mcp_servers/common.py +76 -0
  156. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  157. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  158. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  159. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  160. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  161. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  162. aip_agents/examples/pii_demo_langgraph_server.py +125 -0
  163. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  164. aip_agents/examples/pii_demo_multi_agent_server.py +246 -0
  165. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  166. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +87 -0
  167. aip_agents/examples/tools/__init__.py +27 -0
  168. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  169. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  170. aip_agents/examples/tools/data_generator_tool.py +103 -0
  171. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  172. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  173. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  174. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  175. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  176. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  177. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  178. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  179. aip_agents/examples/tools/multiply_tool.py +43 -0
  180. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  181. aip_agents/examples/tools/random_chart_tool.py +142 -0
  182. aip_agents/examples/tools/serper_tool.py +202 -0
  183. aip_agents/examples/tools/stock_tools.py +82 -0
  184. aip_agents/examples/tools/table_generator_tool.py +167 -0
  185. aip_agents/examples/tools/time_tool.py +82 -0
  186. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  187. aip_agents/guardrails/__init__.py +83 -0
  188. aip_agents/guardrails/engines/__init__.py +69 -0
  189. aip_agents/guardrails/engines/base.py +90 -0
  190. aip_agents/guardrails/engines/nemo.py +101 -0
  191. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  192. aip_agents/guardrails/exceptions.py +39 -0
  193. aip_agents/guardrails/manager.py +163 -0
  194. aip_agents/guardrails/middleware.py +199 -0
  195. aip_agents/guardrails/schemas.py +63 -0
  196. aip_agents/guardrails/utils.py +45 -0
  197. aip_agents/integration/__init__.py +43 -0
  198. aip_agents/integration/agent.py +58 -0
  199. aip_agents/integration/guardrails.py +52 -0
  200. aip_agents/integration/hitl.py +71 -0
  201. aip_agents/integration/ptc.py +95 -0
  202. aip_agents/integration/skills.py +37 -0
  203. aip_agents/integration/storage.py +81 -0
  204. aip_agents/integration/version.py +103 -0
  205. aip_agents/mcp/__init__.py +1 -0
  206. aip_agents/mcp/client/__init__.py +60 -0
  207. aip_agents/mcp/client/base_mcp_client.py +377 -0
  208. aip_agents/mcp/client/connection_manager.py +231 -0
  209. aip_agents/mcp/client/errors.py +119 -0
  210. aip_agents/mcp/client/google_adk/__init__.py +45 -0
  211. aip_agents/mcp/client/google_adk/client.py +383 -0
  212. aip_agents/mcp/client/langchain/__init__.py +11 -0
  213. aip_agents/mcp/client/langchain/client.py +265 -0
  214. aip_agents/mcp/client/persistent_session.py +704 -0
  215. aip_agents/mcp/client/session_pool.py +358 -0
  216. aip_agents/mcp/client/transports.py +288 -0
  217. aip_agents/mcp/utils/__init__.py +7 -0
  218. aip_agents/mcp/utils/config_validator.py +139 -0
  219. aip_agents/memory/__init__.py +14 -0
  220. aip_agents/memory/adapters/__init__.py +10 -0
  221. aip_agents/memory/adapters/base_adapter.py +815 -0
  222. aip_agents/memory/adapters/mem0.py +286 -0
  223. aip_agents/memory/base.py +84 -0
  224. aip_agents/memory/constants.py +49 -0
  225. aip_agents/memory/factory.py +86 -0
  226. aip_agents/memory/guidance.py +20 -0
  227. aip_agents/memory/simple_memory.py +47 -0
  228. aip_agents/middleware/__init__.py +65 -0
  229. aip_agents/middleware/backends/__init__.py +72 -0
  230. aip_agents/middleware/backends/in_memory.py +578 -0
  231. aip_agents/middleware/backends/local_disk.py +1203 -0
  232. aip_agents/middleware/backends/protocol.py +353 -0
  233. aip_agents/middleware/backends/sandbox.py +1431 -0
  234. aip_agents/middleware/backends/sandbox_files.py +113 -0
  235. aip_agents/middleware/backends/utils.py +293 -0
  236. aip_agents/middleware/base.py +239 -0
  237. aip_agents/middleware/filesystem.py +690 -0
  238. aip_agents/middleware/manager.py +291 -0
  239. aip_agents/middleware/memory.py +532 -0
  240. aip_agents/middleware/pii_middleware.py +192 -0
  241. aip_agents/middleware/schema.py +19 -0
  242. aip_agents/middleware/skills.py +872 -0
  243. aip_agents/middleware/task_delegation.py +1656 -0
  244. aip_agents/middleware/todolist.py +276 -0
  245. aip_agents/middleware/tools/__init__.py +23 -0
  246. aip_agents/middleware/tools/edit_file.py +70 -0
  247. aip_agents/middleware/tools/execute.py +428 -0
  248. aip_agents/middleware/tools/grep_file.py +79 -0
  249. aip_agents/middleware/tools/ls.py +71 -0
  250. aip_agents/middleware/tools/read_file.py +239 -0
  251. aip_agents/middleware/tools/skill_load.py +193 -0
  252. aip_agents/middleware/tools/skill_resource.py +338 -0
  253. aip_agents/middleware/tools/write_file.py +114 -0
  254. aip_agents/middleware/vision.py +322 -0
  255. aip_agents/ptc/__init__.py +87 -0
  256. aip_agents/ptc/custom_tools.py +511 -0
  257. aip_agents/ptc/custom_tools_payload.py +499 -0
  258. aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
  259. aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +24 -0
  260. aip_agents/ptc/custom_tools_templates/custom_init.py.template +26 -0
  261. aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +149 -0
  262. aip_agents/ptc/custom_tools_templates/custom_registry.py.template +112 -0
  263. aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
  264. aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +87 -0
  265. aip_agents/ptc/doc_gen.py +122 -0
  266. aip_agents/ptc/exceptions.py +57 -0
  267. aip_agents/ptc/executor.py +627 -0
  268. aip_agents/ptc/mcp/__init__.py +45 -0
  269. aip_agents/ptc/mcp/sandbox_bridge.py +679 -0
  270. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  271. aip_agents/ptc/mcp/templates/mcp_client.py.template +240 -0
  272. aip_agents/ptc/naming.py +430 -0
  273. aip_agents/ptc/payload.py +26 -0
  274. aip_agents/ptc/prompt_builder.py +674 -0
  275. aip_agents/ptc/ptc_helper.py +16 -0
  276. aip_agents/ptc/sandbox_bridge.py +297 -0
  277. aip_agents/ptc/template_utils.py +33 -0
  278. aip_agents/ptc/templates/__init__.py +1 -0
  279. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  280. aip_agents/ptc/tool_def_helpers.py +101 -0
  281. aip_agents/ptc/tool_enrichment.py +163 -0
  282. aip_agents/resilience/__init__.py +10 -0
  283. aip_agents/resilience/base.py +46 -0
  284. aip_agents/resilience/tool.py +1661 -0
  285. aip_agents/sandbox/__init__.py +62 -0
  286. aip_agents/sandbox/defaults.py +220 -0
  287. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1-datasaur.Dockerfile +120 -0
  288. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1.Dockerfile +100 -0
  289. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2-datasaur.Dockerfile +143 -0
  290. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2.Dockerfile +119 -0
  291. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3-datasaur.Dockerfile +173 -0
  292. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3.Dockerfile +130 -0
  293. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.Dockerfile +164 -0
  294. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.patch_js_context.py +41 -0
  295. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.requirements.txt +29 -0
  296. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2.Dockerfile +87 -0
  297. aip_agents/sandbox/e2b_runtime.py +27 -0
  298. aip_agents/sandbox/image_registry.py +98 -0
  299. aip_agents/sandbox/providers/__init__.py +76 -0
  300. aip_agents/sandbox/providers/base.py +392 -0
  301. aip_agents/sandbox/providers/bedrock.py +28 -0
  302. aip_agents/sandbox/providers/e2b.py +230 -0
  303. aip_agents/sandbox/providers/opensandbox.py +163 -0
  304. aip_agents/sandbox/sandbox_runtime.py +967 -0
  305. aip_agents/sandbox/template_builder.py +240 -0
  306. aip_agents/sandbox/types.py +141 -0
  307. aip_agents/sandbox/validation.py +50 -0
  308. aip_agents/schema/__init__.py +106 -0
  309. aip_agents/schema/a2a.py +58 -0
  310. aip_agents/schema/agent.py +117 -0
  311. aip_agents/schema/artifacts.py +75 -0
  312. aip_agents/schema/attachments.py +425 -0
  313. aip_agents/schema/hitl.py +157 -0
  314. aip_agents/schema/langgraph.py +47 -0
  315. aip_agents/schema/model_id.py +103 -0
  316. aip_agents/schema/model_usage.py +50 -0
  317. aip_agents/schema/runtime_metadata.py +78 -0
  318. aip_agents/schema/step_limit.py +108 -0
  319. aip_agents/schema/storage.py +40 -0
  320. aip_agents/schema/tool_payloads.py +68 -0
  321. aip_agents/sentry/__init__.py +11 -0
  322. aip_agents/sentry/sentry.py +267 -0
  323. aip_agents/skills/__init__.py +30 -0
  324. aip_agents/skills/errors.py +110 -0
  325. aip_agents/skills/installer.py +479 -0
  326. aip_agents/skills/models.py +769 -0
  327. aip_agents/skills/stager.py +224 -0
  328. aip_agents/skills/validation.py +53 -0
  329. aip_agents/storage/__init__.py +41 -0
  330. aip_agents/storage/base.py +85 -0
  331. aip_agents/storage/clients/__init__.py +12 -0
  332. aip_agents/storage/clients/minio_client.py +318 -0
  333. aip_agents/storage/config.py +62 -0
  334. aip_agents/storage/providers/__init__.py +15 -0
  335. aip_agents/storage/providers/base.py +106 -0
  336. aip_agents/storage/providers/memory.py +114 -0
  337. aip_agents/storage/providers/object_storage.py +214 -0
  338. aip_agents/tools/__init__.py +65 -0
  339. aip_agents/tools/agent_schedule_tool.py +392 -0
  340. aip_agents/tools/browser_use/__init__.py +83 -0
  341. aip_agents/tools/browser_use/action_parser.py +104 -0
  342. aip_agents/tools/browser_use/browser_use_tool.py +1118 -0
  343. aip_agents/tools/browser_use/llm_config.py +120 -0
  344. aip_agents/tools/browser_use/minio_storage.py +203 -0
  345. aip_agents/tools/browser_use/schemas.py +121 -0
  346. aip_agents/tools/browser_use/session.py +13 -0
  347. aip_agents/tools/browser_use/session_errors.py +132 -0
  348. aip_agents/tools/browser_use/steel_session_recording.py +321 -0
  349. aip_agents/tools/browser_use/streaming.py +263 -0
  350. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  351. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  352. aip_agents/tools/browser_use/types.py +37 -0
  353. aip_agents/tools/code_sandbox/__init__.py +48 -0
  354. aip_agents/tools/code_sandbox/code_sandbox_tool.py +350 -0
  355. aip_agents/tools/code_sandbox/constant.py +13 -0
  356. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +346 -0
  357. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +65 -0
  358. aip_agents/tools/computer_use/__init__.py +75 -0
  359. aip_agents/tools/computer_use/computer_use_tool.py +910 -0
  360. aip_agents/tools/computer_use/schemas.py +78 -0
  361. aip_agents/tools/computer_use/streaming.py +198 -0
  362. aip_agents/tools/computer_use/types.py +32 -0
  363. aip_agents/tools/constants.py +177 -0
  364. aip_agents/tools/date_range_tool.py +554 -0
  365. aip_agents/tools/document_loader/__init__.py +44 -0
  366. aip_agents/tools/document_loader/base_reader.py +302 -0
  367. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  368. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  369. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  370. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  371. aip_agents/tools/execute_ptc_code.py +582 -0
  372. aip_agents/tools/gl_connector/__init__.py +5 -0
  373. aip_agents/tools/gl_connector/tool.py +518 -0
  374. aip_agents/tools/gl_connector_tools.py +170 -0
  375. aip_agents/tools/memory_search/__init__.py +29 -0
  376. aip_agents/tools/memory_search/base.py +213 -0
  377. aip_agents/tools/memory_search/mem0.py +424 -0
  378. aip_agents/tools/memory_search/schema.py +81 -0
  379. aip_agents/tools/memory_search_tool.py +34 -0
  380. aip_agents/tools/streaming_common.py +577 -0
  381. aip_agents/tools/time_tool.py +117 -0
  382. aip_agents/tools/tool_config_injector.py +300 -0
  383. aip_agents/tools/web_search/__init__.py +15 -0
  384. aip_agents/tools/web_search/serper_tool.py +187 -0
  385. aip_agents/types/__init__.py +73 -0
  386. aip_agents/types/a2a_events.py +13 -0
  387. aip_agents/utils/__init__.py +91 -0
  388. aip_agents/utils/a2a_connector.py +1758 -0
  389. aip_agents/utils/artifact_helpers.py +542 -0
  390. aip_agents/utils/artifacts/__init__.py +20 -0
  391. aip_agents/utils/artifacts/tracker.py +367 -0
  392. aip_agents/utils/attachment_helpers.py +178 -0
  393. aip_agents/utils/constants.py +22 -0
  394. aip_agents/utils/datetime/__init__.py +34 -0
  395. aip_agents/utils/datetime/normalization.py +231 -0
  396. aip_agents/utils/datetime/timezone.py +206 -0
  397. aip_agents/utils/env_loader.py +27 -0
  398. aip_agents/utils/event_handler_registry.py +58 -0
  399. aip_agents/utils/file_prompt_utils.py +276 -0
  400. aip_agents/utils/file_watchers/__init__.py +33 -0
  401. aip_agents/utils/file_watchers/base.py +46 -0
  402. aip_agents/utils/file_watchers/e2b.py +128 -0
  403. aip_agents/utils/file_watchers/factory.py +39 -0
  404. aip_agents/utils/file_watchers/ls_diff.py +88 -0
  405. aip_agents/utils/file_watchers/sandbox.py +66 -0
  406. aip_agents/utils/file_watchers/watchdog.py +97 -0
  407. aip_agents/utils/final_response_builder.py +211 -0
  408. aip_agents/utils/formatter_llm_client.py +258 -0
  409. aip_agents/utils/langgraph/__init__.py +19 -0
  410. aip_agents/utils/langgraph/converter.py +399 -0
  411. aip_agents/utils/langgraph/delegation_context.py +22 -0
  412. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  413. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  414. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  415. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1329 -0
  416. aip_agents/utils/langgraph/tool_output_management.py +1047 -0
  417. aip_agents/utils/logger.py +195 -0
  418. aip_agents/utils/metadata/__init__.py +27 -0
  419. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  420. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  421. aip_agents/utils/metadata/activity_narrative/builder.py +822 -0
  422. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  423. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  424. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  425. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  426. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  427. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  428. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  429. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  430. aip_agents/utils/metadata_helper.py +363 -0
  431. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  432. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  433. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  434. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  435. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  436. aip_agents/utils/pii/__init__.py +29 -0
  437. aip_agents/utils/pii/pii_handler.py +555 -0
  438. aip_agents/utils/pii/pii_helper.py +225 -0
  439. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  440. aip_agents/utils/reference_helper.py +273 -0
  441. aip_agents/utils/sse_chunk_transformer.py +836 -0
  442. aip_agents/utils/step_limit_manager.py +265 -0
  443. aip_agents/utils/token_usage_helper.py +531 -0
  444. aip_agents_binary-0.0.0.dist-info/METADATA +825 -0
  445. aip_agents_binary-0.0.0.dist-info/RECORD +447 -0
  446. aip_agents_binary-0.0.0.dist-info/WHEEL +5 -0
  447. aip_agents_binary-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,214 @@
1
+ """Object storage provider implementation.
2
+
3
+ Authors:
4
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
5
+ Fachriza Adhiatma (fachriza.d.adhiatma@gdplabs.id)
6
+ """
7
+
8
+ import json
9
+ import pickle
10
+ from typing import Any
11
+
12
+ from aip_agents.storage.base import BaseObjectStorageClient
13
+ from aip_agents.storage.providers.base import BaseStorageProvider, StorageError
14
+ from aip_agents.utils.logger import get_logger
15
+
16
+ logger = get_logger(__name__)
17
+
18
+
19
+ class ObjectStorageProvider(BaseStorageProvider):
20
+ """Object storage provider for S3-compatible storage.
21
+
22
+ Works with any S3-compatible storage including AWS S3, MinIO,
23
+ Google Cloud Storage (with S3 compatibility), etc.
24
+
25
+ Best for:
26
+ - Very large datasets
27
+ - Distributed systems
28
+ - Cloud deployments
29
+ - Long-term storage
30
+ - Multi-region access
31
+ """
32
+
33
+ def __init__(self, client: BaseObjectStorageClient, prefix: str = "", use_json: bool = False):
34
+ """Initialize object storage provider.
35
+
36
+ Args:
37
+ client: Object storage client instance
38
+ prefix: Prefix for all keys (like a directory)
39
+ use_json: Use JSON format (True) or pickle (False)
40
+ """
41
+ self.client = client
42
+ self.prefix = prefix.rstrip("/") + "/" if prefix else ""
43
+ self.use_json = use_json
44
+
45
+ def _get_object_key(self, key: str) -> str:
46
+ """Get full object key with prefix.
47
+
48
+ Args:
49
+ key: Storage key
50
+
51
+ Returns:
52
+ Full object key with prefix
53
+ """
54
+ return f"{self.prefix}{key}"
55
+
56
+ def _serialize_data(self, data: Any) -> bytes:
57
+ """Serialize data to bytes.
58
+
59
+ Args:
60
+ data: Data to serialize
61
+
62
+ Returns:
63
+ Serialized data as bytes
64
+
65
+ Raises:
66
+ StorageError: If serialization fails
67
+ """
68
+ try:
69
+ if self.use_json:
70
+ return json.dumps(data, default=str).encode("utf-8")
71
+ return pickle.dumps(data)
72
+ except Exception as e:
73
+ raise StorageError(f"Failed to serialize data: {e}") from e
74
+
75
+ def _deserialize_data(self, data_bytes: bytes) -> Any:
76
+ """Deserialize data from bytes.
77
+
78
+ Args:
79
+ data_bytes: Serialized data
80
+
81
+ Returns:
82
+ Deserialized data
83
+
84
+ Raises:
85
+ StorageError: If deserialization fails
86
+ """
87
+ try:
88
+ if self.use_json:
89
+ return json.loads(data_bytes.decode("utf-8"))
90
+ return pickle.loads(data_bytes)
91
+ except Exception as e:
92
+ raise StorageError(f"Failed to deserialize data: {e}") from e
93
+
94
+ def store(self, key: str, data: Any) -> None:
95
+ """Store data in object storage.
96
+
97
+ Args:
98
+ key: Unique identifier for the data
99
+ data: Data to store
100
+
101
+ Raises:
102
+ StorageError: If storage operation fails
103
+ """
104
+ object_key = self._get_object_key(key)
105
+
106
+ try:
107
+ data_bytes = self._serialize_data(data)
108
+
109
+ self.client.upload(
110
+ object_key=object_key,
111
+ file_stream=data_bytes,
112
+ content_type="application/json" if self.use_json else "application/octet-stream",
113
+ metadata={"storage_key": key, "format": "json" if self.use_json else "pickle"},
114
+ )
115
+
116
+ logger.debug(f"Stored data to object storage: {object_key}")
117
+
118
+ except Exception as e:
119
+ raise StorageError(f"Failed to store data in object storage: {e}") from e
120
+
121
+ def retrieve(self, key: str) -> Any:
122
+ """Retrieve data from object storage.
123
+
124
+ Args:
125
+ key: Unique identifier for the data
126
+
127
+ Returns:
128
+ The stored data
129
+
130
+ Raises:
131
+ KeyError: If key not found
132
+ StorageError: If retrieval operation fails
133
+ """
134
+ object_key = self._get_object_key(key)
135
+
136
+ try:
137
+ data_bytes = self.client.get(object_key)
138
+ return self._deserialize_data(data_bytes)
139
+
140
+ except KeyError as e:
141
+ raise KeyError(f"Key '{key}' not found in object storage: {e}") from e
142
+ except Exception as e:
143
+ raise StorageError(f"Failed to retrieve data from object storage: {e}") from e
144
+
145
+ def exists(self, key: str) -> bool:
146
+ """Check if object exists.
147
+
148
+ Args:
149
+ key: Unique identifier to check
150
+
151
+ Returns:
152
+ True if key exists, False otherwise
153
+ """
154
+ object_key = self._get_object_key(key)
155
+
156
+ try:
157
+ return self.client.object_exists(object_key)
158
+ except Exception as e:
159
+ logger.warning(f"Failed to check object existence: {e}")
160
+ return False
161
+
162
+ def delete(self, key: str) -> None:
163
+ """Delete object.
164
+
165
+ Args:
166
+ key: Unique identifier for the data
167
+ """
168
+ object_key = self._get_object_key(key)
169
+
170
+ try:
171
+ self.client.delete(object_key)
172
+ logger.debug(f"Deleted object: {object_key}")
173
+ except Exception as e:
174
+ logger.warning(f"Failed to delete object {object_key}: {e}")
175
+
176
+ def list_keys(self, prefix: str = "") -> list[str]:
177
+ """List all keys with optional prefix.
178
+
179
+ Args:
180
+ prefix: Optional prefix to filter keys
181
+
182
+ Raises:
183
+ NotImplementedError: list_keys is not implemented
184
+ """
185
+ raise NotImplementedError("list_keys not yet implemented for object storage")
186
+
187
+ def clear(self) -> None:
188
+ """Clear all objects with the configured prefix.
189
+
190
+ Warning:
191
+ This is a dangerous operation!
192
+ """
193
+ logger.warning("clear() not implemented for object storage - too dangerous")
194
+ raise NotImplementedError("Batch clear not implemented for safety reasons")
195
+
196
+ def get_presigned_url(self, key: str, expires_hours: int = 24) -> str | None:
197
+ """Generate presigned URL for direct access.
198
+
199
+ Args:
200
+ key: Storage key
201
+ expires_hours: URL expiration in hours
202
+
203
+ Returns:
204
+ Presigned URL for direct access
205
+
206
+ Raises:
207
+ StorageError: If URL generation fails
208
+ """
209
+ object_key = self._get_object_key(key)
210
+
211
+ try:
212
+ return self.client.generate_presigned_url(object_key=object_key, expires=expires_hours)
213
+ except Exception as e:
214
+ raise StorageError(f"Failed to generate presigned URL: {e}") from e
@@ -0,0 +1,65 @@
1
+ """Initialize Tools Module for AIP Agents."""
2
+
3
+ from importlib import import_module
4
+ from typing import TYPE_CHECKING
5
+
6
+ from aip_agents.tools.date_range_tool import DateRangeTool
7
+ from aip_agents.tools.gl_connector import GLConnectorTool
8
+ from aip_agents.tools.gl_connector_tools import (
9
+ BOSA_AUTOMATED_TOOLS,
10
+ GL_CONNECTORS_AUTOMATED_TOOLS,
11
+ )
12
+ from aip_agents.tools.time_tool import TimeTool
13
+ from aip_agents.tools.web_search import GoogleSerperTool
14
+ from aip_agents.utils.logger import get_logger
15
+
16
+ logger = get_logger(__name__)
17
+
18
+ __all__ = [
19
+ "BOSA_AUTOMATED_TOOLS",
20
+ "GL_CONNECTORS_AUTOMATED_TOOLS",
21
+ "GLConnectorTool",
22
+ "GoogleSerperTool",
23
+ "TimeTool",
24
+ "DateRangeTool",
25
+ ]
26
+
27
+
28
+ def _register_optional(module_path: str, export_name: str) -> None:
29
+ """Try to import optional tool module and expose it in __all__.
30
+
31
+ Args:
32
+ module_path: The module path to import.
33
+ export_name: The name to export in __all__.
34
+ """
35
+ try:
36
+ module = import_module(module_path)
37
+ tool = getattr(module, export_name)
38
+ __all__.append(export_name)
39
+ globals()[export_name] = tool
40
+
41
+ except ImportError as e:
42
+ logger.debug(f"Module {module_path} not found: {e}")
43
+ except AttributeError as e:
44
+ logger.debug(f"Tool {export_name} not found in module {module_path}: {e}")
45
+ except Exception as e:
46
+ logger.debug(f"Unexpected error loading {export_name}: {e}")
47
+
48
+
49
+ _register_optional("aip_agents.tools.browser_use", "BrowserUseTool")
50
+ _register_optional("aip_agents.tools.code_sandbox", "CodeSandboxTool")
51
+ _register_optional("aip_agents.tools.code_sandbox", "E2BCodeSandboxTool")
52
+ _register_optional("aip_agents.tools.document_loader", "DocxReaderTool")
53
+ _register_optional("aip_agents.tools.document_loader", "ExcelReaderTool")
54
+ _register_optional("aip_agents.tools.document_loader", "PDFReaderTool")
55
+ _register_optional("aip_agents.tools.execute_ptc_code", "create_execute_ptc_code_tool")
56
+
57
+ if TYPE_CHECKING:
58
+ from aip_agents.tools.browser_use import BrowserUseTool
59
+ from aip_agents.tools.code_sandbox import CodeSandboxTool, E2BCodeSandboxTool
60
+ from aip_agents.tools.document_loader import (
61
+ DocxReaderTool,
62
+ ExcelReaderTool,
63
+ PDFReaderTool,
64
+ )
65
+ from aip_agents.tools.execute_ptc_code import create_execute_ptc_code_tool
@@ -0,0 +1,392 @@
1
+ """Agent-bound schedule CRUD native tool.
2
+
3
+ Authors:
4
+ Saul Sayers (saul.sayers@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import re
11
+ from typing import Any, Literal
12
+
13
+ import httpx
14
+ from langchain_core.runnables import RunnableConfig
15
+ from langchain_core.tools import BaseTool
16
+ from pydantic import BaseModel, Field, SecretStr
17
+
18
+
19
+ class AgentScheduleToolConfig(BaseModel):
20
+ """Runtime configuration for AgentScheduleNativeTool."""
21
+
22
+ api_base_url: str = Field(description="Base URL for AIP backend API")
23
+ api_key: SecretStr = Field(description="API key used for schedule endpoint calls")
24
+ request_timeout_seconds: int = Field(default=30, ge=1, le=120)
25
+
26
+
27
+ class AgentScheduleToolInput(BaseModel):
28
+ """Action-based input for schedule CRUD operations."""
29
+
30
+ action: Literal["create", "list", "update", "delete"] = Field(description="CRUD action to perform")
31
+ schedule_id: str | None = Field(default=None)
32
+ input: str | None = Field(default=None)
33
+ schedule: dict[str, Any] | None = Field(default=None)
34
+ page: int | None = Field(default=None)
35
+ limit: int | None = Field(default=None)
36
+
37
+ class Config:
38
+ """Reject unknown fields like agent_id/account_id in payload."""
39
+
40
+ extra = "forbid"
41
+
42
+
43
+ class AgentScheduleNativeTool(BaseTool):
44
+ """Native tool for schedule CRUD scoped to trusted bound agent."""
45
+
46
+ name: str = "agent_schedule_native_tool"
47
+ description: str = (
48
+ "Create, list, update, or delete schedules for the current bound agent only. "
49
+ "Identity is resolved from trusted runtime metadata."
50
+ )
51
+ args_schema: type[BaseModel] = AgentScheduleToolInput
52
+ tool_config_schema: type[BaseModel] = AgentScheduleToolConfig
53
+
54
+ def _run(
55
+ self,
56
+ action: str,
57
+ schedule_id: str | None = None,
58
+ input: str | None = None,
59
+ schedule: dict[str, Any] | None = None,
60
+ page: int | None = None,
61
+ limit: int | None = None,
62
+ metadata: dict[str, Any] | None = None,
63
+ config: RunnableConfig | None = None,
64
+ ) -> dict[str, Any]:
65
+ """Execute schedule CRUD action with bound-agent safeguards."""
66
+ effective_metadata = self._resolve_effective_metadata(config, metadata)
67
+ validation_error = self._validate_action_inputs(action, schedule_id, input, schedule, page, limit)
68
+ if validation_error:
69
+ return self._error(action, validation_error)
70
+
71
+ runtime = self._resolve_runtime_config(config, effective_metadata)
72
+ if isinstance(runtime, dict) and runtime.get("error"):
73
+ return self._error(action, runtime["error"])
74
+
75
+ bound_agent_id = self._resolve_bound_agent_id(effective_metadata)
76
+ if not bound_agent_id:
77
+ return self._error(action, "bound_agent_id not found in runtime metadata")
78
+
79
+ action_handlers: dict[str, tuple[str, Any]] = {
80
+ "create": ("Schedule created", lambda: self._handle_create(bound_agent_id, input, schedule, runtime)),
81
+ "list": ("Schedules listed", lambda: self._handle_list(bound_agent_id, page, limit, runtime)),
82
+ "update": (
83
+ "Schedule updated",
84
+ lambda: self._handle_update(bound_agent_id, schedule_id, input, schedule, runtime),
85
+ ),
86
+ "delete": ("Schedule deleted", lambda: self._handle_delete(bound_agent_id, schedule_id, runtime)),
87
+ }
88
+
89
+ handler_entry = action_handlers.get(action)
90
+ if handler_entry is None:
91
+ return self._error(action, "Unsupported action") # pragma: no cover
92
+
93
+ success_message, handler = handler_entry
94
+ try:
95
+ data = handler()
96
+ if isinstance(data, dict) and data.get("_error"):
97
+ return self._error(action, data["_error"])
98
+ return self._success(action, success_message, data)
99
+ except RuntimeError as exc:
100
+ # Sanitize: extract only the part after the first ": " to avoid leaking raw API bodies
101
+ # into tool responses consumed by the LLM.
102
+ msg = str(exc)
103
+ safe_msg = msg.split(": ", 1)[-1] if ": " in msg else "Schedule action failed"
104
+ if " {" in safe_msg:
105
+ safe_msg = safe_msg.split(" {", 1)[0]
106
+ return self._error(action, safe_msg)
107
+ except Exception:
108
+ return self._error(action, "Schedule action failed")
109
+
110
+ async def _arun(
111
+ self,
112
+ action: str,
113
+ schedule_id: str | None = None,
114
+ input: str | None = None,
115
+ schedule: dict[str, Any] | None = None,
116
+ page: int | None = None,
117
+ limit: int | None = None,
118
+ metadata: dict[str, Any] | None = None,
119
+ config: RunnableConfig | None = None,
120
+ ) -> dict[str, Any]:
121
+ return await asyncio.to_thread(
122
+ self._run,
123
+ action,
124
+ schedule_id,
125
+ input,
126
+ schedule,
127
+ page,
128
+ limit,
129
+ metadata,
130
+ config,
131
+ )
132
+
133
+ def _handle_create(
134
+ self,
135
+ bound_agent_id: str,
136
+ input_value: str | None,
137
+ schedule: dict[str, Any] | None,
138
+ runtime: dict[str, Any],
139
+ ) -> dict[str, Any]:
140
+ path = f"/agents/{bound_agent_id}/schedule"
141
+ payload = {"input": input_value, "schedule": schedule}
142
+ return self._call_api("POST", path, runtime, payload)
143
+
144
+ def _handle_list(
145
+ self,
146
+ bound_agent_id: str,
147
+ page: int | None,
148
+ limit: int | None,
149
+ runtime: dict[str, Any],
150
+ ) -> dict[str, Any]:
151
+ query_params: dict[str, Any] = {"agent_id": bound_agent_id}
152
+ if page is not None:
153
+ query_params["page"] = page
154
+ if limit is not None:
155
+ query_params["limit"] = limit
156
+ return self._call_api("GET", "/agents/schedules", runtime, query_params=query_params)
157
+
158
+ def _check_schedule_ownership(
159
+ self,
160
+ bound_agent_id: str,
161
+ schedule_id: str | None,
162
+ runtime: dict[str, Any],
163
+ ) -> str | None:
164
+ detail = self._call_api("GET", f"/agents/schedules/{schedule_id}", runtime)
165
+ owner_id = self._extract_owner_id(detail)
166
+ if owner_id != bound_agent_id:
167
+ return "Forbidden: schedule does not belong to bound agent"
168
+ return None
169
+
170
+ def _handle_update(
171
+ self,
172
+ bound_agent_id: str,
173
+ schedule_id: str | None,
174
+ input_value: str | None,
175
+ schedule: dict[str, Any] | None,
176
+ runtime: dict[str, Any],
177
+ ) -> dict[str, Any]:
178
+ ownership_error = self._check_schedule_ownership(bound_agent_id, schedule_id, runtime)
179
+ if ownership_error:
180
+ return {"_error": ownership_error}
181
+ payload = {"input": input_value, "schedule": schedule}
182
+ return self._call_api("PUT", f"/agents/schedules/{schedule_id}", runtime, payload)
183
+
184
+ def _handle_delete(
185
+ self,
186
+ bound_agent_id: str,
187
+ schedule_id: str | None,
188
+ runtime: dict[str, Any],
189
+ ) -> dict[str, Any]:
190
+ ownership_error = self._check_schedule_ownership(bound_agent_id, schedule_id, runtime)
191
+ if ownership_error:
192
+ return {"_error": ownership_error}
193
+ return self._call_api("DELETE", f"/agents/schedules/{schedule_id}", runtime)
194
+
195
+ @staticmethod
196
+ def _resolve_effective_metadata(
197
+ config: RunnableConfig | None,
198
+ metadata: dict[str, Any] | None,
199
+ ) -> dict[str, Any]:
200
+ effective_metadata: dict[str, Any] = {}
201
+ if isinstance(config, dict):
202
+ config_metadata = config.get("metadata")
203
+ if isinstance(config_metadata, dict):
204
+ effective_metadata.update(config_metadata)
205
+ if isinstance(metadata, dict):
206
+ # These keys carry trusted runtime config values (from agent runner) and must not be overridden by untrusted metadata.
207
+ PROTECTED_KEYS = {
208
+ "bound_agent_id",
209
+ "api_base_url",
210
+ "api_key",
211
+ "aip_base_url",
212
+ "aip_api_key",
213
+ "request_timeout_seconds",
214
+ }
215
+ for key, value in metadata.items():
216
+ key_str = str(key)
217
+ if key_str in PROTECTED_KEYS and key_str in effective_metadata:
218
+ continue
219
+ effective_metadata[key_str] = value
220
+ return effective_metadata
221
+
222
+ @staticmethod
223
+ def _validate_action_inputs(
224
+ action: str,
225
+ schedule_id: str | None,
226
+ input_value: str | None,
227
+ schedule: dict[str, Any] | None,
228
+ page: int | None,
229
+ limit: int | None,
230
+ ) -> str | None:
231
+ valid_actions = {"create", "list", "update", "delete"}
232
+ if action not in valid_actions:
233
+ return f"Invalid action '{action}'"
234
+ return (
235
+ AgentScheduleNativeTool._validate_schedule_payload(action, input_value, schedule)
236
+ or AgentScheduleNativeTool._validate_schedule_id(action, schedule_id)
237
+ or AgentScheduleNativeTool._validate_pagination(action, page, limit)
238
+ )
239
+
240
+ @staticmethod
241
+ def _validate_schedule_payload(action: str, input_value: str | None, schedule: dict[str, Any] | None) -> str | None:
242
+ if action not in {"create", "update"}:
243
+ return None
244
+ if not input_value or not schedule:
245
+ return "'input' and 'schedule' are required for create/update" # pragma: no cover
246
+ required_schedule_fields = {"minute", "hour", "day_of_month", "month", "day_of_week"}
247
+ missing_fields = sorted(
248
+ field
249
+ for field in required_schedule_fields
250
+ if not isinstance(schedule.get(field), str) or not schedule.get(field)
251
+ )
252
+ if missing_fields:
253
+ return f"Incomplete schedule payload, missing fields: {', '.join(missing_fields)}"
254
+ return None
255
+
256
+ @staticmethod
257
+ def _validate_schedule_id(action: str, schedule_id: str | None) -> str | None:
258
+ if action not in {"update", "delete"}:
259
+ return None
260
+ if not schedule_id:
261
+ return "'schedule_id' is required for update/delete"
262
+ if not re.match(r"^[a-zA-Z0-9\-_]+$", schedule_id):
263
+ return "'schedule_id' contains invalid characters"
264
+ return None
265
+
266
+ @staticmethod
267
+ def _validate_pagination(action: str, page: int | None, limit: int | None) -> str | None:
268
+ if action != "list":
269
+ return None
270
+ if page is not None and page < 1:
271
+ return "'page' must be a positive integer"
272
+ if limit is not None and limit < 1:
273
+ return "'limit' must be a positive integer"
274
+ return None
275
+
276
+ def _resolve_runtime_config(
277
+ self,
278
+ config: RunnableConfig | None,
279
+ metadata: dict[str, Any] | None,
280
+ ) -> dict[str, Any]:
281
+ tool_cfg = None
282
+ get_tool_config = getattr(self, "get_tool_config", None)
283
+ if callable(get_tool_config):
284
+ tool_cfg = get_tool_config(config)
285
+
286
+ if tool_cfg is not None:
287
+ if hasattr(tool_cfg, "model_dump"):
288
+ dumped = tool_cfg.model_dump(exclude_none=True)
289
+ api_key = dumped.get("api_key")
290
+ if isinstance(api_key, SecretStr):
291
+ dumped["api_key"] = api_key.get_secret_value()
292
+ return self._normalize_runtime_config(
293
+ dumped.get("api_base_url"),
294
+ dumped.get("api_key"),
295
+ dumped.get("request_timeout_seconds", 30),
296
+ )
297
+ if isinstance(tool_cfg, dict):
298
+ api_key = tool_cfg.get("api_key")
299
+ if isinstance(api_key, SecretStr):
300
+ tool_cfg = {**tool_cfg, "api_key": api_key.get_secret_value()}
301
+ return self._normalize_runtime_config(
302
+ tool_cfg.get("api_base_url"),
303
+ tool_cfg.get("api_key"),
304
+ tool_cfg.get("request_timeout_seconds", 30),
305
+ )
306
+
307
+ flat = metadata or {}
308
+ api_base_url = flat.get("api_base_url") or flat.get("aip_base_url")
309
+ api_key = flat.get("api_key") or flat.get("aip_api_key")
310
+ timeout = flat.get("request_timeout_seconds", 30)
311
+ return self._normalize_runtime_config(api_base_url, api_key, timeout)
312
+
313
+ @staticmethod
314
+ def _normalize_runtime_config(
315
+ api_base_url: Any,
316
+ api_key: Any,
317
+ timeout: Any,
318
+ ) -> dict[str, Any]:
319
+ if not api_base_url or not api_key:
320
+ return {"error": "Missing tool runtime config: api_base_url/api_key"}
321
+
322
+ try:
323
+ timeout_seconds = int(timeout)
324
+ except (TypeError, ValueError):
325
+ return {"error": "Invalid tool runtime config: request_timeout_seconds must be an integer"}
326
+
327
+ if timeout_seconds < 1 or timeout_seconds > 120:
328
+ return {"error": "Invalid tool runtime config: request_timeout_seconds must be between 1 and 120"}
329
+
330
+ return {
331
+ "api_base_url": str(api_base_url),
332
+ "api_key": str(api_key),
333
+ "request_timeout_seconds": timeout_seconds,
334
+ }
335
+
336
+ @staticmethod
337
+ def _resolve_bound_agent_id(metadata: dict[str, Any] | None) -> str | None:
338
+ if not isinstance(metadata, dict):
339
+ return None # pragma: no cover
340
+ value = metadata.get("bound_agent_id")
341
+ return str(value) if value else None
342
+
343
+ @staticmethod
344
+ def _extract_owner_id(schedule_detail: dict[str, Any]) -> str | None:
345
+ metadata = schedule_detail.get("metadata") if isinstance(schedule_detail, dict) else None
346
+ if not isinstance(metadata, dict):
347
+ wrapped = schedule_detail.get("data") if isinstance(schedule_detail, dict) else None
348
+ metadata = wrapped.get("metadata") if isinstance(wrapped, dict) else None
349
+ if isinstance(metadata, dict):
350
+ raw = metadata.get("bound_agent_id")
351
+ if raw:
352
+ return str(raw)
353
+ return None
354
+
355
+ def _call_api(
356
+ self,
357
+ method: str,
358
+ path: str,
359
+ runtime: dict[str, Any],
360
+ payload: dict[str, Any] | None = None,
361
+ query_params: dict[str, Any] | None = None,
362
+ ) -> dict[str, Any]:
363
+ base_url = runtime["api_base_url"].rstrip("/")
364
+ url = f"{base_url}{path}"
365
+ headers = {
366
+ "X-API-Key": runtime["api_key"],
367
+ }
368
+ try:
369
+ with httpx.Client(timeout=runtime["request_timeout_seconds"]) as client:
370
+ resp = client.request(method, url, json=payload, params=query_params, headers=headers)
371
+ resp.raise_for_status()
372
+ if not resp.content:
373
+ return {}
374
+ try:
375
+ decoded = resp.json()
376
+ except ValueError as exc:
377
+ raise RuntimeError(f"Schedule API {method} {path} returned invalid JSON") from exc
378
+ if isinstance(decoded, dict):
379
+ return decoded
380
+ return {"items": decoded}
381
+ except httpx.HTTPStatusError as exc:
382
+ raise RuntimeError(f"Schedule API {method} {path} failed: {exc.response.status_code}") from exc
383
+ except httpx.RequestError as exc:
384
+ raise RuntimeError(f"Schedule API {method} {path} unreachable: {exc}") from exc
385
+
386
+ @staticmethod
387
+ def _success(action: str, message: str, data: dict[str, Any]) -> dict[str, Any]:
388
+ return {"success": True, "action": action, "message": message, "data": data}
389
+
390
+ @staticmethod
391
+ def _error(action: str, message: str) -> dict[str, Any]:
392
+ return {"success": False, "action": action, "message": message, "data": {}}