How to add thread-level persistence (functional API)¶
Prerequisites
This guide assumes familiarity with the following:
Many AI applications need memory to share context across multiple interactions on the same thread (e.g., multiple turns of a conversation). In LangGraph functional API, this kind of memory can be added to any entrypoint() workflow using thread-level persistence.
When creating a LangGraph workflow, you can set it up to persist its results by using a checkpointer:
-
Create an instance of a checkpointer:
-
Pass
checkpointer
instance to theentrypoint()
wrapper function: -
Retrieve
previous
state from the prior execution within the workflow: -
Optionally choose which values will be returned from the workflow and which will be saved by the checkpointer as
previous
:import { entrypoint, getPreviousState } from "@langchain/langgraph"; const workflow = entrypoint({ name: "workflow", checkpointer, }, async (inputs) => { const previous = getPreviousState(); const result = doSomething(previous, inputs); ... return entrypoint.final({ value: result, save: combineState(inputs, result), }); });
This guide shows how you can add thread-level persistence to your workflow.
Note
If you need memory that is shared across multiple conversations or users (cross-thread persistence), check out this how-to guide.
Note
If you need to add thread-level persistence to a StateGraph
, check out this how-to guide.
Setup¶
Note
This guide requires @langchain/langgraph>=0.2.42
.
First, install the required dependencies for this example:
Next, we need to set API keys for Anthropic (the LLM we will use):
Set up LangSmith for LangGraph development
Sign up for LangSmith to quickly spot issues and improve the performance of your LangGraph projects. LangSmith lets you use trace data to debug, test, and monitor your LLM apps built with LangGraph — read more about how to get started here
Example: simple chatbot with short-term memory¶
We will be using a workflow with a single task that calls a chat model.
Let's first define the model we'll be using:
import { ChatAnthropic } from "@langchain/anthropic";
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-latest",
});
Now we can define our task and workflow. To add in persistence, we need to pass in a Checkpointer to the entrypoint() wrapper function.
import type { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
import {
addMessages,
entrypoint,
task,
getPreviousState,
MemorySaver,
} from "@langchain/langgraph";
const callModel = task("callModel", async (messages: BaseMessageLike[]) => {
const response = model.invoke(messages);
return response;
});
const checkpointer = new MemorySaver();
const workflow = entrypoint({
name: "workflow",
checkpointer,
}, async (inputs: BaseMessageLike[]) => {
const previous = getPreviousState<BaseMessage>() ?? [];
const messages = addMessages(previous, inputs);
const response = await callModel(messages);
return entrypoint.final({
value: response,
save: addMessages(messages, response),
});
});
If we try to use this workflow, the context of the conversation will be persisted across interactions.
Note
If you're using LangGraph Cloud or LangGraph Studio, you don't need to pass checkpointer to the entrypoint
wrapper, since it's done automatically.
Here's how this works in practice:
const config = {
configurable: { thread_id: "1" },
streamMode: "values" as const,
};
const inputMessage = { role: "user", content: "hi! I'm bob" };
const stream = await workflow.stream(
[inputMessage],
config,
);
for await (const chunk of stream) {
console.log("=".repeat(30), `${chunk.getType()} message`, "=".repeat(30));
console.log(chunk.content);
}
============================== ai message ==============================
Hi Bob! I'm Claude. Nice to meet you! How can I help you today?
const followupStream = await workflow.stream(
[{ role: "user", content: "what's my name?" }],
config,
);
for await (const chunk of followupStream) {
console.log("=".repeat(30), `${chunk.getType()} message`, "=".repeat(30));
console.log(chunk.content);
}
============================== ai message ==============================
Your name is Bob - you just told me that in your first message.
thread_id
. Poof! All the memories are gone!
const newStream = await workflow.stream(
[{ role: "user", content: "what's my name?" }],
{
configurable: {
thread_id: "2",
},
streamMode: "values",
},
);
for await (const chunk of newStream) {
console.log("=".repeat(30), `${chunk.getType()} message`, "=".repeat(30));
console.log(chunk.content);
}
============================== ai message ==============================
I don't know your name as we just started chatting. Would you like to introduce yourself?
Streaming tokens
If you would like to stream LLM tokens from your chatbot, you can use streamMode: "messages"
. Check out this how-to guide to learn more.