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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. aip_agents/__init__.py +66 -0
  2. aip_agents/a2a/__init__.py +19 -0
  3. aip_agents/a2a/server/__init__.py +10 -0
  4. aip_agents/a2a/server/base_executor.py +1125 -0
  5. aip_agents/a2a/server/google_adk_executor.py +198 -0
  6. aip_agents/a2a/server/langflow_executor.py +180 -0
  7. aip_agents/a2a/server/langgraph_executor.py +270 -0
  8. aip_agents/a2a/types.py +232 -0
  9. aip_agents/a2ui/__init__.py +26 -0
  10. aip_agents/a2ui/catalog_provider.py +109 -0
  11. aip_agents/a2ui/examples/0.8/booking_form.json +1 -0
  12. aip_agents/a2ui/examples/0.8/confirmation.json +1 -0
  13. aip_agents/a2ui/examples/0.8/single_column_list.json +1 -0
  14. aip_agents/a2ui/examples/0.9/booking_form.json +1 -0
  15. aip_agents/a2ui/examples/0.9/confirmation.json +1 -0
  16. aip_agents/a2ui/examples/0.9/single_column_list.json +1 -0
  17. aip_agents/a2ui/middleware.py +754 -0
  18. aip_agents/a2ui/models.py +153 -0
  19. aip_agents/a2ui/public_url.py +138 -0
  20. aip_agents/a2ui/stream_filter.py +67 -0
  21. aip_agents/agent/__init__.py +72 -0
  22. aip_agents/agent/base_agent.py +1437 -0
  23. aip_agents/agent/base_langgraph_agent.py +4048 -0
  24. aip_agents/agent/google_adk_agent.py +932 -0
  25. aip_agents/agent/google_adk_constants.py +11 -0
  26. aip_agents/agent/graph_runtime.py +185 -0
  27. aip_agents/agent/hitl/__init__.py +24 -0
  28. aip_agents/agent/hitl/config.py +28 -0
  29. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  30. aip_agents/agent/hitl/manager.py +532 -0
  31. aip_agents/agent/hitl/models.py +18 -0
  32. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  33. aip_agents/agent/hitl/prompt/base.py +42 -0
  34. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  35. aip_agents/agent/hitl/registry.py +149 -0
  36. aip_agents/agent/interface.py +138 -0
  37. aip_agents/agent/interfaces.py +65 -0
  38. aip_agents/agent/langflow_agent.py +464 -0
  39. aip_agents/agent/langgraph_react_agent.py +3741 -0
  40. aip_agents/agent/model_invocation_adapter.py +346 -0
  41. aip_agents/agent/system_instruction_context.py +34 -0
  42. aip_agents/agent/tool_execution_engine.py +587 -0
  43. aip_agents/audio_interface/__init__.py +30 -0
  44. aip_agents/audio_interface/audio_agent_adapter.py +254 -0
  45. aip_agents/audio_interface/config.py +146 -0
  46. aip_agents/audio_interface/errors.py +15 -0
  47. aip_agents/audio_interface/livekit_audio_session.py +504 -0
  48. aip_agents/audio_interface/livekit_realtime_audio_session.py +142 -0
  49. aip_agents/audio_interface/session_factory.py +40 -0
  50. aip_agents/clients/__init__.py +10 -0
  51. aip_agents/clients/langflow/__init__.py +10 -0
  52. aip_agents/clients/langflow/client.py +477 -0
  53. aip_agents/clients/langflow/types.py +18 -0
  54. aip_agents/common/__init__.py +5 -0
  55. aip_agents/common/callable_introspection.py +17 -0
  56. aip_agents/constants.py +23 -0
  57. aip_agents/context/__init__.py +5 -0
  58. aip_agents/context/budget.py +237 -0
  59. aip_agents/context/compaction.py +339 -0
  60. aip_agents/context/metadata.py +169 -0
  61. aip_agents/context/offload.py +449 -0
  62. aip_agents/context/preparation.py +196 -0
  63. aip_agents/context/prompt.py +61 -0
  64. aip_agents/context/runtime.py +149 -0
  65. aip_agents/context/session_recall.py +69 -0
  66. aip_agents/context/state.py +48 -0
  67. aip_agents/context/utils.py +44 -0
  68. aip_agents/credentials/manager.py +132 -0
  69. aip_agents/examples/__init__.py +5 -0
  70. aip_agents/examples/audio_livekit_session.py +116 -0
  71. aip_agents/examples/compare_streaming_client.py +783 -0
  72. aip_agents/examples/compare_streaming_server.py +141 -0
  73. aip_agents/examples/gl_connectors_smoke.py +148 -0
  74. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  75. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  76. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  77. aip_agents/examples/hello_world_a2a_google_adk_server.py +78 -0
  78. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  79. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  80. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  81. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  82. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  83. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +104 -0
  84. aip_agents/examples/hello_world_a2a_langchain_server.py +78 -0
  85. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +77 -0
  86. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  87. aip_agents/examples/hello_world_a2a_langflow_server.py +80 -0
  88. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  89. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  90. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +91 -0
  91. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  92. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  93. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  94. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  95. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  96. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  97. aip_agents/examples/hello_world_a2a_langgraph_server.py +83 -0
  98. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +78 -0
  99. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +131 -0
  100. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  101. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  102. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +250 -0
  103. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  104. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +79 -0
  105. aip_agents/examples/hello_world_filesystem.py +162 -0
  106. aip_agents/examples/hello_world_google_adk.py +41 -0
  107. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  108. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  109. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  110. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  111. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  112. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  113. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  114. aip_agents/examples/hello_world_langchain.py +28 -0
  115. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  116. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  117. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  118. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  119. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  120. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  121. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  122. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  123. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  124. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  125. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  126. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  127. aip_agents/examples/hello_world_langgraph.py +39 -0
  128. aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
  129. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  130. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  131. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  132. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  133. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  134. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  135. aip_agents/examples/hello_world_langgraph_skills.py +62 -0
  136. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  137. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  138. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  139. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  140. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  141. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  142. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  143. aip_agents/examples/hello_world_multi_agent_tool_output_client.py +57 -0
  144. aip_agents/examples/hello_world_multi_agent_tool_output_server.py +131 -0
  145. aip_agents/examples/hello_world_pii_logger.py +21 -0
  146. aip_agents/examples/hello_world_ptc.py +49 -0
  147. aip_agents/examples/hello_world_ptc_custom_tools.py +83 -0
  148. aip_agents/examples/hello_world_sentry.py +133 -0
  149. aip_agents/examples/hello_world_step_limits.py +273 -0
  150. aip_agents/examples/hello_world_stock_a2a_server.py +100 -0
  151. aip_agents/examples/hello_world_tool_output_client.py +55 -0
  152. aip_agents/examples/hello_world_tool_output_server.py +113 -0
  153. aip_agents/examples/hitl_demo.py +724 -0
  154. aip_agents/examples/mcp_configs/configs.py +63 -0
  155. aip_agents/examples/mcp_servers/common.py +76 -0
  156. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  157. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  158. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  159. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  160. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  161. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  162. aip_agents/examples/pii_demo_langgraph_server.py +125 -0
  163. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  164. aip_agents/examples/pii_demo_multi_agent_server.py +246 -0
  165. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  166. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +87 -0
  167. aip_agents/examples/tools/__init__.py +27 -0
  168. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  169. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  170. aip_agents/examples/tools/data_generator_tool.py +103 -0
  171. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  172. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  173. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  174. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  175. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  176. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  177. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  178. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  179. aip_agents/examples/tools/multiply_tool.py +43 -0
  180. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  181. aip_agents/examples/tools/random_chart_tool.py +142 -0
  182. aip_agents/examples/tools/serper_tool.py +202 -0
  183. aip_agents/examples/tools/stock_tools.py +82 -0
  184. aip_agents/examples/tools/table_generator_tool.py +167 -0
  185. aip_agents/examples/tools/time_tool.py +82 -0
  186. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  187. aip_agents/guardrails/__init__.py +83 -0
  188. aip_agents/guardrails/engines/__init__.py +69 -0
  189. aip_agents/guardrails/engines/base.py +90 -0
  190. aip_agents/guardrails/engines/nemo.py +101 -0
  191. aip_agents/guardrails/engines/phrase_matcher.py +113 -0
  192. aip_agents/guardrails/exceptions.py +39 -0
  193. aip_agents/guardrails/manager.py +163 -0
  194. aip_agents/guardrails/middleware.py +199 -0
  195. aip_agents/guardrails/schemas.py +63 -0
  196. aip_agents/guardrails/utils.py +45 -0
  197. aip_agents/integration/__init__.py +43 -0
  198. aip_agents/integration/agent.py +58 -0
  199. aip_agents/integration/guardrails.py +52 -0
  200. aip_agents/integration/hitl.py +71 -0
  201. aip_agents/integration/ptc.py +95 -0
  202. aip_agents/integration/skills.py +37 -0
  203. aip_agents/integration/storage.py +81 -0
  204. aip_agents/integration/version.py +103 -0
  205. aip_agents/mcp/__init__.py +1 -0
  206. aip_agents/mcp/client/__init__.py +60 -0
  207. aip_agents/mcp/client/base_mcp_client.py +377 -0
  208. aip_agents/mcp/client/connection_manager.py +231 -0
  209. aip_agents/mcp/client/errors.py +119 -0
  210. aip_agents/mcp/client/google_adk/__init__.py +45 -0
  211. aip_agents/mcp/client/google_adk/client.py +383 -0
  212. aip_agents/mcp/client/langchain/__init__.py +11 -0
  213. aip_agents/mcp/client/langchain/client.py +265 -0
  214. aip_agents/mcp/client/persistent_session.py +704 -0
  215. aip_agents/mcp/client/session_pool.py +358 -0
  216. aip_agents/mcp/client/transports.py +288 -0
  217. aip_agents/mcp/utils/__init__.py +7 -0
  218. aip_agents/mcp/utils/config_validator.py +139 -0
  219. aip_agents/memory/__init__.py +14 -0
  220. aip_agents/memory/adapters/__init__.py +10 -0
  221. aip_agents/memory/adapters/base_adapter.py +815 -0
  222. aip_agents/memory/adapters/mem0.py +286 -0
  223. aip_agents/memory/base.py +84 -0
  224. aip_agents/memory/constants.py +49 -0
  225. aip_agents/memory/factory.py +86 -0
  226. aip_agents/memory/guidance.py +20 -0
  227. aip_agents/memory/simple_memory.py +47 -0
  228. aip_agents/middleware/__init__.py +65 -0
  229. aip_agents/middleware/backends/__init__.py +72 -0
  230. aip_agents/middleware/backends/in_memory.py +578 -0
  231. aip_agents/middleware/backends/local_disk.py +1203 -0
  232. aip_agents/middleware/backends/protocol.py +353 -0
  233. aip_agents/middleware/backends/sandbox.py +1431 -0
  234. aip_agents/middleware/backends/sandbox_files.py +113 -0
  235. aip_agents/middleware/backends/utils.py +293 -0
  236. aip_agents/middleware/base.py +239 -0
  237. aip_agents/middleware/filesystem.py +690 -0
  238. aip_agents/middleware/manager.py +291 -0
  239. aip_agents/middleware/memory.py +532 -0
  240. aip_agents/middleware/pii_middleware.py +192 -0
  241. aip_agents/middleware/schema.py +19 -0
  242. aip_agents/middleware/skills.py +872 -0
  243. aip_agents/middleware/task_delegation.py +1656 -0
  244. aip_agents/middleware/todolist.py +276 -0
  245. aip_agents/middleware/tools/__init__.py +23 -0
  246. aip_agents/middleware/tools/edit_file.py +70 -0
  247. aip_agents/middleware/tools/execute.py +428 -0
  248. aip_agents/middleware/tools/grep_file.py +79 -0
  249. aip_agents/middleware/tools/ls.py +71 -0
  250. aip_agents/middleware/tools/read_file.py +239 -0
  251. aip_agents/middleware/tools/skill_load.py +193 -0
  252. aip_agents/middleware/tools/skill_resource.py +338 -0
  253. aip_agents/middleware/tools/write_file.py +114 -0
  254. aip_agents/middleware/vision.py +322 -0
  255. aip_agents/ptc/__init__.py +87 -0
  256. aip_agents/ptc/custom_tools.py +511 -0
  257. aip_agents/ptc/custom_tools_payload.py +499 -0
  258. aip_agents/ptc/custom_tools_templates/__init__.py +1 -0
  259. aip_agents/ptc/custom_tools_templates/custom_build_function.py.template +24 -0
  260. aip_agents/ptc/custom_tools_templates/custom_init.py.template +26 -0
  261. aip_agents/ptc/custom_tools_templates/custom_invoke.py.template +149 -0
  262. aip_agents/ptc/custom_tools_templates/custom_registry.py.template +112 -0
  263. aip_agents/ptc/custom_tools_templates/custom_sources_init.py.template +7 -0
  264. aip_agents/ptc/custom_tools_templates/custom_wrapper.py.template +87 -0
  265. aip_agents/ptc/doc_gen.py +122 -0
  266. aip_agents/ptc/exceptions.py +57 -0
  267. aip_agents/ptc/executor.py +627 -0
  268. aip_agents/ptc/mcp/__init__.py +45 -0
  269. aip_agents/ptc/mcp/sandbox_bridge.py +679 -0
  270. aip_agents/ptc/mcp/templates/__init__.py +1 -0
  271. aip_agents/ptc/mcp/templates/mcp_client.py.template +240 -0
  272. aip_agents/ptc/naming.py +430 -0
  273. aip_agents/ptc/payload.py +26 -0
  274. aip_agents/ptc/prompt_builder.py +674 -0
  275. aip_agents/ptc/ptc_helper.py +16 -0
  276. aip_agents/ptc/sandbox_bridge.py +297 -0
  277. aip_agents/ptc/template_utils.py +33 -0
  278. aip_agents/ptc/templates/__init__.py +1 -0
  279. aip_agents/ptc/templates/ptc_helper.py.template +134 -0
  280. aip_agents/ptc/tool_def_helpers.py +101 -0
  281. aip_agents/ptc/tool_enrichment.py +163 -0
  282. aip_agents/resilience/__init__.py +10 -0
  283. aip_agents/resilience/base.py +46 -0
  284. aip_agents/resilience/tool.py +1661 -0
  285. aip_agents/sandbox/__init__.py +62 -0
  286. aip_agents/sandbox/defaults.py +220 -0
  287. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1-datasaur.Dockerfile +120 -0
  288. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-1.Dockerfile +100 -0
  289. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2-datasaur.Dockerfile +143 -0
  290. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-2.Dockerfile +119 -0
  291. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3-datasaur.Dockerfile +173 -0
  292. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-3.Dockerfile +130 -0
  293. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.Dockerfile +164 -0
  294. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.patch_js_context.py +41 -0
  295. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2-4-runner-managed.requirements.txt +29 -0
  296. aip_agents/sandbox/dockerfiles/aip-agents-sandbox-v2.Dockerfile +87 -0
  297. aip_agents/sandbox/e2b_runtime.py +27 -0
  298. aip_agents/sandbox/image_registry.py +98 -0
  299. aip_agents/sandbox/providers/__init__.py +76 -0
  300. aip_agents/sandbox/providers/base.py +392 -0
  301. aip_agents/sandbox/providers/bedrock.py +28 -0
  302. aip_agents/sandbox/providers/e2b.py +230 -0
  303. aip_agents/sandbox/providers/opensandbox.py +163 -0
  304. aip_agents/sandbox/sandbox_runtime.py +967 -0
  305. aip_agents/sandbox/template_builder.py +240 -0
  306. aip_agents/sandbox/types.py +141 -0
  307. aip_agents/sandbox/validation.py +50 -0
  308. aip_agents/schema/__init__.py +106 -0
  309. aip_agents/schema/a2a.py +58 -0
  310. aip_agents/schema/agent.py +117 -0
  311. aip_agents/schema/artifacts.py +75 -0
  312. aip_agents/schema/attachments.py +425 -0
  313. aip_agents/schema/hitl.py +157 -0
  314. aip_agents/schema/langgraph.py +47 -0
  315. aip_agents/schema/model_id.py +103 -0
  316. aip_agents/schema/model_usage.py +50 -0
  317. aip_agents/schema/runtime_metadata.py +78 -0
  318. aip_agents/schema/step_limit.py +108 -0
  319. aip_agents/schema/storage.py +40 -0
  320. aip_agents/schema/tool_payloads.py +68 -0
  321. aip_agents/sentry/__init__.py +11 -0
  322. aip_agents/sentry/sentry.py +267 -0
  323. aip_agents/skills/__init__.py +30 -0
  324. aip_agents/skills/errors.py +110 -0
  325. aip_agents/skills/installer.py +479 -0
  326. aip_agents/skills/models.py +769 -0
  327. aip_agents/skills/stager.py +224 -0
  328. aip_agents/skills/validation.py +53 -0
  329. aip_agents/storage/__init__.py +41 -0
  330. aip_agents/storage/base.py +85 -0
  331. aip_agents/storage/clients/__init__.py +12 -0
  332. aip_agents/storage/clients/minio_client.py +318 -0
  333. aip_agents/storage/config.py +62 -0
  334. aip_agents/storage/providers/__init__.py +15 -0
  335. aip_agents/storage/providers/base.py +106 -0
  336. aip_agents/storage/providers/memory.py +114 -0
  337. aip_agents/storage/providers/object_storage.py +214 -0
  338. aip_agents/tools/__init__.py +65 -0
  339. aip_agents/tools/agent_schedule_tool.py +392 -0
  340. aip_agents/tools/browser_use/__init__.py +83 -0
  341. aip_agents/tools/browser_use/action_parser.py +104 -0
  342. aip_agents/tools/browser_use/browser_use_tool.py +1118 -0
  343. aip_agents/tools/browser_use/llm_config.py +120 -0
  344. aip_agents/tools/browser_use/minio_storage.py +203 -0
  345. aip_agents/tools/browser_use/schemas.py +121 -0
  346. aip_agents/tools/browser_use/session.py +13 -0
  347. aip_agents/tools/browser_use/session_errors.py +132 -0
  348. aip_agents/tools/browser_use/steel_session_recording.py +321 -0
  349. aip_agents/tools/browser_use/streaming.py +263 -0
  350. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  351. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  352. aip_agents/tools/browser_use/types.py +37 -0
  353. aip_agents/tools/code_sandbox/__init__.py +48 -0
  354. aip_agents/tools/code_sandbox/code_sandbox_tool.py +350 -0
  355. aip_agents/tools/code_sandbox/constant.py +13 -0
  356. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +346 -0
  357. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +65 -0
  358. aip_agents/tools/computer_use/__init__.py +75 -0
  359. aip_agents/tools/computer_use/computer_use_tool.py +910 -0
  360. aip_agents/tools/computer_use/schemas.py +78 -0
  361. aip_agents/tools/computer_use/streaming.py +198 -0
  362. aip_agents/tools/computer_use/types.py +32 -0
  363. aip_agents/tools/constants.py +177 -0
  364. aip_agents/tools/date_range_tool.py +554 -0
  365. aip_agents/tools/document_loader/__init__.py +44 -0
  366. aip_agents/tools/document_loader/base_reader.py +302 -0
  367. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  368. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  369. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  370. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  371. aip_agents/tools/execute_ptc_code.py +582 -0
  372. aip_agents/tools/gl_connector/__init__.py +5 -0
  373. aip_agents/tools/gl_connector/tool.py +518 -0
  374. aip_agents/tools/gl_connector_tools.py +170 -0
  375. aip_agents/tools/memory_search/__init__.py +29 -0
  376. aip_agents/tools/memory_search/base.py +213 -0
  377. aip_agents/tools/memory_search/mem0.py +424 -0
  378. aip_agents/tools/memory_search/schema.py +81 -0
  379. aip_agents/tools/memory_search_tool.py +34 -0
  380. aip_agents/tools/streaming_common.py +577 -0
  381. aip_agents/tools/time_tool.py +117 -0
  382. aip_agents/tools/tool_config_injector.py +300 -0
  383. aip_agents/tools/web_search/__init__.py +15 -0
  384. aip_agents/tools/web_search/serper_tool.py +187 -0
  385. aip_agents/types/__init__.py +73 -0
  386. aip_agents/types/a2a_events.py +13 -0
  387. aip_agents/utils/__init__.py +91 -0
  388. aip_agents/utils/a2a_connector.py +1758 -0
  389. aip_agents/utils/artifact_helpers.py +542 -0
  390. aip_agents/utils/artifacts/__init__.py +20 -0
  391. aip_agents/utils/artifacts/tracker.py +367 -0
  392. aip_agents/utils/attachment_helpers.py +178 -0
  393. aip_agents/utils/constants.py +22 -0
  394. aip_agents/utils/datetime/__init__.py +34 -0
  395. aip_agents/utils/datetime/normalization.py +231 -0
  396. aip_agents/utils/datetime/timezone.py +206 -0
  397. aip_agents/utils/env_loader.py +27 -0
  398. aip_agents/utils/event_handler_registry.py +58 -0
  399. aip_agents/utils/file_prompt_utils.py +276 -0
  400. aip_agents/utils/file_watchers/__init__.py +33 -0
  401. aip_agents/utils/file_watchers/base.py +46 -0
  402. aip_agents/utils/file_watchers/e2b.py +128 -0
  403. aip_agents/utils/file_watchers/factory.py +39 -0
  404. aip_agents/utils/file_watchers/ls_diff.py +88 -0
  405. aip_agents/utils/file_watchers/sandbox.py +66 -0
  406. aip_agents/utils/file_watchers/watchdog.py +97 -0
  407. aip_agents/utils/final_response_builder.py +211 -0
  408. aip_agents/utils/formatter_llm_client.py +258 -0
  409. aip_agents/utils/langgraph/__init__.py +19 -0
  410. aip_agents/utils/langgraph/converter.py +399 -0
  411. aip_agents/utils/langgraph/delegation_context.py +22 -0
  412. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  413. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  414. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  415. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1329 -0
  416. aip_agents/utils/langgraph/tool_output_management.py +1047 -0
  417. aip_agents/utils/logger.py +195 -0
  418. aip_agents/utils/metadata/__init__.py +27 -0
  419. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  420. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  421. aip_agents/utils/metadata/activity_narrative/builder.py +822 -0
  422. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  423. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  424. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  425. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  426. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  427. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  428. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  429. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  430. aip_agents/utils/metadata_helper.py +363 -0
  431. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  432. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  433. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  434. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  435. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  436. aip_agents/utils/pii/__init__.py +29 -0
  437. aip_agents/utils/pii/pii_handler.py +555 -0
  438. aip_agents/utils/pii/pii_helper.py +225 -0
  439. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  440. aip_agents/utils/reference_helper.py +273 -0
  441. aip_agents/utils/sse_chunk_transformer.py +836 -0
  442. aip_agents/utils/step_limit_manager.py +265 -0
  443. aip_agents/utils/token_usage_helper.py +531 -0
  444. aip_agents_binary-0.0.0.dist-info/METADATA +825 -0
  445. aip_agents_binary-0.0.0.dist-info/RECORD +447 -0
  446. aip_agents_binary-0.0.0.dist-info/WHEEL +5 -0
  447. aip_agents_binary-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,504 @@
1
+ """LiveKit-based audio session (local-only).
2
+
3
+ This session:
4
+ - connects to a LiveKit server/room
5
+ - uses OpenAI STT/TTS via `livekit-plugins-openai` when enabled
6
+ - routes final user transcripts into an AIP agent
7
+ - speaks the agent reply back into the room
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import asyncio
13
+ import importlib
14
+ import importlib.util
15
+ import inspect
16
+ import os
17
+ import uuid
18
+ from typing import Any
19
+
20
+ from aip_agents.audio_interface.audio_agent_adapter import AIPAudioAgentAdapter
21
+ from aip_agents.audio_interface.config import AudioSessionConfig, LiveKitConfig
22
+ from aip_agents.audio_interface.errors import AudioConfigError, AudioSessionUnavailableError
23
+ from aip_agents.utils.logger import get_logger
24
+
25
+ logger = get_logger(__name__)
26
+
27
+ _INTERRUPTED_SPEECH_NOTE = (
28
+ "[aip-audio] NOTE: speech was interrupted. If you are not hearing TTS, mute your mic after speaking "
29
+ "(or reduce background noise)."
30
+ )
31
+ _INTERRUPTED_SPEECH_RETRY_DELAY_S = 0.35
32
+ _TRANSCRIPT_DEBOUNCE_S = 0.6
33
+ _TOOL_WAIT_MESSAGE_DEFAULT = "Please wait a moment while I check that."
34
+ _TOOL_WAIT_DELAY_S_DEFAULT = 0.0
35
+
36
+
37
+ def _ensure_livekit_available() -> None:
38
+ if importlib.util.find_spec("livekit") is None or importlib.util.find_spec("livekit.agents") is None:
39
+ raise AudioSessionUnavailableError(
40
+ "LiveKit audio provider is unavailable. Install with: "
41
+ "pip install 'aip-agents[audio]' (or 'poetry install -E audio' in dev)"
42
+ )
43
+
44
+
45
+ def _ensure_livekit_openai_plugin_available() -> None:
46
+ if importlib.util.find_spec("livekit.plugins.openai") is None:
47
+ raise AudioSessionUnavailableError(
48
+ "OpenAI STT/TTS requires livekit-plugins-openai. Install with: "
49
+ "pip install 'aip-agents[audio]' (or 'poetry install -E audio' in dev)"
50
+ )
51
+
52
+
53
+ class LiveKitAudioSession:
54
+ """Manage a LiveKit room connection and stream audio to/from an agent."""
55
+
56
+ def __init__(self, config: AudioSessionConfig) -> None:
57
+ """Create a session for the given `AudioSessionConfig`."""
58
+ provider = (config.provider or "livekit").strip().lower()
59
+ if provider != "livekit":
60
+ raise AudioConfigError(
61
+ f"LiveKitAudioSession requires provider='livekit' (got provider={config.provider!r})"
62
+ )
63
+ self._config = config
64
+ self._session: Any | None = None
65
+ self._close_event = asyncio.Event()
66
+ self._room: Any | None = None
67
+ self._http_context_mod: Any | None = None
68
+ self._pending_tasks: set[asyncio.Task[Any]] = set()
69
+ self._adapter: AIPAudioAgentAdapter | None = None
70
+ self._room_name: str | None = None
71
+ self._pending_transcripts: list[str] = []
72
+ self._flush_task: asyncio.Task[Any] | None = None
73
+ # Serialize agent turns. This avoids concurrent calls into the agent/checkpointer
74
+ # which can corrupt conversation state and produce "generic" responses.
75
+ self._turn_lock = asyncio.Lock()
76
+
77
+ @staticmethod
78
+ def _debug_enabled() -> bool:
79
+ return os.environ.get("AIP_AUDIO_DEBUG") == "1"
80
+
81
+ def _resolve_livekit_config(self) -> LiveKitConfig:
82
+ cfg = self._config.provider_config
83
+ if isinstance(cfg, LiveKitConfig):
84
+ return cfg
85
+ if isinstance(cfg, dict):
86
+ url = cfg.get("url")
87
+ if not url:
88
+ raise AudioConfigError("livekit.url is required")
89
+ return LiveKitConfig(
90
+ url=str(url),
91
+ api_key=cfg.get("api_key"),
92
+ api_secret=cfg.get("api_secret"),
93
+ room_name=cfg.get("room_name"),
94
+ identity=cfg.get("identity"),
95
+ openai_stt_model=cfg.get("openai_stt_model"),
96
+ openai_tts_model=cfg.get("openai_tts_model"),
97
+ openai_voice=cfg.get("openai_voice"),
98
+ openai_use_realtime_stt=bool(cfg.get("openai_use_realtime_stt", True)),
99
+ openai_api_key=cfg.get("openai_api_key"),
100
+ openai_base_url=cfg.get("openai_base_url"),
101
+ )
102
+ raise AudioConfigError("provider_config must be LiveKitConfig or dict")
103
+
104
+ def _wants_openai_stt_tts(self, lk_cfg: LiveKitConfig) -> bool:
105
+ model_cfg = self._config.model
106
+ if model_cfg is not None and (model_cfg.provider or "").lower() == "openai":
107
+ return True
108
+ return bool(
109
+ lk_cfg.openai_stt_model
110
+ or lk_cfg.openai_tts_model
111
+ or lk_cfg.openai_voice
112
+ or lk_cfg.openai_api_key
113
+ or lk_cfg.openai_base_url
114
+ )
115
+
116
+ def _build_openai_stt_tts(self, lk_cfg: LiveKitConfig) -> tuple[Any, Any]:
117
+ _ensure_livekit_openai_plugin_available()
118
+ openai_plugin = importlib.import_module("livekit.plugins.openai")
119
+
120
+ api_key = lk_cfg.resolve_openai_api_key()
121
+ base_url = lk_cfg.openai_base_url
122
+ model_cfg = self._config.model
123
+
124
+ stt_model = lk_cfg.openai_stt_model or "gpt-4o-transcribe"
125
+ tts_model = lk_cfg.openai_tts_model or (model_cfg.model if model_cfg else None) or "gpt-4o-mini-tts"
126
+ voice = lk_cfg.openai_voice or (model_cfg.voice if model_cfg else None) or "echo"
127
+
128
+ STT = getattr(openai_plugin, "STT")
129
+ TTS = getattr(openai_plugin, "TTS")
130
+
131
+ stt_obj = STT(
132
+ model=stt_model,
133
+ api_key=api_key,
134
+ base_url=base_url,
135
+ use_realtime=bool(lk_cfg.openai_use_realtime_stt),
136
+ )
137
+ tts_obj = TTS(
138
+ model=tts_model,
139
+ voice=voice,
140
+ api_key=api_key,
141
+ base_url=base_url,
142
+ )
143
+ return stt_obj, tts_obj
144
+
145
+ async def start(self, aip_agent: Any, *, instructions: str) -> None:
146
+ """Connect to LiveKit and start the voice pipeline."""
147
+ # Reset close flag in case this instance is restarted.
148
+ self._close_event.clear()
149
+ _ensure_livekit_available()
150
+
151
+ lk_cfg = self._resolve_livekit_config()
152
+ rtc, room_options_cls, agent_cls, agent_session_cls, access_token_cls, video_grants_cls = (
153
+ self._resolve_livekit_runtime()
154
+ )
155
+
156
+ room_name, token = self._build_join_token(lk_cfg, access_token_cls, video_grants_cls)
157
+
158
+ room = rtc.Room()
159
+ await room.connect(lk_cfg.url, token)
160
+ self._room = room
161
+
162
+ session_kwargs = self._build_agent_session_kwargs(lk_cfg)
163
+
164
+ session: Any = agent_session_cls(**session_kwargs)
165
+ lk_agent: Any = agent_cls(instructions=instructions, llm=None)
166
+
167
+ # Initialize our adapter/handlers before starting the LiveKit session so that
168
+ # any early text input is routed to the AIP agent (instead of LiveKit's default
169
+ # text handler, which expects an internal LLM and will throw).
170
+ self._register_session_handlers(session=session, aip_agent=aip_agent, room_name=room_name)
171
+
172
+ # Route LiveKit Meet chat input into the AIP agent (and avoid LiveKit generating its own replies).
173
+ # This keeps text messages working even though we pass `llm=None` to LiveKit.
174
+ room_io = importlib.import_module("livekit.agents.voice.room_io")
175
+ text_input_options_cls = getattr(room_io, "TextInputOptions", None)
176
+
177
+ async def _text_input_cb(_: Any, ev: Any) -> None:
178
+ try:
179
+ session.interrupt()
180
+ except Exception:
181
+ # Best-effort; interruption is not critical.
182
+ pass
183
+
184
+ text = getattr(ev, "text", "") or ""
185
+ text = str(text).strip()
186
+ if text:
187
+ await self._handle_final_transcript(session=session, text=text)
188
+
189
+ await session.start(
190
+ lk_agent,
191
+ room=room,
192
+ room_options=room_options_cls(
193
+ text_input=(
194
+ text_input_options_cls(text_input_cb=_text_input_cb) if callable(text_input_options_cls) else False
195
+ ),
196
+ audio_input=bool(self._config.io.input_enabled),
197
+ audio_output=bool(self._config.io.output_enabled),
198
+ ),
199
+ )
200
+
201
+ self._session = session
202
+ # Handlers already registered above.
203
+
204
+ def _resolve_livekit_runtime(self) -> tuple[Any, Any, Any, Any, Any, Any]:
205
+ rtc = importlib.import_module("livekit.rtc")
206
+ agents_mod = importlib.import_module("livekit.agents")
207
+ room_io = importlib.import_module("livekit.agents.voice.room_io")
208
+ api_mod = importlib.import_module("livekit.api")
209
+
210
+ agent_cls = getattr(agents_mod, "Agent", None)
211
+ agent_session_cls = getattr(agents_mod, "AgentSession", None)
212
+ access_token_cls = getattr(api_mod, "AccessToken", None)
213
+ video_grants_cls = getattr(api_mod, "VideoGrants", None)
214
+ room_options_cls = getattr(room_io, "RoomOptions", None)
215
+
216
+ if (
217
+ agent_cls is None
218
+ or agent_session_cls is None
219
+ or access_token_cls is None
220
+ or video_grants_cls is None
221
+ or room_options_cls is None
222
+ ):
223
+ raise AudioSessionUnavailableError("LiveKit packages installed but missing expected symbols")
224
+
225
+ return rtc, room_options_cls, agent_cls, agent_session_cls, access_token_cls, video_grants_cls
226
+
227
+ def _build_join_token(self, lk_cfg: LiveKitConfig, access_token_cls: Any, video_grants_cls: Any) -> tuple[str, str]:
228
+ api_key = lk_cfg.resolve_api_key()
229
+ api_secret = lk_cfg.resolve_api_secret()
230
+
231
+ room_name = lk_cfg.room_name or f"aip-audio-{uuid.uuid4().hex[:8]}"
232
+ identity = lk_cfg.identity or "aip-agent"
233
+
234
+ token = (
235
+ access_token_cls(api_key=api_key, api_secret=api_secret)
236
+ .with_identity(identity)
237
+ .with_grants(video_grants_cls(room_join=True, room=room_name, agent=True))
238
+ .to_jwt()
239
+ )
240
+ return room_name, token
241
+
242
+ def _build_agent_session_kwargs(self, lk_cfg: LiveKitConfig) -> dict[str, Any]:
243
+ session_kwargs: dict[str, Any] = {}
244
+ if not self._wants_openai_stt_tts(lk_cfg):
245
+ return session_kwargs
246
+
247
+ self._ensure_http_context_initialized()
248
+ stt_obj, tts_obj = self._build_openai_stt_tts(lk_cfg)
249
+ session_kwargs.update({"stt": stt_obj, "tts": tts_obj, "llm": None})
250
+ return session_kwargs
251
+
252
+ def _ensure_http_context_initialized(self) -> None:
253
+ # livekit-plugins-openai (realtime STT) uses livekit.agents.utils.http_context
254
+ # which is normally initialized by the worker/job runtime.
255
+ # Since we're running standalone, initialize it here.
256
+ http_context = importlib.import_module("livekit.agents.utils.http_context")
257
+ new_ctx = getattr(http_context, "_new_session_ctx", None)
258
+ if callable(new_ctx):
259
+ new_ctx()
260
+ self._http_context_mod = http_context
261
+
262
+ def _register_session_handlers(self, *, session: Any, aip_agent: Any, room_name: str) -> None:
263
+ self._adapter = AIPAudioAgentAdapter(aip_agent)
264
+ self._room_name = room_name
265
+ self._pending_transcripts = []
266
+ self._flush_task = None
267
+
268
+ @session.on("close")
269
+ def _on_close(_: Any) -> None:
270
+ self._close_event.set()
271
+
272
+ @session.on("user_input_transcribed")
273
+ def _on_transcript(ev: Any) -> None:
274
+ self._on_user_input_transcribed(session=session, ev=ev)
275
+
276
+ def _on_user_input_transcribed(self, *, session: Any, ev: Any) -> None:
277
+ if not getattr(ev, "is_final", False):
278
+ return
279
+
280
+ text = getattr(ev, "transcript", "") or ""
281
+ if not str(text).strip():
282
+ return
283
+
284
+ self._pending_transcripts.append(str(text).strip())
285
+
286
+ if self._flush_task is not None and not self._flush_task.done():
287
+ self._flush_task.cancel()
288
+
289
+ task = asyncio.create_task(self._flush_pending_transcripts(session=session))
290
+ self._flush_task = task
291
+ self._pending_tasks.add(task)
292
+
293
+ def _done(t: asyncio.Task[Any]) -> None:
294
+ self._pending_tasks.discard(t)
295
+
296
+ task.add_done_callback(_done)
297
+
298
+ async def _flush_pending_transcripts(self, *, session: Any) -> None:
299
+ """Wait briefly for STT to finish segmenting, then send one combined turn to the agent."""
300
+ try:
301
+ await asyncio.sleep(_TRANSCRIPT_DEBOUNCE_S)
302
+ except asyncio.CancelledError:
303
+ # Cancellation is expected when multiple final transcripts arrive quickly.
304
+ # Re-raise to preserve cancellation semantics.
305
+ raise
306
+ except Exception:
307
+ logger.exception("Unexpected error in flush task")
308
+ return
309
+
310
+ combined = " ".join(self._pending_transcripts).strip()
311
+ self._pending_transcripts.clear()
312
+ if combined:
313
+ await self._handle_final_transcript(session=session, text=combined)
314
+
315
+ async def _handle_final_transcript(self, *, session: Any, text: str) -> None:
316
+ async with self._turn_lock:
317
+ room_name = self._room_name or "unknown"
318
+ debug = self._debug_enabled()
319
+
320
+ if debug:
321
+ print(f"[aip-audio] room={room_name} user: {text}")
322
+ print(f"[aip-audio] room={room_name} user_repr={text!r} len={len(text)}")
323
+
324
+ if self._adapter is None:
325
+ return
326
+
327
+ final_received = asyncio.Event()
328
+ wait_task: asyncio.Task[Any] | None = None
329
+ turn_started = False
330
+ wait_scheduled = False
331
+
332
+ async def _on_a2a_event(ev: dict[str, Any]) -> None:
333
+ # Add await to satisfy 'async' keyword usage and ensure cooperative scheduling
334
+ await asyncio.sleep(0)
335
+ nonlocal wait_task, turn_started, wait_scheduled
336
+ if self._is_status_update_event(ev) or self._is_tool_call_event(ev):
337
+ turn_started = True
338
+ if not wait_scheduled:
339
+ # Emit at most one wait message per turn.
340
+ wait_task = self._schedule_wait_message(session, final_received, debug)
341
+ wait_scheduled = wait_task is not None
342
+ elif turn_started and self._is_final_response_event(ev):
343
+ # Some streams may include historical final_response events before
344
+ # events belonging to the current turn. Ignore those stale finals.
345
+ final_received.set()
346
+
347
+ reply = await self._adapter.generate_reply(text, prefer_a2a_stream=True, on_a2a_event=_on_a2a_event)
348
+
349
+ final_received.set()
350
+ # Avoid an "identity check" (`is not None`) here; Sonar can incorrectly
351
+ # infer `wait_task` is always None due to the nonlocal assignment.
352
+ if wait_task and not wait_task.done():
353
+ wait_task.cancel()
354
+
355
+ if debug:
356
+ print(f"[aip-audio] room={room_name} agent: {reply}")
357
+
358
+ await self._say_reply_if_enabled(session=session, reply=reply, debug=debug)
359
+
360
+ def _is_tool_call_event(self, ev: dict[str, Any]) -> bool:
361
+ raw_type = ev.get("event_type")
362
+ # Ensure raw_type is not None and has 'value' before accessing it, or use str conversion
363
+ event_type = raw_type.value if hasattr(raw_type, "value") else str(raw_type)
364
+ return event_type == "tool_call"
365
+
366
+ def _is_final_response_event(self, ev: dict[str, Any]) -> bool:
367
+ raw_type = ev.get("event_type")
368
+ event_type = raw_type.value if hasattr(raw_type, "value") else str(raw_type)
369
+ return event_type == "final_response"
370
+
371
+ def _is_status_update_event(self, ev: dict[str, Any]) -> bool:
372
+ raw_type = ev.get("event_type")
373
+ event_type = raw_type.value if hasattr(raw_type, "value") else str(raw_type)
374
+ return event_type == "status_update"
375
+
376
+ def _schedule_wait_message(
377
+ self, session: Any, final_received: asyncio.Event, debug: bool
378
+ ) -> asyncio.Task[Any] | None:
379
+ """Schedule a background task to speak the wait message if the tool takes too long."""
380
+ wait_message = os.environ.get("AIP_AUDIO_TOOL_WAIT_MESSAGE", _TOOL_WAIT_MESSAGE_DEFAULT).strip()
381
+ if not wait_message:
382
+ return None
383
+
384
+ try:
385
+ delay_s = float(os.environ.get("AIP_AUDIO_TOOL_WAIT_DELAY_S", str(_TOOL_WAIT_DELAY_S_DEFAULT)))
386
+ except Exception:
387
+ delay_s = _TOOL_WAIT_DELAY_S_DEFAULT
388
+
389
+ if debug:
390
+ print(f"[aip-audio] scheduling tool-wait message after {delay_s:.2f}s")
391
+
392
+ async def _delayed_say() -> None:
393
+ if delay_s > 0:
394
+ await asyncio.sleep(delay_s)
395
+ if final_received.is_set():
396
+ if debug:
397
+ print("[aip-audio] tool-wait message suppressed (final already received)")
398
+ return
399
+ if debug:
400
+ print("[aip-audio] speaking tool-wait message")
401
+ await self._say_reply_if_enabled(session=session, reply=wait_message, debug=debug)
402
+
403
+ task = asyncio.create_task(_delayed_say())
404
+ self._pending_tasks.add(task)
405
+ task.add_done_callback(lambda t: self._pending_tasks.discard(t))
406
+ return task
407
+
408
+ async def _say_reply_if_enabled(self, *, session: Any, reply: str, debug: bool) -> None:
409
+ if not (self._config.io.output_enabled and reply):
410
+ return
411
+
412
+ handle = session.say(reply)
413
+ if not inspect.isawaitable(handle):
414
+ return
415
+
416
+ await handle
417
+ if debug:
418
+ await self._handle_interrupted_speech(handle, session, reply)
419
+
420
+ async def _handle_interrupted_speech(self, handle: Any, session: Any, reply: str) -> None:
421
+ """Retry speaking once if interrupted immediately."""
422
+ if not bool(getattr(handle, "interrupted", False)):
423
+ return
424
+
425
+ print(_INTERRUPTED_SPEECH_NOTE)
426
+ # Best-effort retry once; VAD/echo can cause immediate interruption on first attempt.
427
+ await asyncio.sleep(_INTERRUPTED_SPEECH_RETRY_DELAY_S)
428
+ retry_handle = session.say(reply)
429
+ if inspect.isawaitable(retry_handle):
430
+ await retry_handle
431
+ if bool(getattr(retry_handle, "interrupted", False)):
432
+ print(_INTERRUPTED_SPEECH_NOTE)
433
+
434
+ async def stop(self) -> None:
435
+ """Stop the session and release LiveKit resources."""
436
+ # Cancellation will naturally propagate; this wrapper exists mainly for API symmetry.
437
+ await self._stop_impl()
438
+
439
+ async def _stop_impl(self) -> None:
440
+ """Best-effort cleanup implementation for `stop()`."""
441
+ await self._cancel_pending_tasks()
442
+ await self._close_session()
443
+ await self._disconnect_room()
444
+ await self._cleanup_http_context()
445
+
446
+ self._close_event.set()
447
+
448
+ # Clear per-session state
449
+ self._adapter = None
450
+ self._room_name = None
451
+ self._pending_transcripts.clear()
452
+ self._flush_task = None
453
+
454
+ async def _cancel_pending_tasks(self) -> None:
455
+ if not self._pending_tasks:
456
+ return
457
+ tasks = tuple(self._pending_tasks)
458
+ for task in tasks:
459
+ task.cancel()
460
+ try:
461
+ await asyncio.gather(*tasks, return_exceptions=True)
462
+ except asyncio.CancelledError:
463
+ raise
464
+ except Exception:
465
+ logger.warning("Error cancelling pending tasks", exc_info=True)
466
+ finally:
467
+ self._pending_tasks.clear()
468
+
469
+ async def _close_session(self) -> None:
470
+ if self._session is None:
471
+ return
472
+ try:
473
+ await self._session.aclose()
474
+ except asyncio.CancelledError:
475
+ raise
476
+ except Exception:
477
+ logger.warning("Error closing LiveKit session", exc_info=True)
478
+ finally:
479
+ self._session = None
480
+
481
+ async def _disconnect_room(self) -> None:
482
+ if self._room is None:
483
+ return
484
+ try:
485
+ await self._room.disconnect()
486
+ except asyncio.CancelledError:
487
+ raise
488
+ except Exception:
489
+ logger.warning("Error disconnecting LiveKit room", exc_info=True)
490
+ finally:
491
+ self._room = None
492
+
493
+ async def _cleanup_http_context(self) -> None:
494
+ try:
495
+ if self._http_context_mod is not None and hasattr(self._http_context_mod, "_close_http_ctx"):
496
+ await self._http_context_mod._close_http_ctx()
497
+ except Exception:
498
+ logger.warning("Error closing http context", exc_info=True)
499
+ finally:
500
+ self._http_context_mod = None
501
+
502
+ async def wait_closed(self) -> None:
503
+ """Wait until the LiveKit session signals close."""
504
+ await self._close_event.wait()
@@ -0,0 +1,142 @@
1
+ """Adapter over gllm_inference LiveKitRealtimeSession.
2
+
3
+ This implementation ports the gllm-inference realtime session API to the local
4
+ audio session interface used by `aip_agents.audio_interface`.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import importlib
11
+ from typing import Any
12
+
13
+ from aip_agents.audio_interface.config import AudioSessionConfig, LiveKitConfig
14
+ from aip_agents.audio_interface.errors import AudioConfigError, AudioSessionUnavailableError
15
+ from aip_agents.utils.logger import get_logger
16
+
17
+ logger = get_logger(__name__)
18
+
19
+
20
+ def _resolve_gllm_livekit_realtime_cls() -> type[Any]:
21
+ """Resolve gllm-inference LiveKitRealtimeSession and validate base interface."""
22
+ try:
23
+ realtime_mod = importlib.import_module("gllm_inference.realtime_session")
24
+ base_mod = importlib.import_module("gllm_inference.realtime_session.realtime_session")
25
+ except Exception as exc:
26
+ raise AudioSessionUnavailableError(
27
+ "gllm-inference realtime_session modules are unavailable. "
28
+ "Install/upgrade gllm-inference with livekit support."
29
+ ) from exc
30
+
31
+ livekit_cls = getattr(realtime_mod, "LiveKitRealtimeSession", None)
32
+ base_cls = getattr(base_mod, "BaseRealtimeSession", None)
33
+
34
+ if livekit_cls is None:
35
+ raise AudioSessionUnavailableError(
36
+ "Installed gllm-inference does not expose LiveKitRealtimeSession. "
37
+ "Upgrade gllm-inference to a version that includes "
38
+ "`gllm_inference.realtime_session.LiveKitRealtimeSession`."
39
+ )
40
+
41
+ if base_cls is not None and not issubclass(livekit_cls, base_cls):
42
+ raise AudioSessionUnavailableError("LiveKitRealtimeSession does not implement BaseRealtimeSession")
43
+
44
+ return livekit_cls
45
+
46
+
47
+ class LiveKitRealtimeAudioSession:
48
+ """Bridge class for provider=`livekit_realtime`."""
49
+
50
+ def __init__(self, config: AudioSessionConfig) -> None:
51
+ """Initialize the LiveKit realtime audio session wrapper.
52
+
53
+ Args:
54
+ config (AudioSessionConfig): Session configuration including provider and provider config.
55
+
56
+ Returns:
57
+ None: This initializer returns None.
58
+ """
59
+ provider = (config.provider or "").strip().lower()
60
+ if provider != "livekit_realtime":
61
+ raise AudioConfigError(
62
+ f"LiveKitRealtimeAudioSession requires provider='livekit_realtime' "
63
+ f"(got provider={config.provider!r})"
64
+ )
65
+
66
+ self._config = config
67
+ self._close_event = asyncio.Event()
68
+ self._session: Any | None = None
69
+ self._run_task: asyncio.Task[Any] | None = None
70
+
71
+ def _resolve_livekit_config(self) -> LiveKitConfig:
72
+ cfg = self._config.provider_config
73
+ if isinstance(cfg, LiveKitConfig):
74
+ return cfg
75
+ if isinstance(cfg, dict):
76
+ url = cfg.get("url")
77
+ if not url:
78
+ raise AudioConfigError("livekit_realtime.url is required")
79
+ return LiveKitConfig(
80
+ url=str(url),
81
+ api_key=cfg.get("api_key"),
82
+ api_secret=cfg.get("api_secret"),
83
+ room_name=cfg.get("room_name"),
84
+ identity=cfg.get("identity"),
85
+ )
86
+ raise AudioConfigError("provider_config must be LiveKitConfig or dict")
87
+
88
+ async def start(self, aip_agent: Any, *, instructions: str) -> None:
89
+ """Start gllm-inference LiveKitRealtimeSession in a background task.
90
+
91
+ Note:
92
+ This provider adapts the transport/session layer only. The `aip_agent`
93
+ and `instructions` parameters are currently ignored by the underlying
94
+ gllm-inference LiveKit realtime session API.
95
+ """
96
+ del aip_agent, instructions
97
+
98
+ if self._run_task is not None and not self._run_task.done():
99
+ raise AudioConfigError("LiveKitRealtimeAudioSession is already started")
100
+
101
+ lk_cfg = self._resolve_livekit_config()
102
+ session_cls = _resolve_gllm_livekit_realtime_cls()
103
+ self._close_event.clear()
104
+
105
+ self._session = session_cls(
106
+ url=lk_cfg.url,
107
+ api_key=lk_cfg.resolve_api_key(),
108
+ api_secret=lk_cfg.resolve_api_secret(),
109
+ room_name=lk_cfg.room_name,
110
+ identity=lk_cfg.identity,
111
+ )
112
+
113
+ async def _run() -> None:
114
+ try:
115
+ await self._session.start()
116
+ finally:
117
+ self._close_event.set()
118
+
119
+ self._run_task = asyncio.create_task(_run())
120
+ await asyncio.sleep(0)
121
+ if self._run_task.done():
122
+ err = self._run_task.exception()
123
+ if err is not None:
124
+ raise err
125
+
126
+ async def stop(self) -> None:
127
+ """Stop background run task."""
128
+ task = self._run_task
129
+ if task is None:
130
+ self._close_event.set()
131
+ return
132
+
133
+ if not task.done():
134
+ task.cancel()
135
+ await asyncio.gather(task, return_exceptions=True)
136
+ self._run_task = None
137
+ self._session = None
138
+ self._close_event.set()
139
+
140
+ async def wait_closed(self) -> None:
141
+ """Wait for session termination."""
142
+ await self._close_event.wait()
@@ -0,0 +1,40 @@
1
+ """Factory for transport-specific audio sessions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Awaitable
6
+ from typing import Any, Protocol
7
+
8
+ from aip_agents.audio_interface.config import AudioSessionConfig
9
+ from aip_agents.audio_interface.errors import AudioConfigError
10
+ from aip_agents.audio_interface.livekit_audio_session import LiveKitAudioSession
11
+ from aip_agents.audio_interface.livekit_realtime_audio_session import LiveKitRealtimeAudioSession
12
+
13
+
14
+ class AudioSession(Protocol):
15
+ """Minimal transport session contract for audio providers."""
16
+
17
+ def start(self, aip_agent: Any, *, instructions: str) -> Awaitable[None]:
18
+ """Start the session."""
19
+
20
+ def stop(self) -> Awaitable[None]:
21
+ """Stop the session and cleanup resources."""
22
+
23
+ def wait_closed(self) -> Awaitable[None]:
24
+ """Wait until the remote session closes."""
25
+
26
+
27
+ def create_audio_session(config: AudioSessionConfig) -> AudioSession:
28
+ """Create an audio transport session for the configured provider.
29
+
30
+ This is the single entry point for "one interface, many implementations".
31
+ """
32
+ provider = (config.provider or "livekit").strip().lower()
33
+ if provider == "livekit":
34
+ return LiveKitAudioSession(config)
35
+ if provider == "livekit_realtime":
36
+ return LiveKitRealtimeAudioSession(config)
37
+
38
+ raise AudioConfigError(
39
+ f"Unsupported audio provider '{config.provider}'. Supported providers: livekit, livekit_realtime"
40
+ )