Message Queues and Streaming UI
Purpose and Scope
Message queues and streaming UI patterns solve a common agent-product problem: users want to keep working while an agent is still running. In the official frontend flow, a chat client can accept several submissions immediately, enqueue them for an active thread, and process them sequentially after the current run completes. That is different from a cosmetic loading indicator. The queue becomes part of the interaction contract: the composer can remain active, pending work can be displayed, and stale entries can be cancelled before they become agent runs.
LangChain core contributes the lower-level message and streaming vocabulary that makes these interfaces predictable across model providers and runtimes. A UI may be implemented in JavaScript, backed by LangGraph Agent Server, and deployed through LangSmith, while the message payloads still need stable content shapes and stream projections. In this repository, those shapes are represented by standard content blocks and per-message streaming objects. Sources: libs/core/langchain_core/messages/content.py, libs/core/langchain_core/language_models/chat_model_stream.py
Relevant Source Files
libs/core/langchain_core/messages/content.pydefines standard multimodal content blocks for LLM input and output, including text, image-style blocks, provider-specificextras, and non-standard content preservation.libs/core/langchain_core/language_models/chat_model_stream.pydefines synchronous and asynchronous per-message stream objects returned by chat model event streaming, including typed projection properties for text, reasoning, tool calls, usage, and final output.
Core Primitives
A streaming UI should treat a chat response as a sequence of structured events, not only as a string that grows over time. The content module defines the central abstraction as a content block: a typed dictionary that can represent ordered text, image, reasoning, and provider-specific data within a single message. This is important for frontend rendering because an agent response may interleave plain markdown text with tool-call metadata, multimodal references, or provider annotations. By using a provider-agnostic block format, the application can render stable UI components while adapters handle provider-specific API schemas. Sources: libs/core/langchain_core/messages/content.py
The same content-block contract also matters for markdown messages. Markdown is usually carried as text, but the surrounding message may include more than markdown. The source documentation explicitly describes messages as lists of blocks so text, images, and other content can appear in one ordered sequence. For UI developers, that means the renderer should not assume that a message body is a single string forever. A practical approach is to render text blocks as markdown, route image blocks to media components, preserve unknown blocks for debugging or custom components, and keep provider-specific metadata in extras rather than dropping it. Sources: libs/core/langchain_core/messages/content.py
Message Queues in the Frontend Flow
In the official frontend pattern, message queuing is enabled when the client submits work with an enqueue multitask strategy. While a thread already has an active run, new submissions are accepted immediately and placed behind the running request. The visible UX should distinguish three states: the currently executing run, queued submissions that have not started, and completed assistant messages. This prevents the common failure mode where users think their follow-up was ignored because the agent is still streaming the previous answer.
The queue helper described in the docs exposes pending entries, queue size, cancellation of a specific entry, and clearing all queued work. Those operations map naturally to UI controls: a queue badge, a pending-message list, a cancel button per entry, and a “clear pending” action when the user changes direction. The source files here do not implement that frontend queue helper, but they explain the message and stream units that queued work eventually produces. Once a queued submission becomes active, it should be rendered through the same streaming pipeline as any other run rather than through a separate special-case path.
Streaming Protocol and UI Projections
ChatModelStream and AsyncChatModelStream are described as the per-message streaming objects returned by chat model event streaming. They expose typed projections such as .text, .reasoning, .tool_calls, .usage, and .output, and they also allow direct iteration over raw protocol events with replay-buffer semantics. For a UI, projections are the safer default: the text projection can feed a markdown renderer, the reasoning projection can feed an expandable reasoning panel where appropriate, tool-call projection can drive tool cards, and usage projection can update token or cost indicators. Sources: libs/core/langchain_core/language_models/chat_model_stream.py
Replay-buffer semantics are especially relevant to long-running interfaces. Multiple independent consumers can observe the same stream without forcing the application to choose between rendering tokens, collecting telemetry, and updating tool-call state. One consumer can progressively append text deltas to the assistant bubble, another can watch tool calls to show external actions, and a third can accumulate final output for persistence. This separation is close to the official event-streaming recommendation for modern agent UIs: consume typed projections independently instead of branching all UI behavior on a single stream chunk shape. Sources: libs/core/langchain_core/language_models/chat_model_stream.py
Implementation Details and Reference
The stream implementation includes helpers for merging tool-call chunks, block deltas, and legacy block shapes. Tool-call chunks preserve sticky identifiers and names while concatenating argument deltas, which is the behavior a UI needs when displaying a tool call before the full JSON argument payload has arrived. Block-delta handling also tolerates older field names and converts legacy text, reasoning, or data blocks into explicit delta forms. The practical implication is that UI code should be prepared for incremental updates and should avoid treating partially streamed tool calls as final until the stream projection finalizes them. Sources: libs/core/langchain_core/language_models/chat_model_stream.py
| Primitive | Source-level contract | UI use |
|---|---|---|
| Content block | Typed provider-agnostic message unit | Render markdown text, images, annotations, and custom blocks in order |
extras | Provider-specific metadata inside standard blocks | Preserve model annotations without breaking the renderer |
NonStandardContentBlock | Escape hatch for unmapped provider data | Keep unknown payloads available for custom UI or logging |
ChatModelStream | Synchronous per-message event stream | Drive server-rendered or blocking streaming flows |
AsyncChatModelStream | Asynchronous per-message event stream | Drive async web servers, websocket handlers, or background stream controllers |
.text | Accumulated text projection | Feed a markdown message component incrementally |
.tool_calls | Accumulated tool-call projection | Show pending, running, and completed tool cards |
.usage | Usage projection | Update usage displays after or during generation |
Execution Flow
A robust long-running agent UI can be organized as a small pipeline. First, accept user input immediately and submit it with queueing semantics when another run is active. Second, display the submission in the pending queue until it starts. Third, when the run becomes active, create or subscribe to the stream controller and render typed projections rather than parsing unstructured text. Fourth, update markdown text from text deltas, tool cards from tool-call deltas, and status indicators from usage or final-output projections. Finally, when the stream completes, persist the finalized message content and dispatch the next queued submission.
This flow keeps queue management separate from message rendering while still using the same content protocol for every completed or in-progress run. Queue entries are about scheduling user submissions on a thread; content blocks are about representing what a model or agent says; stream projections are about observing how that answer arrives. Keeping those layers separate makes cancellation safer, because cancelling a queued entry does not require rewriting the active assistant stream, and changing the renderer does not require changing queue policy.
Next Steps
Use this page as the bridge between frontend queue behavior and LangChain core streaming semantics. If you are building the client, start with the official message-queue pattern and make pending entries visible in the chat UI. If you are implementing the backend stream path, align emitted messages with content blocks and expose typed stream projections so clients can render markdown, tools, reasoning, and usage independently. For adjacent topics, continue to event streaming, messages and content APIs, and frontend overview pages.