Skip to main content

Messages

When to use this

Read this page to understand the message format used by graph nodes, the API, and the TypeScript client. Every piece of content — text, images, tool calls, reasoning — is represented as a typed ContentBlock inside a Message.

Import paths

from agentflow.core.state import Message
from agentflow.core.state.message_block import (
TextBlock, ImageBlock, AudioBlock, VideoBlock,
DocumentBlock, DataBlock,
ToolCallBlock, RemoteToolCallBlock, ToolResultBlock,
ReasoningBlock, AnnotationBlock, ErrorBlock,
MediaRef, AnnotationRef, ContentBlock,
)
from agentflow.core.state.message import TokenUsages

Message

The top-level message object. All conversations are list[Message].

Fields

FieldTypeDefaultDescription
message_idstr | intauto-generatedUnique message identifier. Generated via the DI-registered ID generator.
role"user" | "assistant" | "system" | "tool"requiredWho sent the message.
contentSequence[ContentBlock]requiredOrdered list of typed content blocks.
deltaboolFalseTrue for streaming intermediate chunks. Delta messages are not persisted to state.
tools_callslist[dict] | NoneNoneRaw tool call dicts from the provider SDK. Used internally.
reasoningstr | NoneNoneReasoning trace (deprecated — use ReasoningBlock in content instead).
timestampfloat | NoneautoUNIX timestamp of message creation.
metadatadict{}Arbitrary key-value metadata.
usagesTokenUsages | NoneNoneToken usage for this message's LLM call.
rawdict | NoneNoneThe raw provider API response.

Constructors

Message.text_message

msg = Message.text_message("Hello")
msg = Message.text_message("Hello", role="assistant")
msg = Message.text_message("Hello", role="user", message_id="msg-001")

Create a plain text user message. The simplest way to construct input for invoke() or ainvoke().

Direct construction

from agentflow.core.state.message_block import TextBlock

msg = Message(
role="user",
content=[TextBlock(text="Hello, world!")],
metadata={"source": "api"},
)

Content blocks

All content blocks share a type discriminator field used for type narrowing.

TextBlock

TextBlock(text="Hello, world!")
TextBlock(text="See reference [1].", annotations=[AnnotationRef(url="https://example.com", title="Source")])
FieldTypeDescription
type"text"Discriminator.
textstrThe text content.
annotationslist[AnnotationRef]Citations or links embedded in the text.

ImageBlock

ImageBlock(media=MediaRef(kind="url", url="https://example.com/photo.jpg", mime_type="image/jpeg"))
ImageBlock(media=MediaRef(kind="file_id", file_id="file-abc123", mime_type="image/png"))
ImageBlock(media=MediaRef(kind="data", data_base64="...", mime_type="image/png"), alt_text="Chart")
FieldTypeDescription
type"image"Discriminator.
mediaMediaRefReference to the image.
alt_textstr | NoneAccessibility text.
bboxlist[float] | NoneBounding box [x1, y1, x2, y2].

AudioBlock

AudioBlock(
media=MediaRef(kind="url", url="https://example.com/recording.mp3"),
transcript="Hello, this is a recording.",
)
FieldTypeDescription
type"audio"Discriminator.
mediaMediaRefReference to the audio.
transcriptstr | NoneTranscript of the audio content.
sample_rateint | NoneSample rate in Hz.
channelsint | NoneNumber of audio channels.

VideoBlock

VideoBlock(
media=MediaRef(kind="url", url="https://example.com/video.mp4"),
thumbnail=MediaRef(kind="url", url="https://example.com/thumb.jpg"),
)
FieldTypeDescription
type"video"Discriminator.
mediaMediaRefReference to the video.
thumbnailMediaRef | NoneThumbnail image reference.

DocumentBlock

DocumentBlock(
media=MediaRef(kind="file_id", file_id="file-def456", mime_type="application/pdf"),
excerpt="This report covers Q4 earnings...",
pages=[1, 2, 3],
)
FieldTypeDescription
type"document"Discriminator.
mediaMediaRefReference to the document file.
textstr | NoneExtracted full text content.
pageslist[int] | NoneRelevant page numbers.
excerptstr | NoneShort excerpt/preview.

DataBlock

DataBlock(mime_type="application/json", data_base64="eyJrZXkiOiAidmFsdWUifQ==")
FieldTypeDescription
type"data"Discriminator.
mime_typestrMIME type of the data.
data_base64str | NoneBase64-encoded payload.
mediaMediaRef | NoneExternal media reference.

ToolCallBlock

Added by the agent when the LLM requests a tool call. Application code usually does not construct these directly.

ToolCallBlock(id="call_abc", name="get_weather", args={"location": "London"})
FieldTypeDescription
type"tool_call"Discriminator.
idstrTool call ID (matches the corresponding ToolResultBlock).
namestrTool function name.
argsdictArguments dict.
tool_typestr | NoneOptional type hint e.g. "web_search", "computer_use".

RemoteToolCallBlock

Sent by the server to the TypeScript client when a tool needs to run in the browser. Do not construct manually.

FieldTypeDescription
type"remote_tool_call"Discriminator.
idstrCall ID.
namestrTool name registered via client.registerTool().
argsdictArguments.
tool_typestrAlways "remote".

ToolResultBlock

The result of a tool call execution. Constructed by ToolNode and returned to the LLM.

ToolResultBlock(tool_call_id="call_abc", content="Sunny, 25°C", is_error=False)
FieldTypeDescription
type"tool_result"Discriminator.
tool_call_idstrMatches the ToolCallBlock.id this is a response to.
contentstr | list | dictThe result returned by the tool.
is_errorboolTrue if the tool execution raised an exception.

ReasoningBlock

Returned by reasoning models (o1, o3, Gemini thinking) to expose the model's thinking trace.

FieldTypeDescription
type"reasoning"Discriminator.
thinkingstrThe reasoning/thinking text.
signaturestr | NoneProvider-specific signature for verifying thinking.

AnnotationBlock

Structured annotation/citation attached to a text response.

FieldTypeDescription
type"annotation"Discriminator.
annotationAnnotationRefThe annotation reference.

ErrorBlock

Represents an error that occurred during execution.

FieldTypeDescription
type"error"Discriminator.
errorstrError message.
codestr | NoneOptional error code.

MediaRef

A polymorphic reference to binary media content. Use the kind discriminator to choose the reference strategy.

# By URL
MediaRef(kind="url", url="https://cdn.example.com/image.png", mime_type="image/png")

# By provider file ID (OpenAI/Gemini uploaded file)
MediaRef(kind="file_id", file_id="file-abc123", mime_type="image/png")

# Inline base64 (small files only — prefer offloading to media store)
MediaRef(kind="data", data_base64="iVBORw0KGgo...", mime_type="image/png")

Fields

FieldTypeDescription
kind"url" | "file_id" | "data"Reference type discriminator.
urlstr | NoneHTTP(S) URL or agentflow://media/{key} for offloaded media.
file_idstr | NoneProvider-managed file ID (OpenAI Files API, Gemini File API).
data_base64str | NoneBase64-encoded content. Use only for small payloads (< 50 KB).
mime_typestr | NoneMIME type, e.g. "image/jpeg", "application/pdf".
size_bytesint | NoneFile size hint.
sha256str | NoneContent hash for integrity verification.
filenamestr | NoneOriginal filename.
widthint | NoneImage width in pixels.
heightint | NoneImage height in pixels.
duration_msint | NoneAudio/video duration in milliseconds.
pageint | NonePage number for documents.

AnnotationRef

AnnotationRef(url="https://arxiv.org/abs/2303.08774", title="GPT-4 Technical Report", page=1)
FieldTypeDescription
urlstr | NoneSource URL.
file_idstr | NoneFile reference.
pageint | NonePage number.
indexint | NoneIndex position in the source.
titlestr | NoneDisplay title.

TokenUsages

Token consumption statistics for a single LLM response.

usages = TokenUsages(
completion_tokens=150,
prompt_tokens=320,
total_tokens=470,
)
FieldTypeDefaultDescription
completion_tokensintrequiredTokens generated in the response.
prompt_tokensintrequiredTokens in the input prompt.
total_tokensintrequiredSum of completion and prompt tokens.
reasoning_tokensint0Tokens used for extended reasoning (o1, Gemini thinking).
cache_creation_input_tokensint0Prompt cache write tokens (Anthropic-style).
cache_read_input_tokensint0Prompt cache read tokens.
image_tokensint | None0Image modality tokens (multimodal models).
audio_tokensint | None0Audio modality tokens.

Inspecting a response

from agentflow.core.state.message_block import TextBlock, ToolCallBlock

result = await app.ainvoke({"messages": [Message.text_message("Hello")]})

for msg in result["messages"]:
for block in msg.content:
if block.type == "text":
print(block.text)
elif block.type == "tool_call":
print(f"Called {block.name}({block.args})")