docs: __start__/__end__ consistency (#1874)

* changes

* skip mermaid

* retries fix

* viz error

* changes
This commit is contained in:
Isaac Francisco
2024-09-30 13:55:53 -07:00
committed by GitHub
parent d29747906d
commit 17dc1108a3
18 changed files with 110 additions and 108 deletions
@@ -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."
@@ -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",
@@ -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",
+5 -4
View File
@@ -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",
+4 -3
View File
@@ -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",
@@ -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()"
]
+4 -2
View File
@@ -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,
@@ -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())"
]
+13 -10
View File
@@ -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",
+5 -4
View File
@@ -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",
@@ -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",
@@ -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",
+5 -5
View File
@@ -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",
+11 -11
View File
@@ -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",
+3 -1
View File
@@ -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()"
@@ -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",
@@ -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",
@@ -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()"
]