Who this is for and what you will be able to do
This guide is for developers building agent applications who want users to watch a model write its answer as it goes. After reading it, you can choose stream modes for progress, token-level output and custom updates, combine several modes in one stream, read reasoning tokens, and know when to turn streaming off for a model.
What streaming gives you
Streaming surfaces live feedback from agent runs to your application. LLM responses can take a while, and displaying output progressively improves the user experience even before the full response is ready.[1]
LangChain has three basic stream modes, set with stream_mode. updates emits state updates after each agent step, and multiple updates in the same step are emitted separately. messages emits tuples of a token and its metadata from any node where an LLM is invoked. custom emits data your nodes write with the stream writer.[1]
For new applications, the page recommends event streaming. It was introduced in LangChain v1.3 and gives separate iterators for messages, values, tool calls and subgraphs, so you do not have to branch on stream_mode chunks.[1]
Before you start
- Versions: the v2 format needs LangGraph 1.1 or later.[1]
- Persistence: to keep multi-turn history with a
thread_id, the agent needs a checkpointer. Locally, pass one explicitly, such asInMemorySaver.[1] - Languages: Python and JavaScript versions are both given. The mode names match, but the syntax differs.[1]
Step one: watch agent progress
- Call
streamorastreamwithstream_mode="updates". The agent emits an event after every step.[1] - In a run where the agent calls a tool once, you see the LLM node's tool call request, then the tool node's
ToolMessagewith the result, then the LLM node's final response.[1] - For multi-turn conversations, pass a
thread_idinconfigso each request continues the same history.[1]
Token by token: stream_mode="messages"
To show the answer as it is generated, switch to messages. The code below prints the node name and content of each chunk:
from langchain.agents import create_agent
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
agent = create_agent(
model="gpt-5-nano",
tools=[get_weather],
)
for chunk in agent.stream( # [!code highlight]
{"messages": [{"role": "user", "content": "What is the weather in SF?"}]},
stream_mode="messages",
version="v2", # [!code highlight]
):
if chunk["type"] == "messages": # [!code highlight]
token, metadata = chunk["data"] # [!code highlight]
print(f"node: {metadata['langgraph_node']}")
print(f"content: {token.content_blocks}")
print("\n")In the output, tool call arguments arrive as tool_call_chunk entries in fragments, and the final answer also comes in small pieces after the tool node runs.[1] The chunks are incremental, not complete messages.
Custom progress from tools: stream_mode="custom"
A slow tool can report its progress with get_stream_writer, imported from langgraph.config. The data it writes is delivered through the custom mode.[1]
from langchain.agents import create_agent
from langgraph.config import get_stream_writer # [!code highlight]
def get_weather(city: str) -> str:
"""Get weather for a given city."""
writer = get_stream_writer() # [!code highlight]
# stream any arbitrary data
writer(f"Looking up data for city: {city}")
writer(f"Acquired data for city: {city}")
return f"It's always sunny in {city}!"
agent = create_agent(
model="claude-sonnet-4-6",
tools=[get_weather],
)
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "What is the weather in SF?"}]},
stream_mode="custom", # [!code highlight]
version="v2", # [!code highlight]
):
if chunk["type"] == "custom": # [!code highlight]
print(chunk["data"]) # [!code highlight]A tool that uses get_stream_writer cannot be invoked outside a LangGraph execution context.[1]
Several modes in one stream
Pass a list, such as stream_mode=["updates", "custom"]. Each chunk is then a StreamPart dict with type, ns and data keys, where type names the mode.[1] The example below uses the v2 format to receive state updates and custom progress together:
from langchain.agents import create_agent
from langgraph.config import get_stream_writer
def get_weather(city: str) -> str:
"""Get weather for a given city."""
writer = get_stream_writer()
writer(f"Looking up data for city: {city}")
writer(f"Acquired data for city: {city}")
return f"It's always sunny in {city}!"
agent = create_agent(
model="gpt-5-nano",
tools=[get_weather],
)
for chunk in agent.stream( # [!code highlight]
{"messages": [{"role": "user", "content": "What is the weather in SF?"}]},
stream_mode=["updates", "custom"],
version="v2", # [!code highlight]
):
print(f"stream_mode: {chunk['type']}") # [!code highlight]
print(f"content: {chunk['data']}") # [!code highlight]
print("\n")In JavaScript, pass streamMode as an array. Each output is a [mode, chunk] tuple.[1]
Common patterns
Reasoning tokens. Some models reason before answering. To stream that part, use stream_mode="messages" and filter for content blocks whose type is "reasoning". Reasoning output must be enabled on the model.[1] Provider formats differ, such as Anthropic thinking blocks and OpenAI reasoning summaries, but LangChain normalizes them into the standard "reasoning" block through content_blocks.[1]
Tool calls. To see a tool call's JSON as it forms and also get the parsed call once it runs, combine modes. messages yields incremental chunks from every LLM call. If completed messages are tracked in state, add updates to read them there.[1]
Human approval. For interrupts during streaming, configure the agent with human-in-the-loop middleware and a checkpointer, collect interrupts in the "updates" mode, and respond with a command.[1]
Multiple agents. Pass a name to each agent. In "messages" mode the name appears in metadata under lc_agent_name. Subagent output also requires subgraphs=True when creating the stream.[1]
Turning streaming off for one model
Sometimes a model's output should not be streamed token by token, for example when only some agents in a multi-agent system should speak to the user, or when a LangSmith deployment should keep certain outputs from reaching the client. Set streaming=False when you initialize the model. If the integration does not accept that parameter, use disable_streaming=True, which all chat models support through the base class.[1]
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="gpt-6.1-sol",
streaming=False # [!code highlight]
)When deploying to LangSmith, set this in graph code before deployment.[1]
Agents as subgraphs
create_agent returns a compiled graph. Used as a node in a parent StateGraph, it becomes a subgraph. The parent's stream_mode="messages" then does not emit the inner agent's LLM tokens unless you pass subgraphs=True.[1]
Limits and common questions
- Without a
thread_idand a checkpointer, multi-turn history is not saved. The examples omitthread_idfor brevity; pass it in production.[1] - A tool that uses
get_stream_writerneeds the LangGraph execution context.[1] - With the v2 format,
invoke()returns aGraphOutput: state is in.value, and interrupts are in.interrupts, an empty tuple when there are none.[1] - Reasoning tokens appear only when the model has reasoning output enabled.[1]
Next steps
For the full set of options, including values, debug and subgraph streaming, read the LangGraph streaming documentation. For React front ends, see the frontend streaming section.[1]
Adapted from the LangChain documentation page *Streaming*[1], released under the MIT License.
Sources
- 1StreamingLangChain docs (MIT)
Cite this page: Streaming in LangChain agents. TellWorld, https://www.tellworld.net/practice/practice-langchain-streaming. Updated 2026-11-14