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,1256 @@
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 datetime
16
+ import logging
17
+ import os
18
+ import pathlib
19
+ import shutil
20
+ import subprocess
21
+ import tempfile
22
+ from collections.abc import Callable
23
+
24
+ import click
25
+ from click.core import ParameterSource
26
+ from rich.console import Console
27
+ from rich.prompt import IntPrompt, Prompt
28
+
29
+ from ..utils.datastores import DATASTORE_TYPES, DATASTORES
30
+ from ..utils.gcp import verify_credentials, verify_vertex_connection
31
+ from ..utils.logging import display_welcome_banner, handle_cli_error
32
+ from ..utils.remote_template import (
33
+ fetch_remote_template,
34
+ get_base_template_name,
35
+ load_remote_template_config,
36
+ merge_template_configs,
37
+ parse_agent_spec,
38
+ )
39
+ from ..utils.template import (
40
+ add_base_template_dependencies_interactively,
41
+ get_available_agents,
42
+ get_deployment_targets,
43
+ get_template_path,
44
+ load_template_config,
45
+ process_template,
46
+ prompt_cicd_runner_selection,
47
+ prompt_datastore_selection,
48
+ prompt_deployment_target,
49
+ prompt_session_type_selection,
50
+ )
51
+
52
+ console = Console()
53
+
54
+ # Export the shared decorator for use by other commands
55
+ __all__ = ["create", "shared_template_options"]
56
+
57
+
58
+ def shared_template_options(f: Callable) -> Callable:
59
+ """Decorator to add shared options for template-based commands."""
60
+ # Apply options in reverse order since decorators are applied bottom-up
61
+ f = click.option(
62
+ "-ag",
63
+ "--agent-garden",
64
+ is_flag=True,
65
+ help="Deployed from Agent Garden - customizes welcome messages",
66
+ default=False,
67
+ )(f)
68
+ f = click.option(
69
+ "--skip-checks",
70
+ is_flag=True,
71
+ help="Skip verification checks for GCP and Vertex AI",
72
+ default=False,
73
+ )(f)
74
+ f = click.option(
75
+ "--region",
76
+ help="GCP region for deployment (default: us-central1)",
77
+ default="us-central1",
78
+ )(f)
79
+ f = click.option(
80
+ "--auto-approve", is_flag=True, help="Skip credential confirmation prompts"
81
+ )(f)
82
+ f = click.option("--debug", is_flag=True, help="Enable debug logging")(f)
83
+ f = click.option(
84
+ "--session-type",
85
+ type=click.Choice(["in_memory", "cloud_sql", "agent_engine"]),
86
+ help="Type of session storage to use",
87
+ )(f)
88
+ f = click.option(
89
+ "--datastore",
90
+ "-ds",
91
+ type=click.Choice(DATASTORE_TYPES),
92
+ help="Type of datastore to use for data ingestion (requires --include-data-ingestion)",
93
+ )(f)
94
+ f = click.option(
95
+ "--include-data-ingestion",
96
+ "-i",
97
+ is_flag=True,
98
+ help="Include data ingestion pipeline in the project",
99
+ )(f)
100
+ f = click.option(
101
+ "--cicd-runner",
102
+ type=click.Choice(["google_cloud_build", "github_actions"]),
103
+ help="CI/CD runner to use",
104
+ )(f)
105
+ f = click.option(
106
+ "--deployment-target",
107
+ "-d",
108
+ type=click.Choice(["agent_engine", "cloud_run"]),
109
+ help="Deployment target name",
110
+ )(f)
111
+ f = click.option(
112
+ "--agent-directory",
113
+ "-dir",
114
+ help="Name of the agent directory (overrides template default)",
115
+ )(f)
116
+ f = click.option(
117
+ "--base-template",
118
+ "-bt",
119
+ help="Base template to use (overrides template default, only for remote templates)",
120
+ )(f)
121
+ return f
122
+
123
+
124
+ def get_available_base_templates() -> list[str]:
125
+ """Get list of available base templates for inheritance.
126
+
127
+ Returns:
128
+ List of base template names.
129
+ """
130
+ agents = get_available_agents()
131
+ return sorted([agent_info["name"] for agent_info in agents.values()])
132
+
133
+
134
+ def validate_base_template(base_template: str) -> bool:
135
+ """Validate that a base template exists.
136
+
137
+ Args:
138
+ base_template: Name of the base template to validate
139
+
140
+ Returns:
141
+ True if the base template exists, False otherwise
142
+ """
143
+ available_templates = get_available_base_templates()
144
+ return base_template in available_templates
145
+
146
+
147
+ def get_standard_ignore_patterns() -> Callable[[str, list[str]], list[str]]:
148
+ """Get standard ignore patterns for copying directories.
149
+
150
+ Returns:
151
+ A callable that can be used with shutil.copytree's ignore parameter.
152
+ """
153
+ exclude_dirs = {
154
+ ".git",
155
+ ".venv",
156
+ "venv",
157
+ "__pycache__",
158
+ ".pytest_cache",
159
+ "node_modules",
160
+ ".next",
161
+ "dist",
162
+ "build",
163
+ ".DS_Store",
164
+ ".vscode",
165
+ ".idea",
166
+ "*.egg-info",
167
+ ".mypy_cache",
168
+ ".coverage",
169
+ "htmlcov",
170
+ ".tox",
171
+ ".cache",
172
+ }
173
+
174
+ def ignore_patterns(dir: str, files: list[str]) -> list[str]:
175
+ return [f for f in files if f in exclude_dirs or f.startswith(".backup_")]
176
+
177
+ return ignore_patterns
178
+
179
+
180
+ def normalize_project_name(project_name: str) -> str:
181
+ """Normalize project name for better compatibility with cloud resources and tools."""
182
+
183
+ needs_normalization = (
184
+ any(char.isupper() for char in project_name) or "_" in project_name
185
+ )
186
+
187
+ if needs_normalization:
188
+ normalized_name = project_name
189
+ console.print(
190
+ "Note: Project names are normalized (lowercase, hyphens only) for better compatibility with cloud resources and tools.",
191
+ style="dim",
192
+ )
193
+ if any(char.isupper() for char in normalized_name):
194
+ normalized_name = normalized_name.lower()
195
+ console.print(
196
+ f"Info: Converting to lowercase for compatibility: '{project_name}' -> '{normalized_name}'",
197
+ style="bold yellow",
198
+ )
199
+
200
+ if "_" in normalized_name:
201
+ # Capture the name state before this specific change
202
+ name_before_hyphenation = normalized_name
203
+ normalized_name = normalized_name.replace("_", "-")
204
+ console.print(
205
+ f"Info: Replacing underscores with hyphens for compatibility: '{name_before_hyphenation}' -> '{normalized_name}'",
206
+ style="yellow",
207
+ )
208
+
209
+ return normalized_name
210
+
211
+ return project_name
212
+
213
+
214
+ @click.command()
215
+ @click.pass_context
216
+ @click.argument("project_name")
217
+ @click.option(
218
+ "--agent",
219
+ "-a",
220
+ help="Template identifier to use. Can be a local agent name (e.g., `chat_agent`), a local path (`local@/path/to/template`), an `adk-samples` shortcut (e.g., `adk@data-science`), or a remote Git URL. Both shorthand (e.g., `github.com/org/repo/path@main`) and full URLs from your browser (e.g., `https://github.com/org/repo/tree/main/path`) are supported. Lists available local templates if omitted.",
221
+ )
222
+ @click.option(
223
+ "--output-dir",
224
+ "-o",
225
+ type=click.Path(),
226
+ help="Output directory for the project (default: current directory)",
227
+ )
228
+ @click.option(
229
+ "--in-folder",
230
+ "-if",
231
+ is_flag=True,
232
+ help="Template files directly into the current directory instead of creating a new project directory",
233
+ default=False,
234
+ )
235
+ @click.option(
236
+ "--skip-welcome",
237
+ is_flag=True,
238
+ hidden=True,
239
+ help="Skip the welcome banner",
240
+ default=False,
241
+ )
242
+ @click.option(
243
+ "--locked",
244
+ is_flag=True,
245
+ hidden=True,
246
+ help="Internal flag for version-locked remote templates",
247
+ default=False,
248
+ )
249
+ @shared_template_options
250
+ @handle_cli_error
251
+ def create(
252
+ ctx: click.Context,
253
+ project_name: str,
254
+ agent: str | None,
255
+ deployment_target: str | None,
256
+ cicd_runner: str | None,
257
+ include_data_ingestion: bool,
258
+ datastore: str | None,
259
+ session_type: str | None,
260
+ debug: bool,
261
+ output_dir: str | None,
262
+ auto_approve: bool,
263
+ region: str,
264
+ skip_checks: bool,
265
+ in_folder: bool,
266
+ agent_directory: str | None,
267
+ agent_garden: bool = False,
268
+ base_template: str | None = None,
269
+ skip_welcome: bool = False,
270
+ locked: bool = False,
271
+ cli_overrides: dict | None = None,
272
+ ) -> None:
273
+ """Create GCP-based AI agent projects from templates."""
274
+ try:
275
+ console = Console()
276
+
277
+ # Display welcome banner (unless skipped)
278
+ if not skip_welcome:
279
+ display_welcome_banner(agent, agent_garden=agent_garden)
280
+ # Validate project name
281
+ if len(project_name) > 26:
282
+ console.print(
283
+ f"Error: Project name '{project_name}' exceeds 26 characters. Please use a shorter name.",
284
+ style="bold red",
285
+ )
286
+ return
287
+
288
+ project_name = normalize_project_name(project_name)
289
+
290
+ # Setup debug logging if enabled
291
+ if debug:
292
+ logging.basicConfig(level=logging.DEBUG)
293
+ console.print("> Debug mode enabled")
294
+ logging.debug("Starting CLI in debug mode")
295
+
296
+ # Convert output_dir to Path if provided, otherwise use current directory
297
+ destination_dir = pathlib.Path(output_dir) if output_dir else pathlib.Path.cwd()
298
+ destination_dir = destination_dir.resolve() # Convert to absolute path
299
+
300
+ if in_folder:
301
+ # For in-folder templating, use the current directory directly
302
+ project_path = destination_dir
303
+ # In-folder mode is permissive - we assume the user wants to enhance their existing repo
304
+
305
+ # Create backup of entire directory before in-folder templating
306
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
307
+ backup_dir = project_path / f".backup_{project_path.name}_{timestamp}"
308
+
309
+ console.print("📦 [blue]Creating backup before modification...[/blue]")
310
+
311
+ try:
312
+ shutil.copytree(
313
+ project_path, backup_dir, ignore=get_standard_ignore_patterns()
314
+ )
315
+ console.print(f"Backup created: [cyan]{backup_dir.name}[/cyan]")
316
+ except Exception as e:
317
+ console.print(
318
+ f"⚠️ [yellow]Warning: Could not create backup: {e}[/yellow]"
319
+ )
320
+ if not auto_approve:
321
+ if not click.confirm("Continue without backup?", default=True):
322
+ console.print("✋ [red]Operation cancelled.[/red]")
323
+ return
324
+
325
+ console.print()
326
+ else:
327
+ # Check if project would exist in output directory
328
+ project_path = destination_dir / project_name
329
+ if project_path.exists():
330
+ console.print(
331
+ f"Error: Project directory '{project_path}' already exists",
332
+ style="bold red",
333
+ )
334
+ return
335
+
336
+ # Agent selection - handle remote templates
337
+ selected_agent = None
338
+ template_source_path = None
339
+ temp_dir_to_clean = None
340
+ remote_spec = None
341
+
342
+ if agent:
343
+ if agent.startswith("local@"):
344
+ path_str = agent.split("@", 1)[1]
345
+ local_path = pathlib.Path(path_str).resolve()
346
+ if not local_path.is_dir():
347
+ raise click.ClickException(
348
+ f"Local path not found or not a directory: {local_path}"
349
+ )
350
+
351
+ # Create a temporary directory and copy the local template to it
352
+ temp_dir = tempfile.mkdtemp(prefix="asp_local_template_")
353
+ temp_dir_to_clean = temp_dir
354
+ template_source_path = pathlib.Path(temp_dir) / local_path.name
355
+ shutil.copytree(
356
+ local_path,
357
+ template_source_path,
358
+ ignore=get_standard_ignore_patterns(),
359
+ )
360
+
361
+ # Check for version lock and execute nested command if found
362
+ from ..utils.remote_template import check_and_execute_with_version_lock
363
+
364
+ if check_and_execute_with_version_lock(
365
+ template_source_path, agent, locked
366
+ ):
367
+ # If we executed with locked version, cleanup and exit
368
+ shutil.rmtree(temp_dir, ignore_errors=True)
369
+ return
370
+
371
+ selected_agent = f"local_{template_source_path.name}"
372
+ if locked:
373
+ # In locked mode, show a nicer message
374
+ console.print("✅ Using version-locked template", style="green")
375
+ else:
376
+ console.print(f"Using local template: {local_path}")
377
+ logging.debug(
378
+ f"Copied local template to temporary dir: {template_source_path}"
379
+ )
380
+ else:
381
+ # Check if it's a remote template specification
382
+ remote_spec = parse_agent_spec(agent)
383
+ if remote_spec:
384
+ if remote_spec.is_adk_samples:
385
+ console.print(
386
+ f"> Fetching template: {remote_spec.template_path}",
387
+ style="bold blue",
388
+ )
389
+ else:
390
+ console.print(f"Fetching remote template: {agent}")
391
+ template_source_path, temp_dir_path = fetch_remote_template(
392
+ remote_spec, agent, locked
393
+ )
394
+ temp_dir_to_clean = str(temp_dir_path)
395
+ selected_agent = f"remote_{hash(agent)}" # Generate unique name for remote template
396
+
397
+ # Show informational message for ADK samples with smart defaults
398
+ if remote_spec.is_adk_samples:
399
+ config = load_remote_template_config(
400
+ template_source_path, is_adk_sample=True
401
+ )
402
+ if not config.get("has_explicit_config", True):
403
+ console = Console()
404
+ console.print(
405
+ "\n[blue]ℹ️ Note: The starter pack uses heuristics to template this ADK sample agent.[/]"
406
+ )
407
+ console.print(
408
+ "[dim] The starter pack attempts to create a working codebase, but you'll need to follow the generated README for complete setup.[/]"
409
+ )
410
+ else:
411
+ # Handle local agent selection
412
+ agents = get_available_agents()
413
+ # First check if it's a valid agent name
414
+ if any(p["name"] == agent for p in agents.values()):
415
+ selected_agent = agent
416
+ else:
417
+ # Try numeric agent selection if input is a number
418
+ try:
419
+ agent_num = int(agent)
420
+ if agent_num in agents:
421
+ selected_agent = agents[agent_num]["name"]
422
+ else:
423
+ raise ValueError(f"Invalid agent number: {agent_num}")
424
+ except ValueError as err:
425
+ raise ValueError(
426
+ f"Invalid agent name or number: {agent}"
427
+ ) from err
428
+
429
+ # Agent selection
430
+ final_agent = selected_agent
431
+ if not final_agent:
432
+ if auto_approve:
433
+ raise click.ClickException(
434
+ "Error: --agent is required when running with --auto-approve."
435
+ )
436
+ final_agent = display_agent_selection(deployment_target)
437
+
438
+ # If browse functionality returned a remote agent spec, process it like CLI input
439
+ if final_agent and final_agent.startswith("adk@"):
440
+ # Set agent to the returned spec for remote processing
441
+ agent = final_agent
442
+
443
+ # Process the remote template spec just like CLI input
444
+ remote_spec = parse_agent_spec(agent)
445
+ if remote_spec:
446
+ if remote_spec.is_adk_samples:
447
+ console.print(
448
+ f"> Fetching template: {remote_spec.template_path}",
449
+ style="bold blue",
450
+ )
451
+ else:
452
+ console.print(f"Fetching remote template: {agent}")
453
+ template_source_path, temp_dir_path = fetch_remote_template(
454
+ remote_spec, agent, locked
455
+ )
456
+ temp_dir_to_clean = str(temp_dir_path)
457
+ final_agent = f"remote_{hash(agent)}" # Generate unique name for remote template
458
+
459
+ # Show informational message for ADK samples with smart defaults
460
+ if remote_spec.is_adk_samples:
461
+ config = load_remote_template_config(
462
+ template_source_path, is_adk_sample=True
463
+ )
464
+ if not config.get("has_explicit_config", True):
465
+ console = Console()
466
+ console.print(
467
+ "\n[blue]ℹ️ Note: The starter pack uses heuristics to template this ADK sample agent.[/]"
468
+ )
469
+ console.print(
470
+ "[dim] The starter pack attempts to create a working codebase, but you'll need to follow the generated README for complete setup.[/]"
471
+ )
472
+
473
+ if debug:
474
+ logging.debug(f"Selected agent: {final_agent}")
475
+
476
+ # Load template configuration based on whether it's remote or local
477
+ if template_source_path:
478
+ # Prepare CLI overrides for remote template config
479
+ # Initialize cli_overrides if not provided (e.g., from enhance command)
480
+ if cli_overrides is None:
481
+ cli_overrides = {}
482
+ if base_template:
483
+ # Validate that the base template exists
484
+ if not validate_base_template(base_template):
485
+ available_templates = get_available_base_templates()
486
+ console.print(
487
+ f"Error: Base template '{base_template}' not found.",
488
+ style="bold red",
489
+ )
490
+ console.print(
491
+ f"Available base templates: {', '.join(available_templates)}",
492
+ style="yellow",
493
+ )
494
+ raise click.Abort()
495
+ cli_overrides["base_template"] = base_template
496
+
497
+ # Load remote template config
498
+ source_config = load_remote_template_config(
499
+ template_source_path,
500
+ cli_overrides,
501
+ is_adk_sample=remote_spec.is_adk_samples if remote_spec else False,
502
+ )
503
+
504
+ # Remote templates now work even without pyproject.toml thanks to defaults
505
+ if debug and source_config:
506
+ logging.debug(f"Final remote template config: {source_config}")
507
+
508
+ # Load base template config for inheritance
509
+ base_template_name = get_base_template_name(source_config)
510
+ if debug:
511
+ logging.debug(f"Using base template: {base_template_name}")
512
+
513
+ base_template_path = (
514
+ pathlib.Path(__file__).parent.parent.parent
515
+ / "agents"
516
+ / base_template_name
517
+ / ".template"
518
+ )
519
+ base_config = load_template_config(base_template_path)
520
+
521
+ # Merge configs: remote inherits from and overrides base
522
+ config = merge_template_configs(base_config, source_config)
523
+ # For remote templates, use the template/ subdirectory as the template source
524
+ template_path = template_source_path / ".template"
525
+ else:
526
+ template_path = (
527
+ pathlib.Path(__file__).parent.parent.parent
528
+ / "agents"
529
+ / final_agent
530
+ / ".template"
531
+ )
532
+ config = load_template_config(template_path)
533
+
534
+ # Apply CLI overrides for local templates if provided (e.g., from enhance command)
535
+ if cli_overrides:
536
+ config = merge_template_configs(config, cli_overrides)
537
+ if debug:
538
+ logging.debug(
539
+ f"Applied CLI overrides to local template config: {cli_overrides}"
540
+ )
541
+ # Data ingestion and datastore selection
542
+ if include_data_ingestion or datastore:
543
+ include_data_ingestion = True
544
+ if not datastore:
545
+ if auto_approve:
546
+ # Default to the first available datastore in non-interactive mode
547
+ datastore = next(iter(DATASTORES.keys()))
548
+ console.print(
549
+ f"Info: --datastore not specified. Defaulting to '{datastore}' in auto-approve mode.",
550
+ style="yellow",
551
+ )
552
+ else:
553
+ datastore = prompt_datastore_selection(
554
+ final_agent, from_cli_flag=True
555
+ )
556
+ if debug:
557
+ logging.debug(f"Data ingestion enabled: {include_data_ingestion}")
558
+ logging.debug(f"Selected datastore type: {datastore}")
559
+ else:
560
+ # Check if the agent requires data ingestion
561
+ if config and config.get("settings", {}).get("requires_data_ingestion"):
562
+ include_data_ingestion = True
563
+ if not datastore:
564
+ if auto_approve:
565
+ datastore = next(iter(DATASTORES.keys()))
566
+ console.print(
567
+ f"Info: --datastore not specified. Defaulting to '{datastore}' in auto-approve mode.",
568
+ style="yellow",
569
+ )
570
+ else:
571
+ datastore = prompt_datastore_selection(final_agent)
572
+ if debug:
573
+ logging.debug(
574
+ f"Data ingestion required by agent: {include_data_ingestion}"
575
+ )
576
+ logging.debug(f"Selected datastore type: {datastore}")
577
+
578
+ # Deployment target selection
579
+ # For remote templates, we need to use the base template name for deployment target selection
580
+ deployment_agent_name = final_agent
581
+ remote_config = None
582
+ if template_source_path:
583
+ # Use the base template name from remote config for deployment target selection
584
+ deployment_agent_name = get_base_template_name(config)
585
+ remote_config = config
586
+
587
+ final_deployment = deployment_target
588
+ if not final_deployment:
589
+ available_targets = get_deployment_targets(
590
+ deployment_agent_name, remote_config=remote_config
591
+ )
592
+ if auto_approve:
593
+ if not available_targets:
594
+ raise click.ClickException(
595
+ f"Error: No deployment targets available for agent '{deployment_agent_name}'."
596
+ )
597
+ final_deployment = available_targets[0]
598
+ console.print(
599
+ f"Info: --deployment-target not specified. Defaulting to '{final_deployment}' in auto-approve mode.",
600
+ style="yellow",
601
+ )
602
+ else:
603
+ final_deployment = prompt_deployment_target(
604
+ deployment_agent_name, remote_config=remote_config
605
+ )
606
+ if debug:
607
+ logging.debug(f"Selected deployment target: {final_deployment}")
608
+
609
+ # Session type validation and selection (only for agents that require session management)
610
+ final_session_type = session_type
611
+
612
+ # Check if agent requires session management
613
+ requires_session = config.get("settings", {}).get("requires_session", False)
614
+
615
+ if requires_session:
616
+ if final_deployment == "agent_engine" and session_type:
617
+ console.print(
618
+ "Error: --session-type cannot be used with agent_engine deployment target. "
619
+ "Agent Engine handles session management internally.",
620
+ style="bold red",
621
+ )
622
+ return
623
+
624
+ if (
625
+ final_deployment is not None
626
+ and final_deployment in ("cloud_run")
627
+ and not session_type
628
+ ):
629
+ if auto_approve:
630
+ final_session_type = "in_memory"
631
+ console.print(
632
+ "Info: --session-type not specified. Defaulting to 'in_memory' in auto-approve mode.",
633
+ style="yellow",
634
+ )
635
+ else:
636
+ final_session_type = prompt_session_type_selection()
637
+ else:
638
+ # Agents that don't require session management always use in-memory sessions
639
+ final_session_type = "in_memory"
640
+ if session_type and session_type != "in_memory":
641
+ console.print(
642
+ "Warning: Session type options are only available for agents that require session management. "
643
+ "Using in-memory sessions for this agent.",
644
+ style="yellow",
645
+ )
646
+
647
+ if debug and final_session_type:
648
+ logging.debug(f"Selected session type: {final_session_type}")
649
+
650
+ # CI/CD runner selection
651
+ final_cicd_runner = cicd_runner
652
+ if not final_cicd_runner:
653
+ if auto_approve or agent_garden:
654
+ final_cicd_runner = "google_cloud_build"
655
+ if not agent_garden:
656
+ console.print(
657
+ "Info: --cicd-runner not specified. Defaulting to 'google_cloud_build' in auto-approve mode.",
658
+ style="yellow",
659
+ )
660
+ else:
661
+ final_cicd_runner = prompt_cicd_runner_selection()
662
+ if debug:
663
+ logging.debug(f"Selected CI/CD runner: {final_cicd_runner}")
664
+
665
+ # Region confirmation (if not explicitly passed)
666
+ if (
667
+ not auto_approve
668
+ and ctx.get_parameter_source("region") != ParameterSource.COMMANDLINE
669
+ ):
670
+ # Show Agent Engine supported regions link if agent_garden flag is set
671
+ if agent_garden:
672
+ console.print(
673
+ "\n📍 [blue]Agent Engine Supported Regions:[/blue]\n"
674
+ " [cyan]https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/overview#supported-regions[/cyan]"
675
+ )
676
+ region = prompt_region_confirmation(region, agent_garden=agent_garden)
677
+ if debug:
678
+ logging.debug(f"Selected region: {region}")
679
+
680
+ # GCP Setup
681
+ logging.debug("Setting up GCP...")
682
+
683
+ creds_info = {}
684
+ if not skip_checks:
685
+ # Set up GCP environment
686
+ try:
687
+ creds_info = setup_gcp_environment(
688
+ auto_approve=auto_approve,
689
+ skip_checks=skip_checks,
690
+ region=region,
691
+ debug=debug,
692
+ agent_garden=agent_garden,
693
+ )
694
+ except Exception as e:
695
+ if debug:
696
+ logging.warning(f"GCP environment setup failed: {e}")
697
+ console.print(f"> ⚠️ {e}", style="bold yellow")
698
+ console.print(
699
+ "> Continuing with template processing...", style="yellow"
700
+ )
701
+
702
+ # Process template
703
+ if not template_source_path:
704
+ template_path = get_template_path(final_agent, debug=debug)
705
+ # template_path is already set above for remote templates
706
+
707
+ if debug:
708
+ logging.debug(f"Template path: {template_path}")
709
+ logging.debug(f"Processing template for project: {project_name}")
710
+
711
+ # Create output directory if it doesn't exist
712
+ if not destination_dir.exists():
713
+ destination_dir.mkdir(parents=True)
714
+
715
+ if debug:
716
+ logging.debug(f"Output directory: {destination_dir}")
717
+
718
+ # Construct CLI overrides for template processing
719
+ final_cli_overrides = cli_overrides or {}
720
+ if agent_directory:
721
+ if "settings" not in final_cli_overrides:
722
+ final_cli_overrides["settings"] = {}
723
+ final_cli_overrides["settings"]["agent_directory"] = agent_directory
724
+
725
+ try:
726
+ # Process template (handles both local and remote templates)
727
+ process_template(
728
+ agent_name=final_agent,
729
+ template_dir=template_path,
730
+ project_name=project_name,
731
+ deployment_target=final_deployment,
732
+ cicd_runner=final_cicd_runner,
733
+ include_data_ingestion=include_data_ingestion,
734
+ datastore=datastore,
735
+ session_type=final_session_type,
736
+ output_dir=destination_dir,
737
+ remote_template_path=template_source_path,
738
+ remote_config=config,
739
+ in_folder=in_folder,
740
+ cli_overrides=final_cli_overrides,
741
+ agent_garden=agent_garden,
742
+ remote_spec=remote_spec,
743
+ )
744
+
745
+ # Replace region in all files if a different region was specified
746
+ if region != "us-central1":
747
+ replace_region_in_files(project_path, region, debug=debug)
748
+
749
+ # Handle base template dependencies if override was used
750
+ if base_template and template_source_path and remote_config:
751
+ # Load base template config to get extra_dependencies
752
+ base_template_path = get_template_path(base_template, debug=debug)
753
+ base_config = load_template_config(base_template_path)
754
+ base_deps = base_config.get("settings", {}).get(
755
+ "extra_dependencies", []
756
+ )
757
+
758
+ if base_deps:
759
+ # Call interactive dependency addition
760
+ add_base_template_dependencies_interactively(
761
+ project_path,
762
+ base_deps,
763
+ base_template,
764
+ auto_approve=auto_approve,
765
+ )
766
+ finally:
767
+ # Clean up the temporary directory if one was created
768
+ if temp_dir_to_clean:
769
+ try:
770
+ shutil.rmtree(temp_dir_to_clean)
771
+ logging.debug(
772
+ f"Successfully cleaned up temporary directory: {temp_dir_to_clean}"
773
+ )
774
+ except OSError as e:
775
+ logging.warning(
776
+ f"Failed to clean up temporary directory {temp_dir_to_clean}: {e}"
777
+ )
778
+
779
+ if not in_folder:
780
+ project_path = destination_dir / project_name
781
+ cd_path = project_path if output_dir else project_name
782
+ else:
783
+ project_path = destination_dir
784
+ cd_path = "."
785
+
786
+ if include_data_ingestion:
787
+ project_id = creds_info.get("project", "")
788
+ console.print(
789
+ f"\n[bold white]===== DATA INGESTION SETUP =====[/bold white]\n"
790
+ f"This agent uses a datastore for grounded responses.\n"
791
+ f"The agent will work without data, but for optimal results:\n"
792
+ f"1. Set up dev environment:\n"
793
+ f" [white italic]export PROJECT_ID={project_id} && cd {cd_path} && make setup-dev-env[/white italic]\n\n"
794
+ f" See deployment/README.md for more info\n"
795
+ f"2. Run the data ingestion pipeline:\n"
796
+ f" [white italic]export PROJECT_ID={project_id} && cd {cd_path} && make data-ingestion[/white italic]\n\n"
797
+ f" See data_ingestion/README.md for more info\n"
798
+ f"[bold white]=================================[/bold white]\n"
799
+ )
800
+ console.print("\n> 👍 Done. Execute the following command to get started:")
801
+
802
+ console.print("\n> Success! Your agent project is ready.")
803
+ console.print(
804
+ f"\n📖 Project README: [cyan]cat {cd_path}/README.md[/]"
805
+ "\n Online Development Guide: [cyan][link=https://goo.gle/asp-dev]https://goo.gle/asp-dev[/link][/cyan]"
806
+ )
807
+ # Determine the correct path to display based on whether output_dir was specified
808
+ console.print("\n🚀 To get started, run the following command:")
809
+
810
+ # Check if the agent has a 'dev' command in its settings
811
+ interactive_command = config.get("settings", {}).get(
812
+ "interactive_command", "playground"
813
+ )
814
+ console.print(
815
+ f" [bold bright_green]cd {cd_path} && make install && make {interactive_command}[/]"
816
+ )
817
+ except Exception:
818
+ if debug:
819
+ logging.exception(
820
+ "An error occurred:"
821
+ ) # This will print the full stack trace
822
+ raise
823
+
824
+
825
+ def prompt_region_confirmation(
826
+ default_region: str = "us-central1", agent_garden: bool = False
827
+ ) -> str:
828
+ """Prompt user to confirm or change the default region."""
829
+ new_region = Prompt.ask(
830
+ "\nEnter desired GCP region (Gemini uses global endpoint by default)",
831
+ default=default_region,
832
+ show_default=True,
833
+ )
834
+
835
+ return new_region if new_region else default_region
836
+
837
+
838
+ def display_agent_selection(deployment_target: str | None = None) -> str:
839
+ """Display available agents and prompt for selection."""
840
+ agents = get_available_agents(deployment_target=deployment_target)
841
+
842
+ if not agents:
843
+ if deployment_target:
844
+ raise click.ClickException(
845
+ f"No agents available for deployment target '{deployment_target}'"
846
+ )
847
+ raise click.ClickException("No valid agents found")
848
+
849
+ console.print("\n> Please select a agent to get started:")
850
+ for num, agent in agents.items():
851
+ console.print(
852
+ f"{num}. [bold]{agent['name']}[/] - [dim]{agent['description']}[/]"
853
+ )
854
+
855
+ # Add special option for adk-samples
856
+ adk_samples_option = len(agents) + 1
857
+ console.print(
858
+ f"{adk_samples_option}. [bold]Browse agents from [link=https://github.com/google/adk-samples]google/adk-samples[/link][/] - [dim]Discover additional samples[/]"
859
+ )
860
+
861
+ choice = IntPrompt.ask(
862
+ "\nEnter the number of your template choice", default=1, show_default=True
863
+ )
864
+
865
+ if choice == adk_samples_option:
866
+ return display_adk_samples_selection()
867
+ elif choice in agents:
868
+ return agents[choice]["name"]
869
+ else:
870
+ raise ValueError(f"Invalid agent selection: {choice}")
871
+
872
+
873
+ def display_adk_samples_selection() -> str:
874
+ """Display adk-samples agents and prompt for selection."""
875
+
876
+ from ..utils.remote_template import fetch_remote_template, parse_agent_spec
877
+
878
+ console.print("\n> Fetching agents from [bold blue]google/adk-samples[/]...")
879
+
880
+ try:
881
+ # Parse the adk-samples repository
882
+ spec = parse_agent_spec("https://github.com/google/adk-samples")
883
+ if not spec:
884
+ raise RuntimeError("Failed to parse adk-samples repository")
885
+
886
+ # Fetch the repository
887
+ repo_path, _ = fetch_remote_template(spec)
888
+
889
+ # Use shared ADK discovery function
890
+ from ..utils.remote_template import discover_adk_agents
891
+
892
+ adk_agents = discover_adk_agents(repo_path)
893
+
894
+ if not adk_agents:
895
+ console.print("No agents found in adk-samples repository", style="yellow")
896
+ # Fall back to local agents
897
+ return display_agent_selection()
898
+
899
+ console.print("\n> Available agents from [bold blue]google/adk-samples[/]:")
900
+
901
+ # Show explanation for inferred agents at the top
902
+ from ..utils.remote_template import display_adk_caveat_if_needed
903
+
904
+ display_adk_caveat_if_needed(adk_agents)
905
+
906
+ for num, agent in adk_agents.items():
907
+ name_with_indicator = agent["name"]
908
+ if not agent.get("has_explicit_config", True):
909
+ name_with_indicator += " *"
910
+
911
+ console.print(
912
+ f"{num}. [bold]{name_with_indicator}[/] - [dim]{agent['description']}[/]"
913
+ )
914
+
915
+ # Add option to go back to local agents
916
+ back_option = len(adk_agents) + 1
917
+ console.print(
918
+ f"{back_option}. [bold]← Back to built-in agents[/] - [dim]Return to local agent selection[/]"
919
+ )
920
+
921
+ choice = IntPrompt.ask(
922
+ "\nEnter the number of your choice", default=1, show_default=True
923
+ )
924
+
925
+ if choice == back_option:
926
+ return display_agent_selection()
927
+ elif choice in adk_agents:
928
+ # Return the adk@ spec for the selected agent
929
+ selected_agent = adk_agents[choice]
930
+ console.print(
931
+ f"\n> Selected: [bold]{selected_agent['name']}[/] from adk-samples"
932
+ )
933
+ return selected_agent["spec"]
934
+ else:
935
+ raise ValueError(f"Invalid agent selection: {choice}")
936
+
937
+ except Exception as e:
938
+ console.print(f"Error fetching adk-samples agents: {e}", style="bold red")
939
+ console.print("Falling back to built-in agents...", style="yellow")
940
+ return display_agent_selection()
941
+
942
+
943
+ def set_gcp_project(project_id: str, set_quota_project: bool = True) -> None:
944
+ """Set the GCP project and optionally the application default quota project.
945
+
946
+ Args:
947
+ project_id: The GCP project ID to set.
948
+ set_quota_project: Whether to set the application default quota project.
949
+ """
950
+ try:
951
+ subprocess.run(
952
+ ["gcloud", "config", "set", "project", project_id],
953
+ check=True,
954
+ capture_output=True,
955
+ text=True,
956
+ )
957
+ except subprocess.CalledProcessError as e:
958
+ console.print(f"\n> Error setting project to {project_id}:")
959
+ console.print(e.stderr)
960
+ raise
961
+
962
+ if set_quota_project:
963
+ try:
964
+ subprocess.run(
965
+ [
966
+ "gcloud",
967
+ "auth",
968
+ "application-default",
969
+ "set-quota-project",
970
+ project_id,
971
+ ],
972
+ check=True,
973
+ capture_output=True,
974
+ text=True,
975
+ )
976
+ except subprocess.CalledProcessError as e:
977
+ logging.debug(f"Setting quota project failed: {e.stderr}")
978
+
979
+ console.print(f"> Successfully configured project: {project_id}")
980
+
981
+
982
+ def setup_gcp_environment(
983
+ auto_approve: bool,
984
+ skip_checks: bool,
985
+ region: str,
986
+ debug: bool,
987
+ agent_garden: bool = False,
988
+ ) -> dict:
989
+ """Set up the GCP environment with proper credentials and project.
990
+
991
+ Args:
992
+ auto_approve: Whether to skip confirmation prompts
993
+ skip_checks: Whether to skip verification checks
994
+ region: GCP region for deployment
995
+ debug: Whether debug logging is enabled
996
+ agent_garden: Whether this deployment is from Agent Garden
997
+
998
+ Returns:
999
+ Dictionary with credential information
1000
+ """
1001
+ # Skip all verification if requested
1002
+ if skip_checks:
1003
+ if debug:
1004
+ logging.debug("Skipping verification checks due to --skip-checks flag")
1005
+ console.print("> Skipping verification checks", style="yellow")
1006
+ return {"project": "unknown"}
1007
+
1008
+ # Verify current GCP credentials
1009
+ if debug:
1010
+ logging.debug("Verifying GCP credentials...")
1011
+ creds_info = verify_credentials()
1012
+ # Handle credential verification and project selection
1013
+ # Skip interactive prompts if auto_approve or agent_garden is set
1014
+ if not auto_approve and not agent_garden:
1015
+ creds_info = _handle_credential_verification(creds_info)
1016
+ # If user chose to skip verification, don't test Vertex AI connection
1017
+ if creds_info.get("skip_vertex_test", False):
1018
+ console.print("> Skipping Vertex AI connection test", style="yellow")
1019
+ else:
1020
+ # Test Vertex AI connection
1021
+ _test_vertex_ai_connection(
1022
+ creds_info["project"], region, agent_garden=agent_garden
1023
+ )
1024
+ else:
1025
+ # Even with auto_approve or agent_garden, we should still set the GCP project
1026
+ set_gcp_project(creds_info["project"], set_quota_project=True)
1027
+ # Test Vertex AI connection
1028
+ _test_vertex_ai_connection(
1029
+ creds_info["project"], region, agent_garden=agent_garden
1030
+ )
1031
+
1032
+ return creds_info
1033
+
1034
+
1035
+ def _handle_credential_verification(creds_info: dict) -> dict:
1036
+ """Handle verification of credentials and project selection.
1037
+
1038
+ Args:
1039
+ creds_info: Current credential information
1040
+
1041
+ Returns:
1042
+ Updated credential information
1043
+ """
1044
+ # Check if running in Cloud Shell
1045
+ if os.environ.get("CLOUD_SHELL") == "true":
1046
+ if creds_info["project"] == "":
1047
+ console.print(
1048
+ "> It looks like you are running in Cloud Shell.", style="bold blue"
1049
+ )
1050
+ console.print(
1051
+ "> You need to set up a project ID to continue, but you haven't setup a project yet.",
1052
+ style="bold blue",
1053
+ )
1054
+ new_project = Prompt.ask("\n> Enter a project ID", default=None)
1055
+ while not new_project:
1056
+ console.print(
1057
+ "> Project ID cannot be empty. Please try again.", style="bold red"
1058
+ )
1059
+ new_project = Prompt.ask("\n> Enter a project ID", default=None)
1060
+ creds_info["project"] = new_project
1061
+ set_gcp_project(creds_info["project"], set_quota_project=False)
1062
+ return creds_info
1063
+
1064
+ # Ask user if current credentials are correct or if they want to skip
1065
+ console.print(f"\n> You are logged in with account: '{creds_info['account']}'")
1066
+ console.print(f"> You are using project: '{creds_info['project']}'")
1067
+
1068
+ choices = ["Y", "skip", "edit"]
1069
+ response = Prompt.ask(
1070
+ "> Do you want to continue? (The CLI will check if Vertex AI is enabled in this project)",
1071
+ choices=choices,
1072
+ default="Y",
1073
+ ).lower()
1074
+
1075
+ if response == "skip":
1076
+ console.print("> Skipping credential verification", style="yellow")
1077
+ creds_info["skip_vertex_test"] = True
1078
+ return creds_info
1079
+
1080
+ change_creds = response == "edit"
1081
+
1082
+ if change_creds:
1083
+ # Handle credential change
1084
+ console.print("\n> Initiating new login...")
1085
+ subprocess.run(["gcloud", "auth", "login", "--update-adc"], check=True)
1086
+ console.print("> Login successful. Verifying new credentials...")
1087
+
1088
+ # Re-verify credentials after login
1089
+ creds_info = verify_credentials()
1090
+
1091
+ # Prompt for project change
1092
+ console.print(
1093
+ f"\n> You are now logged in with account: '{creds_info['account']}'."
1094
+ )
1095
+ console.print(f"> Current project is: '{creds_info['project']}'.")
1096
+ choices = ["y", "skip", "edit"]
1097
+ response = Prompt.ask(
1098
+ "> Do you want to continue? (The CLI will verify Vertex AI access in this project)",
1099
+ choices=choices,
1100
+ default="y",
1101
+ ).lower()
1102
+
1103
+ if response == "skip":
1104
+ console.print("> Skipping project verification", style="yellow")
1105
+ creds_info["skip_vertex_test"] = True
1106
+ return creds_info
1107
+
1108
+ if response == "edit":
1109
+ # Prompt for new project ID
1110
+ new_project = Prompt.ask("\n> Enter the new project ID")
1111
+ creds_info["project"] = new_project
1112
+
1113
+ set_gcp_project(creds_info["project"], set_quota_project=True)
1114
+ return creds_info
1115
+
1116
+
1117
+ def _test_vertex_ai_connection(
1118
+ project_id: str, region: str, auto_approve: bool = False, agent_garden: bool = False
1119
+ ) -> None:
1120
+ """Test connection to Vertex AI.
1121
+
1122
+ Args:
1123
+ project_id: GCP project ID
1124
+ region: GCP region for deployment
1125
+ auto_approve: Whether to auto-approve API enablement
1126
+ agent_garden: Whether this deployment is from Agent Garden
1127
+ """
1128
+ console.print("> Testing GCP and Vertex AI Connection...")
1129
+ try:
1130
+ context = "agent-garden" if agent_garden else None
1131
+ verify_vertex_connection(
1132
+ project_id=project_id,
1133
+ location=region,
1134
+ auto_approve=auto_approve,
1135
+ context=context,
1136
+ )
1137
+ console.print(
1138
+ f"> ✓ Successfully verified connection to Vertex AI in project {project_id}"
1139
+ )
1140
+ except Exception as e:
1141
+ console.print(
1142
+ f"> ✗ Failed to connect to Vertex AI: {e!s}\n"
1143
+ f"> Please check your authentication settings and permissions. "
1144
+ f"Visit https://cloud.google.com/vertex-ai/docs/authentication for help.",
1145
+ style="bold red",
1146
+ )
1147
+ raise
1148
+
1149
+
1150
+ def replace_region_in_files(
1151
+ project_path: pathlib.Path, new_region: str, debug: bool = False
1152
+ ) -> None:
1153
+ """Replace all instances of 'us-central1' with the specified region in project files.
1154
+ Also handles vertex_ai_search region mapping.
1155
+
1156
+ Args:
1157
+ project_path: Path to the project directory
1158
+ new_region: The new region to use
1159
+ debug: Whether to enable debug logging
1160
+ """
1161
+ if debug:
1162
+ logging.debug(
1163
+ f"Replacing region 'us-central1' with '{new_region}' in {project_path}"
1164
+ )
1165
+
1166
+ # Define allowed file extensions
1167
+ allowed_extensions = {
1168
+ ".md",
1169
+ ".py",
1170
+ ".tfvars",
1171
+ ".yaml",
1172
+ ".tf",
1173
+ ".yml",
1174
+ "Makefile",
1175
+ "makefile",
1176
+ }
1177
+
1178
+ # Skip directories that shouldn't be modified
1179
+ skip_dirs = {".git", "__pycache__", "venv", ".venv", "node_modules"}
1180
+
1181
+ # Determine data_store_region region value
1182
+ if new_region.startswith("us"):
1183
+ data_store_region = "us"
1184
+ elif new_region.startswith("europe"):
1185
+ data_store_region = "eu"
1186
+ else:
1187
+ data_store_region = "global"
1188
+
1189
+ for file_path in project_path.rglob("*"):
1190
+ # Skip directories and files with unwanted extensions
1191
+ if (
1192
+ file_path.is_dir()
1193
+ or any(skip_dir in file_path.parts for skip_dir in skip_dirs)
1194
+ or (
1195
+ file_path.suffix not in allowed_extensions
1196
+ and file_path.name not in allowed_extensions
1197
+ )
1198
+ ):
1199
+ continue
1200
+
1201
+ try:
1202
+ content = file_path.read_text()
1203
+ modified = False
1204
+
1205
+ # Replace standard region references
1206
+ if "us-central1" in content:
1207
+ if debug:
1208
+ logging.debug(f"Replacing region in {file_path}")
1209
+ content = content.replace("us-central1", new_region)
1210
+ modified = True
1211
+
1212
+ # Replace data_store_region region if present (all variants)
1213
+ if 'data_store_region = "us"' in content:
1214
+ if debug:
1215
+ logging.debug(f"Replacing vertex_ai_search region in {file_path}")
1216
+ content = content.replace(
1217
+ 'data_store_region = "us"',
1218
+ f'data_store_region = "{data_store_region}"',
1219
+ )
1220
+ modified = True
1221
+ elif 'data_store_region="us"' in content:
1222
+ if debug:
1223
+ logging.debug(f"Replacing data_store_region in {file_path}")
1224
+ content = content.replace(
1225
+ 'data_store_region="us"', f'data_store_region="{data_store_region}"'
1226
+ )
1227
+ modified = True
1228
+ elif 'data-store-region="us"' in content:
1229
+ if debug:
1230
+ logging.debug(f"Replacing data-store-region in {file_path}")
1231
+ content = content.replace(
1232
+ 'data-store-region="us"', f'data-store-region="{data_store_region}"'
1233
+ )
1234
+ modified = True
1235
+ elif "_DATA_STORE_REGION: us" in content:
1236
+ if debug:
1237
+ logging.debug(f"Replacing _DATA_STORE_REGION in {file_path}")
1238
+ content = content.replace(
1239
+ "_DATA_STORE_REGION: us", f"_DATA_STORE_REGION: {data_store_region}"
1240
+ )
1241
+ modified = True
1242
+ elif '"DATA_STORE_REGION", "us"' in content:
1243
+ if debug:
1244
+ logging.debug(f"Replacing DATA_STORE_REGION in {file_path}")
1245
+ content = content.replace(
1246
+ '"DATA_STORE_REGION", "us"',
1247
+ f'"DATA_STORE_REGION", "{data_store_region}"',
1248
+ )
1249
+ modified = True
1250
+
1251
+ if modified:
1252
+ file_path.write_text(content)
1253
+
1254
+ except UnicodeDecodeError:
1255
+ # Skip files that can't be read as text
1256
+ continue