aip-agents-binary 0.5.25b1__py3-none-macosx_13_0_arm64.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 (566) 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 +3 -0
  5. aip_agents/a2a/server/__init__.py +10 -0
  6. aip_agents/a2a/server/__init__.pyi +4 -0
  7. aip_agents/a2a/server/base_executor.py +1086 -0
  8. aip_agents/a2a/server/base_executor.pyi +73 -0
  9. aip_agents/a2a/server/google_adk_executor.py +198 -0
  10. aip_agents/a2a/server/google_adk_executor.pyi +51 -0
  11. aip_agents/a2a/server/langflow_executor.py +180 -0
  12. aip_agents/a2a/server/langflow_executor.pyi +43 -0
  13. aip_agents/a2a/server/langgraph_executor.py +270 -0
  14. aip_agents/a2a/server/langgraph_executor.pyi +47 -0
  15. aip_agents/a2a/types.py +232 -0
  16. aip_agents/a2a/types.pyi +132 -0
  17. aip_agents/agent/__init__.py +27 -0
  18. aip_agents/agent/__init__.pyi +9 -0
  19. aip_agents/agent/base_agent.py +970 -0
  20. aip_agents/agent/base_agent.pyi +221 -0
  21. aip_agents/agent/base_langgraph_agent.py +2948 -0
  22. aip_agents/agent/base_langgraph_agent.pyi +232 -0
  23. aip_agents/agent/google_adk_agent.py +926 -0
  24. aip_agents/agent/google_adk_agent.pyi +141 -0
  25. aip_agents/agent/google_adk_constants.py +6 -0
  26. aip_agents/agent/google_adk_constants.pyi +3 -0
  27. aip_agents/agent/hitl/__init__.py +24 -0
  28. aip_agents/agent/hitl/__init__.pyi +6 -0
  29. aip_agents/agent/hitl/config.py +28 -0
  30. aip_agents/agent/hitl/config.pyi +15 -0
  31. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  32. aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +42 -0
  33. aip_agents/agent/hitl/manager.py +532 -0
  34. aip_agents/agent/hitl/manager.pyi +200 -0
  35. aip_agents/agent/hitl/models.py +18 -0
  36. aip_agents/agent/hitl/models.pyi +3 -0
  37. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  38. aip_agents/agent/hitl/prompt/__init__.pyi +4 -0
  39. aip_agents/agent/hitl/prompt/base.py +42 -0
  40. aip_agents/agent/hitl/prompt/base.pyi +24 -0
  41. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  42. aip_agents/agent/hitl/prompt/deferred.pyi +30 -0
  43. aip_agents/agent/hitl/registry.py +149 -0
  44. aip_agents/agent/hitl/registry.pyi +101 -0
  45. aip_agents/agent/interface.py +138 -0
  46. aip_agents/agent/interface.pyi +81 -0
  47. aip_agents/agent/interfaces.py +65 -0
  48. aip_agents/agent/interfaces.pyi +44 -0
  49. aip_agents/agent/langflow_agent.py +464 -0
  50. aip_agents/agent/langflow_agent.pyi +133 -0
  51. aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
  52. aip_agents/agent/langgraph_memory_enhancer_agent.pyi +49 -0
  53. aip_agents/agent/langgraph_react_agent.py +2596 -0
  54. aip_agents/agent/langgraph_react_agent.pyi +131 -0
  55. aip_agents/agent/system_instruction_context.py +34 -0
  56. aip_agents/agent/system_instruction_context.pyi +13 -0
  57. aip_agents/clients/__init__.py +10 -0
  58. aip_agents/clients/__init__.pyi +4 -0
  59. aip_agents/clients/langflow/__init__.py +10 -0
  60. aip_agents/clients/langflow/__init__.pyi +4 -0
  61. aip_agents/clients/langflow/client.py +477 -0
  62. aip_agents/clients/langflow/client.pyi +140 -0
  63. aip_agents/clients/langflow/types.py +18 -0
  64. aip_agents/clients/langflow/types.pyi +7 -0
  65. aip_agents/constants.py +23 -0
  66. aip_agents/constants.pyi +7 -0
  67. aip_agents/credentials/manager.py +132 -0
  68. aip_agents/examples/__init__.py +5 -0
  69. aip_agents/examples/__init__.pyi +0 -0
  70. aip_agents/examples/compare_streaming_client.py +783 -0
  71. aip_agents/examples/compare_streaming_client.pyi +48 -0
  72. aip_agents/examples/compare_streaming_server.py +142 -0
  73. aip_agents/examples/compare_streaming_server.pyi +18 -0
  74. aip_agents/examples/demo_memory_recall.py +401 -0
  75. aip_agents/examples/demo_memory_recall.pyi +58 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_client.pyi +9 -0
  78. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  79. aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +9 -0
  80. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  81. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +9 -0
  82. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  83. aip_agents/examples/hello_world_a2a_google_adk_server.pyi +15 -0
  84. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  85. aip_agents/examples/hello_world_a2a_langchain_client.pyi +5 -0
  86. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  87. aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +5 -0
  88. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  89. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +5 -0
  90. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  91. aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +5 -0
  92. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  93. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +5 -0
  94. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  95. aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +15 -0
  96. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  97. aip_agents/examples/hello_world_a2a_langchain_server.pyi +15 -0
  98. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  99. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +15 -0
  100. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  101. aip_agents/examples/hello_world_a2a_langflow_client.pyi +9 -0
  102. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  103. aip_agents/examples/hello_world_a2a_langflow_server.pyi +14 -0
  104. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  105. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +5 -0
  106. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  107. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +5 -0
  108. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  109. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +16 -0
  110. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  111. aip_agents/examples/hello_world_a2a_langgraph_client.pyi +9 -0
  112. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  113. aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +9 -0
  114. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  115. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +2 -0
  116. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  117. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +9 -0
  118. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  119. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +5 -0
  120. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  121. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +5 -0
  122. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  123. aip_agents/examples/hello_world_a2a_langgraph_server.pyi +14 -0
  124. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  125. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +15 -0
  126. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  127. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +15 -0
  128. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  129. aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +48 -0
  130. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  131. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +48 -0
  132. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  133. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +45 -0
  134. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  135. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +5 -0
  136. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  137. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +15 -0
  138. aip_agents/examples/hello_world_google_adk.py +41 -0
  139. aip_agents/examples/hello_world_google_adk.pyi +5 -0
  140. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  141. aip_agents/examples/hello_world_google_adk_mcp_http.pyi +5 -0
  142. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  143. aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +5 -0
  144. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  145. aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +5 -0
  146. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  147. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +5 -0
  148. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  149. aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +5 -0
  150. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  151. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +5 -0
  152. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  153. aip_agents/examples/hello_world_google_adk_stream.pyi +5 -0
  154. aip_agents/examples/hello_world_langchain.py +28 -0
  155. aip_agents/examples/hello_world_langchain.pyi +5 -0
  156. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  157. aip_agents/examples/hello_world_langchain_lm_invoker.pyi +2 -0
  158. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  159. aip_agents/examples/hello_world_langchain_mcp_http.pyi +5 -0
  160. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  161. aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +16 -0
  162. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  163. aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +5 -0
  164. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  165. aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +18 -0
  166. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  167. aip_agents/examples/hello_world_langchain_mcp_sse.pyi +5 -0
  168. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  169. aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +5 -0
  170. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  171. aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +5 -0
  172. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  173. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +5 -0
  174. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  175. aip_agents/examples/hello_world_langchain_stream.pyi +5 -0
  176. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  177. aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +5 -0
  178. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  179. aip_agents/examples/hello_world_langflow_agent.pyi +35 -0
  180. aip_agents/examples/hello_world_langgraph.py +39 -0
  181. aip_agents/examples/hello_world_langgraph.pyi +5 -0
  182. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  183. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +5 -0
  184. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  185. aip_agents/examples/hello_world_langgraph_mcp_http.pyi +5 -0
  186. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  187. aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +5 -0
  188. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  189. aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +5 -0
  190. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  191. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +5 -0
  192. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  193. aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +5 -0
  194. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  195. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +5 -0
  196. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  197. aip_agents/examples/hello_world_langgraph_stream.pyi +5 -0
  198. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  199. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +5 -0
  200. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  201. aip_agents/examples/hello_world_model_switch_cli.pyi +30 -0
  202. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  203. aip_agents/examples/hello_world_multi_agent_adk.pyi +6 -0
  204. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  205. aip_agents/examples/hello_world_multi_agent_langchain.pyi +5 -0
  206. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  207. aip_agents/examples/hello_world_multi_agent_langgraph.pyi +5 -0
  208. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  209. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +5 -0
  210. aip_agents/examples/hello_world_pii_logger.py +21 -0
  211. aip_agents/examples/hello_world_pii_logger.pyi +5 -0
  212. aip_agents/examples/hello_world_sentry.py +133 -0
  213. aip_agents/examples/hello_world_sentry.pyi +21 -0
  214. aip_agents/examples/hello_world_step_limits.py +273 -0
  215. aip_agents/examples/hello_world_step_limits.pyi +17 -0
  216. aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
  217. aip_agents/examples/hello_world_stock_a2a_server.pyi +17 -0
  218. aip_agents/examples/hello_world_tool_output_client.py +46 -0
  219. aip_agents/examples/hello_world_tool_output_client.pyi +5 -0
  220. aip_agents/examples/hello_world_tool_output_server.py +114 -0
  221. aip_agents/examples/hello_world_tool_output_server.pyi +19 -0
  222. aip_agents/examples/hitl_demo.py +724 -0
  223. aip_agents/examples/hitl_demo.pyi +67 -0
  224. aip_agents/examples/mcp_configs/configs.py +63 -0
  225. aip_agents/examples/mcp_servers/common.py +76 -0
  226. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  227. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  228. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  229. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  230. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  231. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  232. aip_agents/examples/pii_demo_langgraph_client.pyi +5 -0
  233. aip_agents/examples/pii_demo_langgraph_server.py +126 -0
  234. aip_agents/examples/pii_demo_langgraph_server.pyi +20 -0
  235. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  236. aip_agents/examples/pii_demo_multi_agent_client.pyi +5 -0
  237. aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
  238. aip_agents/examples/pii_demo_multi_agent_server.pyi +40 -0
  239. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  240. aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +5 -0
  241. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
  242. aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +19 -0
  243. aip_agents/examples/tools/__init__.py +27 -0
  244. aip_agents/examples/tools/__init__.pyi +9 -0
  245. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  246. aip_agents/examples/tools/adk_arithmetic_tools.pyi +24 -0
  247. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  248. aip_agents/examples/tools/adk_weather_tool.pyi +18 -0
  249. aip_agents/examples/tools/data_generator_tool.py +103 -0
  250. aip_agents/examples/tools/data_generator_tool.pyi +15 -0
  251. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  252. aip_agents/examples/tools/data_visualization_tool.pyi +19 -0
  253. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  254. aip_agents/examples/tools/image_artifact_tool.pyi +26 -0
  255. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  256. aip_agents/examples/tools/langchain_arithmetic_tools.pyi +17 -0
  257. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  258. aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +20 -0
  259. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  260. aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +25 -0
  261. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  262. aip_agents/examples/tools/langchain_weather_tool.pyi +19 -0
  263. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  264. aip_agents/examples/tools/langgraph_streaming_tool.pyi +43 -0
  265. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  266. aip_agents/examples/tools/mock_retrieval_tool.pyi +13 -0
  267. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  268. aip_agents/examples/tools/pii_demo_tools.pyi +54 -0
  269. aip_agents/examples/tools/random_chart_tool.py +142 -0
  270. aip_agents/examples/tools/random_chart_tool.pyi +20 -0
  271. aip_agents/examples/tools/serper_tool.py +202 -0
  272. aip_agents/examples/tools/serper_tool.pyi +16 -0
  273. aip_agents/examples/tools/stock_tools.py +82 -0
  274. aip_agents/examples/tools/stock_tools.pyi +36 -0
  275. aip_agents/examples/tools/table_generator_tool.py +167 -0
  276. aip_agents/examples/tools/table_generator_tool.pyi +22 -0
  277. aip_agents/examples/tools/time_tool.py +82 -0
  278. aip_agents/examples/tools/time_tool.pyi +15 -0
  279. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  280. aip_agents/examples/tools/weather_forecast_tool.pyi +14 -0
  281. aip_agents/executor/agent_executor.py +473 -0
  282. aip_agents/executor/base.py +48 -0
  283. aip_agents/guardrails/__init__.py +83 -0
  284. aip_agents/guardrails/__init__.pyi +6 -0
  285. aip_agents/guardrails/engines/__init__.py +69 -0
  286. aip_agents/guardrails/engines/__init__.pyi +4 -0
  287. aip_agents/guardrails/engines/base.py +90 -0
  288. aip_agents/guardrails/engines/base.pyi +61 -0
  289. aip_agents/guardrails/engines/nemo.py +101 -0
  290. aip_agents/guardrails/engines/nemo.pyi +46 -0
  291. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  292. aip_agents/guardrails/engines/phrase_matcher.pyi +48 -0
  293. aip_agents/guardrails/exceptions.py +39 -0
  294. aip_agents/guardrails/exceptions.pyi +23 -0
  295. aip_agents/guardrails/manager.py +163 -0
  296. aip_agents/guardrails/manager.pyi +42 -0
  297. aip_agents/guardrails/middleware.py +199 -0
  298. aip_agents/guardrails/middleware.pyi +87 -0
  299. aip_agents/guardrails/schemas.py +63 -0
  300. aip_agents/guardrails/schemas.pyi +43 -0
  301. aip_agents/guardrails/utils.py +45 -0
  302. aip_agents/guardrails/utils.pyi +19 -0
  303. aip_agents/mcp/__init__.py +1 -0
  304. aip_agents/mcp/__init__.pyi +0 -0
  305. aip_agents/mcp/client/__init__.py +14 -0
  306. aip_agents/mcp/client/__init__.pyi +5 -0
  307. aip_agents/mcp/client/base_mcp_client.py +369 -0
  308. aip_agents/mcp/client/base_mcp_client.pyi +148 -0
  309. aip_agents/mcp/client/connection_manager.py +193 -0
  310. aip_agents/mcp/client/connection_manager.pyi +48 -0
  311. aip_agents/mcp/client/google_adk/__init__.py +11 -0
  312. aip_agents/mcp/client/google_adk/__init__.pyi +3 -0
  313. aip_agents/mcp/client/google_adk/client.py +381 -0
  314. aip_agents/mcp/client/google_adk/client.pyi +75 -0
  315. aip_agents/mcp/client/langchain/__init__.py +11 -0
  316. aip_agents/mcp/client/langchain/__init__.pyi +3 -0
  317. aip_agents/mcp/client/langchain/client.py +265 -0
  318. aip_agents/mcp/client/langchain/client.pyi +48 -0
  319. aip_agents/mcp/client/persistent_session.py +362 -0
  320. aip_agents/mcp/client/persistent_session.pyi +113 -0
  321. aip_agents/mcp/client/session_pool.py +351 -0
  322. aip_agents/mcp/client/session_pool.pyi +101 -0
  323. aip_agents/mcp/client/transports.py +228 -0
  324. aip_agents/mcp/client/transports.pyi +123 -0
  325. aip_agents/mcp/utils/__init__.py +7 -0
  326. aip_agents/mcp/utils/__init__.pyi +0 -0
  327. aip_agents/mcp/utils/config_validator.py +139 -0
  328. aip_agents/mcp/utils/config_validator.pyi +82 -0
  329. aip_agents/memory/__init__.py +14 -0
  330. aip_agents/memory/__init__.pyi +5 -0
  331. aip_agents/memory/adapters/__init__.py +10 -0
  332. aip_agents/memory/adapters/__init__.pyi +4 -0
  333. aip_agents/memory/adapters/base_adapter.py +717 -0
  334. aip_agents/memory/adapters/base_adapter.pyi +150 -0
  335. aip_agents/memory/adapters/mem0.py +84 -0
  336. aip_agents/memory/adapters/mem0.pyi +22 -0
  337. aip_agents/memory/base.py +84 -0
  338. aip_agents/memory/base.pyi +60 -0
  339. aip_agents/memory/constants.py +49 -0
  340. aip_agents/memory/constants.pyi +25 -0
  341. aip_agents/memory/factory.py +86 -0
  342. aip_agents/memory/factory.pyi +24 -0
  343. aip_agents/memory/guidance.py +20 -0
  344. aip_agents/memory/guidance.pyi +3 -0
  345. aip_agents/memory/simple_memory.py +47 -0
  346. aip_agents/memory/simple_memory.pyi +23 -0
  347. aip_agents/middleware/__init__.py +17 -0
  348. aip_agents/middleware/__init__.pyi +5 -0
  349. aip_agents/middleware/base.py +96 -0
  350. aip_agents/middleware/base.pyi +75 -0
  351. aip_agents/middleware/manager.py +150 -0
  352. aip_agents/middleware/manager.pyi +84 -0
  353. aip_agents/middleware/todolist.py +274 -0
  354. aip_agents/middleware/todolist.pyi +125 -0
  355. aip_agents/schema/__init__.py +69 -0
  356. aip_agents/schema/__init__.pyi +9 -0
  357. aip_agents/schema/a2a.py +56 -0
  358. aip_agents/schema/a2a.pyi +40 -0
  359. aip_agents/schema/agent.py +111 -0
  360. aip_agents/schema/agent.pyi +65 -0
  361. aip_agents/schema/hitl.py +157 -0
  362. aip_agents/schema/hitl.pyi +89 -0
  363. aip_agents/schema/langgraph.py +37 -0
  364. aip_agents/schema/langgraph.pyi +28 -0
  365. aip_agents/schema/model_id.py +97 -0
  366. aip_agents/schema/model_id.pyi +54 -0
  367. aip_agents/schema/step_limit.py +108 -0
  368. aip_agents/schema/step_limit.pyi +63 -0
  369. aip_agents/schema/storage.py +40 -0
  370. aip_agents/schema/storage.pyi +21 -0
  371. aip_agents/sentry/__init__.py +11 -0
  372. aip_agents/sentry/__init__.pyi +3 -0
  373. aip_agents/sentry/sentry.py +151 -0
  374. aip_agents/sentry/sentry.pyi +48 -0
  375. aip_agents/storage/__init__.py +41 -0
  376. aip_agents/storage/__init__.pyi +8 -0
  377. aip_agents/storage/base.py +85 -0
  378. aip_agents/storage/base.pyi +58 -0
  379. aip_agents/storage/clients/__init__.py +12 -0
  380. aip_agents/storage/clients/__init__.pyi +3 -0
  381. aip_agents/storage/clients/minio_client.py +318 -0
  382. aip_agents/storage/clients/minio_client.pyi +137 -0
  383. aip_agents/storage/config.py +62 -0
  384. aip_agents/storage/config.pyi +29 -0
  385. aip_agents/storage/providers/__init__.py +15 -0
  386. aip_agents/storage/providers/__init__.pyi +5 -0
  387. aip_agents/storage/providers/base.py +106 -0
  388. aip_agents/storage/providers/base.pyi +88 -0
  389. aip_agents/storage/providers/memory.py +114 -0
  390. aip_agents/storage/providers/memory.pyi +79 -0
  391. aip_agents/storage/providers/object_storage.py +214 -0
  392. aip_agents/storage/providers/object_storage.pyi +98 -0
  393. aip_agents/tools/__init__.py +53 -0
  394. aip_agents/tools/__init__.pyi +9 -0
  395. aip_agents/tools/browser_use/__init__.py +82 -0
  396. aip_agents/tools/browser_use/__init__.pyi +14 -0
  397. aip_agents/tools/browser_use/action_parser.py +103 -0
  398. aip_agents/tools/browser_use/action_parser.pyi +18 -0
  399. aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
  400. aip_agents/tools/browser_use/browser_use_tool.pyi +50 -0
  401. aip_agents/tools/browser_use/llm_config.py +120 -0
  402. aip_agents/tools/browser_use/llm_config.pyi +52 -0
  403. aip_agents/tools/browser_use/minio_storage.py +198 -0
  404. aip_agents/tools/browser_use/minio_storage.pyi +109 -0
  405. aip_agents/tools/browser_use/schemas.py +119 -0
  406. aip_agents/tools/browser_use/schemas.pyi +32 -0
  407. aip_agents/tools/browser_use/session.py +76 -0
  408. aip_agents/tools/browser_use/session.pyi +4 -0
  409. aip_agents/tools/browser_use/session_errors.py +132 -0
  410. aip_agents/tools/browser_use/session_errors.pyi +53 -0
  411. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  412. aip_agents/tools/browser_use/steel_session_recording.pyi +63 -0
  413. aip_agents/tools/browser_use/streaming.py +813 -0
  414. aip_agents/tools/browser_use/streaming.pyi +81 -0
  415. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  416. aip_agents/tools/browser_use/structured_data_parser.pyi +86 -0
  417. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  418. aip_agents/tools/browser_use/structured_data_recovery.pyi +43 -0
  419. aip_agents/tools/browser_use/types.py +78 -0
  420. aip_agents/tools/browser_use/types.pyi +45 -0
  421. aip_agents/tools/code_sandbox/__init__.py +26 -0
  422. aip_agents/tools/code_sandbox/__init__.pyi +3 -0
  423. aip_agents/tools/code_sandbox/constant.py +13 -0
  424. aip_agents/tools/code_sandbox/constant.pyi +4 -0
  425. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +306 -0
  426. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +102 -0
  427. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  428. aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +29 -0
  429. aip_agents/tools/constants.py +177 -0
  430. aip_agents/tools/constants.pyi +138 -0
  431. aip_agents/tools/document_loader/__init__.py +44 -0
  432. aip_agents/tools/document_loader/__init__.pyi +7 -0
  433. aip_agents/tools/document_loader/base_reader.py +302 -0
  434. aip_agents/tools/document_loader/base_reader.pyi +75 -0
  435. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  436. aip_agents/tools/document_loader/docx_reader_tool.pyi +10 -0
  437. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  438. aip_agents/tools/document_loader/excel_reader_tool.pyi +26 -0
  439. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  440. aip_agents/tools/document_loader/pdf_reader_tool.pyi +11 -0
  441. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  442. aip_agents/tools/document_loader/pdf_splitter.pyi +18 -0
  443. aip_agents/tools/gl_connector/__init__.py +5 -0
  444. aip_agents/tools/gl_connector/__init__.pyi +3 -0
  445. aip_agents/tools/gl_connector/tool.py +383 -0
  446. aip_agents/tools/gl_connector/tool.pyi +74 -0
  447. aip_agents/tools/gl_connector_tools.py +119 -0
  448. aip_agents/tools/gl_connector_tools.pyi +39 -0
  449. aip_agents/tools/memory_search/__init__.py +22 -0
  450. aip_agents/tools/memory_search/__init__.pyi +5 -0
  451. aip_agents/tools/memory_search/base.py +200 -0
  452. aip_agents/tools/memory_search/base.pyi +69 -0
  453. aip_agents/tools/memory_search/mem0.py +258 -0
  454. aip_agents/tools/memory_search/mem0.pyi +19 -0
  455. aip_agents/tools/memory_search/schema.py +48 -0
  456. aip_agents/tools/memory_search/schema.pyi +15 -0
  457. aip_agents/tools/memory_search_tool.py +26 -0
  458. aip_agents/tools/memory_search_tool.pyi +3 -0
  459. aip_agents/tools/time_tool.py +117 -0
  460. aip_agents/tools/time_tool.pyi +16 -0
  461. aip_agents/tools/tool_config_injector.py +300 -0
  462. aip_agents/tools/tool_config_injector.pyi +26 -0
  463. aip_agents/tools/web_search/__init__.py +15 -0
  464. aip_agents/tools/web_search/__init__.pyi +3 -0
  465. aip_agents/tools/web_search/serper_tool.py +187 -0
  466. aip_agents/tools/web_search/serper_tool.pyi +19 -0
  467. aip_agents/types/__init__.py +70 -0
  468. aip_agents/types/__init__.pyi +36 -0
  469. aip_agents/types/a2a_events.py +13 -0
  470. aip_agents/types/a2a_events.pyi +3 -0
  471. aip_agents/utils/__init__.py +79 -0
  472. aip_agents/utils/__init__.pyi +11 -0
  473. aip_agents/utils/a2a_connector.py +1757 -0
  474. aip_agents/utils/a2a_connector.pyi +146 -0
  475. aip_agents/utils/artifact_helpers.py +502 -0
  476. aip_agents/utils/artifact_helpers.pyi +203 -0
  477. aip_agents/utils/constants.py +22 -0
  478. aip_agents/utils/constants.pyi +10 -0
  479. aip_agents/utils/datetime/__init__.py +34 -0
  480. aip_agents/utils/datetime/__init__.pyi +4 -0
  481. aip_agents/utils/datetime/normalization.py +231 -0
  482. aip_agents/utils/datetime/normalization.pyi +95 -0
  483. aip_agents/utils/datetime/timezone.py +206 -0
  484. aip_agents/utils/datetime/timezone.pyi +48 -0
  485. aip_agents/utils/env_loader.py +27 -0
  486. aip_agents/utils/env_loader.pyi +10 -0
  487. aip_agents/utils/event_handler_registry.py +58 -0
  488. aip_agents/utils/event_handler_registry.pyi +23 -0
  489. aip_agents/utils/file_prompt_utils.py +176 -0
  490. aip_agents/utils/file_prompt_utils.pyi +21 -0
  491. aip_agents/utils/final_response_builder.py +211 -0
  492. aip_agents/utils/final_response_builder.pyi +34 -0
  493. aip_agents/utils/formatter_llm_client.py +231 -0
  494. aip_agents/utils/formatter_llm_client.pyi +71 -0
  495. aip_agents/utils/langgraph/__init__.py +19 -0
  496. aip_agents/utils/langgraph/__init__.pyi +3 -0
  497. aip_agents/utils/langgraph/converter.py +128 -0
  498. aip_agents/utils/langgraph/converter.pyi +49 -0
  499. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  500. aip_agents/utils/langgraph/tool_managers/__init__.pyi +5 -0
  501. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  502. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +35 -0
  503. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  504. aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +48 -0
  505. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
  506. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +56 -0
  507. aip_agents/utils/langgraph/tool_output_management.py +967 -0
  508. aip_agents/utils/langgraph/tool_output_management.pyi +292 -0
  509. aip_agents/utils/logger.py +195 -0
  510. aip_agents/utils/logger.pyi +60 -0
  511. aip_agents/utils/metadata/__init__.py +27 -0
  512. aip_agents/utils/metadata/__init__.pyi +5 -0
  513. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  514. aip_agents/utils/metadata/activity_metadata_helper.pyi +25 -0
  515. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  516. aip_agents/utils/metadata/activity_narrative/__init__.pyi +7 -0
  517. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  518. aip_agents/utils/metadata/activity_narrative/builder.pyi +35 -0
  519. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  520. aip_agents/utils/metadata/activity_narrative/constants.pyi +10 -0
  521. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  522. aip_agents/utils/metadata/activity_narrative/context.pyi +32 -0
  523. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  524. aip_agents/utils/metadata/activity_narrative/formatters.pyi +48 -0
  525. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  526. aip_agents/utils/metadata/activity_narrative/utils.pyi +12 -0
  527. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  528. aip_agents/utils/metadata/schemas/__init__.pyi +4 -0
  529. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  530. aip_agents/utils/metadata/schemas/activity_schema.pyi +18 -0
  531. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  532. aip_agents/utils/metadata/schemas/thinking_schema.pyi +20 -0
  533. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  534. aip_agents/utils/metadata/thinking_metadata_helper.pyi +4 -0
  535. aip_agents/utils/metadata_helper.py +358 -0
  536. aip_agents/utils/metadata_helper.pyi +117 -0
  537. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  538. aip_agents/utils/name_preprocessor/__init__.pyi +6 -0
  539. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  540. aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +52 -0
  541. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  542. aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +38 -0
  543. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  544. aip_agents/utils/name_preprocessor/name_preprocessor.pyi +41 -0
  545. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  546. aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +34 -0
  547. aip_agents/utils/pii/__init__.py +25 -0
  548. aip_agents/utils/pii/__init__.pyi +5 -0
  549. aip_agents/utils/pii/pii_handler.py +397 -0
  550. aip_agents/utils/pii/pii_handler.pyi +96 -0
  551. aip_agents/utils/pii/pii_helper.py +207 -0
  552. aip_agents/utils/pii/pii_helper.pyi +78 -0
  553. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  554. aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +73 -0
  555. aip_agents/utils/reference_helper.py +273 -0
  556. aip_agents/utils/reference_helper.pyi +81 -0
  557. aip_agents/utils/sse_chunk_transformer.py +831 -0
  558. aip_agents/utils/sse_chunk_transformer.pyi +166 -0
  559. aip_agents/utils/step_limit_manager.py +265 -0
  560. aip_agents/utils/step_limit_manager.pyi +112 -0
  561. aip_agents/utils/token_usage_helper.py +156 -0
  562. aip_agents/utils/token_usage_helper.pyi +60 -0
  563. aip_agents_binary-0.5.25b1.dist-info/METADATA +681 -0
  564. aip_agents_binary-0.5.25b1.dist-info/RECORD +566 -0
  565. aip_agents_binary-0.5.25b1.dist-info/WHEEL +5 -0
  566. aip_agents_binary-0.5.25b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,397 @@
1
+ """PII handler for masking/demasking at the tool calling level.
2
+
3
+ This module provides the ToolPIIHandler class for handling PII operations
4
+ during tool execution in LangGraph agents. Tag replacement works with the
5
+ mapping supplied by the runner, while advanced NER-powered detection is only
6
+ enabled when NER_API_URL and NER_API_KEY environment variables are set.
7
+
8
+ Authors:
9
+ Fachriza Adhiatma (fachriza.d.adhiatma@gdplabs.id)
10
+
11
+ References:
12
+ 1. https://gdplabs.gitbook.io/sdk/tutorials/security-and-privacy/pii-masking#anonymizer-with-ner
13
+ """
14
+
15
+ import os
16
+ from enum import Enum
17
+ from typing import Any
18
+
19
+ try:
20
+ from gllm_privacy.pii_detector import TextAnalyzer, TextAnonymizer
21
+ from gllm_privacy.pii_detector.anonymizer import Operation
22
+ from gllm_privacy.pii_detector.constants import GLLM_PRIVACY_ENTITIES, Entities
23
+ from gllm_privacy.pii_detector.recognizer.gdplabs_ner_api_remote_recognizer import (
24
+ GDPLabsNerApiRemoteRecognizer,
25
+ )
26
+
27
+ _HAS_GLLM_PRIVACY = True
28
+ except ImportError: # pragma: no cover
29
+ TextAnalyzer = Any # type: ignore[assignment]
30
+ TextAnonymizer = Any # type: ignore[assignment]
31
+ GDPLabsNerApiRemoteRecognizer = Any # type: ignore[assignment]
32
+
33
+ class _Operation(str, Enum):
34
+ ANONYMIZE = "ANONYMIZE"
35
+ DEANONYMIZE = "DEANONYMIZE"
36
+
37
+ Operation = _Operation
38
+
39
+ GLLM_PRIVACY_ENTITIES = [] # type: ignore[assignment]
40
+ Entities = None # type: ignore[assignment]
41
+ _HAS_GLLM_PRIVACY = False
42
+
43
+ from aip_agents.utils.logger import LoggerManager
44
+ from aip_agents.utils.pii.uuid_deanonymizer_mapping import UUIDDeanonymizerMapping
45
+
46
+ logger = LoggerManager().get_logger(__name__)
47
+
48
+ NER_API_URL_ENV_VAR = "NER_API_URL"
49
+ NER_API_KEY_ENV_VAR = "NER_API_KEY"
50
+ NER_API_TIMEOUT = 10
51
+ if _HAS_GLLM_PRIVACY:
52
+ EXCLUDED_ENTITIES = [Entities.URL.value]
53
+ DEFAULT_SUPPORTED_ENTITIES = [entity for entity in GLLM_PRIVACY_ENTITIES if entity not in EXCLUDED_ENTITIES]
54
+ else:
55
+ EXCLUDED_ENTITIES = []
56
+ DEFAULT_SUPPORTED_ENTITIES = []
57
+
58
+
59
+ class ToolPIIHandler:
60
+ """Handles PII masking/demasking for tool calling.
61
+
62
+ Tag replacement based on runner-provided mappings always works. Optional
63
+ NER-powered masking/de-masking is only enabled when NER_API_URL and
64
+ NER_API_KEY environment variables are set.
65
+
66
+ Attributes:
67
+ flat_pii_mapping: Flat mapping from runner service (tag → value)
68
+ text_analyzer: GLLM Privacy TextAnalyzer instance
69
+ text_anonymizer: GLLM Privacy TextAnonymizer instance
70
+ enable_ner: Whether NER is enabled
71
+ """
72
+
73
+ def __init__(
74
+ self,
75
+ pii_mapping: dict[str, str] | None = None,
76
+ ner_api_url: str | None = None,
77
+ ner_api_key: str | None = None,
78
+ ) -> None:
79
+ """Initialize PII handler (private - use create_if_enabled() instead).
80
+
81
+ Initializes GLLM Privacy components (TextAnalyzer, TextAnonymizer) if NER credentials
82
+ are provided. Creates dual recognizers for Indonesian and English languages.
83
+ Pre-loads any existing PII mappings into the anonymizer's internal state.
84
+
85
+ Args:
86
+ pii_mapping: Existing PII mapping from runner service (flat format: tag -> value)
87
+ ner_api_url: NER API endpoint URL
88
+ ner_api_key: NER API authentication key
89
+ """
90
+ self.flat_pii_mapping: dict[str, str] = pii_mapping or {}
91
+ self.enable_ner: bool = bool(ner_api_url and ner_api_key)
92
+ self.text_analyzer: TextAnalyzer | None = None
93
+ self.text_anonymizer: TextAnonymizer | None = None
94
+
95
+ if self.enable_ner and not _HAS_GLLM_PRIVACY:
96
+ logger.warning(
97
+ "NER is configured (NER_API_URL/NER_API_KEY present) but optional dependency 'gllm-privacy' "
98
+ "is not installed. Continuing with NER disabled."
99
+ )
100
+ self.enable_ner = False
101
+
102
+ if self.enable_ner:
103
+ try:
104
+ headers = {"X-Api-Key": ner_api_key}
105
+
106
+ id_recognizer = GDPLabsNerApiRemoteRecognizer(
107
+ api_url=ner_api_url,
108
+ supported_language="id",
109
+ api_headers=headers,
110
+ api_timeout=NER_API_TIMEOUT,
111
+ )
112
+ en_recognizer = GDPLabsNerApiRemoteRecognizer(
113
+ api_url=ner_api_url,
114
+ supported_language="en",
115
+ api_headers=headers,
116
+ api_timeout=NER_API_TIMEOUT,
117
+ )
118
+
119
+ self.text_analyzer = TextAnalyzer(additional_recognizers=[id_recognizer, en_recognizer])
120
+
121
+ # Initialize with UUID-based deanonymizer mapping
122
+ uuid_mapping = UUIDDeanonymizerMapping(uuid_length=8)
123
+ self.text_anonymizer = TextAnonymizer(
124
+ text_analyzer=self.text_analyzer,
125
+ deanonymizer_mapping=uuid_mapping,
126
+ )
127
+
128
+ if self.flat_pii_mapping:
129
+ gllm_mapping = self._convert_flat_to_gllm_format(self.flat_pii_mapping)
130
+ self.text_anonymizer._deanonymizer_mapping.update(gllm_mapping, use_uuid_suffix=False)
131
+
132
+ except Exception as e: # noqa: BLE001
133
+ logger.warning(f"Failed to initialize GLLM Privacy components: {e}")
134
+ self.enable_ner = False
135
+
136
+ @classmethod
137
+ def create_if_enabled(cls, pii_mapping: dict[str, str] | None = None) -> "ToolPIIHandler | None":
138
+ """Create ToolPIIHandler when mappings or NER configuration exist.
139
+
140
+ Args:
141
+ pii_mapping: Existing PII mapping from runner service
142
+
143
+ Returns:
144
+ ToolPIIHandler instance when mapping or NER config is available, None otherwise
145
+ """
146
+ ner_api_url = os.getenv(NER_API_URL_ENV_VAR)
147
+ ner_api_key = os.getenv(NER_API_KEY_ENV_VAR)
148
+
149
+ if ner_api_url and ner_api_key:
150
+ return cls(pii_mapping, ner_api_url, ner_api_key)
151
+
152
+ if pii_mapping:
153
+ return cls(pii_mapping)
154
+
155
+ return None
156
+
157
+ @classmethod
158
+ def create_mapping_only(cls, pii_mapping: dict[str, str] | None = None) -> "ToolPIIHandler | None":
159
+ """Create ToolPIIHandler in mapping-only mode (no NER).
160
+
161
+ Args:
162
+ pii_mapping: Existing PII mapping from runner service
163
+
164
+ Returns:
165
+ ToolPIIHandler instance when mapping exists, None otherwise
166
+ """
167
+ if not pii_mapping:
168
+ return None
169
+
170
+ return cls(pii_mapping)
171
+
172
+ def deanonymize_tool_args(self, args: dict[str, Any]) -> dict[str, Any]:
173
+ """Replace PII tags in tool arguments with real values.
174
+
175
+ Recursively processes dictionaries, lists, and strings to replace all PII tags
176
+ (e.g., '<EMAIL_1>') with their corresponding real values from flat_pii_mapping.
177
+
178
+ Args:
179
+ args: Tool arguments that may contain PII tags
180
+
181
+ Returns:
182
+ Arguments with tags replaced by real values
183
+ """
184
+ return self._process_value(args, operation=Operation.DEANONYMIZE)
185
+
186
+ def anonymize_tool_output(self, output: Any) -> tuple[Any, dict[str, str]]:
187
+ """Mask PII values in tool output.
188
+
189
+ Handles string and dictionary outputs. For strings, uses two-phase anonymization:
190
+ first masks known PII, then detects new PII via NER. For dictionaries, recursively
191
+ processes all string values. Returns updated mapping with any newly discovered PII.
192
+
193
+ Args:
194
+ output: Tool output that may contain PII values (string, dict, or other)
195
+
196
+ Returns:
197
+ Tuple of (anonymized_output, updated_flat_pii_mapping)
198
+ """
199
+ if isinstance(output, str):
200
+ anonymized, updated_mapping = self._anonymize_text(output)
201
+ return anonymized, updated_mapping
202
+ elif isinstance(output, dict):
203
+ anonymized, updated_mapping = self._anonymize_dict(output)
204
+ return anonymized, updated_mapping
205
+ else:
206
+ # For non-string, non-dict outputs, return as-is
207
+ return output, self.flat_pii_mapping
208
+
209
+ @staticmethod
210
+ def _convert_flat_to_gllm_format(
211
+ flat_mapping: dict[str, str],
212
+ ) -> dict[str, dict[str, str]]:
213
+ """Convert flat PII mapping to GLLM Privacy nested format.
214
+
215
+ Transforms flat format {'<PERSON_1>': 'Alice'} to nested format
216
+ {'PERSON': {'<PERSON_1>': 'Alice'}} required by GLLM Privacy's internal state.
217
+
218
+ Args:
219
+ flat_mapping: Flat mapping (tag → value)
220
+
221
+ Returns:
222
+ Nested mapping organized by entity type
223
+ """
224
+ gllm_mapping: dict[str, dict[str, str]] = {}
225
+
226
+ for tag, value in flat_mapping.items():
227
+ tag_content = tag.strip("<>")
228
+ parts = tag_content.rsplit("_", 1)
229
+ entity_type = parts[0] if parts else "UNKNOWN"
230
+
231
+ if entity_type not in gllm_mapping:
232
+ gllm_mapping[entity_type] = {}
233
+
234
+ gllm_mapping[entity_type][tag] = value
235
+
236
+ return gllm_mapping
237
+
238
+ @staticmethod
239
+ def _convert_gllm_to_flat_format(
240
+ gllm_mapping: dict[str, dict[str, str]],
241
+ ) -> dict[str, str]:
242
+ """Convert GLLM Privacy nested format back to flat format.
243
+
244
+ Inverse of _convert_flat_to_gllm_format. Flattens nested structure
245
+ {'PERSON': {'<PERSON_1>': 'Alice'}} back to {'<PERSON_1>': 'Alice'}.
246
+
247
+ Args:
248
+ gllm_mapping: Nested mapping from GLLM Privacy
249
+
250
+ Returns:
251
+ Flat mapping (tag → value)
252
+ """
253
+ flat_mapping: dict[str, str] = {}
254
+
255
+ for entity_type, tags_dict in gllm_mapping.items():
256
+ for tag, value in tags_dict.items():
257
+ flat_mapping[tag] = value
258
+
259
+ return flat_mapping
260
+
261
+ def _deanonymize_text(self, text: str) -> str:
262
+ """Deanonymize a single text string by replacing PII tags with real values.
263
+
264
+ Uses GLLM Privacy's TextAnonymizer.deanonymize() when NER is enabled,
265
+ otherwise falls back to simple string replacement. GLLM Privacy's deanonymize()
266
+ doesn't require entities parameter as they're configured during TextAnalyzer init.
267
+
268
+ Args:
269
+ text: Text containing PII tags (e.g., '<EMAIL_1>', '<PERSON_1>')
270
+
271
+ Returns:
272
+ Text with PII tags replaced by their real values from flat_pii_mapping
273
+ """
274
+ if self.enable_ner and self.text_anonymizer:
275
+ try:
276
+ restored_text = self.text_anonymizer.deanonymize(text=text)
277
+ return restored_text
278
+ except Exception as e:
279
+ logger.warning(f"GLLM Privacy deanonymization failed: {e}, falling back to simple replacement")
280
+ return self._replace_tags_in_text(text)
281
+ else:
282
+ return self._replace_tags_in_text(text)
283
+
284
+ def _anonymize_text(self, text: str) -> tuple[str, dict[str, str]]:
285
+ """Anonymize PII in text using a two-phase approach.
286
+
287
+ Phase 1: Masks known PII values using existing flat_pii_mapping (simple replacement).
288
+ Phase 2: Uses NER (if enabled) to detect and mask NEW PII values not in mapping.
289
+ Phase 3: Extracts newly discovered PII from GLLM Privacy's internal state.
290
+ Phase 4-5: Converts and merges new mappings into flat_pii_mapping.
291
+
292
+ Args:
293
+ text: Text that may contain real PII values to be masked
294
+
295
+ Returns:
296
+ Tuple of (anonymized_text, updated_flat_pii_mapping including new discoveries)
297
+ """
298
+ anonymized = self._mask_with_existing_mapping(text)
299
+
300
+ if self.enable_ner and self.text_anonymizer:
301
+ try:
302
+ result = self.text_anonymizer.anonymize(text=anonymized, entities=DEFAULT_SUPPORTED_ENTITIES)
303
+ if hasattr(result, "text"):
304
+ anonymized = result.text
305
+ else:
306
+ anonymized = result
307
+
308
+ gllm_mapping = self.text_anonymizer.deanonymizer_mapping
309
+ if gllm_mapping:
310
+ new_flat_mapping = self._convert_gllm_to_flat_format(gllm_mapping)
311
+ self.flat_pii_mapping.update(new_flat_mapping)
312
+ except Exception as e:
313
+ logger.warning(f"GLLM Privacy anonymization failed: {e}, continuing with masked text")
314
+
315
+ return anonymized, self.flat_pii_mapping
316
+
317
+ def _anonymize_dict(self, data: dict[str, Any]) -> tuple[dict[str, Any], dict[str, str]]:
318
+ """Anonymize PII in dictionary recursively.
319
+
320
+ Processes string values with _anonymize_text(), recursively handles nested dicts,
321
+ and processes list items. Non-string/dict/list values are preserved as-is.
322
+
323
+ Args:
324
+ data: Dictionary that may contain PII values
325
+
326
+ Returns:
327
+ Tuple of (anonymized_dict, updated_mapping)
328
+ """
329
+ anonymized = {}
330
+
331
+ for key, value in data.items():
332
+ if isinstance(value, str):
333
+ anonymized_value, _ = self._anonymize_text(value)
334
+ anonymized[key] = anonymized_value
335
+ elif isinstance(value, dict):
336
+ anonymized[key], _ = self._anonymize_dict(value)
337
+ elif isinstance(value, list):
338
+ anonymized[key] = [self._anonymize_text(item)[0] if isinstance(item, str) else item for item in value]
339
+ else:
340
+ anonymized[key] = value
341
+
342
+ return anonymized, self.flat_pii_mapping
343
+
344
+ def _mask_with_existing_mapping(self, text: str) -> str:
345
+ """Mask PII using existing flat mapping.
346
+
347
+ Iterates through mapping in reverse order of value length to handle overlapping
348
+ PII values correctly (longer values are replaced first).
349
+
350
+ Args:
351
+ text: Text to mask
352
+
353
+ Returns:
354
+ Text with known PII values replaced by tags
355
+ """
356
+ masked = text
357
+ for tag, value in sorted(self.flat_pii_mapping.items(), key=lambda x: len(x[1]), reverse=True):
358
+ if value in masked:
359
+ masked = masked.replace(value, tag)
360
+ return masked
361
+
362
+ def _replace_tags_in_text(self, text: str) -> str:
363
+ """Replace PII tags with real values in text using flat mapping.
364
+
365
+ Args:
366
+ text: Text containing PII tags
367
+
368
+ Returns:
369
+ Text with tags replaced by values
370
+ """
371
+ replaced = text
372
+ for tag, value in self.flat_pii_mapping.items():
373
+ if tag in replaced:
374
+ replaced = replaced.replace(tag, value)
375
+ return replaced
376
+
377
+ def _process_value(self, value: Any, operation: Operation) -> Any:
378
+ """Process a value recursively based on operation type.
379
+
380
+ Args:
381
+ value: Value to process
382
+ operation: Operation enum value (ANONYMIZE or DEANONYMIZE)
383
+
384
+ Returns:
385
+ Processed value
386
+ """
387
+ if isinstance(value, str):
388
+ if operation == Operation.DEANONYMIZE:
389
+ return self._deanonymize_text(value)
390
+ else:
391
+ return self._anonymize_text(value)[0]
392
+ elif isinstance(value, dict):
393
+ return {k: self._process_value(v, operation) for k, v in value.items()}
394
+ elif isinstance(value, list):
395
+ return [self._process_value(item, operation) for item in value]
396
+ else:
397
+ return value
@@ -0,0 +1,96 @@
1
+ from _typeshed import Incomplete
2
+ from aip_agents.utils.logger import LoggerManager as LoggerManager
3
+ from aip_agents.utils.pii.uuid_deanonymizer_mapping import UUIDDeanonymizerMapping as UUIDDeanonymizerMapping
4
+ from enum import Enum
5
+ from gllm_privacy.pii_detector import TextAnalyzer, TextAnonymizer
6
+ from typing import Any
7
+
8
+ TextAnalyzer = Any
9
+ TextAnonymizer = Any
10
+ GDPLabsNerApiRemoteRecognizer = Any
11
+
12
+ class _Operation(str, Enum):
13
+ ANONYMIZE: str
14
+ DEANONYMIZE: str
15
+
16
+ logger: Incomplete
17
+ NER_API_URL_ENV_VAR: str
18
+ NER_API_KEY_ENV_VAR: str
19
+ NER_API_TIMEOUT: int
20
+ EXCLUDED_ENTITIES: Incomplete
21
+ DEFAULT_SUPPORTED_ENTITIES: Incomplete
22
+
23
+ class ToolPIIHandler:
24
+ """Handles PII masking/demasking for tool calling.
25
+
26
+ Tag replacement based on runner-provided mappings always works. Optional
27
+ NER-powered masking/de-masking is only enabled when NER_API_URL and
28
+ NER_API_KEY environment variables are set.
29
+
30
+ Attributes:
31
+ flat_pii_mapping: Flat mapping from runner service (tag → value)
32
+ text_analyzer: GLLM Privacy TextAnalyzer instance
33
+ text_anonymizer: GLLM Privacy TextAnonymizer instance
34
+ enable_ner: Whether NER is enabled
35
+ """
36
+ flat_pii_mapping: dict[str, str]
37
+ enable_ner: bool
38
+ text_analyzer: TextAnalyzer | None
39
+ text_anonymizer: TextAnonymizer | None
40
+ def __init__(self, pii_mapping: dict[str, str] | None = None, ner_api_url: str | None = None, ner_api_key: str | None = None) -> None:
41
+ """Initialize PII handler (private - use create_if_enabled() instead).
42
+
43
+ Initializes GLLM Privacy components (TextAnalyzer, TextAnonymizer) if NER credentials
44
+ are provided. Creates dual recognizers for Indonesian and English languages.
45
+ Pre-loads any existing PII mappings into the anonymizer's internal state.
46
+
47
+ Args:
48
+ pii_mapping: Existing PII mapping from runner service (flat format: tag -> value)
49
+ ner_api_url: NER API endpoint URL
50
+ ner_api_key: NER API authentication key
51
+ """
52
+ @classmethod
53
+ def create_if_enabled(cls, pii_mapping: dict[str, str] | None = None) -> ToolPIIHandler | None:
54
+ """Create ToolPIIHandler when mappings or NER configuration exist.
55
+
56
+ Args:
57
+ pii_mapping: Existing PII mapping from runner service
58
+
59
+ Returns:
60
+ ToolPIIHandler instance when mapping or NER config is available, None otherwise
61
+ """
62
+ @classmethod
63
+ def create_mapping_only(cls, pii_mapping: dict[str, str] | None = None) -> ToolPIIHandler | None:
64
+ """Create ToolPIIHandler in mapping-only mode (no NER).
65
+
66
+ Args:
67
+ pii_mapping: Existing PII mapping from runner service
68
+
69
+ Returns:
70
+ ToolPIIHandler instance when mapping exists, None otherwise
71
+ """
72
+ def deanonymize_tool_args(self, args: dict[str, Any]) -> dict[str, Any]:
73
+ """Replace PII tags in tool arguments with real values.
74
+
75
+ Recursively processes dictionaries, lists, and strings to replace all PII tags
76
+ (e.g., '<EMAIL_1>') with their corresponding real values from flat_pii_mapping.
77
+
78
+ Args:
79
+ args: Tool arguments that may contain PII tags
80
+
81
+ Returns:
82
+ Arguments with tags replaced by real values
83
+ """
84
+ def anonymize_tool_output(self, output: Any) -> tuple[Any, dict[str, str]]:
85
+ """Mask PII values in tool output.
86
+
87
+ Handles string and dictionary outputs. For strings, uses two-phase anonymization:
88
+ first masks known PII, then detects new PII via NER. For dictionaries, recursively
89
+ processes all string values. Returns updated mapping with any newly discovered PII.
90
+
91
+ Args:
92
+ output: Tool output that may contain PII values (string, dict, or other)
93
+
94
+ Returns:
95
+ Tuple of (anonymized_output, updated_flat_pii_mapping)
96
+ """
@@ -0,0 +1,207 @@
1
+ """PII mapping helper functions for LangGraph agent state management.
2
+
3
+ This module provides reducer functions and extraction utilities for managing
4
+ PII mappings across tool execution and multi-agent delegation scenarios.
5
+
6
+ Authors:
7
+ Fachriza Adhiatma (fachriza.d.adhiatma@gdplabs.id)
8
+ """
9
+
10
+ from typing import Any
11
+
12
+ from aip_agents.utils.logger import LoggerManager
13
+
14
+ logger = LoggerManager().get_logger(__name__)
15
+
16
+
17
+ def normalize_enable_pii(enable_pii: Any) -> bool | None:
18
+ """Normalize enable_pii value from agent configuration.
19
+
20
+ Args:
21
+ enable_pii: Raw enable_pii value from agent configuration.
22
+
23
+ Returns:
24
+ The normalized enable_pii flag when explicitly set (True/False), otherwise None.
25
+ """
26
+ if enable_pii is None:
27
+ return None
28
+
29
+ if isinstance(enable_pii, bool):
30
+ return enable_pii
31
+
32
+ logger.warning("Ignoring invalid enable_pii value from agent config: %s", enable_pii)
33
+ return None
34
+
35
+
36
+ def _get_pii_mapping_from_metadata(metadata: dict[str, Any] | None) -> dict[str, str] | None:
37
+ """Extract the pii_mapping dictionary from metadata structures.
38
+
39
+ Args:
40
+ metadata: Metadata payload that may contain a pii_mapping key directly or nested
41
+ inside another metadata dictionary.
42
+
43
+ Returns:
44
+ A dict containing tag-to-value mappings when found, otherwise None.
45
+ """
46
+ if not isinstance(metadata, dict):
47
+ return None
48
+
49
+ metadata_dict = metadata
50
+ if "pii_mapping" not in metadata_dict and isinstance(metadata.get("metadata"), dict):
51
+ metadata_dict = metadata["metadata"]
52
+
53
+ pii_mapping = metadata_dict.get("pii_mapping")
54
+ if isinstance(pii_mapping, dict) and pii_mapping:
55
+ return pii_mapping # type: ignore[return-value]
56
+ return None
57
+
58
+
59
+ def _replace_content_segments(content: str, replacements: list[tuple[str, str]]) -> str:
60
+ """Apply sequential placeholder replacements on a response string.
61
+
62
+ Args:
63
+ content: Original response content containing placeholders.
64
+ replacements: List of (placeholder, actual_value) tuples to apply in order.
65
+
66
+ Returns:
67
+ The content string with all replacements applied.
68
+ """
69
+ if not replacements:
70
+ return content
71
+
72
+ result = content
73
+ for placeholder, actual in replacements:
74
+ result = result.replace(placeholder, actual)
75
+ return result
76
+
77
+
78
+ def add_pii_mappings(
79
+ left: dict[str, str] | None,
80
+ right: dict[str, str] | None,
81
+ ) -> dict[str, str]:
82
+ """Reducer function to merge PII mappings from multiple sources.
83
+
84
+ This is a LangGraph reducer function that merges PII mappings from:
85
+ - Parent agent's initial mapping
86
+ - Tool outputs with newly discovered PII
87
+ - Subagent responses with their discovered PII
88
+
89
+ Args:
90
+ left: Existing PII mapping (or None)
91
+ right: New PII mapping to merge (or None)
92
+
93
+ Returns:
94
+ Merged PII mapping dictionary
95
+
96
+ Note:
97
+ - Right (new) mappings take precedence over left (existing)
98
+ - Handles None/non-dict cases gracefully
99
+ - Preserves all unique PII tags
100
+ - Returns empty dict if both inputs are None/empty
101
+ """
102
+ # Handle None/non-dict inputs
103
+ left_dict = left if isinstance(left, dict) else {}
104
+ right_dict = right if isinstance(right, dict) else {}
105
+
106
+ # Merge: right takes precedence
107
+ merged = {**left_dict, **right_dict}
108
+
109
+ return merged if merged else {}
110
+
111
+
112
+ def extract_pii_mapping_from_agent_response(result: Any) -> dict[str, str] | None:
113
+ """Extract PII mapping from subagent response.
114
+
115
+ Used by DelegationToolManager to propagate PII mappings from subagents
116
+ back to parent agents.
117
+
118
+ Args:
119
+ result: The result returned by the delegated agent
120
+
121
+ Returns:
122
+ PII mapping dictionary if found, None otherwise
123
+
124
+ Note:
125
+ - Checks if result is a dict
126
+ - Extracts 'full_final_state' from result
127
+ - Extracts 'pii_mapping' from full_final_state
128
+ - Validates mapping is a non-empty dict
129
+ - Returns None if any step fails
130
+ """
131
+ # Validate result is a dict
132
+ if not isinstance(result, dict):
133
+ return None
134
+
135
+ # Extract full_final_state
136
+ full_final_state = result.get("full_final_state")
137
+ if not isinstance(full_final_state, dict):
138
+ return None
139
+
140
+ # Extract pii_mapping
141
+ pii_mapping = full_final_state.get("pii_mapping")
142
+ if not isinstance(pii_mapping, dict) or not pii_mapping:
143
+ return None
144
+
145
+ logger.info(f"Extracted PII mapping with {len(pii_mapping)} entries from agent response")
146
+ return pii_mapping # type: ignore
147
+
148
+
149
+ def deanonymize_final_response_content(
150
+ content: str,
151
+ is_final_response: bool,
152
+ metadata: dict[str, Any] | None,
153
+ ) -> str:
154
+ """Deanonymize final response content using PII mapping from metadata.
155
+
156
+ Args:
157
+ content: Final response content that may contain PII tags.
158
+ is_final_response: Flag indicating whether this message is a final response.
159
+ metadata: Optional metadata dict (or event payload containing ``metadata``) with
160
+ ``pii_mapping`` tag-to-value mapping.
161
+
162
+ Returns:
163
+ Content string with PII tags replaced by real values when applicable.
164
+ """
165
+ if not is_final_response:
166
+ return content
167
+
168
+ pii_mapping = _get_pii_mapping_from_metadata(metadata)
169
+ if not pii_mapping:
170
+ return content
171
+
172
+ replacements = [
173
+ (tag, value) for tag, value in pii_mapping.items() if isinstance(tag, str) and isinstance(value, str) and tag
174
+ ]
175
+
176
+ return _replace_content_segments(content, replacements)
177
+
178
+
179
+ def anonymize_final_response_content(content: str, metadata: dict[str, Any] | None) -> str:
180
+ """Anonymize final response content using PII mapping from metadata.
181
+
182
+ Args:
183
+ content: Final response content that may contain real PII values.
184
+ metadata: Metadata dict (or event payload containing ``metadata``) with
185
+ ``pii_mapping`` tag-to-value mapping.
186
+
187
+ Returns:
188
+ Content string with real PII values replaced by their PII tags when mapping is present.
189
+ """
190
+ if not isinstance(content, str) or not content:
191
+ return content
192
+
193
+ pii_mapping = _get_pii_mapping_from_metadata(metadata)
194
+ if not pii_mapping:
195
+ return content
196
+
197
+ replacements = [
198
+ (tag, value) for tag, value in pii_mapping.items() if isinstance(tag, str) and isinstance(value, str) and value
199
+ ]
200
+ if not replacements:
201
+ return content
202
+
203
+ # Replace longer values first to avoid partial replacements (e.g., "John" before "John Smith").
204
+ replacements.sort(key=lambda item: len(item[1]), reverse=True)
205
+ normalized_pairs = [(value, tag) for tag, value in replacements]
206
+
207
+ return _replace_content_segments(content, normalized_pairs)