• When you build a basic chatbot using LangGraph, you run into an immediate limitation: by default, your chatbot has amnesia.
  • Every time a user starts a conversation, the bot has no recollection of previous interactions.
  • This happens because without memory management, each invocation of your graph is completely independent.
  • This is where the concept of checkpointers in LangGraph come into the picture.

Checkpointer

  • A checkpointer in LangGraph is essentially a way to save the state of your agent or workflow at specific points during execution.

  • Think of it like saving your progress in a video game. When you reach a checkpoint:

    • The current state of everything is saved
    • If something goes wrong later, you can return to this saved point.
  • In the context of LangGraph nodes and workflows:

    • Nodes are the individual steps or components in your workflow
    • Checkpoints save the complete sate after a node finishes its work if an error occurs in a later node, you can resume from the last checkpoint rather than starting the entire workflow again.
  • This is particularly useful for complex workflows where:

    • Processing takes significant time or resources.
    • You want to implement retry mechanisms.
    • You need persistance across sessions or server restarts.

Thread ID

  • A thread ID is simply a unique identifier for each specific conversation or workflow execution. Think of it like:
    • A unique session ID for a user.
    • A conversation ID that groups related messages together
  • The thread ID is necessary because:
    • You might have multiple conversations/workflows running simultaneously.
    • Each needs its own separate saved state.
    • The thread ID helps the system know which saved state belongs to which conversation
  • Without thread IDs, all your conversations would share the same state, which would cause confusion and errors.

MemorySaver (InMemorySaver)

 
from typing import TypedDict, Annotated
from langgraph.graph import add_messages, StateGraph, END
from langchain_groq import ChatGroq
from langchain_core.messages import AIMessage, HumanMessage
from dotenv import load_dotenv
from langgraph.checkpoint.memory import MemorySaver
 
load_dotenv()
 
memory = MemorySaver()
 
llm = ChatGroq(model="llama-3.1-8b-instant")
 
class BasicChatState(TypedDict):
    messages: Annotated[list, add_messages]
 
 
def chatbot(state: BasicChatState):
    return {
        "messages": [llm.invoke(state["messages"])],
    }
 
graph = StateGraph(BasicChatState)
 
graph.add_node("chatbot", chatbot)
graph.set_entry_point("chatbot")
graph.add_edge("chatbot", END)
 
app = graph.compile(checkpointer=memory)
 
# The config is used to set the thread_id for the in-memory checkpointing
# To store and use the in-memory checkpoint, you need to set the thread_id same for all the conversations
config = {"configurable": {
        "thread_id": 1,
    }
}
 
while True:
    user_input = input("User: ")
    if(user_input == "exit"):
        break
 
    result =  app.invoke({
        "messages": [HumanMessage(content=user_input)]
    }, config=config)
    print("AI: ", result["messages"][-1].content)
 
# The in-memory checkpointing will store the state of the conversations
# You can retrieve the state of the conversations using the get_state methos
print(app.get_state(config=config))
 

SqliteSaver

from typing import TypedDict, Annotated
from langgraph.graph import add_messages, StateGraph, END
from langchain_groq import ChatGroq
from langchain_core.messages import AIMessage, HumanMessage
from dotenv import load_dotenv
# from langgraph.checkpoint.memory import MemorySaver
from langgraph.checkpoint.sqlite import SqliteSaver
import sqlite3
 
load_dotenv()
 
# LangGraph works with multiple threads
# Sqlite only supports one thread at a time
# So we need to create a connection to the sqlite database explicitly stating to not check for the same thread
sqlite_conn = sqlite3.connect("checkpoint.sqlite", check_same_thread=False)
 
memory = SqliteSaver(sqlite_conn)
 
llm = ChatGroq(model="llama-3.1-8b-instant")
 
class BasicChatState(TypedDict):
    messages: Annotated[list, add_messages]
 
 
def chatbot(state: BasicChatState):
    return {
        "messages": [llm.invoke(state["messages"])],
    }
 
graph = StateGraph(BasicChatState)
 
graph.add_node("chatbot", chatbot)
graph.set_entry_point("chatbot")
graph.add_edge("chatbot", END)
 
app = graph.compile(checkpointer=memory)
 
# The config is used to set the thread_id for the in-memory checkpointing
# To store and use the in-memory checkpoint, you need to set the thread_id same for all the conversations
config = {"configurable": {
        "thread_id": 1,
    }
}
 
while True:
    user_input = input("User: ")
    if(user_input == "exit"):
        break
 
    result =  app.invoke({
        "messages": [HumanMessage(content=user_input)]
    }, config=config)
    print("AI: ", result["messages"][-1].content)
 
# The in-memory checkpointing will store the state of the conversations
# You can retrieve the state of the conversations using the get_state methos
print(app.get_state(config=config))