Implementation:Langchain ai Langgraph UI Messages
| Attribute | Value |
|---|---|
| Source | `libs/langgraph/langgraph/graph/ui.py` (227 lines) |
| Domain | UI, Graph |
| Principle | UI_Component_Management |
| Library | langgraph |
| Import | `from langgraph.graph.ui import push_ui_message, delete_ui_message, ui_message_reducer` |
Overview
The `ui.py` module provides the UI message system for LangGraph, enabling graph nodes to push, update, merge, and delete UI component messages during execution. It defines two `TypedDict` message types (`UIMessage` and `RemoveUIMessage`), two imperative functions for sending messages (`push_ui_message` and `delete_ui_message`), and a reducer function (`ui_message_reducer`) for merging UI message lists in graph state.
Description
This module implements a lightweight protocol for managing UI component state within LangGraph executions. The system works in two ways:
Imperative API -- `push_ui_message` and `delete_ui_message` are called from within graph nodes to immediately stream UI events to clients and simultaneously update graph state.
Reducer-based API -- `ui_message_reducer` is designed to be used as an `Annotated` reducer on a state key (typically `"ui"`), merging incoming UI messages with existing ones while handling removals and property merging.
TypedDicts
`UIMessage` -- Represents a UI component to render:
- `type` -- Always `"ui"`.
- `id` -- Unique identifier for the message.
- `name` -- Name of the UI component to render.
- `props` -- Dictionary of properties passed to the UI component.
- `metadata` -- Additional metadata (merge flag, run_id, tags, name, message_id).
`RemoveUIMessage` -- Represents a request to remove a UI component:
- `type` -- Always `"remove-ui"`.
- `id` -- Identifier of the UI message to remove.
`AnyUIMessage` -- Union type: `UIMessage | RemoveUIMessage`.
Functions
`push_ui_message(name, props, ...)` -- Creates a `UIMessage`, streams it via the stream writer, and optionally writes it to the graph state channel. Supports optional `id`, `metadata`, `message` association, `state_key` configuration, and `merge` mode for incremental property updates.
`delete_ui_message(id, ...)` -- Creates a `RemoveUIMessage`, streams it, and writes it to the graph state channel to remove the specified UI component.
`ui_message_reducer(left, right)` -- Merges two lists of UI messages. It handles:
- Adding new messages (appends to the list).
- Updating existing messages by ID (replaces in place).
- Merging props when the `merge` metadata flag is `True`.
- Removing messages when a `RemoveUIMessage` with a matching ID is encountered.
- Raising `ValueError` if attempting to remove a message with an ID that does not exist.
Usage
from langgraph.graph.ui import push_ui_message, delete_ui_message, ui_message_reducer
from typing import Annotated, TypedDict
class State(TypedDict):
ui: Annotated[list, ui_message_reducer]
def my_node(state: State) -> State:
push_ui_message("ChatBubble", {"content": "Processing..."})
return state
Code Reference
UIMessage TypedDict
| Field | Type | Description |
|---|---|---|
| `type` | `Literal["ui"]` | Discriminator for UI messages. |
| `id` | `str` | Unique identifier for the message. |
| `name` | `str` | Name of the UI component to render. |
| `props` | `dict[str, Any]` | Properties to pass to the UI component. |
| `metadata` | `dict[str, Any]` | Additional metadata (merge flag, run_id, tags, etc.). |
RemoveUIMessage TypedDict
| Field | Type | Description |
|---|---|---|
| `type` | `Literal["remove-ui"]` | Discriminator for removal messages. |
| `id` | `str` | ID of the UI message to remove. |
push_ui_message
| Parameter | Type | Default | Description |
|---|---|---|---|
| `name` | `str` | required | Name of the UI component. |
| `props` | `dict[str, Any]` | required | Properties for the UI component. |
| `id` | None` | `None` | Optional ID; auto-generated UUID if omitted. |
| `metadata` | None` | `None` | Additional metadata to include. |
| `message` | None` | `None` | Optional LangChain message to associate. |
| `state_key` | None` | `"ui"` | State key to write the message to. Set to `None` to skip state update. |
| `merge` | `bool` | `False` | If `True`, merge props with existing message of same ID. |
delete_ui_message
| Parameter | Type | Default | Description |
|---|---|---|---|
| `id` | `str` | required | ID of the UI message to remove. |
| `state_key` | `str` | `"ui"` | State key where UI messages are stored. |
ui_message_reducer
| Parameter | Type | Description |
|---|---|---|
| `left` | AnyUIMessage` | Existing UI messages (or a single message). |
| `right` | AnyUIMessage` | New UI messages to merge (or a single message). |
| Returns | `list[AnyUIMessage]` | Combined list with updates and removals applied. |
I/O Contract
| Aspect | Detail |
|---|---|
| Input | `push_ui_message`: component name and props. `delete_ui_message`: message ID. `ui_message_reducer`: two lists of UI messages. |
| Output | `push_ui_message` returns a `UIMessage` dict. `delete_ui_message` returns a `RemoveUIMessage` dict. `ui_message_reducer` returns a merged `list[AnyUIMessage]`. |
| Side Effects | `push_ui_message` and `delete_ui_message` invoke the stream writer and send events to the graph state channel via `CONFIG_KEY_SEND`. |
| Errors | `ui_message_reducer` raises `ValueError` if a `RemoveUIMessage` references a non-existent ID. |
Usage Examples
Pushing a UI Component
from langgraph.graph.ui import push_ui_message
def chatbot_node(state):
msg = push_ui_message(
name="ChatBubble",
props={"content": "Thinking..."},
)
# msg["id"] is a UUID string
return state
Updating a UI Component with Merge
from langgraph.graph.ui import push_ui_message
def update_node(state):
# First push creates the component
msg = push_ui_message("ProgressBar", {"progress": 0}, id="progress-1")
# Later push merges props into the existing component
push_ui_message("ProgressBar", {"progress": 50}, id="progress-1", merge=True)
return state
Deleting a UI Component
from langgraph.graph.ui import delete_ui_message
def cleanup_node(state):
delete_ui_message("progress-1")
return state
Using the Reducer in State
from typing import Annotated, TypedDict
from langgraph.graph.ui import ui_message_reducer
class State(TypedDict):
ui: Annotated[list, ui_message_reducer]
messages: list
Related Pages
- Langchain_ai_Langgraph_Public_Constants -- Constants like `CONF` used internally by the UI message functions.
- Langchain_ai_Langgraph_PregelProtocol -- The graph execution protocol that streams UI messages.
- Langchain_ai_Langgraph_Runtime_Class -- Runtime context that provides the stream writer.