新版文档(v1.x)
LangGraph
Memory

Memory

AI applications need memory (opens in a new tab) to share context across multiple interactions. In LangGraph, you can add two types of memory:

Add short-term memory

Short-term memory (thread-level persistence (opens in a new tab)) enables agents to track multi-turn conversations. To add short-term memory:

from langgraph.checkpoint.memory import InMemorySaver  # [!code highlight]
from langgraph.graph import StateGraph
 
checkpointer = InMemorySaver()  # [!code highlight]
 
builder = StateGraph(...)
graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]
 
graph.invoke(
    {"messages": [{"role": "user", "content": "hi! i am Bob"}]},
    {"configurable": {"thread_id": "1"}},  # [!code highlight]
)

Use in production

In production, use a checkpointer backed by a database:

from langgraph.checkpoint.postgres import PostgresSaver
 
DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:  # [!code highlight]
    builder = StateGraph(...)
    graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

Example: using Postgres checkpointer

:::python

pip install -U "psycopg[binary,pool]" langgraph langgraph-checkpoint-postgres
You need to call `checkpointer.setup()` the first time you're using Postgres checkpointer

Sync

  ```python
  from langchain.chat_models import init_chat_model
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph.checkpoint.postgres import PostgresSaver  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")

  DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
  with PostgresSaver.from_conn_string(DB_URI) as checkpointer:  # [!code highlight]
      # checkpointer.setup()

      def call_model(state: MessagesState):
          response = model.invoke(state["messages"])
          return {"messages": response}

      builder = StateGraph(MessagesState)
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

      config = {
          "configurable": {
              "thread_id": "1"  # [!code highlight]
          }
      }

      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          print(snapshot)

      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "what's my name?"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          print(snapshot)
        

### Async
      ```python
      from langchain.chat_models import init_chat_model
      from langgraph.graph import StateGraph, MessagesState, START
      from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver  # [!code highlight]

      model = init_chat_model(model="claude-haiku-4-5-20251001")

      DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
      async with AsyncPostgresSaver.from_conn_string(DB_URI) as checkpointer:  # [!code highlight]
          # await checkpointer.setup()

          async def call_model(state: MessagesState):
              response = await model.ainvoke(state["messages"])
              return {"messages": response}

          builder = StateGraph(MessagesState)
          builder.add_node(call_model)
          builder.add_edge(START, "call_model")

          graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

          config = {
              "configurable": {
                  "thread_id": "1"  # [!code highlight]
              }
          }

          stream = await graph.astream_events(
              {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
              config,  # [!code highlight]
              version="v3",
          )
          async for message in stream.messages:
              async for token in message.text:
                  print(token, end="", flush=True)

          stream = await graph.astream_events(
              {"messages": [{"role": "user", "content": "what's my name?"}]},
              config,  # [!code highlight]
              version="v3",
          )
          async for message in stream.messages:
              async for token in message.text:
                  print(token, end="", flush=True)

:::

:::js

npm install @langchain/langgraph-checkpoint-postgres
You need to call `checkpointer.setup()` the first time you're using Postgres checkpointer
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
 
const State = new StateSchema({
  messages: MessagesValue,
});
 
const model = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
 
const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
const checkpointer = PostgresSaver.fromConnString(DB_URI);
// await checkpointer.setup();
 
const callModel: GraphNode<typeof State> = async (state) => {
  const response = await model.invoke(state.messages);
  return { messages: [response] };
};
 
const builder = new StateGraph(State)
  .addNode("call_model", callModel)
  .addEdge(START, "call_model");
 
const graph = builder.compile({ checkpointer });
 
const config = {
  configurable: {
    thread_id: "1"
  }
};
 
const stream1 = await graph.streamEvents(
  { messages: [{ role: "user", content: "hi! I'm bob" }] },
  { ...config, version: "v3" }
);
for await (const snapshot of stream1.values) {
  console.log(snapshot);
}
 
const stream2 = await graph.streamEvents(
  { messages: [{ role: "user", content: "what's my name?" }] },
  { ...config, version: "v3" }
);
for await (const snapshot of stream2.values) {
  console.log(snapshot);
}

:::

Example: using MongoDB checkpointer

:::python

pip install -U pymongo langgraph langgraph-checkpoint-mongodb
**Setup**
To use the [MongoDB checkpointer](https://pypi.org/project/langgraph-checkpoint-mongodb/), you will need a MongoDB cluster. Follow [this guide](https://www.mongodb.com/docs/guides/atlas/cluster/) to create a cluster if you don't already have one. For an agent-focused walkthrough, see [short-term memory with MongoDB Atlas](https://docs.langchain.com/oss/integrations/memory/mongodb-short-term-memory).

Sync

  ```python
  from langchain.chat_models import init_chat_model
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph.checkpoint.mongodb import MongoDBSaver  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")

  MONGODB_URI = "localhost:27017"
  with MongoDBSaver.from_conn_string(MONGODB_URI) as checkpointer:  # [!code highlight]

      def call_model(state: MessagesState):
          response = model.invoke(state["messages"])
          return {"messages": response}

      builder = StateGraph(MessagesState)
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

      config = {
          "configurable": {
              "thread_id": "1"  # [!code highlight]
          }
      }

      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          print(snapshot)

      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "what's my name?"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          print(snapshot)
        

### Async
      ```python
      from langchain.chat_models import init_chat_model
      from langgraph.graph import StateGraph, MessagesState, START
      from langgraph.checkpoint.mongodb.aio import AsyncMongoDBSaver  # [!code highlight]

      model = init_chat_model(model="claude-haiku-4-5-20251001")

      MONGODB_URI = "localhost:27017"
      async with AsyncMongoDBSaver.from_conn_string(MONGODB_URI) as checkpointer:  # [!code highlight]

          async def call_model(state: MessagesState):
              response = await model.ainvoke(state["messages"])
              return {"messages": response}

          builder = StateGraph(MessagesState)
          builder.add_node(call_model)
          builder.add_edge(START, "call_model")

          graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

          config = {
              "configurable": {
                  "thread_id": "1"  # [!code highlight]
              }
          }

          stream = await graph.astream_events(
              {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
              config,  # [!code highlight]
              version="v3",
          )
          async for message in stream.messages:
              async for token in message.text:
                  print(token, end="", flush=True)

          stream = await graph.astream_events(
              {"messages": [{"role": "user", "content": "what's my name?"}]},
              config,  # [!code highlight]
              version="v3",
          )
          async for message in stream.messages:
              async for token in message.text:
                  print(token, end="", flush=True)

:::

:::js

npm install @langchain/langgraph-checkpoint-mongodb

Setup To use MongoDBSaver, you will need a MongoDB cluster. Follow this guide (opens in a new tab) to create a cluster if you don't already have one. For an agent-focused walkthrough, see short-term memory with MongoDB Atlas (opens in a new tab).

import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { MongoDBSaver } from "@langchain/langgraph-checkpoint-mongodb";
import { MongoClient } from "mongodb";
 
const State = new StateSchema({
  messages: MessagesValue,
});
 
const model = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
 
const client = new MongoClient("mongodb://user:password@localhost:27017");
const checkpointer = new MongoDBSaver({ client, dbName: "langgraph" });
 
const callModel: GraphNode<typeof State> = async (state) => {
  const response = await model.invoke(state.messages);
  return { messages: [response] };
};
 
const builder = new StateGraph(State)
  .addNode("call_model", callModel)
  .addEdge(START, "call_model");
 
const graph = builder.compile({ checkpointer });
 
const config = { configurable: { thread_id: "1" } };
 
const stream1 = await graph.streamEvents(
  { messages: [{ role: "user", content: "hi! I'm bob" }] },
  { ...config, version: "v3" }
);
for await (const snapshot of stream1.values) {
  console.log(snapshot);
}
 
const stream2 = await graph.streamEvents(
  { messages: [{ role: "user", content: "what's my name?" }] },
  { ...config, version: "v3" }
);
for await (const snapshot of stream2.values) {
  console.log(snapshot);
}

:::

Example: using Redis checkpointer

pip install -U langgraph langgraph-checkpoint-redis
You need to call `checkpointer.setup()` the first time you're using Redis checkpointer.

Sync

  ```python
  from langchain.chat_models import init_chat_model
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph.checkpoint.redis import RedisSaver  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")

  DB_URI = "redis://localhost:6379"
  with RedisSaver.from_conn_string(DB_URI) as checkpointer:  # [!code highlight]
      # checkpointer.setup()

      def call_model(state: MessagesState):
          response = model.invoke(state["messages"])
          return {"messages": response}

      builder = StateGraph(MessagesState)
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

      config = {
          "configurable": {
              "thread_id": "1"  # [!code highlight]
          }
      }

      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          print(snapshot)

      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "what's my name?"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          print(snapshot)
  ```
    

Async

  ```python
  from langchain.chat_models import init_chat_model
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph.checkpoint.redis.aio import AsyncRedisSaver  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")

  DB_URI = "redis://localhost:6379"
  async with AsyncRedisSaver.from_conn_string(DB_URI) as checkpointer:  # [!code highlight]
      # await checkpointer.asetup()

      async def call_model(state: MessagesState):
          response = await model.ainvoke(state["messages"])
          return {"messages": response}

      builder = StateGraph(MessagesState)
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

      config = {
          "configurable": {
              "thread_id": "1"  # [!code highlight]
          }
      }

      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
          config,  # [!code highlight]
          version="v3",
      )
      async for message in stream.messages:
          async for token in message.text:
              print(token, end="", flush=True)

      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "what's my name?"}]},
          config,  # [!code highlight]
          version="v3",
      )
      async for message in stream.messages:
          async for token in message.text:
              print(token, end="", flush=True)
        
    


### Example: using Oracle checkpointer

pip install -U langgraph langgraph-oracledb


>     
>     **Setup**
>     To use the [Oracle checkpointer](https://pypi.org/project/langgraph-oracledb/), you will need an Oracle AI Database instance. A local container (for example `gvenzl/oracle-free:23-slim`) or an Oracle Autonomous Database in OCI both work.

>     
>     You need to call `checkpointer.setup()` the first time you're using the Oracle checkpointer.

  

### Sync
    ```python
    from langchain.chat_models import init_chat_model
    from langgraph.graph import StateGraph, MessagesState, START
    from langgraph_oracledb.checkpoint.oracle import OracleSaver  # [!code highlight]

    model = init_chat_model(model="claude-haiku-4-5-20251001")

    DB_URI = "user/password@localhost:1521/FREEPDB1"
    with OracleSaver.from_conn_string(DB_URI) as checkpointer:  # [!code highlight]
        # checkpointer.setup()

        def call_model(state: MessagesState):
            response = model.invoke(state["messages"])
            return {"messages": response}

        builder = StateGraph(MessagesState)
        builder.add_node(call_model)
        builder.add_edge(START, "call_model")

        graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

        config = {
            "configurable": {
                "thread_id": "1"  # [!code highlight]
            }
        }

        stream = graph.stream_events(
            {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
            config,  # [!code highlight]
            version="v3",
        )
        for snapshot in stream.values:
            print(snapshot)

        stream = graph.stream_events(
            {"messages": [{"role": "user", "content": "what's my name?"}]},
            config,  # [!code highlight]
            version="v3",
        )
        for snapshot in stream.values:
            print(snapshot)

Async

  ```python
  from langchain.chat_models import init_chat_model
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph_oracledb.checkpoint.oracle import AsyncOracleSaver  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")

  DB_URI = "user/password@localhost:1521/FREEPDB1"
  async with AsyncOracleSaver.from_conn_string(DB_URI) as checkpointer:  # [!code highlight]
      # await checkpointer.setup()

      async def call_model(state: MessagesState):
          response = await model.ainvoke(state["messages"])
          return {"messages": response}

      builder = StateGraph(MessagesState)
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

      config = {
          "configurable": {
              "thread_id": "1"  # [!code highlight]
          }
      }

      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
          config,  # [!code highlight]
          version="v3",
      )
      async for message in stream.messages:
          async for token in message.text:
              print(token, end="", flush=True)

      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "what's my name?"}]},
          config,  # [!code highlight]
          version="v3",
      )
      async for message in stream.messages:
          async for token in message.text:
              print(token, end="", flush=True)
        
    


### Use in subgraphs

If your graph contains [subgraphs](https://docs.langchain.com/oss/langgraph/use-subgraphs), you only need to provide the checkpointer when compiling the parent graph. LangGraph will automatically propagate the checkpointer to the child subgraphs.

```python
from langgraph.graph import START, StateGraph
from langgraph.checkpoint.memory import InMemorySaver
from typing import TypedDict

class State(TypedDict):
    foo: str

# Subgraph

def subgraph_node_1(state: State):
    return {"foo": state["foo"] + "bar"}

subgraph_builder = StateGraph(State)
subgraph_builder.add_node(subgraph_node_1)
subgraph_builder.add_edge(START, "subgraph_node_1")
subgraph = subgraph_builder.compile()  # [!code highlight]

# Parent graph

builder = StateGraph(State)
builder.add_node("node_1", subgraph)  # [!code highlight]
builder.add_edge(START, "node_1")

checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)  # [!code highlight]

You can configure subgraph-specific checkpointing behavior. See subgraph persistence (opens in a new tab) for details on persistence levels including interrupt support and stateful continuations.

subgraph_builder = StateGraph(...)
subgraph = subgraph_builder.compile(checkpointer=True)  # [!code highlight]

Add long-term memory

Use long-term memory to store user-specific or application-specific data across conversations.

from langgraph.store.memory import InMemoryStore  # [!code highlight]
from langgraph.graph import StateGraph
 
store = InMemoryStore()  # [!code highlight]
 
builder = StateGraph(...)
graph = builder.compile(store=store)  # [!code highlight]

Access the store inside nodes

Once you compile a graph with a store, LangGraph automatically injects the store into your node functions. The recommended way to access the store is through the Runtime object.

from dataclasses import dataclass
from langgraph.runtime import Runtime
from langgraph.graph import StateGraph, MessagesState, START
from langchain_core.utils.uuid import uuid7
@dataclass
class Context:
    user_id: str
 
async def call_model(state: MessagesState, runtime: Runtime[Context]):  # [!code highlight]
    user_id = runtime.context.user_id  # [!code highlight]
    namespace = (user_id, "memories")
 
    # Search for relevant memories
    memories = await runtime.store.asearch(  # [!code highlight]
        namespace, query=state["messages"][-1].content, limit=3
    )
    info = "\n".join([d.value["data"] for d in memories])
 
    # ... Use memories in model call
 
    # Store a new memory
    await runtime.store.aput(  # [!code highlight]
        namespace, str(uuid7()), {"data": "User prefers dark mode"}
    )
 
builder = StateGraph(MessagesState, context_schema=Context)  # [!code highlight]
builder.add_node(call_model)
builder.add_edge(START, "call_model")
graph = builder.compile(store=store)
 
# Pass context at invocation time
graph.invoke(
    {"messages": [{"role": "user", "content": "hi"}]},
    {"configurable": {"thread_id": "1"}},
    context=Context(user_id="1"),  # [!code highlight]
)

Use in production

In production, use a store backed by a database:

from langgraph.store.postgres import PostgresStore
 
DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
with PostgresStore.from_conn_string(DB_URI) as store:  # [!code highlight]
    builder = StateGraph(...)
    graph = builder.compile(store=store)  # [!code highlight]

Example: using Postgres store

:::python

pip install -U "psycopg[binary,pool]" langgraph langgraph-checkpoint-postgres
You need to call `store.setup()` the first time you're using Postgres store

Async

  ```python
  from dataclasses import dataclass
  from langchain.chat_models import init_chat_model
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
  from langgraph.store.postgres.aio import AsyncPostgresStore  # [!code highlight]
  from langgraph.runtime import Runtime  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")

  @dataclass
  class Context:
      user_id: str

  async def call_model(  # [!code highlight]
      state: MessagesState,
      runtime: Runtime[Context],  # [!code highlight]
  ):
      user_id = runtime.context.user_id  # [!code highlight]
      namespace = ("memories", user_id)
      memories = await runtime.store.asearch(namespace, query=str(state["messages"][-1].content))  # [!code highlight]
      info = "\n".join([d.value["data"] for d in memories])
      system_msg = f"You are a helpful assistant talking to the user. User info: {info}"

      # Store new memories if the user asks the model to remember
      last_message = state["messages"][-1]
      if "remember" in last_message.content.lower():
          memory = "User name is Bob"
          await runtime.store.aput(namespace, str(uuid7()), {"data": memory})  # [!code highlight]

      response = await model.ainvoke(
          [{"role": "system", "content": system_msg}] + state["messages"]
      )
      return {"messages": response}

  DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"

  async with (
      AsyncPostgresStore.from_conn_string(DB_URI) as store,  # [!code highlight]
      AsyncPostgresSaver.from_conn_string(DB_URI) as checkpointer,
  ):
      # await store.setup()
      # await checkpointer.setup()

      builder = StateGraph(MessagesState, context_schema=Context)  # [!code highlight]
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(
          checkpointer=checkpointer,
          store=store,  # [!code highlight]
      )

      config = {"configurable": {"thread_id": "1"}}
      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "Hi! Remember: my name is Bob"}]},
          config,
          version="v3",
          context=Context(user_id="1"),  # [!code highlight]
      )
      async for message in stream.messages:
          async for token in message.text:
              print(token, end="", flush=True)

      config = {"configurable": {"thread_id": "2"}}
      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "what is my name?"}]},
          config,
          version="v3",
          context=Context(user_id="1"),  # [!code highlight]
      )
      async for message in stream.messages:
          async for token in message.text:
              print(token, end="", flush=True)
        

### Sync
      ```python
      from dataclasses import dataclass
      from langchain.chat_models import init_chat_model
      from langgraph.graph import StateGraph, MessagesState, START
      from langgraph.checkpoint.postgres import PostgresSaver
      from langgraph.store.postgres import PostgresStore  # [!code highlight]
      from langgraph.runtime import Runtime  # [!code highlight]

      model = init_chat_model(model="claude-haiku-4-5-20251001")

      @dataclass
      class Context:
          user_id: str

      def call_model(  # [!code highlight]
          state: MessagesState,
          runtime: Runtime[Context],  # [!code highlight]
      ):
          user_id = runtime.context.user_id  # [!code highlight]
          namespace = ("memories", user_id)
          memories = runtime.store.search(namespace, query=str(state["messages"][-1].content))  # [!code highlight]
          info = "\n".join([d.value["data"] for d in memories])
          system_msg = f"You are a helpful assistant talking to the user. User info: {info}"

          # Store new memories if the user asks the model to remember
          last_message = state["messages"][-1]
          if "remember" in last_message.content.lower():
              memory = "User name is Bob"
              runtime.store.put(namespace, str(uuid7()), {"data": memory})  # [!code highlight]

          response = model.invoke(
              [{"role": "system", "content": system_msg}] + state["messages"]
          )
          return {"messages": response}

      DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"

      with (
          PostgresStore.from_conn_string(DB_URI) as store,  # [!code highlight]
          PostgresSaver.from_conn_string(DB_URI) as checkpointer,
      ):
          # store.setup()
          # checkpointer.setup()

          builder = StateGraph(MessagesState, context_schema=Context)  # [!code highlight]
          builder.add_node(call_model)
          builder.add_edge(START, "call_model")

          graph = builder.compile(
              checkpointer=checkpointer,
              store=store,  # [!code highlight]
          )

          config = {"configurable": {"thread_id": "1"}}
          stream = graph.stream_events(
              {"messages": [{"role": "user", "content": "Hi! Remember: my name is Bob"}]},
              config,
              version="v3",
              context=Context(user_id="1"),  # [!code highlight]
          )
          for snapshot in stream.values:
              print(snapshot)

          config = {"configurable": {"thread_id": "2"}}
          stream = graph.stream_events(
              {"messages": [{"role": "user", "content": "what is my name?"}]},
              config,
              version="v3",
              context=Context(user_id="1"),  # [!code highlight]
          )
          for snapshot in stream.values:
              print(snapshot)

:::

:::js

npm install @langchain/langgraph-checkpoint-postgres
You need to call `store.setup()` the first time you're using Postgres store
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres/store";
 
const State = new StateSchema({
  messages: MessagesValue,
});
 
const model = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
 
const callModel: GraphNode<typeof State> = async (state, runtime) => {
  const userId = runtime.context?.userId;
  const namespace = ["memories", userId];
  const memories = await runtime.store?.search(namespace, { query: state.messages.at(-1)?.content });
  const info = memories?.map(d => d.value.data).join("\n") || "";
  const systemMsg = `You are a helpful assistant talking to the user. User info: ${info}`;
 
  // Store new memories if the user asks the model to remember
  const lastMessage = state.messages.at(-1);
  if (lastMessage?.content?.toLowerCase().includes("remember")) {
    const memory = "User name is Bob";
    await runtime.store?.put(namespace, uuid7(), { data: memory });
  }
 
  const response = await model.invoke([
    { role: "system", content: systemMsg },
    ...state.messages
  ]);
  return { messages: [response] };
};
 
const DB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable";
 
const store = PostgresStore.fromConnString(DB_URI);
const checkpointer = PostgresSaver.fromConnString(DB_URI);
// await store.setup();
// await checkpointer.setup();
 
const builder = new StateGraph(State)
  .addNode("call_model", callModel)
  .addEdge(START, "call_model");
 
const graph = builder.compile({
  checkpointer,
  store,
});
 
const stream1 = await graph.streamEvents(
  { messages: [{ role: "user", content: "Hi! Remember: my name is Bob" }] },
  { configurable: { thread_id: "1" }, context: { userId: "1" }, version: "v3" }
);
for await (const snapshot of stream1.values) {
  console.log(snapshot);
}
 
const stream2 = await graph.streamEvents(
  { messages: [{ role: "user", content: "what is my name?" }] },
  { configurable: { thread_id: "2" }, context: { userId: "1" }, version: "v3" }
);
for await (const snapshot of stream2.values) {
  console.log(snapshot);
}

:::

Example: using MongoDB store

:::js

npm install @langchain/langgraph-checkpoint-mongodb

For an agent-focused walkthrough, see long-term memory with MongoDB Atlas (opens in a new tab).

import { ChatAnthropic } from "@langchain/anthropic";
import { MemorySaver, StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
import { MongoDBStore } from "@langchain/langgraph-checkpoint-mongodb";
 
const State = new StateSchema({
  messages: MessagesValue,
});
 
const model = new ChatAnthropic({ model: "claude-sonnet-4-6" });
 
const callModel: GraphNode<typeof State> = async (state, runtime) => {
  const userId = runtime.context?.userId;
  const namespace = ["memories", userId];
  const memories = await runtime.store?.search(namespace);
  const info = memories?.map(d => d.value.data).join("\n") || "n/a";
  const systemMsg = `You are a helpful assistant talking to the user. User info: ${info}`;
 
  // Store new memories if the user asks the model to remember
  const lastMessage = state.messages.at(-1);
  if (lastMessage?.content?.toLowerCase().includes("remember")) {
    const memory = "User name is Bob";
    await runtime.store?.put(namespace, uuid7(), { data: memory });
  }
 
  const response = await model.invoke([
    { role: "system", content: systemMsg },
    ...state.messages
  ]);
  return { messages: [response] };
};
 
const MONGODB_URI = "mongodb://user:password@localhost:27017";
 
const store = await MongoDBStore.fromConnString(MONGODB_URI, {
  dbName: "langgraph",
  collectionName: "store",
});
 
const checkpointer = new MemorySaver();
 
const builder = new StateGraph(State)
  .addNode("call_model", callModel)
  .addEdge(START, "call_model");
 
const graph = builder.compile({ checkpointer, store });
 
const stream1 = await graph.streamEvents(
  { messages: [{ role: "user", content: "Hi! Remember: my name is Bob" }] },
  { configurable: { thread_id: "1" }, context: { userId: "1" }, version: "v3" }
);
for await (const snapshot of stream1.values) {
  console.log(snapshot);
}
 
const stream2 = await graph.streamEvents(
  { messages: [{ role: "user", content: "what is my name?" }] },
  { configurable: { thread_id: "2" }, context: { userId: "1" }, version: "v3" }
);
for await (const snapshot of stream2.values) {
  console.log(snapshot);
}

:::

Example: using Redis store

pip install -U langgraph langgraph-checkpoint-redis
You need to call `store.setup()` the first time you're using [Redis store](https://pypi.org/project/langgraph-checkpoint-redis/).

Async

  ```python
  from dataclasses import dataclass
  from langchain.chat_models import init_chat_model
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph.checkpoint.redis.aio import AsyncRedisSaver
  from langgraph.store.redis.aio import AsyncRedisStore  # [!code highlight]
  from langgraph.runtime import Runtime  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")

  @dataclass
  class Context:
      user_id: str

  async def call_model(  # [!code highlight]
      state: MessagesState,
      runtime: Runtime[Context],  # [!code highlight]
  ):
      user_id = runtime.context.user_id  # [!code highlight]
      namespace = ("memories", user_id)
      memories = await runtime.store.asearch(namespace, query=str(state["messages"][-1].content))  # [!code highlight]
      info = "\n".join([d.value["data"] for d in memories])
      system_msg = f"You are a helpful assistant talking to the user. User info: {info}"

      # Store new memories if the user asks the model to remember
      last_message = state["messages"][-1]
      if "remember" in last_message.content.lower():
          memory = "User name is Bob"
          await runtime.store.aput(namespace, str(uuid7()), {"data": memory})  # [!code highlight]

      response = await model.ainvoke(
          [{"role": "system", "content": system_msg}] + state["messages"]
      )
      return {"messages": response}

  DB_URI = "redis://localhost:6379"

  async with (
      AsyncRedisStore.from_conn_string(DB_URI) as store,  # [!code highlight]
      AsyncRedisSaver.from_conn_string(DB_URI) as checkpointer,
  ):
      # await store.setup()
      # await checkpointer.asetup()

      builder = StateGraph(MessagesState, context_schema=Context)  # [!code highlight]
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(
          checkpointer=checkpointer,
          store=store,  # [!code highlight]
      )

      config = {"configurable": {"thread_id": "1"}}
      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "Hi! Remember: my name is Bob"}]},
          config,
          version="v3",
          context=Context(user_id="1"),  # [!code highlight]
      )
      async for snapshot in stream.values:
          snapshot["messages"][-1].pretty_print()

      config = {"configurable": {"thread_id": "2"}}
      stream = await graph.astream_events(
          {"messages": [{"role": "user", "content": "what is my name?"}]},
          config,
          version="v3",
          context=Context(user_id="1"),  # [!code highlight]
      )
      async for snapshot in stream.values:
          snapshot["messages"][-1].pretty_print()
        

### Sync
      ```python
      from dataclasses import dataclass
      from langchain.chat_models import init_chat_model
      from langgraph.graph import StateGraph, MessagesState, START
      from langgraph.checkpoint.redis import RedisSaver
      from langgraph.store.redis import RedisStore  # [!code highlight]
      from langgraph.runtime import Runtime  # [!code highlight]

      model = init_chat_model(model="claude-haiku-4-5-20251001")

      @dataclass
      class Context:
          user_id: str

      def call_model(  # [!code highlight]
          state: MessagesState,
          runtime: Runtime[Context],  # [!code highlight]
      ):
          user_id = runtime.context.user_id  # [!code highlight]
          namespace = ("memories", user_id)
          memories = runtime.store.search(namespace, query=str(state["messages"][-1].content))  # [!code highlight]
          info = "\n".join([d.value["data"] for d in memories])
          system_msg = f"You are a helpful assistant talking to the user. User info: {info}"

          # Store new memories if the user asks the model to remember
          last_message = state["messages"][-1]
          if "remember" in last_message.content.lower():
              memory = "User name is Bob"
              runtime.store.put(namespace, str(uuid7()), {"data": memory})  # [!code highlight]

          response = model.invoke(
              [{"role": "system", "content": system_msg}] + state["messages"]
          )
          return {"messages": response}

      DB_URI = "redis://localhost:6379"

      with (
          RedisStore.from_conn_string(DB_URI) as store,  # [!code highlight]
          RedisSaver.from_conn_string(DB_URI) as checkpointer,
      ):
          store.setup()
          checkpointer.setup()

          builder = StateGraph(MessagesState, context_schema=Context)  # [!code highlight]
          builder.add_node(call_model)
          builder.add_edge(START, "call_model")

          graph = builder.compile(
              checkpointer=checkpointer,
              store=store,  # [!code highlight]
          )

          config = {"configurable": {"thread_id": "1"}}
          stream = graph.stream_events(
              {"messages": [{"role": "user", "content": "Hi! Remember: my name is Bob"}]},
              config,
              version="v3",
              context=Context(user_id="1"),  # [!code highlight]
          )
          for snapshot in stream.values:
              snapshot["messages"][-1].pretty_print()

          config = {"configurable": {"thread_id": "2"}}
          stream = graph.stream_events(
              {"messages": [{"role": "user", "content": "what is my name?"}]},
              config,
              version="v3",
              context=Context(user_id="1"),  # [!code highlight]
          )
          for snapshot in stream.values:
              snapshot["messages"][-1].pretty_print()

Example: using Oracle store

pip install -U langgraph langgraph-oracledb langchain-openai
**Setup**
To use the [Oracle store](https://pypi.org/project/langgraph-oracledb/), you will need an Oracle AI Database instance — the vector index used for semantic `search` requires [Oracle AI Vector Search](https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/).
You need to call `store.setup()` and `checkpointer.setup()` the first time you're using the Oracle store and checkpointer.

Sync

  ```python

  from langchain.chat_models import init_chat_model
  from langchain.embeddings import init_embeddings
  from langchain_core.runnables import RunnableConfig
  from langgraph.graph import StateGraph, MessagesState, START
  from langgraph.store.base import BaseStore
  from langgraph_oracledb.checkpoint.oracle import OracleSaver
  from langgraph_oracledb.store.oracle import OracleStore  # [!code highlight]

  model = init_chat_model(model="claude-haiku-4-5-20251001")
  embeddings = init_embeddings("openai:text-embedding-3-small")

  DB_URI = "user/password@localhost:1521/FREEPDB1"

  with (
      OracleStore.from_conn_string(  # [!code highlight]
          DB_URI,
          index={"embed": embeddings, "dims": 1536},  # [!code highlight]
      ) as store,
      OracleSaver.from_conn_string(DB_URI) as checkpointer,
  ):
      store.setup()
      checkpointer.setup()

      def call_model(
          state: MessagesState,
          config: RunnableConfig,
          *,
          store: BaseStore,  # [!code highlight]
      ):
          user_id = config["configurable"]["user_id"]
          namespace = ("memories", user_id)
          memories = store.search(namespace, query=str(state["messages"][-1].content))  # [!code highlight]
          info = "\n".join([d.value["data"] for d in memories])
          system_msg = f"You are a helpful assistant talking to the user. User info: {info}"

          # Store new memories if the user asks the model to remember
          last_message = state["messages"][-1]
          if "remember" in last_message.content.lower():
              memory = "User name is Bob"
              store.put(namespace, str(uuid7()), {"data": memory})  # [!code highlight]

          response = model.invoke(
              [{"role": "system", "content": system_msg}] + state["messages"]
          )
          return {"messages": response}

      builder = StateGraph(MessagesState)
      builder.add_node(call_model)
      builder.add_edge(START, "call_model")

      graph = builder.compile(
          checkpointer=checkpointer,
          store=store,  # [!code highlight]
      )

      config = {
          "configurable": {
              "thread_id": "1",  # [!code highlight]
              "user_id": "1",  # [!code highlight]
          }
      }
      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "Hi! Remember: my name is Bob"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          snapshot["messages"][-1].pretty_print()

      config = {
          "configurable": {
              "thread_id": "2",  # [!code highlight]
              "user_id": "1",
          }
      }

      stream = graph.stream_events(
          {"messages": [{"role": "user", "content": "what is my name?"}]},
          config,  # [!code highlight]
          version="v3",
      )
      for snapshot in stream.values:
          snapshot["messages"][-1].pretty_print()
        

### Async
      ```python

      from langchain.chat_models import init_chat_model
      from langchain.embeddings import init_embeddings
      from langchain_core.runnables import RunnableConfig
      from langgraph.graph import StateGraph, MessagesState, START
      from langgraph.store.base import BaseStore
      from langgraph_oracledb.checkpoint.oracle import AsyncOracleSaver
      from langgraph_oracledb.store.oracle import AsyncOracleStore  # [!code highlight]

      model = init_chat_model(model="claude-haiku-4-5-20251001")
      embeddings = init_embeddings("openai:text-embedding-3-small")

      DB_URI = "user/password@localhost:1521/FREEPDB1"

      async with (
          AsyncOracleStore.from_conn_string(  # [!code highlight]
              DB_URI,
              index={"embed": embeddings, "dims": 1536},  # [!code highlight]
          ) as store,
          AsyncOracleSaver.from_conn_string(DB_URI) as checkpointer,
      ):
          await store.setup()
          await checkpointer.setup()

          async def call_model(
              state: MessagesState,
              config: RunnableConfig,
              *,
              store: BaseStore,  # [!code highlight]
          ):
              user_id = config["configurable"]["user_id"]
              namespace = ("memories", user_id)
              memories = await store.asearch(namespace, query=str(state["messages"][-1].content))  # [!code highlight]
              info = "\n".join([d.value["data"] for d in memories])
              system_msg = f"You are a helpful assistant talking to the user. User info: {info}"

              # Store new memories if the user asks the model to remember
              last_message = state["messages"][-1]
              if "remember" in last_message.content.lower():
                  memory = "User name is Bob"
                  await store.aput(namespace, str(uuid7()), {"data": memory})  # [!code highlight]

              response = await model.ainvoke(
                  [{"role": "system", "content": system_msg}] + state["messages"]
              )
              return {"messages": response}

          builder = StateGraph(MessagesState)
          builder.add_node(call_model)
          builder.add_edge(START, "call_model")

          graph = builder.compile(
              checkpointer=checkpointer,
              store=store,  # [!code highlight]
          )

          config = {
              "configurable": {
                  "thread_id": "1",  # [!code highlight]
                  "user_id": "1",  # [!code highlight]
              }
          }
          stream = await graph.astream_events(
              {"messages": [{"role": "user", "content": "Hi! Remember: my name is Bob"}]},
              config,  # [!code highlight]
              version="v3",
          )
          async for snapshot in stream.values:
              snapshot["messages"][-1].pretty_print()

          config = {
              "configurable": {
                  "thread_id": "2",  # [!code highlight]
                  "user_id": "1",
              }
          }

          stream = await graph.astream_events(
              {"messages": [{"role": "user", "content": "what is my name?"}]},
              config,  # [!code highlight]
              version="v3",
          )
          async for snapshot in stream.values:
              snapshot["messages"][-1].pretty_print()

Use semantic search

Enable semantic search in your graph's memory store to let graph agents search for items in the store by semantic similarity.

from langchain.embeddings import init_embeddings
from langgraph.store.memory import InMemoryStore
 
# Create store with semantic search enabled
embeddings = init_embeddings("openai:text-embedding-3-small")
store = InMemoryStore(
    index={
        "embed": embeddings,
        "dims": 1536,
    }
)
 
store.put(("user_123", "memories"), "1", {"text": "I love pizza"})
store.put(("user_123", "memories"), "2", {"text": "I am a plumber"})
 
items = store.search(
    ("user_123", "memories"), query="I'm hungry", limit=1
)

Long-term memory with semantic search

:::python

```python

from langchain.embeddings import init_embeddings
from langchain.chat_models import init_chat_model
from langgraph.store.memory import InMemoryStore
from langgraph.graph import START, MessagesState, StateGraph
from langgraph.runtime import Runtime  # [!code highlight]

model = init_chat_model("gpt-5.4-mini")

# Create store with semantic search enabled
embeddings = init_embeddings("openai:text-embedding-3-small")
store = InMemoryStore(
    index={
        "embed": embeddings,
        "dims": 1536,
    }
)

store.put(("user_123", "memories"), "1", {"text": "I love pizza"})
store.put(("user_123", "memories"), "2", {"text": "I am a plumber"})

async def chat(state: MessagesState, runtime: Runtime):  # [!code highlight]
    # Search based on user's last message
    items = await runtime.store.asearch(  # [!code highlight]
        ("user_123", "memories"), query=state["messages"][-1].content, limit=2
    )
    memories = "\n".join(item.value["text"] for item in items)
    memories = f"## Memories of user\n{memories}" if memories else ""
    response = await model.ainvoke(
        [
            {"role": "system", "content": f"You are a helpful assistant.\n{memories}"},
            *state["messages"],
        ]
    )
    return {"messages": [response]}


builder = StateGraph(MessagesState)
builder.add_node(chat)
builder.add_edge(START, "chat")
graph = builder.compile(store=store)

stream = await graph.astream_events(
    {"messages": [{"role": "user", "content": "I'm hungry"}]},
    version="v3",
)
async for message in stream.messages:
    async for token in message.text:
        print(token, end="", flush=True)
```
:::

:::js

InMemoryStore

    ```typescript
    import { OpenAIEmbeddings, ChatOpenAI } from "@langchain/openai";
    import { StateGraph, StateSchema, MessagesValue, GraphNode, START, InMemoryStore } from "@langchain/langgraph";

    const State = new StateSchema({
      messages: MessagesValue,
    });

    const model = new ChatOpenAI({ model: "gpt-5.4-mini" });

    // Create store with semantic search enabled
    const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
    const store = new InMemoryStore({
      index: {
        embeddings,
        dims: 1536,
      }
    });

    await store.put(["user_123", "memories"], "1", { text: "I love pizza" });
    await store.put(["user_123", "memories"], "2", { text: "I am a plumber" });

    const chat: GraphNode<typeof State> = async (state, runtime) => {
      // Search based on user's last message
      const items = await runtime.store.search(
        ["user_123", "memories"],
        { query: state.messages.at(-1)?.content, limit: 2 }
      );
      const memories = items.map(item => item.value.text).join("\n");
      const memoriesText = memories ? `## Memories of user\n${memories}` : "";

      const response = await model.invoke([
        { role: "system", content: `You are a helpful assistant.\n${memoriesText}` },
        ...state.messages,
      ]);

      return { messages: [response] };
    };

    const builder = new StateGraph(State)
      .addNode("chat", chat)
      .addEdge(START, "chat");
    const graph = builder.compile({ store });

    const stream = await graph.streamEvents(
      { messages: [{ role: "user", content: "I'm hungry" }] },
      { version: "v3" }
    );
    for await (const message of stream.messages) {
      for await (const token of message.text) {
        process.stdout.write(token);
      }
    }
    ```
  

MongoDB (manual embedding)

    ```typescript
    import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
    import { MongoDBStore } from "@langchain/langgraph-checkpoint-mongodb";
    import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";

    const State = new StateSchema({
      messages: MessagesValue,
    });

    const model = new ChatOpenAI({ model: "gpt-5.4-mini" });

    // Create store with semantic search enabled
    const MONGODB_URI = "mongodb://user:password@localhost:27017";
    const store = await MongoDBStore.fromConnString(MONGODB_URI, {
      dbName: "langgraph",
      collectionName: "store",
      embeddings: new OpenAIEmbeddings({ model: "text-embedding-3-small" }),
      indexConfig: {
        name: "store_vector_index",
        dims: 1536,
        embeddingKey: "text",
      },
    });

    await store.put(["user_123", "memories"], "1", { text: "I love pizza" });
    await store.put(["user_123", "memories"], "2", { text: "I am a plumber" });

    const chat: GraphNode<typeof State> = async (state, runtime) => {
      // Search based on user's last message
      const items = await runtime.store.search(
        ["user_123", "memories"],
        { query: state.messages.at(-1)?.content, limit: 2 }
      );
      const memories = items.map(item => item.value.text).join("\n");
      const memoriesText = memories ? `## Memories of user\n${memories}` : "";

      const response = await model.invoke([
        { role: "system", content: `You are a helpful assistant.\n${memoriesText}` },
        ...state.messages,
      ]);

      return { messages: [response] };
    };

    const builder = new StateGraph(State)
      .addNode("chat", chat)
      .addEdge(START, "chat");
    const graph = builder.compile({ store });

    const stream = await graph.streamEvents(
      { messages: [{ role: "user", content: "I'm hungry" }] },
      { version: "v3" }
    );
    for await (const message of stream.messages) {
      for await (const token of message.text) {
        process.stdout.write(token);
      }
    }
    ```
  

MongoDB (auto embedding)

    Auto embedding requires MongoDB Atlas. MongoDB generates embeddings server-side via Voyage AI. See the [Automated Embedding documentation](https://www.mongodb.com/docs/atlas/atlas-vector-search/automated-embedding/) for more information.
    ```typescript
    import { StateGraph, StateSchema, MessagesValue, GraphNode, START } from "@langchain/langgraph";
    import { MongoDBStore } from "@langchain/langgraph-checkpoint-mongodb";
    import { ChatOpenAI } from "@langchain/openai";

    const State = new StateSchema({
      messages: MessagesValue,
    });

    const model = new ChatOpenAI({ model: "gpt-5.4-mini" });

    // Auto embedding: no embeddings instance needed.
    // Configure the Voyage AI model and the field path MongoDB will read server-side.
    const MONGODB_URI = "mongodb://user:password@localhost:27017";
    const store = await MongoDBStore.fromConnString(MONGODB_URI, {
      dbName: "langgraph",
      collectionName: "store",
      indexConfig: {
        name: "store_vector_index",
        path: "value.content",  // MongoDB reads this field and embeds it server-side
        model: "voyage-4",      // Voyage AI model used by MongoDB Atlas
      },
    });

    // Values must have the content field matching the configured path (value.content)
    await store.put(["user_123", "memories"], "1", { content: "I love pizza" });
    await store.put(["user_123", "memories"], "2", { content: "I am a plumber" });

    const chat: GraphNode<typeof State> = async (state, runtime) => {
      // MongoDB generates the query embedding server-side
      const items = await runtime.store.search(
        ["user_123", "memories"],
        { query: state.messages.at(-1)?.content, limit: 2 }
      );
      const memories = items.map(item => item.value.content).join("\n");
      const memoriesText = memories ? `## Memories of user\n${memories}` : "";

      const response = await model.invoke([
        { role: "system", content: `You are a helpful assistant.\n${memoriesText}` },
        ...state.messages,
      ]);

      return { messages: [response] };
    };

    const builder = new StateGraph(State)
      .addNode("chat", chat)
      .addEdge(START, "chat");
    const graph = builder.compile({ store });

    const stream = await graph.streamEvents(
      { messages: [{ role: "user", content: "I'm hungry" }] },
      { version: "v3" }
    );
    for await (const message of stream.messages) {
      for await (const token of message.text) {
        process.stdout.write(token);
      }
    }
    ```
  

:::

Manage short-term memory

With short-term memory enabled, long conversations can exceed the LLM's context window. Common solutions are:

This allows the agent to keep track of the conversation without exceeding the LLM's context window.

Trim messages

Most LLMs have a maximum supported context window (denominated in tokens). One way to decide when to truncate messages is to count the tokens in the message history and truncate whenever it approaches that limit. If you're using LangChain, you can use the trim messages utility and specify the number of tokens to keep from the list, as well as the strategy (e.g., keep the last max_tokens) to use for handling the boundary.

To trim message history, use the trim_messages (opens in a new tab) function:

from langchain_core.messages.utils import (  # [!code highlight]
    trim_messages,  # [!code highlight]
    count_tokens_approximately  # [!code highlight]
)  # [!code highlight]
 
def call_model(state: MessagesState):
    messages = trim_messages(  # [!code highlight]
        state["messages"],
        strategy="last",
        token_counter=count_tokens_approximately,
        max_tokens=128,
        start_on="human",
        end_on=("human", "tool"),
    )
    response = model.invoke(messages)
    return {"messages": [response]}
 
builder = StateGraph(MessagesState)
builder.add_node(call_model)
...

Full example: trim messages

:::python

from langchain_core.messages.utils import (
    trim_messages,  # [!code highlight]
    count_tokens_approximately  # [!code highlight]
)
from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, START, MessagesState
 
model = init_chat_model("claude-sonnet-4-6")
summarization_model = model.bind(max_tokens=128)
 
def call_model(state: MessagesState):
    messages = trim_messages(  # [!code highlight]
        state["messages"],
        strategy="last",
        token_counter=count_tokens_approximately,
        max_tokens=128,
        start_on="human",
        end_on=("human", "tool"),
    )
    response = model.invoke(messages)
    return {"messages": [response]}
 
checkpointer = InMemorySaver()
builder = StateGraph(MessagesState)
builder.add_node(call_model)
builder.add_edge(START, "call_model")
graph = builder.compile(checkpointer=checkpointer)
 
config = {"configurable": {"thread_id": "1"}}
graph.invoke({"messages": "hi, my name is bob"}, config)
graph.invoke({"messages": "write a short poem about cats"}, config)
graph.invoke({"messages": "now do the same but for dogs"}, config)
final_response = graph.invoke({"messages": "what's my name?"}, config)
 
final_response["messages"][-1].pretty_print()
================================== Ai Message ==================================

Your name is Bob, as you mentioned when you first introduced yourself.

:::

:::js

import { trimMessages } from "@langchain/core/messages";
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START, MemorySaver } from "@langchain/langgraph";
 
const State = new StateSchema({
  messages: MessagesValue,
});
 
const model = new ChatAnthropic({ model: "claude-sonnet-4-6" });
 
const callModel: GraphNode<typeof State> = async (state) => {
  const messages = trimMessages(state.messages, {
    strategy: "last",
    maxTokens: 128,
    startOn: "human",
    endOn: ["human", "tool"],
    tokenCounter: model,
  });
  const response = await model.invoke(messages);
  return { messages: [response] };
};
 
const checkpointer = new MemorySaver();
const builder = new StateGraph(State)
  .addNode("call_model", callModel)
  .addEdge(START, "call_model");
const graph = builder.compile({ checkpointer });
 
const config = { configurable: { thread_id: "1" } };
await graph.invoke({ messages: [{ role: "user", content: "hi, my name is bob" }] }, config);
await graph.invoke({ messages: [{ role: "user", content: "write a short poem about cats" }] }, config);
await graph.invoke({ messages: [{ role: "user", content: "now do the same but for dogs" }] }, config);
const finalResponse = await graph.invoke({ messages: [{ role: "user", content: "what's my name?" }] }, config);
 
console.log(finalResponse.messages.at(-1)?.content);
Your name is Bob, as you mentioned when you first introduced yourself.

:::

Delete messages

You can delete messages from the graph state to manage the message history. This is useful when you want to remove specific messages or clear the entire message history.

To delete messages from the graph state, you can use the RemoveMessage. For RemoveMessage to work, you need to use a state key with @[add_messages] reducer (opens in a new tab), like MessagesState (opens in a new tab).

To remove specific messages:

from langchain.messages import RemoveMessage  # [!code highlight]
 
def delete_messages(state):
    messages = state["messages"]
    if len(messages) > 2:
        # remove the earliest two messages
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}  # [!code highlight]

To remove all messages:

from langgraph.graph.message import REMOVE_ALL_MESSAGES  # [!code highlight]
 
def delete_messages(state):
    return {"messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES)]}  # [!code highlight]

When deleting messages, make sure that the resulting message history is valid. Check the limitations of the LLM provider you're using. For example:

  • Some providers expect message history to start with a user message
  • Most providers require assistant messages with tool calls to be followed by corresponding tool result messages.

Full example: delete messages

:::python

from langchain.messages import RemoveMessage  # [!code highlight]
 
def delete_messages(state):
    messages = state["messages"]
    if len(messages) > 2:
        # remove the earliest two messages
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}  # [!code highlight]
 
def call_model(state: MessagesState):
    response = model.invoke(state["messages"])
    return {"messages": response}
 
builder = StateGraph(MessagesState)
builder.add_sequence([call_model, delete_messages])
builder.add_edge(START, "call_model")
 
checkpointer = InMemorySaver()
app = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "1"}}
 
stream = app.stream_events(
    {"messages": [{"role": "user", "content": "hi! I'm bob"}]},
    config,
    version="v3"
)
for snapshot in stream.values:
    print([(message.type, message.content) for message in snapshot["messages"]])
 
stream = app.stream_events(
    {"messages": [{"role": "user", "content": "what's my name?"}]},
    config,
    version="v3"
)
for snapshot in stream.values:
    print([(message.type, message.content) for message in snapshot["messages"]])
[('human', "hi! I'm bob")]
[('human', "hi! I'm bob"), ('ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?')]
[('human', "hi! I'm bob"), ('ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'), ('human', "what's my name?")]
[('human', "hi! I'm bob"), ('ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'), ('human', "what's my name?"), ('ai', 'Your name is Bob.')]
[('human', "what's my name?"), ('ai', 'Your name is Bob.')]

:::

:::js

import { RemoveMessage } from "@langchain/core/messages";
import { ChatAnthropic } from "@langchain/anthropic";
import { StateGraph, StateSchema, MessagesValue, GraphNode, START, MemorySaver } from "@langchain/langgraph";
 
const State = new StateSchema({
  messages: MessagesValue,
});
 
const model = new ChatAnthropic({ model: "claude-sonnet-4-6" });
 
const deleteMessages: GraphNode<typeof State> = (state) => {
  const messages = state.messages;
  if (messages.length > 2) {
    // remove the earliest two messages
    return { messages: messages.slice(0, 2).map(m => new RemoveMessage({ id: m.id })) };
  }
  return {};
};
 
const callModel: GraphNode<typeof State> = async (state) => {
  const response = await model.invoke(state.messages);
  return { messages: [response] };
};
 
const builder = new StateGraph(State)
  .addNode("call_model", callModel)
  .addNode("delete_messages", deleteMessages)
  .addEdge(START, "call_model")
  .addEdge("call_model", "delete_messages");
 
const checkpointer = new MemorySaver();
const app = builder.compile({ checkpointer });
 
const config = { configurable: { thread_id: "1" } };
 
const stream1 = await app.streamEvents(
  { messages: [{ role: "user", content: "hi! I'm bob" }] },
  { ...config, version: "v3" }
);
for await (const snapshot of stream1.values) {
  console.log(snapshot.messages.map(message => [message.getType(), message.content]));
}
 
const stream2 = await app.streamEvents(
  { messages: [{ role: "user", content: "what's my name?" }] },
  { ...config, version: "v3" }
);
for await (const snapshot of stream2.values) {
  console.log(snapshot.messages.map(message => [message.getType(), message.content]));
}
[['human', "hi! I'm bob"]]
[['human', "hi! I'm bob"], ['ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?']]
[['human', "hi! I'm bob"], ['ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'], ['human', "what's my name?"]]
[['human', "hi! I'm bob"], ['ai', 'Hi Bob! How are you doing today? Is there anything I can help you with?'], ['human', "what's my name?"], ['ai', 'Your name is Bob.']]
[['human', "what's my name?"], ['ai', 'Your name is Bob.']]

:::

Summarize messages

The problem with trimming or removing messages, as shown above, is that you may lose information from culling of the message queue. Because of this, some applications benefit from a more sophisticated approach of summarizing the message history using a chat model.

Summary

Prompting and orchestration logic can be used to summarize the message history. For example, in LangGraph you can extend the MessagesState (opens in a new tab) to include a summary key:

from langgraph.graph import MessagesState
class State(MessagesState):
    summary: str

Then, you can generate a summary of the chat history, using any existing summary as context for the next summary. This summarize_conversation node can be called after some number of messages have accumulated in the messages state key.

def summarize_conversation(state: State):
 
    # First, we get any existing summary
    summary = state.get("summary", "")
 
    # Create our summarization prompt
    if summary:
 
        # A summary already exists
        summary_message = (
            f"This is a summary of the conversation to date: {summary}\n\n"
            "Extend the summary by taking into account the new messages above:"
        )
 
    else:
        summary_message = "Create a summary of the conversation above:"
 
    # Add prompt to our history
    messages = state["messages"] + [HumanMessage(content=summary_message)]
    response = model.invoke(messages)
 
    # Delete all but the 2 most recent messages
    delete_messages = [RemoveMessage(id=m.id) for m in state["messages"][:-2]]
    return {"summary": response.content, "messages": delete_messages}

Full example: summarize messages

:::python

from typing import Any, TypedDict
 
from langchain.chat_models import init_chat_model
from langchain.messages import AnyMessage
from langchain_core.messages.utils import count_tokens_approximately
from langgraph.graph import StateGraph, START, MessagesState
from langgraph.checkpoint.memory import InMemorySaver
from langmem.short_term import SummarizationNode, RunningSummary  # [!code highlight]
 
model = init_chat_model("claude-sonnet-4-6")
summarization_model = model.bind(max_tokens=128)
 
class State(MessagesState):
    context: dict[str, RunningSummary]  # [!code highlight]
 
class LLMInputState(TypedDict):  # [!code highlight]
    summarized_messages: list[AnyMessage]
    context: dict[str, RunningSummary]
 
summarization_node = SummarizationNode(  # [!code highlight]
    token_counter=count_tokens_approximately,
    model=summarization_model,
    max_tokens=256,
    max_tokens_before_summary=256,
    max_summary_tokens=128,
)
 
def call_model(state: LLMInputState):  # [!code highlight]
    response = model.invoke(state["summarized_messages"])
    return {"messages": [response]}
 
checkpointer = InMemorySaver()
builder = StateGraph(State)
builder.add_node(call_model)
builder.add_node("summarize", summarization_node)  # [!code highlight]
builder.add_edge(START, "summarize")
builder.add_edge("summarize", "call_model")
graph = builder.compile(checkpointer=checkpointer)
 
# Invoke the graph
config = {"configurable": {"thread_id": "1"}}
graph.invoke({"messages": "hi, my name is bob"}, config)
graph.invoke({"messages": "write a short poem about cats"}, config)
graph.invoke({"messages": "now do the same but for dogs"}, config)
final_response = graph.invoke({"messages": "what's my name?"}, config)
 
final_response["messages"][-1].pretty_print()
print("\nSummary:", final_response["context"]["running_summary"].summary)
  1. We will keep track of our running summary in the context field

(expected by the SummarizationNode).

  1. Define private state that will be used only for filtering

the inputs to call_model node.

  1. We're passing a private input state here to isolate the messages returned by the summarization node
================================== Ai Message ==================================

From our conversation, I can see that you introduced yourself as Bob. That's the name you shared with me when we began talking.

Summary: In this conversation, I was introduced to Bob, who then asked me to write a poem about cats. I composed a poem titled "The Mystery of Cats" that captured cats' graceful movements, independent nature, and their special relationship with humans. Bob then requested a similar poem about dogs, so I wrote "The Joy of Dogs," which highlighted dogs' loyalty, enthusiasm, and loving companionship. Both poems were written in a similar style but emphasized the distinct characteristics that make each pet special.

:::

:::js

import { ChatAnthropic } from "@langchain/anthropic";
import {
  SystemMessage,
  HumanMessage,
  RemoveMessage,
} from "@langchain/core/messages";
import {
  StateGraph,
  StateSchema,
  MessagesValue,
  GraphNode,
  ConditionalEdgeRouter,
  START,
  END,
  MemorySaver,
} from "@langchain/langgraph";
import * as z from "zod";
 
const memory = new MemorySaver();
 
// We will add a `summary` attribute (in addition to `messages` key)
const GraphState = new StateSchema({
  messages: MessagesValue,
  summary: z.string().default(""),
});
 
// We will use this model for both the conversation and the summarization
const model = new ChatAnthropic({ model: "claude-haiku-4-5-20251001" });
 
// Define the logic to call the model
const callModel: GraphNode<typeof GraphState> = async (state) => {
  // If a summary exists, we add this in as a system message
  const { summary } = state;
  let { messages } = state;
  if (summary) {
    const systemMessage = new SystemMessage({
      id: uuid7(),
      content: `Summary of conversation earlier: ${summary}`,
    });
    messages = [systemMessage, ...messages];
  }
  const response = await model.invoke(messages);
  // We return an object, because this will get added to the existing state
  return { messages: [response] };
};
 
// We now define the logic for determining whether to end or summarize the conversation
const shouldContinue: ConditionalEdgeRouter<{ InputSchema: typeof GraphState; Nodes: "summarize_conversation" }> = (state) => {
  const messages = state.messages;
  // If there are more than six messages, then we summarize the conversation
  if (messages.length > 6) {
    return "summarize_conversation";
  }
  // Otherwise we can just end
  return END;
};
 
const summarizeConversation: GraphNode<typeof GraphState> = async (state) => {
  // First, we summarize the conversation
  const { summary, messages } = state;
  let summaryMessage: string;
  if (summary) {
    // If a summary already exists, we use a different system prompt
    // to summarize it than if one didn't
    summaryMessage =
      `This is summary of the conversation to date: ${summary}\n\n` +
      "Extend the summary by taking into account the new messages above:";
  } else {
    summaryMessage = "Create a summary of the conversation above:";
  }
 
  const allMessages = [
    ...messages,
    new HumanMessage({ id: uuid7(), content: summaryMessage }),
  ];
 
  const response = await model.invoke(allMessages);
 
  // We now need to delete messages that we no longer want to show up
  // I will delete all but the last two messages, but you can change this
  const deleteMessages = messages
    .slice(0, -2)
    .map((m) => new RemoveMessage({ id: m.id! }));
 
  if (typeof response.content !== "string") {
    throw new Error("Expected a string response from the model");
  }
 
  return { summary: response.content, messages: deleteMessages };
};
 
// Define a new graph
const workflow = new StateGraph(GraphState)
  // Define the conversation node and the summarize node
  .addNode("conversation", callModel)
  .addNode("summarize_conversation", summarizeConversation)
  // Set the entrypoint as conversation
  .addEdge(START, "conversation")
  // We now add a conditional edge
  .addConditionalEdges(
    // First, we define the start node. We use `conversation`.
    // This means these are the edges taken after the `conversation` node is called.
    "conversation",
    // Next, we pass in the function that will determine which node is called next.
    shouldContinue,
  )
  // We now add a normal edge from `summarize_conversation` to END.
  // This means that after `summarize_conversation` is called, we end.
  .addEdge("summarize_conversation", END);
 
// Finally, we compile it!
const app = workflow.compile({ checkpointer: memory });

:::

Manage checkpoints

You can view and delete the information stored by the checkpointer.

View thread state

Graph/Functional API

```python
config = {
    "configurable": {
        "thread_id": "1",  # [!code highlight]
        # optionally provide an ID for a specific checkpoint,
        # otherwise the latest checkpoint is shown
        # "checkpoint_id": "1f029ca3-1f5b-6704-8004-820c16b69a5a"  # [!code highlight]

    }
}
graph.get_state(config)  # [!code highlight]

    ```
    StateSnapshot(
        values={'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today?), HumanMessage(content="what's my name?"), AIMessage(content='Your name is Bob.')]}, next=(),
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1f5b-6704-8004-820c16b69a5a'}},
        metadata={
            'source': 'loop',
            'writes': {'call_model': {'messages': AIMessage(content='Your name is Bob.')}},
            'step': 4,
            'parents': {},
            'thread_id': '1'
        },
        created_at='2025-05-05T16:01:24.680462+00:00',
        parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1790-6b0a-8003-baf965b6a38f'}},
        tasks=(),
        interrupts=()
    )
    ```
    

### Checkpointer API
    ```python
    config = {
        "configurable": {
            "thread_id": "1",  # [!code highlight]
            # optionally provide an ID for a specific checkpoint,
            # otherwise the latest checkpoint is shown
            # "checkpoint_id": "1f029ca3-1f5b-6704-8004-820c16b69a5a"  # [!code highlight]

        }
    }
    checkpointer.get_tuple(config)  # [!code highlight]
```
CheckpointTuple(
    config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1f5b-6704-8004-820c16b69a5a'}},
    checkpoint={
        'v': 3,
        'ts': '2025-05-05T16:01:24.680462+00:00',
        'id': '1f029ca3-1f5b-6704-8004-820c16b69a5a',
        'channel_versions': {'__start__': '00000000000000000000000000000005.0.5290678567601859', 'messages': '00000000000000000000000000000006.0.3205149138784782', 'branch:to:call_model': '00000000000000000000000000000006.0.14611156755133758'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000004.0.5736472536395331'}, 'call_model': {'branch:to:call_model': '00000000000000000000000000000005.0.1410174088651449'}},
        'channel_values': {'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today?), HumanMessage(content="what's my name?"), AIMessage(content='Your name is Bob.')]},
    },
    metadata={
        'source': 'loop',
        'writes': {'call_model': {'messages': AIMessage(content='Your name is Bob.')}},
        'step': 4,
        'parents': {},
        'thread_id': '1'
    },
    parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1790-6b0a-8003-baf965b6a38f'}},
    pending_writes=[]
)
```

View the history of the thread

Graph/Functional API

```python
config = {
    "configurable": {
        "thread_id": "1"  # [!code highlight]
    }
}
list(graph.get_state_history(config))  # [!code highlight]

    ```
    [
        StateSnapshot(
            values={'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?'), HumanMessage(content="what's my name?"), AIMessage(content='Your name is Bob.')]},
            next=(),
            config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1f5b-6704-8004-820c16b69a5a'}},
            metadata={'source': 'loop', 'writes': {'call_model': {'messages': AIMessage(content='Your name is Bob.')}}, 'step': 4, 'parents': {}, 'thread_id': '1'},
            created_at='2025-05-05T16:01:24.680462+00:00',
            parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1790-6b0a-8003-baf965b6a38f'}},
            tasks=(),
            interrupts=()
        ),
        StateSnapshot(
            values={'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?'), HumanMessage(content="what's my name?")]},
            next=('call_model',),
            config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1790-6b0a-8003-baf965b6a38f'}},
            metadata={'source': 'loop', 'writes': None, 'step': 3, 'parents': {}, 'thread_id': '1'},
            created_at='2025-05-05T16:01:23.863421+00:00',
            parent_config={...}
            tasks=(PregelTask(id='8ab4155e-6b15-b885-9ce5-bed69a2c305c', name='call_model', path=('__pregel_pull', 'call_model'), error=None, interrupts=(), state=None, result={'messages': AIMessage(content='Your name is Bob.')}),),
            interrupts=()
        ),
        StateSnapshot(
            values={'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?')]},
            next=('__start__',),
            config={...},
            metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'role': 'user', 'content': "what's my name?"}]}}, 'step': 2, 'parents': {}, 'thread_id': '1'},
            created_at='2025-05-05T16:01:23.863173+00:00',
            parent_config={...}
            tasks=(PregelTask(id='24ba39d6-6db1-4c9b-f4c5-682aeaf38dcd', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result={'messages': [{'role': 'user', 'content': "what's my name?"}]}),),
            interrupts=()
        ),
        StateSnapshot(
            values={'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?')]},
            next=(),
            config={...},
            metadata={'source': 'loop', 'writes': {'call_model': {'messages': AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?')}}, 'step': 1, 'parents': {}, 'thread_id': '1'},
            created_at='2025-05-05T16:01:23.862295+00:00',
            parent_config={...}
            tasks=(),
            interrupts=()
        ),
        StateSnapshot(
            values={'messages': [HumanMessage(content="hi! I'm bob")]},
            next=('call_model',),
            config={...},
            metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {}, 'thread_id': '1'},
            created_at='2025-05-05T16:01:22.278960+00:00',
            parent_config={...}
            tasks=(PregelTask(id='8cbd75e0-3720-b056-04f7-71ac805140a0', name='call_model', path=('__pregel_pull', 'call_model'), error=None, interrupts=(), state=None, result={'messages': AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?')}),),
            interrupts=()
        ),
        StateSnapshot(
            values={'messages': []},
            next=('__start__',),
            config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-0870-6ce2-bfff-1f3f14c3e565'}},
            metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'role': 'user', 'content': "hi! I'm bob"}]}}, 'step': -1, 'parents': {}, 'thread_id': '1'},
            created_at='2025-05-05T16:01:22.277497+00:00',
            parent_config=None,
            tasks=(PregelTask(id='d458367b-8265-812c-18e2-33001d199ce6', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result={'messages': [{'role': 'user', 'content': "hi! I'm bob"}]}),),
            interrupts=()
        )
    ]
    ```
    

### Checkpointer API
    ```python
    config = {
        "configurable": {
            "thread_id": "1"  # [!code highlight]
        }
    }
    list(checkpointer.list(config))  # [!code highlight]
```
[
    CheckpointTuple(
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1f5b-6704-8004-820c16b69a5a'}},
        checkpoint={
            'v': 3,
            'ts': '2025-05-05T16:01:24.680462+00:00',
            'id': '1f029ca3-1f5b-6704-8004-820c16b69a5a',
            'channel_versions': {'__start__': '00000000000000000000000000000005.0.5290678567601859', 'messages': '00000000000000000000000000000006.0.3205149138784782', 'branch:to:call_model': '00000000000000000000000000000006.0.14611156755133758'},
            'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000004.0.5736472536395331'}, 'call_model': {'branch:to:call_model': '00000000000000000000000000000005.0.1410174088651449'}},
            'channel_values': {'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?'), HumanMessage(content="what's my name?"), AIMessage(content='Your name is Bob.')]},
        },
        metadata={'source': 'loop', 'writes': {'call_model': {'messages': AIMessage(content='Your name is Bob.')}}, 'step': 4, 'parents': {}, 'thread_id': '1'},
        parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1790-6b0a-8003-baf965b6a38f'}},
        pending_writes=[]
    ),
    CheckpointTuple(
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-1790-6b0a-8003-baf965b6a38f'}},
        checkpoint={
            'v': 3,
            'ts': '2025-05-05T16:01:23.863421+00:00',
            'id': '1f029ca3-1790-6b0a-8003-baf965b6a38f',
            'channel_versions': {'__start__': '00000000000000000000000000000005.0.5290678567601859', 'messages': '00000000000000000000000000000006.0.3205149138784782', 'branch:to:call_model': '00000000000000000000000000000006.0.14611156755133758'},
            'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000004.0.5736472536395331'}, 'call_model': {'branch:to:call_model': '00000000000000000000000000000005.0.1410174088651449'}},
            'channel_values': {'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?'), HumanMessage(content="what's my name?")], 'branch:to:call_model': None}
        },
        metadata={'source': 'loop', 'writes': None, 'step': 3, 'parents': {}, 'thread_id': '1'},
        parent_config={...},
        pending_writes=[('8ab4155e-6b15-b885-9ce5-bed69a2c305c', 'messages', AIMessage(content='Your name is Bob.'))]
    ),
    CheckpointTuple(
        config={...},
        checkpoint={
            'v': 3,
            'ts': '2025-05-05T16:01:23.863173+00:00',
            'id': '1f029ca3-1790-616e-8002-9e021694a0cd',
            'channel_versions': {'__start__': '00000000000000000000000000000004.0.5736472536395331', 'messages': '00000000000000000000000000000003.0.7056767754077798', 'branch:to:call_model': '00000000000000000000000000000003.0.22059023329132854'},
            'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.7040775356287469'}, 'call_model': {'branch:to:call_model': '00000000000000000000000000000002.0.9300422176788571'}},
            'channel_values': {'__start__': {'messages': [{'role': 'user', 'content': "what's my name?"}]}, 'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?')]}
        },
        metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'role': 'user', 'content': "what's my name?"}]}}, 'step': 2, 'parents': {}, 'thread_id': '1'},
        parent_config={...},
        pending_writes=[('24ba39d6-6db1-4c9b-f4c5-682aeaf38dcd', 'messages', [{'role': 'user', 'content': "what's my name?"}]), ('24ba39d6-6db1-4c9b-f4c5-682aeaf38dcd', 'branch:to:call_model', None)]
    ),
    CheckpointTuple(
        config={...},
        checkpoint={
            'v': 3,
            'ts': '2025-05-05T16:01:23.862295+00:00',
            'id': '1f029ca3-178d-6f54-8001-d7b180db0c89',
            'channel_versions': {'__start__': '00000000000000000000000000000002.0.18673090920108737', 'messages': '00000000000000000000000000000003.0.7056767754077798', 'branch:to:call_model': '00000000000000000000000000000003.0.22059023329132854'},
            'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.7040775356287469'}, 'call_model': {'branch:to:call_model': '00000000000000000000000000000002.0.9300422176788571'}},
            'channel_values': {'messages': [HumanMessage(content="hi! I'm bob"), AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?')]}
        },
        metadata={'source': 'loop', 'writes': {'call_model': {'messages': AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?')}}, 'step': 1, 'parents': {}, 'thread_id': '1'},
        parent_config={...},
        pending_writes=[]
    ),
    CheckpointTuple(
        config={...},
        checkpoint={
            'v': 3,
            'ts': '2025-05-05T16:01:22.278960+00:00',
            'id': '1f029ca3-0874-6612-8000-339f2abc83b1',
            'channel_versions': {'__start__': '00000000000000000000000000000002.0.18673090920108737', 'messages': '00000000000000000000000000000002.0.30296526818059655', 'branch:to:call_model': '00000000000000000000000000000002.0.9300422176788571'},
            'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.7040775356287469'}},
            'channel_values': {'messages': [HumanMessage(content="hi! I'm bob")], 'branch:to:call_model': None}
        },
        metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {}, 'thread_id': '1'},
        parent_config={...},
        pending_writes=[('8cbd75e0-3720-b056-04f7-71ac805140a0', 'messages', AIMessage(content='Hi Bob! How are you doing today? Is there anything I can help you with?'))]
    ),
    CheckpointTuple(
        config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1f029ca3-0870-6ce2-bfff-1f3f14c3e565'}},
        checkpoint={
            'v': 3,
            'ts': '2025-05-05T16:01:22.277497+00:00',
            'id': '1f029ca3-0870-6ce2-bfff-1f3f14c3e565',
            'channel_versions': {'__start__': '00000000000000000000000000000001.0.7040775356287469'},
            'versions_seen': {'__input__': {}},
            'channel_values': {'__start__': {'messages': [{'role': 'user', 'content': "hi! I'm bob"}]}}
        },
        metadata={'source': 'input', 'writes': {'__start__': {'messages': [{'role': 'user', 'content': "hi! I'm bob"}]}}, 'step': -1, 'parents': {}, 'thread_id': '1'},
        parent_config=None,
        pending_writes=[('d458367b-8265-812c-18e2-33001d199ce6', 'messages', [{'role': 'user', 'content': "hi! I'm bob"}]), ('d458367b-8265-812c-18e2-33001d199ce6', 'branch:to:call_model', None)]
    )
]
```

Delete all checkpoints for a thread

thread_id = "1"
checkpointer.delete_thread(thread_id)

Database management

If you are using any database-backed persistence implementation (such as Postgres, Redis, or Oracle) to store short and/or long-term memory, you will need to run migrations to set up the required schema before you can use it with your database.

By convention, most database-specific libraries define a setup() method on the checkpointer or store instance that runs the required migrations. However, you should check with your specific implementation of @[BaseCheckpointSaver] or @[BaseStore] to confirm the exact method name and usage.

We recommend running migrations as a dedicated deployment step, or you can ensure they're run as part of server startup.