{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ur8xi4C7S06n"
      },
      "outputs": [],
      "source": [
        "# Copyright 2026 Google LLC\n",
        "#\n",
        "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "#     https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JAPoU8Sm5E6e"
      },
      "source": [
        "# Deploy your containerized agent on Agent Runtime (prev. Agent Engine)\n",
        "\n",
        " <table align=\"left\">\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://www.gstatic.com/pantheon/images/bigquery/welcome_page/colab-logo.svg\" alt=\"Google Colaboratory logo\"><br> Open in Colab\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://console.cloud.google.com/agent-platform/colab/import/https:%2F%2Fraw.githubusercontent.com%2FGoogleCloudPlatform%2Fgenerative-ai%2Fmain%2Fagents%2Fagent_engine%2Ftutorial_deploy_your_containerised_agent.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://lh3.googleusercontent.com/JmcxdQi-qOpctIvWKgPtrzZdJJK-J3sWE1RsfjZNwshCFgE_9fULcNpuXYTilIR2hjwN\" alt=\"Google Cloud Colab Enterprise logo\"><br> Open in Colab Enterprise\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://console.cloud.google.com/agent-platform/workbench/instances?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/generative-ai/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://storage.googleapis.com/github-repo/workbench-icon.svg\" alt=\"Workbench logo\"><br> Open in Workbench\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\">\n",
        "      <img width=\"32px\" src=\"https://raw.githubusercontent.com/primer/octicons/refs/heads/main/icons/mark-github-24.svg\" alt=\"GitHub logo\"><br> View on GitHub\n",
        "    </a>\n",
        "  </td>\n",
        "</table>\n",
        "\n",
        "<div style=\"clear: both;\"></div>\n",
        "\n",
        "<p>\n",
        "<b>Share to:</b>\n",
        "\n",
        "<a href=\"https://www.linkedin.com/sharing/share-offsite/?url=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\" target=\"_blank\">\n",
        "  <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/8/81/LinkedIn_icon.svg\" alt=\"LinkedIn logo\">\n",
        "</a>\n",
        "\n",
        "<a href=\"https://bsky.app/intent/compose?text=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\" target=\"_blank\">\n",
        "  <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/7/7a/Bluesky_Logo.svg\" alt=\"Bluesky logo\">\n",
        "</a>\n",
        "\n",
        "<a href=\"https://twitter.com/intent/tweet?url=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\" target=\"_blank\">\n",
        "  <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/5/5a/X_icon_2.svg\" alt=\"X logo\">\n",
        "</a>\n",
        "\n",
        "<a href=\"https://reddit.com/submit?url=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\" target=\"_blank\">\n",
        "  <img width=\"20px\" src=\"https://redditinc.com/hubfs/Reddit%20Inc/Brand/Reddit_Logo.png\" alt=\"Reddit logo\">\n",
        "</a>\n",
        "\n",
        "<a href=\"https://www.facebook.com/sharer/sharer.php?u=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_deploy_your_containerised_agent.ipynb\" target=\"_blank\">\n",
        "  <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg\" alt=\"Facebook logo\">\n",
        "</a>\n",
        "</p>\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "84f0f73a0f76"
      },
      "source": [
        "| Author |\n",
        "| --- |\n",
        "| [Hemanand Rajendran](https://github.com/heyanand) |"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tvgnzT1CKxrO"
      },
      "source": [
        "## Overview\n",
        "\n",
        "[Agent Runtime](https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/runtime) (previously Agent Engine) offers a managed runtime environment designed for deploying, running, and scaling AI agents effectively. In a standard deployment, the platform automatically bundles your source code and package dependencies into a generic container image.\n",
        "\n",
        "However, many enterprise workloads require complete ownership over the runtime environment. To support this requirement, Agent Runtime provides [**Bring Your Own Container (BYOC)**](https://docs.cloud.google.com/gemini-enterprise-agent-platform/build/runtime/setup#byoc) capability, allowing developers to bypass the platform's built-in containerization step and deploy pre-built custom container images.\n",
        "\n",
        "This notebook outlines the end-to-end process for containerizing an agent built with the Google Agent Development Kit (ADK), configuring the necessary Google Cloud platform prerequisites, and deploying it to Agent Runtime using the SDK."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "61RBz8LLbxCR"
      },
      "source": [
        "## Get started"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "No17Cw5hgx12"
      },
      "source": [
        "### Install the Python SDK\n",
        "This step install the google-cloud-aiplatform package containing the modules for agent engine and the ADK framework.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tFy3H3aPgx12"
      },
      "outputs": [],
      "source": [
        "%pip install \"google-cloud-aiplatform[agent_engines,adk]>=1.144\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dmWOrTJ3gx13"
      },
      "source": [
        "### Authenticate your notebook environment\n",
        "\n",
        "If you are running this notebook in **Google Colab**, run the cell below to authenticate your account."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NyKGtVQjgx13"
      },
      "outputs": [],
      "source": [
        "import sys\n",
        "\n",
        "if \"google.colab\" in sys.modules:\n",
        "    from google.colab import auth\n",
        "\n",
        "    auth.authenticate_user()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DF4l8DTdWgPY"
      },
      "source": [
        "### Set Google Cloud project information\n",
        "\n",
        "To get started using Agent Platform, you must have an existing Google Cloud project and [enable the Agent Platform API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com).\n",
        "\n",
        "Learn more about [setting up a project](https://docs.cloud.google.com/resource-manager/docs/creating-managing-projects) and a [development environment](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "Nqwi-5ufWp_B"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "\n",
        "import vertexai\n",
        "\n",
        "# fmt: off\n",
        "PROJECT_ID = \"\"  # @param {type: \"string\", placeholder: \"[your-project-id]\", isTemplate: true}\n",
        "LOCATION = \"\" # @param {type: \"string\", placeholder: \"us-central1\", isTemplate: true}\n",
        "MODEL = \"gemini-3.1-flash-lite\" # @param {type: \"string\", placeholder: \"[gemini-3.1-flash-lite]\", isTemplate: true}\n",
        "MODEL_REGION = \"global\" # @param {type: \"string\", placeholder: \"[global]\", isTemplate: true}\n",
        "# fmt: on\n",
        "\n",
        "if not PROJECT_ID or PROJECT_ID == \"[your-project-id]\":\n",
        "    PROJECT_ID = str(os.environ.get(\"GOOGLE_CLOUD_PROJECT\"))\n",
        "if not LOCATION:\n",
        "    LOCATION = os.environ.get(\"GOOGLE_CLOUD_REGION\")\n",
        "\n",
        "\n",
        "client = vertexai.Client(\n",
        "    project=PROJECT_ID,\n",
        "    location=LOCATION,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EdvJRUWRNGHE"
      },
      "source": [
        "## Source Files Setup"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zJaEif2iJ7ai"
      },
      "source": [
        "Create the directory for housing the agent code and the terraform templates"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Lit4Af5tJxys"
      },
      "outputs": [],
      "source": [
        "! rm -rf ./weather-agent-code && mkdir ./weather-agent-code && mkdir ./weather-agent-code/weather_agent # location for the agent code"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l-oSwnwCmymI"
      },
      "source": [
        "The following code defines the logic for a weather-retrieval agent and saves it to a file named `agent.py`.\n",
        "\n",
        "Here's the breakdown:\n",
        "<br>1. **Tool Definition (get_temperature)**: This function acts as a 'tool' for the agent. In this example, it uses the random library to simulate fetching a temperature for a specific location. It includes a docstring that the LLM uses to understand when and how to call the function.\n",
        "<br>2. **Agent Initialization**: It creates an instance of the Agent class from the google.adk library.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1720b1448a6c"
      },
      "outputs": [],
      "source": [
        "# Create a config.json file to store the configurations\n",
        "import json\n",
        "\n",
        "config_json = {\n",
        "    \"PROJECT_ID\": f\"{PROJECT_ID}\",\n",
        "    \"MODEL\": f\"{MODEL}\",\n",
        "    \"MODEL_REGION\": f\"{MODEL_REGION}\",\n",
        "    \"LOCATION\": f\"{LOCATION}\",\n",
        "}\n",
        "\n",
        "json.dump(config_json, open(\"./weather-agent-code/weather_agent/config.json\", \"w\"))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "il0k7btDKcmX"
      },
      "outputs": [],
      "source": [
        "%%writefile ./weather-agent-code/weather_agent/agent.py\n",
        "import json\n",
        "import random\n",
        "from google.adk.agents import Agent\n",
        "from google.adk.models.google_llm import Gemini\n",
        "\n",
        "from functools import cached_property\n",
        "from google.genai import Client\n",
        "\n",
        "\n",
        "llm_config = json.load(open(\"weather_agent/config.json\"))\n",
        "\n",
        "PROJECT_ID = llm_config[\"PROJECT_ID\"]\n",
        "MODEL = llm_config[\"MODEL\"]\n",
        "MODEL_REGION = llm_config[\"MODEL_REGION\"]\n",
        "\n",
        "# Overriding Gemini class to set location to global for models with Global endpoint.\n",
        "class GlobalGemini(Gemini):\n",
        "  @cached_property\n",
        "  def api_client(self) -> Client:\n",
        "    return Client(vertexai=True, location=\"global\")\n",
        "\n",
        "def get_temperature(place: str) -> str:\n",
        "    '''Returns the current temperature of a given place.\n",
        "\n",
        "    Args:\n",
        "        place: The name of the city or location.\n",
        "\n",
        "    Returns:\n",
        "        str: A string describing the temperature.\n",
        "    '''\n",
        "    # In a real scenario, this would call a weather API.\n",
        "    # For this sample, we'll return a simulated temperature.\n",
        "\n",
        "    temp = random.randint(-10, 40)\n",
        "    return f\"The current temperature in {place} is {temp}°C.\"\n",
        "\n",
        "llm_model = GlobalGemini(model=MODEL) if MODEL_REGION == \"global\" else Gemini(model=MODEL)\n",
        "\n",
        "root_agent = Agent(\n",
        "    model=llm_model,\n",
        "    name='weather_agent',\n",
        "    description='An agent that provides temperature information for locations.',\n",
        "    instruction='You are a helpful assistant that can provide the current temperature for any given place using the get_temperature tool.',\n",
        "    tools=[get_temperature],\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CtCtMLJBLgs_"
      },
      "outputs": [],
      "source": [
        "# Init file\n",
        "%%writefile ./weather-agent-code/weather_agent/__init__.py"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pxmWlOb-nP_y"
      },
      "source": [
        "The following code included in the `main.py` sets up a FastAPI web server to act as a runtime for the AI agent. Here's a breakdown of what it does:\n",
        "\n",
        "1. **FastAPI Setup**: It defines a\n",
        "standard web application with a QueryRequest schema to handle incoming user inputs and specify which agent method to call<br>\n",
        "2. **ADK Integration**: It wraps your root_agent using agent_engines.AdkApp, which provides the standard interface for interacting with agents built using the Agent Development Kit (ADK).<br>\n",
        "3. **Endpoint** - /api/reasoning_engine: This is a standard POST endpoint that receives a query, executes the agent's logic, and returns a final JSON response.<br>\n",
        "4. **Endpoint** - /api/stream_reasoning_engine: This handles streaming responses, allowing you to see the agent's thought process or partial answers in real-time as they are generated.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "bTUA7OiuMBQW"
      },
      "outputs": [],
      "source": [
        "%%writefile ./weather-agent-code/main.py\n",
        "import inspect\n",
        "import json\n",
        "import logging\n",
        "import os\n",
        "from typing import Any, Dict, Optional\n",
        "import uvicorn\n",
        "import vertexai\n",
        "from weather_agent.agent import root_agent\n",
        "from fastapi import FastAPI, encoders, responses, Request\n",
        "from vertexai import agent_engines\n",
        "\n",
        "app = FastAPI()\n",
        "\n",
        "config_json = json.load(open(\"weather_agent/config.json\"))\n",
        "PROJECT_ID = config_json[\"PROJECT_ID\"]\n",
        "LOCATION = config_json[\"LOCATION\"]\n",
        "MODEL_REGION = config_json[\"MODEL_REGION\"]\n",
        "\n",
        "vertexai.init(project=PROJECT_ID, location=MODEL_REGION)\n",
        "adk_app = agent_engines.AdkApp(agent=root_agent)\n",
        "\n",
        "def _encode_chunk_to_json(chunk):\n",
        "  try:\n",
        "    json_chunk = encoders.jsonable_encoder(chunk)\n",
        "    return json.dumps(json_chunk) + \"\\n\"\n",
        "  except Exception:\n",
        "    logging.exception(\"Failed to encode chunk\")\n",
        "    return None\n",
        "\n",
        "async def json_generator(output):\n",
        "  async for chunk in output:\n",
        "    encoded_chunk = _encode_chunk_to_json(chunk)\n",
        "    if encoded_chunk is None:\n",
        "      break\n",
        "    yield encoded_chunk\n",
        "\n",
        "async def _invoke_callable_or_raise(invocation_callable, invocation_payload):\n",
        "  if inspect.iscoroutinefunction(invocation_callable):\n",
        "    return await invocation_callable(**invocation_payload)\n",
        "  else:\n",
        "    return invocation_callable(**invocation_payload)\n",
        "\n",
        "@app.post(\"/api/reasoning_engine\")\n",
        "async def query(request: Request) -> responses.JSONResponse:\n",
        "    request_json = await request.json()\n",
        "    class_method = request_json.get(\"class_method\")\n",
        "    input_val = request_json.get(\"input\")\n",
        "    method = getattr(adk_app, class_method)\n",
        "    output = await _invoke_callable_or_raise(method, input_val or {})\n",
        "    try:\n",
        "      json_serialized_content = encoders.jsonable_encoder({\"output\": output})\n",
        "    except ValueError as encoding_error:\n",
        "      logging.exception(\"Failed to encode response\")\n",
        "      raise encoding_error\n",
        "    return responses.JSONResponse(content=json_serialized_content)\n",
        "\n",
        "@app.post(\"/api/stream_reasoning_engine\")\n",
        "async def stream_query(request: Request) -> responses.StreamingResponse:\n",
        "    request_json = await request.json()\n",
        "    class_method = request_json.get(\"class_method\")\n",
        "    input_val = request_json.get(\"input\")\n",
        "    method = getattr(adk_app, class_method)\n",
        "    output = await _invoke_callable_or_raise(method, input_val or {})\n",
        "    return responses.StreamingResponse(\n",
        "        content=json_generator(output),\n",
        "        media_type=\"application/json\",\n",
        "    )\n",
        "\n",
        "if __name__ == \"__main__\":\n",
        "    uvicorn.run(app, host=\"0.0.0.0\", port=int(os.environ.get(\"PORT\", 8080)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OyCLNaCypI8o"
      },
      "source": [
        "Let's create the file containing the list of all dependencies needed to containerize the agent."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "67bfc84c"
      },
      "outputs": [],
      "source": [
        "%%writefile ./weather-agent-code/requirements.txt\n",
        "fastapi\n",
        "uvicorn\n",
        "vertexai\n",
        "google-cloud-aiplatform[agent_engines,adk]>=1.144\n",
        "pydantic"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zbMvdj6LnBtn"
      },
      "source": [
        "The following cell creates a `Dockerfile`, which contains the instructions to build a container image for the agent."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Sx6_y1nxciyP"
      },
      "outputs": [],
      "source": [
        "%%writefile ./weather-agent-code/Dockerfile\n",
        "FROM python:3.11-slim\n",
        "\n",
        "WORKDIR /app\n",
        "\n",
        "COPY weather_agent/ /app/weather_agent/\n",
        "COPY main.py .\n",
        "COPY requirements.txt .\n",
        "RUN pip install -r requirements.txt\n",
        "\n",
        "CMD [\"sh\", \"-c\", \"uvicorn main:app --host 0.0.0.0 --port $PORT\"]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sWQeYtmscS1h"
      },
      "source": [
        "## Container Build"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iguxWX5Af43K"
      },
      "source": [
        "Let's create a repository in Google Artifact Registry to save the container images containing the agent code."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wtmkqsmxcuer"
      },
      "outputs": [],
      "source": [
        "!gcloud auth login"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "P6malbJ0c2Xr"
      },
      "outputs": [],
      "source": [
        "!gcloud services enable cloudbuild.googleapis.com compute.googleapis.com artifactregistry.googleapis.com --project=$PROJECT_ID"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7qSEs6R2PpMR"
      },
      "outputs": [],
      "source": [
        "REPOSITORY_NAME = \"agents-repo\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Nt1BZ9VGc5Aw"
      },
      "outputs": [],
      "source": [
        "!gcloud artifacts repositories create $REPOSITORY_NAME \\\n",
        "    --project=$PROJECT_ID \\\n",
        "    --repository-format=docker \\\n",
        "    --location=$LOCATION \\\n",
        "    --description=\"Docker repository\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ID3jq5u6XTRL"
      },
      "source": [
        "Let's validate if the repository has been successfully created."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WJOf-k-7e3Zm"
      },
      "outputs": [],
      "source": [
        "!gcloud artifacts repositories list --project=$PROJECT_ID --location=$LOCATION"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Xib3U7g_KbR5"
      },
      "outputs": [],
      "source": [
        "PROJECT_NUMBER = !gcloud projects describe $PROJECT_ID --format=\"value(projectNumber)\"\n",
        "PROJECT_NUMBER = PROJECT_NUMBER[0]\n",
        "PROJECT_NUMBER"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BP1HhVQzvX0L"
      },
      "source": [
        "Grant the Artifact Registry Writer role (`roles/artifactregistry.writer`) and Storage Object Viewer (`roles/storage.objectViewer`) to the project's **Default Compute Service Account**. This permission is necessary so that when we build the agent container (using Cloud Build or local tools), the service account has the authority to **upload** (push) the resulting Docker image to the Artifact Registry repository."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XrXq8di7Kfsc"
      },
      "outputs": [],
      "source": [
        "!gcloud projects add-iam-policy-binding $PROJECT_ID \\\n",
        "    --member=\"serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com\" \\\n",
        "    --role=\"roles/artifactregistry.writer\" \\\n",
        "    --condition=None"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nnFeMNQKbpQI"
      },
      "outputs": [],
      "source": [
        "!gcloud projects add-iam-policy-binding $PROJECT_NUMBER \\\n",
        "    --member=\"serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com\" \\\n",
        "    --role=\"roles/storage.objectViewer\"  \\\n",
        "    --condition=None"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8UBSz__GX7Sv"
      },
      "source": [
        "Let's grant the Artifact Registry permissions to the Reasoning Engine Service Agent and the AI Platform Service Agent"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QN4teU-pX6zJ"
      },
      "outputs": [],
      "source": [
        "!gcloud projects add-iam-policy-binding $PROJECT_NUMBER \\\n",
        "    --member=\"serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com\" \\\n",
        "    --role=\"roles/artifactregistry.reader\"  --condition=None\n",
        "!gcloud projects add-iam-policy-binding $PROJECT_NUMBER \\\n",
        "    --member=\"serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform.iam.gserviceaccount.com\" \\\n",
        "    --role=\"roles/artifactregistry.reader\"  --condition=None"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ixDDfad4wLT6"
      },
      "source": [
        "Let's use the Dockerfile to build the container image using Cloud Build. At the end of the build process, the container image is pushed to the Artifact Registry repository created earlier."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2eecf992"
      },
      "outputs": [],
      "source": [
        "!gcloud config set project {PROJECT_ID}\n",
        "!gcloud builds submit \\\n",
        "    --project={PROJECT_ID} \\\n",
        "    --region={LOCATION} \\\n",
        "    --tag {LOCATION}-docker.pkg.dev/{PROJECT_ID}/{REPOSITORY_NAME}/weather-agent-image:latest \\\n",
        "    ./weather-agent-code"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e43229f3ad4f"
      },
      "source": [
        "## Deploy the agent with SDK"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eKCr1evSr885"
      },
      "source": [
        "This code deploys your custom containerized agent to Agent Runtime. Here's the breakdown:\n",
        "\n",
        "\n",
        "1. **client.agent_engines.create**: This method registers and deploys the agent on Google Cloud.\n",
        "2. **display_name & description**: Sets the metadata for the agent as it will appear in the Google Cloud Console.\n",
        "3. **container_spec**: This is the 'Bring Your Own Container' (BYOC) part. It points to the Docker image you previously built and pushed to Artifact Registry using the image_uri.\n",
        "4. **class_methods**: This list defines the API interface for your agent. It maps standard agent actions (like creating sessions or streaming queries) to the methods available in your code, supporting both synchronous and asynchronous modes.\n",
        "5. **agent_framework**: Setting this to 'google-adk' tells the platform that your agent is built using the Google Agent Development Kit, which enables features like the playground in the Google Cloud Platform console.\n",
        "\n",
        "\n",
        "After execution, the `remote_agent` holds a reference to the deployed agent, allowing us to interact with it via the SDK."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "cf93d5f0ce00"
      },
      "outputs": [],
      "source": [
        "remote_agent = client.agent_engines.create(\n",
        "    config={\n",
        "        \"display_name\": \"byoc_weather_agent\",\n",
        "        \"description\": \"byoc testing with example agent\",\n",
        "        \"container_spec\": {\n",
        "            \"image_uri\": f\"{LOCATION}-docker.pkg.dev/{PROJECT_ID}/{REPOSITORY_NAME}/weather-agent-image:latest\"\n",
        "        },\n",
        "        \"class_methods\": [\n",
        "            # For convenience to interact with the agent through the Python SDK\n",
        "            # https://docs.cloud.google.com/gemini-enterprise-agent-platform/scale/runtime/use-an-adk-agent\n",
        "            {\"api_mode\": \"\", \"name\": \"get_session\"},\n",
        "            {\"api_mode\": \"\", \"name\": \"list_sessions\"},\n",
        "            {\"api_mode\": \"\", \"name\": \"create_session\"},\n",
        "            {\"api_mode\": \"\", \"name\": \"delete_session\"},\n",
        "            {\"api_mode\": \"async\", \"name\": \"async_get_session\"},\n",
        "            {\"api_mode\": \"async\", \"name\": \"async_list_sessions\"},\n",
        "            {\"api_mode\": \"async\", \"name\": \"async_create_session\"},\n",
        "            {\"api_mode\": \"async\", \"name\": \"async_delete_session\"},\n",
        "            {\"api_mode\": \"async\", \"name\": \"async_add_session_to_memory\"},\n",
        "            {\"api_mode\": \"async\", \"name\": \"async_search_memory\"},\n",
        "            {\"api_mode\": \"stream\", \"name\": \"stream_query\"},\n",
        "            {\"api_mode\": \"async_stream\", \"name\": \"async_stream_query\"},\n",
        "            {\"api_mode\": \"async_stream\", \"name\": \"streaming_agent_run_with_events\"},\n",
        "        ],\n",
        "        \"agent_framework\": \"google-adk\",  # For usage through the playground on Google Cloud console\n",
        "    },\n",
        ")\n",
        "remote_agent"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jG6w_uzAwv9d"
      },
      "source": [
        "## Query"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1YQUXBIHjy_b"
      },
      "source": [
        "Let's test if the agent is responding to our queries. We will be querying the stream_query API of the remote agent."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DXlzhiFojUBQ"
      },
      "outputs": [],
      "source": [
        "import json\n",
        "\n",
        "import requests\n",
        "from google import auth as google_auth\n",
        "from google.auth.transport import requests as google_requests\n",
        "\n",
        "def get_identity_token():\n",
        "    credentials, _ = google_auth.default()\n",
        "    auth_request = google_requests.Request()\n",
        "    credentials.refresh(auth_request)\n",
        "    return credentials.token\n",
        "\n",
        "\n",
        "response = requests.post(\n",
        "    f\"https://{LOCATION}-aiplatform.googleapis.com/v1/{remote_agent.api_resource.name}:streamQuery\",\n",
        "    headers={\n",
        "        \"Content-Type\": \"application/json\",\n",
        "        \"Authorization\": f\"Bearer {get_identity_token()}\",\n",
        "    },\n",
        "    data=json.dumps(\n",
        "        {\n",
        "            \"class_method\": \"async_stream_query\",\n",
        "            \"input\": {\n",
        "                \"user_id\": \"test_user_123\",\n",
        "                \"message\": \"What is the temperature in New Delhi?\",\n",
        "            },\n",
        "        }\n",
        "    ),\n",
        "    stream=True,\n",
        ")\n",
        "for chunk in response.iter_content(chunk_size=8192):\n",
        "    print(chunk)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2a4e033321ad"
      },
      "source": [
        "## Cleaning up\n",
        "\n",
        "To clean up all Google Cloud resources used in this project, you can [delete the Google Cloud project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects) you used for the tutorial.\n",
        "\n",
        "Otherwise, you can delete the individual resources you created in this tutorial by running the cell below."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "18914706"
      },
      "outputs": [],
      "source": [
        "# 1. Delete the Reasoning Engines (Agents)\n",
        "# Note: We retrieve the list first to ensure we delete the ones created in this session\n",
        "try:\n",
        "    page_size = 100\n",
        "    reasoning_engines = client.agent_engines.list()\n",
        "    for engine in reasoning_engines:\n",
        "        if (\n",
        "            \"weather_agent\" in engine.api_resource.display_name\n",
        "            or \"MetadataAgent\" in engine.api_resource.display_name\n",
        "        ):\n",
        "            print(f\"Deleting Reasoning Engine: {engine.api_resource.name}\")\n",
        "            engine.delete(force=True)\n",
        "except Exception as e:\n",
        "    print(f\"Error deleting reasoning engines: {e}\")\n",
        "\n",
        "# 2. Delete the Artifact Registry Repository\n",
        "!gcloud artifacts repositories delete $REPOSITORY_NAME --location=$LOCATION --project=$PROJECT_ID --quiet\n",
        "\n",
        "# 3. Clean up local directories\n",
        "!rm -rf ./weather-agent-code ./tenant_sa_setup\n",
        "\n",
        "print(\"Cleanup of specific resources completed.\")"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "tutorial_deploy_your_containerised_agent.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
