Skip to main content

Overview

Messages are the fundamental building blocks of conversations in core-ai. They represent the dialogue between users, the assistant, and tools.

Message Types

core-ai supports five message types:

System Message

System messages set the context and behavior for the assistant. Put them at the start of the conversation for standing instructions, or later in the conversation for instructions that apply from that point on. See System message placement for where each model accepts them.
providerOptions is keyed by provider id, like the request-level providerOptions. For example, Anthropic accepts a per-message cacheControl. See the Anthropic provider reference. User, tool result, and discovery result messages accept providerOptions the same way. Example:

User Message

User messages represent input from the user. They can be simple text or multi-modal content with images, files, and audio.
Simple Text:
Multi-Modal Content:

Assistant Message

Assistant messages contain the model’s responses, including text, reasoning, tool calls, and discovery calls.
Example:
Assistant messages use a parts array to support multiple content types (text, reasoning, tool calls, discovery calls) in a single message.

Tool Result Message

Tool result messages provide the results of tool calls back to the model.
Example:

Tool Discovery Result Message

Tool discovery result messages answer a discovery call with the tools your application found. See Tool Discovery.
Example:
Answer each discovery call with exactly one discovery result, placed with the step’s tool results. The tools it lists stay declared on later generate() and stream() requests while the message is in the history, unless tools declares the same name. When nothing matched, tools is empty and an optional note tells the model why.

User Content Parts

User messages can include multiple types of content:

Text Part

Simple text content:
Example:

Image Part

Images can be provided as URLs or base64-encoded data:
URL Image:
Base64 Image:

File Part

Files can be attached with mime type information:
Example:

Audio Parts

Audio parts contain base64-encoded audio:
Not all providers support all content types. For example, Anthropic accepts PDF file parts only. Check the provider pages for model-specific input limits.

Assistant Content Parts

Assistant messages can contain text, reasoning, tool calls, and discovery calls:

Text Part

Regular text responses:

Reasoning Part

Extended thinking and reasoning content from the model:
The metadata field is application-owned. Provider adapters ignore it. The providerMetadata object is provider-namespaced. The top-level key is the provider identifier, such as 'anthropic', 'google', 'openai', or 'azure-openai'. Adapters treat other providers’ keys as foreign and downgrade those reasoning blocks to plain text instead of forwarding opaque metadata.
Use getProviderMetadata() when you want typed access to provider-specific metadata.
Accessing Reasoning:

Tool Call Part

Requests to call external tools:
Pass tool call parts back into the next request unchanged. Some providers put data on them that has to return verbatim: Gemini 3 rejects a request when a function call from the current turn is replayed without its thought signature.

Tool Discovery Call Part

The model asking for tools the request does not declare yet, when the request sets toolDiscovery:
names lists exact tool names, query describes the capability the model needs. Like a tool call part, the part wraps the call. Discovery calls appear in GenerateResult.discoveryCalls, never in toolCalls. Pass them back unchanged, like tool call parts, and answer each one with a tool discovery result message before the next request.

Part metadata

Text-bearing parts can carry optional metadata for application and middleware state. Use it for annotations such as classification results, provenance, validation status, or text transformation spans. This metadata stays with the part in message history and generated results. Provider adapters do not serialize it to provider APIs. core-ai has three metadata layers:
  • GenerateOptions.metadata is call-level context for observability and middleware.
  • ReasoningPart.providerMetadata, ToolCallPart.providerMetadata, and ToolDiscoveryCallPart.providerMetadata are provider-owned data used to round-trip reasoning blocks, tool calls, and discovery calls with the same provider.
  • Application-owned metadata on system messages, text parts, reasoning parts, tool calls, discovery calls, tool results, and discovery results.

Multi-Turn Conversations

Build conversations by passing message history:

System message placement

System messages before the first non-system message are leading system messages. Every model accepts them. Anthropic and Google adapters send them as the top-level system prompt (Google joins them into one systemInstruction); the other adapters send them as messages. A system message after that is a later system message. It applies from its position on, which suits instructions that arrive mid-conversation, such as a mode switch. Models differ in where they accept one, and each model reports its rule in model.capabilities.messages.systemPlacement: Each rule accepts everything the stricter rules accept. A tool discovery result counts as a tool message. Adapters send a later system message where you put it; they never move it or merge it into the leading system prompt. The Anthropic adapter drops system messages without non-whitespace text, which Anthropic rejects. When a system message breaks the model’s rule, the request fails with UnsupportedSystemMessagePlacementError before anything is sent:
To add an instruction for the next reply on any model that accepts later system messages, append it directly after the latest user or tool message. On 'leading' models, send the instruction as part of a user message instead:
To check a conversation without sending it, call getSystemMessagePlacementIssues(). Pass a stricter rule than your current model’s to keep the conversation portable across models:

Helper functions

For message helpers like resultToMessage() and assistantMessage(), see the utilities reference.

Tool Call Flow

Here’s a complete example of handling tool calls:

Best Practices

Keep system messages concise: System messages set the tone but shouldn’t contain too much information. For large context, consider using user messages with retrieved content.
Use resultToMessage for history: Always convert generation results to messages using resultToMessage() to maintain proper conversation history with all content parts.
Multi-modal order matters: When combining text and images, place the text part first to provide context for what you’re asking about the image.

Next Steps