Open-Source Wikis

/

LangChain

/

Primitives

/

Messages and content blocks

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:

  1. Message typesHumanMessage, AIMessage, SystemMessage, ToolMessage, FunctionMessage, ChatMessage, plus their *Chunk streaming counterparts.
  2. Content blocks — typed pieces of message content introduced in v1: TextContentBlock, ImageContentBlock, AudioContentBlock, VideoContentBlock, FileContentBlock, ReasoningContentBlock, ToolCall, ServerToolCall, ServerToolResult, Citation, Annotation, and a NonStandardContentBlock escape 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| User

A typical loop inside an agent:

  1. The agent state has messages: [SystemMessage, HumanMessage, ...].
  2. The model produces AIMessage(content=..., tool_calls=[ToolCall(name="search", args={...}, id="...")]).
  3. The agent runs each ToolCall and appends a ToolMessage(tool_call_id=..., content=...) per result.
  4. The model is called again with the updated message list. It either issues more tool calls or returns a final AIMessage with 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 ReasoningContentBlock blocks alongside text.
  • A model that runs server-side tools (OpenAI's web search, Anthropic's computer use) can emit ServerToolCall and ServerToolResult blocks.
  • A vision response can interleave TextContentBlock and ImageContentBlock blocks.
  • Citations are first-class via Citation blocks.

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=True

Tool 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.py modules.
  • The agent state in libs/langchain_v1/langchain/agents/middleware/types.py keeps messages: Annotated[list[AnyMessage], add_messages] so langgraph's reducer accumulates them.
  • The summarization middleware uses trim_messages and produces a SystemMessage with 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 in block_translators/, and update messages/__init__.py's __all__ and _dynamic_imports. Every partner needs a translator update.
  • To add a new helper like trim_messages, place it in libs/core/langchain_core/messages/utils.py and re-export.
  • To support a new provider's tool-result shape, extend ToolMessage minimally; prefer a content block instead.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Messages and content blocks – LangChain wiki | Factory