From 072f2d43ebdc839d16ec77fc78bc688030a9abdb Mon Sep 17 00:00:00 2001 From: ccurme Date: Fri, 31 Jan 2025 17:41:56 -0500 Subject: [PATCH] docs: add graph API basics section (#3228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ![Screenshot 2025-01-30 at 3 53 05 PM](https://github.com/user-attachments/assets/c97018ea-38ff-4b38-96df-55c6d7f926f3) --------- Co-authored-by: Vadym Barda --- docs/docs/how-tos/branching.ipynb | 419 ++++++++--------------- docs/docs/how-tos/index.md | 24 +- docs/docs/how-tos/recursion-limit.ipynb | 309 +++++++++++++++-- docs/docs/how-tos/sequence.ipynb | 355 +++++++++++++++++++ docs/docs/how-tos/state-reducers.ipynb | 430 ++++++++++++++++++++++++ docs/mkdocs.yml | 16 +- 6 files changed, 1215 insertions(+), 338 deletions(-) create mode 100644 docs/docs/how-tos/sequence.ipynb create mode 100644 docs/docs/how-tos/state-reducers.ipynb diff --git a/docs/docs/how-tos/branching.ipynb b/docs/docs/how-tos/branching.ipynb index cbecf4f554..d1d6dd4942 100644 --- a/docs/docs/how-tos/branching.ipynb +++ b/docs/docs/how-tos/branching.ipynb @@ -80,16 +80,14 @@ "id": "d6c05fc4-ecd8-483f-a9fd-b1a055f922d9", "metadata": {}, "source": [ - "## Parallel node fan-out and fan-in\n", + "## How to run graph nodes in parallel\n", "\n", - "In this example, we fan out from `Node A` to `B and C` and then fan in to `D`. With our state, [we specify the reducer add operation](https://langchain-ai.github.io/langgraph/concepts/low_level/#reducers). This will combine or accumulate values for the specific key in the State, rather than simply overwriting the existing value. For lists, this means concatenating the new list with the existing list. \n", - "\n", - "Note that LangGraph uses `Annotated` type to specify reducer functions for specific keys in the State: it maintains the original type (`list`) for type checking, but allows attaching the reducer function (`add`) to the type without changing the type itself." + "In this example, we fan out from `Node A` to `B and C` and then fan in to `D`. With our state, [we specify the reducer add operation](https://langchain-ai.github.io/langgraph/concepts/low_level/#reducers). This will combine or accumulate values for the specific key in the State, rather than simply overwriting the existing value. For lists, this means concatenating the new list with the existing list. See [this guide](../../how-tos/state-reducers) for more detail on updating state with reducers." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "09372b8b-edea-4b9d-9ec3-3d93ce1ba819", "metadata": {}, "outputs": [], @@ -107,21 +105,32 @@ " aggregate: Annotated[list, operator.add]\n", "\n", "\n", - "class ReturnNodeValue:\n", - " def __init__(self, node_secret: str):\n", - " self._value = node_secret\n", + "def a(state: State):\n", + " print(f'Adding \"A\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"A\"]}\n", + "\n", + "\n", + "def b(state: State):\n", + " print(f'Adding \"B\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B\"]}\n", + "\n", "\n", - " def __call__(self, state: State) -> Any:\n", - " print(f\"Adding {self._value} to {state['aggregate']}\")\n", - " return {\"aggregate\": [self._value]}\n", + "def c(state: State):\n", + " print(f'Adding \"C\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"C\"]}\n", + "\n", + "\n", + "def d(state: State):\n", + " print(f'Adding \"D\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"D\"]}\n", "\n", "\n", "builder = StateGraph(State)\n", - "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "builder.add_node(c)\n", + "builder.add_node(d)\n", "builder.add_edge(START, \"a\")\n", - "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n", - "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n", - "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n", "builder.add_edge(\"a\", \"b\")\n", "builder.add_edge(\"a\", \"c\")\n", "builder.add_edge(\"b\", \"d\")\n", @@ -132,13 +141,13 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "66f52a20", "metadata": {}, "outputs": [ { "data": { - "image/jpeg": "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", + "image/png": "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", "text/plain": [ "" ] @@ -163,27 +172,27 @@ }, { "cell_type": "code", - "execution_count": 4, - "id": "38846b01", + "execution_count": 8, + "id": "81646784-5e7d-4096-980d-9fdfafd6e7a3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Adding I'm A to []\n", - "Adding I'm B to [\"I'm A\"]\n", - "Adding I'm C to [\"I'm A\"]\n", - "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\"]\n" + "Adding \"A\" to []\n", + "Adding \"B\" to ['A']\n", + "Adding \"C\" to ['A']\n", + "Adding \"D\" to ['A', 'B', 'C']\n" ] }, { "data": { "text/plain": [ - "{'aggregate': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm D\"]}" + "{'aggregate': ['A', 'B', 'C', 'D']}" ] }, - "execution_count": 4, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -192,13 +201,26 @@ "graph.invoke({\"aggregate\": []}, {\"configurable\": {\"thread_id\": \"foo\"}})" ] }, + { + "cell_type": "markdown", + "id": "ea5495cf-9564-40c6-bc2d-0b2a8f72a5df", + "metadata": {}, + "source": [ + "!!! note\n", + "\n", + " In the above example, nodes `\"b\"` and `\"c\"` are executed concurrently in the same [superstep](../../concepts/low_level/#graphs). Because they are in the same step, node `\"d\"` executes after both `\"b\"` and `\"c\"` are finished.\n", + "\n", + " Importantly, updates from a parallel superstep may not be ordered consistently. If you need a consistent, predetermined ordering of updates from a parallel superstep, you should write the outputs to a separate field in the state together with a value with which to order them." + ] + }, { "cell_type": "markdown", "id": "c392b3d2", "metadata": {}, "source": [ "
Exception handling?\n", - "

LangGraph executes nodes within \"supersteps\", meaning that while parallel branches are executed in parallel, the entire superstep is transactional. If any of these branches raises an exception, none of the updates are applied to the state (the entire superstep errors).

\n", + "

LangGraph executes nodes within \"supersteps\", meaning that while parallel branches are executed in parallel, the entire superstep is transactional. If any of these branches raises an exception, none of the updates are applied to the state (the entire superstep errors).

\n", + "

Importantly, when using a checkpointer, results from successful nodes within a superstep are saved, and don't repeat when resumed.

\n", " If you have error-prone (perhaps want to handle flakey API calls), LangGraph provides two ways to address this:
\n", "
    \n", "
  1. You can write regular python code within your node to catch and handle exceptions.
  2. \n", @@ -215,62 +237,46 @@ "source": [ "## Parallel node fan-out and fan-in with extra steps\n", "\n", - "The above example showed how to fan-out and fan-in when each path was only one step. But what if one path had more than one step?" + "The above example showed how to fan-out and fan-in when each path was only one step. But what if one path had more than one step? Let's add a node `b_2` in the \"b\" branch:" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "259a7704-5aa0-4e4c-aeef-cca04e8be0ff", "metadata": {}, "outputs": [], "source": [ - "import operator\n", - "from typing import Annotated\n", - "\n", - "from typing_extensions import TypedDict\n", - "\n", - "from langgraph.graph import StateGraph\n", - "\n", - "\n", - "class State(TypedDict):\n", - " # The operator.add reducer fn makes this append-only\n", - " aggregate: Annotated[list, operator.add]\n", - "\n", - "\n", - "class ReturnNodeValue:\n", - " def __init__(self, node_secret: str):\n", - " self._value = node_secret\n", - "\n", - " def __call__(self, state: State) -> Any:\n", - " print(f\"Adding {self._value} to {state['aggregate']}\")\n", - " return {\"aggregate\": [self._value]}\n", + "def b_2(state: State):\n", + " print(f'Adding \"B_2\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B_2\"]}\n", "\n", "\n", "builder = StateGraph(State)\n", - "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "builder.add_node(b_2)\n", + "builder.add_node(c)\n", + "builder.add_node(d)\n", "builder.add_edge(START, \"a\")\n", - "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n", - "builder.add_node(\"b2\", ReturnNodeValue(\"I'm B2\"))\n", - "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n", - "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n", "builder.add_edge(\"a\", \"b\")\n", "builder.add_edge(\"a\", \"c\")\n", - "builder.add_edge(\"b\", \"b2\")\n", - "builder.add_edge([\"b2\", \"c\"], \"d\")\n", + "builder.add_edge(\"b\", \"b_2\")\n", + "# highlight-next-line\n", + "builder.add_edge([\"b_2\", \"c\"], \"d\")\n", "builder.add_edge(\"d\", END)\n", "graph = builder.compile()" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "83320227-8ab3-44c0-b6cf-064a7a425b9f", "metadata": {}, "outputs": [ { "data": { - "image/jpeg": "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", + "image/png": "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", "text/plain": [ "" ] @@ -287,28 +293,28 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "3f971fa3-29e4-466f-a85e-2863bfecf7fe", + "execution_count": 6, + "id": "2b8659a9-bacd-4620-a160-8a08d10f7192", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Adding I'm A to []\n", - "Adding I'm B to [\"I'm A\"]\n", - "Adding I'm C to [\"I'm A\"]\n", - "Adding I'm B2 to [\"I'm A\", \"I'm B\", \"I'm C\"]\n", - "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\"]\n" + "Adding \"A\" to []\n", + "Adding \"B\" to ['A']\n", + "Adding \"C\" to ['A']\n", + "Adding \"B_2\" to ['A', 'B', 'C']\n", + "Adding \"D\" to ['A', 'B', 'C', 'B_2']\n" ] }, { "data": { "text/plain": [ - "{'aggregate': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\", \"I'm D\"]}" + "{'aggregate': ['A', 'B', 'C', 'B_2', 'D']}" ] }, - "execution_count": 7, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -317,6 +323,18 @@ "graph.invoke({\"aggregate\": []})" ] }, + { + "cell_type": "markdown", + "id": "00d33cb0-5a47-4057-bc55-0fc14c6034fc", + "metadata": {}, + "source": [ + "!!! note\n", + "\n", + "

    In the above example, nodes `\"b\"` and `\"c\"` are executed concurrently in the same [superstep](../../concepts/low_level/#graphs). What happens in the next step?

    \n", + "

    We use `add_edge([\"b_2\", \"c\"], \"d\")` here to force node `\"d\"` to only run when both nodes `\"b_2\"` and `\"c\"` have finished execution. If we added two separate edges,\n", + " node `\"d\"` would run twice: after node `b2` finishes and once again after node `c` (in whichever order those nodes finish).

    " + ] + }, { "cell_type": "markdown", "id": "d45f4477", @@ -324,9 +342,7 @@ "source": [ "## Conditional Branching\n", "\n", - "If your fan-out is not deterministic, you can use [add_conditional_edges](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph.add_conditional_edges) directly.\n", - "\n", - "If you have a known \"sink\" node that the conditional branches will route to afterwards, you can provide `then=` when creating the conditional edges." + "If your fan-out is not deterministic, you can use [add_conditional_edges](https://langchain-ai.github.io/langgraph/reference/graphs/#langgraph.graph.StateGraph.add_conditional_edges) directly." ] }, { @@ -341,31 +357,48 @@ "\n", "from typing_extensions import TypedDict\n", "\n", - "from langgraph.graph import END, START, StateGraph\n", + "from langgraph.graph import StateGraph, START, END\n", "\n", "\n", "class State(TypedDict):\n", - " # The operator.add reducer fn makes this append-only\n", " aggregate: Annotated[list, operator.add]\n", + " # Add a key to the state. We will set this key to determine\n", + " # how we branch.\n", " which: str\n", "\n", "\n", - "class ReturnNodeValue:\n", - " def __init__(self, node_secret: str):\n", - " self._value = node_secret\n", + "def a(state: State):\n", + " print(f'Adding \"A\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"A\"]}\n", + "\n", + "\n", + "def b(state: State):\n", + " print(f'Adding \"B\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B\"]}\n", + "\n", "\n", - " def __call__(self, state: State) -> Any:\n", - " print(f\"Adding {self._value} to {state['aggregate']}\")\n", - " return {\"aggregate\": [self._value]}\n", + "def c(state: State):\n", + " print(f'Adding \"C\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"C\"]}\n", + "\n", + "\n", + "def d(state: State):\n", + " print(f'Adding \"D\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"D\"]}\n", + "\n", + "\n", + "def e(state: State):\n", + " print(f'Adding \"E\" to {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"E\"]}\n", "\n", "\n", "builder = StateGraph(State)\n", - "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "builder.add_node(c)\n", + "builder.add_node(d)\n", + "builder.add_node(e)\n", "builder.add_edge(START, \"a\")\n", - "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n", - "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n", - "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n", - "builder.add_node(\"e\", ReturnNodeValue(\"I'm E\"))\n", "\n", "\n", "def route_bc_or_cd(state: State) -> Sequence[str]:\n", @@ -383,7 +416,6 @@ "for node in intermediates:\n", " builder.add_edge(node, \"e\")\n", "\n", - "\n", "builder.add_edge(\"e\", END)\n", "graph = builder.compile()" ] @@ -391,12 +423,12 @@ { "cell_type": "code", "execution_count": 9, - "id": "1d0e6c56", + "id": "264da3f8-f5de-499b-8287-73797c1e3511", "metadata": {}, "outputs": [ { "data": { - "image/jpeg": "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", + "image/png": "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Uv3www8zZ850Op1UbhcJP7nke/LkyQ0bNlB/eZ/qM+QKhSIvL++pp55i/IUJf7jku2vXrm+//XbZsmXUbxrBpZ2AgICioqLXX3/92LFj1G8dI8uaNWtOnz793XffIdk6smuS+fn527Zt++GHH1AVABuz7/J9++23eTzeu+++i6oAlBfTP//88+rqagqusiDB1Lv02FTFAAARpElEQVR8jUbj3Llz+/TpM2PGDIRlIJ4FMm/evPT09OHDh9fVUbf6OfbQjhw5MmLEiFdffRX52SH0j3fJyspKTU3Nzc196aWXsrKyUJdDGubNDluxYsXt27d/++031IUA9D2uR3h4+O7du4uKit577z3UtZCGSbPDrFbr1KlTQ0NDfWe+lE8E12Pp0qVdunSZO3duWVkZ6lpIEBQUxIyDs3379r322mtvvfVWTk4O6lr+gn6ocKfRo0f36NHjtddeGzRoENqx/6NrbGxkwMHZggUL2Gz26tWrURdyNx/qcT0iIyO3bt3qcDhycnJofcQmk8m4XN/qFx7I8ePHn3zyyQEDBrz//vuoa2mFj+7Z2bNn9+vXLzc3d9q0adRMkyOd0Wik75Xt5cuX19fXHz161GevWvtcj9uiQ4cOu3fvrqysfO211+jY9UokEjpOazx58uScOXM6duy4cuVKn02t7/a4Ld58880LFy7k5uaOHz9++vTpqMt5AGazmXbTGhcvXqzVapctWxYQEIC6ljb4bo/bIiUlZffu3WazecKECTR6wIRKpRIKYT1+kXS//PJLWlpaenr6qlWrfD+1NOhxW7zyyivDhg1btGhRWlravHnzUJfTNo1GExsbi7qKthkMhoULF8pkstOnT9PoBk9vnyzpOzZt2vTrr79OmzatT58+qGtpxdixYysrK1vu8iUIgiCITp06UTzP2ksbNmw4c+ZMdnb2U089hbqWB0ODocJdpkyZ8vHHH//000/z5s1rbKTuYd5e6tu3L4vFaum6WCyWUql8/vnnUdd1t3Pnzo0dO1an061cuZJ2qaVlj9uisLBw2bJl2dnZubm5qGv5S319/UsvveTpdD26d+/+1VdfIS3qfzgcjvfee6+2tnbhwoW0GMy0in49bovMzMz9+/fbbLYRI0acPXsWdTl/Cg0NvXMpY4VC8cwzzyCt6H/s2rWrd+/e6enp33zzDX1TS+/gerz00ktfffXV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", "text/plain": [ "" ] @@ -414,23 +446,23 @@ { "cell_type": "code", "execution_count": 10, - "id": "7134f652", + "id": "ffe8e4aa-55c1-43b4-ba64-74583359a35b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Adding I'm A to []\n", - "Adding I'm B to [\"I'm A\"]\n", - "Adding I'm C to [\"I'm A\"]\n", - "Adding I'm E to [\"I'm A\", \"I'm B\", \"I'm C\"]\n" + "Adding \"A\" to []\n", + "Adding \"B\" to ['A']\n", + "Adding \"C\" to ['A']\n", + "Adding \"E\" to ['A', 'B', 'C']\n" ] }, { "data": { "text/plain": [ - "{'aggregate': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm E\"], 'which': 'bc'}" + "{'aggregate': ['A', 'B', 'C', 'E'], 'which': 'bc'}" ] }, "execution_count": 10, @@ -445,23 +477,23 @@ { "cell_type": "code", "execution_count": 11, - "id": "b130e694", + "id": "3bc6b3b4-a0c8-471a-9029-ddf2472c385c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Adding I'm A to []\n", - "Adding I'm C to [\"I'm A\"]\n", - "Adding I'm D to [\"I'm A\"]\n", - "Adding I'm E to [\"I'm A\", \"I'm C\", \"I'm D\"]\n" + "Adding \"A\" to []\n", + "Adding \"C\" to ['A']\n", + "Adding \"D\" to ['A']\n", + "Adding \"E\" to ['A', 'C', 'D']\n" ] }, { "data": { "text/plain": [ - "{'aggregate': [\"I'm A\", \"I'm C\", \"I'm D\", \"I'm E\"], 'which': 'cd'}" + "{'aggregate': ['A', 'C', 'D', 'E'], 'which': 'cd'}" ] }, "execution_count": 11, @@ -475,196 +507,13 @@ }, { "cell_type": "markdown", - "id": "952cd6f3", - "metadata": {}, - "source": [ - "## Stable Sorting\n", - "\n", - "When fanned out, nodes are run in parallel as a single \"superstep\". The updates from each superstep are all applied to the state in sequence once the superstep has completed. \n", - "\n", - "If you need consistent, predetermined ordering of updates from a parallel superstep, you should write the outputs (along with an identifying key) to a separate field in your state, then combine them in the \"sink\" node by adding regular `edge`'s from each of the fanout nodes to the rendezvous point.\n", - "\n", - "For instance, suppose I want to order the outputs of the parallel step by \"reliability\"." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "836bc12d", - "metadata": {}, - "outputs": [], - "source": [ - "import operator\n", - "from typing import Annotated, Sequence\n", - "\n", - "from typing_extensions import TypedDict\n", - "\n", - "from langgraph.graph import StateGraph\n", - "\n", - "\n", - "def reduce_fanouts(left, right):\n", - " if left is None:\n", - " left = []\n", - " if not right:\n", - " # Overwrite\n", - " return []\n", - " return left + right\n", - "\n", - "\n", - "class State(TypedDict):\n", - " # The operator.add reducer fn makes this append-only\n", - " aggregate: Annotated[list, operator.add]\n", - " fanout_values: Annotated[list, reduce_fanouts]\n", - " which: str\n", - "\n", - "\n", - "builder = StateGraph(State)\n", - "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n", - "builder.add_edge(START, \"a\")\n", - "\n", - "\n", - "class ParallelReturnNodeValue:\n", - " def __init__(\n", - " self,\n", - " node_secret: str,\n", - " reliability: float,\n", - " ):\n", - " self._value = node_secret\n", - " self._reliability = reliability\n", - "\n", - " def __call__(self, state: State) -> Any:\n", - " print(f\"Adding {self._value} to {state['aggregate']} in parallel.\")\n", - " return {\n", - " \"fanout_values\": [\n", - " {\n", - " \"value\": [self._value],\n", - " \"reliability\": self._reliability,\n", - " }\n", - " ]\n", - " }\n", - "\n", - "\n", - "builder.add_node(\"b\", ParallelReturnNodeValue(\"I'm B\", reliability=0.9))\n", - "\n", - "builder.add_node(\"c\", ParallelReturnNodeValue(\"I'm C\", reliability=0.1))\n", - "builder.add_node(\"d\", ParallelReturnNodeValue(\"I'm D\", reliability=0.3))\n", - "\n", - "\n", - "def aggregate_fanout_values(state: State) -> Any:\n", - " # Sort by reliability\n", - " ranked_values = sorted(\n", - " state[\"fanout_values\"], key=lambda x: x[\"reliability\"], reverse=True\n", - " )\n", - " return {\n", - " \"aggregate\": [x[\"value\"] for x in ranked_values] + [\"I'm E\"],\n", - " \"fanout_values\": [],\n", - " }\n", - "\n", - "\n", - "builder.add_node(\"e\", aggregate_fanout_values)\n", - "\n", - "\n", - "def route_bc_or_cd(state: State) -> Sequence[str]:\n", - " if state[\"which\"] == \"cd\":\n", - " return [\"c\", \"d\"]\n", - " return [\"b\", \"c\"]\n", - "\n", - "\n", - "intermediates = [\"b\", \"c\", \"d\"]\n", - "builder.add_conditional_edges(\"a\", route_bc_or_cd, intermediates)\n", - "\n", - "for node in intermediates:\n", - " builder.add_edge(node, \"e\")\n", - "\n", - "builder.add_edge(\"e\", END)\n", - "graph = builder.compile()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "932c497e", + "id": "639a3653-0fa3-4b6d-bf53-63479c6b00fe", "metadata": {}, - "outputs": [ - { - "data": { - "image/jpeg": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], "source": [ - "from IPython.display import Image, display\n", + "## Next steps\n", "\n", - "display(Image(graph.get_graph().draw_mermaid_png()))" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "933b3afd", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Adding I'm A to []\n", - "Adding I'm B to [\"I'm A\"] in parallel.\n", - "Adding I'm C to [\"I'm A\"] in parallel.\n" - ] - }, - { - "data": { - "text/plain": [ - "{'aggregate': [\"I'm A\", [\"I'm B\"], [\"I'm C\"], \"I'm E\"],\n", - " 'fanout_values': [],\n", - " 'which': 'bc'}" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "graph.invoke({\"aggregate\": [], \"which\": \"bc\", \"fanout_values\": []})" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "e30531bf", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Adding I'm A to []\n", - "Adding I'm C to [\"I'm A\"] in parallel.\n", - "Adding I'm D to [\"I'm A\"] in parallel.\n" - ] - }, - { - "data": { - "text/plain": [ - "{'aggregate': [\"I'm A\", [\"I'm D\"], [\"I'm C\"], \"I'm E\"],\n", - " 'fanout_values': [],\n", - " 'which': 'cd'}" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "graph.invoke({\"aggregate\": [], \"which\": \"cd\"})" + "- Continue with the [Graph API Basics](../../how-tos/#graph-api-basics) guides.\n", + "- Learn how to create [map-reduce](../../how-tos/map-reduce/) branches in which different states can be distributed to multiple instances of a node." ] } ], @@ -684,7 +533,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index e141f8bc3d..30d249b016 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -9,16 +9,24 @@ Here you’ll find answers to “How do I...?” types of questions. These guide ## LangGraph -### Controllability +### Graph API Basics -LangGraph offers a high level of control over the execution of your graph. +- [How to update graph state from nodes](state-reducers.ipynb) +- [How to create a sequence of steps](sequence.ipynb) +- [How to create branches for parallel execution](branching.ipynb) +- [How to create and control loops with recursion limits](recursion-limit.ipynb) +- [How to visualize your graph](visualization.ipynb) -These how-to guides show how to achieve that controllability. +### Fine-grained Control + +These guides demonstrate LangGraph features that grant fine-grained control over the +execution of your graph. -- [How to create branches for parallel execution](branching.ipynb) - [How to create map-reduce branches for parallel execution](map-reduce.ipynb) -- [How to control graph recursion limit](recursion-limit.ipynb) -- [How to combine control flow and state updates with Command](command.ipynb) +- [How to update state and jump to nodes in graphs and subgraphs](command.ipynb) +- [How to add runtime configuration to your graph](configuration.ipynb) +- [How to add node retries](node-retries.ipynb) +- [How to return state before hitting recursion limit](return-when-recursion-limit-hits.ipynb) ### Persistence @@ -142,12 +150,8 @@ See the below guides for how to implement multi-agent workflows with the (beta) ### Other - [How to run graph asynchronously](async.ipynb) -- [How to visualize your graph](visualization.ipynb) -- [How to add runtime configuration to your graph](configuration.ipynb) -- [How to add node retries](node-retries.ipynb) - [How to force tool-calling agent to structure output](react-agent-structured-output.ipynb) - [How to pass custom LangSmith run ID for graph runs](run-id-langsmith.ipynb) -- [How to return state before hitting recursion limit](return-when-recursion-limit-hits.ipynb) - [How to integrate LangGraph with AutoGen, CrewAI, and other frameworks](autogen-integration.ipynb) See the below guide for how to integrate with other frameworks using the (beta) diff --git a/docs/docs/how-tos/recursion-limit.ipynb b/docs/docs/how-tos/recursion-limit.ipynb index 1489c16a91..467ec80325 100644 --- a/docs/docs/how-tos/recursion-limit.ipynb +++ b/docs/docs/how-tos/recursion-limit.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# How to control graph recursion limit\n", + "# How to create and control loops\n", "\n", "
    \n", "

    Prerequisites

    \n", @@ -31,9 +31,46 @@ "
    \n", "\n", "\n", - "You can set the graph recursion limit when invoking or streaming the graph. The recursion limit sets the number of **supersteps** that the graph is allowed to execute before it raises an error. Read more about the concept of recursion limits [here](https://langchain-ai.github.io/langgraph/concepts/low_level/#recursion-limit). Let's see an example of this in a simple graph with parallel branches to better understand exactly how the recursion limit works.\n", + "When creating a graph with a loop, we require a mechanism for terminating execution. This is most commonly done by adding a [conditional edge](../../concepts/low_level/#conditional-edges) that routes to the [END](../../concepts/low_level/#end-node) node once we reach some termination condition.\n", "\n", - "If you want to see an example of how you can return the last value of your state instead of receiving a recursion limit error form your graph, read [this how-to](https://langchain-ai.github.io/langgraph/how-tos/return-when-recursion-limit-hits/).\n", + "You can also set the graph recursion limit when invoking or streaming the graph. The recursion limit sets the number of [supersteps](../../concepts/low_level/#graphs) that the graph is allowed to execute before it raises an error. Read more about the concept of recursion limits [here](../../concepts/low_level/#recursion-limit). \n", + "\n", + "Let's consider a simple graph with a loop to better understand how these mechanisms work.\n", + "\n", + "!!! tip\n", + "\n", + " To return the last value of your state instead of receiving a recursion limit error, read [this how-to](../../how-tos/return-when-recursion-limit-hits/).\n", + "\n", + "\n", + "## Summary\n", + "\n", + "When creating a loop, you can include a conditional edge that specifies a termination condition:\n", + "```python\n", + "builder = StateGraph(State)\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "\n", + "def route(state: State) -> Literal[\"b\", END]:\n", + " if termination_condition(state):\n", + " return END\n", + " else:\n", + " return \"a\"\n", + "\n", + "builder.add_edge(START, \"a\")\n", + "builder.add_conditional_edges(\"a\", route)\n", + "builder.add_edge(\"b\", \"a\")\n", + "graph = builder.compile()\n", + "```\n", + "\n", + "To control the recursion limit, specify `\"recursion_limit\"` in the config. This will raise a `GraphRecursionError`, which you can catch and handle:\n", + "```python\n", + "from langgraph.errors import GraphRecursionError\n", + "\n", + "try:\n", + " graph.invoke(inputs, {\"recursion_limit\": 3})\n", + "except GraphRecursionError:\n", + " print(\"Recursion Error\")\n", + "```\n", "\n", "## Setup\n", "\n", @@ -66,17 +103,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Define the graph" + "## Define the graph\n", + "\n", + "Let's define a graph with a simple loop. Note that we use a conditional edge to implement a termination condition." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import operator\n", - "from typing import Annotated, Any\n", + "from typing import Annotated, Literal\n", "\n", "from typing_extensions import TypedDict\n", "\n", @@ -88,44 +127,44 @@ " aggregate: Annotated[list, operator.add]\n", "\n", "\n", - "def node_a(state):\n", - " return {\"aggregate\": [\"I'm A\"]}\n", + "def a(state: State):\n", + " print(f'Node A sees {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"A\"]}\n", "\n", "\n", - "def node_b(state):\n", - " return {\"aggregate\": [\"I'm B\"]}\n", + "def b(state: State):\n", + " print(f'Node B sees {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B\"]}\n", "\n", "\n", - "def node_c(state):\n", - " return {\"aggregate\": [\"I'm C\"]}\n", + "# Define nodes\n", + "builder = StateGraph(State)\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", "\n", "\n", - "def node_d(state):\n", - " return {\"aggregate\": [\"I'm A\"]}\n", + "# Define edges\n", + "def route(state: State) -> Literal[\"b\", END]:\n", + " if len(state[\"aggregate\"]) < 7:\n", + " return \"b\"\n", + " else:\n", + " return END\n", "\n", "\n", - "builder = StateGraph(State)\n", - "builder.add_node(\"a\", node_a)\n", "builder.add_edge(START, \"a\")\n", - "builder.add_node(\"b\", node_b)\n", - "builder.add_node(\"c\", node_c)\n", - "builder.add_node(\"d\", node_d)\n", - "builder.add_edge(\"a\", \"b\")\n", - "builder.add_edge(\"a\", \"c\")\n", - "builder.add_edge(\"b\", \"d\")\n", - "builder.add_edge(\"c\", \"d\")\n", - "builder.add_edge(\"d\", END)\n", + "builder.add_conditional_edges(\"a\", route)\n", + "builder.add_edge(\"b\", \"a\")\n", "graph = builder.compile()" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { - "image/jpeg": "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", + "image/png": "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", "text/plain": [ "" ] @@ -144,9 +183,53 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As we can see, our graph will execute nodes `b` and `c` in parallel (i.e. in a single super-step), which means that if we run this graph it should take exactly 3 steps. We can set the recursion limit to 3 first to check that it raises an error (the recursion limit is inclusive, so if the limit is 3 the graph will raise an error when it reaches step 3) as expected: \n", + "This architecture is similar to a [ReAct agent](../../how-tos/#prebuilt-react-agent) in which node `\"a\"` is a tool-calling model, and node `\"b\"` represents the tools.\n", "\n", - "## Use the graph" + "In our `route` conditional edge, we specify that we should end after the `\"aggregate\"` list in the state passes a threshold length.\n", + "\n", + "Invoking the graph, we see that we alternate between nodes `\"a\"` and `\"b\"` before terminating once we reach the termination condition." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node A sees []\n", + "Node B sees ['A']\n", + "Node A sees ['A', 'B']\n", + "Node B sees ['A', 'B', 'A']\n", + "Node A sees ['A', 'B', 'A', 'B']\n", + "Node B sees ['A', 'B', 'A', 'B', 'A']\n", + "Node A sees ['A', 'B', 'A', 'B', 'A', 'B']\n" + ] + }, + { + "data": { + "text/plain": [ + "{'aggregate': ['A', 'B', 'A', 'B', 'A', 'B', 'A']}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.invoke({\"aggregate\": []})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Impose a recursion limit\n", + "\n", + "In some applications, we may not have a guarantee that we will reach a given termination condition. In these cases, we can set the graph's [recursion limit](../../concepts/low_level/#recursion-limit). This will raise a `GraphRecursionError` after a given number of [supersteps](../../concepts/low_level/#graphs). We can then catch and handle this exception:" ] }, { @@ -158,6 +241,10 @@ "name": "stdout", "output_type": "stream", "text": [ + "Node A sees []\n", + "Node B sees ['A']\n", + "Node A sees ['A', 'B']\n", + "Node B sees ['A', 'B', 'A']\n", "Recursion Error\n" ] } @@ -166,7 +253,7 @@ "from langgraph.errors import GraphRecursionError\n", "\n", "try:\n", - " graph.invoke({\"aggregate\": []}, {\"recursion_limit\": 3})\n", + " graph.invoke({\"aggregate\": []}, {\"recursion_limit\": 4})\n", "except GraphRecursionError:\n", " print(\"Recursion Error\")" ] @@ -175,7 +262,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Success! The graph raised an error as expected - now let's test setting the recursion limit to 4 and ensure that the graph succeeds in this case:" + "Note that this time we terminate after the fourth step." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Loops with branches\n", + "\n", + "To better understand how the recursion limit works, let's consider a more complex example. Below we implement a loop, but one step fans out into two nodes:" ] }, { @@ -184,19 +280,158 @@ "metadata": {}, "outputs": [], "source": [ - "try:\n", - " graph.invoke({\"aggregate\": []}, {\"recursion_limit\": 4})\n", - "except GraphRecursionError:\n", - " print(\"Recursion Error\")" + "import operator\n", + "from typing import Annotated, Literal\n", + "\n", + "from typing_extensions import TypedDict\n", + "\n", + "from langgraph.graph import StateGraph, START, END\n", + "\n", + "\n", + "class State(TypedDict):\n", + " aggregate: Annotated[list, operator.add]\n", + "\n", + "\n", + "def a(state: State):\n", + " print(f'Node A sees {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"A\"]}\n", + "\n", + "\n", + "def b(state: State):\n", + " print(f'Node B sees {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"B\"]}\n", + "\n", + "\n", + "def c(state: State):\n", + " print(f'Node C sees {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"C\"]}\n", + "\n", + "\n", + "def d(state: State):\n", + " print(f'Node D sees {state[\"aggregate\"]}')\n", + " return {\"aggregate\": [\"D\"]}\n", + "\n", + "\n", + "# Define nodes\n", + "builder = StateGraph(State)\n", + "builder.add_node(a)\n", + "builder.add_node(b)\n", + "builder.add_node(c)\n", + "builder.add_node(d)\n", + "\n", + "\n", + "# Define edges\n", + "def route(state: State) -> Literal[\"b\", END]:\n", + " if len(state[\"aggregate\"]) < 7:\n", + " return \"b\"\n", + " else:\n", + " return END\n", + "\n", + "\n", + "builder.add_edge(START, \"a\")\n", + "builder.add_conditional_edges(\"a\", route)\n", + "builder.add_edge(\"b\", \"c\")\n", + "builder.add_edge(\"b\", \"d\")\n", + "builder.add_edge([\"c\", \"d\"], \"a\")\n", + "graph = builder.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This graph looks complex, but can be conceptualized as loop of [supersteps](../../concepts/low_level/#graphs):\n", + "\n", + "1. Node A\n", + "2. Node B\n", + "3. Nodes C and D\n", + "4. Node A\n", + "5. ...\n", + "\n", + "We have a loop of four supersteps, where nodes C and D are executed concurrently.\n", + "\n", + "Invoking the graph as before, we see that we complete two full \"laps\" before hitting the termination condition:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node A sees []\n", + "Node B sees ['A']\n", + "Node D sees ['A', 'B']\n", + "Node C sees ['A', 'B']\n", + "Node A sees ['A', 'B', 'C', 'D']\n", + "Node B sees ['A', 'B', 'C', 'D', 'A']\n", + "Node D sees ['A', 'B', 'C', 'D', 'A', 'B']\n", + "Node C sees ['A', 'B', 'C', 'D', 'A', 'B']\n", + "Node A sees ['A', 'B', 'C', 'D', 'A', 'B', 'C', 'D']\n" + ] + } + ], + "source": [ + "result = graph.invoke({\"aggregate\": []})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Perfect, just as we expected the graph runs successfully in this case. \n", + "However, if we set the recursion limit to four, we only complete one lap because each lap is four supersteps:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node A sees []\n", + "Node B sees ['A']\n", + "Node C sees ['A', 'B']\n", + "Node D sees ['A', 'B']\n", + "Node A sees ['A', 'B', 'C', 'D']\n", + "Recursion Error\n" + ] + } + ], + "source": [ + "from langgraph.errors import GraphRecursionError\n", "\n", - "Setting the correct graph recursion limit is important for avoiding graph runs stuck in long-running loops and thus helps minimize unnecessary costs" + "try:\n", + " result = graph.invoke({\"aggregate\": []}, {\"recursion_limit\": 4})\n", + "except GraphRecursionError:\n", + " print(\"Recursion Error\")" ] } ], @@ -216,7 +451,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/docs/docs/how-tos/sequence.ipynb b/docs/docs/how-tos/sequence.ipynb new file mode 100644 index 0000000000..1b805a5218 --- /dev/null +++ b/docs/docs/how-tos/sequence.ipynb @@ -0,0 +1,355 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# How to create a sequence of steps\n", + "\n", + "!!! info \"Prerequisites\"\n", + " This guide assumes familiarity with the following:\n", + "\n", + " - [How to define and update graph state](../../how-tos/state-reducers)\n", + "\n", + "This guide demonstrates how to construct a simple sequence of steps. We will demonstrate:\n", + "\n", + "1. How to build a sequential graph\n", + "2. Built-in short-hand for constructing similar graphs.\n", + "\n", + "\n", + "# Summary\n", + "\n", + "To add a sequence of nodes, we use the `.add_node` and `.add_edge` methods of our [graph](../../concepts/low_level/#stategraph):\n", + "```python\n", + "from langgraph.graph import START, StateGraph\n", + "\n", + "graph_builder = StateGraph(State)\n", + "\n", + "# Add nodes\n", + "graph_builder.add_node(step_1)\n", + "graph_builder.add_node(step_2)\n", + "graph_builder.add_node(step_3)\n", + "\n", + "# Add edges\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "graph_builder.add_edge(\"step_1\", \"step_2\")\n", + "graph_builder.add_edge(\"step_2\", \"step_3\")\n", + "```\n", + "\n", + "We can also use the built-in shorthand `.add_sequence`:\n", + "```python\n", + "graph_builder = StateGraph(State).add_sequence([step_1, step_2, step_3])\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "```\n", + "\n", + "\n", + "
    \n", + "Why split application steps into a sequence with LangGraph?\n", + "\n", + "LangGraph makes it easy to add an underlying persistence layer to your application.\n", + "This allows state to be checkpointed in between the execution of nodes, so your LangGraph nodes govern:\n", + "\n", + "
      \n", + "
    • How state updates are [checkpointed](../../concepts/persistence/)
    • \n", + "
    • How interruptions are resumed in [human-in-the-loop](../../concepts/human_in_the_loop/) workflows
    • \n", + "
    • How we can \"rewind\" and branch-off executions using LangGraph's [time travel](../../concepts/time-travel/) features
    • \n", + "
    \n", + "\n", + "They also determine how execution steps are [streamed](../../concepts/streaming/), and how your application is visualized\n", + "and debugged using [LangGraph Studio](../../concepts/langgraph_studio/).\n", + "\n", + "
    \n", + "\n", + "## Setup\n", + "\n", + "First, let's install langgraph:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "

    Set up LangSmith for better debugging

    \n", + "

    \n", + " Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM aps built with LangGraph — read more about how to get started in the docs. \n", + "

    \n", + "
    " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Build the graph\n", + "\n", + "Let's demonstrate a simple usage example. We will create a sequence of three steps:\n", + "\n", + "1. Populate a value in a key of the state\n", + "2. Update the same value\n", + "3. Populate a different value\n", + "\n", + "### Define state\n", + "\n", + "Let's first define our [state](../../concepts/low_level/#state). This governs the [schema of the graph](../../concepts/low_level/#schema), and can also specify how to apply updates. See [this guide](../../how-tos/state-reducers) for more detail.\n", + "\n", + "In our case, we will just keep track of two values:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from typing_extensions import TypedDict\n", + "\n", + "\n", + "class State(TypedDict):\n", + " value_1: str\n", + " value_2: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define nodes\n", + "\n", + "Our [nodes](../../concepts/low_level/#nodes) are just Python functions that read our graph's state and make updates to it. The first argument to this function will always be the state:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def step_1(state: State):\n", + " return {\"value_1\": \"a\"}\n", + "\n", + "\n", + "def step_2(state: State):\n", + " current_value_1 = state[\"value_1\"]\n", + " return {\"value_1\": f\"{current_value_1} b\"}\n", + "\n", + "\n", + "def step_3(state: State):\n", + " return {\"value_2\": 10}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "!!! note\n", + "\n", + " Note that when issuing updates to the state, each node can just specify the value of the key it wishes to update.\n", + "\n", + "By default, this will **overwrite** the value of the corresponding key. You can also use [reducers](../../concepts/low_level/#reducers) to control how updates are processed— for example, you can append successive updates to a key instead. See [this guide](../../how-tos/state-reducers) for more detail.\n", + "\n", + "### Define graph\n", + "\n", + "We use [StateGraph](../../concepts/low_level/#stategraph) to define a graph that operates on this state.\n", + "\n", + "We will then use [add_node](../../concepts/low_level/#messagesstate) and [add_edge](../../concepts/low_level/#edges) to populate our graph and define its control flow." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import START, StateGraph\n", + "\n", + "graph_builder = StateGraph(State)\n", + "\n", + "# Add nodes\n", + "graph_builder.add_node(step_1)\n", + "graph_builder.add_node(step_2)\n", + "graph_builder.add_node(step_3)\n", + "\n", + "# Add edges\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "graph_builder.add_edge(\"step_1\", \"step_2\")\n", + "graph_builder.add_edge(\"step_2\", \"step_3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "!!! tip \"Specifying custom names\"\n", + "\n", + " You can specify custom names for nodes using `.add_node`:\n", + "\n", + " ```python\n", + " graph_builder.add_node(\"my_node\", step_1)\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that:\n", + "\n", + "- `.add_edge` takes the names of nodes, which for functions defaults to `node.__name__`.\n", + "- We must specify the entry point of the graph. For this we add an edge with the [START node](../../concepts/low_level/#start-node).\n", + "- The graph halts when there are no more nodes to execute.\n", + "\n", + "We next [compile](../../concepts/low_level/#compiling-your-graph) our graph. This provides a few basic checks on the structure of the graph (e.g., identifying orphaned nodes). If we were adding persistence to our application via a [checkpointer](../../concepts/persistence/), it would also be passed in here." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "graph = graph_builder.compile()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "LangGraph provides built-in utilities for visualizing your graph. Let's inspect our sequence. See [this guide](../../how-tos/visualization) for detail on visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Usage\n", + "\n", + "Let's proceed with a simple invocation:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'value_1': 'a b', 'value_2': 10}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph.invoke({\"value_1\": \"c\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that:\n", + "\n", + "- We kicked off invocation by providing a value for a single state key. We must always provide a value for at least one key.\n", + "- The value we passed in was overwritten by the first node.\n", + "- The second node updated the value.\n", + "- The third node populated a different value." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Built-in shorthand\n", + "\n", + "!!! info \"Prerequisites\"\n", + " `.add_sequence` requires `langgraph>=0.2.46`\n", + "\n", + "\n", + "LangGraph includes a built-in shorthand `.add_sequence` for convenience:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'value_1': 'a b', 'value_2': 10}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# highlight-next-line\n", + "graph_builder = StateGraph(State).add_sequence([step_1, step_2, step_3])\n", + "graph_builder.add_edge(START, \"step_1\")\n", + "\n", + "graph = graph_builder.compile()\n", + "\n", + "graph.invoke({\"value_1\": \"c\"})" + ] + } + ], + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/docs/how-tos/state-reducers.ipynb b/docs/docs/how-tos/state-reducers.ipynb new file mode 100644 index 0000000000..dd66a15c05 --- /dev/null +++ b/docs/docs/how-tos/state-reducers.ipynb @@ -0,0 +1,430 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# How to update graph state from nodes\n", + "\n", + "This guide demonstrates how to define and update [state](../../concepts/low_level/#state) in LangGraph. We will demonstrate:\n", + "\n", + "1. How to use state to define a graph's [schema](../../concepts/low_level/#schema)\n", + "2. How to use [reducers](../../concepts/low_level/#reducers) to control how state updates are processed.\n", + "\n", + "We will use [messages](../../concepts/low_level/#messagesstate) in our examples. This represents a versatile formulation of state for many LLM applications. See our [concepts page](../../concepts/low_level/#working-with-messages-in-graph-state) for more detail.\n", + "\n", + "## Setup\n", + "\n", + "First, let's install langgraph:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langgraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + "

    Set up LangSmith for better debugging

    \n", + "

    \n", + " Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM aps built with LangGraph — read more about how to get started in the docs. \n", + "

    \n", + "
    " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example graph\n", + "\n", + "### Define state\n", + "[State](../../concepts/low_level/#state) in LangGraph can be a `TypedDict`, `Pydantic` model, or dataclass. Below we will use `TypedDict`. See [this guide](../../how-tos/state-model) for detail on using Pydantic.\n", + "\n", + "By default, graphs will have the same input and output schema, and the state determines that schema. See [this guide](../../how-tos/input_output_schema/) for how to define distinct input and output schemas.\n", + "\n", + "Let's consider a simple example:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import AnyMessage\n", + "from typing_extensions import TypedDict\n", + "\n", + "\n", + "class State(TypedDict):\n", + " messages: list[AnyMessage]\n", + " extra_field: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This state tracks a list of [message](https://python.langchain.com/docs/concepts/messages/) objects, as well as an extra integer field.\n", + "\n", + "### Define graph structure\n", + "\n", + "Let's build an example graph with a single node. Our [node](../../concepts/low_level/#nodes) is just a Python function that reads our graph's state and makes updates to it. The first argument to this function will always be the state:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.messages import AIMessage\n", + "\n", + "\n", + "def node(state: State):\n", + " messages = state[\"messages\"]\n", + " new_message = AIMessage(\"Hello!\")\n", + "\n", + " return {\"messages\": messages + [new_message], \"extra_field\": 10}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This node simply appends a message to our message list, and populates an extra field.\n", + "\n", + "!!! important\n", + "\n", + " Nodes should return updates to the state directly, instead of mutating the state.\n", + "\n", + "Let's next define a simple graph containing this node. We use [StateGraph](../../concepts/low_level/#stategraph) to define a graph that operates on this state. We then use [add_node](../../concepts/low_level/#messagesstate) populate our graph." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import StateGraph\n", + "\n", + "graph_builder = StateGraph(State)\n", + "graph_builder.add_node(node)\n", + "graph_builder.set_entry_point(\"node\")\n", + "graph = graph_builder.compile()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "LangGraph provides built-in utilities for visualizing your graph. Let's inspect our graph. See [this guide](../../how-tos/visualization) for detail on visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAGsAAACGCAIAAABVB+MHAAAAAXNSR0IArs4c6QAADyxJREFUeJztnX9UE1e+wG8yk1+TXyQEEuQ3DxEURFt0qdIKK9ouRShH+2wtfeqrnrpl23PW/tofdm1Pz7o9bHfrtn2v+LZsz2n1PLX7+kqzulb7LFuBomDf2qAgP4IoIUh+kUkmyUxmkv0jLnUlv0gmZHDz+c/MnTtfP9yZufO9d+6wvF4vSBAF7HgHsOBJGIyWhMFoSRiMloTBaEkYjBY4yv1tZrfV5HbYKAdKkW6vx7MA+kYQDGCYjUggRAzLVBxEFJUEVmT9QZMeH/kWG9VgXIQFvCxEDCESSCCEPdQCMAhzWHaUdKCUw0biTg+Hy84rEeaXiiTJnAhqm7NB+zTZpTZ6AUhScHJLhKkZ/AiOyij0o06tBrPcJEQyeE2tgsuf25VtbgZ7Tpv7uqxrNimW3Cuee6hMR9Nh7fqTsfzh5NL7k8Lfaw4G297T5a8ULSuXRhrhwuDiF2bTJLGxURVm+XBbbOsroyu/L7vr9QEA7q2WZxcK297ThbuDNwze36c1TrjCKXnXMPRX29E3r4dTMvRZ3PaebuX3ZVlLEBr+vguK/vOoTuusflwZvFgIg71nzAIRtOy+u//k9UvvF2aBMMR/P9h10D5Najqt/7T6AABl1fIvjxuClwlmsEttXLNJQXdUC4z7apO71MYgBQIaNOlxLwB3Zb9vTty7XmacwF0YGahAQIMj32JJikieciKjr68Px/F47R4coQTW9jkCbQ1ocFSD5ZYIYxTTHajV6h07djidzrjsHpK8EpFWYw+01b9B1OzmIex5e+aNuPn4OhKxa30+couFdgsZKO0UwKDJHaMhvLGxsT179lRUVNTU1Bw4cMDj8ajV6jfeeAMAUF1dXVZWplarAQA3b97cv39/dXV1eXn51q1bT5065dt9enq6rKzso48+2rdvX0VFxe7du/3uTjuk22s1uv1u8p8ac9goRAzFIpTXX3/92rVrzz//PIZhvb29bDZ77dq1jY2Nhw8fPnjwoEgkysrKAgCQJHn58uUtW7YkJSWdPXt23759mZmZy5Yt81XS2tr66KOPtrS0QBCkVCpn7047iARyoJQs1c+mAAZRCpHExODExERhYWFDQwMAoLGxEQAgl8szMjIAAMXFxUlJt5Ii6enpH3/8MYvFAgDU19dXV1e3t7fPGCwpKWlqapqpc/butCOUwBjq/3Yc8E7C4cZkAKCmpqa7u7u5udlsNgcvOTg4uHfv3oceeqihoYGiKJPJNLNp9erVsYgtCFw+O9DDm39NfCHbZgnYA4qGpqamvXv3nj59uq6u7vjx44GK9fT0bN++nSCI/fv3Nzc3S6VSj8czs1UgEMQitiBYjW5E7P989f8rIoYdtpgYZLFY27Ztq6+vP3DgQHNzc0FBwYoVK3ybbv8jv//++xkZGQcPHoRhOExlMZ2+EuTG4L8NimQQTxCTs9jX8xAKhXv27AEADAwMzAgyGL57Ap2eni4oKPDpIwjC4XDc3gbvYPbutCOUQmKZ/+cL/21QruQZxolpA5GUwqU3lJdfflkkEpWXl3d0dAAAioqKAAClpaUQBL355pt1dXU4jm/evNnXL2lra5NKpUeOHEFRdGRkJFArm707vTHrhp0eEgQaP4FeffVVvxtsFhKzkmm5NF9xxsfHOzo6Tp065XQ6n3322crKSgCARCJRKpVnzpw5d+4ciqK1tbWlpaVarfbo0aO9vb0bNmzYunXr559/XlhYmJyc/OGHH1ZUVCxdunSmztm70xvzpb9MK3P4qhz/zxcB84MTWmf/eXR9qPziPwMnWvUV9QppgCxBwMHmRXmCC6fMNwYdmQX+s9MoitbV1fndlJGRMT4+Pvv3devWvfbaa2FHHiG7du0aHh6e/XtRUVF/f//s34uLi999991AtfVfQHkCdiB9IXLUUzdcXx43bH0+0+9Wj8czOTnpv1KW/2oFAoFMJgt0OLowGAxut58nsEBRcblchSJgGrT1ldHHX8oM1JUJneX/6n8NWQVIzrJ5StIwjcvdVgdKrdooD1ImRJflgYaUv3xiQE3+H6rvbiZGnAM9tuD6QDijnbiLanlpmI4RxIWEE3Mf+slIOCXDGi8mcOrQT4ftVnfUgS0MpsZdrb/QkqQnnMLhzvpw2qn/br7+4L8p0/Pv8oHj4Uu23tOWx14MN0s2t5lHXx6bQi3utZsUinRepBEyF92I82u1SZnNu78hJfy95jz77fqAo1NtzCpElJn83GIhBLPmHiqzIFwebZ998prLrCfu25ScljO3x7AIZ2COfGsf/MY22octuVfM4bGFElgohfgItBCmsAKIzXLYSAwlMZSyW93jg868YlFBmSi7MJJOW4QGZ7g+4LBMERhKYlbK4/GSBJ0KKYrSaDQz6S+64CFsX9pZKIGS07hRXtmjNRhT7HZ7bW1te3t7vAMJRmIuf7QkDEYL0w36UrBMhukG/eajGAXTDcZuCJgumG5weno63iGEgOkGFy1aFO8QQsB0gxMTE/EOIQRMN1hSUhLvEELAdIMajSbeIYSA6QaZD9MNBhlFYwhMN2g0BnsTgQkw3WBKyhzSxXGB6QZjOiOLFphukPkw3WB+fn68QwgB0w36nUPEKJhukPkw3eDtMy2ZCdMNXrlyJd4hhIDpBpkP0w0mcjPRksjN3P0w3WBitDNaEqOddz9MN5gYL46WxHhxtCxevDjeIYSA6QaHhobiHUIImG6Q+TDdoEoV7lqU8YLpBgO9/MgcmG6wuLg43iGEgOkG+/r64h1CCJhuMNEGoyXRBqMlM9P/G/bMgYlv5OzevXtiYgKGYY/HYzQaFQoFm812u90nT56Md2h+YGIbfOKJJ1AU1el0er3e7Xbr9XqdTgdBMVlJLXqYaLCysvKOx2Gv18vYARMmGgQAPPnkkwjy3QuDaWlpjz32WFwjCghDDVZVVeXm5s5co0tLS5cvXx7voPzDUIMAgJ07d/rSqwqFgrENkNEGKysr8/LyfEPGjL0I0vCdpgggXB6HjXSgFIEHWRIPAAAe2fg0bjlWU7lT24cFKQaxAVfARiQwImRz+PN9y56//qBu2Dl8yT7W78BQkiuAuDxYIOUQTir6mnkIjFlwN05RpIcvhPOXC/OWC1XZ87QU9HwYvHrR9tevUNzpReSIJAXhIjFcat1lI2wGh8Pi4AvZqzcm5cZ+vavYGtSPuc4cnoL5nJR/kXN483rFcGGEccQMw96aHcrIPgIWJjE0qOm09nVjskwZX0zzQprhg1lchhHTAw3yvGJRjA4RK4MXTlu0V3DVEka8y6DTTJatl8ToOxcxMfj1ScvYEKEqYNDrSPr+qWWrhcsrJLTXTH9/sK/Len0IZ5Q+AEBaUaqm0zbWH6xXFBk0G5wcc/Z1Y8oCRpy8d5C+XNXxmcU2TfNaijQbPHPEkJQR83VCI0aySHrm8BS9ddJpcKAXhfmcON55QyJWIHbUqxum81swdBq89JVNkRdqzc14o8iTXTxrobFC2gzqhp0E7p2HbvP53rYXXvkeikb41iwi5RtuEIE+1xIBtBkc0dgFsoWxPKZQgYz2Bfzu0lyhzeDYgFOSsjAMihSI9nLAb3/NFXpOOjfhsZndmWGkDPb9cv3mTS/39bdfudop4IvKVzVsrNrl24SiRvWp3/UPdXkoMie7dNODz6Wpbr3YqZu4+unJ397QXZGIFSnJ/7BC6rD24skz/zkxOSgWyfNzy36w4YcScYiuqEDM1Wlo+zgWPW3QgVI8QbiJuaOfvLZIVfDMUy33lP7g9NnfX7naCQAgCFfLB01D2p6HN/5oc91PUNTY8kGT02kDANw0XHvvDz9EUUPNhmfWrdmm01+dqWpopOf3Hz6nTM3910d+/sCabdpr/9/yQRNBuIIHAHEgyu2hSHoexuhpgxhKwrxwDa6+p279uh0AgEWqggsX2waHu5cuWXvx0p+njNee3vkfi/PKAAC52St+9VbDue5jG6t2nfj8HRaL/ezTrSKhDADAYrM/UTf7qvr0xG/Kyxoaal/w/bMg/3u/fnvr4MiF4qIHgsfARWAMJSVyGnI2NJ3FuIcnDLcbyOXeWioWgiCpJNWKGgAA2tFv+HyRTx8AQC5LS1XkjOv6CcJ1dbj7vlWbffoAABD7Vsxmi/6mYdRovtHd++nt9VvR0H1mYRIXd1IAMMYgIoZdaCRXFjYb9ngoAIATt884ulUnIkVtRtRmpChSLkubva/NbgIAbKjatXxp1e2/i0NdBwEAqNElktKTNKTJoAQiXFHl66WS1Os3/mGSkc1uSpIqfVrtdj99YAFfDABwu/HUlJw5Hcvr9bpxj0BEz4gKPXcSRAyJkqL6Y+Rkljic6NjfJU5MDhlNN3KzV/D5QkVy5qXL/0eSd/aBUxRZSVJVzzdqnLj1lEZR5OxisyFxSpVLW8eLHoMsFosnYNuMkXey7il9KCU566NjP+vu/fT8xc8+OPKiSChbs3ozAGBj1S6Tefyd/9rV2f1x14X/ae88MnPQ+pofozbjO4ee6jz/x3NfH3v70FNdF/4Y8lg2o1Mio+3ZibYe9eKVQswUuUEIgndvfzsjvUj959+1nfhNqiL7madaxCK5T27Dwy84nNY/nX7nwkV1duZ3czJLllb+e+NvIYjz2cm3vmj/g0ymystZGfJYmBlbvIK2ESjactQ2i7vt0GRGKdMXXAQAjJ6/sf0X2Ww2Pevh09YGxTKOXMmxTtL2vBkjTGPT+StFdOmjObt1/yPJhpEQX+OMO/qrloq6ZBorpNOgWMYpXCWe1ttorJNeTNcta+uSfZ+kpQuas/wV9Qp0wuqyE/RWSwt2k8OL4yuraB6EoH+srvGnWSNf62ivNkpInNL3G7c8l057zTEZL3Y5qONv6dJLVBCHEZOfcYwwDBkffzEjFt+jicn8QT4CbXlu0XDXuBMNkWiaB+wmbLJ/attLMdEX85lHJ1r1DgcrKVM2z9OOfOAYYblukaWwH3wyhi+Ixnz220AP2tFmSs4S8yWIQDpP38fCLC6Hxe60uCrqk/NKYjXnyMc8zcDs67R+24liVlKiEnL4HJgHc3gQzKXpKukFboIicZIkKMKOT0865CpuSYWkaBX9s2RmM6/vNGFWcvQyNjmG+z6OzOFCdjrmYIgVXLeLEklhqQJWZvFyi4V8ZP7uYEx8K2xhwdy5/AuFhMFoSRiMloTBaEkYjJaEwWj5G9cmpR4/Ig13AAAAAElFTkSuQmCC", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, our graph just executes a single node." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use graph\n", + "\n", + "Let's proceed with a simple invocation:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'messages': [HumanMessage(content='Hi', additional_kwargs={}, response_metadata={}),\n", + " AIMessage(content='Hello!', additional_kwargs={}, response_metadata={})],\n", + " 'extra_field': 10}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.messages import HumanMessage\n", + "\n", + "result = graph.invoke({\"messages\": [HumanMessage(\"Hi\")]})\n", + "result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that:\n", + "\n", + "- We kicked off invocation by updating a single key of the state.\n", + "- We receive the entire state in the invocation result.\n", + "\n", + "For convenience, we frequently inspect the content of [message objects](https://python.langchain.com/docs/concepts/messages/) via pretty-print:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Hi\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hello!\n" + ] + } + ], + "source": [ + "for message in result[\"messages\"]:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Process state updates with reducers\n", + "\n", + "Each key in the state can have its own independent [reducer](../../concepts/low_level/#reducers) function, which controls how updates from nodes are applied. If no reducer function is explicitly specified then it is assumed that all updates to the key should override it.\n", + "\n", + "For `TypedDict` state schemas, we can define reducers by annotating the corresponding field of the state with a reducer function.\n", + "\n", + "In the earlier example, our node updated the `\"messages\"` key in the state by appending a message to it. Below, we add a reducer to this key, such that updates are automatically appended:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from typing_extensions import Annotated\n", + "\n", + "\n", + "def add(left, right):\n", + " \"\"\"Can also import `add` from the `operator` built-in.\"\"\"\n", + " return left + right\n", + "\n", + "\n", + "class State(TypedDict):\n", + " # highlight-next-line\n", + " messages: Annotated[list[AnyMessage], add]\n", + " extra_field: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now our node can be simplified:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def node(state: State):\n", + " new_message = AIMessage(\"Hello!\")\n", + " # highlight-next-line\n", + " return {\"messages\": [new_message], \"extra_field\": 10}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Hi\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hello!\n" + ] + } + ], + "source": [ + "from langgraph.graph import START\n", + "\n", + "\n", + "graph = StateGraph(State).add_node(node).add_edge(START, \"node\").compile()\n", + "\n", + "result = graph.invoke({\"messages\": [HumanMessage(\"Hi\")]})\n", + "\n", + "for message in result[\"messages\"]:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### MessagesState\n", + "\n", + "In practice, there are additional considerations for updating lists of messages:\n", + "\n", + "- We may wish to update an existing message in the state.\n", + "- We may want to accept short-hands for [message formats](../../concepts/low_level/#using-messages-in-your-graph), such as [OpenAI format](https://python.langchain.com/docs/concepts/messages/#openai-format).\n", + "\n", + "LangGraph includes a built-in reducer `add_messages` that handles these considerations:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph.message import add_messages\n", + "\n", + "\n", + "class State(TypedDict):\n", + " # highlight-next-line\n", + " messages: Annotated[list[AnyMessage], add_messages]\n", + " extra_field: int\n", + "\n", + "\n", + "def node(state: State):\n", + " new_message = AIMessage(\"Hello!\")\n", + " return {\"messages\": [new_message], \"extra_field\": 10}\n", + "\n", + "\n", + "graph = StateGraph(State).add_node(node).set_entry_point(\"node\").compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "Hi\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hello!\n" + ] + } + ], + "source": [ + "# highlight-next-line\n", + "input_message = {\"role\": \"user\", \"content\": \"Hi\"}\n", + "\n", + "result = graph.invoke({\"messages\": [input_message]})\n", + "\n", + "for message in result[\"messages\"]:\n", + " message.pretty_print()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a versatile representation of state for applications involving [chat models](https://python.langchain.com/docs/concepts/chat_models/). LangGraph includes a pre-built `MessagesState` for convenience, so that we can have:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import MessagesState\n", + "\n", + "\n", + "class State(MessagesState):\n", + " extra_field: int" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "- Continue with the [Graph API Basics](../../how-tos/#graph-api-basics) guides.\n", + "- See more detail on [state management](../../how-tos/#state-management)." + ] + } + ], + "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.10.4" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 760139a54a..28bff1634a 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -104,12 +104,20 @@ nav: - how-tos/index.md - LangGraph: - LangGraph: how-tos#langgraph + - Graph API Basics: + - Graph API Basics: how-tos#graph-api-basics + - how-tos/state-reducers.ipynb + - how-tos/sequence.ipynb + - how-tos/branching.ipynb + - how-tos/recursion-limit.ipynb + - how-tos/visualization.ipynb - Controllability: - Controllability: how-tos#controllability - - how-tos/branching.ipynb - how-tos/map-reduce.ipynb - - how-tos/recursion-limit.ipynb - how-tos/command.ipynb + - how-tos/configuration.ipynb + - how-tos/node-retries.ipynb + - how-tos/return-when-recursion-limit-hits.ipynb - Persistence: - Persistence: how-tos#persistence - how-tos/persistence.ipynb @@ -177,12 +185,8 @@ nav: - Other: - Other: how-tos#other - how-tos/async.ipynb - - how-tos/visualization.ipynb - - how-tos/configuration.ipynb - - how-tos/node-retries.ipynb - how-tos/react-agent-structured-output.ipynb - how-tos/run-id-langsmith.ipynb - - how-tos/return-when-recursion-limit-hits.ipynb - how-tos/autogen-integration.ipynb - how-tos/autogen-integration-functional.ipynb - Prebuilt ReAct Agent: