Messages

In shortReference for Message, the twelve content block types, MediaRef, AnnotationRef and TokenUsages, the typed format for all agent communication.

  • 9 min read
  • 7 sections
  • Updated
  • v0.10.0
  • Markdown

A Message is the unit of conversation in 10xGraph: a role plus an ordered list of typed content blocks (text, images, tool calls, reasoning and more). Graph nodes, the API and the TypeScript client all exchange this shape. This page lists every field, constructor and block. For the concepts, see State and messages.

Import paths

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

Most of these names are also exported from tenxgraph.core.state (everything above except RemoteToolCallBlock and generate_id).


Message

A Pydantic model holding one conversation turn. Conversations in graph state are list[Message].

Fields

Field Type Default Description
message_id str | int auto-generated Unique message identifier. Generated by generate_id, which honors an ID generator registered in the dependency container.
role "user" | "assistant" | "system" | "tool" required Who sent the message.
content Sequence[ContentBlock] required Ordered list of typed content blocks.
delta bool False True for streaming intermediate chunks.
tools_calls list[dict] | None None Raw tool call dicts from the provider SDK. Used internally.
reasoning str | None None Reasoning trace (marked for removal in the source; use ReasoningBlock in content instead).
timestamp float | None current time UNIX timestamp of message creation.
metadata dict {} Arbitrary key-value metadata.
usages TokenUsages | None None Token usage for this message’s LLM call.
raw dict | None None The raw provider API response.
parsed_content dict | BaseModel | None None Structured output parsed by the provider converter when the agent uses output_schema. Read it in preference to parsing the text block yourself.

Constructors and helpers

Use these class methods to build messages without assembling blocks by hand. All of them generate a message_id through generate_id when you do not pass one.

Method Signature Returns
text_message (content: str, role="user", message_id: str | None = None) Message with one TextBlock.
tool_message (content: Sequence[ContentBlock], message_id: str | None = None, meta: dict | None = None) Message with role="tool".
image_message (*, image_url=None, image_base64=None, mime_type="image/png", text=None, role="user", message_id=None) Message with an optional text block and an ImageBlock. Raises ValueError if neither image_url nor image_base64 is given.
multimodal_message (content_blocks: Sequence[ContentBlock], role="user", message_id=None) Message from your own blocks.
from_file (file_path: str, mime_type=None, text=None, role="user", message_id=None) Message with the file base64-encoded inline. Raises FileNotFoundError.
with_image async (data: bytes, mime_type: str, store, text=None, role="user", message_id=None) Message whose image is saved to a media store, keeping only a MediaRef.
with_file async (file_path: str, store, mime_type=None, text=None, role="user", message_id=None) Same as from_file, but stored through a media store. Raises FileNotFoundError.

from_file and with_file pick the block type from the MIME type: image/* gives ImageBlock, audio/* gives AudioBlock, video/* gives VideoBlock, anything else gives DocumentBlock. If the MIME type is not passed, it is guessed from the file extension, falling back to application/octet-stream. The store argument is a media store; see Media.

Python
from tenxgraph.core.state import Message

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

Instance methods

Method Returns Description
text() str Concatenates the text of every TextBlock and ToolResultBlock in content. Non-string tool outputs are JSON-encoded. Other block types are skipped.
attach_media(media: MediaRef, as_type) None Appends an ImageBlock, AudioBlock, VideoBlock or DocumentBlock to content. as_type is "image", "audio", "video" or "document"; anything else raises ValueError. content must be a mutable list.

Direct construction

Python
from tenxgraph.core.state import Message
from tenxgraph.core.state.message_block import TextBlock

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

Content blocks

Every item in Message.content is one of twelve block types. They share a type discriminator field with a fixed literal value, so you can narrow by checking block.type. The union is exported as ContentBlock.

TextBlock

Python
TextBlock(text="Hello, world!")
TextBlock(text="See reference [1].", annotations=[AnnotationRef(url="https://example.com", title="Source")])
Field Type Description
type "text" Discriminator.
text str The text content.
annotations list[AnnotationRef] Citations or links embedded in the text.

ImageBlock

Python
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")
Field Type Description
type "image" Discriminator.
media MediaRef Reference to the image.
alt_text str | None Accessibility text.
bbox list[float] | None Bounding box [x1, y1, x2, y2].

AudioBlock

Python
AudioBlock(
    media=MediaRef(kind="url", url="https://example.com/recording.mp3"),
    transcript="Hello, this is a recording.",
)
Field Type Description
type "audio" Discriminator.
media MediaRef Reference to the audio.
transcript str | None Transcript of the audio content.
sample_rate int | None Sample rate in Hz.
channels int | None Number of audio channels.

VideoBlock

Python
VideoBlock(
    media=MediaRef(kind="url", url="https://example.com/video.mp4"),
    thumbnail=MediaRef(kind="url", url="https://example.com/thumb.jpg"),
)
Field Type Description
type "video" Discriminator.
media MediaRef Reference to the video.
thumbnail MediaRef | None Thumbnail image reference.

DocumentBlock

Python
DocumentBlock(
    media=MediaRef(kind="file_id", file_id="file-def456", mime_type="application/pdf"),
    excerpt="This report covers Q4 earnings...",
    pages=[1, 2, 3],
)
Field Type Description
type "document" Discriminator.
media MediaRef Reference to the document file.
text str | None Extracted full text content.
pages list[int] | None Relevant page numbers.
excerpt str | None Short excerpt/preview.

DataBlock

Python
DataBlock(mime_type="application/json", data_base64="eyJrZXkiOiAidmFsdWUifQ==")
Field Type Description
type "data" Discriminator.
mime_type str MIME type of the data.
data_base64 str | None Base64-encoded payload.
media MediaRef | None External media reference.

ToolCallBlock

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

Python
ToolCallBlock(id="call_abc", name="get_weather", args={"location": "London"})
Field Type Description
type "tool_call" Discriminator.
id str Tool call ID (matches the corresponding ToolResultBlock).
name str Tool function name.
args dict Arguments dict.
tool_type str | None Optional 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.

Field Type Description
type "remote_tool_call" Discriminator.
id str Call ID.
name str Configured remote-tool name with a matching client handler.
args dict Arguments.
tool_type str Defaults to "remote".

ToolResultBlock

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

Python
ToolResultBlock(call_id="call_abc", output={"order_id": "A1001", "status": "shipped"}, is_error=False)
Field Type Description
type "tool_result" Discriminator.
call_id str Matches the ToolCallBlock.id this is a response to.
output Any The result returned by the tool (string, dict, MediaRef or a list of blocks).
is_error bool True if the tool execution failed. Default False.
status "completed" | "failed" | None Status of the tool call.

ReasoningBlock

Exposes a reasoning model’s thinking trace when the provider returns one.

Field Type Description
type "reasoning" Discriminator.
summary str Summary of the reasoning.
details list[str] | None Detailed reasoning steps.

AnnotationBlock

Structured annotation or citation attached to a text response.

Field Type Description
type "annotation" Discriminator.
kind "citation" | "note" Kind of annotation. Default "citation".
refs list[AnnotationRef] Annotation references.
spans list[tuple[int, int]] | None Spans covered by the annotation.

ErrorBlock

Represents an error that occurred during execution.

Field Type Description
type "error" Discriminator.
message str Error message.
code str | None Optional error code.
data dict | None Additional error data.

MediaRef

A reference to binary media that is stored by URL, provider file ID or inline base64. The kind field (default "url") says which of url, file_id or data_base64 holds the content.

Python
# 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 a media store for larger ones)
MediaRef(kind="data", data_base64="iVBORw0KGgo...", mime_type="image/png")

Fields

Field Type Description
kind "url" | "file_id" | "data" Reference type discriminator.
url str | None HTTP(S) URL or graph://media/{key} for offloaded media.
file_id str | None Provider-managed file ID (OpenAI Files API, Gemini File API).
data_base64 str | None Base64-encoded content. Use only for small payloads.
mime_type str | None MIME type, e.g. "image/jpeg", "application/pdf".
size_bytes int | None File size hint.
sha256 str | None Content hash for integrity verification.
filename str | None Original filename.
width int | None Image width in pixels.
height int | None Image height in pixels.
duration_ms int | None Audio/video duration in milliseconds.
page int | None Page number for documents.

AnnotationRef

A pointer to the source of a citation or note, used by TextBlock.annotations and AnnotationBlock.refs.

Python
AnnotationRef(url="https://arxiv.org/abs/2303.08774", title="GPT-4 Technical Report", page=1)
Field Type Description
url str | None Source URL.
file_id str | None File reference.
page int | None Page number.
index int | None Index position in the source.
title str | None Display title.

TokenUsages

Token counts for a single LLM response, stored on Message.usages.

Python
usages = TokenUsages(
    completion_tokens=150,
    prompt_tokens=320,
    total_tokens=470,
)
Field Type Default Description
completion_tokens int required Tokens generated in the response.
prompt_tokens int required Tokens in the input prompt.
total_tokens int required Total tokens for the call, as reported by the provider.
reasoning_tokens int 0 Tokens used for extended reasoning (reasoning models).
cache_creation_input_tokens int 0 Prompt cache write tokens (Anthropic-style).
cache_read_input_tokens int 0 Prompt cache read tokens.
image_tokens int | None 0 Image modality tokens (multimodal models).
audio_tokens int | None 0 Audio modality tokens.

Inspect the blocks in a message

Check block.type to narrow a block to its class and read its fields. This example builds a message in memory so it runs without a model.

Python
from tenxgraph.core.state import Message
from tenxgraph.core.state.message_block import TextBlock, ToolCallBlock

msg = Message(
    role="assistant",
    content=[
        TextBlock(text="Checking the weather."),
        ToolCallBlock(id="call_1", name="get_weather", args={"location": "London"}),
    ],
)

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

With a compiled graph, the same loop works on the messages in the result: result["messages"] after app.invoke(...). See State for the state object.

Last updated for v0.10.0Edit this page on GitHubReport an issue