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,511 @@
1
+ """Custom LangChain tools configuration for PTC sandbox.
2
+
3
+ This module provides configuration classes for defining custom LangChain tools
4
+ that can be used inside the PTC sandbox alongside MCP tools.
5
+
6
+ Authors:
7
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import ast
13
+ from dataclasses import dataclass, field
14
+ from pathlib import Path
15
+ from typing import Literal, TypedDict
16
+
17
+ from aip_agents.ptc.exceptions import PTCError
18
+ from aip_agents.ptc.naming import (
19
+ is_valid_identifier,
20
+ project_custom_tool_signature,
21
+ sanitize_function_name,
22
+ )
23
+ from aip_agents.utils.logger import get_logger
24
+
25
+ logger = get_logger(__name__)
26
+
27
+
28
+ class PTCPackageToolDef(TypedDict, total=False):
29
+ """Definition for a package-based tool.
30
+
31
+ Required fields:
32
+ name: Tool name (used for imports and config lookup).
33
+ kind: Must be "package".
34
+ import_path: Dotted import path (e.g., "aip_agents.tools.time_tool").
35
+ class_name: Class name to import (e.g., "TimeTool").
36
+
37
+ Optional fields:
38
+ package_path: Path to source directory to bundle.
39
+ If omitted, the tool must already be available in sandbox (e.g., from site-packages).
40
+ description: Tool description (derived from tool object at construction time).
41
+ input_schema: JSON schema for tool input (derived from tool object at construction time).
42
+ """
43
+
44
+ name: str
45
+ kind: Literal["package"]
46
+ import_path: str
47
+ class_name: str
48
+ package_path: str # Optional
49
+ description: str # Optional - derived from tool object
50
+ input_schema: dict # Optional - derived from tool object
51
+
52
+
53
+ class PTCFileToolDef(TypedDict, total=False):
54
+ """Definition for a single-file tool.
55
+
56
+ Required fields:
57
+ name: Tool name (used for imports and config lookup).
58
+ kind: Must be "file".
59
+ file_path: Path to the Python file containing the tool.
60
+ class_name: Class name to import from the file.
61
+
62
+ Optional fields:
63
+ description: Tool description (derived from tool object at construction time).
64
+ input_schema: JSON schema for tool input (derived from tool object at construction time).
65
+ """
66
+
67
+ name: str
68
+ kind: Literal["file"]
69
+ file_path: str
70
+ class_name: str
71
+ description: str # Optional - derived from tool object
72
+ input_schema: dict # Optional - derived from tool object
73
+
74
+
75
+ # Union type for tool definitions
76
+ PTCToolDef = PTCPackageToolDef | PTCFileToolDef
77
+
78
+
79
+ class PTCCustomToolValidationError(PTCError):
80
+ """Error raised when custom tool configuration is invalid."""
81
+
82
+ pass
83
+
84
+
85
+ @dataclass
86
+ class PTCCustomToolConfig:
87
+ """Configuration for custom LangChain tools in PTC sandbox.
88
+
89
+ This config controls which user-defined LangChain tools are available
90
+ in the sandbox for use by PTC code.
91
+
92
+ Attributes:
93
+ enabled: Whether custom tools are enabled.
94
+ bundle_roots: List of allowed root directories for bundling tool sources.
95
+ Paths outside these roots will cause validation errors.
96
+ requirements: List of pip requirements to install in sandbox.
97
+ tools: List of tool definitions (package or file tools).
98
+
99
+ Example:
100
+ >>> config = PTCCustomToolConfig(
101
+ ... enabled=True,
102
+ ... bundle_roots=["/app/tools"],
103
+ ... requirements=["pydantic>=2.0"],
104
+ ... tools=[
105
+ ... {
106
+ ... "name": "time_tool",
107
+ ... "kind": "package",
108
+ ... "import_path": "aip_agents.tools.time_tool",
109
+ ... "class_name": "TimeTool",
110
+ ... },
111
+ ... ],
112
+ ... )
113
+ """
114
+
115
+ enabled: bool = False
116
+ bundle_roots: list[str] = field(default_factory=list)
117
+ requirements: list[str] = field(default_factory=list)
118
+ tools: list[PTCToolDef] = field(default_factory=list)
119
+
120
+
121
+ def _validate_class_name(class_name: str, tool_name: str) -> None:
122
+ """Validate a class name used in generated code.
123
+
124
+ Args:
125
+ class_name: Class name to validate.
126
+ tool_name: Tool name for error messages.
127
+
128
+ Raises:
129
+ PTCCustomToolValidationError: If class name is invalid.
130
+ """
131
+ if not is_valid_identifier(class_name):
132
+ raise PTCCustomToolValidationError(
133
+ f"Tool '{tool_name}' has invalid class_name '{class_name}'. Must be a valid Python identifier."
134
+ )
135
+
136
+
137
+ def _validate_package_tool(tool: PTCToolDef, name: str) -> None:
138
+ """Validate a package tool definition.
139
+
140
+ Args:
141
+ tool: Package tool definition to validate.
142
+ name: Tool name for error messages.
143
+
144
+ Raises:
145
+ PTCCustomToolValidationError: If package tool definition is invalid.
146
+ """
147
+ import_path = tool.get("import_path", "")
148
+ if not import_path:
149
+ raise PTCCustomToolValidationError(f"Package tool '{name}' missing required 'import_path' field")
150
+ if import_path.startswith("."):
151
+ raise PTCCustomToolValidationError(f"Package tool '{name}' import_path must be absolute: '{import_path}'")
152
+ if any(not part.isidentifier() for part in import_path.split(".")):
153
+ raise PTCCustomToolValidationError(f"Package tool '{name}' has invalid import_path: '{import_path}'")
154
+ class_name = tool.get("class_name", "")
155
+ if not class_name:
156
+ raise PTCCustomToolValidationError(f"Package tool '{name}' missing required 'class_name' field")
157
+ _validate_class_name(class_name, name)
158
+
159
+
160
+ def _validate_file_tool(tool: PTCToolDef, name: str) -> None:
161
+ """Validate a file tool definition.
162
+
163
+ Args:
164
+ tool: File tool definition to validate.
165
+ name: Tool name for error messages.
166
+
167
+ Raises:
168
+ PTCCustomToolValidationError: If file tool definition is invalid.
169
+ """
170
+ if not tool.get("file_path"):
171
+ raise PTCCustomToolValidationError(f"File tool '{name}' missing required 'file_path' field")
172
+ class_name = tool.get("class_name", "")
173
+ if not class_name:
174
+ raise PTCCustomToolValidationError(f"File tool '{name}' missing required 'class_name' field")
175
+ _validate_class_name(class_name, name)
176
+
177
+
178
+ def validate_tool_def(tool: PTCToolDef) -> None:
179
+ """Validate a tool definition has required fields.
180
+
181
+ Args:
182
+ tool: Tool definition to validate.
183
+
184
+ Raises:
185
+ PTCCustomToolValidationError: If tool definition is invalid.
186
+ """
187
+ name = tool.get("name")
188
+ kind = tool.get("kind")
189
+
190
+ if not name:
191
+ raise PTCCustomToolValidationError("Tool definition missing required 'name' field")
192
+
193
+ if kind not in ("package", "file"):
194
+ raise PTCCustomToolValidationError(f"Tool '{name}' has invalid kind '{kind}'. Must be 'package' or 'file'.")
195
+
196
+ if kind == "package":
197
+ _validate_package_tool(tool, name)
198
+ elif kind == "file":
199
+ _validate_file_tool(tool, name)
200
+
201
+
202
+ def _validate_input_schema(schema: dict, tool_name: str) -> None:
203
+ """Validate input_schema by projecting it into a canonical signature.
204
+
205
+ This provides fail-fast validation at config time for:
206
+ - Parameter sanitization collisions (e.g., 'user-id' and 'user_id' map to same name)
207
+ - Reserved name collisions (e.g., schema key sanitizing to 'kwargs')
208
+
209
+ Args:
210
+ schema: The input_schema JSON schema to validate.
211
+ tool_name: Tool name for error messages.
212
+
213
+ Raises:
214
+ PTCCustomToolValidationError: If input_schema has invalid parameter names.
215
+ """
216
+ try:
217
+ project_custom_tool_signature(schema)
218
+ except ValueError as e:
219
+ raise PTCCustomToolValidationError(f"Tool '{tool_name}' has invalid input_schema: {e}") from e
220
+
221
+
222
+ def validate_path_within_bundle_roots(
223
+ path: str | Path,
224
+ bundle_roots: list[str],
225
+ tool_name: str,
226
+ ) -> None:
227
+ """Validate that a path is within one of the bundle roots.
228
+
229
+ Args:
230
+ path: Path to validate.
231
+ bundle_roots: List of allowed root directories.
232
+ tool_name: Tool name for error messages.
233
+
234
+ Raises:
235
+ PTCCustomToolValidationError: If path is outside all bundle roots.
236
+ """
237
+ if not bundle_roots:
238
+ raise PTCCustomToolValidationError(
239
+ f"Tool '{tool_name}' requires bundle_roots to be set when using package_path or file_path"
240
+ )
241
+
242
+ # Resolve real path to prevent symlink escapes
243
+ real_path = Path(path).resolve()
244
+
245
+ for root in bundle_roots:
246
+ real_root = Path(root).resolve()
247
+ try:
248
+ real_path.relative_to(real_root)
249
+ return # Path is within this root
250
+ except ValueError:
251
+ continue
252
+
253
+ raise PTCCustomToolValidationError(
254
+ f"Tool '{tool_name}' path '{path}' is outside all bundle_roots. Allowed roots: {bundle_roots}"
255
+ )
256
+
257
+
258
+ def detect_relative_imports(file_path: str | Path) -> list[str]:
259
+ """Detect relative imports in a Python file using AST.
260
+
261
+ Args:
262
+ file_path: Path to the Python file to scan.
263
+
264
+ Returns:
265
+ List of relative import statements found.
266
+
267
+ Raises:
268
+ PTCCustomToolValidationError: If file cannot be read or parsed.
269
+ """
270
+ path = Path(file_path)
271
+ if not path.exists():
272
+ raise PTCCustomToolValidationError(f"File not found: {file_path}")
273
+
274
+ try:
275
+ source = path.read_text(encoding="utf-8")
276
+ except Exception as exc:
277
+ raise PTCCustomToolValidationError(f"Cannot read file '{file_path}': {exc}") from exc
278
+
279
+ try:
280
+ tree = ast.parse(source, filename=str(path))
281
+ except SyntaxError as exc:
282
+ raise PTCCustomToolValidationError(f"Syntax error in file '{file_path}': {exc}") from exc
283
+
284
+ relative_imports: list[str] = []
285
+ for node in ast.walk(tree):
286
+ if isinstance(node, ast.ImportFrom):
287
+ # level > 0 indicates relative import (e.g., from . import X, from ..foo import Y)
288
+ if node.level > 0:
289
+ module = node.module or ""
290
+ dots = "." * node.level
291
+ import_str = f"from {dots}{module} import ..."
292
+ relative_imports.append(import_str)
293
+
294
+ return relative_imports
295
+
296
+
297
+ def check_name_collisions(tools: list[PTCToolDef]) -> None:
298
+ """Check for tool name collisions after sanitization.
299
+
300
+ Args:
301
+ tools: List of tool definitions.
302
+
303
+ Raises:
304
+ PTCCustomToolValidationError: If two tools sanitize to the same name.
305
+ """
306
+ sanitized_to_original: dict[str, list[str]] = {}
307
+
308
+ for tool in tools:
309
+ name = tool.get("name", "")
310
+ sanitized = sanitize_function_name(name)
311
+
312
+ if sanitized not in sanitized_to_original:
313
+ sanitized_to_original[sanitized] = []
314
+ sanitized_to_original[sanitized].append(name)
315
+
316
+ # Check for collisions
317
+ for sanitized, originals in sanitized_to_original.items():
318
+ if len(originals) > 1:
319
+ raise PTCCustomToolValidationError(
320
+ f"Tool name collision: tools {originals} all sanitize to '{sanitized}'. Please use unique tool names."
321
+ )
322
+
323
+
324
+ def _validate_tool_paths(tool: dict, bundle_roots: list[str]) -> None:
325
+ """Validate paths for a single tool and check for relative imports.
326
+
327
+ Args:
328
+ tool: Tool definition dict with kind, name, and path fields.
329
+ bundle_roots: List of allowed bundle root directories.
330
+
331
+ Raises:
332
+ PTCCustomToolValidationError: If paths are invalid or file uses relative imports.
333
+ """
334
+ kind = tool.get("kind")
335
+ name = tool.get("name", "")
336
+
337
+ if kind == "package":
338
+ package_path = tool.get("package_path")
339
+ if package_path:
340
+ path = Path(package_path)
341
+ if not path.exists() or not path.is_dir():
342
+ raise PTCCustomToolValidationError(
343
+ f"Package tool '{name}' package_path does not exist or is not a directory: '{package_path}'"
344
+ )
345
+ validate_path_within_bundle_roots(package_path, bundle_roots, name)
346
+
347
+ elif kind == "file":
348
+ file_path = tool.get("file_path")
349
+ if file_path:
350
+ validate_path_within_bundle_roots(file_path, bundle_roots, name)
351
+
352
+ relative_imports = detect_relative_imports(file_path)
353
+ if relative_imports:
354
+ raise PTCCustomToolValidationError(
355
+ f"File tool '{name}' uses relative imports which are not supported: "
356
+ f"{relative_imports}. Use a package tool with package_path instead."
357
+ )
358
+
359
+
360
+ def validate_custom_tool_config(config: PTCCustomToolConfig) -> None:
361
+ """Validate the complete custom tool configuration.
362
+
363
+ This runs all validation checks:
364
+ - Tool definitions have required fields
365
+ - Input schemas have valid parameter names (no collisions/reserved names)
366
+ - Paths are within bundle roots
367
+ - File tools don't use relative imports
368
+ - No name collisions
369
+
370
+ Args:
371
+ config: Custom tool configuration to validate.
372
+
373
+ Raises:
374
+ PTCCustomToolValidationError: If configuration is invalid.
375
+ """
376
+ if not config.enabled or not config.tools:
377
+ return
378
+
379
+ # Validate each tool definition
380
+ for tool in config.tools:
381
+ validate_tool_def(tool)
382
+
383
+ # Validate input_schema for each tool (fail-fast for param name issues)
384
+ for tool in config.tools:
385
+ if "input_schema" not in tool:
386
+ continue
387
+
388
+ name = tool.get("name", "")
389
+ raw_schema = tool.get("input_schema")
390
+ if not isinstance(raw_schema, dict):
391
+ raise PTCCustomToolValidationError(
392
+ f"Tool '{name}' has invalid input_schema: expected dict, got {type(raw_schema).__name__}"
393
+ )
394
+ _validate_input_schema(raw_schema, name)
395
+
396
+ # Check for name collisions
397
+ check_name_collisions(config.tools)
398
+
399
+ # Validate paths and check for relative imports
400
+ for tool in config.tools:
401
+ _validate_tool_paths(tool, config.bundle_roots)
402
+
403
+
404
+ def extract_tool_metadata(tool: object) -> dict:
405
+ """Extract metadata from a LangChain BaseTool instance.
406
+
407
+ Extracts name, description, and input_schema from the tool object.
408
+ This is called at agent construction time to populate tool definitions.
409
+
410
+ Args:
411
+ tool: A LangChain BaseTool instance (or compatible object).
412
+
413
+ Returns:
414
+ Dict with keys:
415
+ - name: str (tool name)
416
+ - description: str (tool description, empty if not available)
417
+ - input_schema: dict (JSON schema, empty object schema if not available)
418
+
419
+ Note:
420
+ If schema inference fails, returns an empty object schema
421
+ ({"type": "object", "properties": {}}) so prompt/index output
422
+ falls back to tool(**kwargs).
423
+ """
424
+ result: dict = {
425
+ "name": "",
426
+ "description": "",
427
+ "input_schema": {"type": "object", "properties": {}},
428
+ }
429
+
430
+ # Extract name
431
+ if hasattr(tool, "name"):
432
+ result["name"] = str(tool.name)
433
+
434
+ # Extract description
435
+ if hasattr(tool, "description"):
436
+ desc = tool.description
437
+ if desc:
438
+ # Clean up multiline description - normalize whitespace
439
+ result["description"] = " ".join(str(desc).split())
440
+
441
+ # Extract input_schema from args_schema (Pydantic model)
442
+ if hasattr(tool, "args_schema") and tool.args_schema is not None:
443
+ try:
444
+ args_schema = tool.args_schema
445
+ # Pydantic v2 uses model_json_schema()
446
+ if hasattr(args_schema, "model_json_schema"):
447
+ schema = args_schema.model_json_schema()
448
+ # Remove Pydantic-specific keys that aren't needed
449
+ schema.pop("title", None)
450
+ schema.pop("$defs", None)
451
+ schema.pop("definitions", None)
452
+ result["input_schema"] = schema
453
+ # Pydantic v1 fallback uses schema()
454
+ elif hasattr(args_schema, "schema"):
455
+ schema = args_schema.schema()
456
+ schema.pop("title", None)
457
+ schema.pop("definitions", None)
458
+ result["input_schema"] = schema
459
+ except Exception:
460
+ # If schema extraction fails, keep the empty object schema
461
+ pass
462
+
463
+ return result
464
+
465
+
466
+ def enrich_tool_def_with_metadata(tool_def: PTCToolDef, tool: object) -> PTCToolDef:
467
+ """Enrich a tool definition with metadata extracted from the tool object.
468
+
469
+ Always derives description and input_schema from the tool object, overwriting
470
+ any user-supplied values. This ensures the tool object is the source of truth.
471
+ Called at agent construction time.
472
+
473
+ Args:
474
+ tool_def: The tool definition to enrich.
475
+ tool: The LangChain BaseTool instance.
476
+
477
+ Returns:
478
+ The tool definition with description and input_schema populated from the tool object.
479
+
480
+ Raises:
481
+ PTCCustomToolValidationError: If tool name in metadata does not match tool_def name.
482
+ """
483
+ metadata = extract_tool_metadata(tool)
484
+
485
+ # Create a mutable copy
486
+ enriched: dict = dict(tool_def)
487
+
488
+ # Validate name matches if metadata provides one
489
+ if metadata["name"] and metadata["name"] != enriched.get("name"):
490
+ raise PTCCustomToolValidationError(
491
+ f"Tool name mismatch: tool_def name '{enriched.get('name')}' does not match "
492
+ f"tool object name '{metadata['name']}'"
493
+ )
494
+
495
+ # Log if we're overwriting existing values
496
+ if "description" in enriched and enriched["description"]:
497
+ logger.debug(
498
+ f"Overwriting user-supplied description for tool '{enriched.get('name', 'unknown')}' "
499
+ f"with derived metadata from tool object"
500
+ )
501
+ if "input_schema" in enriched and enriched["input_schema"]:
502
+ logger.debug(
503
+ f"Overwriting user-supplied input_schema for tool '{enriched.get('name', 'unknown')}' "
504
+ f"with derived metadata from tool object"
505
+ )
506
+
507
+ # Always overwrite with derived metadata (tool object is source of truth)
508
+ enriched["description"] = metadata["description"]
509
+ enriched["input_schema"] = metadata["input_schema"]
510
+
511
+ return enriched # type: ignore[return-value]