Skip to content

Commit 3e1ca46

Browse files
yeesiancopybara-github
authored andcommitted
feat: Add --trigger_sources to cli_deploy_agent_engine
Co-authored-by: Yeesian Ng <ysian@google.com> PiperOrigin-RevId: 899594873
1 parent ce113a8 commit 3e1ca46

3 files changed

Lines changed: 272 additions & 172 deletions

File tree

src/google/adk/cli/cli_deploy.py

Lines changed: 113 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ def _ensure_agent_engine_dependency(requirements_txt_path: str) -> None:
6767
FROM python:3.11-slim
6868
WORKDIR /app
6969
70+
RUN apt-get update && \
71+
apt-get upgrade -y && \
72+
apt-get install -y git
73+
7074
# Create a non-root user
7175
RUN adduser --disabled-password --gecos "" myuser
7276
@@ -83,7 +87,7 @@ def _ensure_agent_engine_dependency(requirements_txt_path: str) -> None:
8387
# Set up environment variables - End
8488
8589
# Install ADK - Start
86-
RUN pip install google-adk=={adk_version}
90+
# RUN pip install google-adk=={adk_version}
8791
# Install ADK - End
8892
8993
# Copy agent - Start
@@ -99,33 +103,7 @@ def _ensure_agent_engine_dependency(requirements_txt_path: str) -> None:
99103
100104
EXPOSE {port}
101105
102-
CMD adk {command} --port={port} {host_option} {service_option} {trace_to_cloud_option} {otel_to_cloud_option} {allow_origins_option} {a2a_option} {trigger_sources_option} "/app/agents"
103-
"""
104-
105-
_AGENT_ENGINE_APP_TEMPLATE: Final[str] = """
106-
import os
107-
import vertexai
108-
from vertexai.agent_engines import AdkApp
109-
110-
if {is_config_agent}:
111-
from google.adk.agents import config_agent_utils
112-
config_path = os.path.join(os.path.dirname(__file__), "root_agent.yaml")
113-
root_agent = config_agent_utils.from_config(config_path)
114-
else:
115-
from .agent import {adk_app_object}
116-
117-
if {express_mode}: # Whether or not to use Express Mode
118-
vertexai.init(api_key=os.environ.get("GOOGLE_API_KEY"))
119-
else:
120-
vertexai.init(
121-
project=os.environ.get("GOOGLE_CLOUD_PROJECT"),
122-
location=os.environ.get("GOOGLE_CLOUD_LOCATION"),
123-
)
124-
125-
adk_app = AdkApp(
126-
{adk_app_type}={adk_app_object},
127-
enable_tracing={trace_to_cloud_option},
128-
)
106+
CMD adk {command} --port={port} {host_option} {service_option} {trace_to_cloud_option} {otel_to_cloud_option} {allow_origins_option} {a2a_option} {trigger_sources_option} {gemini_enterprise_option} "/app/agents"
129107
"""
130108

131109
_AGENT_ENGINE_CLASS_METHODS = [
@@ -830,7 +808,7 @@ def to_agent_engine(
830808
*,
831809
agent_folder: str,
832810
temp_folder: Optional[str] = None,
833-
adk_app: str,
811+
adk_app: Optional[str] = None,
834812
staging_bucket: Optional[str] = None,
835813
trace_to_cloud: Optional[bool] = None,
836814
otel_to_cloud: Optional[bool] = None,
@@ -846,36 +824,26 @@ def to_agent_engine(
846824
env_file: Optional[str] = None,
847825
agent_engine_config_file: Optional[str] = None,
848826
skip_agent_import_validation: bool = True,
827+
trigger_sources: Optional[str] = None,
828+
artifact_service_uri: Optional[str] = None,
829+
adk_version: Optional[str] = None,
849830
):
850831
"""Deploys an agent to Vertex AI Agent Engine.
851832
852833
`agent_folder` should contain the following files:
853834
854835
- __init__.py
855836
- agent.py
856-
- <adk_app>.py (optional, for customization; will be autogenerated otherwise)
857837
- requirements.txt (optional, for additional dependencies)
858838
- .env (optional, for environment variables)
859839
- ... (other required source files)
860840
861-
The contents of `adk_app` should look something like:
862-
863-
```
864-
from agent import <adk_app_object>
865-
from vertexai.agent_engines import AdkApp
866-
867-
adk_app = AdkApp(
868-
agent=<adk_app_object>, # or `app=<adk_app_object>`
869-
)
870-
```
871-
872841
Args:
873842
agent_folder (str): The folder (absolute path) containing the agent source
874843
code.
875844
temp_folder (str): The temp folder for the generated Agent Engine source
876845
files. It will be replaced with the generated files if it already exists.
877-
adk_app (str): The name of the file (without .py) containing the AdkApp
878-
instance.
846+
adk_app (str): Deprecated. This argument is no longer required or used.
879847
staging_bucket (str): Deprecated. This argument is no longer required or
880848
used.
881849
trace_to_cloud (bool): Whether to enable Cloud Trace.
@@ -884,13 +852,12 @@ def to_agent_engine(
884852
api_key (str): Optional. The API key to use for Express Mode.
885853
If not provided, the API key from the GOOGLE_API_KEY environment variable
886854
will be used. It will only be used if GOOGLE_GENAI_USE_VERTEXAI is true.
887-
adk_app_object (str): Optional. The Python object corresponding to the root
888-
ADK agent or app. Defaults to `root_agent` if not specified.
855+
adk_app_object (str): Deprecated. This argument is no longer required or
856+
used.
889857
agent_engine_id (str): Optional. The ID of the Agent Engine instance to
890858
update. If not specified, a new Agent Engine instance will be created.
891-
absolutize_imports (bool): Optional. Default is True. Whether to absolutize
892-
imports. If True, all relative imports will be converted to absolute
893-
import statements.
859+
absolutize_imports (bool): Deprecated. This argument is no longer required
860+
or used.
894861
project (str): Optional. Google Cloud project id for the deployed agent. If
895862
not specified, the project from the `GOOGLE_CLOUD_PROJECT` environment
896863
variable will be used. It will be ignored if `api_key` is specified.
@@ -899,31 +866,41 @@ def to_agent_engine(
899866
variable will be used. It will be ignored if `api_key` is specified.
900867
display_name (str): Optional. The display name of the Agent Engine.
901868
description (str): Optional. The description of the Agent Engine.
902-
requirements_file (str): Optional. The filepath to the `requirements.txt`
903-
file to use. If not specified, the `requirements.txt` file in the
904-
`agent_folder` will be used.
869+
requirements_file (str): Deprecated. This argument is no longer required or
870+
used.
905871
env_file (str): Optional. The filepath to the `.env` file for environment
906872
variables. If not specified, the `.env` file in the `agent_folder` will be
907873
used. The values of `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION`
908874
will be overridden by `project` and `region` if they are specified.
909875
agent_engine_config_file (str): The filepath to the agent engine config file
910876
to use. If not specified, the `.agent_engine_config.json` file in the
911877
`agent_folder` will be used.
912-
skip_agent_import_validation (bool): Optional. Default is True. If True,
913-
skip the pre-deployment import validation of `agent.py`. This can be
914-
useful when the local environment does not have the same dependencies as
915-
the deployment environment.
878+
skip_agent_import_validation (bool): Deprecated. This argument is no longer
879+
required or used.
880+
trigger_sources (str): Optional. Comma-separated list of trigger sources to
881+
enable (e.g., 'pubsub,eventarc'). Registers /trigger/* endpoints for
882+
batch and event-driven agent invocations.
883+
artifact_service_uri (str): Optional. The URI of the artifact service.
884+
adk_version (str): Optional. The ADK version to use in Agent Engine
885+
deployment.
916886
"""
917887
app_name = os.path.basename(agent_folder)
918888
display_name = display_name or app_name
919889
parent_folder = os.path.dirname(agent_folder)
920-
adk_app_object = adk_app_object or 'root_agent'
921-
if adk_app_object not in ['root_agent', 'app']:
922-
click.echo(
923-
f'Invalid adk_app_object: {adk_app_object}. Please use "root_agent"'
924-
' or "app".'
890+
if adk_app_object:
891+
warnings.warn(
892+
'WARNING: `--adk_app_object` is deprecated and will be removed in the'
893+
' future. Please drop it from the list of arguments.',
894+
DeprecationWarning,
895+
stacklevel=2,
896+
)
897+
if adk_app:
898+
warnings.warn(
899+
'WARNING: `adk_app` is deprecated and will be removed in a future'
900+
' release. Please drop it from the list of arguments.',
901+
DeprecationWarning,
902+
stacklevel=2,
925903
)
926-
return
927904
if staging_bucket:
928905
warnings.warn(
929906
'WARNING: `staging_bucket` is deprecated and will be removed in a'
@@ -943,15 +920,14 @@ def to_agent_engine(
943920
did_change_cwd = True
944921
tmp_app_name = app_name + '_tmp' + datetime.now().strftime('%Y%m%d_%H%M%S')
945922
temp_folder = temp_folder or tmp_app_name
946-
agent_src_path = os.path.join(parent_folder, temp_folder)
923+
agent_src_path = os.path.join(parent_folder, temp_folder, 'agents', app_name)
947924
click.echo(f'Staging all files in: {agent_src_path}')
948925
# remove agent_src_path if it exists
949926
if os.path.exists(agent_src_path):
950927
click.echo('Removing existing files')
951928
shutil.rmtree(agent_src_path)
952929

953930
try:
954-
click.echo(f'Staging all files in: {agent_src_path}')
955931
ignore_patterns = None
956932
ae_ignore_path = os.path.join(agent_folder, '.ae_ignore')
957933
if os.path.exists(ae_ignore_path):
@@ -966,6 +942,7 @@ def to_agent_engine(
966942
ignore=ignore_patterns,
967943
dirs_exist_ok=True,
968944
)
945+
os.chdir(os.path.join(parent_folder, temp_folder))
969946
click.echo('Copying agent source code complete.')
970947

971948
project = _resolve_project(project)
@@ -1002,30 +979,13 @@ def to_agent_engine(
1002979
)
1003980
agent_config['description'] = description
1004981

1005-
requirements_txt_path = os.path.join(agent_src_path, 'requirements.txt')
1006982
if requirements_file:
1007-
if os.path.exists(requirements_txt_path):
1008-
click.echo(
1009-
f'Overwriting {requirements_txt_path} with {requirements_file}'
1010-
)
1011-
shutil.copyfile(requirements_file, requirements_txt_path)
1012-
elif 'requirements_file' in agent_config:
1013-
if os.path.exists(requirements_txt_path):
1014-
click.echo(
1015-
f'Overwriting {requirements_txt_path} with'
1016-
f' {agent_config["requirements_file"]}'
1017-
)
1018-
shutil.copyfile(agent_config['requirements_file'], requirements_txt_path)
1019-
else:
1020-
# Attempt to read requirements from requirements.txt in the dir (if any).
1021-
if not os.path.exists(requirements_txt_path):
1022-
click.echo(f'Creating {requirements_txt_path}...')
1023-
with open(requirements_txt_path, 'w', encoding='utf-8') as f:
1024-
f.write(_AGENT_ENGINE_REQUIREMENT + '\n')
1025-
click.echo(f'Created {requirements_txt_path}')
1026-
_ensure_agent_engine_dependency(requirements_txt_path)
1027-
agent_config['requirements_file'] = f'{temp_folder}/requirements.txt'
1028-
983+
warnings.warn(
984+
'WARNING: `--requirements_file` is deprecated and will be removed in the'
985+
' future. Please drop it from the list of arguments.',
986+
DeprecationWarning,
987+
stacklevel=2,
988+
)
1029989
env_vars = {}
1030990
if not env_file:
1031991
# Attempt to read the env variables from .env in the dir (if any).
@@ -1094,92 +1054,97 @@ def to_agent_engine(
10941054

10951055
from ..utils._google_client_headers import get_tracking_headers
10961056

1057+
http_options = {'headers': get_tracking_headers()}
10971058
if project and region:
1098-
click.echo('Initializing Vertex AI...')
1059+
click.echo('Initializing Client with project and region...')
10991060
client = vertexai.Client(
11001061
project=project,
11011062
location=region,
1102-
http_options={'headers': get_tracking_headers()},
1063+
http_options=http_options,
11031064
)
11041065
elif api_key:
1105-
click.echo('Initializing Vertex AI in Express Mode with API key...')
1106-
client = vertexai.Client(
1107-
api_key=api_key, http_options={'headers': get_tracking_headers()}
1108-
)
1066+
click.echo('Initializing Client with Express Mode API key...')
1067+
client = vertexai.Client(api_key=api_key, http_options=http_options)
11091068
else:
11101069
click.echo(
11111070
'No project/region or api_key provided. '
11121071
'Please specify either project/region or api_key.'
11131072
)
11141073
return
1115-
click.echo('Vertex AI initialized.')
1116-
1117-
is_config_agent = False
1118-
config_root_agent_file = os.path.join(agent_src_path, 'root_agent.yaml')
1119-
if os.path.exists(config_root_agent_file):
1120-
click.echo(f'Config agent detected: {config_root_agent_file}')
1121-
is_config_agent = True
1122-
1123-
# Validate that the agent module can be imported before deployment.
1124-
if not skip_agent_import_validation:
1125-
click.echo('Validating agent module...')
1126-
_validate_agent_import(agent_src_path, adk_app_object, is_config_agent)
1127-
1128-
adk_app_file = os.path.join(temp_folder, f'{adk_app}.py')
1129-
if adk_app_object == 'root_agent':
1130-
adk_app_type = 'agent'
1131-
elif adk_app_object == 'app':
1132-
adk_app_type = 'app'
1133-
else:
1134-
click.echo(
1135-
f'Invalid adk_app_object: {adk_app_object}. Please use "root_agent"'
1136-
' or "app".'
1074+
1075+
if skip_agent_import_validation:
1076+
warnings.warn(
1077+
'WARNING: `--skip-agent-import-validation` is deprecated and will be'
1078+
' removed in the future. Please drop it from the list of arguments.',
1079+
DeprecationWarning,
1080+
stacklevel=2,
11371081
)
1138-
return
1139-
with open(adk_app_file, 'w', encoding='utf-8') as f:
1140-
f.write(
1141-
_AGENT_ENGINE_APP_TEMPLATE.format(
1142-
app_name=app_name,
1143-
trace_to_cloud_option=trace_to_cloud,
1144-
is_config_agent=is_config_agent,
1145-
agent_folder=f'./{temp_folder}',
1146-
adk_app_object=adk_app_object,
1147-
adk_app_type=adk_app_type,
1148-
express_mode=api_key is not None,
1149-
)
1082+
def create_dockerfile_for_agent_engine(resource_name: str):
1083+
requirements_txt_path = os.path.join(agent_src_path, 'requirements.txt')
1084+
install_agent_deps = (
1085+
f'RUN pip install -r "/app/agents/{app_name}/requirements.txt"'
1086+
if os.path.exists(requirements_txt_path)
1087+
else '# No requirements.txt found.'
1088+
)
1089+
trigger_sources_option = (
1090+
f'--trigger_sources={trigger_sources}' if trigger_sources else ''
1091+
)
1092+
agent_engine_uri = f'agentengine://{resource_name}'
1093+
dockerfile_content = _DOCKERFILE_TEMPLATE.format(
1094+
gcp_project_id=project,
1095+
gcp_region=region,
1096+
app_name=app_name,
1097+
port=8080,
1098+
command='api_server',
1099+
install_agent_deps=install_agent_deps,
1100+
service_option=_get_service_option_by_adk_version(
1101+
adk_version,
1102+
agent_engine_uri, # session_service_uri
1103+
artifact_service_uri,
1104+
agent_engine_uri, # memory_service_uri
1105+
False, # use_local_storage
1106+
),
1107+
trace_to_cloud_option='--trace_to_cloud' if trace_to_cloud else '',
1108+
otel_to_cloud_option='--otel_to_cloud' if otel_to_cloud else '',
1109+
allow_origins_option='', # Not supported for now.
1110+
adk_version=adk_version,
1111+
host_option='--host=0.0.0.0',
1112+
a2a_option='', # '--a2a',
1113+
trigger_sources_option=trigger_sources_option,
1114+
gemini_enterprise_option=f'--gemini_enterprise_app_name={app_name}',
11501115
)
1151-
click.echo(f'Created {adk_app_file}')
1152-
click.echo('Files and dependencies resolved')
1116+
with open('Dockerfile', 'w', encoding='utf-8') as f:
1117+
f.write(dockerfile_content)
1118+
11531119
if absolutize_imports:
1154-
click.echo(
1155-
'Agent Engine deployments have switched to source-based deployment, '
1156-
'so it is no longer necessary to absolutize imports.'
1120+
warnings.warn(
1121+
'WARNING: `--absolutize_imports` is deprecated and will be removed'
1122+
' in the future. Please drop it from the list of arguments.',
1123+
DeprecationWarning,
1124+
stacklevel=2,
11571125
)
11581126
click.echo('Deploying to agent engine...')
1159-
agent_config['entrypoint_module'] = f'{temp_folder}.{adk_app}'
1160-
agent_config['entrypoint_object'] = 'adk_app'
1161-
agent_config['source_packages'] = [temp_folder]
1127+
agent_config['source_packages'] = [f'agents/{app_name}', 'Dockerfile']
1128+
agent_config['image_spec'] = {} # Use the Dockerfile
11621129
agent_config['class_methods'] = _AGENT_ENGINE_CLASS_METHODS
11631130
agent_config['agent_framework'] = 'google-adk'
11641131

11651132
if not agent_engine_id:
1166-
agent_engine = client.agent_engines.create(config=agent_config)
1167-
click.secho(
1168-
f'✅ Created agent engine: {agent_engine.api_resource.name}',
1169-
fg='green',
1170-
)
1171-
_print_agent_engine_url(agent_engine.api_resource.name)
1172-
else:
1173-
if project and region and not agent_engine_id.startswith('projects/'):
1174-
agent_engine_id = f'projects/{project}/locations/{region}/reasoningEngines/{agent_engine_id}'
1175-
client.agent_engines.update(name=agent_engine_id, config=agent_config)
1176-
click.secho(f'✅ Updated agent engine: {agent_engine_id}', fg='green')
1177-
_print_agent_engine_url(agent_engine_id)
1133+
agent_engine = client.agent_engines.create()
1134+
agent_engine_id = agent_engine.api_resource.name
1135+
click.secho(f'Created a new agent engine: {agent_engine_id}', fg='green')
1136+
elif project and region and not agent_engine_id.startswith('projects/'):
1137+
agent_engine_id = f'projects/{project}/locations/{region}/reasoningEngines/{agent_engine_id}'
1138+
click.echo('Creating Dockerfile...')
1139+
create_dockerfile_for_agent_engine(agent_engine_id)
1140+
click.echo(f'Dockerfile created at {os.getcwd()}/Dockerfile.')
1141+
client.agent_engines.update(name=agent_engine_id, config=agent_config)
1142+
click.secho(f'Deployed to agent engine: {agent_engine_id}', fg='green')
1143+
_print_agent_engine_url(agent_engine_id)
11781144
finally:
11791145
click.echo(f'Cleaning up the temp folder: {temp_folder}')
1146+
os.chdir(original_cwd)
11801147
shutil.rmtree(agent_src_path)
1181-
if did_change_cwd:
1182-
os.chdir(original_cwd)
11831148

11841149

11851150
def to_gke(

0 commit comments

Comments
 (0)