From 17dc1108a3eb6f92285e8929469d6947a6833a4e Mon Sep 17 00:00:00 2001 From: Isaac Francisco <78627776+isahers1@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:55:53 -0700 Subject: [PATCH] docs: __start__/__end__ consistency (#1874) * changes * skip mermaid * retries fix * viz error * changes --- docs/_scripts/prepare_notebooks_for_ci.py | 7 +++ .../docs/how-tos/memory/delete-messages.ipynb | 8 ++- .../memory/manage-conversation-history.ipynb | 14 +++-- docs/docs/how-tos/pass-config-to-tools.ipynb | 9 +-- docs/docs/how-tos/persistence.ipynb | 7 ++- .../return-when-recursion-limit-hits.ipynb | 10 ++-- docs/docs/how-tos/streaming-tokens.ipynb | 6 +- .../docs/how-tos/subgraphs-manage-state.ipynb | 6 +- docs/docs/how-tos/tool-calling-errors.ipynb | 23 +++---- docs/docs/how-tos/tool-calling.ipynb | 9 +-- .../information-gather-prompting.ipynb | 4 +- .../customer-support/customer-support.ipynb | 60 ++++++------------- docs/docs/tutorials/extraction/retries.ipynb | 10 ++-- docs/docs/tutorials/introduction.ipynb | 22 +++---- docs/docs/tutorials/lats/lats.ipynb | 4 +- .../multi-agent-collaboration.ipynb | 8 +-- .../plan-and-execute/plan-and-execute.ipynb | 7 ++- docs/docs/tutorials/reflexion/reflexion.ipynb | 4 +- 18 files changed, 110 insertions(+), 108 deletions(-) diff --git a/docs/_scripts/prepare_notebooks_for_ci.py b/docs/_scripts/prepare_notebooks_for_ci.py index 3219dc7e2..b57d93daa 100644 --- a/docs/_scripts/prepare_notebooks_for_ci.py +++ b/docs/_scripts/prepare_notebooks_for_ci.py @@ -62,6 +62,9 @@ def is_magic_command(code: str) -> bool: def is_comment(code: str) -> bool: return code.strip().startswith("#") +def is_mermaid_command(code: str) -> bool: + return "draw_mermaid_png" in code.strip() + def add_vcr_to_notebook( notebook: nbformat.NotebookNode, cassette_prefix: str @@ -84,6 +87,10 @@ def add_vcr_to_notebook( if all(are_magic_lines): continue + # skip if using mermaid + if any(is_mermaid_command(line) for line in lines): + continue + if any(are_magic_lines): raise ValueError( "Cannot process code cells with mixed magic and non-magic code." diff --git a/docs/docs/how-tos/memory/delete-messages.ipynb b/docs/docs/how-tos/memory/delete-messages.ipynb index 30838954e..805407307 100644 --- a/docs/docs/how-tos/memory/delete-messages.ipynb +++ b/docs/docs/how-tos/memory/delete-messages.ipynb @@ -107,7 +107,7 @@ "from langchain_core.tools import tool\n", "\n", "from langgraph.checkpoint.memory import MemorySaver\n", - "from langgraph.graph import MessagesState, StateGraph, START\n", + "from langgraph.graph import MessagesState, StateGraph, START, END\n", "from langgraph.prebuilt import ToolNode\n", "\n", "memory = MemorySaver()\n", @@ -127,12 +127,12 @@ "bound_model = model.bind_tools(tools)\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"action\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " \"\"\"Return the next node to execute.\"\"\"\n", " last_message = state[\"messages\"][-1]\n", " # If there is no function call, then we finish\n", " if not last_message.tool_calls:\n", - " return \"__end__\"\n", + " return END\n", " # Otherwise if there is, we continue\n", " return \"action\"\n", "\n", @@ -162,6 +162,8 @@ " \"agent\",\n", " # Next, we pass in the function that will determine which node is called next.\n", " should_continue,\n", + " # Next, we pass in the path map - all the possible nodes this edge could go to\n", + " ['action', END]\n", ")\n", "\n", "# We now add a normal edge from `tools` to `agent`.\n", diff --git a/docs/docs/how-tos/memory/manage-conversation-history.ipynb b/docs/docs/how-tos/memory/manage-conversation-history.ipynb index efa26872c..3b3370f30 100644 --- a/docs/docs/how-tos/memory/manage-conversation-history.ipynb +++ b/docs/docs/how-tos/memory/manage-conversation-history.ipynb @@ -98,7 +98,7 @@ "from langchain_core.tools import tool\n", "\n", "from langgraph.checkpoint.memory import MemorySaver\n", - "from langgraph.graph import MessagesState, StateGraph, START\n", + "from langgraph.graph import MessagesState, StateGraph, START, END\n", "from langgraph.prebuilt import ToolNode\n", "\n", "memory = MemorySaver()\n", @@ -118,12 +118,12 @@ "bound_model = model.bind_tools(tools)\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"action\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " \"\"\"Return the next node to execute.\"\"\"\n", " last_message = state[\"messages\"][-1]\n", " # If there is no function call, then we finish\n", " if not last_message.tool_calls:\n", - " return \"__end__\"\n", + " return END\n", " # Otherwise if there is, we continue\n", " return \"action\"\n", "\n", @@ -153,6 +153,8 @@ " \"agent\",\n", " # Next, we pass in the function that will determine which node is called next.\n", " should_continue,\n", + " # Next, we pass in the path map - all the possible nodes this edge could go to\n", + " ['action',END]\n", ")\n", "\n", "# We now add a normal edge from `tools` to `agent`.\n", @@ -247,12 +249,12 @@ "bound_model = model.bind_tools(tools)\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"action\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " \"\"\"Return the next node to execute.\"\"\"\n", " last_message = state[\"messages\"][-1]\n", " # If there is no function call, then we finish\n", " if not last_message.tool_calls:\n", - " return \"__end__\"\n", + " return END\n", " # Otherwise if there is, we continue\n", " return \"action\"\n", "\n", @@ -288,6 +290,8 @@ " \"agent\",\n", " # Next, we pass in the function that will determine which node is called next.\n", " should_continue,\n", + " # Next, we pass in the pathmap - all the possible nodes this edge could go to\n", + " ['action', END]\n", ")\n", "\n", "# We now add a normal edge from `tools` to `agent`.\n", diff --git a/docs/docs/how-tos/pass-config-to-tools.ipynb b/docs/docs/how-tos/pass-config-to-tools.ipynb index e8b919357..cb65edfbe 100644 --- a/docs/docs/how-tos/pass-config-to-tools.ipynb +++ b/docs/docs/how-tos/pass-config-to-tools.ipynb @@ -180,15 +180,15 @@ "source": [ "from typing import Literal\n", "\n", - "from langgraph.graph import StateGraph, MessagesState\n", + "from langgraph.graph import StateGraph, MessagesState, START, END\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"tools\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", " if last_message.tool_calls:\n", " return \"tools\"\n", - " return \"__end__\"\n", + " return END\n", "\n", "\n", "def call_model(state: MessagesState):\n", @@ -203,10 +203,11 @@ "workflow.add_node(\"agent\", call_model)\n", "workflow.add_node(\"tools\", tool_node)\n", "\n", - "workflow.add_edge(\"__start__\", \"agent\")\n", + "workflow.add_edge(START, \"agent\")\n", "workflow.add_conditional_edges(\n", " \"agent\",\n", " should_continue,\n", + " [\"tools\",END]\n", ")\n", "workflow.add_edge(\"tools\", \"agent\")\n", "\n", diff --git a/docs/docs/how-tos/persistence.ipynb b/docs/docs/how-tos/persistence.ipynb index 2f928fc24..0784dd54c 100644 --- a/docs/docs/how-tos/persistence.ipynb +++ b/docs/docs/how-tos/persistence.ipynb @@ -283,14 +283,14 @@ "source": [ "# Define the function that determines whether to continue or not\n", "from typing import Literal\n", + "from langgraph.graph import END\n", "\n", - "\n", - "def should_continue(state: State) -> Literal[\"action\", \"__end__\"]:\n", + "def should_continue(state: State):\n", " \"\"\"Return the next node to execute.\"\"\"\n", " last_message = state[\"messages\"][-1]\n", " # If there is no function call, then we finish\n", " if not last_message.tool_calls:\n", - " return \"__end__\"\n", + " return END\n", " # Otherwise if there is, we continue\n", " return \"action\"\n", "\n", @@ -339,6 +339,7 @@ " \"agent\",\n", " # Next, we pass in the function that will determine which node is called next.\n", " should_continue,\n", + " [\"action\", END]\n", ")\n", "\n", "# We now add a normal edge from `tools` to `agent`.\n", diff --git a/docs/docs/how-tos/return-when-recursion-limit-hits.ipynb b/docs/docs/how-tos/return-when-recursion-limit-hits.ipynb index eb193c10a..7e9f3291a 100644 --- a/docs/docs/how-tos/return-when-recursion-limit-hits.ipynb +++ b/docs/docs/how-tos/return-when-recursion-limit-hits.ipynb @@ -65,7 +65,7 @@ "\n", "def router(state: State):\n", " if state['value'] == \"end\":\n", - " return \"__end__\"\n", + " return END\n", " else:\n", " return \"action\"\n", "\n", @@ -80,7 +80,7 @@ "workflow.add_node('decision',decision_node)\n", "workflow.add_node('action',action_node)\n", "workflow.add_edge(START,'decision')\n", - "workflow.add_conditional_edges('decision',router)\n", + "workflow.add_conditional_edges('decision',router,['action',END])\n", "workflow.add_edge('action','decision')\n", "app = workflow.compile()" ] @@ -177,9 +177,9 @@ "def router(state: State):\n", " # Force the agent to end if it is on the last step\n", " if state['is_last_step']:\n", - " return \"__end__\"\n", + " return END\n", " if state['value'] == \"end\":\n", - " return \"__end__\"\n", + " return END\n", " else:\n", " return \"action\"\n", "\n", @@ -194,7 +194,7 @@ "workflow.add_node('decision',decision_node)\n", "workflow.add_node('action',action_node)\n", "workflow.add_edge(START,'decision')\n", - "workflow.add_conditional_edges('decision',router)\n", + "workflow.add_conditional_edges('decision',router,['action',END])\n", "workflow.add_edge('action','decision')\n", "app = workflow.compile()" ] diff --git a/docs/docs/how-tos/streaming-tokens.ipynb b/docs/docs/how-tos/streaming-tokens.ipynb index ce228f4b2..50843d096 100644 --- a/docs/docs/how-tos/streaming-tokens.ipynb +++ b/docs/docs/how-tos/streaming-tokens.ipynb @@ -282,7 +282,7 @@ "\n", "\n", "# Define the function that determines whether to continue or not\n", - "def should_continue(state: State) -> Literal[\"__end__\", \"tools\"]:\n", + "def should_continue(state: State):\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", " # If there is no function call, then we finish\n", @@ -338,6 +338,8 @@ " \"agent\",\n", " # Next, we pass in the function that will determine which node is called next.\n", " should_continue,\n", + " # Next we pass in the path map - all the nodes this edge could go to\n", + " [\"tools\",END]\n", ")\n", "\n", "workflow.add_edge(\"tools\", \"agent\")\n", @@ -443,7 +445,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.9.6" } }, "nbformat": 4, diff --git a/docs/docs/how-tos/subgraphs-manage-state.ipynb b/docs/docs/how-tos/subgraphs-manage-state.ipynb index 304653e6e..0da0afdc5 100644 --- a/docs/docs/how-tos/subgraphs-manage-state.ipynb +++ b/docs/docs/how-tos/subgraphs-manage-state.ipynb @@ -772,17 +772,17 @@ " # Dummy logic that will always continue\n", " return {\"to_continue\": True}\n", "\n", - "def route_after_prediction(state: GrandfatherState) -> Literal[\"graph\", \"__end__\"]:\n", + "def route_after_prediction(state: GrandfatherState):\n", " if state['to_continue']:\n", " return \"graph\"\n", " else:\n", - " return \"__end__\"\n", + " return END\n", "\n", "grandparent_graph = StateGraph(GrandfatherState)\n", "grandparent_graph.add_node(router_node)\n", "grandparent_graph.add_node(\"graph\", graph)\n", "grandparent_graph.add_edge(START, \"router_node\")\n", - "grandparent_graph.add_conditional_edges(\"router_node\", route_after_prediction)\n", + "grandparent_graph.add_conditional_edges(\"router_node\", route_after_prediction, ['graph',END])\n", "grandparent_graph.add_edge(\"graph\", END)\n", "grandparent_graph = grandparent_graph.compile(checkpointer=MemorySaver())" ] diff --git a/docs/docs/how-tos/tool-calling-errors.ipynb b/docs/docs/how-tos/tool-calling-errors.ipynb index fb1e814c8..0ff1809eb 100644 --- a/docs/docs/how-tos/tool-calling-errors.ipynb +++ b/docs/docs/how-tos/tool-calling-errors.ipynb @@ -124,7 +124,7 @@ "from typing import Literal\n", "\n", "from langchain_anthropic import ChatAnthropic\n", - "from langgraph.graph import StateGraph, MessagesState\n", + "from langgraph.graph import StateGraph, MessagesState, START, END\n", "from langgraph.prebuilt import ToolNode\n", "\n", "tool_node = ToolNode([get_weather])\n", @@ -134,12 +134,12 @@ ").bind_tools([get_weather])\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"tools\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", " if last_message.tool_calls:\n", " return \"tools\"\n", - " return \"__end__\"\n", + " return END\n", "\n", "\n", "def call_model(state: MessagesState):\n", @@ -154,10 +154,11 @@ "workflow.add_node(\"agent\", call_model)\n", "workflow.add_node(\"tools\", tool_node)\n", "\n", - "workflow.add_edge(\"__start__\", \"agent\")\n", + "workflow.add_edge(START, \"agent\")\n", "workflow.add_conditional_edges(\n", " \"agent\",\n", " should_continue,\n", + " ['tools',END]\n", ")\n", "workflow.add_edge(\"tools\", \"agent\")\n", "\n", @@ -319,12 +320,12 @@ "model_with_tools = model.bind_tools([master_haiku_generator])\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"tools\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", " if last_message.tool_calls:\n", " return \"tools\"\n", - " return \"__end__\"\n", + " return END\n", "\n", "\n", "def call_model(state: MessagesState):\n", @@ -339,10 +340,11 @@ "workflow.add_node(\"agent\", call_model)\n", "workflow.add_node(\"tools\", tool_node)\n", "\n", - "workflow.add_edge(\"__start__\", \"agent\")\n", + "workflow.add_edge(START, \"agent\")\n", "workflow.add_conditional_edges(\n", " \"agent\",\n", " should_continue,\n", + " ['tools',END]\n", ")\n", "workflow.add_edge(\"tools\", \"agent\")\n", "\n", @@ -424,12 +426,12 @@ "better_model_with_tools = better_model.bind_tools([master_haiku_generator])\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"tools\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", " if last_message.tool_calls:\n", " return \"tools\"\n", - " return \"__end__\"\n", + " return END\n", "\n", "\n", "def should_fallback(\n", @@ -480,10 +482,11 @@ "workflow.add_node(\"remove_failed_tool_call_attempt\", remove_failed_tool_call_attempt)\n", "workflow.add_node(\"fallback_agent\", call_fallback_model)\n", "\n", - "workflow.add_edge(\"__start__\", \"agent\")\n", + "workflow.add_edge(START, \"agent\")\n", "workflow.add_conditional_edges(\n", " \"agent\",\n", " should_continue,\n", + " ['tools',END]\n", ")\n", "workflow.add_conditional_edges(\"tools\", should_fallback)\n", "workflow.add_edge(\"remove_failed_tool_call_attempt\", \"fallback_agent\")\n", diff --git a/docs/docs/how-tos/tool-calling.ipynb b/docs/docs/how-tos/tool-calling.ipynb index 92bb9697b..f895915f0 100644 --- a/docs/docs/how-tos/tool-calling.ipynb +++ b/docs/docs/how-tos/tool-calling.ipynb @@ -311,15 +311,15 @@ "source": [ "from typing import Literal\n", "\n", - "from langgraph.graph import StateGraph, MessagesState\n", + "from langgraph.graph import StateGraph, MessagesState, START, END\n", "\n", "\n", - "def should_continue(state: MessagesState) -> Literal[\"tools\", \"__end__\"]:\n", + "def should_continue(state: MessagesState):\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", " if last_message.tool_calls:\n", " return \"tools\"\n", - " return \"__end__\"\n", + " return END\n", "\n", "\n", "def call_model(state: MessagesState):\n", @@ -334,10 +334,11 @@ "workflow.add_node(\"agent\", call_model)\n", "workflow.add_node(\"tools\", tool_node)\n", "\n", - "workflow.add_edge(\"__start__\", \"agent\")\n", + "workflow.add_edge(START, \"agent\")\n", "workflow.add_conditional_edges(\n", " \"agent\",\n", " should_continue,\n", + " ['tools',END]\n", ")\n", "workflow.add_edge(\"tools\", \"agent\")\n", "\n", diff --git a/docs/docs/tutorials/chatbots/information-gather-prompting.ipynb b/docs/docs/tutorials/chatbots/information-gather-prompting.ipynb index 4f7c6bf7b..03e253513 100644 --- a/docs/docs/tutorials/chatbots/information-gather-prompting.ipynb +++ b/docs/docs/tutorials/chatbots/information-gather-prompting.ipynb @@ -229,7 +229,7 @@ "from langgraph.graph import END\n", "\n", "\n", - "def get_state(state) -> Literal[\"add_tool_message\", \"info\", \"__end__\"]:\n", + "def get_state(state):\n", " messages = state[\"messages\"]\n", " if isinstance(messages[-1], AIMessage) and messages[-1].tool_calls:\n", " return \"add_tool_message\"\n", @@ -285,7 +285,7 @@ " }\n", "\n", "\n", - "workflow.add_conditional_edges(\"info\", get_state)\n", + "workflow.add_conditional_edges(\"info\", get_state, [\"add_tool_message\", \"info\", END])\n", "workflow.add_edge(\"add_tool_message\", \"prompt\")\n", "workflow.add_edge(\"prompt\", END)\n", "workflow.add_edge(START, \"info\")\n", diff --git a/docs/docs/tutorials/customer-support/customer-support.ipynb b/docs/docs/tutorials/customer-support/customer-support.ipynb index 1da82c973..26c7fc510 100644 --- a/docs/docs/tutorials/customer-support/customer-support.ipynb +++ b/docs/docs/tutorials/customer-support/customer-support.ipynb @@ -2545,7 +2545,7 @@ "builder.add_edge(\"fetch_user_info\", \"assistant\")\n", "\n", "\n", - "def route_tools(state: State) -> Literal[\"safe_tools\", \"sensitive_tools\", \"__end__\"]:\n", + "def route_tools(state: State):\n", " next_node = tools_condition(state)\n", " # If no tools are invoked, return to the user\n", " if next_node == END:\n", @@ -2562,6 +2562,7 @@ "builder.add_conditional_edges(\n", " \"assistant\",\n", " route_tools,\n", + " [\"safe_tools\", \"sensitive_tools\", END]\n", ")\n", "builder.add_edge(\"safe_tools\", \"assistant\")\n", "builder.add_edge(\"sensitive_tools\", \"assistant\")\n", @@ -3523,12 +3524,7 @@ "\n", "def route_update_flight(\n", " state: State,\n", - ") -> Literal[\n", - " \"update_flight_sensitive_tools\",\n", - " \"update_flight_safe_tools\",\n", - " \"leave_skill\",\n", - " \"__end__\",\n", - "]:\n", + "):\n", " route = tools_condition(state)\n", " if route == END:\n", " return END\n", @@ -3544,7 +3540,7 @@ "\n", "builder.add_edge(\"update_flight_sensitive_tools\", \"update_flight\")\n", "builder.add_edge(\"update_flight_safe_tools\", \"update_flight\")\n", - "builder.add_conditional_edges(\"update_flight\", route_update_flight)\n", + "builder.add_conditional_edges(\"update_flight\", route_update_flight, [\"update_flight_sensitive_tools\",\"update_flight_safe_tools\",\"leave_skill\",END])\n", "\n", "\n", "# This node will be shared for exiting all specialized assistants\n", @@ -3608,12 +3604,7 @@ "\n", "def route_book_car_rental(\n", " state: State,\n", - ") -> Literal[\n", - " \"book_car_rental_safe_tools\",\n", - " \"book_car_rental_sensitive_tools\",\n", - " \"leave_skill\",\n", - " \"__end__\",\n", - "]:\n", + "):\n", " route = tools_condition(state)\n", " if route == END:\n", " return END\n", @@ -3629,7 +3620,7 @@ "\n", "builder.add_edge(\"book_car_rental_sensitive_tools\", \"book_car_rental\")\n", "builder.add_edge(\"book_car_rental_safe_tools\", \"book_car_rental\")\n", - "builder.add_conditional_edges(\"book_car_rental\", route_book_car_rental)" + "builder.add_conditional_edges(\"book_car_rental\", route_book_car_rental, [\"book_car_rental_safe_tools\",\"book_car_rental_sensitive_tools\",\"leave_skill\",END])" ] }, { @@ -3665,9 +3656,7 @@ "\n", "def route_book_hotel(\n", " state: State,\n", - ") -> Literal[\n", - " \"leave_skill\", \"book_hotel_safe_tools\", \"book_hotel_sensitive_tools\", \"__end__\"\n", - "]:\n", + "):\n", " route = tools_condition(state)\n", " if route == END:\n", " return END\n", @@ -3683,7 +3672,7 @@ "\n", "builder.add_edge(\"book_hotel_sensitive_tools\", \"book_hotel\")\n", "builder.add_edge(\"book_hotel_safe_tools\", \"book_hotel\")\n", - "builder.add_conditional_edges(\"book_hotel\", route_book_hotel)" + "builder.add_conditional_edges(\"book_hotel\", route_book_hotel, [\"leave_skill\", \"book_hotel_safe_tools\", \"book_hotel_sensitive_tools\", END])" ] }, { @@ -3720,12 +3709,7 @@ "\n", "def route_book_excursion(\n", " state: State,\n", - ") -> Literal[\n", - " \"book_excursion_safe_tools\",\n", - " \"book_excursion_sensitive_tools\",\n", - " \"leave_skill\",\n", - " \"__end__\",\n", - "]:\n", + "):\n", " route = tools_condition(state)\n", " if route == END:\n", " return END\n", @@ -3741,7 +3725,7 @@ "\n", "builder.add_edge(\"book_excursion_sensitive_tools\", \"book_excursion\")\n", "builder.add_edge(\"book_excursion_safe_tools\", \"book_excursion\")\n", - "builder.add_conditional_edges(\"book_excursion\", route_book_excursion)" + "builder.add_conditional_edges(\"book_excursion\", route_book_excursion, [\"book_excursion_safe_tools\",\"book_excursion_sensitive_tools\",\"leave_skill\",END])" ] }, { @@ -3768,13 +3752,7 @@ "\n", "def route_primary_assistant(\n", " state: State,\n", - ") -> Literal[\n", - " \"primary_assistant_tools\",\n", - " \"enter_update_flight\",\n", - " \"enter_book_hotel\",\n", - " \"enter_book_excursion\",\n", - " \"__end__\",\n", - "]:\n", + "):\n", " route = tools_condition(state)\n", " if route == END:\n", " return END\n", @@ -3797,14 +3775,14 @@ "builder.add_conditional_edges(\n", " \"primary_assistant\",\n", " route_primary_assistant,\n", - " {\n", - " \"enter_update_flight\": \"enter_update_flight\",\n", - " \"enter_book_car_rental\": \"enter_book_car_rental\",\n", - " \"enter_book_hotel\": \"enter_book_hotel\",\n", - " \"enter_book_excursion\": \"enter_book_excursion\",\n", - " \"primary_assistant_tools\": \"primary_assistant_tools\",\n", - " END: END,\n", - " },\n", + " [\n", + " \"enter_update_flight\",\n", + " \"enter_book_car_rental\",\n", + " \"enter_book_hotel\",\n", + " \"enter_book_excursion\",\n", + " \"primary_assistant_tools\",\n", + " END,\n", + " ],\n", ")\n", "builder.add_edge(\"primary_assistant_tools\", \"primary_assistant\")\n", "\n", diff --git a/docs/docs/tutorials/extraction/retries.ipynb b/docs/docs/tutorials/extraction/retries.ipynb index f68e0e342..23e5eef9f 100644 --- a/docs/docs/tutorials/extraction/retries.ipynb +++ b/docs/docs/tutorials/extraction/retries.ipynb @@ -286,16 +286,16 @@ " builder.add_edge(START, \"count_messages\")\n", " builder.add_edge(\"count_messages\", \"llm\")\n", "\n", - " def route_validator(state: State) -> Literal[\"validator\", \"__end__\"]:\n", + " def route_validator(state: State):\n", " if state[\"messages\"][-1].tool_calls or tool_choice is not None:\n", " return \"validator\"\n", - " return \"__end__\"\n", + " return END\n", "\n", - " builder.add_conditional_edges(\"llm\", route_validator)\n", + " builder.add_conditional_edges(\"llm\", route_validator, ['validator',END])\n", " builder.add_edge(\"fallback\", \"validator\")\n", " max_attempts = retry_strategy.get(\"max_attempts\", 3)\n", "\n", - " def route_validation(state: State) -> Literal[\"finalizer\", \"fallback\"]:\n", + " def route_validation(state: State):\n", " if state[\"attempt_number\"] > max_attempts:\n", " raise ValueError(\n", " f\"Could not extract a valid value in {max_attempts} attempts.\"\n", @@ -307,7 +307,7 @@ " return \"fallback\"\n", " return \"finalizer\"\n", "\n", - " builder.add_conditional_edges(\"validator\", route_validation)\n", + " builder.add_conditional_edges(\"validator\", route_validation, [\"finalizer\", \"fallback\"])\n", "\n", " builder.add_edge(\"finalizer\", END)\n", "\n", diff --git a/docs/docs/tutorials/introduction.ipynb b/docs/docs/tutorials/introduction.ipynb index 9aa6f0ab6..4707fe311 100644 --- a/docs/docs/tutorials/introduction.ipynb +++ b/docs/docs/tutorials/introduction.ipynb @@ -464,7 +464,7 @@ "from langchain_anthropic import ChatAnthropic\n", "from typing_extensions import TypedDict\n", "\n", - "from langgraph.graph import StateGraph, START\n", + "from langgraph.graph import StateGraph, START, END\n", "from langgraph.graph.message import add_messages\n", "\n", "\n", @@ -552,7 +552,7 @@ "\n", "Below, call define a router function called `route_tools`, that checks for tool_calls in the chatbot's output. Provide this function to the graph by calling `add_conditional_edges`, which tells the graph that whenever the `chatbot` node completes to check this function to see where to go next. \n", "\n", - "The condition will route to `tools` if tool calls are present and \"`__end__`\" if not.\n", + "The condition will route to `tools` if tool calls are present and `END` if not.\n", "\n", "Later, we will replace this with the prebuilt [tools_condition](https://langchain-ai.github.io/langgraph/reference/prebuilt/#tools_condition) to be more concise, but implementing it ourselves first makes things more clear. " ] @@ -569,7 +569,7 @@ "\n", "def route_tools(\n", " state: State,\n", - ") -> Literal[\"tools\", \"__end__\"]:\n", + "):\n", " \"\"\"\n", " Use in the conditional_edge to route to the ToolNode if the last message\n", " has tool calls. Otherwise, route to the end.\n", @@ -582,10 +582,10 @@ " raise ValueError(f\"No messages found in input state to tool_edge: {state}\")\n", " if hasattr(ai_message, \"tool_calls\") and len(ai_message.tool_calls) > 0:\n", " return \"tools\"\n", - " return \"__end__\"\n", + " return END\n", "\n", "\n", - "# The `tools_condition` function returns \"tools\" if the chatbot asks to use a tool, and \"__end__\" if\n", + "# The `tools_condition` function returns \"tools\" if the chatbot asks to use a tool, and \"END\" if\n", "# it is fine directly responding. This conditional routing defines the main agent loop.\n", "graph_builder.add_conditional_edges(\n", " \"chatbot\",\n", @@ -595,7 +595,7 @@ " # want to use a node named something else apart from \"tools\",\n", " # You can update the value of the dictionary to something else\n", " # e.g., \"tools\": \"my_tools\"\n", - " {\"tools\": \"tools\", \"__end__\": \"__end__\"},\n", + " {\"tools\": \"tools\", END: END},\n", ")\n", "# Any time a tool is called, we return to the chatbot to decide the next step\n", "graph_builder.add_edge(\"tools\", \"chatbot\")\n", @@ -610,7 +610,7 @@ "source": [ "**Notice** that conditional edges start from a single node. This tells the graph \"any time the '`chatbot`' node runs, either go to 'tools' if it calls a tool, or end the loop if it responds directly. \n", "\n", - "Like the prebuilt `tools_condition`, our function returns the \"`__end__`\" string if no tool calls are made. When the graph transitions to `__end__`, it has no more tasks to complete and ceases execution. Because the condition can return `__end__`, we don't need to explicitly set a `finish_point` this time. Our graph already has a way to finish!\n", + "Like the prebuilt `tools_condition`, our function returns the `END` string if no tool calls are made. When the graph transitions to `END`, it has no more tasks to complete and ceases execution. Because the condition can return `END`, we don't need to explicitly set a `finish_point` this time. Our graph already has a way to finish!\n", "\n", "Let's visualize the graph we've built. The following function has some additional dependencies to run that are unimportant for this tutorial." ] @@ -1115,7 +1115,7 @@ "id": "627f4998-6780-4cce-8f3c-9a5580888e3a", "metadata": {}, "source": [ - "The snapshot above contains the current state values, corresponding config, and the `next` node to process. In our case, the graph has reached an `__end__` state, so `next` is empty.\n", + "The snapshot above contains the current state values, corresponding config, and the `next` node to process. In our case, the graph has reached an `END` state, so `next` is empty.\n", "\n", "**Congratulations!** Your chatbot can now maintain conversation state across sessions thanks to LangGraph's checkpointing system. This opens up exciting possibilities for more natural, contextual interactions. LangGraph's checkpointing even handles **arbitrarily complex graph states**, which is much more expressive and powerful than simple chat memory.\n", "\n", @@ -2243,7 +2243,7 @@ "graph_builder.add_conditional_edges(\n", " \"chatbot\",\n", " select_next_node,\n", - " {\"human\": \"human\", \"tools\": \"tools\", \"__end__\": \"__end__\"},\n", + " {\"human\": \"human\", \"tools\": \"tools\", END: END},\n", ")" ] }, @@ -2744,7 +2744,7 @@ "graph_builder.add_node(\"human\", human_node)\n", "\n", "\n", - "def select_next_node(state: State) -> Literal[\"human\", \"tools\", \"__end__\"]:\n", + "def select_next_node(state: State):\n", " if state[\"ask_human\"]:\n", " return \"human\"\n", " # Otherwise, we can route as before\n", @@ -2754,7 +2754,7 @@ "graph_builder.add_conditional_edges(\n", " \"chatbot\",\n", " select_next_node,\n", - " {\"human\": \"human\", \"tools\": \"tools\", \"__end__\": \"__end__\"},\n", + " {\"human\": \"human\", \"tools\": \"tools\", END: END},\n", ")\n", "graph_builder.add_edge(\"tools\", \"chatbot\")\n", "graph_builder.add_edge(\"human\", \"chatbot\")\n", diff --git a/docs/docs/tutorials/lats/lats.ipynb b/docs/docs/tutorials/lats/lats.ipynb index 7d721c061..ca3f1b7be 100644 --- a/docs/docs/tutorials/lats/lats.ipynb +++ b/docs/docs/tutorials/lats/lats.ipynb @@ -655,7 +655,7 @@ "from langgraph.graph import END, StateGraph, START\n", "\n", "\n", - "def should_loop(state: TreeState) -> Literal[\"expand\", \"__end__\"]:\n", + "def should_loop(state: TreeState):\n", " \"\"\"Determine whether to continue the tree search.\"\"\"\n", " root = state[\"root\"]\n", " if root.is_solved:\n", @@ -675,11 +675,13 @@ " \"start\",\n", " # Either expand/rollout or finish\n", " should_loop,\n", + " ['expand',END]\n", ")\n", "builder.add_conditional_edges(\n", " \"expand\",\n", " # Either continue to rollout or finish\n", " should_loop,\n", + " ['expand',END]\n", ")\n", "\n", "graph = builder.compile()" diff --git a/docs/docs/tutorials/multi_agent/multi-agent-collaboration.ipynb b/docs/docs/tutorials/multi_agent/multi-agent-collaboration.ipynb index 7ae239c78..e1016d101 100644 --- a/docs/docs/tutorials/multi_agent/multi-agent-collaboration.ipynb +++ b/docs/docs/tutorials/multi_agent/multi-agent-collaboration.ipynb @@ -312,7 +312,7 @@ "from typing import Literal\n", "\n", "\n", - "def router(state) -> Literal[\"call_tool\", \"__end__\", \"continue\"]:\n", + "def router(state):\n", " # This is the router\n", " messages = state[\"messages\"]\n", " last_message = messages[-1]\n", @@ -321,7 +321,7 @@ " return \"call_tool\"\n", " if \"FINAL ANSWER\" in last_message.content:\n", " # Any agent decided the work is done\n", - " return \"__end__\"\n", + " return END\n", " return \"continue\"" ] }, @@ -351,12 +351,12 @@ "workflow.add_conditional_edges(\n", " \"Researcher\",\n", " router,\n", - " {\"continue\": \"chart_generator\", \"call_tool\": \"call_tool\", \"__end__\": END},\n", + " {\"continue\": \"chart_generator\", \"call_tool\": \"call_tool\", END: END},\n", ")\n", "workflow.add_conditional_edges(\n", " \"chart_generator\",\n", " router,\n", - " {\"continue\": \"Researcher\", \"call_tool\": \"call_tool\", \"__end__\": END},\n", + " {\"continue\": \"Researcher\", \"call_tool\": \"call_tool\", END: END},\n", ")\n", "\n", "workflow.add_conditional_edges(\n", diff --git a/docs/docs/tutorials/plan-and-execute/plan-and-execute.ipynb b/docs/docs/tutorials/plan-and-execute/plan-and-execute.ipynb index a0bf803e4..9279e8961 100644 --- a/docs/docs/tutorials/plan-and-execute/plan-and-execute.ipynb +++ b/docs/docs/tutorials/plan-and-execute/plan-and-execute.ipynb @@ -391,7 +391,7 @@ "outputs": [], "source": [ "from typing import Literal\n", - "\n", + "from langgraph.graph import END\n", "\n", "async def execute_step(state: PlanExecute):\n", " plan = state[\"plan\"]\n", @@ -420,9 +420,9 @@ " return {\"plan\": output.action.steps}\n", "\n", "\n", - "def should_end(state: PlanExecute) -> Literal[\"agent\", \"__end__\"]:\n", + "def should_end(state: PlanExecute):\n", " if \"response\" in state and state[\"response\"]:\n", - " return \"__end__\"\n", + " return END\n", " else:\n", " return \"agent\"" ] @@ -459,6 +459,7 @@ " \"replan\",\n", " # Next, we pass in the function that will determine which node is called next.\n", " should_end,\n", + " [\"agent\",END]\n", ")\n", "\n", "# Finally, we compile it!\n", diff --git a/docs/docs/tutorials/reflexion/reflexion.ipynb b/docs/docs/tutorials/reflexion/reflexion.ipynb index bec31a140..73a4eee6f 100644 --- a/docs/docs/tutorials/reflexion/reflexion.ipynb +++ b/docs/docs/tutorials/reflexion/reflexion.ipynb @@ -437,7 +437,7 @@ " return i\n", "\n", "\n", - "def event_loop(state: list) -> Literal[\"execute_tools\", \"__end__\"]:\n", + "def event_loop(state: list):\n", " # in our case, we'll just stop after N plans\n", " num_iterations = _get_num_iterations(state['messages'])\n", " if num_iterations > MAX_ITERATIONS:\n", @@ -446,7 +446,7 @@ "\n", "\n", "# revise -> execute_tools OR end\n", - "builder.add_conditional_edges(\"revise\", event_loop)\n", + "builder.add_conditional_edges(\"revise\", event_loop, [\"execute_tools\", END])\n", "builder.add_edge(START, \"draft\")\n", "graph = builder.compile()" ]