google-adk 0.0.1__py3-none-any.whl → 0.0.2__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 (312) hide show
  1. google/adk/__init__.py +20 -0
  2. google/adk/agents/__init__.py +32 -0
  3. google/adk/agents/active_streaming_tool.py +38 -0
  4. google/adk/agents/base_agent.py +345 -0
  5. google/adk/agents/callback_context.py +112 -0
  6. google/adk/agents/invocation_context.py +181 -0
  7. google/adk/agents/langgraph_agent.py +140 -0
  8. google/adk/agents/live_request_queue.py +64 -0
  9. google/adk/agents/llm_agent.py +376 -0
  10. google/adk/agents/loop_agent.py +62 -0
  11. google/adk/agents/parallel_agent.py +96 -0
  12. google/adk/agents/readonly_context.py +46 -0
  13. google/adk/agents/remote_agent.py +50 -0
  14. google/adk/agents/run_config.py +87 -0
  15. google/adk/agents/sequential_agent.py +45 -0
  16. google/adk/agents/transcription_entry.py +34 -0
  17. google/adk/artifacts/__init__.py +23 -0
  18. google/adk/artifacts/base_artifact_service.py +128 -0
  19. google/adk/artifacts/gcs_artifact_service.py +195 -0
  20. google/adk/artifacts/in_memory_artifact_service.py +133 -0
  21. google/adk/auth/__init__.py +22 -0
  22. google/adk/auth/auth_credential.py +220 -0
  23. google/adk/auth/auth_handler.py +268 -0
  24. google/adk/auth/auth_preprocessor.py +116 -0
  25. google/adk/auth/auth_schemes.py +67 -0
  26. google/adk/auth/auth_tool.py +55 -0
  27. google/adk/cli/__init__.py +15 -0
  28. google/adk/cli/__main__.py +18 -0
  29. google/adk/cli/agent_graph.py +122 -0
  30. google/adk/cli/browser/adk_favicon.svg +17 -0
  31. google/adk/cli/browser/assets/audio-processor.js +51 -0
  32. google/adk/cli/browser/assets/config/runtime-config.json +3 -0
  33. google/adk/cli/browser/index.html +33 -0
  34. google/adk/cli/browser/main-XUU6OGCC.js +75 -0
  35. google/adk/cli/browser/polyfills-FFHMD2TL.js +18 -0
  36. google/adk/cli/browser/styles-4VDSPQ37.css +17 -0
  37. google/adk/cli/cli.py +181 -0
  38. google/adk/cli/cli_deploy.py +181 -0
  39. google/adk/cli/cli_eval.py +282 -0
  40. google/adk/cli/cli_tools_click.py +479 -0
  41. google/adk/cli/fast_api.py +774 -0
  42. google/adk/cli/media_streamer/__init__.py +19 -0
  43. google/adk/cli/media_streamer/index.html +228 -0
  44. google/adk/cli/utils/__init__.py +49 -0
  45. google/adk/cli/utils/envs.py +57 -0
  46. google/adk/cli/utils/evals.py +93 -0
  47. google/adk/cli/utils/logs.py +72 -0
  48. google/adk/code_executors/__init__.py +49 -0
  49. google/adk/code_executors/base_code_executor.py +97 -0
  50. google/adk/code_executors/code_execution_utils.py +256 -0
  51. google/adk/code_executors/code_executor_context.py +202 -0
  52. google/adk/code_executors/container_code_executor.py +196 -0
  53. google/adk/code_executors/unsafe_local_code_executor.py +71 -0
  54. google/adk/code_executors/vertex_ai_code_executor.py +234 -0
  55. google/adk/evaluation/__init__.py +31 -0
  56. google/adk/evaluation/agent_evaluator.py +329 -0
  57. google/adk/evaluation/evaluation_constants.py +24 -0
  58. google/adk/evaluation/evaluation_generator.py +270 -0
  59. google/adk/evaluation/response_evaluator.py +135 -0
  60. google/adk/evaluation/trajectory_evaluator.py +184 -0
  61. google/adk/events/__init__.py +21 -0
  62. google/adk/events/event.py +130 -0
  63. google/adk/events/event_actions.py +55 -0
  64. google/adk/examples/__init__.py +28 -0
  65. google/adk/examples/base_example_provider.py +35 -0
  66. google/adk/examples/example.py +27 -0
  67. google/adk/examples/example_util.py +123 -0
  68. google/adk/examples/vertex_ai_example_store.py +104 -0
  69. google/adk/flows/__init__.py +14 -0
  70. google/adk/flows/llm_flows/__init__.py +20 -0
  71. google/adk/flows/llm_flows/_base_llm_processor.py +52 -0
  72. google/adk/flows/llm_flows/_code_execution.py +458 -0
  73. google/adk/flows/llm_flows/_nl_planning.py +129 -0
  74. google/adk/flows/llm_flows/agent_transfer.py +132 -0
  75. google/adk/flows/llm_flows/audio_transcriber.py +109 -0
  76. google/adk/flows/llm_flows/auto_flow.py +49 -0
  77. google/adk/flows/llm_flows/base_llm_flow.py +559 -0
  78. google/adk/flows/llm_flows/basic.py +72 -0
  79. google/adk/flows/llm_flows/contents.py +370 -0
  80. google/adk/flows/llm_flows/functions.py +486 -0
  81. google/adk/flows/llm_flows/identity.py +47 -0
  82. google/adk/flows/llm_flows/instructions.py +137 -0
  83. google/adk/flows/llm_flows/single_flow.py +57 -0
  84. google/adk/memory/__init__.py +35 -0
  85. google/adk/memory/base_memory_service.py +74 -0
  86. google/adk/memory/in_memory_memory_service.py +62 -0
  87. google/adk/memory/vertex_ai_rag_memory_service.py +177 -0
  88. google/adk/models/__init__.py +31 -0
  89. google/adk/models/anthropic_llm.py +243 -0
  90. google/adk/models/base_llm.py +87 -0
  91. google/adk/models/base_llm_connection.py +76 -0
  92. google/adk/models/gemini_llm_connection.py +200 -0
  93. google/adk/models/google_llm.py +331 -0
  94. google/adk/models/lite_llm.py +673 -0
  95. google/adk/models/llm_request.py +98 -0
  96. google/adk/models/llm_response.py +111 -0
  97. google/adk/models/registry.py +102 -0
  98. google/adk/planners/__init__.py +23 -0
  99. google/adk/planners/base_planner.py +66 -0
  100. google/adk/planners/built_in_planner.py +75 -0
  101. google/adk/planners/plan_re_act_planner.py +208 -0
  102. google/adk/runners.py +456 -0
  103. google/adk/sessions/__init__.py +41 -0
  104. google/adk/sessions/base_session_service.py +133 -0
  105. google/adk/sessions/database_session_service.py +522 -0
  106. google/adk/sessions/in_memory_session_service.py +206 -0
  107. google/adk/sessions/session.py +54 -0
  108. google/adk/sessions/state.py +71 -0
  109. google/adk/sessions/vertex_ai_session_service.py +356 -0
  110. google/adk/telemetry.py +189 -0
  111. google/adk/tests/__init__.py +14 -0
  112. google/adk/tests/integration/.env.example +10 -0
  113. google/adk/tests/integration/__init__.py +18 -0
  114. google/adk/tests/integration/conftest.py +119 -0
  115. google/adk/tests/integration/fixture/__init__.py +14 -0
  116. google/adk/tests/integration/fixture/agent_with_config/__init__.py +15 -0
  117. google/adk/tests/integration/fixture/agent_with_config/agent.py +88 -0
  118. google/adk/tests/integration/fixture/callback_agent/__init__.py +15 -0
  119. google/adk/tests/integration/fixture/callback_agent/agent.py +105 -0
  120. google/adk/tests/integration/fixture/context_update_test/OWNERS +1 -0
  121. google/adk/tests/integration/fixture/context_update_test/__init__.py +15 -0
  122. google/adk/tests/integration/fixture/context_update_test/agent.py +43 -0
  123. google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +582 -0
  124. google/adk/tests/integration/fixture/context_variable_agent/__init__.py +15 -0
  125. google/adk/tests/integration/fixture/context_variable_agent/agent.py +115 -0
  126. google/adk/tests/integration/fixture/customer_support_ma/__init__.py +15 -0
  127. google/adk/tests/integration/fixture/customer_support_ma/agent.py +172 -0
  128. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +15 -0
  129. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +338 -0
  130. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +69 -0
  131. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +6 -0
  132. google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +15 -0
  133. google/adk/tests/integration/fixture/flow_complex_spark/agent.py +182 -0
  134. google/adk/tests/integration/fixture/flow_complex_spark/sample.debug.log +243 -0
  135. google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +190 -0
  136. google/adk/tests/integration/fixture/hello_world_agent/__init__.py +15 -0
  137. google/adk/tests/integration/fixture/hello_world_agent/agent.py +95 -0
  138. google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +24 -0
  139. google/adk/tests/integration/fixture/hello_world_agent/test_config.json +6 -0
  140. google/adk/tests/integration/fixture/home_automation_agent/__init__.py +15 -0
  141. google/adk/tests/integration/fixture/home_automation_agent/agent.py +304 -0
  142. google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +5 -0
  143. google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +5 -0
  144. google/adk/tests/integration/fixture/home_automation_agent/test_config.json +5 -0
  145. google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +18 -0
  146. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +17 -0
  147. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +6 -0
  148. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +18 -0
  149. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +17 -0
  150. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +5 -0
  151. google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +5 -0
  152. google/adk/tests/integration/fixture/tool_agent/__init__.py +15 -0
  153. google/adk/tests/integration/fixture/tool_agent/agent.py +218 -0
  154. google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
  155. google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +15 -0
  156. google/adk/tests/integration/fixture/trip_planner_agent/agent.py +110 -0
  157. google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +13 -0
  158. google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +5 -0
  159. google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +13 -0
  160. google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +5 -0
  161. google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +7 -0
  162. google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +19 -0
  163. google/adk/tests/integration/models/__init__.py +14 -0
  164. google/adk/tests/integration/models/test_google_llm.py +65 -0
  165. google/adk/tests/integration/test_callback.py +70 -0
  166. google/adk/tests/integration/test_context_variable.py +67 -0
  167. google/adk/tests/integration/test_evalute_agent_in_fixture.py +76 -0
  168. google/adk/tests/integration/test_multi_agent.py +28 -0
  169. google/adk/tests/integration/test_multi_turn.py +42 -0
  170. google/adk/tests/integration/test_single_agent.py +23 -0
  171. google/adk/tests/integration/test_sub_agent.py +26 -0
  172. google/adk/tests/integration/test_system_instruction.py +177 -0
  173. google/adk/tests/integration/test_tools.py +287 -0
  174. google/adk/tests/integration/test_with_test_file.py +34 -0
  175. google/adk/tests/integration/tools/__init__.py +14 -0
  176. google/adk/tests/integration/utils/__init__.py +16 -0
  177. google/adk/tests/integration/utils/asserts.py +75 -0
  178. google/adk/tests/integration/utils/test_runner.py +97 -0
  179. google/adk/tests/unittests/__init__.py +14 -0
  180. google/adk/tests/unittests/agents/__init__.py +14 -0
  181. google/adk/tests/unittests/agents/test_base_agent.py +407 -0
  182. google/adk/tests/unittests/agents/test_langgraph_agent.py +191 -0
  183. google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +138 -0
  184. google/adk/tests/unittests/agents/test_llm_agent_fields.py +231 -0
  185. google/adk/tests/unittests/agents/test_loop_agent.py +136 -0
  186. google/adk/tests/unittests/agents/test_parallel_agent.py +92 -0
  187. google/adk/tests/unittests/agents/test_sequential_agent.py +114 -0
  188. google/adk/tests/unittests/artifacts/__init__.py +14 -0
  189. google/adk/tests/unittests/artifacts/test_artifact_service.py +276 -0
  190. google/adk/tests/unittests/auth/test_auth_handler.py +575 -0
  191. google/adk/tests/unittests/conftest.py +73 -0
  192. google/adk/tests/unittests/fast_api/__init__.py +14 -0
  193. google/adk/tests/unittests/fast_api/test_fast_api.py +269 -0
  194. google/adk/tests/unittests/flows/__init__.py +14 -0
  195. google/adk/tests/unittests/flows/llm_flows/__init__.py +14 -0
  196. google/adk/tests/unittests/flows/llm_flows/_test_examples.py +142 -0
  197. google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +311 -0
  198. google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +244 -0
  199. google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +346 -0
  200. google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +93 -0
  201. google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +258 -0
  202. google/adk/tests/unittests/flows/llm_flows/test_identity.py +66 -0
  203. google/adk/tests/unittests/flows/llm_flows/test_instructions.py +164 -0
  204. google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +142 -0
  205. google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +46 -0
  206. google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +269 -0
  207. google/adk/tests/unittests/models/__init__.py +14 -0
  208. google/adk/tests/unittests/models/test_google_llm.py +224 -0
  209. google/adk/tests/unittests/models/test_litellm.py +804 -0
  210. google/adk/tests/unittests/models/test_models.py +60 -0
  211. google/adk/tests/unittests/sessions/__init__.py +14 -0
  212. google/adk/tests/unittests/sessions/test_session_service.py +227 -0
  213. google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +246 -0
  214. google/adk/tests/unittests/streaming/__init__.py +14 -0
  215. google/adk/tests/unittests/streaming/test_streaming.py +50 -0
  216. google/adk/tests/unittests/tools/__init__.py +14 -0
  217. google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +499 -0
  218. google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +204 -0
  219. google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +600 -0
  220. google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +630 -0
  221. google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +345 -0
  222. google/adk/tests/unittests/tools/google_api_tool/__init__.py +13 -0
  223. google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +657 -0
  224. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +145 -0
  225. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +68 -0
  226. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +153 -0
  227. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +196 -0
  228. google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +573 -0
  229. google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +436 -0
  230. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +1367 -0
  231. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +628 -0
  232. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +139 -0
  233. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +406 -0
  234. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +966 -0
  235. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +201 -0
  236. google/adk/tests/unittests/tools/retrieval/__init__.py +14 -0
  237. google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +147 -0
  238. google/adk/tests/unittests/tools/test_agent_tool.py +167 -0
  239. google/adk/tests/unittests/tools/test_base_tool.py +141 -0
  240. google/adk/tests/unittests/tools/test_build_function_declaration.py +277 -0
  241. google/adk/tests/unittests/utils.py +304 -0
  242. google/adk/tools/__init__.py +51 -0
  243. google/adk/tools/_automatic_function_calling_util.py +346 -0
  244. google/adk/tools/agent_tool.py +176 -0
  245. google/adk/tools/apihub_tool/__init__.py +19 -0
  246. google/adk/tools/apihub_tool/apihub_toolset.py +209 -0
  247. google/adk/tools/apihub_tool/clients/__init__.py +13 -0
  248. google/adk/tools/apihub_tool/clients/apihub_client.py +332 -0
  249. google/adk/tools/apihub_tool/clients/secret_client.py +115 -0
  250. google/adk/tools/application_integration_tool/__init__.py +19 -0
  251. google/adk/tools/application_integration_tool/application_integration_toolset.py +230 -0
  252. google/adk/tools/application_integration_tool/clients/connections_client.py +903 -0
  253. google/adk/tools/application_integration_tool/clients/integration_client.py +253 -0
  254. google/adk/tools/base_tool.py +144 -0
  255. google/adk/tools/built_in_code_execution_tool.py +59 -0
  256. google/adk/tools/crewai_tool.py +72 -0
  257. google/adk/tools/example_tool.py +62 -0
  258. google/adk/tools/exit_loop_tool.py +23 -0
  259. google/adk/tools/function_parameter_parse_util.py +307 -0
  260. google/adk/tools/function_tool.py +87 -0
  261. google/adk/tools/get_user_choice_tool.py +28 -0
  262. google/adk/tools/google_api_tool/__init__.py +14 -0
  263. google/adk/tools/google_api_tool/google_api_tool.py +59 -0
  264. google/adk/tools/google_api_tool/google_api_tool_set.py +107 -0
  265. google/adk/tools/google_api_tool/google_api_tool_sets.py +55 -0
  266. google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +521 -0
  267. google/adk/tools/google_search_tool.py +68 -0
  268. google/adk/tools/langchain_tool.py +86 -0
  269. google/adk/tools/load_artifacts_tool.py +113 -0
  270. google/adk/tools/load_memory_tool.py +58 -0
  271. google/adk/tools/load_web_page.py +41 -0
  272. google/adk/tools/long_running_tool.py +39 -0
  273. google/adk/tools/mcp_tool/__init__.py +42 -0
  274. google/adk/tools/mcp_tool/conversion_utils.py +161 -0
  275. google/adk/tools/mcp_tool/mcp_tool.py +113 -0
  276. google/adk/tools/mcp_tool/mcp_toolset.py +272 -0
  277. google/adk/tools/openapi_tool/__init__.py +21 -0
  278. google/adk/tools/openapi_tool/auth/__init__.py +19 -0
  279. google/adk/tools/openapi_tool/auth/auth_helpers.py +498 -0
  280. google/adk/tools/openapi_tool/auth/credential_exchangers/__init__.py +25 -0
  281. google/adk/tools/openapi_tool/auth/credential_exchangers/auto_auth_credential_exchanger.py +105 -0
  282. google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +55 -0
  283. google/adk/tools/openapi_tool/auth/credential_exchangers/oauth2_exchanger.py +117 -0
  284. google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +97 -0
  285. google/adk/tools/openapi_tool/common/__init__.py +19 -0
  286. google/adk/tools/openapi_tool/common/common.py +300 -0
  287. google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +32 -0
  288. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py +231 -0
  289. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +144 -0
  290. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +260 -0
  291. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +496 -0
  292. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +268 -0
  293. google/adk/tools/preload_memory_tool.py +72 -0
  294. google/adk/tools/retrieval/__init__.py +36 -0
  295. google/adk/tools/retrieval/base_retrieval_tool.py +37 -0
  296. google/adk/tools/retrieval/files_retrieval.py +33 -0
  297. google/adk/tools/retrieval/llama_index_retrieval.py +41 -0
  298. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +107 -0
  299. google/adk/tools/tool_context.py +90 -0
  300. google/adk/tools/toolbox_tool.py +46 -0
  301. google/adk/tools/transfer_to_agent_tool.py +21 -0
  302. google/adk/tools/vertex_ai_search_tool.py +96 -0
  303. google/adk/version.py +16 -0
  304. google_adk-0.0.1.dist-info/LICENSE.txt → google_adk-0.0.2.dist-info/LICENSE +32 -0
  305. google_adk-0.0.2.dist-info/METADATA +73 -0
  306. google_adk-0.0.2.dist-info/RECORD +308 -0
  307. {google_adk-0.0.1.dist-info → google_adk-0.0.2.dist-info}/WHEEL +1 -2
  308. google_adk-0.0.2.dist-info/entry_points.txt +3 -0
  309. agent_kit/__init__.py +0 -0
  310. google_adk-0.0.1.dist-info/METADATA +0 -15
  311. google_adk-0.0.1.dist-info/RECORD +0 -6
  312. google_adk-0.0.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,630 @@
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 json
16
+ from unittest import mock
17
+
18
+ from google.adk.tools.application_integration_tool.clients.connections_client import ConnectionsClient
19
+ from google.adk.tools.application_integration_tool.clients.integration_client import IntegrationClient
20
+ import google.auth
21
+ import google.auth.transport.requests
22
+ from google.auth.transport.requests import Request
23
+ from google.oauth2 import service_account
24
+ import pytest
25
+ import requests
26
+ from requests import exceptions
27
+
28
+
29
+ @pytest.fixture
30
+ def project():
31
+ return "test-project"
32
+
33
+
34
+ @pytest.fixture
35
+ def location():
36
+ return "us-central1"
37
+
38
+
39
+ @pytest.fixture
40
+ def integration_name():
41
+ return "test-integration"
42
+
43
+
44
+ @pytest.fixture
45
+ def trigger_name():
46
+ return "test-trigger"
47
+
48
+
49
+ @pytest.fixture
50
+ def connection_name():
51
+ return "test-connection"
52
+
53
+
54
+ @pytest.fixture
55
+ def mock_credentials():
56
+ creds = mock.create_autospec(google.auth.credentials.Credentials)
57
+ creds.token = "test_token"
58
+ return creds
59
+
60
+
61
+ @pytest.fixture
62
+ def mock_auth_request():
63
+ return mock.create_autospec(Request)
64
+
65
+
66
+ @pytest.fixture
67
+ def mock_connections_client():
68
+ with mock.patch(
69
+ "google.adk.tools.application_integration_tool.clients.integration_client.ConnectionsClient"
70
+ ) as mock_client:
71
+ mock_instance = mock.create_autospec(ConnectionsClient)
72
+ mock_client.return_value = mock_instance
73
+ yield mock_client
74
+
75
+
76
+ class TestIntegrationClient:
77
+
78
+ def test_initialization(
79
+ self, project, location, integration_name, trigger_name, connection_name
80
+ ):
81
+ client = IntegrationClient(
82
+ project=project,
83
+ location=location,
84
+ integration=integration_name,
85
+ trigger=trigger_name,
86
+ connection=connection_name,
87
+ entity_operations={"entity": ["LIST"]},
88
+ actions=["action1"],
89
+ service_account_json=json.dumps({"email": "test@example.com"}),
90
+ )
91
+ assert client.project == project
92
+ assert client.location == location
93
+ assert client.integration == integration_name
94
+ assert client.trigger == trigger_name
95
+ assert client.connection == connection_name
96
+ assert client.entity_operations == {"entity": ["LIST"]}
97
+ assert client.actions == ["action1"]
98
+ assert client.service_account_json == json.dumps(
99
+ {"email": "test@example.com"}
100
+ )
101
+ assert client.credential_cache is None
102
+
103
+ def test_get_openapi_spec_for_integration_success(
104
+ self,
105
+ project,
106
+ location,
107
+ integration_name,
108
+ trigger_name,
109
+ mock_credentials,
110
+ mock_connections_client,
111
+ ):
112
+ expected_spec = {"openapi": "3.0.0", "info": {"title": "Test Integration"}}
113
+ mock_response = mock.MagicMock()
114
+ mock_response.status_code = 200
115
+ mock_response.json.return_value = {"openApiSpec": json.dumps(expected_spec)}
116
+
117
+ with mock.patch.object(
118
+ IntegrationClient,
119
+ "_get_access_token",
120
+ return_value=mock_credentials.token,
121
+ ), mock.patch("requests.post", return_value=mock_response):
122
+ client = IntegrationClient(
123
+ project=project,
124
+ location=location,
125
+ integration=integration_name,
126
+ trigger=trigger_name,
127
+ connection=None,
128
+ entity_operations=None,
129
+ actions=None,
130
+ service_account_json=None,
131
+ )
132
+ spec = client.get_openapi_spec_for_integration()
133
+ assert spec == expected_spec
134
+ requests.post.assert_called_once_with(
135
+ f"https://{location}-integrations.googleapis.com/v1/projects/{project}/locations/{location}:generateOpenApiSpec",
136
+ headers={
137
+ "Content-Type": "application/json",
138
+ "Authorization": f"Bearer {mock_credentials.token}",
139
+ },
140
+ json={
141
+ "apiTriggerResources": [{
142
+ "integrationResource": integration_name,
143
+ "triggerId": [trigger_name],
144
+ }],
145
+ "fileFormat": "JSON",
146
+ },
147
+ )
148
+
149
+ def test_get_openapi_spec_for_integration_credential_error(
150
+ self,
151
+ project,
152
+ location,
153
+ integration_name,
154
+ trigger_name,
155
+ mock_connections_client,
156
+ ):
157
+ with mock.patch.object(
158
+ IntegrationClient,
159
+ "_get_access_token",
160
+ side_effect=ValueError(
161
+ "Please provide a service account that has the required permissions"
162
+ " to access the connection."
163
+ ),
164
+ ):
165
+ client = IntegrationClient(
166
+ project=project,
167
+ location=location,
168
+ integration=integration_name,
169
+ trigger=trigger_name,
170
+ connection=None,
171
+ entity_operations=None,
172
+ actions=None,
173
+ service_account_json=None,
174
+ )
175
+ with pytest.raises(
176
+ Exception,
177
+ match=(
178
+ "An unexpected error occurred: Please provide a service account"
179
+ " that has the required permissions to access the connection."
180
+ ),
181
+ ):
182
+ client.get_openapi_spec_for_integration()
183
+
184
+ @pytest.mark.parametrize(
185
+ "status_code, response_text",
186
+ [(404, "Not Found"), (400, "Bad Request"), (404, ""), (400, "")],
187
+ )
188
+ def test_get_openapi_spec_for_integration_request_error_not_found_or_bad_request(
189
+ self,
190
+ project,
191
+ location,
192
+ integration_name,
193
+ trigger_name,
194
+ mock_credentials,
195
+ status_code,
196
+ response_text,
197
+ mock_connections_client,
198
+ ):
199
+ mock_response = mock.MagicMock()
200
+ mock_response.status_code = status_code
201
+ mock_response.raise_for_status.side_effect = exceptions.HTTPError(
202
+ f"HTTP error {status_code}: {response_text}"
203
+ )
204
+
205
+ with mock.patch.object(
206
+ IntegrationClient,
207
+ "_get_access_token",
208
+ return_value=mock_credentials.token,
209
+ ), mock.patch("requests.post", return_value=mock_response):
210
+ client = IntegrationClient(
211
+ project=project,
212
+ location=location,
213
+ integration=integration_name,
214
+ trigger=trigger_name,
215
+ connection=None,
216
+ entity_operations=None,
217
+ actions=None,
218
+ service_account_json=None,
219
+ )
220
+ with pytest.raises(
221
+ ValueError,
222
+ match=(
223
+ "Invalid request. Please check the provided values of"
224
+ f" project\\({project}\\), location\\({location}\\),"
225
+ f" integration\\({integration_name}\\) and"
226
+ f" trigger\\({trigger_name}\\)."
227
+ ),
228
+ ):
229
+ client.get_openapi_spec_for_integration()
230
+
231
+ def test_get_openapi_spec_for_integration_other_request_error(
232
+ self,
233
+ project,
234
+ location,
235
+ integration_name,
236
+ trigger_name,
237
+ mock_credentials,
238
+ mock_connections_client,
239
+ ):
240
+ mock_response = mock.MagicMock()
241
+ mock_response.status_code = 500
242
+ mock_response.raise_for_status.side_effect = exceptions.HTTPError(
243
+ "Internal Server Error"
244
+ )
245
+
246
+ with mock.patch.object(
247
+ IntegrationClient,
248
+ "_get_access_token",
249
+ return_value=mock_credentials.token,
250
+ ), mock.patch("requests.post", return_value=mock_response):
251
+ client = IntegrationClient(
252
+ project=project,
253
+ location=location,
254
+ integration=integration_name,
255
+ trigger=trigger_name,
256
+ connection=None,
257
+ entity_operations=None,
258
+ actions=None,
259
+ service_account_json=None,
260
+ )
261
+ with pytest.raises(ValueError, match="Request error: "):
262
+ client.get_openapi_spec_for_integration()
263
+
264
+ def test_get_openapi_spec_for_integration_unexpected_error(
265
+ self,
266
+ project,
267
+ location,
268
+ integration_name,
269
+ trigger_name,
270
+ mock_credentials,
271
+ mock_connections_client,
272
+ ):
273
+ with mock.patch.object(
274
+ IntegrationClient,
275
+ "_get_access_token",
276
+ return_value=mock_credentials.token,
277
+ ), mock.patch(
278
+ "requests.post", side_effect=Exception("Something went wrong")
279
+ ):
280
+ client = IntegrationClient(
281
+ project=project,
282
+ location=location,
283
+ integration=integration_name,
284
+ trigger=trigger_name,
285
+ connection=None,
286
+ entity_operations=None,
287
+ actions=None,
288
+ service_account_json=None,
289
+ )
290
+ with pytest.raises(
291
+ Exception, match="An unexpected error occurred: Something went wrong"
292
+ ):
293
+ client.get_openapi_spec_for_integration()
294
+
295
+ def test_get_openapi_spec_for_connection_no_entity_operations_or_actions(
296
+ self, project, location, connection_name, mock_connections_client
297
+ ):
298
+ client = IntegrationClient(
299
+ project=project,
300
+ location=location,
301
+ integration=None,
302
+ trigger=None,
303
+ connection=connection_name,
304
+ entity_operations=None,
305
+ actions=None,
306
+ service_account_json=None,
307
+ )
308
+ with pytest.raises(
309
+ ValueError,
310
+ match=(
311
+ "No entity operations or actions provided. Please provide at least"
312
+ " one of them."
313
+ ),
314
+ ):
315
+ client.get_openapi_spec_for_connection()
316
+
317
+ def test_get_openapi_spec_for_connection_with_entity_operations(
318
+ self, project, location, connection_name, mock_connections_client
319
+ ):
320
+ entity_operations = {"entity1": ["LIST", "GET"]}
321
+
322
+ mock_connections_client_instance = mock_connections_client.return_value
323
+ mock_connections_client_instance.get_connector_base_spec.return_value = {
324
+ "components": {"schemas": {}},
325
+ "paths": {},
326
+ }
327
+ mock_connections_client_instance.get_entity_schema_and_operations.return_value = (
328
+ {"type": "object", "properties": {"id": {"type": "string"}}},
329
+ ["LIST", "GET"],
330
+ )
331
+ mock_connections_client_instance.connector_payload.return_value = {
332
+ "type": "object"
333
+ }
334
+ mock_connections_client_instance.list_operation.return_value = {"get": {}}
335
+ mock_connections_client_instance.list_operation_request.return_value = {
336
+ "type": "object"
337
+ }
338
+ mock_connections_client_instance.get_operation.return_value = {"get": {}}
339
+ mock_connections_client_instance.get_operation_request.return_value = {
340
+ "type": "object"
341
+ }
342
+
343
+ client = IntegrationClient(
344
+ project=project,
345
+ location=location,
346
+ integration=None,
347
+ trigger=None,
348
+ connection=connection_name,
349
+ entity_operations=entity_operations,
350
+ actions=None,
351
+ service_account_json=None,
352
+ )
353
+ spec = client.get_openapi_spec_for_connection()
354
+ assert "paths" in spec
355
+ assert (
356
+ f"/v2/projects/{project}/locations/{location}/integrations/ExecuteConnection:execute?triggerId=api_trigger/ExecuteConnection#list_entity1"
357
+ in spec["paths"]
358
+ )
359
+ assert (
360
+ f"/v2/projects/{project}/locations/{location}/integrations/ExecuteConnection:execute?triggerId=api_trigger/ExecuteConnection#get_entity1"
361
+ in spec["paths"]
362
+ )
363
+ mock_connections_client.assert_called_once_with(
364
+ project, location, connection_name, None
365
+ )
366
+ mock_connections_client_instance.get_connector_base_spec.assert_called_once()
367
+ mock_connections_client_instance.get_entity_schema_and_operations.assert_any_call(
368
+ "entity1"
369
+ )
370
+ mock_connections_client_instance.connector_payload.assert_any_call(
371
+ {"type": "object", "properties": {"id": {"type": "string"}}}
372
+ )
373
+ mock_connections_client_instance.list_operation.assert_called_once()
374
+ mock_connections_client_instance.get_operation.assert_called_once()
375
+
376
+ def test_get_openapi_spec_for_connection_with_actions(
377
+ self, project, location, connection_name, mock_connections_client
378
+ ):
379
+ actions = ["TestAction"]
380
+ mock_connections_client_instance = (
381
+ mock_connections_client.return_value
382
+ ) # Corrected line
383
+ mock_connections_client_instance.get_connector_base_spec.return_value = {
384
+ "components": {"schemas": {}},
385
+ "paths": {},
386
+ }
387
+ mock_connections_client_instance.get_action_schema.return_value = {
388
+ "inputSchema": {
389
+ "type": "object",
390
+ "properties": {"input": {"type": "string"}},
391
+ },
392
+ "outputSchema": {
393
+ "type": "object",
394
+ "properties": {"output": {"type": "string"}},
395
+ },
396
+ "displayName": "TestAction",
397
+ }
398
+ mock_connections_client_instance.connector_payload.side_effect = [
399
+ {"type": "object"},
400
+ {"type": "object"},
401
+ ]
402
+ mock_connections_client_instance.action_request.return_value = {
403
+ "type": "object"
404
+ }
405
+ mock_connections_client_instance.action_response.return_value = {
406
+ "type": "object"
407
+ }
408
+ mock_connections_client_instance.get_action_operation.return_value = {
409
+ "post": {}
410
+ }
411
+
412
+ client = IntegrationClient(
413
+ project=project,
414
+ location=location,
415
+ integration=None,
416
+ trigger=None,
417
+ connection=connection_name,
418
+ entity_operations=None,
419
+ actions=actions,
420
+ service_account_json=None,
421
+ )
422
+ spec = client.get_openapi_spec_for_connection()
423
+ assert "paths" in spec
424
+ assert (
425
+ f"/v2/projects/{project}/locations/{location}/integrations/ExecuteConnection:execute?triggerId=api_trigger/ExecuteConnection#TestAction"
426
+ in spec["paths"]
427
+ )
428
+ mock_connections_client.assert_called_once_with(
429
+ project, location, connection_name, None
430
+ )
431
+ mock_connections_client_instance.get_connector_base_spec.assert_called_once()
432
+ mock_connections_client_instance.get_action_schema.assert_called_once_with(
433
+ "TestAction"
434
+ )
435
+ mock_connections_client_instance.connector_payload.assert_any_call(
436
+ {"type": "object", "properties": {"input": {"type": "string"}}}
437
+ )
438
+ mock_connections_client_instance.connector_payload.assert_any_call(
439
+ {"type": "object", "properties": {"output": {"type": "string"}}}
440
+ )
441
+ mock_connections_client_instance.action_request.assert_called_once_with(
442
+ "TestAction"
443
+ )
444
+ mock_connections_client_instance.action_response.assert_called_once_with(
445
+ "TestAction"
446
+ )
447
+ mock_connections_client_instance.get_action_operation.assert_called_once()
448
+
449
+ def test_get_openapi_spec_for_connection_invalid_operation(
450
+ self, project, location, connection_name, mock_connections_client
451
+ ):
452
+ entity_operations = {"entity1": ["INVALID"]}
453
+ mock_connections_client_instance = mock_connections_client.return_value
454
+ mock_connections_client_instance.get_connector_base_spec.return_value = {
455
+ "components": {"schemas": {}},
456
+ "paths": {},
457
+ }
458
+ mock_connections_client_instance.get_entity_schema_and_operations.return_value = (
459
+ {"type": "object", "properties": {"id": {"type": "string"}}},
460
+ ["LIST", "GET"],
461
+ )
462
+
463
+ client = IntegrationClient(
464
+ project=project,
465
+ location=location,
466
+ integration=None,
467
+ trigger=None,
468
+ connection=connection_name,
469
+ entity_operations=entity_operations,
470
+ actions=None,
471
+ service_account_json=None,
472
+ )
473
+ with pytest.raises(
474
+ ValueError, match="Invalid operation: INVALID for entity: entity1"
475
+ ):
476
+ client.get_openapi_spec_for_connection()
477
+
478
+ def test_get_access_token_with_service_account_json(
479
+ self, project, location, integration_name, trigger_name, connection_name
480
+ ):
481
+ service_account_json = json.dumps({
482
+ "client_email": "test@example.com",
483
+ "private_key": "test_key",
484
+ })
485
+ mock_creds = mock.create_autospec(service_account.Credentials)
486
+ mock_creds.token = "sa_token"
487
+ mock_creds.expired = False
488
+
489
+ with mock.patch(
490
+ "google.oauth2.service_account.Credentials.from_service_account_info",
491
+ return_value=mock_creds,
492
+ ), mock.patch.object(mock_creds, "refresh", return_value=None):
493
+ client = IntegrationClient(
494
+ project=project,
495
+ location=location,
496
+ integration=integration_name,
497
+ trigger=trigger_name,
498
+ connection=connection_name,
499
+ entity_operations=None,
500
+ actions=None,
501
+ service_account_json=service_account_json,
502
+ )
503
+ token = client._get_access_token()
504
+ assert token == "sa_token"
505
+ service_account.Credentials.from_service_account_info.assert_called_once_with(
506
+ json.loads(service_account_json),
507
+ scopes=["https://www.googleapis.com/auth/cloud-platform"],
508
+ )
509
+ mock_creds.refresh.assert_called_once()
510
+
511
+ def test_get_access_token_with_default_credentials(
512
+ self,
513
+ project,
514
+ location,
515
+ integration_name,
516
+ trigger_name,
517
+ connection_name,
518
+ mock_credentials,
519
+ ):
520
+ mock_credentials.expired = False
521
+ with mock.patch(
522
+ "google.adk.tools.application_integration_tool.clients.integration_client.default_service_credential",
523
+ return_value=(mock_credentials, "test_project_id"),
524
+ ), mock.patch.object(mock_credentials, "refresh", return_value=None):
525
+ client = IntegrationClient(
526
+ project=project,
527
+ location=location,
528
+ integration=integration_name,
529
+ trigger=trigger_name,
530
+ connection=connection_name,
531
+ entity_operations=None,
532
+ actions=None,
533
+ service_account_json=None,
534
+ )
535
+ token = client._get_access_token()
536
+ assert token == "test_token"
537
+
538
+ def test_get_access_token_no_valid_credentials(
539
+ self, project, location, integration_name, trigger_name, connection_name
540
+ ):
541
+ with mock.patch(
542
+ "google.adk.tools.application_integration_tool.clients.integration_client.default_service_credential",
543
+ return_value=(None, None),
544
+ ), mock.patch(
545
+ "google.oauth2.service_account.Credentials.from_service_account_info",
546
+ return_value=None,
547
+ ):
548
+ client = IntegrationClient(
549
+ project=project,
550
+ location=location,
551
+ integration=integration_name,
552
+ trigger=trigger_name,
553
+ connection=connection_name,
554
+ entity_operations=None,
555
+ actions=None,
556
+ service_account_json=None,
557
+ )
558
+ try:
559
+ client._get_access_token()
560
+ assert False, "ValueError was not raised" # Explicitly fail if no error
561
+ except ValueError as e:
562
+ assert (
563
+ "Please provide a service account that has the required permissions"
564
+ " to access the connection."
565
+ in str(e)
566
+ )
567
+
568
+ def test_get_access_token_uses_cached_token(
569
+ self,
570
+ project,
571
+ location,
572
+ integration_name,
573
+ trigger_name,
574
+ connection_name,
575
+ mock_credentials,
576
+ ):
577
+ mock_credentials.token = "cached_token"
578
+ mock_credentials.expired = False
579
+ client = IntegrationClient(
580
+ project=project,
581
+ location=location,
582
+ integration=integration_name,
583
+ trigger=trigger_name,
584
+ connection=connection_name,
585
+ entity_operations=None,
586
+ actions=None,
587
+ service_account_json=None,
588
+ )
589
+ client.credential_cache = mock_credentials # Simulate a cached credential
590
+ with mock.patch("google.auth.default") as mock_default, mock.patch(
591
+ "google.oauth2.service_account.Credentials.from_service_account_info"
592
+ ) as mock_sa:
593
+ token = client._get_access_token()
594
+ assert token == "cached_token"
595
+ mock_default.assert_not_called()
596
+ mock_sa.assert_not_called()
597
+
598
+ def test_get_access_token_refreshes_expired_token(
599
+ self,
600
+ project,
601
+ location,
602
+ integration_name,
603
+ trigger_name,
604
+ connection_name,
605
+ mock_credentials,
606
+ ):
607
+ mock_credentials = mock.create_autospec(google.auth.credentials.Credentials)
608
+ mock_credentials.token = "old_token"
609
+ mock_credentials.expired = True
610
+ mock_credentials.refresh.return_value = None
611
+ mock_credentials.token = "new_token" # Simulate token refresh
612
+
613
+ with mock.patch(
614
+ "google.adk.tools.application_integration_tool.clients.integration_client.default_service_credential",
615
+ return_value=(mock_credentials, "test_project_id"),
616
+ ):
617
+ client = IntegrationClient(
618
+ project=project,
619
+ location=location,
620
+ integration=integration_name,
621
+ trigger=trigger_name,
622
+ connection=connection_name,
623
+ entity_operations=None,
624
+ actions=None,
625
+ service_account_json=None,
626
+ )
627
+ client.credential_cache = mock_credentials
628
+ token = client._get_access_token()
629
+ assert token == "new_token"
630
+ mock_credentials.refresh.assert_called_once()