agent-starter-pack 0.9.2__py3-none-any.whl → 0.10.1__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 (57) hide show
  1. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/METADATA +2 -2
  2. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/RECORD +54 -52
  3. agents/adk_base/.template/templateconfig.yaml +1 -1
  4. agents/adk_gemini_fullstack/.template/templateconfig.yaml +2 -1
  5. agents/agentic_rag/.template/templateconfig.yaml +1 -1
  6. agents/agentic_rag/README.md +1 -1
  7. agents/live_api/tests/integration/test_server_e2e.py +7 -1
  8. agents/live_api/tests/unit/test_server.py +2 -1
  9. llm.txt +3 -2
  10. src/base_template/Makefile +4 -3
  11. src/base_template/README.md +7 -2
  12. src/base_template/deployment/README.md +6 -121
  13. src/base_template/deployment/terraform/github.tf +284 -0
  14. src/base_template/deployment/terraform/providers.tf +5 -0
  15. src/base_template/deployment/terraform/variables.tf +40 -1
  16. src/base_template/deployment/terraform/vars/env.tfvars +7 -1
  17. src/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
  18. src/base_template/deployment/terraform/{build_triggers.tf → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} } +33 -18
  19. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +114 -0
  20. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
  21. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +170 -0
  22. src/base_template/{deployment/cd/deploy-to-prod.yaml → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml } +7 -7
  23. src/base_template/{deployment/cd/staging.yaml → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml } +7 -7
  24. src/cli/commands/create.py +149 -4
  25. src/cli/commands/list.py +1 -1
  26. src/cli/commands/setup_cicd.py +293 -299
  27. src/cli/utils/cicd.py +19 -7
  28. src/cli/utils/remote_template.py +4 -4
  29. src/cli/utils/template.py +67 -19
  30. src/deployment_targets/cloud_run/app/server.py +19 -0
  31. src/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -3
  32. src/deployment_targets/cloud_run/deployment/terraform/service.tf +4 -3
  33. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +35 -0
  34. src/deployment_targets/cloud_run/tests/load_test/README.md +1 -1
  35. src/frontends/live_api_react/frontend/package-lock.json +19 -16
  36. src/frontends/streamlit/frontend/side_bar.py +1 -1
  37. src/frontends/streamlit/frontend/utils/chat_utils.py +1 -1
  38. src/frontends/streamlit/frontend/utils/local_chat_history.py +2 -2
  39. src/resources/docs/adk-cheatsheet.md +1 -1
  40. src/resources/locks/uv-adk_base-agent_engine.lock +164 -131
  41. src/resources/locks/uv-adk_base-cloud_run.lock +177 -144
  42. src/resources/locks/uv-adk_gemini_fullstack-agent_engine.lock +164 -131
  43. src/resources/locks/uv-adk_gemini_fullstack-cloud_run.lock +177 -144
  44. src/resources/locks/uv-agentic_rag-agent_engine.lock +223 -190
  45. src/resources/locks/uv-agentic_rag-cloud_run.lock +251 -218
  46. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +315 -485
  47. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +358 -531
  48. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +281 -249
  49. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +323 -290
  50. src/resources/locks/uv-live_api-cloud_run.lock +350 -327
  51. src/resources/setup_cicd/cicd_variables.tf +0 -41
  52. src/resources/setup_cicd/github.tf +0 -87
  53. src/resources/setup_cicd/providers.tf +0 -39
  54. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/WHEEL +0 -0
  55. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/entry_points.txt +0 -0
  56. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/licenses/LICENSE +0 -0
  57. /src/base_template/{deployment/ci → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}}/pr_checks.yaml +0 -0
@@ -0,0 +1,65 @@
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
+ name: PR Checks
16
+
17
+ on:
18
+ pull_request:
19
+ branches:
20
+ - main
21
+ paths:
22
+ - 'app/**'
23
+ - 'data_ingestion/**'
24
+ - 'tests/**'
25
+ - 'deployment/**'
26
+ - 'uv.lock'
27
+ {%- if cookiecutter.data_ingestion %}
28
+ - 'data_ingestion/**'
29
+ {%- endif %}
30
+
31
+ jobs:
32
+ test:
33
+ runs-on: ubuntu-latest
34
+ permissions:
35
+ contents: 'read'
36
+ id-token: 'write'
37
+ steps:
38
+ - name: Checkout code
39
+ uses: actions/checkout@v4
40
+
41
+ - id: 'auth'
42
+ name: 'Authenticate to Google Cloud'
43
+ uses: 'google-github-actions/auth@v2'
44
+ with:
45
+ workload_identity_provider: 'projects/{% raw %}${{ vars.GCP_PROJECT_NUMBER }}{% endraw %}/locations/global/workloadIdentityPools/{% raw %}${{ secrets.WIF_POOL_ID }}{% endraw %}/providers/{% raw %}${{ secrets.WIF_PROVIDER_ID }}{% endraw %}'
46
+ service_account: '{% raw %}${{ secrets.GCP_SERVICE_ACCOUNT }}{% endraw %}'
47
+ create_credentials_file: true
48
+ project_id: {% raw %}${{ vars.CICD_PROJECT_ID }}{% endraw %}
49
+
50
+
51
+ - name: Set up Python 3.11
52
+ uses: actions/setup-python@v4
53
+ with:
54
+ python-version: '3.11'
55
+
56
+ - name: Install uv and dependencies
57
+ run: |
58
+ pip install uv==0.6.12
59
+ uv sync --locked
60
+
61
+ - name: Run unit tests
62
+ run: uv run pytest tests/unit
63
+
64
+ - name: Run integration tests
65
+ run: uv run pytest tests/integration
@@ -0,0 +1,170 @@
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
+ name: Deploy to Staging
16
+
17
+ on:
18
+ push:
19
+ branches:
20
+ - main
21
+ paths:
22
+ - 'app/**'
23
+ - 'data_ingestion/**'
24
+ - 'tests/**'
25
+ - 'deployment/**'
26
+ - 'uv.lock'
27
+
28
+ jobs:
29
+ deploy_and_test_staging:
30
+ runs-on: ubuntu-latest
31
+ permissions:
32
+ contents: 'read'
33
+ id-token: 'write'
34
+
35
+ steps:
36
+ - name: Checkout code
37
+ uses: actions/checkout@v4
38
+
39
+ - name: Set up Python 3.11
40
+ uses: actions/setup-python@v4
41
+ with:
42
+ python-version: '3.11'
43
+
44
+ - id: 'auth'
45
+ name: 'Authenticate to Google Cloud'
46
+ uses: 'google-github-actions/auth@v2'
47
+ with:
48
+ workload_identity_provider: 'projects/{% raw %}${{ vars.GCP_PROJECT_NUMBER }}{% endraw %}/locations/global/workloadIdentityPools/{% raw %}${{ secrets.WIF_POOL_ID }}{% endraw %}/providers/{% raw %}${{ secrets.WIF_PROVIDER_ID }}{% endraw %}'
49
+ service_account: '{% raw %}${{ secrets.GCP_SERVICE_ACCOUNT }}{% endraw %}'
50
+ create_credentials_file: true
51
+ project_id: {% raw %}${{ vars.CICD_PROJECT_ID }}{% endraw %}
52
+ {%- if cookiecutter.deployment_target == 'cloud_run' %}
53
+
54
+ - name: Set up Cloud SDK
55
+ uses: 'google-github-actions/setup-gcloud@v2'
56
+ {%- endif %}
57
+ {%- if cookiecutter.data_ingestion %}
58
+
59
+ - name: Deploy data ingestion pipeline (Staging)
60
+ run: |
61
+ cd data_ingestion && pip install uv==0.6.12 && cd data_ingestion_pipeline && \
62
+ uv sync --locked && uv run python submit_pipeline.py
63
+ env:
64
+ PIPELINE_ROOT: {% raw %}${{ vars.PIPELINE_GCS_ROOT_STAGING }}{% endraw %}
65
+ REGION: {% raw %}${{ vars.REGION }}{% endraw %}
66
+ {%- if cookiecutter.datastore_type == "vertex_ai_search" %}
67
+ DATA_STORE_REGION: {% raw %}${{ vars.DATA_STORE_REGION }}{% endraw %}
68
+ DATA_STORE_ID: {% raw %}${{ vars.DATA_STORE_ID_STAGING }}{% endraw %}
69
+ {%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %}
70
+ VECTOR_SEARCH_INDEX: {% raw %}${{ vars.VECTOR_SEARCH_INDEX_STAGING }}{% endraw %}
71
+ VECTOR_SEARCH_INDEX_ENDPOINT: {% raw %}${{ vars.VECTOR_SEARCH_INDEX_ENDPOINT_STAGING }}{% endraw %}
72
+ VECTOR_SEARCH_BUCKET: {% raw %}${{ vars.VECTOR_SEARCH_BUCKET_STAGING }}{% endraw %}
73
+ {%- endif %}
74
+ PROJECT_ID: {% raw %}${{ vars.STAGING_PROJECT_ID }}{% endraw %}
75
+ SERVICE_ACCOUNT: {% raw %}${{ vars.PIPELINE_SA_EMAIL_STAGING }}{% endraw %}
76
+ PIPELINE_NAME: {% raw %}${{ vars.PIPELINE_NAME }}{% endraw %}
77
+ {%- endif %}
78
+
79
+ {%- if cookiecutter.deployment_target == 'cloud_run' %}
80
+
81
+ - name: Configure Docker for Artifact Registry
82
+ run: |
83
+ gcloud auth configure-docker {% raw %}${{ vars.REGION }}{% endraw %}-docker.pkg.dev --quiet
84
+
85
+ - name: Build and Push Docker Image
86
+ run: |
87
+ docker build -t {% raw %}${{ vars.REGION }}{% endraw %}-docker.pkg.dev/{% raw %}${{ vars.CICD_PROJECT_ID }}{% endraw %}/{% raw %}${{ vars.ARTIFACT_REGISTRY_REPO_NAME }}{% endraw %}/{% raw %}${{ vars.CONTAINER_NAME }}{% endraw %} \
88
+ --build-arg COMMIT_SHA={% raw %}${{ github.sha }}{% endraw %} .
89
+ docker push {% raw %}${{ vars.REGION }}{% endraw %}-docker.pkg.dev/{% raw %}${{ vars.CICD_PROJECT_ID }}{% endraw %}/{% raw %}${{ vars.ARTIFACT_REGISTRY_REPO_NAME }}{% endraw %}/{% raw %}${{ vars.CONTAINER_NAME }}{% endraw %}
90
+
91
+ - name: Deploy to Staging (Cloud Run)
92
+ run: |
93
+ gcloud run deploy {{cookiecutter.project_name}} \
94
+ --image {% raw %}${{ vars.REGION }}{% endraw %}-docker.pkg.dev/{% raw %}${{ vars.CICD_PROJECT_ID }}{% endraw %}/{% raw %}${{ vars.ARTIFACT_REGISTRY_REPO_NAME }}{% endraw %}/{% raw %}${{ vars.CONTAINER_NAME }}{% endraw %} \
95
+ --region {% raw %}${{ vars.REGION }}{% endraw %} \
96
+ --project {% raw %}${{ vars.STAGING_PROJECT_ID }}{% endraw %}
97
+
98
+ - name: Fetch Staging Service URL
99
+ id: fetch-url
100
+ run: |
101
+ _STAGING_URL=$(gcloud run services describe {{cookiecutter.project_name}} \
102
+ --region {% raw %}${{ vars.REGION }}{% endraw %} --project {% raw %}${{ vars.STAGING_PROJECT_ID }}{% endraw %} --format="value(status.url)")
103
+ echo "_staging_url=${_STAGING_URL}" >> $GITHUB_OUTPUT
104
+
105
+ - name: Fetch ID Token
106
+ id: fetch-token
107
+ run: |
108
+ _ID_TOKEN=$(gcloud auth print-identity-token --impersonate-service-account={% raw %}${{ secrets.GCP_SERVICE_ACCOUNT }}{% endraw %} -q)
109
+ echo "::add-mask::${_ID_TOKEN}"
110
+ echo "_id_token=${_ID_TOKEN}" >> $GITHUB_OUTPUT
111
+
112
+ {%- elif cookiecutter.deployment_target == 'agent_engine' %}
113
+
114
+ - name: Install uv and dependencies
115
+ run: |
116
+ pip install uv==0.6.12
117
+ uv sync --locked
118
+
119
+ - name: Deploy to Staging (Agent Engine)
120
+ run: |
121
+ uv export --no-hashes --no-sources --no-header --no-dev --no-emit-project --no-annotate --locked > .requirements.txt
122
+ uv run app/agent_engine_app.py \
123
+ --project {% raw %}${{ vars.STAGING_PROJECT_ID }}{% endraw %} \
124
+ --location {% raw %}${{ vars.REGION }}{% endraw %} \
125
+ --set-env-vars="COMMIT_SHA={% raw %}${{ github.sha }}{% endraw %}{%- if cookiecutter.data_ingestion %}{%- if cookiecutter.datastore_type == "vertex_ai_search" %},DATA_STORE_ID={% raw %}${{ vars.DATA_STORE_ID_STAGING }}{% endraw %},DATA_STORE_REGION={% raw %}${{ vars.DATA_STORE_REGION }}{% endraw %}{%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %},VECTOR_SEARCH_INDEX={% raw %}${{ vars.VECTOR_SEARCH_INDEX_STAGING }}{% endraw %},VECTOR_SEARCH_INDEX_ENDPOINT={% raw %}${{ vars.VECTOR_SEARCH_INDEX_ENDPOINT_STAGING }}{% endraw %},VECTOR_SEARCH_BUCKET={% raw %}${{ vars.VECTOR_SEARCH_BUCKET_STAGING }}{% endraw %}{%- endif %}{%- endif %}"
126
+
127
+ - name: Fetch Auth Token
128
+ id: fetch-token
129
+ run: |
130
+ _AUTH_TOKEN=$(gcloud auth print-access-token -q)
131
+ echo "::add-mask::${_AUTH_TOKEN}"
132
+ echo "_auth_token=${_AUTH_TOKEN}" >> $GITHUB_OUTPUT
133
+ {%- endif %}
134
+
135
+ - name: Run load test
136
+ run: |
137
+ {%- if cookiecutter.deployment_target == 'cloud_run' %}
138
+ export _ID_TOKEN="{% raw %}${{ steps.fetch-token.outputs._id_token }}{% endraw %}"
139
+ export _STAGING_URL="{% raw %}${{ steps.fetch-url.outputs._staging_url }}{% endraw %}"
140
+ {%- elif cookiecutter.deployment_target == 'agent_engine' %}
141
+ export _AUTH_TOKEN="{% raw %}${{ steps.fetch-token.outputs._auth_token }}{% endraw %}"
142
+ {%- endif %}
143
+ pip install locust==2.31.1
144
+ locust -f tests/load_test/load_test.py \
145
+ --headless \
146
+ {%- if cookiecutter.deployment_target == 'cloud_run' %}
147
+ -H ${_STAGING_URL} \
148
+ -t 30s -u 10 -r 0.5 \
149
+ {%- elif cookiecutter.deployment_target == 'agent_engine' %}
150
+ -t 30s -u 2 -r 0.5 \
151
+ {%- endif %}
152
+ --csv=tests/load_test/.results/results \
153
+ --html=tests/load_test/.results/report.html
154
+
155
+ - name: Export Load Test Results to GCS
156
+ run: |
157
+ TIMESTAMP=$(date +%Y%m%d-%H%M%S)
158
+ gcloud storage cp --recursive tests/load_test/.results gs://{% raw %}${{ vars.BUCKET_NAME_LOAD_TEST_RESULTS }}{% endraw %}/results-${TIMESTAMP} --quiet
159
+ echo "_________________________________________________________________________"
160
+ echo "Load test results copied to gs://{% raw %}${{ vars.BUCKET_NAME_LOAD_TEST_RESULTS }}{% endraw %}/results-${TIMESTAMP}"
161
+ echo "HTTP link: https://console.cloud.google.com/storage/browser/{% raw %}${{ vars.BUCKET_NAME_LOAD_TEST_RESULTS }}{% endraw %}/results-${TIMESTAMP}"
162
+ echo "_________________________________________________________________________"
163
+
164
+ call_production_workflow:
165
+ needs: deploy_and_test_staging
166
+ uses: ./.github/workflows/deploy-to-prod.yaml
167
+ permissions:
168
+ contents: 'read'
169
+ id-token: 'write'
170
+ secrets: inherit
@@ -23,18 +23,18 @@ steps:
23
23
  cd data_ingestion && pip install uv==0.6.12 --user && cd data_ingestion_pipeline && \
24
24
  uv sync --locked && uv run python submit_pipeline.py
25
25
  env:
26
- - "PIPELINE_ROOT=${_PIPELINE_GCS_ROOT}"
26
+ - "PIPELINE_ROOT=${_PIPELINE_GCS_ROOT_PROD}"
27
27
  - "REGION=${_REGION}"
28
28
  {%- if cookiecutter.datastore_type == "vertex_ai_search" %}
29
29
  - "DATA_STORE_REGION=${_DATA_STORE_REGION}"
30
- - "DATA_STORE_ID=${_DATA_STORE_ID}"
30
+ - "DATA_STORE_ID=${_DATA_STORE_ID_PROD}"
31
31
  {%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %}
32
- - "VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX}"
33
- - "VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT}"
34
- - "VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET}"
32
+ - "VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX_PROD}"
33
+ - "VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT_PROD}"
34
+ - "VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET_PROD}"
35
35
  {%- endif %}
36
36
  - "PROJECT_ID=${_PROD_PROJECT_ID}"
37
- - "SERVICE_ACCOUNT=${_PIPELINE_SA_EMAIL}"
37
+ - "SERVICE_ACCOUNT=${_PIPELINE_SA_EMAIL_PROD}"
38
38
  - "PIPELINE_NAME=${_PIPELINE_NAME}"
39
39
  - "CRON_SCHEDULE=${_PIPELINE_CRON_SCHEDULE}"
40
40
  - "DISABLE_CACHING=TRUE"
@@ -77,7 +77,7 @@ steps:
77
77
  uv run app/agent_engine_app.py \
78
78
  --project ${_PROD_PROJECT_ID} \
79
79
  --location ${_REGION} \
80
- --set-env-vars="COMMIT_SHA=${COMMIT_SHA}{%- if cookiecutter.data_ingestion %}{%- if cookiecutter.datastore_type == "vertex_ai_search" %},DATA_STORE_ID=${_DATA_STORE_ID},DATA_STORE_REGION=${_DATA_STORE_REGION}{%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %},VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX},VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT},VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET}{%- endif %}{%- endif %}"
80
+ --set-env-vars="COMMIT_SHA=${COMMIT_SHA}{%- if cookiecutter.data_ingestion %}{%- if cookiecutter.datastore_type == "vertex_ai_search" %},DATA_STORE_ID=${_DATA_STORE_ID_PROD},DATA_STORE_REGION=${_DATA_STORE_REGION}{%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %},VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX_PROD},VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT_PROD},VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET_PROD}{%- endif %}{%- endif %}"
81
81
  env:
82
82
  - 'PATH=/usr/local/bin:/usr/bin:~/.local/bin'
83
83
  {%- endif %}
@@ -23,18 +23,18 @@ steps:
23
23
  cd data_ingestion && pip install uv==0.6.12 --user && cd data_ingestion_pipeline && \
24
24
  uv sync --locked && uv run python submit_pipeline.py
25
25
  env:
26
- - "PIPELINE_ROOT=${_PIPELINE_GCS_ROOT}"
26
+ - "PIPELINE_ROOT=${_PIPELINE_GCS_ROOT_STAGING}"
27
27
  - "REGION=${_REGION}"
28
28
  {%- if cookiecutter.datastore_type == "vertex_ai_search" %}
29
29
  - "DATA_STORE_REGION=${_DATA_STORE_REGION}"
30
- - "DATA_STORE_ID=${_DATA_STORE_ID}"
30
+ - "DATA_STORE_ID=${_DATA_STORE_ID_STAGING}"
31
31
  {%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %}
32
- - "VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX}"
33
- - "VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT}"
34
- - "VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET}"
32
+ - "VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX_STAGING}"
33
+ - "VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT_STAGING}"
34
+ - "VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET_STAGING}"
35
35
  {%- endif %}
36
36
  - "PROJECT_ID=${_STAGING_PROJECT_ID}"
37
- - "SERVICE_ACCOUNT=${_PIPELINE_SA_EMAIL}"
37
+ - "SERVICE_ACCOUNT=${_PIPELINE_SA_EMAIL_STAGING}"
38
38
  - "PIPELINE_NAME=${_PIPELINE_NAME}"
39
39
  - 'PATH=/usr/local/bin:/usr/bin:~/.local/bin'
40
40
  {%- endif %}
@@ -111,7 +111,7 @@ steps:
111
111
  uv run app/agent_engine_app.py \
112
112
  --project ${_STAGING_PROJECT_ID} \
113
113
  --location ${_REGION} \
114
- --set-env-vars="COMMIT_SHA=${COMMIT_SHA}{%- if cookiecutter.data_ingestion %}{%- if cookiecutter.datastore_type == "vertex_ai_search" %},DATA_STORE_ID=${_DATA_STORE_ID},DATA_STORE_REGION=${_DATA_STORE_REGION}{%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %},VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX},VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT},VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET}{%- endif %}{%- endif %}"
114
+ --set-env-vars="COMMIT_SHA=${COMMIT_SHA}{%- if cookiecutter.data_ingestion %}{%- if cookiecutter.datastore_type == "vertex_ai_search" %},DATA_STORE_ID=${_DATA_STORE_ID_STAGING},DATA_STORE_REGION=${_DATA_STORE_REGION}{%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %},VECTOR_SEARCH_INDEX=${_VECTOR_SEARCH_INDEX_STAGING},VECTOR_SEARCH_INDEX_ENDPOINT=${_VECTOR_SEARCH_INDEX_ENDPOINT_STAGING},VECTOR_SEARCH_BUCKET=${_VECTOR_SEARCH_BUCKET_STAGING}{%- endif %}{%- endif %}"
115
115
  env:
116
116
  - 'PATH=/usr/local/bin:/usr/bin:~/.local/bin'
117
117
 
@@ -40,6 +40,7 @@ from ..utils.template import (
40
40
  get_template_path,
41
41
  load_template_config,
42
42
  process_template,
43
+ prompt_cicd_runner_selection,
43
44
  prompt_datastore_selection,
44
45
  prompt_deployment_target,
45
46
  prompt_session_type_selection,
@@ -96,6 +97,11 @@ def normalize_project_name(project_name: str) -> str:
96
97
  type=click.Choice(["agent_engine", "cloud_run"]),
97
98
  help="Deployment target name",
98
99
  )
100
+ @click.option(
101
+ "--cicd-runner",
102
+ type=click.Choice(["google_cloud_build", "github_actions"]),
103
+ help="CI/CD runner to use",
104
+ )
99
105
  @click.option(
100
106
  "--include-data-ingestion",
101
107
  "-i",
@@ -110,7 +116,7 @@ def normalize_project_name(project_name: str) -> str:
110
116
  )
111
117
  @click.option(
112
118
  "--session-type",
113
- type=click.Choice(["in_memory", "alloydb"]),
119
+ type=click.Choice(["in_memory", "alloydb", "agent_engine"]),
114
120
  help="Type of session storage to use",
115
121
  )
116
122
  @click.option("--debug", is_flag=True, help="Enable debug logging")
@@ -140,6 +146,7 @@ def create(
140
146
  project_name: str,
141
147
  agent: str | None,
142
148
  deployment_target: str | None,
149
+ cicd_runner: str | None,
143
150
  include_data_ingestion: bool,
144
151
  datastore: str | None,
145
152
  session_type: str | None,
@@ -270,6 +277,27 @@ def create(
270
277
  )
271
278
  final_agent = display_agent_selection(deployment_target)
272
279
 
280
+ # If browse functionality returned a remote agent spec, process it like CLI input
281
+ if final_agent and final_agent.startswith("adk@"):
282
+ # Set agent to the returned spec for remote processing
283
+ agent = final_agent
284
+
285
+ # Process the remote template spec just like CLI input
286
+ remote_spec = parse_agent_spec(agent)
287
+ if remote_spec:
288
+ if remote_spec.is_adk_samples:
289
+ console.print(
290
+ f"> Fetching template: {remote_spec.template_path}",
291
+ style="bold blue",
292
+ )
293
+ else:
294
+ console.print(f"Fetching remote template: {agent}")
295
+ template_source_path, temp_dir_path = fetch_remote_template(
296
+ remote_spec
297
+ )
298
+ temp_dir_to_clean = str(temp_dir_path)
299
+ final_agent = f"remote_{hash(agent)}" # Generate unique name for remote template
300
+
273
301
  if debug:
274
302
  logging.debug(f"Selected agent: {final_agent}")
275
303
 
@@ -430,6 +458,20 @@ def create(
430
458
  if debug and final_session_type:
431
459
  logging.debug(f"Selected session type: {final_session_type}")
432
460
 
461
+ # CI/CD runner selection
462
+ final_cicd_runner = cicd_runner
463
+ if not final_cicd_runner:
464
+ if auto_approve:
465
+ final_cicd_runner = "google_cloud_build"
466
+ console.print(
467
+ "Info: --cicd-runner not specified. Defaulting to 'google_cloud_build' in auto-approve mode.",
468
+ style="yellow",
469
+ )
470
+ else:
471
+ final_cicd_runner = prompt_cicd_runner_selection()
472
+ if debug:
473
+ logging.debug(f"Selected CI/CD runner: {final_cicd_runner}")
474
+
433
475
  # Region confirmation (if not explicitly passed)
434
476
  if (
435
477
  not auto_approve
@@ -460,7 +502,7 @@ def create(
460
502
  )
461
503
  console.print(
462
504
  "> Please check your authentication settings and permissions. "
463
- "Visit https://cloud.google.com/vertex-ai/docs/authentication for help.",
505
+ "> Visit https://cloud.google.com/vertex-ai/docs/authentication for help.",
464
506
  style="yellow",
465
507
  )
466
508
  console.print(
@@ -490,6 +532,7 @@ def create(
490
532
  template_path,
491
533
  project_name,
492
534
  deployment_target=final_deployment,
535
+ cicd_runner=final_cicd_runner,
493
536
  include_data_ingestion=include_data_ingestion,
494
537
  datastore=datastore,
495
538
  session_type=final_session_type,
@@ -584,14 +627,116 @@ def display_agent_selection(deployment_target: str | None = None) -> str:
584
627
  f"{num}. [bold]{agent['name']}[/] - [dim]{agent['description']}[/]"
585
628
  )
586
629
 
630
+ # Add special option for adk-samples
631
+ adk_samples_option = len(agents) + 1
632
+ console.print(
633
+ f"{adk_samples_option}. [bold]Browse agents from [link=https://github.com/google/adk-samples]google/adk-samples[/link][/] - [dim]Discover additional samples[/]"
634
+ )
635
+
587
636
  choice = IntPrompt.ask(
588
637
  "\nEnter the number of your template choice", default=1, show_default=True
589
638
  )
590
639
 
591
- if choice not in agents:
640
+ if choice == adk_samples_option:
641
+ return display_adk_samples_selection()
642
+ elif choice in agents:
643
+ return agents[choice]["name"]
644
+ else:
592
645
  raise ValueError(f"Invalid agent selection: {choice}")
593
646
 
594
- return agents[choice]["name"]
647
+
648
+ def display_adk_samples_selection() -> str:
649
+ """Display adk-samples agents and prompt for selection."""
650
+
651
+ from ..utils.remote_template import fetch_remote_template, parse_agent_spec
652
+
653
+ console.print("\n> Fetching agents from [bold blue]google/adk-samples[/]...")
654
+
655
+ try:
656
+ # Parse the adk-samples repository
657
+ spec = parse_agent_spec("https://github.com/google/adk-samples")
658
+ if not spec:
659
+ raise RuntimeError("Failed to parse adk-samples repository")
660
+
661
+ # Fetch the repository
662
+ repo_path, _ = fetch_remote_template(spec)
663
+
664
+ # Scan for agents in the repository
665
+ adk_agents = {}
666
+ agent_count = 1
667
+
668
+ # Search for templateconfig.yaml files to identify agents
669
+ for config_path in sorted(repo_path.glob("**/templateconfig.yaml")):
670
+ try:
671
+ import yaml
672
+
673
+ with open(config_path, encoding="utf-8") as f:
674
+ config = yaml.safe_load(f)
675
+
676
+ agent_name = config.get("name", config_path.parent.parent.name)
677
+ description = config.get("description", "No description available")
678
+
679
+ # Get the relative path from repo root
680
+ relative_path = config_path.parent.parent.relative_to(repo_path)
681
+
682
+ # For adk-samples, use just the agent name for the spec
683
+ # This handles cases like python/agents/gemini-fullstack -> gemini-fullstack
684
+ agent_spec_name = (
685
+ relative_path.name
686
+ if relative_path != relative_path.parent
687
+ else str(relative_path)
688
+ )
689
+
690
+ adk_agents[agent_count] = {
691
+ "name": agent_name,
692
+ "description": description,
693
+ "path": str(relative_path),
694
+ "spec": f"adk@{agent_spec_name}",
695
+ }
696
+ agent_count += 1
697
+
698
+ except Exception as e:
699
+ logging.warning(
700
+ f"Could not load agent from {config_path.parent.parent}: {e}"
701
+ )
702
+
703
+ if not adk_agents:
704
+ console.print("No agents found in adk-samples repository", style="yellow")
705
+ # Fall back to local agents
706
+ return display_agent_selection()
707
+
708
+ console.print("\n> Available agents from [bold blue]google/adk-samples[/]:")
709
+ for num, agent in adk_agents.items():
710
+ console.print(
711
+ f"{num}. [bold]{agent['name']}[/] - [dim]{agent['description']}[/]"
712
+ )
713
+
714
+ # Add option to go back to local agents
715
+ back_option = len(adk_agents) + 1
716
+ console.print(
717
+ f"{back_option}. [bold]← Back to built-in agents[/] - [dim]Return to local agent selection[/]"
718
+ )
719
+
720
+ choice = IntPrompt.ask(
721
+ "\nEnter the number of your choice", default=1, show_default=True
722
+ )
723
+
724
+ if choice == back_option:
725
+ return display_agent_selection()
726
+ elif choice in adk_agents:
727
+ # Return the adk@ spec for the selected agent
728
+ selected_agent = adk_agents[choice]
729
+ console.print(
730
+ f"\n> Selected: [bold]{selected_agent['name']}[/] from adk-samples"
731
+ )
732
+ return selected_agent["spec"]
733
+ else:
734
+ raise ValueError(f"Invalid agent selection: {choice}")
735
+
736
+ except Exception as e:
737
+ console.print(f"Error fetching adk-samples agents: {e}", style="bold red")
738
+ console.print("Falling back to built-in agents...", style="yellow")
739
+ return display_agent_selection()
595
740
 
596
741
 
597
742
  def set_gcp_project(project_id: str, set_quota_project: bool = True) -> None:
src/cli/commands/list.py CHANGED
@@ -45,7 +45,7 @@ def display_agents_from_path(base_path: pathlib.Path, source_name: str) -> None:
45
45
  # Search for templateconfig.yaml files to identify agents
46
46
  for config_path in sorted(base_path.glob("**/templateconfig.yaml")):
47
47
  try:
48
- with open(config_path) as f:
48
+ with open(config_path, encoding="utf-8") as f:
49
49
  config = yaml.safe_load(f)
50
50
 
51
51
  agent_name = config.get("name", config_path.parent.parent.name)