aip-agents-binary 0.0.0b2__py3-none-any.whl → 0.5.11__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 (506) hide show
  1. aip_agents/__init__.py +65 -0
  2. aip_agents/__init__.pyi +19 -0
  3. aip_agents/a2a/__init__.py +19 -0
  4. aip_agents/a2a/__init__.pyi +1 -1
  5. aip_agents/a2a/server/__init__.py +10 -0
  6. aip_agents/a2a/server/base_executor.py +1086 -0
  7. aip_agents/a2a/server/base_executor.pyi +16 -6
  8. aip_agents/a2a/server/google_adk_executor.py +198 -0
  9. aip_agents/a2a/server/google_adk_executor.pyi +11 -11
  10. aip_agents/a2a/server/langflow_executor.py +180 -0
  11. aip_agents/a2a/server/langflow_executor.pyi +43 -0
  12. aip_agents/a2a/server/langgraph_executor.py +267 -0
  13. aip_agents/a2a/server/langgraph_executor.pyi +11 -12
  14. aip_agents/a2a/types.py +232 -0
  15. aip_agents/a2a/types.pyi +54 -47
  16. aip_agents/agent/__init__.py +27 -0
  17. aip_agents/agent/__init__.pyi +3 -1
  18. aip_agents/agent/base_agent.py +970 -0
  19. aip_agents/agent/base_agent.pyi +24 -8
  20. aip_agents/agent/base_langgraph_agent.py +2942 -0
  21. aip_agents/agent/base_langgraph_agent.pyi +93 -25
  22. aip_agents/agent/google_adk_agent.py +926 -0
  23. aip_agents/agent/google_adk_agent.pyi +34 -21
  24. aip_agents/agent/google_adk_constants.py +6 -0
  25. aip_agents/agent/google_adk_constants.pyi +3 -0
  26. aip_agents/agent/hitl/__init__.py +24 -0
  27. aip_agents/agent/hitl/__init__.pyi +6 -0
  28. aip_agents/agent/hitl/config.py +28 -0
  29. aip_agents/agent/hitl/config.pyi +15 -0
  30. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  31. aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +42 -0
  32. aip_agents/agent/hitl/manager.py +522 -0
  33. aip_agents/agent/hitl/manager.pyi +199 -0
  34. aip_agents/agent/hitl/models.py +18 -0
  35. aip_agents/agent/hitl/models.pyi +3 -0
  36. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  37. aip_agents/agent/hitl/prompt/__init__.pyi +4 -0
  38. aip_agents/agent/hitl/prompt/base.py +42 -0
  39. aip_agents/agent/hitl/prompt/base.pyi +24 -0
  40. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  41. aip_agents/agent/hitl/prompt/deferred.pyi +30 -0
  42. aip_agents/agent/interface.py +138 -0
  43. aip_agents/agent/interface.pyi +5 -5
  44. aip_agents/agent/interfaces.py +65 -0
  45. aip_agents/agent/interfaces.pyi +44 -0
  46. aip_agents/agent/langflow_agent.py +464 -0
  47. aip_agents/agent/langflow_agent.pyi +133 -0
  48. aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
  49. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +49 -0
  50. aip_agents/agent/langgraph_react_agent.py +2514 -0
  51. aip_agents/agent/langgraph_react_agent.pyi +51 -56
  52. aip_agents/agent/system_instruction_context.py +34 -0
  53. aip_agents/agent/system_instruction_context.pyi +13 -0
  54. aip_agents/clients/__init__.py +10 -0
  55. aip_agents/clients/__init__.pyi +4 -0
  56. aip_agents/clients/langflow/__init__.py +10 -0
  57. aip_agents/clients/langflow/__init__.pyi +4 -0
  58. aip_agents/clients/langflow/client.py +477 -0
  59. aip_agents/clients/langflow/client.pyi +140 -0
  60. aip_agents/clients/langflow/types.py +18 -0
  61. aip_agents/clients/langflow/types.pyi +7 -0
  62. aip_agents/constants.py +23 -0
  63. aip_agents/constants.pyi +1 -0
  64. aip_agents/credentials/manager.py +132 -0
  65. aip_agents/examples/__init__.py +5 -0
  66. aip_agents/examples/compare_streaming_client.py +783 -0
  67. aip_agents/examples/compare_streaming_client.pyi +48 -0
  68. aip_agents/examples/compare_streaming_server.py +142 -0
  69. aip_agents/examples/compare_streaming_server.pyi +18 -0
  70. aip_agents/examples/demo_memory_recall.py +401 -0
  71. aip_agents/examples/demo_memory_recall.pyi +58 -0
  72. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  73. aip_agents/examples/hello_world_a2a_google_adk_client.pyi +2 -2
  74. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  75. aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +2 -2
  76. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +2 -2
  78. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  79. aip_agents/examples/hello_world_a2a_google_adk_server.pyi +7 -2
  80. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  81. aip_agents/examples/hello_world_a2a_langchain_client.pyi +1 -1
  82. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  83. aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +1 -1
  84. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  85. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +1 -1
  86. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  87. aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +1 -1
  88. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  89. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +1 -1
  90. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  91. aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +8 -3
  92. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  93. aip_agents/examples/hello_world_a2a_langchain_server.pyi +7 -2
  94. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  95. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +7 -2
  96. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  97. aip_agents/examples/hello_world_a2a_langflow_client.pyi +9 -0
  98. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  99. aip_agents/examples/hello_world_a2a_langflow_server.pyi +14 -0
  100. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  101. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +1 -1
  102. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  103. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +1 -1
  104. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  105. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +7 -2
  106. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  107. aip_agents/examples/hello_world_a2a_langgraph_client.pyi +2 -2
  108. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  109. aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +2 -2
  110. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  111. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +1 -1
  112. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  113. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +3 -3
  114. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  115. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +1 -1
  116. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  117. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +1 -1
  118. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  119. aip_agents/examples/hello_world_a2a_langgraph_server.pyi +7 -2
  120. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  121. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +7 -2
  122. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  123. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +7 -2
  124. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  125. aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +1 -1
  126. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  127. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +31 -6
  128. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  129. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +33 -5
  130. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  131. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +1 -1
  132. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  133. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +7 -2
  134. aip_agents/examples/hello_world_google_adk.py +41 -0
  135. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  136. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  137. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  138. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  139. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  140. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  141. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  142. aip_agents/examples/hello_world_langchain.py +28 -0
  143. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  144. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  145. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  146. aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +16 -0
  147. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  148. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  149. aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +18 -0
  150. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  151. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  152. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  153. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  154. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  155. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  156. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  157. aip_agents/examples/hello_world_langflow_agent.pyi +35 -0
  158. aip_agents/examples/hello_world_langgraph.py +39 -0
  159. aip_agents/examples/hello_world_langgraph_bosa_twitter.py +41 -0
  160. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  161. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  162. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  163. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  164. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  165. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  166. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  167. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  168. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  169. aip_agents/examples/hello_world_model_switch_cli.pyi +18 -3
  170. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  171. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  172. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  173. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  174. aip_agents/examples/hello_world_pii_logger.py +21 -0
  175. aip_agents/examples/hello_world_pii_logger.pyi +1 -1
  176. aip_agents/examples/hello_world_sentry.py +133 -0
  177. aip_agents/examples/hello_world_sentry.pyi +1 -1
  178. aip_agents/examples/hello_world_step_limits.py +273 -0
  179. aip_agents/examples/hello_world_step_limits.pyi +17 -0
  180. aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
  181. aip_agents/examples/hello_world_stock_a2a_server.pyi +7 -2
  182. aip_agents/examples/hello_world_tool_output_client.py +46 -0
  183. aip_agents/examples/hello_world_tool_output_client.pyi +1 -1
  184. aip_agents/examples/hello_world_tool_output_server.py +114 -0
  185. aip_agents/examples/hello_world_tool_output_server.pyi +7 -2
  186. aip_agents/examples/hitl_demo.py +724 -0
  187. aip_agents/examples/hitl_demo.pyi +67 -0
  188. aip_agents/examples/mcp_configs/configs.py +63 -0
  189. aip_agents/examples/mcp_servers/common.py +76 -0
  190. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  191. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  192. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  193. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  194. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  195. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  196. aip_agents/examples/pii_demo_langgraph_client.pyi +5 -0
  197. aip_agents/examples/pii_demo_langgraph_server.py +126 -0
  198. aip_agents/examples/pii_demo_langgraph_server.pyi +20 -0
  199. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  200. aip_agents/examples/pii_demo_multi_agent_client.pyi +5 -0
  201. aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
  202. aip_agents/examples/pii_demo_multi_agent_server.pyi +40 -0
  203. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  204. aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +5 -0
  205. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
  206. aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +19 -0
  207. aip_agents/examples/tools/__init__.py +27 -0
  208. aip_agents/examples/tools/__init__.pyi +3 -1
  209. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  210. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  211. aip_agents/examples/tools/adk_weather_tool.pyi +1 -1
  212. aip_agents/examples/tools/data_generator_tool.py +103 -0
  213. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  214. aip_agents/examples/tools/data_visualization_tool.pyi +2 -0
  215. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  216. aip_agents/examples/tools/image_artifact_tool.pyi +2 -0
  217. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  218. aip_agents/examples/tools/langchain_arithmetic_tools.pyi +7 -0
  219. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  220. aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +0 -1
  221. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  222. aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +2 -0
  223. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  224. aip_agents/examples/tools/langchain_weather_tool.pyi +1 -1
  225. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  226. aip_agents/examples/tools/langgraph_streaming_tool.pyi +1 -1
  227. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  228. aip_agents/examples/tools/mock_retrieval_tool.pyi +13 -0
  229. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  230. aip_agents/examples/tools/pii_demo_tools.pyi +54 -0
  231. aip_agents/examples/tools/random_chart_tool.py +142 -0
  232. aip_agents/examples/tools/random_chart_tool.pyi +20 -0
  233. aip_agents/examples/tools/serper_tool.py +202 -0
  234. aip_agents/examples/tools/stock_tools.py +82 -0
  235. aip_agents/examples/tools/stock_tools.pyi +18 -3
  236. aip_agents/examples/tools/table_generator_tool.py +167 -0
  237. aip_agents/examples/tools/time_tool.py +82 -0
  238. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  239. aip_agents/executor/agent_executor.py +473 -0
  240. aip_agents/executor/base.py +48 -0
  241. aip_agents/mcp/__init__.py +1 -0
  242. aip_agents/mcp/client/__init__.py +14 -0
  243. aip_agents/mcp/client/__init__.pyi +4 -3
  244. aip_agents/mcp/client/base_mcp_client.py +369 -0
  245. aip_agents/mcp/client/base_mcp_client.pyi +148 -0
  246. aip_agents/mcp/client/connection_manager.py +193 -0
  247. aip_agents/mcp/client/connection_manager.pyi +48 -0
  248. aip_agents/mcp/client/google_adk/__init__.py +11 -0
  249. aip_agents/mcp/client/google_adk/__init__.pyi +1 -1
  250. aip_agents/mcp/client/google_adk/client.py +381 -0
  251. aip_agents/mcp/client/google_adk/client.pyi +50 -13
  252. aip_agents/mcp/client/langchain/__init__.py +11 -0
  253. aip_agents/mcp/client/langchain/__init__.pyi +1 -1
  254. aip_agents/mcp/client/langchain/client.py +265 -0
  255. aip_agents/mcp/client/langchain/client.pyi +47 -2
  256. aip_agents/mcp/client/persistent_session.py +359 -0
  257. aip_agents/mcp/client/persistent_session.pyi +113 -0
  258. aip_agents/mcp/client/session_pool.py +351 -0
  259. aip_agents/mcp/client/session_pool.pyi +101 -0
  260. aip_agents/mcp/client/transports.py +215 -0
  261. aip_agents/mcp/client/transports.pyi +123 -0
  262. aip_agents/mcp/utils/__init__.py +7 -0
  263. aip_agents/mcp/utils/__init__.pyi +0 -0
  264. aip_agents/mcp/utils/config_validator.py +139 -0
  265. aip_agents/mcp/utils/config_validator.pyi +82 -0
  266. aip_agents/memory/__init__.py +14 -0
  267. aip_agents/memory/__init__.pyi +5 -0
  268. aip_agents/memory/adapters/__init__.py +10 -0
  269. aip_agents/memory/adapters/__init__.pyi +4 -0
  270. aip_agents/memory/adapters/base_adapter.py +717 -0
  271. aip_agents/memory/adapters/base_adapter.pyi +150 -0
  272. aip_agents/memory/adapters/mem0.py +84 -0
  273. aip_agents/memory/adapters/mem0.pyi +22 -0
  274. aip_agents/memory/base.py +84 -0
  275. aip_agents/memory/base.pyi +15 -4
  276. aip_agents/memory/constants.py +49 -0
  277. aip_agents/memory/constants.pyi +25 -0
  278. aip_agents/memory/factory.py +86 -0
  279. aip_agents/memory/factory.pyi +24 -0
  280. aip_agents/memory/guidance.py +20 -0
  281. aip_agents/memory/guidance.pyi +3 -0
  282. aip_agents/memory/simple_memory.py +47 -0
  283. aip_agents/memory/simple_memory.pyi +6 -5
  284. aip_agents/middleware/__init__.py +17 -0
  285. aip_agents/middleware/__init__.pyi +5 -0
  286. aip_agents/middleware/base.py +88 -0
  287. aip_agents/middleware/base.pyi +71 -0
  288. aip_agents/middleware/manager.py +128 -0
  289. aip_agents/middleware/manager.pyi +80 -0
  290. aip_agents/middleware/todolist.py +274 -0
  291. aip_agents/middleware/todolist.pyi +125 -0
  292. aip_agents/schema/__init__.py +69 -0
  293. aip_agents/schema/__init__.pyi +9 -0
  294. aip_agents/schema/a2a.py +56 -0
  295. aip_agents/schema/a2a.pyi +40 -0
  296. aip_agents/schema/agent.py +111 -0
  297. aip_agents/schema/agent.pyi +65 -0
  298. aip_agents/schema/hitl.py +157 -0
  299. aip_agents/schema/hitl.pyi +89 -0
  300. aip_agents/schema/langgraph.py +37 -0
  301. aip_agents/schema/langgraph.pyi +28 -0
  302. aip_agents/schema/model_id.py +97 -0
  303. aip_agents/schema/model_id.pyi +54 -0
  304. aip_agents/schema/step_limit.py +108 -0
  305. aip_agents/schema/step_limit.pyi +63 -0
  306. aip_agents/schema/storage.py +40 -0
  307. aip_agents/schema/storage.pyi +21 -0
  308. aip_agents/sentry/__init__.py +11 -0
  309. aip_agents/sentry/__init__.pyi +1 -1
  310. aip_agents/sentry/sentry.py +151 -0
  311. aip_agents/sentry/sentry.pyi +2 -2
  312. aip_agents/storage/__init__.py +41 -0
  313. aip_agents/storage/base.py +85 -0
  314. aip_agents/storage/base.pyi +1 -2
  315. aip_agents/storage/clients/__init__.py +12 -0
  316. aip_agents/storage/clients/minio_client.py +318 -0
  317. aip_agents/storage/config.py +62 -0
  318. aip_agents/storage/config.pyi +9 -46
  319. aip_agents/storage/providers/__init__.py +15 -0
  320. aip_agents/storage/providers/base.py +106 -0
  321. aip_agents/storage/providers/base.pyi +2 -3
  322. aip_agents/storage/providers/memory.py +114 -0
  323. aip_agents/storage/providers/object_storage.py +214 -0
  324. aip_agents/storage/providers/object_storage.pyi +1 -1
  325. aip_agents/tools/__init__.py +6 -0
  326. aip_agents/tools/__init__.pyi +3 -2
  327. aip_agents/tools/bosa_tools.py +105 -0
  328. aip_agents/tools/bosa_tools.pyi +2 -2
  329. aip_agents/tools/browser_use/__init__.py +82 -0
  330. aip_agents/tools/browser_use/__init__.pyi +14 -0
  331. aip_agents/tools/browser_use/action_parser.py +103 -0
  332. aip_agents/tools/browser_use/action_parser.pyi +18 -0
  333. aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
  334. aip_agents/tools/browser_use/browser_use_tool.pyi +50 -0
  335. aip_agents/tools/browser_use/llm_config.py +120 -0
  336. aip_agents/tools/browser_use/llm_config.pyi +52 -0
  337. aip_agents/tools/browser_use/minio_storage.py +198 -0
  338. aip_agents/tools/browser_use/minio_storage.pyi +109 -0
  339. aip_agents/tools/browser_use/schemas.py +119 -0
  340. aip_agents/tools/browser_use/schemas.pyi +32 -0
  341. aip_agents/tools/browser_use/session.py +76 -0
  342. aip_agents/tools/browser_use/session.pyi +4 -0
  343. aip_agents/tools/browser_use/session_errors.py +132 -0
  344. aip_agents/tools/browser_use/session_errors.pyi +53 -0
  345. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  346. aip_agents/tools/browser_use/steel_session_recording.pyi +63 -0
  347. aip_agents/tools/browser_use/streaming.py +813 -0
  348. aip_agents/tools/browser_use/streaming.pyi +81 -0
  349. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  350. aip_agents/tools/browser_use/structured_data_parser.pyi +86 -0
  351. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  352. aip_agents/tools/browser_use/structured_data_recovery.pyi +43 -0
  353. aip_agents/tools/browser_use/types.py +78 -0
  354. aip_agents/tools/browser_use/types.pyi +45 -0
  355. aip_agents/tools/code_sandbox/__init__.py +26 -0
  356. aip_agents/tools/code_sandbox/__init__.pyi +3 -0
  357. aip_agents/tools/code_sandbox/constant.py +13 -0
  358. aip_agents/tools/code_sandbox/constant.pyi +4 -0
  359. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +257 -0
  360. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +86 -0
  361. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  362. aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +29 -0
  363. aip_agents/tools/constants.py +165 -0
  364. aip_agents/tools/constants.pyi +105 -100
  365. aip_agents/tools/document_loader/__init__.py +37 -0
  366. aip_agents/tools/document_loader/__init__.pyi +7 -0
  367. aip_agents/tools/document_loader/base_reader.py +262 -0
  368. aip_agents/tools/document_loader/base_reader.pyi +62 -0
  369. aip_agents/tools/document_loader/docx_reader_tool.py +53 -0
  370. aip_agents/tools/document_loader/docx_reader_tool.pyi +6 -0
  371. aip_agents/tools/document_loader/excel_reader_tool.py +160 -0
  372. aip_agents/tools/document_loader/excel_reader_tool.pyi +26 -0
  373. aip_agents/tools/document_loader/pdf_reader_tool.py +67 -0
  374. aip_agents/tools/document_loader/pdf_reader_tool.pyi +11 -0
  375. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  376. aip_agents/tools/document_loader/pdf_splitter.pyi +18 -0
  377. aip_agents/tools/gl_connector/__init__.py +5 -0
  378. aip_agents/tools/gl_connector/__init__.pyi +3 -0
  379. aip_agents/tools/gl_connector/tool.py +351 -0
  380. aip_agents/tools/gl_connector/tool.pyi +74 -0
  381. aip_agents/tools/memory_search/__init__.py +22 -0
  382. aip_agents/tools/memory_search/__init__.pyi +5 -0
  383. aip_agents/tools/memory_search/base.py +200 -0
  384. aip_agents/tools/memory_search/base.pyi +69 -0
  385. aip_agents/tools/memory_search/mem0.py +258 -0
  386. aip_agents/tools/memory_search/mem0.pyi +19 -0
  387. aip_agents/tools/memory_search/schema.py +48 -0
  388. aip_agents/tools/memory_search/schema.pyi +15 -0
  389. aip_agents/tools/memory_search_tool.py +26 -0
  390. aip_agents/tools/memory_search_tool.pyi +3 -0
  391. aip_agents/tools/tool_config_injector.py +300 -0
  392. aip_agents/tools/web_search/__init__.py +15 -0
  393. aip_agents/tools/web_search/__init__.pyi +3 -0
  394. aip_agents/tools/web_search/serper_tool.py +187 -0
  395. aip_agents/tools/web_search/serper_tool.pyi +19 -0
  396. aip_agents/types/__init__.py +70 -0
  397. aip_agents/types/__init__.pyi +35 -2
  398. aip_agents/types/a2a_events.py +13 -0
  399. aip_agents/types/a2a_events.pyi +2 -73
  400. aip_agents/utils/__init__.py +79 -0
  401. aip_agents/utils/__init__.pyi +8 -2
  402. aip_agents/utils/a2a_connector.py +1757 -0
  403. aip_agents/utils/a2a_connector.pyi +15 -4
  404. aip_agents/utils/artifact_helpers.py +502 -0
  405. aip_agents/utils/artifact_helpers.pyi +26 -2
  406. aip_agents/utils/constants.py +22 -0
  407. aip_agents/utils/constants.pyi +10 -0
  408. aip_agents/utils/datetime/__init__.py +34 -0
  409. aip_agents/utils/datetime/__init__.pyi +4 -0
  410. aip_agents/utils/datetime/normalization.py +231 -0
  411. aip_agents/utils/datetime/normalization.pyi +95 -0
  412. aip_agents/utils/datetime/timezone.py +206 -0
  413. aip_agents/utils/datetime/timezone.pyi +48 -0
  414. aip_agents/utils/env_loader.py +27 -0
  415. aip_agents/utils/env_loader.pyi +10 -0
  416. aip_agents/utils/event_handler_registry.py +58 -0
  417. aip_agents/utils/event_handler_registry.pyi +23 -0
  418. aip_agents/utils/file_prompt_utils.py +176 -0
  419. aip_agents/utils/file_prompt_utils.pyi +21 -0
  420. aip_agents/utils/final_response_builder.py +211 -0
  421. aip_agents/utils/final_response_builder.pyi +34 -0
  422. aip_agents/utils/formatter_llm_client.py +231 -0
  423. aip_agents/utils/formatter_llm_client.pyi +71 -0
  424. aip_agents/utils/langgraph/__init__.py +19 -0
  425. aip_agents/utils/langgraph/converter.py +128 -0
  426. aip_agents/utils/langgraph/converter.pyi +1 -1
  427. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  428. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  429. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +2 -2
  430. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  431. aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +2 -3
  432. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
  433. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +17 -11
  434. aip_agents/utils/langgraph/tool_output_management.py +967 -0
  435. aip_agents/utils/langgraph/tool_output_management.pyi +15 -33
  436. aip_agents/utils/logger.py +195 -0
  437. aip_agents/utils/logger.pyi +60 -0
  438. aip_agents/utils/metadata/__init__.py +27 -0
  439. aip_agents/utils/metadata/__init__.pyi +5 -0
  440. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  441. aip_agents/utils/metadata/activity_metadata_helper.pyi +25 -0
  442. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  443. aip_agents/utils/metadata/activity_narrative/__init__.pyi +7 -0
  444. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  445. aip_agents/utils/metadata/activity_narrative/builder.pyi +35 -0
  446. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  447. aip_agents/utils/metadata/activity_narrative/constants.pyi +10 -0
  448. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  449. aip_agents/utils/metadata/activity_narrative/context.pyi +32 -0
  450. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  451. aip_agents/utils/metadata/activity_narrative/formatters.pyi +48 -0
  452. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  453. aip_agents/utils/metadata/activity_narrative/utils.pyi +12 -0
  454. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  455. aip_agents/utils/metadata/schemas/__init__.pyi +4 -0
  456. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  457. aip_agents/utils/metadata/schemas/activity_schema.pyi +18 -0
  458. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  459. aip_agents/utils/metadata/schemas/thinking_schema.pyi +20 -0
  460. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  461. aip_agents/utils/metadata/thinking_metadata_helper.pyi +4 -0
  462. aip_agents/utils/metadata_helper.py +358 -0
  463. aip_agents/utils/metadata_helper.pyi +55 -48
  464. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  465. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  466. aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +1 -2
  467. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  468. aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +1 -1
  469. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  470. aip_agents/utils/name_preprocessor/name_preprocessor.pyi +4 -4
  471. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  472. aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +1 -1
  473. aip_agents/utils/pii/__init__.py +25 -0
  474. aip_agents/utils/pii/__init__.pyi +5 -0
  475. aip_agents/utils/pii/pii_handler.py +397 -0
  476. aip_agents/utils/pii/pii_handler.pyi +96 -0
  477. aip_agents/utils/pii/pii_helper.py +207 -0
  478. aip_agents/utils/pii/pii_helper.pyi +78 -0
  479. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  480. aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +73 -0
  481. aip_agents/utils/reference_helper.py +273 -0
  482. aip_agents/utils/reference_helper.pyi +38 -6
  483. aip_agents/utils/sse_chunk_transformer.py +831 -0
  484. aip_agents/utils/sse_chunk_transformer.pyi +166 -0
  485. aip_agents/utils/step_limit_manager.py +265 -0
  486. aip_agents/utils/step_limit_manager.pyi +112 -0
  487. aip_agents/utils/token_usage_helper.py +156 -0
  488. aip_agents/utils/token_usage_helper.pyi +1 -1
  489. aip_agents_binary-0.5.11.dist-info/METADATA +689 -0
  490. aip_agents_binary-0.5.11.dist-info/RECORD +542 -0
  491. {aip_agents_binary-0.0.0b2.dist-info → aip_agents_binary-0.5.11.dist-info}/WHEEL +2 -1
  492. aip_agents_binary-0.5.11.dist-info/top_level.txt +1 -0
  493. aip_agents/agent/types.pyi +0 -106
  494. aip_agents/examples/hello_world_a2a_multi_agent_coordinator_client.pyi +0 -15
  495. aip_agents/examples/hello_world_a2a_multi_agent_coordinator_client_streaming.pyi +0 -5
  496. aip_agents/examples/hello_world_a2a_multi_agent_coordinator_server.pyi +0 -11
  497. aip_agents/examples/hello_world_langgraph_bosa.pyi +0 -5
  498. aip_agents/examples/tools/pr_details_bosa_tool.pyi +0 -26
  499. aip_agents/tools/base.pyi +0 -44
  500. aip_agents/tools/base_bosa_tools.pyi +0 -12
  501. aip_agents/tools/bosa_connector.pyi +0 -30
  502. aip_agents/tools/bosa_tools_interface.pyi +0 -26
  503. aip_agents/tools/nested_agent_tool.pyi +0 -45
  504. aip_agents/utils/logger_manager.pyi +0 -151
  505. aip_agents_binary-0.0.0b2.dist-info/METADATA +0 -277
  506. aip_agents_binary-0.0.0b2.dist-info/RECORD +0 -157
@@ -0,0 +1,231 @@
1
+ """Timestamp normalization helpers.
2
+
3
+ Authors:
4
+ Putu Ravindra Wiguna (putu.r.wiguna@gdplabs.id)
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime, timedelta
10
+ from typing import Any, Final
11
+
12
+ from aip_agents.utils.logger import get_logger
13
+
14
+ __all__ = [
15
+ "normalize_timestamp_to_date",
16
+ "format_created_updated_label",
17
+ "is_valid_date_string",
18
+ "next_day_iso",
19
+ ]
20
+
21
+
22
+ logger: Final = get_logger(__name__)
23
+
24
+ DEFAULT_DATE_FORMAT: Final[str] = "%Y-%m-%d"
25
+ DATETIME_FORMAT: Final[str] = "%Y-%m-%d %H:%M:%S"
26
+ ISO_FORMATS: Final[list[str | None]] = [
27
+ None, # ISO format
28
+ "%Y-%m-%d %H:%M:%S",
29
+ "%Y-%m-%d",
30
+ "%Y-%m-%dT%H:%M:%S",
31
+ "%Y-%m-%dT%H:%M:%S%z",
32
+ "%Y-%m-%dT%H:%M:%S.%f",
33
+ "%Y-%m-%dT%H:%M:%S.%f%z",
34
+ ]
35
+
36
+
37
+ def normalize_timestamp_to_date(value: Any) -> str | None:
38
+ """Normalize various timestamp representations into a YYYY-MM-DD string.
39
+
40
+ This function handles multiple input types and formats, converting them to
41
+ a standardized ISO date string. It gracefully handles invalid inputs by
42
+ returning None or the string representation.
43
+
44
+ Args:
45
+ value: The timestamp value to normalize. Can be:
46
+ - None or empty string: Returns None
47
+ - int/float: Unix timestamp (seconds since epoch)
48
+ - str: ISO format, datetime string, or other string representation
49
+ - Other types: Converted to string representation
50
+
51
+ Returns:
52
+ str | None: A YYYY-MM-DD formatted date string, or None if the input
53
+ cannot be parsed as a valid date.
54
+
55
+ Raises:
56
+ ValueError: If the timestamp value is invalid (e.g., negative timestamp).
57
+
58
+ Examples:
59
+ >>> normalize_timestamp_to_date(1640995200) # Unix timestamp
60
+ '2022-01-01'
61
+ >>> normalize_timestamp_to_date("2022-01-01T12:00:00")
62
+ '2022-01-01'
63
+ >>> normalize_timestamp_to_date("invalid")
64
+ 'invalid'
65
+ >>> normalize_timestamp_to_date(None)
66
+ None
67
+ """
68
+ # Handle None and empty values
69
+ if value in (None, ""):
70
+ return None
71
+
72
+ if isinstance(value, int | float):
73
+ return _normalize_numeric_timestamp(value)
74
+ elif isinstance(value, str):
75
+ return _normalize_string_timestamp(value)
76
+ else:
77
+ # For any other type, convert to string
78
+ return str(value)
79
+
80
+
81
+ def _normalize_numeric_timestamp(value: int | float) -> str | None:
82
+ """Normalize numeric timestamp to ISO date string.
83
+
84
+ Args:
85
+ value: Numeric timestamp (seconds since epoch).
86
+
87
+ Returns:
88
+ ISO date string or None if invalid.
89
+
90
+ Raises:
91
+ ValueError: If timestamp is negative.
92
+ """
93
+ try:
94
+ if value < 0:
95
+ raise ValueError(f"Negative timestamp value: {value}")
96
+ return datetime.fromtimestamp(value).date().isoformat()
97
+ except (OSError, OverflowError, ValueError):
98
+ logger.warning(f"Invalid timestamp value: {value}")
99
+ return None
100
+
101
+
102
+ def _normalize_string_timestamp(ts: str) -> str | None:
103
+ """Normalize string timestamp to ISO date string.
104
+
105
+ Args:
106
+ ts: String timestamp in various formats.
107
+
108
+ Returns:
109
+ ISO date string, original string if unparseable, or None if empty.
110
+ """
111
+ ts = ts.strip()
112
+ if not ts:
113
+ return None
114
+
115
+ # Handle ISO format with timezone
116
+ iso_candidate = ts.replace("Z", "+00:00") if ts.endswith("Z") else ts
117
+
118
+ # Try different parsing strategies
119
+ for fmt in ISO_FORMATS:
120
+ try:
121
+ if fmt is None:
122
+ dt = datetime.fromisoformat(iso_candidate)
123
+ else:
124
+ dt = datetime.strptime(ts, fmt)
125
+ return dt.date().isoformat()
126
+ except ValueError:
127
+ continue
128
+
129
+ # If no format worked, return the original string
130
+ # This preserves the original behavior for unparseable strings
131
+ return ts
132
+
133
+
134
+ def format_created_updated_label(created_at: Any | None, updated_at: Any | None) -> str | None:
135
+ """Build a compact label combining created/updated timestamps when available.
136
+
137
+ Creates a human-readable label that shows creation and update timestamps
138
+ in a compact format. If both dates are the same, only shows one date.
139
+
140
+ Args:
141
+ created_at: The creation timestamp (any format supported by normalize_timestamp_to_date).
142
+ updated_at: The update timestamp (any format supported by normalize_timestamp_to_date).
143
+
144
+ Returns:
145
+ str | None: A formatted label string, or None if both inputs are invalid/empty.
146
+ Examples: "2022-01-01", "2022-01-01 (updated 2022-01-02)", "updated 2022-01-01"
147
+
148
+ Examples:
149
+ >>> format_created_updated_label("2022-01-01", "2022-01-02")
150
+ '2022-01-01 (updated 2022-01-02)'
151
+ >>> format_created_updated_label("2022-01-01", "2022-01-01")
152
+ '2022-01-01'
153
+ >>> format_created_updated_label(None, "2022-01-01")
154
+ 'updated 2022-01-01'
155
+ >>> format_created_updated_label("2022-01-01", None)
156
+ '2022-01-01'
157
+ """
158
+ created = normalize_timestamp_to_date(created_at)
159
+ updated = normalize_timestamp_to_date(updated_at)
160
+
161
+ if created and updated:
162
+ if created == updated:
163
+ return created
164
+ return f"{created} (updated {updated})"
165
+ if created:
166
+ return created
167
+ if updated:
168
+ return f"updated {updated}"
169
+ return None
170
+
171
+
172
+ def is_valid_date_string(date_str: str, fmt: str = DEFAULT_DATE_FORMAT) -> bool:
173
+ """Validate that a date string matches the provided strftime format.
174
+
175
+ Args:
176
+ date_str: The date string to validate. Must be a non-empty string.
177
+ fmt: The strftime format pattern to validate against.
178
+ Defaults to YYYY-MM-DD format.
179
+
180
+ Returns:
181
+ bool: True if the date string matches the format, False otherwise.
182
+
183
+ Raises:
184
+ ValueError: If the format string is empty or invalid, or if date_str is not a string.
185
+
186
+ Examples:
187
+ >>> is_valid_date_string("2022-01-01")
188
+ True
189
+ >>> is_valid_date_string("2022-13-01")
190
+ False
191
+ >>> is_valid_date_string("01-01-2022", "%m-%d-%Y")
192
+ True
193
+ >>> is_valid_date_string("", "%Y-%m-%d")
194
+ False
195
+ """
196
+ if not isinstance(date_str, str):
197
+ raise ValueError(f"date_str must be a string, got {type(date_str).__name__}")
198
+
199
+ if not fmt or not fmt.strip():
200
+ raise ValueError("Format string cannot be empty")
201
+
202
+ if not date_str or not date_str.strip():
203
+ return False
204
+
205
+ try:
206
+ datetime.strptime(date_str.strip(), fmt)
207
+ return True
208
+ except (ValueError, TypeError) as e:
209
+ logger.debug(f"Date string '{date_str}' does not match format '{fmt}': {e}")
210
+ return False
211
+
212
+
213
+ def next_day_iso(date_str: str) -> str:
214
+ """Return the ISO date string for the day after the given ``YYYY-MM-DD`` date.
215
+
216
+ Args:
217
+ date_str: A date string in ``YYYY-MM-DD`` format.
218
+
219
+ Returns:
220
+ str: The next day's date in ``YYYY-MM-DD`` format.
221
+
222
+ Raises:
223
+ ValueError: If ``date_str`` is not a valid ``YYYY-MM-DD`` date string.
224
+ """
225
+ try:
226
+ pivot = datetime.strptime(date_str.strip(), "%Y-%m-%d")
227
+ except ValueError as exc:
228
+ raise ValueError(f"Invalid date string '{date_str}'. Expected YYYY-MM-DD.") from exc
229
+
230
+ next_day = pivot + timedelta(days=1)
231
+ return next_day.date().isoformat()
@@ -0,0 +1,95 @@
1
+ from typing import Any
2
+
3
+ __all__ = ['normalize_timestamp_to_date', 'format_created_updated_label', 'is_valid_date_string', 'next_day_iso']
4
+
5
+ def normalize_timestamp_to_date(value: Any) -> str | None:
6
+ '''Normalize various timestamp representations into a YYYY-MM-DD string.
7
+
8
+ This function handles multiple input types and formats, converting them to
9
+ a standardized ISO date string. It gracefully handles invalid inputs by
10
+ returning None or the string representation.
11
+
12
+ Args:
13
+ value: The timestamp value to normalize. Can be:
14
+ - None or empty string: Returns None
15
+ - int/float: Unix timestamp (seconds since epoch)
16
+ - str: ISO format, datetime string, or other string representation
17
+ - Other types: Converted to string representation
18
+
19
+ Returns:
20
+ str | None: A YYYY-MM-DD formatted date string, or None if the input
21
+ cannot be parsed as a valid date.
22
+
23
+ Raises:
24
+ ValueError: If the timestamp value is invalid (e.g., negative timestamp).
25
+
26
+ Examples:
27
+ >>> normalize_timestamp_to_date(1640995200) # Unix timestamp
28
+ \'2022-01-01\'
29
+ >>> normalize_timestamp_to_date("2022-01-01T12:00:00")
30
+ \'2022-01-01\'
31
+ >>> normalize_timestamp_to_date("invalid")
32
+ \'invalid\'
33
+ >>> normalize_timestamp_to_date(None)
34
+ None
35
+ '''
36
+ def format_created_updated_label(created_at: Any | None, updated_at: Any | None) -> str | None:
37
+ '''Build a compact label combining created/updated timestamps when available.
38
+
39
+ Creates a human-readable label that shows creation and update timestamps
40
+ in a compact format. If both dates are the same, only shows one date.
41
+
42
+ Args:
43
+ created_at: The creation timestamp (any format supported by normalize_timestamp_to_date).
44
+ updated_at: The update timestamp (any format supported by normalize_timestamp_to_date).
45
+
46
+ Returns:
47
+ str | None: A formatted label string, or None if both inputs are invalid/empty.
48
+ Examples: "2022-01-01", "2022-01-01 (updated 2022-01-02)", "updated 2022-01-01"
49
+
50
+ Examples:
51
+ >>> format_created_updated_label("2022-01-01", "2022-01-02")
52
+ \'2022-01-01 (updated 2022-01-02)\'
53
+ >>> format_created_updated_label("2022-01-01", "2022-01-01")
54
+ \'2022-01-01\'
55
+ >>> format_created_updated_label(None, "2022-01-01")
56
+ \'updated 2022-01-01\'
57
+ >>> format_created_updated_label("2022-01-01", None)
58
+ \'2022-01-01\'
59
+ '''
60
+ def is_valid_date_string(date_str: str, fmt: str = ...) -> bool:
61
+ '''Validate that a date string matches the provided strftime format.
62
+
63
+ Args:
64
+ date_str: The date string to validate. Must be a non-empty string.
65
+ fmt: The strftime format pattern to validate against.
66
+ Defaults to YYYY-MM-DD format.
67
+
68
+ Returns:
69
+ bool: True if the date string matches the format, False otherwise.
70
+
71
+ Raises:
72
+ ValueError: If the format string is empty or invalid, or if date_str is not a string.
73
+
74
+ Examples:
75
+ >>> is_valid_date_string("2022-01-01")
76
+ True
77
+ >>> is_valid_date_string("2022-13-01")
78
+ False
79
+ >>> is_valid_date_string("01-01-2022", "%m-%d-%Y")
80
+ True
81
+ >>> is_valid_date_string("", "%Y-%m-%d")
82
+ False
83
+ '''
84
+ def next_day_iso(date_str: str) -> str:
85
+ """Return the ISO date string for the day after the given ``YYYY-MM-DD`` date.
86
+
87
+ Args:
88
+ date_str: A date string in ``YYYY-MM-DD`` format.
89
+
90
+ Returns:
91
+ str: The next day's date in ``YYYY-MM-DD`` format.
92
+
93
+ Raises:
94
+ ValueError: If ``date_str`` is not a valid ``YYYY-MM-DD`` date string.
95
+ """
@@ -0,0 +1,206 @@
1
+ """Timezone-related helper functions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Final
7
+ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
8
+
9
+ from aip_agents.utils.logger import get_logger
10
+
11
+ __all__ = ["get_timezone_aware_now", "ensure_utc_datetime"]
12
+
13
+ logger: Final = get_logger(__name__)
14
+
15
+
16
+ def get_timezone_aware_now(timezone: str, fallback_timezone: str = "UTC") -> tuple[datetime, str, bool]:
17
+ """Return a timezone-aware datetime alongside a timezone label.
18
+
19
+ This function creates a timezone-aware datetime object using the modern
20
+ zoneinfo module (Python 3.9+). It gracefully falls back to UTC if the
21
+ requested timezone is invalid.
22
+
23
+ Args:
24
+ timezone: Desired timezone name in IANA format (e.g., "America/New_York",
25
+ "Europe/London", "Asia/Tokyo"). Must be a non-empty string.
26
+ fallback_timezone: Fallback timezone name to use when the primary
27
+ timezone is invalid. Defaults to "UTC". Must be a non-empty string.
28
+
29
+ Returns:
30
+ tuple[datetime, str, bool]: A 3-tuple containing:
31
+ - now: A timezone-aware datetime object representing the current time
32
+ - timezone_label: A human-readable timezone label (e.g., "EST", "UTC")
33
+ - used_fallback: Boolean indicating whether the fallback timezone was used
34
+
35
+ Raises:
36
+ ValueError: If timezone or fallback_timezone are empty/invalid, or if both
37
+ the requested timezone and fallback timezone are invalid.
38
+
39
+ Examples:
40
+ >>> now, label, fallback = get_timezone_aware_now("America/New_York")
41
+ >>> print(f"Current time: {now}, Timezone: {label}, Used fallback: {fallback}")
42
+ Current time: 2024-01-15 10:30:00-05:00, Timezone: EST, Used fallback: False
43
+
44
+ >>> now, label, fallback = get_timezone_aware_now("Invalid/Timezone")
45
+ >>> print(f"Used fallback: {fallback}, Label: {label}")
46
+ Used fallback: True, Label: UTC
47
+ """
48
+ timezone = _normalize_timezone("timezone", timezone)
49
+ fallback_timezone = _normalize_timezone("fallback_timezone", fallback_timezone)
50
+
51
+ tz, used_fallback = _load_timezone_with_case_enforcement(timezone, fallback_timezone)
52
+ now = datetime.now(tz)
53
+ timezone_label = _resolve_timezone_label(now, timezone, fallback_timezone, used_fallback)
54
+
55
+ return now, timezone_label, used_fallback
56
+
57
+
58
+ def ensure_utc_datetime(value: datetime) -> datetime:
59
+ """Normalize a datetime to UTC (attaching UTC to naive values).
60
+
61
+ Args:
62
+ value: Datetime to normalize.
63
+
64
+ Returns:
65
+ datetime: Timezone-aware datetime expressed in UTC.
66
+
67
+ Raises:
68
+ TypeError: If ``value`` is not a ``datetime`` instance.
69
+ """
70
+ if not isinstance(value, datetime):
71
+ raise TypeError(f"Expected datetime, received {type(value).__name__}")
72
+
73
+ if value.tzinfo is None or value.tzinfo.utcoffset(value) is None:
74
+ return value.replace(tzinfo=ZoneInfo("UTC"))
75
+
76
+ return value.astimezone(ZoneInfo("UTC"))
77
+
78
+
79
+ def _normalize_timezone(param_name: str, value: str) -> str:
80
+ """Validate and normalize a timezone parameter.
81
+
82
+ Args:
83
+ param_name: Name of the parameter being validated, used in error messages.
84
+ value: Raw timezone string provided by the caller.
85
+
86
+ Returns:
87
+ A stripped, non-empty timezone string.
88
+
89
+ Raises:
90
+ ValueError: If ``value`` is not a non-empty string.
91
+ """
92
+ if not isinstance(value, str) or not value.strip():
93
+ raise ValueError(f"{param_name} must be a non-empty string")
94
+ return value.strip()
95
+
96
+
97
+ def _load_timezone_with_case_enforcement(timezone: str, fallback_timezone: str) -> tuple[ZoneInfo, bool]:
98
+ """Load timezone ensuring case-sensitive behavior, falling back when needed.
99
+
100
+ Args:
101
+ timezone: Requested timezone name in IANA format.
102
+ fallback_timezone: Fallback timezone to use when the requested one is invalid.
103
+
104
+ Returns:
105
+ A tuple containing the resolved ``ZoneInfo`` object and a boolean indicating
106
+ whether the fallback timezone was used.
107
+
108
+ Raises:
109
+ ValueError: If both ``timezone`` and ``fallback_timezone`` are invalid.
110
+ """
111
+ requested_tz = _load_requested_timezone(timezone)
112
+ if requested_tz is not None:
113
+ return requested_tz, False
114
+
115
+ logger.warning(
116
+ "Timezone '%s' unavailable, attempting fallback to '%s'",
117
+ timezone,
118
+ fallback_timezone,
119
+ )
120
+ return _load_fallback_timezone(timezone, fallback_timezone)
121
+
122
+
123
+ def _load_requested_timezone(timezone: str) -> ZoneInfo | None:
124
+ """Attempt to load the requested timezone with case enforcement.
125
+
126
+ Args:
127
+ timezone: Requested timezone name in IANA format.
128
+
129
+ Returns:
130
+ The corresponding ``ZoneInfo`` if the timezone exists and matches case,
131
+ otherwise ``None``.
132
+ """
133
+ try:
134
+ candidate_tz = ZoneInfo(timezone)
135
+ except ZoneInfoNotFoundError:
136
+ logger.warning("Timezone '%s' was not found", timezone)
137
+ return None
138
+
139
+ canonical_key = getattr(candidate_tz, "key", None)
140
+ if canonical_key and canonical_key.lower() == timezone.lower() and canonical_key != timezone:
141
+ logger.warning(
142
+ "Timezone '%s' resolved to canonical key '%s'; case-sensitive handling requires an exact match",
143
+ timezone,
144
+ canonical_key,
145
+ )
146
+ return None
147
+
148
+ logger.debug("Successfully loaded timezone: %s", timezone)
149
+ return candidate_tz
150
+
151
+
152
+ def _load_fallback_timezone(timezone: str, fallback_timezone: str) -> tuple[ZoneInfo, bool]:
153
+ """Load the fallback timezone or raise a descriptive error.
154
+
155
+ Args:
156
+ timezone: Original requested timezone name, used for error messaging.
157
+ fallback_timezone: Fallback timezone to attempt loading.
158
+
159
+ Returns:
160
+ A tuple containing the fallback ``ZoneInfo`` and ``True`` to indicate that the
161
+ fallback was used.
162
+
163
+ Raises:
164
+ ValueError: If the fallback timezone is also invalid.
165
+ """
166
+ try:
167
+ fallback_tz = ZoneInfo(fallback_timezone)
168
+ except ZoneInfoNotFoundError as ex:
169
+ error_msg = (
170
+ f"Both requested timezone '{timezone}' and fallback timezone "
171
+ f"'{fallback_timezone}' are invalid. Please provide valid IANA timezone names."
172
+ )
173
+ logger.error(error_msg)
174
+ raise ValueError(error_msg) from ex
175
+
176
+ logger.debug("Successfully loaded fallback timezone: %s", fallback_timezone)
177
+ return fallback_tz, True
178
+
179
+
180
+ def _resolve_timezone_label(
181
+ now: datetime,
182
+ timezone: str,
183
+ fallback_timezone: str,
184
+ used_fallback: bool,
185
+ ) -> str:
186
+ """Determine the human-readable label for the timezone used.
187
+
188
+ Args:
189
+ now: The timezone-aware datetime generated using the resolved timezone.
190
+ timezone: Requested timezone name in IANA format.
191
+ fallback_timezone: Fallback timezone name in IANA format.
192
+ used_fallback: Whether the fallback timezone was used.
193
+
194
+ Returns:
195
+ A human-readable timezone label for display purposes.
196
+ """
197
+ if used_fallback:
198
+ return fallback_timezone
199
+
200
+ try:
201
+ timezone_label = now.strftime("%Z")
202
+ if not timezone_label:
203
+ return timezone
204
+ return timezone_label
205
+ except (ValueError, OSError):
206
+ return timezone
@@ -0,0 +1,48 @@
1
+ from datetime import datetime
2
+
3
+ __all__ = ['get_timezone_aware_now', 'ensure_utc_datetime']
4
+
5
+ def get_timezone_aware_now(timezone: str, fallback_timezone: str = 'UTC') -> tuple[datetime, str, bool]:
6
+ '''Return a timezone-aware datetime alongside a timezone label.
7
+
8
+ This function creates a timezone-aware datetime object using the modern
9
+ zoneinfo module (Python 3.9+). It gracefully falls back to UTC if the
10
+ requested timezone is invalid.
11
+
12
+ Args:
13
+ timezone: Desired timezone name in IANA format (e.g., "America/New_York",
14
+ "Europe/London", "Asia/Tokyo"). Must be a non-empty string.
15
+ fallback_timezone: Fallback timezone name to use when the primary
16
+ timezone is invalid. Defaults to "UTC". Must be a non-empty string.
17
+
18
+ Returns:
19
+ tuple[datetime, str, bool]: A 3-tuple containing:
20
+ - now: A timezone-aware datetime object representing the current time
21
+ - timezone_label: A human-readable timezone label (e.g., "EST", "UTC")
22
+ - used_fallback: Boolean indicating whether the fallback timezone was used
23
+
24
+ Raises:
25
+ ValueError: If timezone or fallback_timezone are empty/invalid, or if both
26
+ the requested timezone and fallback timezone are invalid.
27
+
28
+ Examples:
29
+ >>> now, label, fallback = get_timezone_aware_now("America/New_York")
30
+ >>> print(f"Current time: {now}, Timezone: {label}, Used fallback: {fallback}")
31
+ Current time: 2024-01-15 10:30:00-05:00, Timezone: EST, Used fallback: False
32
+
33
+ >>> now, label, fallback = get_timezone_aware_now("Invalid/Timezone")
34
+ >>> print(f"Used fallback: {fallback}, Label: {label}")
35
+ Used fallback: True, Label: UTC
36
+ '''
37
+ def ensure_utc_datetime(value: datetime) -> datetime:
38
+ """Normalize a datetime to UTC (attaching UTC to naive values).
39
+
40
+ Args:
41
+ value: Datetime to normalize.
42
+
43
+ Returns:
44
+ datetime: Timezone-aware datetime expressed in UTC.
45
+
46
+ Raises:
47
+ TypeError: If ``value`` is not a ``datetime`` instance.
48
+ """
@@ -0,0 +1,27 @@
1
+ """Utility helpers for loading local .env files in examples and apps."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def load_local_env(override: bool = True) -> None:
11
+ """Load environment variables from a .env file if python-dotenv is available.
12
+
13
+ Args:
14
+ override (bool, optional): Whether to override existing environment variables. Defaults to True.
15
+ """
16
+ try:
17
+ from dotenv import find_dotenv, load_dotenv # type: ignore[import-not-found] # noqa: PLC0415
18
+ except ImportError:
19
+ logger.warning("Could not load .env file: python-dotenv not installed")
20
+ return
21
+
22
+ try:
23
+ env_path = find_dotenv(usecwd=True)
24
+ load_dotenv(env_path, override=override)
25
+ logger.debug("Successfully loaded .env from %s", env_path)
26
+ except Exception as exc: # pragma: no cover - behaviour depends on environment
27
+ logger.warning("Could not load .env file: %s", exc)
@@ -0,0 +1,10 @@
1
+ from _typeshed import Incomplete
2
+
3
+ logger: Incomplete
4
+
5
+ def load_local_env(override: bool = True) -> None:
6
+ """Load environment variables from a .env file if python-dotenv is available.
7
+
8
+ Args:
9
+ override (bool, optional): Whether to override existing environment variables. Defaults to True.
10
+ """
@@ -0,0 +1,58 @@
1
+ """Shared event handler registry for streaming payload processing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from aip_agents.schema.a2a import A2AStreamEventType
8
+ from aip_agents.utils.logger import get_logger
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ class EventHandlerRegistry:
14
+ """Registry for tracking known streaming events with pass-through behaviour."""
15
+
16
+ def __init__(self) -> None:
17
+ """Initialize the event handler registry with known event types."""
18
+ self._known_events: set[str] = {self._normalize_event_type(event) for event in A2AStreamEventType}
19
+
20
+ def handle(self, event_type: A2AStreamEventType | str | None, payload: dict[str, Any]) -> dict[str, Any]:
21
+ """Return the payload unchanged while logging unknown events.
22
+
23
+ Args:
24
+ event_type (A2AStreamEventType | str | None): The type of the streaming event.
25
+ payload (dict[str, Any]): The event payload data.
26
+
27
+ Returns:
28
+ dict[str, Any]: The payload unchanged (pass-through).
29
+ """
30
+ normalized = self._normalize_event_type(event_type)
31
+ if normalized not in self._known_events:
32
+ logger.info(
33
+ "Unknown event type encountered in event handler registry; using default passthrough.",
34
+ extra={
35
+ "event_type": normalized,
36
+ "metadata_keys": list((payload.get("metadata") or {}).keys()) if isinstance(payload, dict) else [],
37
+ },
38
+ )
39
+ return payload
40
+
41
+ @staticmethod
42
+ def _normalize_event_type(event_type: A2AStreamEventType | str | None) -> str:
43
+ """Return a lowercase string for the supplied event type or ``unknown``.
44
+
45
+ Args:
46
+ event_type: Enum instance, string literal, or None representing the event.
47
+
48
+ Returns:
49
+ str: Normalized event type identifier.
50
+ """
51
+ if isinstance(event_type, A2AStreamEventType):
52
+ return event_type.value
53
+ if isinstance(event_type, str) and event_type:
54
+ return event_type
55
+ return "unknown"
56
+
57
+
58
+ DEFAULT_EVENT_HANDLER_REGISTRY = EventHandlerRegistry()