google-adk 0.0.1__py3-none-any.whl → 0.0.3__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 (345) hide show
  1. google/adk/._version.py +0 -0
  2. google/adk/__init__.py +20 -0
  3. google/adk/agents/__init__.py +32 -0
  4. google/adk/agents/active_streaming_tool.py +38 -0
  5. google/adk/agents/base_agent.py +345 -0
  6. google/adk/agents/callback_context.py +112 -0
  7. google/adk/agents/invocation_context.py +181 -0
  8. google/adk/agents/langgraph_agent.py +140 -0
  9. google/adk/agents/live_request_queue.py +64 -0
  10. google/adk/agents/llm_agent.py +376 -0
  11. google/adk/agents/loop_agent.py +62 -0
  12. google/adk/agents/parallel_agent.py +96 -0
  13. google/adk/agents/readonly_context.py +46 -0
  14. google/adk/agents/remote_agent.py +50 -0
  15. google/adk/agents/run_config.py +87 -0
  16. google/adk/agents/sequential_agent.py +45 -0
  17. google/adk/agents/transcription_entry.py +34 -0
  18. google/adk/artifacts/__init__.py +23 -0
  19. google/adk/artifacts/base_artifact_service.py +128 -0
  20. google/adk/artifacts/gcs_artifact_service.py +195 -0
  21. google/adk/artifacts/in_memory_artifact_service.py +133 -0
  22. google/adk/auth/__init__.py +22 -0
  23. google/adk/auth/auth_credential.py +220 -0
  24. google/adk/auth/auth_handler.py +268 -0
  25. google/adk/auth/auth_preprocessor.py +116 -0
  26. google/adk/auth/auth_schemes.py +67 -0
  27. google/adk/auth/auth_tool.py +55 -0
  28. google/adk/cli/__init__.py +15 -0
  29. google/adk/cli/__main__.py +18 -0
  30. google/adk/cli/agent_graph.py +148 -0
  31. google/adk/cli/browser/adk_favicon.svg +17 -0
  32. google/adk/cli/browser/assets/audio-processor.js +51 -0
  33. google/adk/cli/browser/assets/config/runtime-config.json +3 -0
  34. google/adk/cli/browser/index.html +33 -0
  35. google/adk/cli/browser/main-SY2WYYGV.js +75 -0
  36. google/adk/cli/browser/polyfills-FFHMD2TL.js +18 -0
  37. google/adk/cli/browser/styles-4VDSPQ37.css +17 -0
  38. google/adk/cli/cli.py +181 -0
  39. google/adk/cli/cli_deploy.py +181 -0
  40. google/adk/cli/cli_eval.py +282 -0
  41. google/adk/cli/cli_tools_click.py +524 -0
  42. google/adk/cli/fast_api.py +784 -0
  43. google/adk/cli/utils/__init__.py +49 -0
  44. google/adk/cli/utils/envs.py +57 -0
  45. google/adk/cli/utils/evals.py +93 -0
  46. google/adk/cli/utils/logs.py +72 -0
  47. google/adk/code_executors/__init__.py +49 -0
  48. google/adk/code_executors/base_code_executor.py +97 -0
  49. google/adk/code_executors/code_execution_utils.py +256 -0
  50. google/adk/code_executors/code_executor_context.py +202 -0
  51. google/adk/code_executors/container_code_executor.py +196 -0
  52. google/adk/code_executors/unsafe_local_code_executor.py +71 -0
  53. google/adk/code_executors/vertex_ai_code_executor.py +234 -0
  54. google/adk/docs/Makefile +20 -0
  55. google/adk/docs/build/doctrees/google-adk.doctree +0 -0
  56. google/adk/docs/build/html/_sources/google-adk.rst.txt +98 -0
  57. google/adk/docs/build/html/_sources/index.rst.txt +7 -0
  58. google/adk/docs/build/html/_static/autodoc_pydantic.css +27 -0
  59. google/adk/docs/build/html/_static/basic.css +925 -0
  60. google/adk/docs/build/html/_static/debug.css +85 -0
  61. google/adk/docs/build/html/_static/doctools.js +156 -0
  62. google/adk/docs/build/html/_static/documentation_options.js +29 -0
  63. google/adk/docs/build/html/_static/file.png +0 -0
  64. google/adk/docs/build/html/_static/language_data.js +199 -0
  65. google/adk/docs/build/html/_static/minus.png +0 -0
  66. google/adk/docs/build/html/_static/plus.png +0 -0
  67. google/adk/docs/build/html/_static/pygments.css +274 -0
  68. google/adk/docs/build/html/_static/scripts/furo-extensions.js +16 -0
  69. google/adk/docs/build/html/_static/scripts/furo.js +19 -0
  70. google/adk/docs/build/html/_static/scripts/furo.js.LICENSE.txt +7 -0
  71. google/adk/docs/build/html/_static/scripts/furo.js.map +1 -0
  72. google/adk/docs/build/html/_static/searchtools.js +620 -0
  73. google/adk/docs/build/html/_static/skeleton.css +312 -0
  74. google/adk/docs/build/html/_static/sphinx_highlight.js +170 -0
  75. google/adk/docs/build/html/_static/styles/furo-extensions.css +18 -0
  76. google/adk/docs/build/html/_static/styles/furo-extensions.css.map +1 -0
  77. google/adk/docs/build/html/_static/styles/furo.css +18 -0
  78. google/adk/docs/build/html/_static/styles/furo.css.map +1 -0
  79. google/adk/docs/build/html/genindex.html +861 -0
  80. google/adk/docs/build/html/google-adk.html +5461 -0
  81. google/adk/docs/build/html/index.html +567 -0
  82. google/adk/docs/build/html/objects.inv +0 -0
  83. google/adk/docs/build/html/py-modindex.html +373 -0
  84. google/adk/docs/build/html/search.html +333 -0
  85. google/adk/docs/build/html/searchindex.js +17 -0
  86. google/adk/docs/source/conf.py +133 -0
  87. google/adk/docs/source/google-adk.rst +98 -0
  88. google/adk/docs/source/index.rst +7 -0
  89. google/adk/evaluation/__init__.py +31 -0
  90. google/adk/evaluation/agent_evaluator.py +329 -0
  91. google/adk/evaluation/evaluation_constants.py +24 -0
  92. google/adk/evaluation/evaluation_generator.py +270 -0
  93. google/adk/evaluation/response_evaluator.py +135 -0
  94. google/adk/evaluation/trajectory_evaluator.py +184 -0
  95. google/adk/events/__init__.py +21 -0
  96. google/adk/events/event.py +130 -0
  97. google/adk/events/event_actions.py +55 -0
  98. google/adk/examples/__init__.py +28 -0
  99. google/adk/examples/base_example_provider.py +35 -0
  100. google/adk/examples/example.py +27 -0
  101. google/adk/examples/example_util.py +123 -0
  102. google/adk/examples/vertex_ai_example_store.py +104 -0
  103. google/adk/flows/__init__.py +14 -0
  104. google/adk/flows/llm_flows/__init__.py +20 -0
  105. google/adk/flows/llm_flows/_base_llm_processor.py +52 -0
  106. google/adk/flows/llm_flows/_code_execution.py +458 -0
  107. google/adk/flows/llm_flows/_nl_planning.py +129 -0
  108. google/adk/flows/llm_flows/agent_transfer.py +132 -0
  109. google/adk/flows/llm_flows/audio_transcriber.py +109 -0
  110. google/adk/flows/llm_flows/auto_flow.py +49 -0
  111. google/adk/flows/llm_flows/base_llm_flow.py +559 -0
  112. google/adk/flows/llm_flows/basic.py +72 -0
  113. google/adk/flows/llm_flows/contents.py +370 -0
  114. google/adk/flows/llm_flows/functions.py +486 -0
  115. google/adk/flows/llm_flows/identity.py +47 -0
  116. google/adk/flows/llm_flows/instructions.py +137 -0
  117. google/adk/flows/llm_flows/single_flow.py +57 -0
  118. google/adk/memory/__init__.py +35 -0
  119. google/adk/memory/base_memory_service.py +74 -0
  120. google/adk/memory/in_memory_memory_service.py +62 -0
  121. google/adk/memory/vertex_ai_rag_memory_service.py +177 -0
  122. google/adk/models/__init__.py +31 -0
  123. google/adk/models/anthropic_llm.py +243 -0
  124. google/adk/models/base_llm.py +87 -0
  125. google/adk/models/base_llm_connection.py +76 -0
  126. google/adk/models/gemini_llm_connection.py +200 -0
  127. google/adk/models/google_llm.py +331 -0
  128. google/adk/models/lite_llm.py +673 -0
  129. google/adk/models/llm_request.py +98 -0
  130. google/adk/models/llm_response.py +111 -0
  131. google/adk/models/registry.py +102 -0
  132. google/adk/planners/__init__.py +23 -0
  133. google/adk/planners/base_planner.py +66 -0
  134. google/adk/planners/built_in_planner.py +75 -0
  135. google/adk/planners/plan_re_act_planner.py +208 -0
  136. google/adk/runners.py +456 -0
  137. google/adk/sessions/__init__.py +41 -0
  138. google/adk/sessions/base_session_service.py +133 -0
  139. google/adk/sessions/database_session_service.py +522 -0
  140. google/adk/sessions/in_memory_session_service.py +206 -0
  141. google/adk/sessions/session.py +54 -0
  142. google/adk/sessions/state.py +71 -0
  143. google/adk/sessions/vertex_ai_session_service.py +356 -0
  144. google/adk/telemetry.py +189 -0
  145. google/adk/tests/__init__.py +14 -0
  146. google/adk/tests/integration/.env.example +10 -0
  147. google/adk/tests/integration/__init__.py +18 -0
  148. google/adk/tests/integration/conftest.py +119 -0
  149. google/adk/tests/integration/fixture/__init__.py +14 -0
  150. google/adk/tests/integration/fixture/agent_with_config/__init__.py +15 -0
  151. google/adk/tests/integration/fixture/agent_with_config/agent.py +88 -0
  152. google/adk/tests/integration/fixture/callback_agent/__init__.py +15 -0
  153. google/adk/tests/integration/fixture/callback_agent/agent.py +105 -0
  154. google/adk/tests/integration/fixture/context_update_test/OWNERS +1 -0
  155. google/adk/tests/integration/fixture/context_update_test/__init__.py +15 -0
  156. google/adk/tests/integration/fixture/context_update_test/agent.py +43 -0
  157. google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +582 -0
  158. google/adk/tests/integration/fixture/context_variable_agent/__init__.py +15 -0
  159. google/adk/tests/integration/fixture/context_variable_agent/agent.py +115 -0
  160. google/adk/tests/integration/fixture/customer_support_ma/__init__.py +15 -0
  161. google/adk/tests/integration/fixture/customer_support_ma/agent.py +172 -0
  162. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +15 -0
  163. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +338 -0
  164. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +69 -0
  165. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +6 -0
  166. google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +15 -0
  167. google/adk/tests/integration/fixture/flow_complex_spark/agent.py +182 -0
  168. google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +190 -0
  169. google/adk/tests/integration/fixture/hello_world_agent/__init__.py +15 -0
  170. google/adk/tests/integration/fixture/hello_world_agent/agent.py +95 -0
  171. google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +24 -0
  172. google/adk/tests/integration/fixture/hello_world_agent/test_config.json +6 -0
  173. google/adk/tests/integration/fixture/home_automation_agent/__init__.py +15 -0
  174. google/adk/tests/integration/fixture/home_automation_agent/agent.py +304 -0
  175. google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +5 -0
  176. google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +5 -0
  177. google/adk/tests/integration/fixture/home_automation_agent/test_config.json +5 -0
  178. google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +18 -0
  179. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +17 -0
  180. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +6 -0
  181. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +18 -0
  182. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +17 -0
  183. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +5 -0
  184. google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +5 -0
  185. google/adk/tests/integration/fixture/tool_agent/__init__.py +15 -0
  186. google/adk/tests/integration/fixture/tool_agent/agent.py +218 -0
  187. google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
  188. google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +15 -0
  189. google/adk/tests/integration/fixture/trip_planner_agent/agent.py +110 -0
  190. google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +13 -0
  191. google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +5 -0
  192. google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +13 -0
  193. google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +5 -0
  194. google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +7 -0
  195. google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +19 -0
  196. google/adk/tests/integration/models/__init__.py +14 -0
  197. google/adk/tests/integration/models/test_google_llm.py +65 -0
  198. google/adk/tests/integration/test_callback.py +70 -0
  199. google/adk/tests/integration/test_context_variable.py +67 -0
  200. google/adk/tests/integration/test_evalute_agent_in_fixture.py +76 -0
  201. google/adk/tests/integration/test_multi_agent.py +28 -0
  202. google/adk/tests/integration/test_multi_turn.py +42 -0
  203. google/adk/tests/integration/test_single_agent.py +23 -0
  204. google/adk/tests/integration/test_sub_agent.py +26 -0
  205. google/adk/tests/integration/test_system_instruction.py +177 -0
  206. google/adk/tests/integration/test_tools.py +287 -0
  207. google/adk/tests/integration/test_with_test_file.py +34 -0
  208. google/adk/tests/integration/tools/__init__.py +14 -0
  209. google/adk/tests/integration/utils/__init__.py +16 -0
  210. google/adk/tests/integration/utils/asserts.py +75 -0
  211. google/adk/tests/integration/utils/test_runner.py +97 -0
  212. google/adk/tests/unittests/__init__.py +14 -0
  213. google/adk/tests/unittests/agents/__init__.py +14 -0
  214. google/adk/tests/unittests/agents/test_base_agent.py +407 -0
  215. google/adk/tests/unittests/agents/test_langgraph_agent.py +191 -0
  216. google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +138 -0
  217. google/adk/tests/unittests/agents/test_llm_agent_fields.py +231 -0
  218. google/adk/tests/unittests/agents/test_loop_agent.py +136 -0
  219. google/adk/tests/unittests/agents/test_parallel_agent.py +92 -0
  220. google/adk/tests/unittests/agents/test_sequential_agent.py +114 -0
  221. google/adk/tests/unittests/artifacts/__init__.py +14 -0
  222. google/adk/tests/unittests/artifacts/test_artifact_service.py +276 -0
  223. google/adk/tests/unittests/auth/test_auth_handler.py +575 -0
  224. google/adk/tests/unittests/conftest.py +73 -0
  225. google/adk/tests/unittests/fast_api/__init__.py +14 -0
  226. google/adk/tests/unittests/fast_api/test_fast_api.py +269 -0
  227. google/adk/tests/unittests/flows/__init__.py +14 -0
  228. google/adk/tests/unittests/flows/llm_flows/__init__.py +14 -0
  229. google/adk/tests/unittests/flows/llm_flows/_test_examples.py +142 -0
  230. google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +311 -0
  231. google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +244 -0
  232. google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +346 -0
  233. google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +93 -0
  234. google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +258 -0
  235. google/adk/tests/unittests/flows/llm_flows/test_identity.py +66 -0
  236. google/adk/tests/unittests/flows/llm_flows/test_instructions.py +164 -0
  237. google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +142 -0
  238. google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +46 -0
  239. google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +269 -0
  240. google/adk/tests/unittests/models/__init__.py +14 -0
  241. google/adk/tests/unittests/models/test_google_llm.py +224 -0
  242. google/adk/tests/unittests/models/test_litellm.py +804 -0
  243. google/adk/tests/unittests/models/test_models.py +60 -0
  244. google/adk/tests/unittests/sessions/__init__.py +14 -0
  245. google/adk/tests/unittests/sessions/test_session_service.py +227 -0
  246. google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +246 -0
  247. google/adk/tests/unittests/streaming/__init__.py +14 -0
  248. google/adk/tests/unittests/streaming/test_streaming.py +50 -0
  249. google/adk/tests/unittests/tools/__init__.py +14 -0
  250. google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +499 -0
  251. google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +204 -0
  252. google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +600 -0
  253. google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +630 -0
  254. google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +345 -0
  255. google/adk/tests/unittests/tools/google_api_tool/__init__.py +13 -0
  256. google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +657 -0
  257. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +145 -0
  258. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +68 -0
  259. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +153 -0
  260. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +196 -0
  261. google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +573 -0
  262. google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +436 -0
  263. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +1367 -0
  264. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +628 -0
  265. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +139 -0
  266. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +406 -0
  267. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +966 -0
  268. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +201 -0
  269. google/adk/tests/unittests/tools/retrieval/__init__.py +14 -0
  270. google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +147 -0
  271. google/adk/tests/unittests/tools/test_agent_tool.py +167 -0
  272. google/adk/tests/unittests/tools/test_base_tool.py +141 -0
  273. google/adk/tests/unittests/tools/test_build_function_declaration.py +277 -0
  274. google/adk/tests/unittests/utils.py +304 -0
  275. google/adk/tools/__init__.py +51 -0
  276. google/adk/tools/_automatic_function_calling_util.py +346 -0
  277. google/adk/tools/agent_tool.py +176 -0
  278. google/adk/tools/apihub_tool/__init__.py +19 -0
  279. google/adk/tools/apihub_tool/apihub_toolset.py +209 -0
  280. google/adk/tools/apihub_tool/clients/__init__.py +13 -0
  281. google/adk/tools/apihub_tool/clients/apihub_client.py +332 -0
  282. google/adk/tools/apihub_tool/clients/secret_client.py +115 -0
  283. google/adk/tools/application_integration_tool/__init__.py +19 -0
  284. google/adk/tools/application_integration_tool/application_integration_toolset.py +230 -0
  285. google/adk/tools/application_integration_tool/clients/connections_client.py +903 -0
  286. google/adk/tools/application_integration_tool/clients/integration_client.py +253 -0
  287. google/adk/tools/base_tool.py +144 -0
  288. google/adk/tools/built_in_code_execution_tool.py +59 -0
  289. google/adk/tools/crewai_tool.py +72 -0
  290. google/adk/tools/example_tool.py +62 -0
  291. google/adk/tools/exit_loop_tool.py +23 -0
  292. google/adk/tools/function_parameter_parse_util.py +307 -0
  293. google/adk/tools/function_tool.py +87 -0
  294. google/adk/tools/get_user_choice_tool.py +28 -0
  295. google/adk/tools/google_api_tool/__init__.py +14 -0
  296. google/adk/tools/google_api_tool/google_api_tool.py +59 -0
  297. google/adk/tools/google_api_tool/google_api_tool_set.py +107 -0
  298. google/adk/tools/google_api_tool/google_api_tool_sets.py +55 -0
  299. google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +521 -0
  300. google/adk/tools/google_search_tool.py +68 -0
  301. google/adk/tools/langchain_tool.py +86 -0
  302. google/adk/tools/load_artifacts_tool.py +113 -0
  303. google/adk/tools/load_memory_tool.py +58 -0
  304. google/adk/tools/load_web_page.py +41 -0
  305. google/adk/tools/long_running_tool.py +39 -0
  306. google/adk/tools/mcp_tool/__init__.py +42 -0
  307. google/adk/tools/mcp_tool/conversion_utils.py +161 -0
  308. google/adk/tools/mcp_tool/mcp_tool.py +113 -0
  309. google/adk/tools/mcp_tool/mcp_toolset.py +272 -0
  310. google/adk/tools/openapi_tool/__init__.py +21 -0
  311. google/adk/tools/openapi_tool/auth/__init__.py +19 -0
  312. google/adk/tools/openapi_tool/auth/auth_helpers.py +498 -0
  313. google/adk/tools/openapi_tool/auth/credential_exchangers/__init__.py +25 -0
  314. google/adk/tools/openapi_tool/auth/credential_exchangers/auto_auth_credential_exchanger.py +105 -0
  315. google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +55 -0
  316. google/adk/tools/openapi_tool/auth/credential_exchangers/oauth2_exchanger.py +117 -0
  317. google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +97 -0
  318. google/adk/tools/openapi_tool/common/__init__.py +19 -0
  319. google/adk/tools/openapi_tool/common/common.py +300 -0
  320. google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +32 -0
  321. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py +231 -0
  322. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +144 -0
  323. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +260 -0
  324. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +496 -0
  325. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +268 -0
  326. google/adk/tools/preload_memory_tool.py +72 -0
  327. google/adk/tools/retrieval/__init__.py +36 -0
  328. google/adk/tools/retrieval/base_retrieval_tool.py +37 -0
  329. google/adk/tools/retrieval/files_retrieval.py +33 -0
  330. google/adk/tools/retrieval/llama_index_retrieval.py +41 -0
  331. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +107 -0
  332. google/adk/tools/tool_context.py +90 -0
  333. google/adk/tools/toolbox_tool.py +46 -0
  334. google/adk/tools/transfer_to_agent_tool.py +21 -0
  335. google/adk/tools/vertex_ai_search_tool.py +96 -0
  336. google/adk/version.py +16 -0
  337. google_adk-0.0.3.dist-info/METADATA +73 -0
  338. google_adk-0.0.3.dist-info/RECORD +340 -0
  339. {google_adk-0.0.1.dist-info → google_adk-0.0.3.dist-info}/WHEEL +1 -2
  340. google_adk-0.0.3.dist-info/entry_points.txt +3 -0
  341. agent_kit/__init__.py +0 -0
  342. google_adk-0.0.1.dist-info/LICENSE.txt +0 -170
  343. google_adk-0.0.1.dist-info/METADATA +0 -15
  344. google_adk-0.0.1.dist-info/RECORD +0 -6
  345. google_adk-0.0.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,784 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import asyncio
16
+ import importlib
17
+ import json
18
+ import logging
19
+ import os
20
+ from pathlib import Path
21
+ import re
22
+ import sys
23
+ import traceback
24
+ import typing
25
+ from typing import Any
26
+ from typing import List
27
+ from typing import Literal
28
+ from typing import Optional
29
+
30
+ import click
31
+ from fastapi import FastAPI
32
+ from fastapi import HTTPException
33
+ from fastapi import Query
34
+ from fastapi.middleware.cors import CORSMiddleware
35
+ from fastapi.responses import FileResponse
36
+ from fastapi.responses import RedirectResponse
37
+ from fastapi.responses import StreamingResponse
38
+ from fastapi.staticfiles import StaticFiles
39
+ from fastapi.websockets import WebSocket
40
+ from fastapi.websockets import WebSocketDisconnect
41
+ from google.genai import types
42
+ import graphviz
43
+ from opentelemetry import trace
44
+ from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
45
+ from opentelemetry.sdk.trace import export
46
+ from opentelemetry.sdk.trace import ReadableSpan
47
+ from opentelemetry.sdk.trace import TracerProvider
48
+ from pydantic import BaseModel
49
+ from pydantic import ValidationError
50
+ from starlette.types import Lifespan
51
+
52
+ from ..agents import RunConfig
53
+ from ..agents.live_request_queue import LiveRequest
54
+ from ..agents.live_request_queue import LiveRequestQueue
55
+ from ..agents.llm_agent import Agent
56
+ from ..agents.run_config import StreamingMode
57
+ from ..artifacts import InMemoryArtifactService
58
+ from ..events.event import Event
59
+ from ..runners import Runner
60
+ from ..sessions.database_session_service import DatabaseSessionService
61
+ from ..sessions.in_memory_session_service import InMemorySessionService
62
+ from ..sessions.session import Session
63
+ from ..sessions.vertex_ai_session_service import VertexAiSessionService
64
+ from .cli_eval import EVAL_SESSION_ID_PREFIX
65
+ from .cli_eval import EvalMetric
66
+ from .cli_eval import EvalMetricResult
67
+ from .cli_eval import EvalStatus
68
+ from .utils import create_empty_state
69
+ from .utils import envs
70
+ from .utils import evals
71
+
72
+ logger = logging.getLogger(__name__)
73
+
74
+ _EVAL_SET_FILE_EXTENSION = ".evalset.json"
75
+
76
+
77
+ class ApiServerSpanExporter(export.SpanExporter):
78
+
79
+ def __init__(self, trace_dict):
80
+ self.trace_dict = trace_dict
81
+
82
+ def export(
83
+ self, spans: typing.Sequence[ReadableSpan]
84
+ ) -> export.SpanExportResult:
85
+ for span in spans:
86
+ if (
87
+ span.name == "call_llm"
88
+ or span.name == "send_data"
89
+ or span.name.startswith("tool_response")
90
+ ):
91
+ attributes = dict(span.attributes)
92
+ attributes["trace_id"] = span.get_span_context().trace_id
93
+ attributes["span_id"] = span.get_span_context().span_id
94
+ if attributes.get("gcp.vertex.agent.event_id", None):
95
+ self.trace_dict[attributes["gcp.vertex.agent.event_id"]] = attributes
96
+ return export.SpanExportResult.SUCCESS
97
+
98
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
99
+ return True
100
+
101
+
102
+ class AgentRunRequest(BaseModel):
103
+ app_name: str
104
+ user_id: str
105
+ session_id: str
106
+ new_message: types.Content
107
+ streaming: bool = False
108
+
109
+
110
+ class AddSessionToEvalSetRequest(BaseModel):
111
+ eval_id: str
112
+ session_id: str
113
+ user_id: str
114
+
115
+
116
+ class RunEvalRequest(BaseModel):
117
+ eval_ids: list[str] # if empty, then all evals in the eval set are run.
118
+ eval_metrics: list[EvalMetric]
119
+
120
+
121
+ class RunEvalResult(BaseModel):
122
+ eval_set_id: str
123
+ eval_id: str
124
+ final_eval_status: EvalStatus
125
+ eval_metric_results: list[tuple[EvalMetric, EvalMetricResult]]
126
+ session_id: str
127
+
128
+
129
+ def get_fast_api_app(
130
+ *,
131
+ agent_dir: str,
132
+ session_db_url: str = "",
133
+ allow_origins: Optional[list[str]] = None,
134
+ web: bool,
135
+ trace_to_cloud: bool = False,
136
+ lifespan: Optional[Lifespan[FastAPI]] = None,
137
+ ) -> FastAPI:
138
+ # InMemory tracing dict.
139
+ trace_dict: dict[str, Any] = {}
140
+
141
+ # Set up tracing in the FastAPI server.
142
+ provider = TracerProvider()
143
+ provider.add_span_processor(
144
+ export.SimpleSpanProcessor(ApiServerSpanExporter(trace_dict))
145
+ )
146
+ envs.load_dotenv()
147
+ enable_cloud_tracing = trace_to_cloud or os.environ.get(
148
+ "ADK_TRACE_TO_CLOUD", "0"
149
+ ).lower() in ["1", "true"]
150
+ if enable_cloud_tracing:
151
+ if project_id := os.environ.get("GOOGLE_CLOUD_PROJECT", None):
152
+ processor = export.BatchSpanProcessor(
153
+ CloudTraceSpanExporter(project_id=project_id)
154
+ )
155
+ provider.add_span_processor(processor)
156
+ else:
157
+ logging.warning(
158
+ "GOOGLE_CLOUD_PROJECT environment variable is not set. Tracing will"
159
+ " not be enabled."
160
+ )
161
+
162
+ trace.set_tracer_provider(provider)
163
+
164
+ # Run the FastAPI server.
165
+ app = FastAPI(lifespan=lifespan)
166
+
167
+ if allow_origins:
168
+ app.add_middleware(
169
+ CORSMiddleware,
170
+ allow_origins=allow_origins,
171
+ allow_credentials=True,
172
+ allow_methods=["*"],
173
+ allow_headers=["*"],
174
+ )
175
+
176
+ if agent_dir not in sys.path:
177
+ sys.path.append(agent_dir)
178
+
179
+ runner_dict = {}
180
+ root_agent_dict = {}
181
+
182
+ # Build the Artifact service
183
+ artifact_service = InMemoryArtifactService()
184
+
185
+ # Build the Session service
186
+ agent_engine_id = ""
187
+ if session_db_url:
188
+ if session_db_url.startswith("agentengine://"):
189
+ # Create vertex session service
190
+ agent_engine_id = session_db_url.split("://")[1]
191
+ if not agent_engine_id:
192
+ raise click.ClickException("Agent engine id can not be empty.")
193
+ envs.load_dotenv_for_agent("", agent_dir)
194
+ session_service = VertexAiSessionService(
195
+ os.environ["GOOGLE_CLOUD_PROJECT"],
196
+ os.environ["GOOGLE_CLOUD_LOCATION"],
197
+ )
198
+ else:
199
+ session_service = DatabaseSessionService(db_url=session_db_url)
200
+ else:
201
+ session_service = InMemorySessionService()
202
+
203
+ @app.get("/list-apps")
204
+ def list_apps() -> list[str]:
205
+ base_path = Path.cwd() / agent_dir
206
+ if not base_path.exists():
207
+ raise HTTPException(status_code=404, detail="Path not found")
208
+ if not base_path.is_dir():
209
+ raise HTTPException(status_code=400, detail="Not a directory")
210
+ agent_names = [
211
+ x
212
+ for x in os.listdir(base_path)
213
+ if os.path.isdir(os.path.join(base_path, x))
214
+ and not x.startswith(".")
215
+ and x != "__pycache__"
216
+ ]
217
+ agent_names.sort()
218
+ return agent_names
219
+
220
+ @app.get("/debug/trace/{event_id}")
221
+ def get_trace_dict(event_id: str) -> Any:
222
+ event_dict = trace_dict.get(event_id, None)
223
+ if event_dict is None:
224
+ raise HTTPException(status_code=404, detail="Trace not found")
225
+ return event_dict
226
+
227
+ @app.get(
228
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}",
229
+ response_model_exclude_none=True,
230
+ )
231
+ def get_session(app_name: str, user_id: str, session_id: str) -> Session:
232
+ # Connect to managed session if agent_engine_id is set.
233
+ app_name = agent_engine_id if agent_engine_id else app_name
234
+ session = session_service.get_session(
235
+ app_name=app_name, user_id=user_id, session_id=session_id
236
+ )
237
+ if not session:
238
+ raise HTTPException(status_code=404, detail="Session not found")
239
+ return session
240
+
241
+ @app.get(
242
+ "/apps/{app_name}/users/{user_id}/sessions",
243
+ response_model_exclude_none=True,
244
+ )
245
+ def list_sessions(app_name: str, user_id: str) -> list[Session]:
246
+ # Connect to managed session if agent_engine_id is set.
247
+ app_name = agent_engine_id if agent_engine_id else app_name
248
+ return [
249
+ session
250
+ for session in session_service.list_sessions(
251
+ app_name=app_name, user_id=user_id
252
+ ).sessions
253
+ # Remove sessions that were generated as a part of Eval.
254
+ if not session.id.startswith(EVAL_SESSION_ID_PREFIX)
255
+ ]
256
+
257
+ @app.post(
258
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}",
259
+ response_model_exclude_none=True,
260
+ )
261
+ def create_session_with_id(
262
+ app_name: str,
263
+ user_id: str,
264
+ session_id: str,
265
+ state: Optional[dict[str, Any]] = None,
266
+ ) -> Session:
267
+ # Connect to managed session if agent_engine_id is set.
268
+ app_name = agent_engine_id if agent_engine_id else app_name
269
+ if (
270
+ session_service.get_session(
271
+ app_name=app_name, user_id=user_id, session_id=session_id
272
+ )
273
+ is not None
274
+ ):
275
+ logger.warning("Session already exists: %s", session_id)
276
+ raise HTTPException(
277
+ status_code=400, detail=f"Session already exists: {session_id}"
278
+ )
279
+
280
+ logger.info("New session created: %s", session_id)
281
+ return session_service.create_session(
282
+ app_name=app_name, user_id=user_id, state=state, session_id=session_id
283
+ )
284
+
285
+ @app.post(
286
+ "/apps/{app_name}/users/{user_id}/sessions",
287
+ response_model_exclude_none=True,
288
+ )
289
+ def create_session(
290
+ app_name: str,
291
+ user_id: str,
292
+ state: Optional[dict[str, Any]] = None,
293
+ ) -> Session:
294
+ # Connect to managed session if agent_engine_id is set.
295
+ app_name = agent_engine_id if agent_engine_id else app_name
296
+
297
+ logger.info("New session created")
298
+ return session_service.create_session(
299
+ app_name=app_name, user_id=user_id, state=state
300
+ )
301
+
302
+ def _get_eval_set_file_path(app_name, agent_dir, eval_set_id) -> str:
303
+ return os.path.join(
304
+ agent_dir,
305
+ app_name,
306
+ eval_set_id + _EVAL_SET_FILE_EXTENSION,
307
+ )
308
+
309
+ @app.post(
310
+ "/apps/{app_name}/eval_sets/{eval_set_id}",
311
+ response_model_exclude_none=True,
312
+ )
313
+ def create_eval_set(
314
+ app_name: str,
315
+ eval_set_id: str,
316
+ ):
317
+ """Creates an eval set, given the id."""
318
+ pattern = r"^[a-zA-Z0-9_]+$"
319
+ if not bool(re.fullmatch(pattern, eval_set_id)):
320
+ raise HTTPException(
321
+ status_code=400,
322
+ detail=(
323
+ f"Invalid eval set id. Eval set id should have the `{pattern}`"
324
+ " format"
325
+ ),
326
+ )
327
+ # Define the file path
328
+ new_eval_set_path = _get_eval_set_file_path(
329
+ app_name, agent_dir, eval_set_id
330
+ )
331
+
332
+ logger.info("Creating eval set file `%s`", new_eval_set_path)
333
+
334
+ if not os.path.exists(new_eval_set_path):
335
+ # Write the JSON string to the file
336
+ logger.info("Eval set file doesn't exist, we will create a new one.")
337
+ with open(new_eval_set_path, "w") as f:
338
+ empty_content = json.dumps([], indent=2)
339
+ f.write(empty_content)
340
+
341
+ @app.get(
342
+ "/apps/{app_name}/eval_sets",
343
+ response_model_exclude_none=True,
344
+ )
345
+ def list_eval_sets(app_name: str) -> list[str]:
346
+ """Lists all eval sets for the given app."""
347
+ eval_set_file_path = os.path.join(agent_dir, app_name)
348
+ eval_sets = []
349
+ for file in os.listdir(eval_set_file_path):
350
+ if file.endswith(_EVAL_SET_FILE_EXTENSION):
351
+ eval_sets.append(
352
+ os.path.basename(file).removesuffix(_EVAL_SET_FILE_EXTENSION)
353
+ )
354
+
355
+ return sorted(eval_sets)
356
+
357
+ @app.post(
358
+ "/apps/{app_name}/eval_sets/{eval_set_id}/add_session",
359
+ response_model_exclude_none=True,
360
+ )
361
+ def add_session_to_eval_set(
362
+ app_name: str, eval_set_id: str, req: AddSessionToEvalSetRequest
363
+ ):
364
+ pattern = r"^[a-zA-Z0-9_]+$"
365
+ if not bool(re.fullmatch(pattern, req.eval_id)):
366
+ raise HTTPException(
367
+ status_code=400,
368
+ detail=f"Invalid eval id. Eval id should have the `{pattern}` format",
369
+ )
370
+
371
+ # Get the session
372
+ session = session_service.get_session(
373
+ app_name=app_name, user_id=req.user_id, session_id=req.session_id
374
+ )
375
+ assert session, "Session not found."
376
+ # Load the eval set file data
377
+ eval_set_file_path = _get_eval_set_file_path(
378
+ app_name, agent_dir, eval_set_id
379
+ )
380
+ with open(eval_set_file_path, "r") as file:
381
+ eval_set_data = json.load(file) # Load JSON into a list
382
+
383
+ if [x for x in eval_set_data if x["name"] == req.eval_id]:
384
+ raise HTTPException(
385
+ status_code=400,
386
+ detail=(
387
+ f"Eval id `{req.eval_id}` already exists in `{eval_set_id}`"
388
+ " eval set."
389
+ ),
390
+ )
391
+
392
+ # Convert the session data to evaluation format
393
+ test_data = evals.convert_session_to_eval_format(session)
394
+
395
+ # Populate the session with initial session state.
396
+ initial_session_state = create_empty_state(_get_root_agent(app_name))
397
+
398
+ eval_set_data.append({
399
+ "name": req.eval_id,
400
+ "data": test_data,
401
+ "initial_session": {
402
+ "state": initial_session_state,
403
+ "app_name": app_name,
404
+ "user_id": req.user_id,
405
+ },
406
+ })
407
+ # Serialize the test data to JSON and write to the eval set file.
408
+ with open(eval_set_file_path, "w") as f:
409
+ f.write(json.dumps(eval_set_data, indent=2))
410
+
411
+ @app.get(
412
+ "/apps/{app_name}/eval_sets/{eval_set_id}/evals",
413
+ response_model_exclude_none=True,
414
+ )
415
+ def list_evals_in_eval_set(
416
+ app_name: str,
417
+ eval_set_id: str,
418
+ ) -> list[str]:
419
+ """Lists all evals in an eval set."""
420
+ # Load the eval set file data
421
+ eval_set_file_path = _get_eval_set_file_path(
422
+ app_name, agent_dir, eval_set_id
423
+ )
424
+ with open(eval_set_file_path, "r") as file:
425
+ eval_set_data = json.load(file) # Load JSON into a list
426
+
427
+ return sorted([x["name"] for x in eval_set_data])
428
+
429
+ @app.post(
430
+ "/apps/{app_name}/eval_sets/{eval_set_id}/run_eval",
431
+ response_model_exclude_none=True,
432
+ )
433
+ def run_eval(
434
+ app_name: str, eval_set_id: str, req: RunEvalRequest
435
+ ) -> list[RunEvalResult]:
436
+ from .cli_eval import run_evals
437
+
438
+ """Runs an eval given the details in the eval request."""
439
+ # Create a mapping from eval set file to all the evals that needed to be
440
+ # run.
441
+ eval_set_file_path = _get_eval_set_file_path(
442
+ app_name, agent_dir, eval_set_id
443
+ )
444
+ eval_set_to_evals = {eval_set_file_path: req.eval_ids}
445
+
446
+ if not req.eval_ids:
447
+ logger.info(
448
+ "Eval ids to run list is empty. We will all evals in the eval set."
449
+ )
450
+ root_agent = _get_root_agent(app_name)
451
+ eval_results = list(
452
+ run_evals(
453
+ eval_set_to_evals,
454
+ root_agent,
455
+ getattr(root_agent, "reset_data", None),
456
+ req.eval_metrics,
457
+ session_service=session_service,
458
+ artifact_service=artifact_service,
459
+ )
460
+ )
461
+
462
+ run_eval_results = []
463
+ for eval_result in eval_results:
464
+ run_eval_results.append(
465
+ RunEvalResult(
466
+ app_name=app_name,
467
+ eval_set_id=eval_set_id,
468
+ eval_id=eval_result.eval_id,
469
+ final_eval_status=eval_result.final_eval_status,
470
+ eval_metric_results=eval_result.eval_metric_results,
471
+ session_id=eval_result.session_id,
472
+ )
473
+ )
474
+ return run_eval_results
475
+
476
+ @app.delete("/apps/{app_name}/users/{user_id}/sessions/{session_id}")
477
+ def delete_session(app_name: str, user_id: str, session_id: str):
478
+ # Connect to managed session if agent_engine_id is set.
479
+ app_name = agent_engine_id if agent_engine_id else app_name
480
+ session_service.delete_session(
481
+ app_name=app_name, user_id=user_id, session_id=session_id
482
+ )
483
+
484
+ @app.get(
485
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}",
486
+ response_model_exclude_none=True,
487
+ )
488
+ def load_artifact(
489
+ app_name: str,
490
+ user_id: str,
491
+ session_id: str,
492
+ artifact_name: str,
493
+ version: Optional[int] = Query(None),
494
+ ) -> Optional[types.Part]:
495
+ app_name = agent_engine_id if agent_engine_id else app_name
496
+ artifact = artifact_service.load_artifact(
497
+ app_name=app_name,
498
+ user_id=user_id,
499
+ session_id=session_id,
500
+ filename=artifact_name,
501
+ version=version,
502
+ )
503
+ if not artifact:
504
+ raise HTTPException(status_code=404, detail="Artifact not found")
505
+ return artifact
506
+
507
+ @app.get(
508
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}/versions/{version_id}",
509
+ response_model_exclude_none=True,
510
+ )
511
+ def load_artifact_version(
512
+ app_name: str,
513
+ user_id: str,
514
+ session_id: str,
515
+ artifact_name: str,
516
+ version_id: int,
517
+ ) -> Optional[types.Part]:
518
+ app_name = agent_engine_id if agent_engine_id else app_name
519
+ artifact = artifact_service.load_artifact(
520
+ app_name=app_name,
521
+ user_id=user_id,
522
+ session_id=session_id,
523
+ filename=artifact_name,
524
+ version=version_id,
525
+ )
526
+ if not artifact:
527
+ raise HTTPException(status_code=404, detail="Artifact not found")
528
+ return artifact
529
+
530
+ @app.get(
531
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts",
532
+ response_model_exclude_none=True,
533
+ )
534
+ def list_artifact_names(
535
+ app_name: str, user_id: str, session_id: str
536
+ ) -> list[str]:
537
+ app_name = agent_engine_id if agent_engine_id else app_name
538
+ return artifact_service.list_artifact_keys(
539
+ app_name=app_name, user_id=user_id, session_id=session_id
540
+ )
541
+
542
+ @app.get(
543
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}/versions",
544
+ response_model_exclude_none=True,
545
+ )
546
+ def list_artifact_versions(
547
+ app_name: str, user_id: str, session_id: str, artifact_name: str
548
+ ) -> list[int]:
549
+ app_name = agent_engine_id if agent_engine_id else app_name
550
+ return artifact_service.list_versions(
551
+ app_name=app_name,
552
+ user_id=user_id,
553
+ session_id=session_id,
554
+ filename=artifact_name,
555
+ )
556
+
557
+ @app.delete(
558
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}/artifacts/{artifact_name}",
559
+ )
560
+ def delete_artifact(
561
+ app_name: str, user_id: str, session_id: str, artifact_name: str
562
+ ):
563
+ app_name = agent_engine_id if agent_engine_id else app_name
564
+ artifact_service.delete_artifact(
565
+ app_name=app_name,
566
+ user_id=user_id,
567
+ session_id=session_id,
568
+ filename=artifact_name,
569
+ )
570
+
571
+ @app.post("/run", response_model_exclude_none=True)
572
+ async def agent_run(req: AgentRunRequest) -> list[Event]:
573
+ # Connect to managed session if agent_engine_id is set.
574
+ app_id = agent_engine_id if agent_engine_id else req.app_name
575
+ session = session_service.get_session(
576
+ app_name=app_id, user_id=req.user_id, session_id=req.session_id
577
+ )
578
+ if not session:
579
+ raise HTTPException(status_code=404, detail="Session not found")
580
+ runner = _get_runner(req.app_name)
581
+ events = [
582
+ event
583
+ async for event in runner.run_async(
584
+ user_id=req.user_id,
585
+ session_id=req.session_id,
586
+ new_message=req.new_message,
587
+ )
588
+ ]
589
+ logger.info("Generated %s events in agent run: %s", len(events), events)
590
+ return events
591
+
592
+ @app.post("/run_sse")
593
+ async def agent_run_sse(req: AgentRunRequest) -> StreamingResponse:
594
+ # Connect to managed session if agent_engine_id is set.
595
+ app_id = agent_engine_id if agent_engine_id else req.app_name
596
+ # SSE endpoint
597
+ session = session_service.get_session(
598
+ app_name=app_id, user_id=req.user_id, session_id=req.session_id
599
+ )
600
+ if not session:
601
+ raise HTTPException(status_code=404, detail="Session not found")
602
+
603
+ # Convert the events to properly formatted SSE
604
+ async def event_generator():
605
+ try:
606
+ stream_mode = StreamingMode.SSE if req.streaming else StreamingMode.NONE
607
+ runner = _get_runner(req.app_name)
608
+ async for event in runner.run_async(
609
+ user_id=req.user_id,
610
+ session_id=req.session_id,
611
+ new_message=req.new_message,
612
+ run_config=RunConfig(streaming_mode=stream_mode),
613
+ ):
614
+ # Format as SSE data
615
+ sse_event = event.model_dump_json(exclude_none=True, by_alias=True)
616
+ logger.info("Generated event in agent run streaming: %s", sse_event)
617
+ yield f"data: {sse_event}\n\n"
618
+ except Exception as e:
619
+ logger.exception("Error in event_generator: %s", e)
620
+ # You might want to yield an error event here
621
+ yield f'data: {{"error": "{str(e)}"}}\n\n'
622
+
623
+ # Returns a streaming response with the proper media type for SSE
624
+ return StreamingResponse(
625
+ event_generator(),
626
+ media_type="text/event-stream",
627
+ )
628
+
629
+ @app.get(
630
+ "/apps/{app_name}/users/{user_id}/sessions/{session_id}/events/{event_id}/graph",
631
+ response_model_exclude_none=True,
632
+ )
633
+ def get_event_graph(
634
+ app_name: str, user_id: str, session_id: str, event_id: str
635
+ ):
636
+ # Connect to managed session if agent_engine_id is set.
637
+ app_id = agent_engine_id if agent_engine_id else app_name
638
+ session = session_service.get_session(
639
+ app_name=app_id, user_id=user_id, session_id=session_id
640
+ )
641
+ session_events = session.events if session else []
642
+ event = next((x for x in session_events if x.id == event_id), None)
643
+ if not event:
644
+ return {}
645
+
646
+ from . import agent_graph
647
+
648
+ function_calls = event.get_function_calls()
649
+ function_responses = event.get_function_responses()
650
+ root_agent = _get_root_agent(app_name)
651
+ dot_graph = None
652
+ if function_calls:
653
+ function_call_highlights = []
654
+ for function_call in function_calls:
655
+ from_name = event.author
656
+ to_name = function_call.name
657
+ function_call_highlights.append((from_name, to_name))
658
+ dot_graph = agent_graph.get_agent_graph(
659
+ root_agent, function_call_highlights
660
+ )
661
+ elif function_responses:
662
+ function_responses_highlights = []
663
+ for function_response in function_responses:
664
+ from_name = function_response.name
665
+ to_name = event.author
666
+ function_responses_highlights.append((from_name, to_name))
667
+ dot_graph = agent_graph.get_agent_graph(
668
+ root_agent, function_responses_highlights
669
+ )
670
+ else:
671
+ from_name = event.author
672
+ to_name = ""
673
+ dot_graph = agent_graph.get_agent_graph(
674
+ root_agent, [(from_name, to_name)]
675
+ )
676
+ if dot_graph and isinstance(dot_graph, graphviz.Digraph):
677
+ return {"dot_src": dot_graph.source}
678
+ else:
679
+ return {}
680
+
681
+ @app.websocket("/run_live")
682
+ async def agent_live_run(
683
+ websocket: WebSocket,
684
+ app_name: str,
685
+ user_id: str,
686
+ session_id: str,
687
+ modalities: List[Literal["TEXT", "AUDIO"]] = Query(
688
+ default=["TEXT", "AUDIO"]
689
+ ), # Only allows "TEXT" or "AUDIO"
690
+ ) -> None:
691
+ await websocket.accept()
692
+
693
+ # Connect to managed session if agent_engine_id is set.
694
+ app_id = agent_engine_id if agent_engine_id else app_name
695
+ session = session_service.get_session(
696
+ app_name=app_id, user_id=user_id, session_id=session_id
697
+ )
698
+ if not session:
699
+ # Accept first so that the client is aware of connection establishment,
700
+ # then close with a specific code.
701
+ await websocket.close(code=1002, reason="Session not found")
702
+ return
703
+
704
+ live_request_queue = LiveRequestQueue()
705
+
706
+ async def forward_events():
707
+ runner = _get_runner(app_name)
708
+ async for event in runner.run_live(
709
+ session=session, live_request_queue=live_request_queue
710
+ ):
711
+ await websocket.send_text(
712
+ event.model_dump_json(exclude_none=True, by_alias=True)
713
+ )
714
+
715
+ async def process_messages():
716
+ try:
717
+ while True:
718
+ data = await websocket.receive_text()
719
+ # Validate and send the received message to the live queue.
720
+ live_request_queue.send(LiveRequest.model_validate_json(data))
721
+ except ValidationError as ve:
722
+ logger.error("Validation error in process_messages: %s", ve)
723
+
724
+ # Run both tasks concurrently and cancel all if one fails.
725
+ tasks = [
726
+ asyncio.create_task(forward_events()),
727
+ asyncio.create_task(process_messages()),
728
+ ]
729
+ done, pending = await asyncio.wait(
730
+ tasks, return_when=asyncio.FIRST_EXCEPTION
731
+ )
732
+ try:
733
+ # This will re-raise any exception from the completed tasks.
734
+ for task in done:
735
+ task.result()
736
+ except WebSocketDisconnect:
737
+ logger.info("Client disconnected during process_messages.")
738
+ except Exception as e:
739
+ logger.exception("Error during live websocket communication: %s", e)
740
+ traceback.print_exc()
741
+ finally:
742
+ for task in pending:
743
+ task.cancel()
744
+
745
+ def _get_root_agent(app_name: str) -> Agent:
746
+ """Returns the root agent for the given app."""
747
+ if app_name in root_agent_dict:
748
+ return root_agent_dict[app_name]
749
+ envs.load_dotenv_for_agent(os.path.basename(app_name), agent_dir)
750
+ agent_module = importlib.import_module(app_name)
751
+ root_agent: Agent = agent_module.agent.root_agent
752
+ root_agent_dict[app_name] = root_agent
753
+ return root_agent
754
+
755
+ def _get_runner(app_name: str) -> Runner:
756
+ """Returns the runner for the given app."""
757
+ if app_name in runner_dict:
758
+ return runner_dict[app_name]
759
+ root_agent = _get_root_agent(app_name)
760
+ runner = Runner(
761
+ app_name=agent_engine_id if agent_engine_id else app_name,
762
+ agent=root_agent,
763
+ artifact_service=artifact_service,
764
+ session_service=session_service,
765
+ )
766
+ runner_dict[app_name] = runner
767
+ return runner
768
+
769
+ if web:
770
+ BASE_DIR = Path(__file__).parent.resolve()
771
+ ANGULAR_DIST_PATH = BASE_DIR / "browser"
772
+
773
+ @app.get("/")
774
+ async def redirect_to_dev_ui():
775
+ return RedirectResponse("/dev-ui")
776
+
777
+ @app.get("/dev-ui")
778
+ async def dev_ui():
779
+ return FileResponse(BASE_DIR / "browser/index.html")
780
+
781
+ app.mount(
782
+ "/", StaticFiles(directory=ANGULAR_DIST_PATH, html=True), name="static"
783
+ )
784
+ return app