agent-starter-pack 0.18.2__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.
- agent_starter_pack/agents/{langgraph_base_react → adk_a2a_base}/.template/templateconfig.yaml +5 -12
- agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
- agent_starter_pack/{frontends/streamlit/frontend/style/app_markdown.py → agents/adk_a2a_base/app/__init__.py} +3 -23
- agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
- agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
- agent_starter_pack/agents/{crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → adk_a2a_base/notebooks/evaluating_adk_agent.ipynb} +163 -199
- agent_starter_pack/agents/adk_a2a_base/tests/integration/test_agent.py +58 -0
- agent_starter_pack/agents/adk_base/app/__init__.py +2 -2
- agent_starter_pack/agents/adk_base/app/agent.py +3 -0
- agent_starter_pack/agents/adk_base/notebooks/adk_app_testing.ipynb +13 -28
- agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
- agent_starter_pack/agents/adk_live/app/agent.py +3 -0
- agent_starter_pack/agents/agentic_rag/app/__init__.py +2 -2
- agent_starter_pack/agents/agentic_rag/app/agent.py +3 -0
- agent_starter_pack/agents/agentic_rag/notebooks/adk_app_testing.ipynb +13 -28
- agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/.template/templateconfig.yaml +12 -9
- agent_starter_pack/agents/langgraph_base/README.md +30 -0
- agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
- agent_starter_pack/agents/{langgraph_base_react → langgraph_base}/app/agent.py +4 -4
- agent_starter_pack/agents/{langgraph_base_react → langgraph_base}/tests/integration/test_agent.py +1 -1
- agent_starter_pack/base_template/.gitignore +4 -2
- agent_starter_pack/base_template/Makefile +110 -16
- agent_starter_pack/base_template/README.md +97 -12
- agent_starter_pack/base_template/deployment/terraform/dev/apis.tf +4 -6
- agent_starter_pack/base_template/deployment/terraform/dev/providers.tf +5 -1
- agent_starter_pack/base_template/deployment/terraform/dev/variables.tf +5 -3
- agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
- agent_starter_pack/base_template/deployment/terraform/github.tf +16 -9
- agent_starter_pack/base_template/deployment/terraform/locals.tf +7 -7
- agent_starter_pack/base_template/deployment/terraform/providers.tf +5 -1
- agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
- agent_starter_pack/base_template/deployment/terraform/storage.tf +0 -9
- agent_starter_pack/base_template/deployment/terraform/variables.tf +15 -19
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +20 -22
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
- agent_starter_pack/base_template/pyproject.toml +5 -17
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +19 -4
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +36 -11
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +24 -5
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +44 -9
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/typing.py +4 -6
- agent_starter_pack/{agents/crewai_coding_crew/app/crew/config/agents.yaml → base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/__init__.py } +9 -23
- 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
- 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
- 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
- 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
- agent_starter_pack/cli/commands/create.py +40 -4
- agent_starter_pack/cli/commands/enhance.py +1 -1
- agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
- agent_starter_pack/cli/main.py +2 -0
- agent_starter_pack/cli/utils/cicd.py +20 -4
- agent_starter_pack/cli/utils/template.py +257 -25
- agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +113 -16
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +2 -2
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +178 -9
- agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +193 -307
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/{utils → app_utils}/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +13 -14
- agent_starter_pack/deployment_targets/cloud_run/Dockerfile +4 -1
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +85 -86
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +139 -107
- agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +228 -12
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +4 -4
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +92 -12
- agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/{server.py → fast_api_app.py} +194 -121
- agent_starter_pack/frontends/adk_live_react/frontend/package-lock.json +18 -18
- agent_starter_pack/frontends/adk_live_react/frontend/src/multimodal-live-types.ts +5 -3
- agent_starter_pack/resources/docs/adk-cheatsheet.md +198 -41
- agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
- agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +1443 -709
- agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +1058 -874
- agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +1443 -709
- agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +1058 -874
- agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +1568 -749
- agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +1123 -929
- agent_starter_pack/resources/locks/{uv-langgraph_base_react-agent_engine.lock → uv-langgraph_base-agent_engine.lock} +1714 -1689
- agent_starter_pack/resources/locks/{uv-langgraph_base_react-cloud_run.lock → uv-langgraph_base-cloud_run.lock} +1285 -2374
- agent_starter_pack/utils/watch_and_rebuild.py +1 -1
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/METADATA +3 -6
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/RECORD +89 -93
- agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
- llm.txt +4 -5
- agent_starter_pack/agents/crewai_coding_crew/README.md +0 -34
- agent_starter_pack/agents/crewai_coding_crew/app/agent.py +0 -47
- agent_starter_pack/agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
- agent_starter_pack/agents/crewai_coding_crew/app/crew/crew.py +0 -71
- agent_starter_pack/agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
- agent_starter_pack/agents/langgraph_base_react/README.md +0 -9
- agent_starter_pack/agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -1574
- agent_starter_pack/base_template/deployment/terraform/dev/log_sinks.tf +0 -69
- agent_starter_pack/base_template/deployment/terraform/log_sinks.tf +0 -79
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +0 -155
- agent_starter_pack/cli/utils/register_gemini_enterprise.py +0 -406
- agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +0 -82
- agent_starter_pack/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +0 -99
- agent_starter_pack/frontends/streamlit/frontend/side_bar.py +0 -214
- agent_starter_pack/frontends/streamlit/frontend/streamlit_app.py +0 -265
- agent_starter_pack/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
- agent_starter_pack/frontends/streamlit/frontend/utils/local_chat_history.py +0 -127
- agent_starter_pack/frontends/streamlit/frontend/utils/message_editing.py +0 -59
- agent_starter_pack/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
- agent_starter_pack/frontends/streamlit/frontend/utils/stream_handler.py +0 -310
- agent_starter_pack/frontends/streamlit/frontend/utils/title_summary.py +0 -94
- agent_starter_pack/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6650
- agent_starter_pack/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -7825
- agent_starter_pack-0.18.2.dist-info/entry_points.txt +0 -3
- /agent_starter_pack/agents/{crewai_coding_crew → langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +0 -0
- /agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/{utils → app_utils}/gcs.py +0 -0
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.18.2.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import datetime
|
|
17
|
+
import importlib
|
|
18
|
+
import inspect
|
|
19
|
+
import json
|
|
20
|
+
import logging
|
|
21
|
+
import warnings
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
import click
|
|
25
|
+
import google.auth
|
|
26
|
+
import vertexai
|
|
27
|
+
from vertexai._genai import _agent_engines_utils
|
|
28
|
+
from vertexai._genai.types import AgentEngine, AgentEngineConfig{%- if cookiecutter.is_adk_live %}, AgentServerMode{%- endif %}
|
|
29
|
+
{%- if cookiecutter.is_adk_live %}
|
|
30
|
+
|
|
31
|
+
from {{cookiecutter.agent_directory}}.app_utils.gcs import create_bucket_if_not_exists
|
|
32
|
+
{%- endif %}
|
|
33
|
+
|
|
34
|
+
# Suppress google-cloud-storage version compatibility warning
|
|
35
|
+
warnings.filterwarnings(
|
|
36
|
+
"ignore", category=FutureWarning, module="google.cloud.aiplatform"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def generate_class_methods_from_agent(agent_instance: Any) -> list[dict[str, Any]]:
|
|
41
|
+
"""Generate method specifications with schemas from agent's register_operations().
|
|
42
|
+
|
|
43
|
+
See: https://docs.cloud.google.com/agent-builder/agent-engine/use/custom#supported-operations
|
|
44
|
+
"""
|
|
45
|
+
registered_operations = _agent_engines_utils._get_registered_operations(
|
|
46
|
+
agent=agent_instance
|
|
47
|
+
)
|
|
48
|
+
class_methods_spec = _agent_engines_utils._generate_class_methods_spec_or_raise(
|
|
49
|
+
agent=agent_instance,
|
|
50
|
+
operations=registered_operations,
|
|
51
|
+
)
|
|
52
|
+
class_methods_list = [
|
|
53
|
+
_agent_engines_utils._to_dict(method_spec) for method_spec in class_methods_spec
|
|
54
|
+
]
|
|
55
|
+
return class_methods_list
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def parse_key_value_pairs(kv_string: str | None) -> dict[str, str]:
|
|
59
|
+
"""Parse key-value pairs from a comma-separated KEY=VALUE string."""
|
|
60
|
+
result = {}
|
|
61
|
+
if kv_string:
|
|
62
|
+
for pair in kv_string.split(","):
|
|
63
|
+
if "=" in pair:
|
|
64
|
+
key, value = pair.split("=", 1)
|
|
65
|
+
result[key.strip()] = value.strip()
|
|
66
|
+
else:
|
|
67
|
+
logging.warning(f"Skipping malformed key-value pair: {pair}")
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def write_deployment_metadata(
|
|
72
|
+
remote_agent: Any,
|
|
73
|
+
metadata_file: str = "deployment_metadata.json",
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Write deployment metadata to file."""
|
|
76
|
+
metadata = {
|
|
77
|
+
"remote_agent_engine_id": remote_agent.api_resource.name,
|
|
78
|
+
"deployment_target": "agent_engine",
|
|
79
|
+
{%- if cookiecutter.is_a2a %}
|
|
80
|
+
"is_a2a": True,
|
|
81
|
+
{%- else %}
|
|
82
|
+
"is_a2a": False,
|
|
83
|
+
{%- endif %}
|
|
84
|
+
"deployment_timestamp": datetime.datetime.now().isoformat(),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
with open(metadata_file, "w") as f:
|
|
88
|
+
json.dump(metadata, f, indent=2)
|
|
89
|
+
|
|
90
|
+
logging.info(f"Agent Engine ID written to {metadata_file}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def print_deployment_success(
|
|
94
|
+
remote_agent: Any,
|
|
95
|
+
location: str,
|
|
96
|
+
project: str,
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Print deployment success message with console URL."""
|
|
99
|
+
# Extract agent engine ID and project number for console URL
|
|
100
|
+
resource_name_parts = remote_agent.api_resource.name.split("/")
|
|
101
|
+
agent_engine_id = resource_name_parts[-1]
|
|
102
|
+
project_number = resource_name_parts[1]
|
|
103
|
+
|
|
104
|
+
{%- if cookiecutter.is_adk_live %}
|
|
105
|
+
print("\n✅ Deployment successful! Run your agent with: `make playground-remote`")
|
|
106
|
+
{%- elif cookiecutter.is_a2a %}
|
|
107
|
+
print(
|
|
108
|
+
"\n✅ Deployment successful! Test your agent: notebooks/adk_a2a_app_testing.ipynb"
|
|
109
|
+
)
|
|
110
|
+
{%- else %}
|
|
111
|
+
print("\n✅ Deployment successful!")
|
|
112
|
+
{%- endif %}
|
|
113
|
+
service_account = remote_agent.api_resource.spec.service_account
|
|
114
|
+
if service_account:
|
|
115
|
+
print(f"Service Account: {service_account}")
|
|
116
|
+
else:
|
|
117
|
+
default_sa = (
|
|
118
|
+
f"service-{project_number}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
|
|
119
|
+
)
|
|
120
|
+
print(f"Service Account: {default_sa}")
|
|
121
|
+
{%- if cookiecutter.is_adk and not cookiecutter.is_adk_live and not cookiecutter.is_a2a %}
|
|
122
|
+
playground_url = f"https://console.cloud.google.com/vertex-ai/agents/locations/{location}/agent-engines/{agent_engine_id}/playground?project={project}"
|
|
123
|
+
print(f"\n📊 Open Console Playground: {playground_url}\n")
|
|
124
|
+
{%- else %}
|
|
125
|
+
console_url = f"https://console.cloud.google.com/vertex-ai/agents/locations/{location}/agent-engines/{agent_engine_id}?project={project}"
|
|
126
|
+
print(f"\n📊 View in Console: {console_url}\n")
|
|
127
|
+
{%- endif %}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@click.command()
|
|
131
|
+
@click.option(
|
|
132
|
+
"--project",
|
|
133
|
+
default=None,
|
|
134
|
+
help="GCP project ID (defaults to application default credentials)",
|
|
135
|
+
)
|
|
136
|
+
@click.option(
|
|
137
|
+
"--location",
|
|
138
|
+
default="us-central1",
|
|
139
|
+
help="GCP region (defaults to us-central1)",
|
|
140
|
+
)
|
|
141
|
+
@click.option(
|
|
142
|
+
"--display-name",
|
|
143
|
+
default="{{cookiecutter.project_name}}",
|
|
144
|
+
help="Display name for the agent engine",
|
|
145
|
+
)
|
|
146
|
+
@click.option(
|
|
147
|
+
"--description",
|
|
148
|
+
default="{{cookiecutter.agent_description}}",
|
|
149
|
+
help="Description of the agent",
|
|
150
|
+
)
|
|
151
|
+
@click.option(
|
|
152
|
+
"--source-packages",
|
|
153
|
+
multiple=True,
|
|
154
|
+
default=["./{{cookiecutter.agent_directory}}"],
|
|
155
|
+
help="Source packages to deploy. Can be specified multiple times (e.g., --source-packages=./app --source-packages=./lib)",
|
|
156
|
+
)
|
|
157
|
+
@click.option(
|
|
158
|
+
"--entrypoint-module",
|
|
159
|
+
default="{{cookiecutter.agent_directory}}.agent_engine_app",
|
|
160
|
+
help="Python module path for the agent entrypoint (required)",
|
|
161
|
+
)
|
|
162
|
+
@click.option(
|
|
163
|
+
"--entrypoint-object",
|
|
164
|
+
default="agent_engine",
|
|
165
|
+
help="Name of the agent instance at module level (required)",
|
|
166
|
+
)
|
|
167
|
+
@click.option(
|
|
168
|
+
"--requirements-file",
|
|
169
|
+
default="{{cookiecutter.agent_directory}}/app_utils/.requirements.txt",
|
|
170
|
+
help="Path to requirements.txt file",
|
|
171
|
+
)
|
|
172
|
+
@click.option(
|
|
173
|
+
"--set-env-vars",
|
|
174
|
+
default=None,
|
|
175
|
+
help="Comma-separated list of environment variables in KEY=VALUE format",
|
|
176
|
+
)
|
|
177
|
+
@click.option(
|
|
178
|
+
"--labels",
|
|
179
|
+
default=None,
|
|
180
|
+
help="Comma-separated list of labels in KEY=VALUE format",
|
|
181
|
+
)
|
|
182
|
+
@click.option(
|
|
183
|
+
"--service-account",
|
|
184
|
+
default=None,
|
|
185
|
+
help="Service account email to use for the agent engine",
|
|
186
|
+
)
|
|
187
|
+
@click.option(
|
|
188
|
+
"--min-instances",
|
|
189
|
+
type=int,
|
|
190
|
+
default=1,
|
|
191
|
+
help="Minimum number of instances (default: 1)",
|
|
192
|
+
)
|
|
193
|
+
@click.option(
|
|
194
|
+
"--max-instances",
|
|
195
|
+
type=int,
|
|
196
|
+
default=10,
|
|
197
|
+
help="Maximum number of instances (default: 10)",
|
|
198
|
+
)
|
|
199
|
+
@click.option(
|
|
200
|
+
"--cpu",
|
|
201
|
+
default="4",
|
|
202
|
+
help="CPU limit (default: 4)",
|
|
203
|
+
)
|
|
204
|
+
@click.option(
|
|
205
|
+
"--memory",
|
|
206
|
+
default="8Gi",
|
|
207
|
+
help="Memory limit (default: 8Gi)",
|
|
208
|
+
)
|
|
209
|
+
@click.option(
|
|
210
|
+
"--container-concurrency",
|
|
211
|
+
type=int,
|
|
212
|
+
default=9,
|
|
213
|
+
help="Container concurrency (default: 9)",
|
|
214
|
+
)
|
|
215
|
+
@click.option(
|
|
216
|
+
"--num-workers",
|
|
217
|
+
type=int,
|
|
218
|
+
default=1,
|
|
219
|
+
help="Number of worker processes (default: 1)",
|
|
220
|
+
)
|
|
221
|
+
def deploy_agent_engine_app(
|
|
222
|
+
project: str | None,
|
|
223
|
+
location: str,
|
|
224
|
+
display_name: str,
|
|
225
|
+
description: str,
|
|
226
|
+
source_packages: tuple[str, ...],
|
|
227
|
+
entrypoint_module: str,
|
|
228
|
+
entrypoint_object: str,
|
|
229
|
+
requirements_file: str,
|
|
230
|
+
set_env_vars: str | None,
|
|
231
|
+
labels: str | None,
|
|
232
|
+
service_account: str | None,
|
|
233
|
+
min_instances: int,
|
|
234
|
+
max_instances: int,
|
|
235
|
+
cpu: str,
|
|
236
|
+
memory: str,
|
|
237
|
+
container_concurrency: int,
|
|
238
|
+
num_workers: int,
|
|
239
|
+
) -> AgentEngine:
|
|
240
|
+
"""Deploy the agent engine app to Vertex AI."""
|
|
241
|
+
|
|
242
|
+
logging.basicConfig(level=logging.INFO)
|
|
243
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
244
|
+
|
|
245
|
+
# Parse environment variables and labels if provided
|
|
246
|
+
env_vars = parse_key_value_pairs(set_env_vars)
|
|
247
|
+
labels_dict = parse_key_value_pairs(labels)
|
|
248
|
+
|
|
249
|
+
# Set GOOGLE_CLOUD_REGION to match deployment location
|
|
250
|
+
env_vars["GOOGLE_CLOUD_REGION"] = location
|
|
251
|
+
|
|
252
|
+
# Add NUM_WORKERS from CLI argument (can be overridden via --set-env-vars)
|
|
253
|
+
if "NUM_WORKERS" not in env_vars:
|
|
254
|
+
env_vars["NUM_WORKERS"] = str(num_workers)
|
|
255
|
+
|
|
256
|
+
# Enable telemetry by default for Agent Engine
|
|
257
|
+
if "GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY" not in env_vars:
|
|
258
|
+
env_vars["GOOGLE_CLOUD_AGENT_ENGINE_ENABLE_TELEMETRY"] = "true"
|
|
259
|
+
if "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT" not in env_vars:
|
|
260
|
+
env_vars["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = "true"
|
|
261
|
+
|
|
262
|
+
if not project:
|
|
263
|
+
_, project = google.auth.default()
|
|
264
|
+
|
|
265
|
+
print("""
|
|
266
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
267
|
+
║ ║
|
|
268
|
+
║ 🤖 DEPLOYING AGENT TO VERTEX AI AGENT ENGINE 🤖 ║
|
|
269
|
+
║ ║
|
|
270
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
271
|
+
""")
|
|
272
|
+
|
|
273
|
+
# Log deployment parameters
|
|
274
|
+
click.echo("\n📋 Deployment Parameters:")
|
|
275
|
+
click.echo(f" Project: {project}")
|
|
276
|
+
click.echo(f" Location: {location}")
|
|
277
|
+
click.echo(f" Display Name: {display_name}")
|
|
278
|
+
click.echo(f" Min Instances: {min_instances}")
|
|
279
|
+
click.echo(f" Max Instances: {max_instances}")
|
|
280
|
+
click.echo(f" CPU: {cpu}")
|
|
281
|
+
click.echo(f" Memory: {memory}")
|
|
282
|
+
click.echo(f" Container Concurrency: {container_concurrency}")
|
|
283
|
+
if service_account:
|
|
284
|
+
click.echo(f" Service Account: {service_account}")
|
|
285
|
+
if env_vars:
|
|
286
|
+
click.echo("\n🌍 Environment Variables:")
|
|
287
|
+
for key, value in sorted(env_vars.items()):
|
|
288
|
+
click.echo(f" {key}: {value}")
|
|
289
|
+
|
|
290
|
+
source_packages_list = list(source_packages)
|
|
291
|
+
|
|
292
|
+
# Initialize vertexai client
|
|
293
|
+
client = vertexai.Client(
|
|
294
|
+
project=project,
|
|
295
|
+
location=location,
|
|
296
|
+
)
|
|
297
|
+
vertexai.init(project=project, location=location)
|
|
298
|
+
|
|
299
|
+
# Add agent garden labels if configured
|
|
300
|
+
{%- if cookiecutter.agent_garden %}
|
|
301
|
+
{%- if cookiecutter.agent_sample_id and cookiecutter.agent_sample_publisher %}
|
|
302
|
+
labels_dict["vertex-agent-sample-id"] = "{{cookiecutter.agent_sample_id}}"
|
|
303
|
+
labels_dict["vertex-agent-sample-publisher"] = "{{cookiecutter.agent_sample_publisher}}"
|
|
304
|
+
labels_dict["deployed-with"] = "agent-garden"
|
|
305
|
+
{%- endif %}
|
|
306
|
+
{%- endif %}
|
|
307
|
+
|
|
308
|
+
# Dynamically import the agent instance to generate class_methods
|
|
309
|
+
logging.info(f"Importing {entrypoint_module}.{entrypoint_object}")
|
|
310
|
+
module = importlib.import_module(entrypoint_module)
|
|
311
|
+
agent_instance = getattr(module, entrypoint_object)
|
|
312
|
+
|
|
313
|
+
# If the agent_instance is a coroutine, await it to get the actual instance
|
|
314
|
+
if inspect.iscoroutine(agent_instance):
|
|
315
|
+
logging.info(f"Detected coroutine, awaiting {entrypoint_object}...")
|
|
316
|
+
agent_instance = asyncio.run(agent_instance)
|
|
317
|
+
|
|
318
|
+
{%- if cookiecutter.is_adk_live %}
|
|
319
|
+
# For adk_live, use pickle-based deployment (source-based not yet supported with EXPERIMENTAL mode)
|
|
320
|
+
# Ensure staging bucket exists for pickle serialization
|
|
321
|
+
staging_bucket_uri = f"gs://{project}-agent-engine"
|
|
322
|
+
|
|
323
|
+
create_bucket_if_not_exists(
|
|
324
|
+
bucket_name=staging_bucket_uri, project=project, location=location
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
config = AgentEngineConfig(
|
|
328
|
+
display_name=display_name,
|
|
329
|
+
description=description,
|
|
330
|
+
env_vars=env_vars,
|
|
331
|
+
extra_packages=source_packages_list,
|
|
332
|
+
service_account=service_account,
|
|
333
|
+
requirements=requirements_file,
|
|
334
|
+
staging_bucket=staging_bucket_uri,
|
|
335
|
+
labels=labels_dict,
|
|
336
|
+
gcs_dir_name=display_name,
|
|
337
|
+
agent_server_mode=AgentServerMode.EXPERIMENTAL, # Enable bidi streaming
|
|
338
|
+
resource_limits={"cpu": cpu, "memory": memory},
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
agent_config = {
|
|
342
|
+
"agent": agent_instance,
|
|
343
|
+
"config": config,
|
|
344
|
+
}
|
|
345
|
+
{%- else %}
|
|
346
|
+
# Generate class methods spec from register_operations
|
|
347
|
+
class_methods_list = generate_class_methods_from_agent(agent_instance)
|
|
348
|
+
|
|
349
|
+
config = AgentEngineConfig(
|
|
350
|
+
display_name=display_name,
|
|
351
|
+
description=description,
|
|
352
|
+
source_packages=source_packages_list,
|
|
353
|
+
entrypoint_module=entrypoint_module,
|
|
354
|
+
entrypoint_object=entrypoint_object,
|
|
355
|
+
class_methods=class_methods_list,
|
|
356
|
+
env_vars=env_vars,
|
|
357
|
+
service_account=service_account,
|
|
358
|
+
requirements_file=requirements_file,
|
|
359
|
+
labels=labels_dict,
|
|
360
|
+
min_instances=min_instances,
|
|
361
|
+
max_instances=max_instances,
|
|
362
|
+
resource_limits={"cpu": cpu, "memory": memory},
|
|
363
|
+
container_concurrency=container_concurrency,
|
|
364
|
+
{%- if cookiecutter.is_adk %}
|
|
365
|
+
agent_framework="google-adk",
|
|
366
|
+
{%- endif %}
|
|
367
|
+
)
|
|
368
|
+
{%- endif %}
|
|
369
|
+
|
|
370
|
+
# Check if an agent with this name already exists
|
|
371
|
+
existing_agents = list(client.agent_engines.list())
|
|
372
|
+
matching_agents = [
|
|
373
|
+
agent
|
|
374
|
+
for agent in existing_agents
|
|
375
|
+
if agent.api_resource.display_name == display_name
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
# Deploy the agent (create or update)
|
|
379
|
+
if matching_agents:
|
|
380
|
+
click.echo(f"\n📝 Updating existing agent: {display_name}")
|
|
381
|
+
else:
|
|
382
|
+
click.echo(f"\n🚀 Creating new agent: {display_name}")
|
|
383
|
+
|
|
384
|
+
click.echo("🚀 Deploying to Vertex AI Agent Engine (this can take 3-5 minutes)...")
|
|
385
|
+
|
|
386
|
+
{%- if cookiecutter.is_adk_live %}
|
|
387
|
+
if matching_agents:
|
|
388
|
+
remote_agent = client.agent_engines.update(
|
|
389
|
+
name=matching_agents[0].api_resource.name,
|
|
390
|
+
agent=agent_instance,
|
|
391
|
+
config=config,
|
|
392
|
+
)
|
|
393
|
+
else:
|
|
394
|
+
remote_agent = client.agent_engines.create(
|
|
395
|
+
agent=agent_instance,
|
|
396
|
+
config=config,
|
|
397
|
+
)
|
|
398
|
+
{%- else %}
|
|
399
|
+
if matching_agents:
|
|
400
|
+
remote_agent = client.agent_engines.update(
|
|
401
|
+
name=matching_agents[0].api_resource.name, config=config
|
|
402
|
+
)
|
|
403
|
+
else:
|
|
404
|
+
remote_agent = client.agent_engines.create(config=config)
|
|
405
|
+
{%- endif %}
|
|
406
|
+
|
|
407
|
+
write_deployment_metadata(remote_agent)
|
|
408
|
+
print_deployment_success(remote_agent, location, project)
|
|
409
|
+
|
|
410
|
+
return remote_agent
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
if __name__ == "__main__":
|
|
414
|
+
deploy_agent_engine_app()
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import asyncio
|
|
16
16
|
import json
|
|
17
17
|
import logging
|
|
18
|
+
import uuid
|
|
18
19
|
from collections.abc import Callable
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from typing import Any, Literal
|
|
@@ -27,7 +28,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
|
27
28
|
from fastapi.responses import FileResponse
|
|
28
29
|
from fastapi.staticfiles import StaticFiles
|
|
29
30
|
from google.cloud import logging as google_cloud_logging
|
|
30
|
-
from pydantic import BaseModel
|
|
31
|
+
from pydantic import BaseModel, Field
|
|
31
32
|
from websockets.exceptions import ConnectionClosedError
|
|
32
33
|
|
|
33
34
|
app = FastAPI()
|
|
@@ -60,7 +61,7 @@ app.state.config = {
|
|
|
60
61
|
"project_id": None,
|
|
61
62
|
"location": "us-central1",
|
|
62
63
|
"local_agent_path": "..agent.root_agent",
|
|
63
|
-
"
|
|
64
|
+
"agent_engine_object_path": "..agent_engine_app.agent_engine",
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
|
|
@@ -299,14 +300,12 @@ def get_connect_and_run_callable(
|
|
|
299
300
|
)
|
|
300
301
|
else:
|
|
301
302
|
# Local agent engine mode
|
|
302
|
-
# Dynamically import
|
|
303
|
-
|
|
303
|
+
# Dynamically import the pre-configured agent_engine object
|
|
304
|
+
agent_engine = _dynamic_import(config["agent_engine_object_path"])
|
|
304
305
|
logging.info(
|
|
305
|
-
f"Starting local agent engine with
|
|
306
|
+
f"Starting local agent engine with object: {type(agent_engine).__name__}"
|
|
306
307
|
)
|
|
307
308
|
|
|
308
|
-
AgentEngineApp = _dynamic_import(config["agent_engine_class_path"])
|
|
309
|
-
agent_engine = AgentEngineApp(agent=agent_callable)
|
|
310
309
|
adapter = WebSocketToQueueAdapter(websocket, agent_engine)
|
|
311
310
|
|
|
312
311
|
logging.info("Starting bidirectional communication with agent engine")
|
|
@@ -331,9 +330,9 @@ class Feedback(BaseModel):
|
|
|
331
330
|
|
|
332
331
|
score: int | float
|
|
333
332
|
text: str | None = ""
|
|
334
|
-
run_id: str
|
|
335
|
-
user_id: str | None
|
|
336
333
|
log_type: Literal["feedback"] = "feedback"
|
|
334
|
+
user_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
335
|
+
session_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
|
|
337
336
|
|
|
338
337
|
|
|
339
338
|
@app.post("/feedback")
|
|
@@ -417,10 +416,10 @@ if __name__ == "__main__":
|
|
|
417
416
|
help="Python path to local agent callable (e.g., 'app.agent.root_agent')",
|
|
418
417
|
)
|
|
419
418
|
parser.add_argument(
|
|
420
|
-
"--agent-engine-
|
|
419
|
+
"--agent-engine-object",
|
|
421
420
|
type=str,
|
|
422
|
-
default="..agent_engine_app.
|
|
423
|
-
help="Python path to agent engine
|
|
421
|
+
default="..agent_engine_app.agent_engine",
|
|
422
|
+
help="Python path to agent engine object instance",
|
|
424
423
|
)
|
|
425
424
|
parser.add_argument(
|
|
426
425
|
"--port",
|
|
@@ -444,7 +443,7 @@ if __name__ == "__main__":
|
|
|
444
443
|
"project_id": None,
|
|
445
444
|
"location": "us-central1",
|
|
446
445
|
"local_agent_path": args.local_agent,
|
|
447
|
-
"
|
|
446
|
+
"agent_engine_object_path": args.agent_engine_object,
|
|
448
447
|
}
|
|
449
448
|
|
|
450
449
|
if args.mode == "remote":
|
|
@@ -512,7 +511,7 @@ if __name__ == "__main__":
|
|
|
512
511
|
else:
|
|
513
512
|
print("Starting server in LOCAL mode")
|
|
514
513
|
print(f" Using agent: {config['local_agent_path']}")
|
|
515
|
-
print(f" Using agent engine
|
|
514
|
+
print(f" Using agent engine object: {config['agent_engine_object_path']}")
|
|
516
515
|
|
|
517
516
|
# Store configuration in app state
|
|
518
517
|
app.state.config = config
|
|
@@ -43,6 +43,9 @@ RUN uv sync --frozen
|
|
|
43
43
|
ARG COMMIT_SHA=""
|
|
44
44
|
ENV COMMIT_SHA=${COMMIT_SHA}
|
|
45
45
|
|
|
46
|
+
ARG AGENT_VERSION=0.0.0
|
|
47
|
+
ENV AGENT_VERSION=${AGENT_VERSION}
|
|
48
|
+
|
|
46
49
|
EXPOSE 8080
|
|
47
50
|
|
|
48
|
-
CMD ["uv", "run", "uvicorn", "{{cookiecutter.agent_directory}}.
|
|
51
|
+
CMD ["uv", "run", "uvicorn", "{{cookiecutter.agent_directory}}.fast_api_app:app", "--host", "0.0.0.0", "--port", "8080"]
|