docs: add note for langgraph cloud checkpointers (#1011)

This commit is contained in:
Vadym Barda
2024-07-12 16:48:03 -04:00
committed by GitHub
parent 4b5152fde5
commit 3ef65c37b3
+178 -27
View File
@@ -38,14 +38,6 @@
"</div> "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0c3fde0a",
"metadata": {},
"outputs": [],
"source": [""]
},
{
"cell_type": "markdown",
"id": "7cbd446a-808f-4394-be92-d45ab818953c",
@@ -62,7 +54,10 @@
"id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
"metadata": {},
"outputs": [],
"source": ["%%capture --no-stderr\n%pip install --quiet -U langgraph langchain_anthropic"]
"source": [
"%%capture --no-stderr\n",
"%pip install --quiet -U langgraph langchain_anthropic"
]
},
{
"cell_type": "markdown",
@@ -78,7 +73,18 @@
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
"metadata": {},
"outputs": [],
"source": ["import getpass\nimport os\n\n\ndef _set_env(var: str):\n if not os.environ.get(var):\n os.environ[var] = getpass.getpass(f\"{var}: \")\n\n\n_set_env(\"ANTHROPIC_API_KEY\")"]
"source": [
"import getpass\n",
"import os\n",
"\n",
"\n",
"def _set_env(var: str):\n",
" if not os.environ.get(var):\n",
" os.environ[var] = getpass.getpass(f\"{var}: \")\n",
"\n",
"\n",
"_set_env(\"ANTHROPIC_API_KEY\")"
]
},
{
"cell_type": "markdown",
@@ -94,7 +100,10 @@
"id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3",
"metadata": {},
"outputs": [],
"source": ["os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n_set_env(\"LANGCHAIN_API_KEY\")"]
"source": [
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"_set_env(\"LANGCHAIN_API_KEY\")"
]
},
{
"cell_type": "markdown",
@@ -112,7 +121,22 @@
"id": "14619607",
"metadata": {},
"outputs": [],
"source": ["from typing import Annotated\n\nfrom typing_extensions import TypedDict\n\nfrom langgraph.graph.message import add_messages\n\n# Add messages essentially does this with more\n# robust handling\n# def add_messages(left: list, right: list):\n# return left + right\n\n\nclass State(TypedDict):\n messages: Annotated[list, add_messages]"]
"source": [
"from typing import Annotated\n",
"\n",
"from typing_extensions import TypedDict\n",
"\n",
"from langgraph.graph.message import add_messages\n",
"\n",
"# Add messages essentially does this with more\n",
"# robust handling\n",
"# def add_messages(left: list, right: list):\n",
"# return left + right\n",
"\n",
"\n",
"class State(TypedDict):\n",
" messages: Annotated[list, add_messages]"
]
},
{
"cell_type": "markdown",
@@ -132,7 +156,19 @@
"id": "d7ef57dd-5d6e-4ad3-9377-a92201c1310e",
"metadata": {},
"outputs": [],
"source": ["from langchain_core.tools import tool\n\n\n@tool\ndef search(query: str):\n \"\"\"Call to surf the web.\"\"\"\n # This is a placeholder for the actual implementation\n return [\"The answer to your question lies within.\"]\n\n\ntools = [search]"]
"source": [
"from langchain_core.tools import tool\n",
"\n",
"\n",
"@tool\n",
"def search(query: str):\n",
" \"\"\"Call to surf the web.\"\"\"\n",
" # This is a placeholder for the actual implementation\n",
" return [\"The answer to your question lies within.\"]\n",
"\n",
"\n",
"tools = [search]"
]
},
{
"cell_type": "markdown",
@@ -149,7 +185,11 @@
"id": "5cf3331e-ccb3-41c8-aeb9-a840a94d41e7",
"metadata": {},
"outputs": [],
"source": ["from langgraph.prebuilt import ToolNode\n\ntool_node = ToolNode(tools)"]
"source": [
"from langgraph.prebuilt import ToolNode\n",
"\n",
"tool_node = ToolNode(tools)"
]
},
{
"cell_type": "markdown",
@@ -179,7 +219,13 @@
"id": "892b54b9-75f0-4804-9ed0-88b5e5532989",
"metadata": {},
"outputs": [],
"source": ["from langchain_openai import ChatOpenAI\n\n# We will set streaming=True so that we can stream tokens\n# See the streaming section for more information on this.\nmodel = ChatOpenAI(temperature=0, streaming=True)"]
"source": [
"from langchain_openai import ChatOpenAI\n",
"\n",
"# We will set streaming=True so that we can stream tokens\n",
"# See the streaming section for more information on this.\n",
"model = ChatOpenAI(temperature=0, streaming=True)"
]
},
{
"cell_type": "markdown",
@@ -197,7 +243,9 @@
"id": "cd3cbae5-d92c-4559-a4aa-44721b80d107",
"metadata": {},
"outputs": [],
"source": ["bound_model = model.bind_tools(tools)"]
"source": [
"bound_model = model.bind_tools(tools)"
]
},
{
"cell_type": "markdown",
@@ -232,7 +280,27 @@
"id": "3b541bb9-900c-40d0-964d-7b5dfee30667",
"metadata": {},
"outputs": [],
"source": ["# Define the function that determines whether to continue or not\nfrom typing import Literal\n\n\ndef should_continue(state: State) -> Literal[\"action\", \"__end__\"]:\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 # Otherwise if there is, we continue\n return \"action\"\n\n\n# Define the function that calls the model\ndef call_model(state: State):\n response = model.invoke(state[\"messages\"])\n # We return a list, because this will get added to the existing list\n return {\"messages\": response}"]
"source": [
"# Define the function that determines whether to continue or not\n",
"from typing import Literal\n",
"\n",
"\n",
"def should_continue(state: State) -> Literal[\"action\", \"__end__\"]:\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",
" # Otherwise if there is, we continue\n",
" return \"action\"\n",
"\n",
"\n",
"# Define the function that calls the model\n",
"def call_model(state: State):\n",
" response = model.invoke(state[\"messages\"])\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": response}"
]
},
{
"cell_type": "markdown",
@@ -248,7 +316,33 @@
"id": "812b4e70-4956-4415-8880-db48b3dcbad2",
"metadata": {},
"outputs": [],
"source": ["from langgraph.graph import StateGraph, START\n\n# Define a new graph\nworkflow = StateGraph(State)\n\n# Define the two nodes we will cycle between\nworkflow.add_node(\"agent\", call_model)\nworkflow.add_node(\"action\", tool_node)\n\n# Set the entrypoint as `agent`\n# This means that this node is the first one called\nworkflow.add_edge(START, \"agent\")\n\n# We now add a conditional edge\nworkflow.add_conditional_edges(\n # First, we define the start node. We use `agent`.\n # This means these are the edges taken after the `agent` node is called.\n \"agent\",\n # Next, we pass in the function that will determine which node is called next.\n should_continue,\n)\n\n# We now add a normal edge from `tools` to `agent`.\n# This means that after `tools` is called, `agent` node is called next.\nworkflow.add_edge(\"action\", \"agent\")"]
"source": [
"from langgraph.graph import StateGraph, START\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(State)\n",
"\n",
"# Define the two nodes we will cycle between\n",
"workflow.add_node(\"agent\", call_model)\n",
"workflow.add_node(\"action\", tool_node)\n",
"\n",
"# Set the entrypoint as `agent`\n",
"# This means that this node is the first one called\n",
"workflow.add_edge(START, \"agent\")\n",
"\n",
"# We now add a conditional edge\n",
"workflow.add_conditional_edges(\n",
" # First, we define the start node. We use `agent`.\n",
" # This means these are the edges taken after the `agent` node is called.\n",
" \"agent\",\n",
" # Next, we pass in the function that will determine which node is called next.\n",
" should_continue,\n",
")\n",
"\n",
"# We now add a normal edge from `tools` to `agent`.\n",
"# This means that after `tools` is called, `agent` node is called next.\n",
"workflow.add_edge(\"action\", \"agent\")"
]
},
{
"cell_type": "markdown",
@@ -266,7 +360,11 @@
"id": "6845ed6a-d155-4105-9160-28849877248b",
"metadata": {},
"outputs": [],
"source": ["from langgraph.checkpoint.sqlite import SqliteSaver\n\nmemory = SqliteSaver.from_conn_string(\":memory:\")"]
"source": [
"from langgraph.checkpoint.sqlite import SqliteSaver\n",
"\n",
"memory = SqliteSaver.from_conn_string(\":memory:\")"
]
},
{
"cell_type": "code",
@@ -274,7 +372,25 @@
"id": "79d29875-8aa8-434c-9f20-1c58346a6249",
"metadata": {},
"outputs": [],
"source": ["# Finally, we compile it!\n# This compiles it into a LangChain Runnable,\n# meaning you can use it as you would any other runnable\napp = workflow.compile(checkpointer=memory)"]
"source": [
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",
"# meaning you can use it as you would any other runnable\n",
"app = workflow.compile(checkpointer=memory)"
]
},
{
"cell_type": "markdown",
"id": "7654ebcc-2179-41b4-92d1-6666f6f8634f",
"metadata": {},
"source": [
"<div class=\"admonition tip\">\n",
" <p class=\"admonition-title\">Note</p>\n",
" <p>\n",
" If you're using LangGraph Cloud, you <strong>don't need</strong> to pass checkpointer when compiling the graph, since it's done automatically.\n",
" </p>\n",
"</div>"
]
},
{
"cell_type": "code",
@@ -293,7 +409,15 @@
"output_type": "display_data"
}
],
"source": ["from IPython.display import Image, display\n\ntry:\n display(Image(app.get_graph().draw_mermaid_png()))\nexcept Exception:\n # This requires some extra dependencies and is optional\n pass"]
"source": [
"from IPython.display import Image, display\n",
"\n",
"try:\n",
" display(Image(app.get_graph().draw_mermaid_png()))\n",
"except Exception:\n",
" # This requires some extra dependencies and is optional\n",
" pass"
]
},
{
"cell_type": "markdown",
@@ -324,7 +448,14 @@
]
}
],
"source": ["from langchain_core.messages import HumanMessage\n\nconfig = {\"configurable\": {\"thread_id\": \"2\"}}\ninput_message = HumanMessage(content=\"hi! I'm bob\")\nfor event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n event[\"messages\"][-1].pretty_print()"]
"source": [
"from langchain_core.messages import HumanMessage\n",
"\n",
"config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
"input_message = HumanMessage(content=\"hi! I'm bob\")\n",
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "code",
@@ -345,7 +476,11 @@
]
}
],
"source": ["input_message = HumanMessage(content=\"what is my name?\")\nfor event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n event[\"messages\"][-1].pretty_print()"]
"source": [
"input_message = HumanMessage(content=\"what is my name?\")\n",
"for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "markdown",
@@ -374,7 +509,15 @@
]
}
],
"source": ["input_message = HumanMessage(content=\"what is my name?\")\nfor event in app.stream(\n {\"messages\": [input_message]},\n {\"configurable\": {\"thread_id\": \"3\"}},\n stream_mode=\"values\",\n):\n event[\"messages\"][-1].pretty_print()"]
"source": [
"input_message = HumanMessage(content=\"what is my name?\")\n",
"for event in app.stream(\n",
" {\"messages\": [input_message]},\n",
" {\"configurable\": {\"thread_id\": \"3\"}},\n",
" stream_mode=\"values\",\n",
"):\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "markdown",
@@ -403,7 +546,15 @@
]
}
],
"source": ["input_message = HumanMessage(content=\"You forgot??\")\nfor event in app.stream(\n {\"messages\": [input_message]},\n {\"configurable\": {\"thread_id\": \"2\"}},\n stream_mode=\"values\",\n):\n event[\"messages\"][-1].pretty_print()"]
"source": [
"input_message = HumanMessage(content=\"You forgot??\")\n",
"for event in app.stream(\n",
" {\"messages\": [input_message]},\n",
" {\"configurable\": {\"thread_id\": \"2\"}},\n",
" stream_mode=\"values\",\n",
"):\n",
" event[\"messages\"][-1].pretty_print()"
]
},
{
"cell_type": "code",
@@ -411,7 +562,7 @@
"id": "eb20430f",
"metadata": {},
"outputs": [],
"source": [""]
"source": []
}
],
"metadata": {
@@ -430,7 +581,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
"version": "3.11.9"
}
},
"nbformat": 4,