Chat History & MemoryLesson 3

RunnableWithMessageHistory (In-Memory)

The last lesson called format_messages, invoked the model, and appended to history on every turn. RunnableWithMessageHistory wraps prompt | model and does that load/save work inside invoke. Pass a session_id in config. Here the store is a Python dict — one InMemoryChatMessageHistory per id.

Plain loop vs wrapper

Same ChatPromptTemplate as before. Only the per-turn history steps move inside the wrapper.

Previous lesson — plain loop

format_messages(history=…)
model.invoke(messages)
history.add_user_message(…)
history.add_ai_message(…)

RunnableWithMessageHistory

chain_with_history.invoke(
    {"input": question},
    config={"configurable": {
        "session_id": "demo-1"
    }},
)
# load, run, save inside invoke
One invoke loads history, runs the chain, and saves the new turn — no manual append loop.

Each invoke

session_id picks the history object. The wrapper loads it, runs the chain, then saves the new question and reply.

Steps inside one invoke (session_id=demo-1):

config

session_id → get_session_history

load

InMemoryChatMessageHistory.messages

chain

prompt | model

save

add_user_message + add_ai_message

Same session_id on the next invoke reuses the stored turns.

The store

get_session_history looks up or creates one InMemoryChatMessageHistory per session_id.

In-memory store — one history per session_id

store = {}

def get_session_history(session_id: str):
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]
A dict in RAM maps each session_id to its own InMemoryChatMessageHistory. History is lost when the process exits.
from langchain_core.chat_history import InMemoryChatMessageHistory

store = {}

def get_session_history(session_id: str):
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

Persistent backends

In-memory data is gone when the script exits. Later lessons swap get_session_history for Redis, PostgreSQL, or MongoDB — the wrapper and chain stay the same:

Each lesson swaps get_session_history — the wrapper and chain stay the same.

Wrap and invoke

Attach the wrapper to your chain, then call invoke with the question and session_id.

chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)
config = {"configurable": {"session_id": "demo-1"}}

reply = chain_with_history.invoke(
    {"input": question},
    config=config,
)

The demo script

runnable_with_message_history_demo.py runs the same two HTML <a> questions with session_id="demo-1".

runnable_with_message_history_demo.py
"""runnable_with_message_history_demo.py"""
chain = prompt | model
InMemoryChatMessageHistory per session_id
RunnableWithMessageHistory(chain, get_session_history, …)
invoke({"input": question}, config=session_id)

Download the code

runnable_with_message_history_demo.py

Two HTML questions — one session_id

Download .py
Uses InMemoryChatMessageHistory from langchain_core — no database required. Venv from Project Setup.

Run it

Activate the venv from Project Setup, then:

python runnable_with_message_history_demo.py
PowerShell — (.venv) active
(.venv) PS C:\projects\langchain-course> python runnable_with_message_history_demo.py
Human: What does the HTML <a> tag do?
AI: The <a> tag creates a hyperlink to another page or resource.
Human: What attribute opens the link in a new tab?
AI: Use target="_blank" on the <a> tag.
The second answer works because both invokes use the same session_id.

Quick reference

  • RunnableWithMessageHistory — wrap an LCEL chain; loads and saves history on each invoke.
  • InMemoryChatMessageHistory — one object per session_id in a Python dict.
  • session_id — set in config["configurable"]; keeps one session's turns separate from another.
  • input_messages_key / history_messages_key — must match the names in your prompt template.
  • Persistent backends: Redis, PostgreSQL, MongoDB.
  • Docs: Message history how-to · RunnableWithMessageHistory.

What's Next

Next: Redis Chat Message History — store sessions in Redis with optional TTL.