diff --git a/docs/cassettes/shared-state_5f36c16f-d68d-456c-b0b7-5eb94fb89ca6.msgpack.zlib b/docs/cassettes/shared-state_5f36c16f-d68d-456c-b0b7-5eb94fb89ca6.msgpack.zlib new file mode 100644 index 000000000..6ea5bfc76 --- /dev/null +++ b/docs/cassettes/shared-state_5f36c16f-d68d-456c-b0b7-5eb94fb89ca6.msgpack.zlib @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md index e84bf6733..57266115a 100644 --- a/docs/docs/how-tos/index.md +++ b/docs/docs/how-tos/index.md @@ -24,6 +24,7 @@ LangGraph makes it easy to persist state across graph runs. The guide below show - [How to manage conversation history](memory/manage-conversation-history.ipynb) - [How to delete messages](memory/delete-messages.ipynb) - [How to add summary conversation memory](memory/add-summary-conversation-history.ipynb) +- [How to share state between threads](memory/shared-state.ipynb) - [How to use Postgres checkpointer for persistence](persistence_postgres.ipynb) - [How to create a custom checkpointer using MongoDB](persistence_mongodb.ipynb) - [How to create a custom checkpointer using Redis](persistence_redis.ipynb) diff --git a/docs/docs/how-tos/memory/shared-state.ipynb b/docs/docs/how-tos/memory/shared-state.ipynb new file mode 100644 index 000000000..254c7bbaf --- /dev/null +++ b/docs/docs/how-tos/memory/shared-state.ipynb @@ -0,0 +1,460 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "d2eecb96-cf0e-47ed-8116-88a7eaa4236d", + "metadata": {}, + "source": [ + "# How to share state between threads\n", + "\n", + "By default, LangGraph state is scoped to a single thread. LangGraph also allows you to store information that can be **shared** across threads.\n", + "\n", + "For instance, you can persist each user’s preferences to a shared memory and reuse them in new conversational threads.\n", + "\n", + "In this guide, we will show how to construct and use a graph that has a shared memory implemented using the `Store` interface.\n", + "\n", + "
\n", + "

Note

\n", + "

\n", + " Support for the Store API that is used in this notebook was added in LangGraph v0.2.32.\n", + "

\n", + "
\n", + "\n", + "## Setup\n", + "\n", + "First, let's install the required packages and set our API keys" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3457aadf", + "metadata": {}, + "outputs": [], + "source": [ + "%%capture --no-stderr\n", + "%pip install -U langchain_openai langgraph" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "aa2c64a7", + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "OPENAI_API_KEY: ········\n" + ] + } + ], + "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(\"OPENAI_API_KEY\")" + ] + }, + { + "cell_type": "markdown", + "id": "51b6817d", + "metadata": {}, + "source": [ + "
\n", + "

Set up LangSmith for LangGraph development

\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 apps built with LangGraph — read more about how to get started here. \n", + "

\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "c4c550b5-1954-496b-8b9d-800361af17dc", + "metadata": {}, + "source": [ + "## Create graph\n", + "\n", + "In this example we will create a graph that will let us store information about a user's preferences. We will do so by defining an `InMemoryStore` - an object that can store data in memory and query that data. We can then pass the store object when compiling the graph. This allows each node in the graph to access the store: when you define node functions, you can define `store` keyword argument, and LangGraph will automatically pass the store object you compiled the graph with.\n", + "\n", + "When storing objects using the `Store` interface you define two things:\n", + "\n", + "* the namespace for the object, a tuple (similar to directories)\n", + "* the object key (similar to filenames)\n", + "\n", + "In our example, we'll be using `(\"memories\", )` as namespace and random UUID as key for each new memory.\n", + "\n", + "Importantly, to determine the user, we will be passing `user_id` via the config keyword argument of each node function." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a7f303d6-612e-4e34-bf36-29d4ed25d802", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Literal, TypedDict, Annotated\n", + "import uuid\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "from langchain_core.runnables import RunnableConfig\n", + "from langgraph.graph import StateGraph, MessagesState, START, END\n", + "from langgraph.store.base import BaseStore\n", + "from langgraph.store.memory import InMemoryStore\n", + "from langgraph.checkpoint.memory import MemorySaver\n", + "\n", + "\n", + "# We will give this as a tool to the agent\n", + "# This will let the agent call this tool to save a fact\n", + "class Info(TypedDict):\n", + " \"\"\"This tool should be called when you want to save a new fact about the user.\n", + "\n", + " Attributes:\n", + " fact (str): A fact about the user.\n", + " topic (str): The topic related the fact is about, i.e. Food, Location, Movies, etc.\n", + " \"\"\"\n", + "\n", + " fact: str\n", + " topic: str\n", + "\n", + "\n", + "# This is the prompt we give the agent\n", + "# We will pass known info into the prompt\n", + "# We will tell it to use the Info tool to save more\n", + "prompt = \"\"\"You are a helpful assistant that learns about users to provide better assistance.\n", + "\n", + "Current user information:\n", + "\n", + "{info}\n", + "\n", + "\n", + "Instructions:\n", + "1. Use the `Info` tool to save new information the user shares.\n", + "2. Save facts, opinions, preferences, and experiences.\n", + "3. Your goal: Improve assistance by building a user profile over time.\n", + "\n", + "Remember: Every piece of information helps you serve the user better in future interactions.\n", + "\"\"\"\n", + "\n", + "\n", + "# We give the model access to the Info tool\n", + "model = ChatOpenAI(model=\"gpt-4o-mini\").bind_tools([Info])\n", + "\n", + "\n", + "def call_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore):\n", + " \"\"\"Call the model.\"\"\"\n", + " user_id = config[\"configurable\"][\"user_id\"]\n", + " memories = store.search((\"memories\", user_id))\n", + " info = \"\\n\".join([d.value[\"fact\"] for d in memories])\n", + " # Format system prompt\n", + " system_msg = prompt.format(info=info)\n", + " # Call model\n", + " response = model.invoke(\n", + " [{\"role\": \"system\", \"content\": system_msg}] + state[\"messages\"]\n", + " )\n", + " return {\"messages\": [response]}\n", + "\n", + "\n", + "# Routing function to decide what to do next\n", + "# If no tool calls, then we end\n", + "# If tool calls, then we update memory\n", + "def route(state):\n", + " if len(state[\"messages\"][-1].tool_calls) == 0:\n", + " return END\n", + " else:\n", + " return \"update_memory\"\n", + "\n", + "\n", + "def update_memory(state: MessagesState, config: RunnableConfig, *, store: BaseStore):\n", + " \"\"\"Update the memory.\"\"\"\n", + " user_id = config[\"configurable\"][\"user_id\"]\n", + " memory_id = str(uuid.uuid4())\n", + " tool_calls = []\n", + " memories = {}\n", + " # Each tool call is a new memory to save\n", + " for tc in state[\"messages\"][-1].tool_calls:\n", + " # We append ToolMessages (to pass back to the LLM)\n", + " # This is needed because OpenAI requires each tool call be followed by a ToolMessage\n", + " tool_calls.append(\n", + " {\"role\": \"tool\", \"content\": \"Saved!\", \"tool_call_id\": tc[\"id\"]}\n", + " )\n", + " # We create a new memory from this tool call\n", + " store.put((\"memories\", user_id), memory_id, {\n", + " \"fact\": tc[\"args\"][\"fact\"],\n", + " \"topic\": tc[\"args\"][\"topic\"],\n", + " })\n", + " # Return the messages and memories to update the state with\n", + " return {\"messages\": tool_calls}\n", + "\n", + "\n", + "# This is the in memory checkpointer we will use\n", + "# We need this because we want to enable threads (conversations)\n", + "checkpointer = MemorySaver()\n", + "\n", + "# This is the in memory store needed to save the memories (i.e. user preferences)\n", + "in_memory_store = InMemoryStore()\n", + "\n", + "# Construct this relatively simple graph\n", + "graph = StateGraph(MessagesState)\n", + "graph.add_node(call_model)\n", + "graph.add_node(update_memory)\n", + "graph.add_edge(\"update_memory\", END)\n", + "graph.add_edge(START, \"call_model\")\n", + "graph.add_conditional_edges(\"call_model\", route, path_map=[END, \"update_memory\"])\n", + "graph = graph.compile(checkpointer=checkpointer, store=in_memory_store)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5f36c16f-d68d-456c-b0b7-5eb94fb89ca6", + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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iAiLQz1cmR1c1DRShlvp5ZKW5SOjnjkfuLoynkaWAEF4Jla53KY3M1z7dGH1qKysudwfR0DpaJlHUQmqqpqb0JmFpe6KEuI2f4MOfotAe4A84PJnWq1UljttNb6GBtNR07BHFE3ua0faepP2nqe8r6UNDTWuip6Ojp4qSjp42ww08DAyOJjRprWtHQAAAADoAF90BERAREQEREGDc7NR3jxU1cDZZKScVNNL3PglALedju9p5XOadd7XuadtcQcayVtWzktty7Se408DHS1sdK6OnqN7Bew7IaSQSYy7bd+sacdusC72WlvcMDKmPmdTzsqYHte5jo5WHbXBzSCPWCAdOa5zTtriCGei0+PXp9e2ahrpaIX2iazx6mo5XPbGXgljhzNa7lcASOmthzdktJW4QEREBERAREQEREBERAREQaO/zT108VmpWyaqmOFZVU9W2GajhLXBr2jRcXOcOVugNaeeYFoB3FPA2mgjhYXlkbQwGR5e4gDXVziST9pJJWhxCLxqS63aVlpkqaurkhFXa3F/aQQvcyJsjz3vb6XM0dGuLgPWTRICIiAiIgIiICIiAiIgn8sqJbNHBe2T1DKagJfW01LRiokqYC0jWgOccjiJPQJOmuHK7Y1vwQRsdQv1T+El8Fl8nPbdXOtkrqAVN506epbHoNm5x/CBzSDznqevN6W0FAiIgIi0l4zfHsfqvFrne7fQVOubsaipYx+vp5Sd6+1Z00VVzamLycW7RS3nTw72ntXvbPvTzp4d7T2r3tn3rbs2NyT0lbTkqUUt508O9p7V72z7086eHe09q97Z96bNjck9JLTkqUUt508O9p7V72z7086eHe09q97Z96bNjck9JLTkqVqMmy6xYXQMrshvVusVE+QQtqbnVR08bnkEhoc8gEkNcdd/Q/Qtb508O9p7V72z71zrwgqbAuOPCW/4lVZNZxNVQl9HM+qj/calnpRP3vp6XQ/YSmzY3JPSS05KvhLxBxbI7cLTasjxC43WN1TUyUGK18U0bIjUOIk5GuLgTzsL3a1zvP0hdDXg3/w6uHli4QY7fcpyu6UFsya6yGhhpamoa2SCljd1JBP+0eN93cxp9a9k+dPDvae1e9s+9NmxuSeklpyVKKW86eHe09q97Z96edPDvae1e9s+9NmxuSeklpyVKKW86eHe09q97Z96edPDvae1e9s+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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "display(Image(graph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "id": "552d4e33-556d-4fa5-8094-2a076bc21529", + "metadata": {}, + "source": [ + "## Run graph on one thread\n", + "\n", + "We can now run the graph on one thread and give it some information" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "18bd8679-3a73-4033-bfb4-5093ac1f5d7f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'call_model': {'messages': [AIMessage(content='Hello! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 178, 'total_tokens': 188, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_74ba47b4ac', 'finish_reason': 'stop', 'logprobs': None}, id='run-8e7b57b7-8231-4811-945d-a2cf2e1adba3-0', usage_metadata={'input_tokens': 178, 'output_tokens': 10, 'total_tokens': 188})]}}\n", + "{'call_model': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_mpPvip6DbMYkZueShwDDws3y', 'function': {'arguments': '{\"fact\":\"User likes pepperoni pizza\",\"topic\":\"Food\"}', 'name': 'Info'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 200, 'total_tokens': 221, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_74ba47b4ac', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-5cd7b512-071b-49ac-9932-de0130774a1e-0', tool_calls=[{'name': 'Info', 'args': {'fact': 'User likes pepperoni pizza', 'topic': 'Food'}, 'id': 'call_mpPvip6DbMYkZueShwDDws3y', 'type': 'tool_call'}], usage_metadata={'input_tokens': 200, 'output_tokens': 21, 'total_tokens': 221})]}}\n", + "{'update_memory': {'messages': [{'role': 'tool', 'content': 'Saved!', 'tool_call_id': 'call_mpPvip6DbMYkZueShwDDws3y'}]}}\n", + "{'call_model': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Jqn1cbXFQcVl4hGrbB660kw1', 'function': {'arguments': '{\"fact\":\"User just moved to San Francisco\",\"topic\":\"Location\"}', 'name': 'Info'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 246, 'total_tokens': 268, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_74ba47b4ac', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-9490584d-1678-47db-bb0a-d0797a7af7c3-0', tool_calls=[{'name': 'Info', 'args': {'fact': 'User just moved to San Francisco', 'topic': 'Location'}, 'id': 'call_Jqn1cbXFQcVl4hGrbB660kw1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 246, 'output_tokens': 22, 'total_tokens': 268})]}}\n", + "{'update_memory': {'messages': [{'role': 'tool', 'content': 'Saved!', 'tool_call_id': 'call_Jqn1cbXFQcVl4hGrbB660kw1'}]}}\n" + ] + } + ], + "source": [ + "config = {\"configurable\": {\"thread_id\": \"1\", \"user_id\": \"1\"}}\n", + "\n", + "# First let's just say hi to the AI\n", + "for update in graph.stream(\n", + " {\"messages\": [{\"role\": \"user\", \"content\": \"hi\"}]}, config, stream_mode=\"updates\"\n", + "):\n", + " print(update)\n", + "\n", + "# Let's continue the conversation (by passing the same config) and tell the AI we like pepperoni pizza\n", + "for update in graph.stream(\n", + " {\"messages\": [{\"role\": \"user\", \"content\": \"i like pepperoni pizza\"}]},\n", + " config,\n", + " stream_mode=\"updates\",\n", + "):\n", + " print(update)\n", + "\n", + "# Let's continue the conversation even further (by passing the same config) and tell the AI we live in SF\n", + "for update in graph.stream(\n", + " {\"messages\": [{\"role\": \"user\", \"content\": \"i also just moved to SF\"}]},\n", + " config,\n", + " stream_mode=\"updates\",\n", + "):\n", + " print(update)" + ] + }, + { + "cell_type": "markdown", + "id": "16f19db5-ff4a-4666-9be5-ef791c39be0a", + "metadata": {}, + "source": [ + "We can now inspect our in-memory store and verify that we have in fact saved the memories for the user:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "57335ef9-c6fc-40d8-8dd7-dce8a600d05b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'fact': 'User likes pepperoni pizza', 'topic': 'Food'}\n", + "{'fact': 'User just moved to San Francisco', 'topic': 'Location'}\n" + ] + } + ], + "source": [ + "for memory in in_memory_store.search((\"memories\", \"1\")):\n", + " print(memory.value)" + ] + }, + { + "cell_type": "markdown", + "id": "b8c416fa-086a-491d-a7d3-57091f6413e3", + "metadata": {}, + "source": [ + "## Run graph on a different thread\n", + "\n", + "We can now run the graph on a different thread and see that it remembers facts about the user (specifically that the user likes pepperoni pizza and lives in SF):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e240f025-ff8b-4d17-beb7-2420c0575dd9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'call_model': {'messages': [AIMessage(content=\"Since you like pepperoni pizza, I can recommend a few places in San Francisco where you can enjoy some delicious pizza. Here are some options:\\n\\n1. **Tony's Pizza Napoletana** - Located in North Beach, this restaurant is famous for its award-winning pizzas. You can find a variety of styles, including classic pepperoni.\\n\\n2. **Pizza Orgasmica** - Known for its fun atmosphere and a wide range of pizza options, including pepperoni, this spot is a local favorite.\\n\\n3. **Little Star Pizza** - This place offers deep-dish and thin-crust options. Their pepperoni pizza is highly recommended.\\n\\n4. **Pizzeria Delfina** - A popular spot known for its artisanal pizzas, including a delicious pepperoni option.\\n\\n5. **The Pizza Place on Noriega** - A casual eatery offering classic pizza options in the Sunset District.\\n\\nWould you like more options, or are you interested in a specific neighborhood for dinner?\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 198, 'prompt_tokens': 205, 'total_tokens': 403, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_74ba47b4ac', 'finish_reason': 'stop', 'logprobs': None}, id='run-e5d7ff2e-1665-49c4-83dd-ec9b3e693b3c-0', usage_metadata={'input_tokens': 205, 'output_tokens': 198, 'total_tokens': 403})]}}\n" + ] + } + ], + "source": [ + "config = {\"configurable\": {\"thread_id\": \"2\", \"user_id\": \"1\"}}\n", + "\n", + "for update in graph.stream(\n", + " {\n", + " \"messages\": [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"where and what should i eat for dinner? Can you list some restaurants?\",\n", + " }\n", + " ]\n", + " },\n", + " config,\n", + " stream_mode=\"updates\",\n", + "):\n", + " print(update)" + ] + }, + { + "cell_type": "markdown", + "id": "091995d3", + "metadata": {}, + "source": [ + "Perfect! The AI recommended restaurants in SF, and included a pizza restaurant at the top of it's list.\n", + "\n", + "Notice that the `messages` in this new thread do NOT contain the messages from the previous thread since we didn't store them as shared values across the `user_id`. However, the `info` we saved in the previous thread was saved since we passed in the same `user_id` in this new thread.\n", + "\n", + "Let's now run the graph for another user to verify that the preferences of the first user are self contained:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f9bf2c15", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'call_model': {'messages': [AIMessage(content=\"To help you better, could you please share your location or the area you're interested in? Additionally, do you have any preferences for cuisine or dietary restrictions?\", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 32, 'prompt_tokens': 192, 'total_tokens': 224, 'completion_tokens_details': {'reasoning_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_74ba47b4ac', 'finish_reason': 'stop', 'logprobs': None}, id='run-fba82c81-703a-4f5e-bd1a-1edf5876425e-0', usage_metadata={'input_tokens': 192, 'output_tokens': 32, 'total_tokens': 224})]}}\n" + ] + } + ], + "source": [ + "config = {\"configurable\": {\"thread_id\": \"3\", \"user_id\": \"2\"}}\n", + "\n", + "for update in graph.stream(\n", + " {\n", + " \"messages\": [\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"where and what should i eat for dinner? Can you list some restaurants?\",\n", + " }\n", + " ]\n", + " },\n", + " config,\n", + " stream_mode=\"updates\",\n", + "):\n", + " print(update)" + ] + }, + { + "cell_type": "markdown", + "id": "b7086cea", + "metadata": {}, + "source": [ + "Perfect! The graph has forgotten all of the previous preferences and has to ask the user for it's location and dietary preferences.\n", + "\n", + "We can also verify that there are no memories stored for user \"2\":" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "dea8be21-c4a0-434c-87ab-706c138beb23", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "in_memory_store.search((\"memories\", \"2\"))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index fc477f961..93b373a67 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -142,6 +142,7 @@ nav: - Manage conversation history: how-tos/memory/manage-conversation-history.ipynb - Delete messages: how-tos/memory/delete-messages.ipynb - Add summary of the conversation history: how-tos/memory/add-summary-conversation-history.ipynb + - Share state between threads: how-tos/memory/shared-state.ipynb - Use Postgres checkpointer for persistence: how-tos/persistence_postgres.ipynb - Create custom checkpointer using MongoDB: how-tos/persistence_mongodb.ipynb - Create custom checkpointer using Redis: how-tos/persistence_redis.ipynb