Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,26 @@ coordinator = LlmAgent(
)
```

### 🚀 Deployment Options

Deploying the Agent Locally with Docker Container:

```bash
adk deploy docker --with_ui <agent-folder>
```

Deploying the Agent in Google Cloud (Cloud Run)

```bash
adk deploy cloud_run --with_ui <agent-folder>
```

You may set the following environment variables in adk command, or in a .env file instead.

```bash
adk deploy cloud_run --with_ui --env GOOGLE_GENAI_USE_VERTEXAI=1 <agent-folder>
```

### Development UI

A built-in development UI to help you test, evaluate, debug, and showcase your agent(s).
Expand Down
174 changes: 32 additions & 142 deletions src/google/adk/cli/cli_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,48 +19,13 @@
import subprocess
from typing import Final
from typing import Optional
from typing import Tuple

import click
from packaging.version import parse

_DOCKERFILE_TEMPLATE: Final[str] = """
FROM python:3.11-slim
WORKDIR /app

# Create a non-root user
RUN adduser --disabled-password --gecos "" myuser

# Switch to the non-root user
USER myuser

# Set up environment variables - Start
ENV PATH="/home/myuser/.local/bin:$PATH"

ENV GOOGLE_GENAI_USE_VERTEXAI=1
ENV GOOGLE_CLOUD_PROJECT={gcp_project_id}
ENV GOOGLE_CLOUD_LOCATION={gcp_region}

# Set up environment variables - End

# Install ADK - Start
RUN pip install google-adk=={adk_version}
# Install ADK - End

# Copy agent - Start

# Set permission
COPY --chown=myuser:myuser "agents/{app_name}/" "/app/agents/{app_name}/"

# Copy agent - End

# Install Agent Deps - Start
{install_agent_deps}
# Install Agent Deps - End

EXPOSE {port}

CMD adk {command} --port={port} {host_option} {service_option} {trace_to_cloud_option} {allow_origins_option} {a2a_option} "/app/agents"
"""
from .config.dockerfile_template import _DOCKERFILE_TEMPLATE
from .deployers.deployer_factory import DeployerFactory

_AGENT_ENGINE_APP_TEMPLATE: Final[str] = """
from vertexai.preview.reasoning_engines import AdkApp
Expand Down Expand Up @@ -98,52 +63,6 @@ def _resolve_project(project_in_option: Optional[str]) -> str:
return project


def _validate_gcloud_extra_args(
extra_gcloud_args: Optional[tuple[str, ...]], adk_managed_args: set[str]
) -> None:
"""Validates that extra gcloud args don't conflict with ADK-managed args.

This function dynamically checks for conflicts based on the actual args
that ADK will set, rather than using a hardcoded list.

Args:
extra_gcloud_args: User-provided extra arguments for gcloud.
adk_managed_args: Set of argument names that ADK will set automatically.
Should include '--' prefix (e.g., '--project').

Raises:
click.ClickException: If any conflicts are found.
"""
if not extra_gcloud_args:
return

# Parse user arguments into a set of argument names for faster lookup
user_arg_names = set()
for arg in extra_gcloud_args:
if arg.startswith('--'):
# Handle both '--arg=value' and '--arg value' formats
arg_name = arg.split('=')[0]
user_arg_names.add(arg_name)

# Check for conflicts with ADK-managed args
conflicts = user_arg_names.intersection(adk_managed_args)

if conflicts:
conflict_list = ', '.join(f"'{arg}'" for arg in sorted(conflicts))
if len(conflicts) == 1:
raise click.ClickException(
f"The argument {conflict_list} conflicts with ADK's automatic"
' configuration. ADK will set this argument automatically, so please'
' remove it from your command.'
)
else:
raise click.ClickException(
f"The arguments {conflict_list} conflict with ADK's automatic"
' configuration. ADK will set these arguments automatically, so'
' please remove them from your command.'
)


def _get_service_option_by_adk_version(
adk_version: str,
session_uri: Optional[str],
Expand Down Expand Up @@ -171,9 +90,10 @@ def _get_service_option_by_adk_version(
return f'--session_db_url={session_uri}' if session_uri else ''


def to_cloud_run(
def run(
*,
agent_folder: str,
provider: str,
project: Optional[str],
region: Optional[str],
service_name: str,
Expand All @@ -190,6 +110,8 @@ def to_cloud_run(
artifact_service_uri: Optional[str] = None,
memory_service_uri: Optional[str] = None,
a2a: bool = False,
provider_args: Tuple[str],
env: Tuple[str],
extra_gcloud_args: Optional[tuple[str, ...]] = None,
):
"""Deploys an agent to Google Cloud Run.
Expand All @@ -209,6 +131,7 @@ def to_cloud_run(

Args:
agent_folder: The folder (absolute path) containing the agent source code.
provider: Target deployment platform (cloud_run, docker, etc).
project: Google Cloud project id.
region: Google Cloud region.
service_name: The service name in Cloud Run.
Expand All @@ -223,10 +146,14 @@ def to_cloud_run(
session_service_uri: The URI of the session service.
artifact_service_uri: The URI of the artifact service.
memory_service_uri: The URI of the memory service.
provider_args: The arguments specific to cloud provider
env: The environment valriables provided
"""
app_name = app_name or os.path.basename(agent_folder)
mode = 'web' if with_ui else 'api_server'
trace_to_cloud_option = '--trace_to_cloud' if trace_to_cloud else ''

click.echo(f'Start generating Cloud Run source files in {temp_folder}')
click.echo(f'Start generating deployment files in {temp_folder}')

# remove temp_folder if exists
if os.path.exists(temp_folder):
Expand Down Expand Up @@ -258,15 +185,15 @@ def to_cloud_run(
gcp_region=region,
app_name=app_name,
port=port,
command='web' if with_ui else 'api_server',
command=mode,
install_agent_deps=install_agent_deps,
service_option=_get_service_option_by_adk_version(
adk_version,
session_service_uri,
artifact_service_uri,
memory_service_uri,
),
trace_to_cloud_option='--trace_to_cloud' if trace_to_cloud else '',
trace_to_cloud_option=trace_to_cloud_option,
allow_origins_option=allow_origins_option,
adk_version=adk_version,
host_option=host_option,
Expand All @@ -279,61 +206,24 @@ def to_cloud_run(
dockerfile_content,
)
click.echo(f'Creating Dockerfile complete: {dockerfile_path}')
click.echo(f'Deploying to {provider}...')

deployer = DeployerFactory.get_deployer(provider)
deployer.deploy(
agent_folder=agent_folder,
temp_folder=temp_folder,
service_name=service_name,
provider_args=provider_args,
env_vars=env,
project=project,
region=region,
port=port,
verbosity=verbosity,
extra_gcloud_args=extra_gcloud_args,
log_level=log_level,
)

# Deploy to Cloud Run
click.echo('Deploying to Cloud Run...')
region_options = ['--region', region] if region else []
project = _resolve_project(project)

# Build the set of args that ADK will manage
adk_managed_args = {'--source', '--project', '--port', '--verbosity'}
if region:
adk_managed_args.add('--region')

# Validate that extra gcloud args don't conflict with ADK-managed args
_validate_gcloud_extra_args(extra_gcloud_args, adk_managed_args)

# Build the command with extra gcloud args
gcloud_cmd = [
'gcloud',
'run',
'deploy',
service_name,
'--source',
temp_folder,
'--project',
project,
*region_options,
'--port',
str(port),
'--verbosity',
log_level.lower() if log_level else verbosity,
]

# Handle labels specially - merge user labels with ADK label
user_labels = []
extra_args_without_labels = []

if extra_gcloud_args:
for arg in extra_gcloud_args:
if arg.startswith('--labels='):
# Extract user-provided labels
user_labels_value = arg[9:] # Remove '--labels=' prefix
user_labels.append(user_labels_value)
else:
extra_args_without_labels.append(arg)

# Combine ADK label with user labels
all_labels = ['created-by=adk']
all_labels.extend(user_labels)
labels_arg = ','.join(all_labels)

gcloud_cmd.extend(['--labels', labels_arg])

# Add any remaining extra passthrough args
gcloud_cmd.extend(extra_args_without_labels)

subprocess.run(gcloud_cmd, check=True)
click.echo(f'Deployment to {provider} complete.')
finally:
click.echo(f'Cleaning up the temp folder: {temp_folder}')
shutil.rmtree(temp_folder)
Expand Down
Loading