diff --git a/docs/core_docs/docs/expression_language/how_to/with_history.mdx b/docs/core_docs/docs/expression_language/how_to/with_history.mdx new file mode 100644 index 000000000000..798c67e7587d --- /dev/null +++ b/docs/core_docs/docs/expression_language/how_to/with_history.mdx @@ -0,0 +1,53 @@ +import CodeBlock from "@theme/CodeBlock"; +import Example from "@examples/guides/expression_language/runnable_history.ts"; + +# Add message history (memory) + +The `RunnableWithMessageHistory` let's us add message history to certain types of chains. + +Specifically, it can be used for any Runnable that takes as input one of + +- a list of `BaseMessage` +- an object with a key that takes a list of `BaseMessage` +- an object with a key that takes the latest message(s) as a string or list of `BaseMessage`, and a separate key that takes historical messages + +And returns as output one of + +- a string that can be treated as the contents of an `AIMessage` +- a list of `BaseMessage` +- an object with a key that contains a list of `BaseMessage` + +Let's take a look at some examples to see how it works. + +<CodeBlock language="typescript">{Example}</CodeBlock> + +## Pass config through the constructor + +You don't always have to pass the `config` object through the `invoke` method. `RunnableWithMessageHistory` supports passing it through the constructor as well. + +To do this, the only change you need to make is remove the second arg (or just the `configurable` key from the second arg) from the `invoke` method, and add it in through the `config` key in the constructor. + +This is a simple example building on top of what we have above: + +```typescript +const config: RunnableConfig = { configurable: { sessionId: "1" } }; + +const withHistory = new RunnableWithMessageHistory({ + runnable, + getMessageHistory: (_sessionId: string) => messageHistory, + inputMessagesKey: "input", + historyMessagesKey: "history", + // Passing config through here instead of through the invoke method + config, +}); + +let output = await withHistory.invoke({ input: "Hello there, I'm Archibald!" }); +console.log("output 1:", output); +/** +output 1: AIMessage { + lc_namespace: [ 'langchain_core', 'messages' ], + content: 'Hello, Archibald! How can I assist you today?', + additional_kwargs: { function_call: undefined, tool_calls: undefined } +} + */ +``` diff --git a/examples/src/guides/expression_language/runnable_history.ts b/examples/src/guides/expression_language/runnable_history.ts new file mode 100644 index 000000000000..afcd7aade6c2 --- /dev/null +++ b/examples/src/guides/expression_language/runnable_history.ts @@ -0,0 +1,69 @@ +import { ChatOpenAI } from "langchain/chat_models/openai"; +import { ChatMessageHistory } from "langchain/stores/message/in_memory"; +import { ChatPromptTemplate, MessagesPlaceholder } from "langchain/prompts"; +import { + RunnableConfig, + RunnableWithMessageHistory, +} from "langchain/runnables"; + +// Instantiate your model and prompt. +const model = new ChatOpenAI({}); +const prompt = ChatPromptTemplate.fromMessages([ + ["ai", "You are a helpful assistant"], + new MessagesPlaceholder("history"), + ["human", "{input}"], +]); + +// Create a simple runnable which just chains the prompt to the model. +const runnable = prompt.pipe(model); + +// Define your session history store. +// This is where you will store your chat history. +const messageHistory = new ChatMessageHistory(); + +// Create your `RunnableWithMessageHistory` object, passing in the +// runnable created above. +const withHistory = new RunnableWithMessageHistory({ + runnable, + // Optionally, you can use a function which tracks history by session ID. + getMessageHistory: (_sessionId: string) => messageHistory, + inputMessagesKey: "input", + // This shows the runnable where to insert the history. + // We set to "history" here because of our MessagesPlaceholder above. + historyMessagesKey: "history", +}); + +// Create your `configurable` object. This is where you pass in the +// `sessionId` which is used to identify chat sessions in your message store. +const config: RunnableConfig = { configurable: { sessionId: "1" } }; + +// Pass in your question, in this example we set the input key +// to be "input" so we need to pass an object with an "input" key. +let output = await withHistory.invoke( + { input: "Hello there, I'm Archibald!" }, + config +); +console.log("output 1:", output); +/** +output 1: AIMessage { + lc_namespace: [ 'langchain_core', 'messages' ], + content: 'Hello, Archibald! How can I assist you today?', + additional_kwargs: { function_call: undefined, tool_calls: undefined } +} + */ + +output = await withHistory.invoke({ input: "What's my name?" }, config); +console.log("output 2:", output); +/** +output 2: AIMessage { + lc_namespace: [ 'langchain_core', 'messages' ], + content: 'Your name is Archibald, as you mentioned earlier. Is there anything specific you would like assistance with, Archibald?', + additional_kwargs: { function_call: undefined, tool_calls: undefined } +} + */ + +/** + * You can see the LangSmith traces here: + * output 1 @link https://smith.langchain.com/public/686f061e-bef4-4b0d-a4fa-04c107b6db98/r + * output 2 @link https://smith.langchain.com/public/c30ba77b-c2f4-440d-a54b-f368ced6467a/r + */ diff --git a/langchain-core/src/runnables/history.ts b/langchain-core/src/runnables/history.ts index fbf12f89c551..29e65802eb79 100644 --- a/langchain-core/src/runnables/history.ts +++ b/langchain-core/src/runnables/history.ts @@ -22,7 +22,10 @@ import { RunnablePassthrough } from "./passthrough.js"; type GetSessionHistoryCallable = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any ...args: Array<any> -) => Promise<BaseChatMessageHistory | BaseListChatMessageHistory>; +) => + | Promise<BaseChatMessageHistory | BaseListChatMessageHistory> + | BaseChatMessageHistory + | BaseListChatMessageHistory; export interface RunnableWithMessageHistoryInputs<RunInput, RunOutput> extends Omit<