{ "cells": [ { "cell_type": "markdown", "id": "8bcd1a3d-7c50-4f58-be4e-1ed654aa33be", "metadata": {}, "source": [ "# Visualization\n", "\n", "This notebook walks through how to visualize the graphs you create. For this example we will use a prebuilt graph, but this works with ANY graphs." ] }, { "cell_type": "markdown", "id": "e130cf70-a30e-47d7-8fd5-464f1a92e374", "metadata": {}, "source": [ "## Set up the chat model and tools\n", "\n", "Here we will define the chat model and tools that we want to use.\n", "Importantly, this model MUST support OpenAI function calling." ] }, { "cell_type": "code", "execution_count": 1, "id": "efb7e3c0-c63f-40f6-93ce-19681d650fc2", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:30.217991Z", "start_time": "2024-04-19T11:25:28.531482Z" } }, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", "from langgraph.prebuilt import chat_agent_executor" ] }, { "cell_type": "code", "execution_count": 2, "id": "a7025f33-3160-41cf-868b-17ebc916fb1d", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:32.431922Z", "start_time": "2024-04-19T11:25:32.168821Z" } }, "outputs": [], "source": [ "# Optional to not need .env\n", "# import os\n", "# os.environ['TAVILY_API_KEY'] = 'foo'\n", "# os.environ['OPENAI_API_KEY'] = 'foo'\n", "\n", "tools = [TavilySearchResults(max_results=1)]\n", "model = ChatOpenAI()" ] }, { "cell_type": "markdown", "id": "43064805-2ac9-4b5a-850c-a68dd7282350", "metadata": { "ExecuteTime": { "end_time": "2024-04-18T12:18:30.586216Z", "start_time": "2024-04-18T12:18:30.469100Z" } }, "source": [ "## Create executor\n", "\n", "We can now use the high level interface to create the executor" ] }, { "cell_type": "code", "execution_count": 3, "id": "32b4ae66-f667-4a8b-a602-503fd0effcd9", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:36.231169Z", "start_time": "2024-04-19T11:25:36.098462Z" } }, "outputs": [], "source": [ "app = chat_agent_executor.create_tool_calling_executor(model, tools)" ] }, { "cell_type": "markdown", "id": "f4fc9378-b141-4b65-b86c-3afba77f7161", "metadata": { "ExecuteTime": { "end_time": "2024-04-18T12:18:30.605220Z", "start_time": "2024-04-18T12:18:30.587191Z" } }, "source": [ "## Ascii\n", "\n", "We can easily visualize this graph in ascii" ] }, { "cell_type": "code", "execution_count": 4, "id": "ca9b980d-1f0a-4286-9157-a870e3d55134", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:37.303260Z", "start_time": "2024-04-19T11:25:37.273032Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " +-----------+ \n", " | __start__ | \n", " +-----------+ \n", " * \n", " * \n", " * \n", " +-------+ \n", " | agent | \n", " +-------+ \n", " * .. \n", " ** .. \n", " * . \n", "+--------+ +---------+ \n", "| action | | __end__ | \n", "+--------+ +---------+ \n" ] } ], "source": [ "app.get_graph().print_ascii()" ] }, { "cell_type": "markdown", "id": "edcd9ad2", "metadata": { "ExecuteTime": { "end_time": "2024-04-18T12:18:30.629307Z", "start_time": "2024-04-18T12:18:30.609323Z" } }, "source": [ "## Mermaid\n", "\n", "We can also convert a graph class into Mermaid syntax." ] }, { "cell_type": "code", "execution_count": 5, "id": "66007b2d", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:38.733126Z", "start_time": "2024-04-19T11:25:38.726838Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "%%{init: {'flowchart': {'curve': 'linear'}}}%%\n", "graph TD;\n", "\t__start__[__start__]:::startclass;\n", "\t__end__[__end__]:::endclass;\n", "\tagent([agent]):::otherclass;\n", "\taction([action]):::otherclass;\n", "\t__start__ --> agent;\n", "\taction --> agent;\n", "\tagent -. continue .-> action;\n", "\tagent -. end .-> __end__;\n", "\tclassDef startclass fill:#ffdfba;\n", "\tclassDef endclass fill:#baffc9;\n", "\tclassDef otherclass fill:#fad7de;\n", "\n" ] } ], "source": [ "print(app.get_graph().draw_mermaid())" ] }, { "cell_type": "markdown", "id": "324d40ed-b665-4416-88f1-5df161546cd9", "metadata": { "ExecuteTime": { "end_time": "2024-04-18T12:18:30.629548Z", "start_time": "2024-04-18T12:18:30.615432Z" } }, "source": [ "## PNG\n", "\n", "If prefered, we could render the Graph into a `.png`. Here we could use three options:\n", "\n", "- Using graphviz (which requires `pip install graphviz`)\n", "- Using Mermaid + Pyppeteer (requires `pip install pyppeteer`)\n", "- Using Mermaid.ink API (does not require additional packages)" ] }, { "cell_type": "code", "execution_count": 6, "id": "6b7dc713", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:40.358604Z", "start_time": "2024-04-19T11:25:40.351636Z" }, "collapsed": false }, "outputs": [], "source": [ "from IPython.display import display, HTML\n", "import base64\n", "\n", "\n", "def display_image(image_bytes: bytes, width=300):\n", " decoded_img_bytes = base64.b64encode(image_bytes).decode(\"utf-8\")\n", " html = f''\n", " display(HTML(html))" ] }, { "cell_type": "markdown", "id": "d821b2f6", "metadata": { "ExecuteTime": { "end_time": "2024-04-18T12:18:30.629629Z", "start_time": "2024-04-18T12:18:30.620092Z" } }, "source": [ "### Using Graphviz" ] }, { "cell_type": "code", "execution_count": 7, "id": "d4234400-75cd-4b13-aeff-828f7fb68ab1", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:42.057704Z", "start_time": "2024-04-19T11:25:42.019017Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: install in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (1.3.5)\n", "Collecting pygraphviz\n", " Using cached pygraphviz-1.12-cp311-cp311-macosx_13_0_arm64.whl\n", "Installing collected packages: pygraphviz\n", "Successfully installed pygraphviz-1.12\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "%%capture --no-stderr\n", "%pip install pygraphviz" ] }, { "cell_type": "code", "execution_count": 8, "id": "ee026342-f560-4ce0-ab43-1718bd19a366", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:42.631675Z", "start_time": "2024-04-19T11:25:42.452377Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display_image(app.get_graph().draw_png())" ] }, { "cell_type": "markdown", "id": "b9e767fc", "metadata": { "ExecuteTime": { "end_time": "2024-04-18T12:18:30.873950Z", "start_time": "2024-04-18T12:18:30.871750Z" } }, "source": [ "### Using Mermaid + Pyppeteer" ] }, { "cell_type": "code", "execution_count": 9, "id": "d403e1e7", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:44.798703Z", "start_time": "2024-04-19T11:25:44.793438Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: install in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (1.3.5)\n", "Collecting pyppeteer\n", " Downloading pyppeteer-2.0.0-py3-none-any.whl.metadata (7.1 kB)\n", "Collecting appdirs<2.0.0,>=1.4.3 (from pyppeteer)\n", " Downloading appdirs-1.4.4-py2.py3-none-any.whl.metadata (9.0 kB)\n", "Requirement already satisfied: certifi>=2023 in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (from pyppeteer) (2024.2.2)\n", "Requirement already satisfied: importlib-metadata>=1.4 in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (from pyppeteer) (6.11.0)\n", "Collecting pyee<12.0.0,>=11.0.0 (from pyppeteer)\n", " Downloading pyee-11.1.0-py3-none-any.whl.metadata (2.8 kB)\n", "Requirement already satisfied: tqdm<5.0.0,>=4.42.1 in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (from pyppeteer) (4.66.2)\n", "Collecting urllib3<2.0.0,>=1.25.8 (from pyppeteer)\n", " Using cached urllib3-1.26.18-py2.py3-none-any.whl.metadata (48 kB)\n", "Collecting websockets<11.0,>=10.0 (from pyppeteer)\n", " Downloading websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl.metadata (6.4 kB)\n", "Requirement already satisfied: zipp>=0.5 in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (from importlib-metadata>=1.4->pyppeteer) (3.17.0)\n", "Requirement already satisfied: typing-extensions in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (from pyee<12.0.0,>=11.0.0->pyppeteer) (4.10.0)\n", "Downloading pyppeteer-2.0.0-py3-none-any.whl (82 kB)\n", "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m82.9/82.9 kB\u001b[0m \u001b[31m2.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading appdirs-1.4.4-py2.py3-none-any.whl (9.6 kB)\n", "Downloading pyee-11.1.0-py3-none-any.whl (15 kB)\n", "Using cached urllib3-1.26.18-py2.py3-none-any.whl (143 kB)\n", "Downloading websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl (97 kB)\n", "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m97.9/97.9 kB\u001b[0m \u001b[31m6.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hInstalling collected packages: appdirs, websockets, urllib3, pyee, pyppeteer\n", " Attempting uninstall: websockets\n", " Found existing installation: websockets 12.0\n", " Uninstalling websockets-12.0:\n", " Successfully uninstalled websockets-12.0\n", " Attempting uninstall: urllib3\n", " Found existing installation: urllib3 2.2.1\n", " Uninstalling urllib3-2.2.1:\n", " Successfully uninstalled urllib3-2.2.1\n", "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", "types-requests 2.31.0.20240311 requires urllib3>=2, but you have urllib3 1.26.18 which is incompatible.\u001b[0m\u001b[31m\n", "\u001b[0mSuccessfully installed appdirs-1.4.4 pyee-11.1.0 pyppeteer-2.0.0 urllib3-1.26.18 websockets-10.4\n", "Note: you may need to restart the kernel to use updated packages.\n", "Requirement already satisfied: install in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (1.3.5)\n", "Requirement already satisfied: nest_asyncio in /Users/wfh/code/lc/langgraph/.venv/lib/python3.11/site-packages (1.6.0)\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "%%capture --no-stderr\n", "%pip install --quiet pyppeteer\n", "%pip install --quiet nest_asyncio" ] }, { "cell_type": "code", "execution_count": 10, "id": "058546ee", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:47.412695Z", "start_time": "2024-04-19T11:25:45.405158Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[INFO] Starting Chromium download.\n", "100%|██████████| 141M/141M [00:09<00:00, 14.2Mb/s] \n", "[INFO] Beginning extraction\n", "[INFO] Chromium extracted to: /Users/wfh/Library/Application Support/pyppeteer/local-chromium/1181205\n" ] }, { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import nest_asyncio\n", "from langchain_core.runnables.graph import CurveStyle, NodeColors, MermaidDrawMethod\n", "\n", "nest_asyncio.apply() # Required for Jupyter Notebook to run async functions\n", "\n", "display_image(\n", " app.get_graph().draw_mermaid_png(\n", " curve_style=CurveStyle.LINEAR,\n", " node_colors=NodeColors(start=\"#ffdfba\", end=\"#baffc9\", other=\"#fad7de\"),\n", " wrap_label_n_words=9,\n", " output_file_path=None,\n", " draw_method=MermaidDrawMethod.PYPPETEER,\n", " background_color=\"white\",\n", " padding=10,\n", " )\n", ")" ] }, { "cell_type": "markdown", "id": "2dd71a7c", "metadata": { "ExecuteTime": { "end_time": "2024-04-18T12:16:58.610115Z", "start_time": "2024-04-18T12:16:57.852988Z" } }, "source": [ "### Using Mermaid.Ink" ] }, { "cell_type": "code", "execution_count": 11, "id": "be37d419", "metadata": { "ExecuteTime": { "end_time": "2024-04-19T11:25:51.865932Z", "start_time": "2024-04-19T11:25:51.640462Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display_image(\n", " app.get_graph().draw_mermaid_png(\n", " draw_method=MermaidDrawMethod.API,\n", " )\n", ")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.2" } }, "nbformat": 4, "nbformat_minor": 5 }