agent-starter-pack 0.3.3__py3-none-any.whl → 0.21.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 (255) hide show
  1. agent_starter_pack/agents/README.md +7 -0
  2. agents/langgraph_base_react/template/.templateconfig.yaml → agent_starter_pack/agents/adk_a2a_base/.template/templateconfig.yaml +5 -10
  3. agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
  4. src/deployment_targets/cloud_run/Dockerfile → agent_starter_pack/agents/adk_a2a_base/app/__init__.py +2 -14
  5. agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
  6. agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
  7. agents/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → agent_starter_pack/agents/adk_a2a_base/notebooks/evaluating_adk_agent.ipynb +163 -199
  8. {agents/adk_base → agent_starter_pack/agents/adk_a2a_base}/tests/integration/test_agent.py +2 -2
  9. agents/adk_base/template/.templateconfig.yaml → agent_starter_pack/agents/adk_base/.template/templateconfig.yaml +4 -1
  10. {agents → agent_starter_pack/agents}/adk_base/README.md +1 -1
  11. agent_starter_pack/agents/adk_base/app/__init__.py +17 -0
  12. {agents → agent_starter_pack/agents}/adk_base/app/agent.py +5 -2
  13. {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +128 -82
  14. agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb → agent_starter_pack/agents/adk_base/notebooks/evaluating_adk_agent.ipynb +181 -207
  15. agent_starter_pack/agents/adk_base/tests/integration/test_agent.py +58 -0
  16. agents/crewai_coding_crew/template/.templateconfig.yaml → agent_starter_pack/agents/adk_live/.template/templateconfig.yaml +5 -9
  17. agent_starter_pack/agents/adk_live/README.md +32 -0
  18. agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
  19. agent_starter_pack/agents/adk_live/app/agent.py +51 -0
  20. agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
  21. agents/agentic_rag/template/.templateconfig.yaml → agent_starter_pack/agents/agentic_rag/.template/templateconfig.yaml +7 -3
  22. {agents → agent_starter_pack/agents}/agentic_rag/README.md +1 -1
  23. agent_starter_pack/agents/agentic_rag/app/__init__.py +17 -0
  24. {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +8 -4
  25. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +128 -82
  26. agent_starter_pack/agents/agentic_rag/notebooks/evaluating_adk_agent.ipynb +1535 -0
  27. {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +3 -3
  28. agent_starter_pack/agents/langgraph_base/.template/templateconfig.yaml +31 -0
  29. agent_starter_pack/agents/langgraph_base/README.md +30 -0
  30. agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
  31. agent_starter_pack/agents/langgraph_base/app/agent.py +34 -0
  32. {agents/crewai_coding_crew → agent_starter_pack/agents/langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +30 -17
  33. {agents/langgraph_base_react → agent_starter_pack/agents/langgraph_base}/tests/integration/test_agent.py +2 -2
  34. {src → agent_starter_pack}/base_template/.gitignore +5 -3
  35. agent_starter_pack/base_template/GEMINI.md +5 -0
  36. agent_starter_pack/base_template/Makefile +339 -0
  37. agent_starter_pack/base_template/README.md +267 -0
  38. agent_starter_pack/base_template/deployment/README.md +11 -0
  39. {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +2 -2
  40. {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +6 -1
  41. {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +12 -11
  42. {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +5 -1
  43. {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +1 -1
  44. {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +10 -10
  45. agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
  46. agent_starter_pack/base_template/deployment/terraform/github.tf +337 -0
  47. {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +20 -41
  48. {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +10 -3
  49. {src/resources/setup_cicd → agent_starter_pack/base_template/deployment/terraform}/providers.tf +8 -1
  50. {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +7 -8
  51. agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
  52. {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +7 -16
  53. {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +61 -28
  54. {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +6 -1
  55. agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
  56. src/base_template/deployment/terraform/build_triggers.tf → agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +55 -38
  57. agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
  58. {src → agent_starter_pack}/base_template/pyproject.toml +24 -37
  59. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +132 -0
  60. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
  61. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +259 -0
  62. src/base_template/deployment/cd/deploy-to-prod.yaml → agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +38 -30
  63. src/base_template/deployment/ci/pr_checks.yaml → agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +5 -5
  64. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +322 -0
  65. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
  66. {src/base_template/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/typing.py +7 -9
  67. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/__init__.py +25 -0
  68. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/part_converter.py +138 -0
  69. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/__init__.py +13 -0
  70. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/a2a_agent_executor.py +265 -0
  71. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/task_result_aggregator.py +152 -0
  72. agent_starter_pack/cli/commands/create.py +1256 -0
  73. agent_starter_pack/cli/commands/enhance.py +611 -0
  74. agent_starter_pack/cli/commands/list.py +203 -0
  75. agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
  76. agent_starter_pack/cli/commands/setup_cicd.py +862 -0
  77. {src → agent_starter_pack}/cli/main.py +10 -2
  78. {src → agent_starter_pack}/cli/utils/cicd.py +139 -48
  79. agent_starter_pack/cli/utils/gcp.py +263 -0
  80. agent_starter_pack/cli/utils/logging.py +103 -0
  81. agent_starter_pack/cli/utils/remote_template.py +677 -0
  82. agent_starter_pack/cli/utils/template.py +1466 -0
  83. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +1 -1
  84. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +20 -6
  85. {src → agent_starter_pack}/data_ingestion/pyproject.toml +1 -0
  86. {src → agent_starter_pack}/data_ingestion/uv.lock +602 -494
  87. agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +484 -0
  88. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
  89. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +424 -0
  90. agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
  91. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +263 -0
  92. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
  93. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +519 -0
  94. agent_starter_pack/deployment_targets/cloud_run/Dockerfile +51 -0
  95. agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +243 -0
  96. agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +417 -0
  97. agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +705 -0
  98. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +321 -0
  99. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +165 -0
  100. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +329 -0
  101. agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/fast_api_app.py +556 -0
  102. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +79 -1044
  103. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
  104. agent_starter_pack/frontends/adk_live_react/frontend/src/App.tsx +65 -0
  105. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
  106. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
  107. agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/SidePanel.tsx +516 -0
  108. agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/side-panel.scss +563 -0
  109. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
  110. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
  111. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
  112. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +40 -2
  113. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
  114. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
  115. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +210 -24
  116. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
  117. agent_starter_pack/resources/docs/adk-cheatsheet.md +1628 -0
  118. agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
  119. agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
  120. agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +4946 -0
  121. agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +4991 -0
  122. agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4963 -0
  123. agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +5006 -0
  124. agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +5487 -0
  125. agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +5532 -0
  126. agent_starter_pack/resources/locks/uv-langgraph_base-agent_engine.lock +5788 -0
  127. agent_starter_pack/resources/locks/uv-langgraph_base-cloud_run.lock +5811 -0
  128. {src → agent_starter_pack}/utils/generate_locks.py +15 -12
  129. {src → agent_starter_pack}/utils/lock_utils.py +4 -7
  130. {src → agent_starter_pack}/utils/watch_and_rebuild.py +2 -2
  131. agent_starter_pack-0.21.0.dist-info/METADATA +182 -0
  132. agent_starter_pack-0.21.0.dist-info/RECORD +171 -0
  133. agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
  134. llm.txt +362 -0
  135. agent_starter_pack-0.3.3.dist-info/METADATA +0 -164
  136. agent_starter_pack-0.3.3.dist-info/RECORD +0 -176
  137. agent_starter_pack-0.3.3.dist-info/entry_points.txt +0 -2
  138. agents/crewai_coding_crew/README.md +0 -34
  139. agents/crewai_coding_crew/app/agent.py +0 -86
  140. agents/crewai_coding_crew/app/crew/config/agents.yaml +0 -39
  141. agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
  142. agents/crewai_coding_crew/app/crew/crew.py +0 -71
  143. agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
  144. agents/langgraph_base_react/README.md +0 -9
  145. agents/langgraph_base_react/app/agent.py +0 -73
  146. agents/live_api/README.md +0 -37
  147. agents/live_api/app/agent.py +0 -78
  148. agents/live_api/app/server.py +0 -196
  149. agents/live_api/app/templates.py +0 -51
  150. agents/live_api/app/vector_store.py +0 -55
  151. agents/live_api/template/.templateconfig.yaml +0 -29
  152. agents/live_api/tests/integration/test_server_e2e.py +0 -254
  153. agents/live_api/tests/load_test/load_test.py +0 -40
  154. agents/live_api/tests/unit/test_server.py +0 -143
  155. src/base_template/Makefile +0 -72
  156. src/base_template/README.md +0 -208
  157. src/base_template/app/__init__.py +0 -3
  158. src/base_template/app/utils/tracing.py +0 -155
  159. src/base_template/deployment/README.md +0 -126
  160. src/base_template/deployment/cd/staging.yaml +0 -216
  161. src/base_template/deployment/terraform/dev/log_sinks.tf +0 -63
  162. src/base_template/deployment/terraform/log_sinks.tf +0 -70
  163. src/base_template/deployment/terraform/providers.tf +0 -37
  164. src/cli/commands/create.py +0 -664
  165. src/cli/commands/setup_cicd.py +0 -829
  166. src/cli/utils/gcp.py +0 -117
  167. src/cli/utils/logging.py +0 -51
  168. src/cli/utils/template.py +0 -737
  169. src/deployment_targets/agent_engine/app/agent_engine_app.py +0 -336
  170. src/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
  171. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -183
  172. src/deployment_targets/agent_engine/tests/load_test/README.md +0 -42
  173. src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -107
  174. src/deployment_targets/cloud_run/app/server.py +0 -154
  175. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +0 -249
  176. src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
  177. src/deployment_targets/cloud_run/tests/load_test/README.md +0 -83
  178. src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -118
  179. src/deployment_targets/cloud_run/uv.lock +0 -6952
  180. src/frontends/live_api_react/frontend/src/App.tsx +0 -205
  181. src/frontends/live_api_react/frontend/src/components/control-tray/ControlTray.tsx +0 -217
  182. src/frontends/live_api_react/frontend/src/components/control-tray/control-tray.scss +0 -201
  183. src/frontends/live_api_react/frontend/src/components/side-panel/SidePanel.tsx +0 -161
  184. src/frontends/live_api_react/frontend/src/components/side-panel/side-panel.scss +0 -285
  185. src/frontends/streamlit/frontend/side_bar.py +0 -214
  186. src/frontends/streamlit/frontend/streamlit_app.py +0 -265
  187. src/frontends/streamlit/frontend/style/app_markdown.py +0 -37
  188. src/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
  189. src/frontends/streamlit/frontend/utils/local_chat_history.py +0 -125
  190. src/frontends/streamlit/frontend/utils/message_editing.py +0 -59
  191. src/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
  192. src/frontends/streamlit/frontend/utils/stream_handler.py +0 -301
  193. src/frontends/streamlit/frontend/utils/title_summary.py +0 -94
  194. src/frontends/streamlit_adk/frontend/side_bar.py +0 -214
  195. src/frontends/streamlit_adk/frontend/streamlit_app.py +0 -314
  196. src/frontends/streamlit_adk/frontend/style/app_markdown.py +0 -37
  197. src/frontends/streamlit_adk/frontend/utils/chat_utils.py +0 -84
  198. src/frontends/streamlit_adk/frontend/utils/local_chat_history.py +0 -110
  199. src/frontends/streamlit_adk/frontend/utils/message_editing.py +0 -61
  200. src/frontends/streamlit_adk/frontend/utils/multimodal_utils.py +0 -223
  201. src/frontends/streamlit_adk/frontend/utils/stream_handler.py +0 -311
  202. src/frontends/streamlit_adk/frontend/utils/title_summary.py +0 -129
  203. src/resources/containers/data_processing/Dockerfile +0 -27
  204. src/resources/containers/e2e-tests/Dockerfile +0 -19
  205. src/resources/idx/.idx/dev.nix +0 -57
  206. src/resources/idx/idx-template.json +0 -21
  207. src/resources/idx/idx-template.nix +0 -26
  208. src/resources/locks/uv-adk_base-agent_engine.lock +0 -5338
  209. src/resources/locks/uv-adk_base-cloud_run.lock +0 -5930
  210. src/resources/locks/uv-agentic_rag-agent_engine.lock +0 -5528
  211. src/resources/locks/uv-agentic_rag-cloud_run.lock +0 -6120
  212. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6231
  213. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -6839
  214. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +0 -5233
  215. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +0 -5862
  216. src/resources/locks/uv-live_api-cloud_run.lock +0 -5832
  217. src/resources/setup_cicd/cicd_variables.tf +0 -41
  218. src/resources/setup_cicd/github.tf +0 -87
  219. {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
  220. {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
  221. {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
  222. {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
  223. {src/deployment_targets/agent_engine/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/gcs.py +0 -0
  224. {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
  225. {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
  226. {src → agent_starter_pack}/cli/utils/version.py +0 -0
  227. {src → agent_starter_pack}/data_ingestion/README.md +0 -0
  228. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
  229. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
  230. {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
  231. {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
  232. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
  233. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
  234. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
  235. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
  236. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
  237. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
  238. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
  239. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
  240. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
  241. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
  242. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
  243. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
  244. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
  245. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
  246. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
  247. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
  248. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
  249. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
  250. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
  251. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
  252. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
  253. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
  254. {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
  255. {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1070 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2025 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Utility to register an Agent Engine to Gemini Enterprise."""
17
+
18
+ import json
19
+ import os
20
+ import subprocess
21
+ from pathlib import Path
22
+
23
+ import click
24
+ import requests
25
+ import vertexai
26
+ from google.auth import default
27
+ from google.auth.transport.requests import Request as GoogleAuthRequest
28
+ from rich.console import Console
29
+
30
+ console = Console(highlight=False)
31
+ console_err = Console(stderr=True, highlight=False)
32
+
33
+
34
+ def get_discovery_engine_endpoint(location: str) -> str:
35
+ """Get the appropriate Discovery Engine API endpoint for the given location.
36
+
37
+ Args:
38
+ location: The location/region (e.g., 'global', 'us', 'eu')
39
+
40
+ Returns:
41
+ The Discovery Engine API endpoint base URL
42
+
43
+ Examples:
44
+ >>> get_discovery_engine_endpoint('global')
45
+ 'https://discoveryengine.googleapis.com'
46
+ >>> get_discovery_engine_endpoint('eu')
47
+ 'https://eu-discoveryengine.googleapis.com'
48
+ >>> get_discovery_engine_endpoint('us')
49
+ 'https://us-discoveryengine.googleapis.com'
50
+ """
51
+ if location == "global":
52
+ return "https://discoveryengine.googleapis.com"
53
+ else:
54
+ # Regional endpoints use the format: https://{region}-discoveryengine.googleapis.com
55
+ return f"https://{location}-discoveryengine.googleapis.com"
56
+
57
+
58
+ def parse_agent_engine_id(agent_engine_id: str) -> dict[str, str] | None:
59
+ """Parse an Agent Engine resource name to extract components.
60
+
61
+ Args:
62
+ agent_engine_id: Agent Engine resource name
63
+ (e.g., projects/PROJECT_NUM/locations/REGION/reasoningEngines/ENGINE_ID)
64
+
65
+ Returns:
66
+ Dictionary with 'project', 'location', 'engine_id' keys, or None if invalid format
67
+ """
68
+ parts = agent_engine_id.split("/")
69
+ if (
70
+ len(parts) == 6
71
+ and parts[0] == "projects"
72
+ and parts[2] == "locations"
73
+ and parts[4] == "reasoningEngines"
74
+ ):
75
+ return {
76
+ "project": parts[1],
77
+ "location": parts[3],
78
+ "engine_id": parts[5],
79
+ }
80
+ return None
81
+
82
+
83
+ def parse_gemini_enterprise_app_id(app_id: str) -> dict[str, str] | None:
84
+ """Parse Gemini Enterprise app resource name to extract components.
85
+
86
+ Args:
87
+ app_id: Gemini Enterprise app resource name
88
+ (e.g., projects/{project_number}/locations/{location}/collections/{collection}/engines/{engine_id})
89
+
90
+ Returns:
91
+ Dictionary with 'project_number', 'location', 'collection', 'engine_id' keys, or None if invalid format
92
+ """
93
+ parts = app_id.split("/")
94
+ if (
95
+ len(parts) == 8
96
+ and parts[0] == "projects"
97
+ and parts[2] == "locations"
98
+ and parts[4] == "collections"
99
+ and parts[6] == "engines"
100
+ ):
101
+ return {
102
+ "project_number": parts[1],
103
+ "location": parts[3],
104
+ "collection": parts[5],
105
+ "engine_id": parts[7],
106
+ }
107
+ return None
108
+
109
+
110
+ def get_access_token() -> str:
111
+ """Get Google Cloud access token.
112
+
113
+ Returns:
114
+ Access token string
115
+
116
+ Raises:
117
+ RuntimeError: If authentication fails
118
+ """
119
+ try:
120
+ credentials, _ = default()
121
+ auth_req = GoogleAuthRequest()
122
+ credentials.refresh(auth_req)
123
+ return credentials.token
124
+ except Exception as e:
125
+ console_err.print(f"Error getting access token: {e}")
126
+ console_err.print(
127
+ "Please ensure you are authenticated with 'gcloud auth application-default login'"
128
+ )
129
+ raise RuntimeError("Failed to get access token") from e
130
+
131
+
132
+ def get_identity_token() -> str:
133
+ """Get Google Cloud identity token.
134
+
135
+ Returns:
136
+ Identity token string
137
+
138
+ Raises:
139
+ RuntimeError: If authentication fails
140
+ """
141
+ try:
142
+ result = subprocess.run(
143
+ ["gcloud", "auth", "print-identity-token"],
144
+ capture_output=True,
145
+ text=True,
146
+ check=True,
147
+ )
148
+ return result.stdout.strip()
149
+ except subprocess.CalledProcessError as e:
150
+ console_err.print(f"Error getting identity token: {e.stderr}")
151
+ console_err.print(
152
+ "Please ensure you are authenticated with 'gcloud auth login'"
153
+ )
154
+ raise RuntimeError("Failed to get identity token") from e
155
+ except FileNotFoundError as e:
156
+ console_err.print("Error: gcloud command not found")
157
+ console_err.print("Please install Google Cloud SDK")
158
+ raise RuntimeError("Failed to get identity token") from e
159
+
160
+
161
+ def fetch_agent_card_from_url(url: str, deployment_target: str) -> dict | None:
162
+ """Fetch agent card from a URL with proper authentication.
163
+
164
+ Args:
165
+ url: The URL to fetch the agent card from
166
+ deployment_target: The deployment target ('agent_engine' or 'cloud_run')
167
+
168
+ Returns:
169
+ Agent card dictionary if successful, None otherwise
170
+ """
171
+ try:
172
+ headers = {}
173
+
174
+ # Use appropriate authentication based on deployment target
175
+ if deployment_target == "agent_engine":
176
+ access_token = get_access_token()
177
+ headers["Authorization"] = f"Bearer {access_token}"
178
+ elif deployment_target == "cloud_run":
179
+ identity_token = get_identity_token()
180
+ headers["Authorization"] = f"Bearer {identity_token}"
181
+ else:
182
+ raise ValueError(
183
+ f"Unknown deployment target: {deployment_target}. "
184
+ f"Expected 'agent_engine' or 'cloud_run'"
185
+ )
186
+
187
+ response = requests.get(url, headers=headers, timeout=10)
188
+ response.raise_for_status()
189
+
190
+ return response.json()
191
+ except requests.exceptions.HTTPError as e:
192
+ console_err.print(
193
+ f"⚠️ HTTP error fetching agent card from {url}: {e}",
194
+ style="yellow",
195
+ )
196
+ if e.response.status_code == 401 or e.response.status_code == 403:
197
+ console_err.print(
198
+ " Authentication failed. Ensure you are logged in with 'gcloud auth application-default login'",
199
+ style="yellow",
200
+ )
201
+ return None
202
+ except Exception as e:
203
+ console_err.print(
204
+ f"⚠️ Could not fetch agent card from {url}: {e}",
205
+ style="yellow",
206
+ )
207
+ return None
208
+
209
+
210
+ def construct_agent_card_url_from_metadata(
211
+ metadata: dict,
212
+ ) -> str | None:
213
+ """Construct agent card URL from deployment metadata (Agent Engine only).
214
+
215
+ Args:
216
+ metadata: Deployment metadata dictionary
217
+
218
+ Returns:
219
+ Agent card URL if construction succeeds, None otherwise
220
+ """
221
+ deployment_target = metadata.get("deployment_target")
222
+
223
+ if deployment_target == "agent_engine":
224
+ # For Agent Engine: construct URL from remote_agent_engine_id
225
+ remote_agent_engine_id = metadata.get("remote_agent_engine_id")
226
+ if remote_agent_engine_id and remote_agent_engine_id != "None":
227
+ parsed = parse_agent_engine_id(remote_agent_engine_id)
228
+ if parsed:
229
+ location = parsed["location"]
230
+ # Agent Engine A2A endpoint format
231
+ agent_card_url = (
232
+ f"https://{location}-aiplatform.googleapis.com/v1beta1/"
233
+ f"{remote_agent_engine_id}/a2a/v1/card"
234
+ )
235
+ return agent_card_url
236
+
237
+ return None
238
+
239
+
240
+ def prompt_for_agent_card_url_with_auto_construct(
241
+ metadata: dict | None,
242
+ default_url: str | None = None,
243
+ ) -> str:
244
+ """Get agent card URL with automatic construction from deployment metadata.
245
+
246
+ Args:
247
+ metadata: Deployment metadata dictionary (can be None)
248
+ default_url: Default agent card URL (e.g., from CLI arg)
249
+
250
+ Returns:
251
+ Agent card URL
252
+ """
253
+ # If default URL provided, show as smart default
254
+ if default_url:
255
+ console.print("\nAgent card URL provided:")
256
+ console.print(f" [bold]{default_url}[/]")
257
+ use_default = click.confirm(
258
+ "Use this agent card URL?", default=True, show_default=True
259
+ )
260
+ if use_default:
261
+ return default_url
262
+
263
+ # Try to auto-construct from metadata (Agent Engine only)
264
+ if metadata:
265
+ auto_url = construct_agent_card_url_from_metadata(metadata)
266
+
267
+ if auto_url:
268
+ # Successfully constructed from Agent Engine metadata
269
+ console.print(
270
+ "\n✅ Found Agent Engine deployment in deployment_metadata.json"
271
+ )
272
+ console.print(f" Agent card URL: [bold]{auto_url}[/]")
273
+
274
+ use_auto = click.confirm(
275
+ "\nUse this agent card URL?", default=True, show_default=True
276
+ )
277
+
278
+ if use_auto:
279
+ return auto_url
280
+
281
+ # Fallback: manual entry
282
+ console.print("\n[blue]" + "=" * 70 + "[/]")
283
+ console.print("[blue]A2A AGENT CARD URL[/]")
284
+ console.print("[blue]" + "=" * 70 + "[/]")
285
+ console.print(
286
+ "\nEnter your agent card URL manually"
287
+ "\n[blue]Example: https://your-service.run.app/a2a/app/.well-known/agent-card.json[/]"
288
+ )
289
+
290
+ agent_card_url = click.prompt(
291
+ "\nAgent card URL",
292
+ type=str,
293
+ ).strip()
294
+
295
+ return agent_card_url
296
+
297
+
298
+ def get_agent_engine_metadata(agent_engine_id: str) -> tuple[str | None, str | None]:
299
+ """Fetch display_name and description from deployed Agent Engine.
300
+
301
+ Args:
302
+ agent_engine_id: Agent Engine resource name
303
+
304
+ Returns:
305
+ Tuple of (display_name, description) - either can be None if not found
306
+ """
307
+ parts = agent_engine_id.split("/")
308
+ if len(parts) < 6:
309
+ return None, None
310
+
311
+ project_id = parts[1]
312
+ location = parts[3]
313
+
314
+ try:
315
+ client = vertexai.Client(project=project_id, location=location)
316
+ agent_engine = client.agent_engines.get(name=agent_engine_id)
317
+
318
+ display_name = getattr(agent_engine.api_resource, "display_name", None)
319
+ description = getattr(agent_engine.api_resource, "description", None)
320
+
321
+ return display_name, description
322
+ except Exception as e:
323
+ console_err.print(f"Warning: Could not fetch metadata from Agent Engine: {e}")
324
+ return None, None
325
+
326
+
327
+ def prompt_for_agent_engine_id(default_from_metadata: str | None) -> str:
328
+ """Prompt user for Agent Engine ID with optional default.
329
+
330
+ Args:
331
+ default_from_metadata: Default value from deployment_metadata.json if available
332
+
333
+ Returns:
334
+ The Agent Engine resource name
335
+ """
336
+ if default_from_metadata:
337
+ console.print("\nFound Agent Engine ID from deployment_metadata.json:")
338
+ console.print(f" [bold]{default_from_metadata}[/]")
339
+ use_default = click.confirm(
340
+ "Use this Agent Engine ID?", default=True, show_default=True
341
+ )
342
+ if use_default:
343
+ return default_from_metadata
344
+
345
+ console.print(
346
+ "\nEnter your Agent Engine resource name"
347
+ "\n[blue]Example: projects/123456789/locations/us-central1/reasoningEngines/1234567890[/]"
348
+ "\n(You can find this in the Agent Builder Console or deployment_metadata.json)"
349
+ )
350
+
351
+ while True:
352
+ agent_engine_id = click.prompt("Agent Engine ID", type=str).strip()
353
+ parsed = parse_agent_engine_id(agent_engine_id)
354
+ if parsed:
355
+ return agent_engine_id
356
+ else:
357
+ console_err.print(
358
+ "❌ Invalid format. Expected: projects/{project}/locations/{location}/reasoningEngines/{id}",
359
+ style="bold red",
360
+ )
361
+
362
+
363
+ def prompt_for_gemini_enterprise_components(
364
+ default_project: str | None = None,
365
+ ) -> str:
366
+ """Prompt user for Gemini Enterprise resource components and construct full ID.
367
+
368
+ Args:
369
+ default_project: Default project number from Agent Engine ID
370
+
371
+ Returns:
372
+ Full Gemini Enterprise app resource name
373
+ """
374
+ console.print("\n[blue]" + "=" * 70 + "[/]")
375
+ console.print("[blue]GEMINI ENTERPRISE CONFIGURATION[/]")
376
+ console.print("[blue]" + "=" * 70 + "[/]")
377
+
378
+ console.print(
379
+ "\nYou need to provide the Gemini Enterprise app details."
380
+ "\nFind these in: Google Cloud Console → Gemini Enterprise → Apps"
381
+ "\nCopy the ID from the 'ID' column for your Gemini Enterprise instance."
382
+ )
383
+
384
+ while True:
385
+ # Project number
386
+ if default_project:
387
+ console.print(f"\n[dim]Default: {default_project}[/]")
388
+ project_number = click.prompt(
389
+ "Project number", type=str, default=default_project or ""
390
+ ).strip()
391
+
392
+ # Location - GE apps are typically in 'global', 'us', or 'eu'
393
+ console.print("\nGemini Enterprise apps are in: global, us, or eu")
394
+ location = click.prompt(
395
+ "Location/Region",
396
+ type=str,
397
+ default="global",
398
+ show_default=True,
399
+ ).strip()
400
+
401
+ # Gemini Enterprise short ID
402
+ console.print(
403
+ "\nEnter your Gemini Enterprise ID (from the 'ID' column in the Apps table)."
404
+ "\n[blue]Example: gemini-enterprise-123456_1234567890[/]"
405
+ )
406
+ ge_short_id = click.prompt("Gemini Enterprise ID", type=str).strip()
407
+
408
+ # Construct full resource name
409
+ # Format: projects/{project_number}/locations/{location}/collections/default_collection/engines/{ge_id}
410
+ full_id = f"projects/{project_number}/locations/{location}/collections/default_collection/engines/{ge_short_id}"
411
+
412
+ console.print("\nConstructed Gemini Enterprise App ID:")
413
+ console.print(f" [bold]{full_id}[/]")
414
+ confirmed = click.confirm("Is this correct?", default=True)
415
+
416
+ if confirmed:
417
+ return full_id
418
+
419
+ click.echo("Let's try again...")
420
+
421
+
422
+ def ensure_discovery_engine_invoker_role(
423
+ project_id: str,
424
+ project_number: str,
425
+ ) -> None:
426
+ """Grant Cloud Run Invoker role to Discovery Engine service account at project level.
427
+
428
+ Silently grants the role if not already present. Only shows warnings/errors
429
+ if there are permission issues or unexpected failures.
430
+
431
+ Args:
432
+ project_id: GCP project ID
433
+ project_number: GCP project number
434
+ """
435
+ try:
436
+ # Construct Discovery Engine service account
437
+ service_account = (
438
+ f"service-{project_number}@gcp-sa-discoveryengine.iam.gserviceaccount.com"
439
+ )
440
+
441
+ result = subprocess.run(
442
+ [
443
+ "gcloud",
444
+ "projects",
445
+ "add-iam-policy-binding",
446
+ project_id,
447
+ f"--member=serviceAccount:{service_account}",
448
+ "--role=roles/run.servicesInvoker",
449
+ "--condition=None",
450
+ "--quiet",
451
+ ],
452
+ capture_output=True,
453
+ text=True,
454
+ )
455
+
456
+ if result.returncode != 0:
457
+ error_msg = result.stderr.lower()
458
+ # Ignore "already exists" type errors
459
+ if "already exists" not in error_msg and "already has" not in error_msg:
460
+ # Permission errors - show warning but don't fail
461
+ if "permission" in error_msg or "forbidden" in error_msg:
462
+ console.print(
463
+ f"\n⚠️ [yellow]Warning: Could not grant roles/run.invoker to {service_account}[/]\n"
464
+ )
465
+
466
+ except Exception:
467
+ pass
468
+
469
+
470
+ def register_a2a_agent(
471
+ agent_card: dict,
472
+ agent_card_url: str,
473
+ gemini_enterprise_app_id: str,
474
+ display_name: str,
475
+ description: str,
476
+ project_id: str | None = None,
477
+ authorization_id: str | None = None,
478
+ ) -> dict:
479
+ """Register an A2A agent to Gemini Enterprise.
480
+
481
+ Args:
482
+ agent_card: Agent card dictionary fetched from the agent
483
+ agent_card_url: URL where the agent card was fetched from
484
+ gemini_enterprise_app_id: Full Gemini Enterprise app resource name
485
+ display_name: Display name for the agent in Gemini Enterprise
486
+ description: Description of the agent
487
+ project_id: Optional GCP project ID for billing
488
+ authorization_id: Optional OAuth authorization ID
489
+
490
+ Returns:
491
+ API response as dictionary
492
+
493
+ Raises:
494
+ requests.HTTPError: If the API request fails
495
+ ValueError: If gemini_enterprise_app_id format is invalid
496
+ """
497
+ parsed = parse_gemini_enterprise_app_id(gemini_enterprise_app_id)
498
+ if not parsed:
499
+ raise ValueError(
500
+ f"Invalid GEMINI_ENTERPRISE_APP_ID format. Expected: "
501
+ f"projects/{{project_number}}/locations/{{location}}/collections/{{collection}}/engines/{{engine_id}}, "
502
+ f"got: {gemini_enterprise_app_id}"
503
+ )
504
+
505
+ project_number = parsed["project_number"]
506
+ as_location = parsed["location"]
507
+ collection = parsed["collection"]
508
+ engine_id = parsed["engine_id"]
509
+
510
+ # Use provided project ID or fallback to project number from GE app
511
+ if not project_id:
512
+ project_id = project_number
513
+
514
+ access_token = get_access_token()
515
+ base_endpoint = get_discovery_engine_endpoint(as_location)
516
+ url = (
517
+ f"{base_endpoint}/v1alpha/projects/{project_number}/"
518
+ f"locations/{as_location}/collections/{collection}/engines/{engine_id}/"
519
+ "assistants/default_assistant/agents"
520
+ )
521
+ headers = {
522
+ "Authorization": f"Bearer {access_token}",
523
+ "Content-Type": "application/json",
524
+ "x-goog-user-project": project_id,
525
+ }
526
+
527
+ # Build payload with A2A agent definition
528
+ payload = {
529
+ "displayName": display_name,
530
+ "description": description,
531
+ "icon": {
532
+ "uri": "https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/smart_toy/default/24px.svg"
533
+ },
534
+ "a2aAgentDefinition": {"jsonAgentCard": json.dumps(agent_card)},
535
+ }
536
+
537
+ # Add authorization config if provided
538
+ if authorization_id:
539
+ payload["authorizationConfig"] = {"agentAuthorization": authorization_id}
540
+
541
+ console.print("\n[blue]Registering A2A agent to Gemini Enterprise...[/]")
542
+ console.print(f" Agent Card URL: {agent_card_url}")
543
+ console.print(f" Gemini Enterprise App: {gemini_enterprise_app_id}")
544
+ console.print(f" Display Name: {display_name}")
545
+
546
+ try:
547
+ response = requests.post(url, headers=headers, json=payload, timeout=30)
548
+ response.raise_for_status()
549
+
550
+ result = response.json()
551
+ console.print("\n✅ Successfully registered A2A agent to Gemini Enterprise!")
552
+ console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
553
+ return result
554
+
555
+ except requests.exceptions.HTTPError as http_err:
556
+ # Check if agent already exists and try to update
557
+ if response.status_code in (400, 409):
558
+ try:
559
+ error_data = response.json()
560
+ error_message = error_data.get("error", {}).get("message", "")
561
+
562
+ if (
563
+ "already exists" in error_message.lower()
564
+ or "duplicate" in error_message.lower()
565
+ ):
566
+ console.print(
567
+ "\n⚠️ [yellow]Agent already registered. Updating existing registration...[/]"
568
+ )
569
+
570
+ # List and find existing agent
571
+ list_response = requests.get(url, headers=headers, timeout=30)
572
+ list_response.raise_for_status()
573
+ agents_list = list_response.json().get("agents", [])
574
+
575
+ # Find matching agent (by URL in agent card)
576
+ existing_agent = None
577
+ for agent in agents_list:
578
+ a2a_def = agent.get("a2aAgentDefinition", {})
579
+ if a2a_def:
580
+ try:
581
+ card = json.loads(a2a_def.get("jsonAgentCard", "{}"))
582
+ if card.get("url") == agent_card_url:
583
+ existing_agent = agent
584
+ break
585
+ except json.JSONDecodeError:
586
+ continue
587
+
588
+ if existing_agent:
589
+ agent_name = existing_agent.get("name")
590
+ update_url = f"{base_endpoint}/v1alpha/{agent_name}"
591
+
592
+ console.print(f" Updating agent: {agent_name}")
593
+
594
+ update_response = requests.patch(
595
+ update_url, headers=headers, json=payload, timeout=30
596
+ )
597
+ update_response.raise_for_status()
598
+
599
+ result = update_response.json()
600
+ console.print(
601
+ "\n✅ Successfully updated A2A agent registration!"
602
+ )
603
+ console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
604
+ return result
605
+ except (ValueError, KeyError) as e:
606
+ console_err.print(
607
+ f"Warning: Could not parse error response from API: {e}"
608
+ )
609
+
610
+ console_err.print(
611
+ f"\n❌ [red]HTTP error occurred: {http_err}[/]",
612
+ style="bold red",
613
+ )
614
+ console_err.print(f" Response: {response.text}")
615
+ raise
616
+ except requests.exceptions.RequestException as req_err:
617
+ console_err.print(
618
+ f"\n❌ [red]Request error occurred: {req_err}[/]",
619
+ style="bold red",
620
+ )
621
+ raise
622
+
623
+
624
+ def register_agent(
625
+ agent_engine_id: str,
626
+ gemini_enterprise_app_id: str,
627
+ display_name: str,
628
+ description: str,
629
+ tool_description: str,
630
+ project_id: str | None = None,
631
+ authorization_id: str | None = None,
632
+ ) -> dict:
633
+ """Register an agent engine to Gemini Enterprise.
634
+
635
+ This function attempts to create a new agent registration. If the agent is already
636
+ registered (same reasoning engine), it will automatically update the existing
637
+ registration instead.
638
+
639
+ Args:
640
+ agent_engine_id: Agent engine resource name (e.g., projects/.../reasoningEngines/...)
641
+ gemini_enterprise_app_id: Full Gemini Enterprise app resource name
642
+ (e.g., projects/{project_number}/locations/{location}/collections/{collection}/engines/{engine_id})
643
+ display_name: Display name for the agent in Gemini Enterprise
644
+ description: Description of the agent
645
+ tool_description: Description of what the tool does
646
+ project_id: Optional GCP project ID for billing (extracted from agent_engine_id if not provided)
647
+ authorization_id: Optional OAuth authorization ID
648
+ (e.g., projects/{project_number}/locations/global/authorizations/{auth_id})
649
+
650
+ Returns:
651
+ API response as dictionary
652
+
653
+ Raises:
654
+ requests.HTTPError: If the API request fails
655
+ ValueError: If gemini_enterprise_app_id format is invalid
656
+ """
657
+ parsed = parse_gemini_enterprise_app_id(gemini_enterprise_app_id)
658
+ if not parsed:
659
+ raise ValueError(
660
+ f"Invalid GEMINI_ENTERPRISE_APP_ID format. Expected: "
661
+ f"projects/{{project_number}}/locations/{{location}}/collections/{{collection}}/engines/{{engine_id}}, "
662
+ f"got: {gemini_enterprise_app_id}"
663
+ )
664
+
665
+ project_number = parsed["project_number"]
666
+ as_location = parsed["location"]
667
+ collection = parsed["collection"]
668
+ engine_id = parsed["engine_id"]
669
+
670
+ # Use project from agent engine if not explicitly provided (for billing header)
671
+ if not project_id:
672
+ parsed_agent = parse_agent_engine_id(agent_engine_id)
673
+ if parsed_agent:
674
+ project_id = parsed_agent["project"]
675
+ else:
676
+ project_id = project_number
677
+
678
+ # Get access token
679
+ access_token = get_access_token()
680
+
681
+ # Build API endpoint with regional support
682
+ base_endpoint = get_discovery_engine_endpoint(as_location)
683
+ url = (
684
+ f"{base_endpoint}/v1alpha/projects/{project_number}/"
685
+ f"locations/{as_location}/collections/{collection}/engines/{engine_id}/"
686
+ "assistants/default_assistant/agents"
687
+ )
688
+
689
+ # Request headers
690
+ headers = {
691
+ "Authorization": f"Bearer {access_token}",
692
+ "Content-Type": "application/json",
693
+ "x-goog-user-project": project_id,
694
+ }
695
+
696
+ # Request body
697
+ adk_agent_definition: dict = {
698
+ "tool_settings": {"tool_description": tool_description},
699
+ "provisioned_reasoning_engine": {"reasoningEngine": agent_engine_id},
700
+ }
701
+
702
+ # Add OAuth authorization if provided
703
+ if authorization_id:
704
+ adk_agent_definition["authorizations"] = [authorization_id]
705
+
706
+ payload = {
707
+ "displayName": display_name,
708
+ "description": description,
709
+ "icon": {
710
+ "uri": "https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/smart_toy/default/24px.svg"
711
+ },
712
+ "adk_agent_definition": adk_agent_definition,
713
+ }
714
+
715
+ console.print("\n[blue]Registering agent to Gemini Enterprise...[/]")
716
+ console.print(f" Agent Engine: {agent_engine_id}")
717
+ console.print(f" Gemini Enterprise App: {gemini_enterprise_app_id}")
718
+ console.print(f" Display Name: {display_name}")
719
+
720
+ try:
721
+ # Try to create a new registration first
722
+ response = requests.post(url, headers=headers, json=payload, timeout=30)
723
+ response.raise_for_status()
724
+
725
+ result = response.json()
726
+ console.print("\n✅ Successfully registered agent to Gemini Enterprise!")
727
+ console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
728
+ return result
729
+
730
+ except requests.exceptions.HTTPError as http_err:
731
+ # Check if the error is because the agent already exists
732
+ if response.status_code in (400, 409):
733
+ try:
734
+ error_data = response.json()
735
+ error_message = error_data.get("error", {}).get("message", "")
736
+
737
+ # Check if error indicates the agent already exists
738
+ if (
739
+ "already exists" in error_message.lower()
740
+ or "duplicate" in error_message.lower()
741
+ ):
742
+ console.print(
743
+ "\n⚠️ [yellow]Agent already registered. Updating existing registration...[/]"
744
+ )
745
+
746
+ # For update, we need to use the specific agent resource name
747
+ # The agent name should be in the error or we can construct it
748
+ # Format: {url}/{agent_id} but we need to find existing agent first
749
+
750
+ # List existing agents to find the one for this reasoning engine
751
+ list_response = requests.get(url, headers=headers, timeout=30)
752
+ list_response.raise_for_status()
753
+ agents_list = list_response.json().get("agents", [])
754
+
755
+ # Find the agent that matches our reasoning engine
756
+ existing_agent = None
757
+ for agent in agents_list:
758
+ re_name = (
759
+ agent.get("adk_agent_definition", {})
760
+ .get("provisioned_reasoning_engine", {})
761
+ .get("reasoningEngine", "")
762
+ )
763
+ if re_name == agent_engine_id:
764
+ existing_agent = agent
765
+ break
766
+
767
+ if existing_agent:
768
+ agent_name = existing_agent.get("name")
769
+ update_url = f"{base_endpoint}/v1alpha/{agent_name}"
770
+
771
+ console.print(f" Updating agent: {agent_name}")
772
+
773
+ # PATCH request to update
774
+ update_response = requests.patch(
775
+ update_url, headers=headers, json=payload, timeout=30
776
+ )
777
+ update_response.raise_for_status()
778
+
779
+ result = update_response.json()
780
+ console.print(
781
+ "\n✅ Successfully updated agent registration in Gemini Enterprise!"
782
+ )
783
+ console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
784
+ return result
785
+ else:
786
+ console_err.print(
787
+ "❌ [red]Could not find existing agent to update[/]",
788
+ style="bold red",
789
+ )
790
+ raise
791
+ except (ValueError, KeyError) as e:
792
+ console_err.print(
793
+ f"Warning: Could not parse error response from API: {e}"
794
+ )
795
+
796
+ # If not an "already exists" error, or update failed, raise the original error
797
+ console_err.print(
798
+ f"\n❌ [red]HTTP error occurred: {http_err}[/]",
799
+ style="bold red",
800
+ )
801
+ console_err.print(f" Response: {response.text}")
802
+ raise
803
+ except requests.exceptions.RequestException as req_err:
804
+ console_err.print(
805
+ f"\n❌ [red]Request error occurred: {req_err}[/]",
806
+ style="bold red",
807
+ )
808
+ raise
809
+
810
+
811
+ @click.command()
812
+ @click.option(
813
+ "--agent-engine-id",
814
+ envvar="AGENT_ENGINE_ID",
815
+ help="Agent Engine resource name (e.g., projects/.../reasoningEngines/...). "
816
+ "If not provided, reads from deployment_metadata.json.",
817
+ )
818
+ @click.option(
819
+ "--metadata-file",
820
+ default="deployment_metadata.json",
821
+ help="Path to deployment metadata file (default: deployment_metadata.json).",
822
+ )
823
+ @click.option(
824
+ "--gemini-enterprise-app-id",
825
+ help="Gemini Enterprise app full resource name "
826
+ "(e.g., projects/{project_number}/locations/{location}/collections/{collection}/engines/{engine_id}). "
827
+ "If not provided, the command will prompt you interactively. "
828
+ "Can also be set via ID or GEMINI_ENTERPRISE_APP_ID env var.",
829
+ )
830
+ @click.option(
831
+ "--display-name",
832
+ envvar="GEMINI_DISPLAY_NAME",
833
+ help="Display name for the agent.",
834
+ )
835
+ @click.option(
836
+ "--description",
837
+ envvar="GEMINI_DESCRIPTION",
838
+ help="Description of the agent.",
839
+ )
840
+ @click.option(
841
+ "--tool-description",
842
+ envvar="GEMINI_TOOL_DESCRIPTION",
843
+ help="Description of what the tool does.",
844
+ )
845
+ @click.option(
846
+ "--project-id",
847
+ envvar="GOOGLE_CLOUD_PROJECT",
848
+ help="GCP project ID (extracted from agent-engine-id if not provided).",
849
+ )
850
+ @click.option(
851
+ "--authorization-id",
852
+ envvar="GEMINI_AUTHORIZATION_ID",
853
+ help="OAuth authorization resource name "
854
+ "(e.g., projects/{project_number}/locations/global/authorizations/{auth_id}).",
855
+ )
856
+ @click.option(
857
+ "--agent-card-url",
858
+ envvar="AGENT_CARD_URL",
859
+ help="URL to fetch the agent card for A2A agents "
860
+ "(e.g., https://your-service.run.app/a2a/app/.well-known/agent-card.json). "
861
+ "If provided, registers as an A2A agent instead of ADK agent.",
862
+ )
863
+ @click.option(
864
+ "--deployment-target",
865
+ envvar="DEPLOYMENT_TARGET",
866
+ type=click.Choice(["agent_engine", "cloud_run"], case_sensitive=False),
867
+ help="Deployment target (agent_engine or cloud_run).",
868
+ )
869
+ @click.option(
870
+ "--project-number",
871
+ envvar="PROJECT_NUMBER",
872
+ help="GCP project number. Used as default when prompting for Gemini Enterprise configuration.",
873
+ )
874
+ def register_gemini_enterprise(
875
+ agent_engine_id: str | None,
876
+ metadata_file: str,
877
+ gemini_enterprise_app_id: str | None,
878
+ display_name: str | None,
879
+ description: str | None,
880
+ tool_description: str | None,
881
+ project_id: str | None,
882
+ authorization_id: str | None,
883
+ agent_card_url: str | None,
884
+ deployment_target: str | None,
885
+ project_number: str | None,
886
+ ) -> None:
887
+ """Register an agent to Gemini Enterprise.
888
+
889
+ This command can run interactively or accept all parameters via command-line options.
890
+ If key parameters are missing, it will prompt the user for input.
891
+ """
892
+ console.print("\n🤖 Agent → Gemini Enterprise Registration\n")
893
+
894
+ # Read metadata file once to determine agent type and deployment target
895
+ metadata = None
896
+ try:
897
+ metadata_path = Path(metadata_file)
898
+ if metadata_path.exists():
899
+ with open(metadata_path, encoding="utf-8") as f:
900
+ metadata = json.load(f)
901
+ except (json.JSONDecodeError, KeyError, FileNotFoundError):
902
+ pass
903
+
904
+ provided_agent_card_url = agent_card_url or os.getenv("AGENT_CARD_URL")
905
+
906
+ if not provided_agent_card_url:
907
+ if metadata:
908
+ is_a2a = metadata.get("is_a2a", False)
909
+ if is_a2a:
910
+ console.print("[blue]→ Detected A2A agent[/]")
911
+ agent_card_url = prompt_for_agent_card_url_with_auto_construct(
912
+ metadata, None
913
+ )
914
+ if not deployment_target:
915
+ deployment_target = metadata.get("deployment_target", "cloud_run")
916
+ else:
917
+ console.print("[blue]→ Detected ADK agent[/]")
918
+ agent_card_url = None
919
+ else:
920
+ agent_card_url = prompt_for_agent_card_url_with_auto_construct(None, None)
921
+ if not deployment_target:
922
+ deployment_target = "cloud_run"
923
+ else:
924
+ agent_card_url = prompt_for_agent_card_url_with_auto_construct(
925
+ metadata, provided_agent_card_url
926
+ )
927
+ if not deployment_target:
928
+ deployment_target = (
929
+ metadata.get("deployment_target", "cloud_run")
930
+ if metadata
931
+ else "cloud_run"
932
+ )
933
+
934
+ # A2A registration
935
+ if agent_card_url:
936
+ # Ensure deployment_target has a value (default to cloud_run if not set)
937
+ resolved_deployment_target = deployment_target or "cloud_run"
938
+ agent_card = fetch_agent_card_from_url(
939
+ agent_card_url, resolved_deployment_target
940
+ )
941
+ if not agent_card:
942
+ raise click.ClickException(
943
+ f"Failed to fetch agent card from {agent_card_url}. "
944
+ "Please verify the URL is correct and the agent is running."
945
+ )
946
+
947
+ console.print(f"✓ Fetched agent card: {agent_card.get('name', 'Unknown')}")
948
+
949
+ resolved_gemini_enterprise_app_id = (
950
+ gemini_enterprise_app_id
951
+ or os.getenv("ID")
952
+ or os.getenv("GEMINI_ENTERPRISE_APP_ID")
953
+ )
954
+
955
+ if not resolved_gemini_enterprise_app_id:
956
+ default_project = project_number
957
+ if (
958
+ not default_project
959
+ and metadata
960
+ and metadata.get("deployment_target") == "agent_engine"
961
+ ):
962
+ remote_agent_engine_id = metadata.get("remote_agent_engine_id")
963
+ if remote_agent_engine_id:
964
+ parsed = parse_agent_engine_id(remote_agent_engine_id)
965
+ if parsed:
966
+ default_project = parsed["project"]
967
+
968
+ resolved_gemini_enterprise_app_id = prompt_for_gemini_enterprise_components(
969
+ default_project=default_project
970
+ )
971
+
972
+ # Get display name and description with smart defaults from agent card
973
+ if not display_name:
974
+ default_display_name = agent_card.get("name") or "My A2A Agent"
975
+ resolved_display_name = click.prompt(
976
+ "Display name", default=default_display_name
977
+ )
978
+ else:
979
+ resolved_display_name = display_name
980
+
981
+ if not description:
982
+ default_description = agent_card.get("description") or "AI Agent"
983
+ resolved_description = click.prompt(
984
+ "Description", default=default_description
985
+ )
986
+ else:
987
+ resolved_description = description
988
+
989
+ # For Cloud Run deployments, ensure Discovery Engine has invoker permissions
990
+ if resolved_deployment_target == "cloud_run":
991
+ parsed_ge = parse_gemini_enterprise_app_id(
992
+ resolved_gemini_enterprise_app_id
993
+ )
994
+ if parsed_ge and project_number:
995
+ ensure_discovery_engine_invoker_role(
996
+ project_id=project_id or parsed_ge["project_number"],
997
+ project_number=project_number,
998
+ )
999
+
1000
+ # Register as A2A agent
1001
+ try:
1002
+ register_a2a_agent(
1003
+ agent_card=agent_card,
1004
+ agent_card_url=agent_card_url,
1005
+ gemini_enterprise_app_id=resolved_gemini_enterprise_app_id,
1006
+ display_name=resolved_display_name,
1007
+ description=resolved_description,
1008
+ project_id=project_id,
1009
+ authorization_id=authorization_id,
1010
+ )
1011
+ except Exception as e:
1012
+ raise click.ClickException(f"Error during A2A registration: {e}") from e
1013
+
1014
+ # ADK
1015
+ else:
1016
+ # Step 1: Get Agent Engine ID
1017
+ resolved_agent_engine_id = agent_engine_id
1018
+
1019
+ if not resolved_agent_engine_id:
1020
+ env_id = os.getenv("AGENT_ENGINE_ID")
1021
+ if env_id:
1022
+ resolved_agent_engine_id = env_id
1023
+ else:
1024
+ metadata_id = (
1025
+ metadata.get("remote_agent_engine_id") if metadata else None
1026
+ )
1027
+ resolved_agent_engine_id = prompt_for_agent_engine_id(metadata_id)
1028
+
1029
+ # Validate and parse Agent Engine ID
1030
+ parsed_ae = parse_agent_engine_id(resolved_agent_engine_id)
1031
+ if not parsed_ae:
1032
+ raise click.ClickException(
1033
+ f"Invalid Agent Engine ID format: {resolved_agent_engine_id}\n"
1034
+ "Expected: projects/{{project}}/locations/{{location}}/reasoningEngines/{{id}}"
1035
+ )
1036
+
1037
+ # Step 2: Get Gemini Enterprise App ID
1038
+ resolved_gemini_enterprise_app_id = (
1039
+ gemini_enterprise_app_id
1040
+ or os.getenv("ID")
1041
+ or os.getenv("GEMINI_ENTERPRISE_APP_ID")
1042
+ )
1043
+
1044
+ if not resolved_gemini_enterprise_app_id:
1045
+ resolved_gemini_enterprise_app_id = prompt_for_gemini_enterprise_components(
1046
+ default_project=parsed_ae["project"]
1047
+ )
1048
+
1049
+ # Step 3: Get display name and description
1050
+ auto_display_name, auto_description = get_agent_engine_metadata(
1051
+ resolved_agent_engine_id
1052
+ )
1053
+
1054
+ resolved_display_name = display_name or auto_display_name or "My Agent"
1055
+ resolved_description = description or auto_description or "AI Agent"
1056
+ resolved_tool_description = tool_description or resolved_description
1057
+
1058
+ # Step 4: Register as ADK agent
1059
+ try:
1060
+ register_agent(
1061
+ agent_engine_id=resolved_agent_engine_id,
1062
+ gemini_enterprise_app_id=resolved_gemini_enterprise_app_id,
1063
+ display_name=resolved_display_name,
1064
+ description=resolved_description,
1065
+ tool_description=resolved_tool_description,
1066
+ project_id=project_id,
1067
+ authorization_id=authorization_id,
1068
+ )
1069
+ except Exception as e:
1070
+ raise click.ClickException(f"Error during ADK registration: {e}") from e