langchain-ai/langchain
Messages and content blocks
The conversation type system. Lives at libs/core/langchain_core/messages/.
Purpose
Every chat model in LangChain takes a list of messages and returns a message. Tools return messages. Agents accumulate messages in their state. The messages/ module defines what those messages look like.
There are two layers:
- Message types —
HumanMessage,AIMessage,SystemMessage,ToolMessage,FunctionMessage,ChatMessage, plus their*Chunkstreaming counterparts. - Content blocks — typed pieces of message content introduced in v1:
TextContentBlock,ImageContentBlock,AudioContentBlock,VideoContentBlock,FileContentBlock,ReasoningContentBlock,ToolCall,ServerToolCall,ServerToolResult,Citation,Annotation, and aNonStandardContentBlockescape hatch.
Directory layout
libs/core/langchain_core/messages/
├── __init__.py # Public re-exports
├── ai.py # AIMessage, AIMessageChunk, UsageMetadata, InputTokenDetails, OutputTokenDetails
├── base.py # BaseMessage, BaseMessageChunk
├── block_translators/ # Cross-format converters (e.g. OpenAI <-> v1 blocks)
├── chat.py # ChatMessage (for non-standard roles)
├── content.py # All content block typed dicts (~1,100 lines)
├── function.py # FunctionMessage (legacy OpenAI functions)
├── human.py # HumanMessage
├── modifier.py # RemoveMessage (state-graph utility)
├── system.py # SystemMessage
├── tool.py # ToolMessage, ToolCall, ToolCallChunk, InvalidToolCall
└── utils.py # convert_to_messages, convert_to_openai_messages, filter_messages,
# trim_messages, merge_message_runs, get_buffer_string (~2,200 lines)Key abstractions
| Symbol | File | Description |
|---|---|---|
BaseMessage |
libs/core/langchain_core/messages/base.py |
Common base; carries content, additional_kwargs, response_metadata, id, name |
BaseMessageChunk |
libs/core/langchain_core/messages/base.py |
Streaming counterpart, supports + for accumulation |
HumanMessage, SystemMessage, AIMessage, ToolMessage, FunctionMessage, ChatMessage |
libs/core/langchain_core/messages/{human,system,ai,tool,function,chat}.py |
The concrete message types |
AIMessageChunk |
libs/core/langchain_core/messages/ai.py |
Streaming AI message; merges into a complete AIMessage via the + operator |
UsageMetadata, InputTokenDetails, OutputTokenDetails |
libs/core/langchain_core/messages/ai.py |
Token usage breakdown |
ToolCall, ToolCallChunk, InvalidToolCall |
libs/core/langchain_core/messages/tool.py |
Tool-call requests inside an AI message |
RemoveMessage |
libs/core/langchain_core/messages/modifier.py |
Sentinel used by langgraph state reducers to remove a message by id |
TextContentBlock, ImageContentBlock, AudioContentBlock, VideoContentBlock, FileContentBlock, ReasoningContentBlock, ServerToolCall, ServerToolCallChunk, ServerToolResult, Citation, Annotation, NonStandardContentBlock, NonStandardAnnotation, PlainTextContentBlock |
libs/core/langchain_core/messages/content.py |
The v1 content-block taxonomy |
convert_to_messages, convert_to_openai_messages, filter_messages, trim_messages, merge_message_runs |
libs/core/langchain_core/messages/utils.py |
Conversion and shaping helpers |
How messages flow
graph LR
User[User]
Prompt[ChatPromptTemplate]
Model[BaseChatModel]
Tools[BaseTool]
User -->|HumanMessage| Prompt
Prompt -->|list of messages| Model
Model -->|AIMessage with ToolCalls| Tools
Tools -->|ToolMessage| Model
Model -->|final AIMessage| UserA typical loop inside an agent:
- The agent state has
messages: [SystemMessage, HumanMessage, ...]. - The model produces
AIMessage(content=..., tool_calls=[ToolCall(name="search", args={...}, id="...")]). - The agent runs each
ToolCalland appends aToolMessage(tool_call_id=..., content=...)per result. - The model is called again with the updated message list. It either issues more tool calls or returns a final
AIMessagewith no tool calls.
Content blocks (v1)
Before v1, AIMessage.content was a str or a list of OpenAI-style dicts. v1 introduces a typed taxonomy so that:
- A model that returns reasoning (e.g. Claude extended thinking, OpenAI o-series, DeepSeek R1) can emit
ReasoningContentBlockblocks alongside text. - A model that runs server-side tools (OpenAI's web search, Anthropic's computer use) can emit
ServerToolCallandServerToolResultblocks. - A vision response can interleave
TextContentBlockandImageContentBlockblocks. - Citations are first-class via
Citationblocks.
Each block is a TypedDict with a "type" discriminator. For example:
TextContentBlock = TypedDict("TextContentBlock", {"type": Literal["text"], "text": str, "id": NotRequired[str]})
ImageContentBlock = TypedDict(
"ImageContentBlock",
{"type": Literal["image"], "source": dict, "id": NotRequired[str]}
)The full taxonomy is in libs/core/langchain_core/messages/content.py.
block_translators/ contains converters between the v1 taxonomy and provider-specific shapes (notably OpenAI). block_translators/openai.py exposes convert_to_openai_data_block and convert_to_openai_image_block used by the OpenAI partner package.
Streaming and chunks
Every concrete message type has a *Chunk counterpart. AIMessageChunk + AIMessageChunk returns a merged chunk; AIMessageChunk + AIMessage returns a complete message. This lets streaming code accumulate without special-casing the final boundary.
acc: AIMessageChunk | None = None
async for chunk in model.astream(messages):
acc = chunk if acc is None else acc + chunk
print(acc.content)
print(acc.tool_calls) # Available after merging
print(acc.usage_metadata) # Accumulated when stream_usage=TrueTool calls have their own chunk shape. Partial tool-call deltas arrive as ToolCallChunk (with a partial args JSON string) and merge into a complete ToolCall once the model finishes.
Utility helpers
convert_to_messages([{"role": "user", "content": "hi"}, ...])— accept loose dict input from users.convert_to_openai_messages(messages)— back-translate to OpenAI-shape dicts for non-LangChain consumers.filter_messages(messages, include_types=..., exclude_types=..., include_names=..., ...)— keep/drop by type or name.trim_messages(messages, max_tokens=..., token_counter=..., strategy="last")— fit within a token budget while keeping the system message.merge_message_runs(messages)— combine consecutive same-role messages into one.
Integration points
- Every chat model implements
BaseChatModel.invoke(messages: list[AnyMessage]) -> AIMessage. - Every partner package translates between provider shape and these types in
_compat.pymodules. - The agent state in
libs/langchain_v1/langchain/agents/middleware/types.pykeepsmessages: Annotated[list[AnyMessage], add_messages]solanggraph's reducer accumulates them. - The summarization middleware uses
trim_messagesand produces aSystemMessagewith the rolling summary.
Entry points for modification
- To add a new content-block type, add the typed dict to
libs/core/langchain_core/messages/content.py, register translators inblock_translators/, and updatemessages/__init__.py's__all__and_dynamic_imports. Every partner needs a translator update. - To add a new helper like
trim_messages, place it inlibs/core/langchain_core/messages/utils.pyand re-export. - To support a new provider's tool-result shape, extend
ToolMessageminimally; prefer a content block instead.
Related
- primitives/language-models — consumer of these messages
- primitives/runnables — the calling protocol
- partners/openai, partners/anthropic — translate between v1 blocks and provider shapes
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.