Messages and context¶
This page defines the Context, message interfaces, content blocks,
constructors, and serialization contract. Complete implementations for
multi-turn conversations, images, persistence, and model switching live in the
corresponding guides.
Message and content model¶
A history is a []llm.Message. Message is an interface with three
implementations, one per role. Each holds a slice of content blocks, and the
role constrains which block types are allowed:
| Message | Role | Allowed content blocks |
|---|---|---|
UserMessage |
user input | TextContent, ImageContent |
AssistantMessage |
model output | TextContent, ThinkingContent, ToolCall |
ToolResultMessage |
a tool's result | TextContent, ImageContent |
The content blocks are the leaf types you read and write:
| Block | Carries |
|---|---|
TextContent |
plain text (valid in any message) |
ImageContent |
base64 image data plus a MIME type |
ThinkingContent |
reasoning text and its provider signature (assistant only) |
ToolCall |
a tool name, an ID, and decoded arguments (assistant only) |
Because both the message and the blocks are typed, a stored conversation round-trips through JSON without manual dispatch — see JSON serialization.
For the common "just send text" case, reach for the convenience constructors below. Build the struct literals by hand only when you need content a constructor does not cover — for example mixing text and an image in one user message, or seeding an assistant turn that carries a tool call. See Sending images for the complete image-input flow.
Build messages¶
Context, Message, and the content blocks are fully general, but most calls
just send some text. Convenience constructors remove the nesting for that path:
llm.Prompt("Explain Go channels briefly.") // Context with one user text message
llm.PromptWithSystem("Be concise.", "Explain...") // ...with a system prompt
llm.UserText("hello") // *UserMessage
llm.AssistantText("hi there") // *AssistantMessage (seed history)
llm.UserImage(data, "image/png") // *UserMessage with one image
llm.ToolResult(callID, name, "result text") // *ToolResultMessage
llm.NewContext(msg1, msg2, ...) // Context from messages
Read a response back with the matching accessors on AssistantMessage:
The longhand struct literals below remain valid; reach for them when you need content a constructor does not cover, such as mixing text and images in one message.
History and model transformation¶
llm stores no session state. The caller owns a []llm.Message, appends each
*AssistantMessage and the next user message, then passes the slice back in
Context.Messages. SystemPrompt belongs to the request context and is not
automatically inserted into the message slice. For concurrency, storage
boundaries, and a complete restore program, see
Saving and restoring conversations.
Before a request, TransformMessages creates a target-model copy of history:
| Stored content | Transformation |
|---|---|
| Image sent to a text-only model | Replace it with a text placeholder |
| Reasoning from the same model | Preserve compatible thinking and signatures |
| Reasoning from another model | Remove model-service-private reasoning |
| Tool-call IDs | Normalize for the target protocol and update matching results |
| Failed or aborted assistant message | Remove it from the replay copy |
| Tool call without a result | Insert a synthetic error result |
“Same model” requires provider, protocol, and model ID to match. Transformation does not mutate caller-owned history; unchanged message objects may be shared with the source slice, so callers should treat input history as immutable.
See Changing models in a conversation for the complete cross-model flow and compatibility checks.
JSON serialization¶
Context round-trips through JSON. Messages carry roles and content blocks
carry types, so unmarshalling restores concrete message and content
implementations. For stores that persist one message per record, use
MarshalMessage and UnmarshalMessage:
data, err := llm.MarshalMessage(messages[0])
if err != nil {
log.Fatal(err)
}
message, err := llm.UnmarshalMessage(data)
if err != nil {
log.Fatal(err)
}
messages = append(messages, message)
UnmarshalMessage returns an error for an unknown role, unknown content type,
or malformed JSON. It does not silently coerce unsupported shapes. For file and
database examples, concurrent writes, and schema-version guidance, see
Saving and restoring conversations. For
encoding, model-capability checks, and security boundaries around images, see
Sending images.
Serialized history is sensitive data
A serialized Context can contain user input, tool results (which may embed
fetched documents or credentials), and provider reasoning signatures. Treat
the JSON as sensitive: do not log it wholesale, and store or transmit it with
the same care as the underlying data.