{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "id": "f705f4be70e9"
      },
      "outputs": [],
      "source": [
        "# Copyright 2025 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": "40503295d0ef"
      },
      "source": [
        "# Constructing a LangGraphAgent"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "56af7aaf9a84"
      },
      "source": [
        "<table align=\"left\">\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/generative-ai/blob/main/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-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/vertex-ai/colab/import/https:%2F%2Fraw.githubusercontent.com%2FGoogleCloudPlatform%2Fgenerative-ai%2Fmain%2Fgemini%2Fagents%2Fgenai-experience-concierge%2Flanggraph-demo%2Fbackend%2Fnotebooks%2Flanggraph-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/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/generative-ai/main/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-agent.ipynb\">\n",
        "      <img src=\"https://www.gstatic.com/images/branding/gcpiconscolors/vertexai/v1/32px.svg\" alt=\"Vertex AI logo\"><br> Open in Vertex AI Workbench\n",
        "    </a>\n",
        "  </td>\n",
        "  <td style=\"text-align: center\">\n",
        "    <a href=\"https://github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-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",
        "<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/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-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/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-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/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-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/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-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/gemini/agents/genai-experience-concierge/langgraph-demo/backend/notebooks/langgraph-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>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dbfe0a3c85ab"
      },
      "source": [
        "| | |\n",
        "|-|-|\n",
        "|Author(s) | [Pablo Gaeta](https://github.com/pablofgaeta) |"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "80df1bbc5c57"
      },
      "source": [
        "## Overview\n",
        "\n",
        "This notebook demonstrates the necessary code to transform any LangGraph graph into a `LangGraphAgent` using the `concierge.langgraph_server` module.\n",
        "\n",
        "This is a useful interactive way to develop agents before deploying them on a server."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "faca4bb1709f"
      },
      "source": [
        "## Import dependencies"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "id": "d376d236bf4b"
      },
      "outputs": [],
      "source": [
        "import asyncio\n",
        "from typing import TypedDict\n",
        "import uuid\n",
        "\n",
        "from concierge.langgraph_server import langgraph_agent, schemas\n",
        "from langgraph import graph, types"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fd56ca055bc6"
      },
      "source": [
        "## Define graph state schema and simple graph implementation"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "9c8f32748aa9"
      },
      "outputs": [],
      "source": [
        "# State schema\n",
        "\n",
        "\n",
        "class State(TypedDict, total=False):\n",
        "    id: str\n",
        "\n",
        "\n",
        "# Node functions\n",
        "\n",
        "\n",
        "async def set_id(state: State):\n",
        "    new_id = state.get(\"id\")\n",
        "    assert new_id is not None, \"must set ID\"\n",
        "\n",
        "    await asyncio.sleep(1)\n",
        "    return types.Command(update=State(id=new_id), goto=\"REVERSE_ID\")\n",
        "\n",
        "\n",
        "async def reverse_id(state: State):\n",
        "    new_id = state.get(\"id\")\n",
        "    assert new_id is not None, \"ID must be set before reversing\"\n",
        "\n",
        "    await asyncio.sleep(1)\n",
        "    return types.Command(update=State(id=new_id[::-1]))\n",
        "\n",
        "\n",
        "# Graph\n",
        "\n",
        "state_graph = graph.StateGraph(state_schema=State)\n",
        "state_graph.add_node(\"SET_ID\", set_id)\n",
        "state_graph.add_node(\"REVERSE_ID\", reverse_id)\n",
        "state_graph.set_entry_point(\"SET_ID\");"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7cb6adb34f11"
      },
      "source": [
        "## Build a _stateful_ agent"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "id": "a49509c54b29"
      },
      "outputs": [],
      "source": [
        "basic_agent = langgraph_agent.LangGraphAgent(\n",
        "    state_graph=state_graph,\n",
        "    checkpointer_config=schemas.MemoryBackendConfig(),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "01c2cd4651a5"
      },
      "source": [
        "**Test**: Run a query in debug stream mode"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {
        "id": "7b0721483f62"
      },
      "outputs": [],
      "source": [
        "async for chunk in basic_agent.stream(\n",
        "    input=State(id=\"hello\"),\n",
        "    config={\"configurable\": {\"thread_id\": uuid.uuid4().hex}},\n",
        "    stream_mode=\"debug\",\n",
        "):\n",
        "    print(chunk)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6cee145a454e"
      },
      "source": [
        "**Test**: Run a query in values mode (full state returned each time)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "id": "c368bf3c6b63"
      },
      "outputs": [],
      "source": [
        "async for chunk in basic_agent.stream(\n",
        "    input=State(id=\"hello\"),\n",
        "    config={\"configurable\": {\"thread_id\": uuid.uuid4().hex}},\n",
        "    stream_mode=\"values\",\n",
        "):\n",
        "    print(\"complete state\", \"=\", chunk)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b42173c19c6b"
      },
      "source": [
        "**Test**: Run a query in updates mode (only state updates returned by each node)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "id": "5702fb63eb29"
      },
      "outputs": [],
      "source": [
        "async for chunk in basic_agent.stream(\n",
        "    input=State(id=\"hello\"),\n",
        "    config={\"configurable\": {\"thread_id\": uuid.uuid4().hex}},\n",
        "    stream_mode=\"updates\",\n",
        "):\n",
        "    print(\"updates\", \"->\", chunk)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e65e2a213b30"
      },
      "source": [
        "**Test**: Run a query with a list of stream modes (yields stream mode and chunk data)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "id": "c30db9a8a393"
      },
      "outputs": [],
      "source": [
        "async for stream_mode, chunk in basic_agent.stream(\n",
        "    input=State(id=\"hello\"),\n",
        "    config={\"configurable\": {\"thread_id\": uuid.uuid4().hex}},\n",
        "    stream_mode=[\"updates\"],\n",
        "):\n",
        "    print(stream_mode, \"->\", chunk)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "34b3574a9425"
      },
      "source": [
        "**Test**: Attempt a stateless query, fails since checkpointer requires thread ID. To run stateless queries, must build a stateless agent like the example in the next section."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "id": "b91f9f8497a2"
      },
      "outputs": [],
      "source": [
        "async for stream_mode, chunk in basic_agent.stream(\n",
        "    input=State(id=\"hello\"),\n",
        "    config={\"configurable\": {\"thread_id\": uuid.uuid4().hex}},\n",
        "    stream_mode=[\"updates\"],\n",
        "):\n",
        "    print(stream_mode, \"->\", chunk)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c78f7af99ca9"
      },
      "source": [
        "## Build a _stateless_ agent"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "id": "693b95986c1a"
      },
      "outputs": [],
      "source": [
        "stateless_basic_agent = langgraph_agent.LangGraphAgent(\n",
        "    state_graph=state_graph,\n",
        "    checkpointer_config=None,  # no checkpointer means it will only work with stateless requests\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "397beba9ee29"
      },
      "source": [
        "**Test**: Run stateless run without threads"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "id": "9f02207bea72"
      },
      "outputs": [],
      "source": [
        "async for chunk in stateless_basic_agent.stream(\n",
        "    input=State(id=\"hello\"),\n",
        "    stream_mode=\"updates\",\n",
        "):\n",
        "    print(\"complete state\", \"=\", chunk)"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "name": "langgraph-agent.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
