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.Assistant Message
Assistant messages contain the model’s responses, including text, reasoning, tool calls, and discovery calls.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.Tool Discovery Result Message
Tool discovery result messages answer a discovery call with the tools your application found. See Tool Discovery.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:Image Part
Images can be provided as URLs or base64-encoded data:File Part
Files can be attached with mime type information:Audio Parts
Audio parts contain base64-encoded audio: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: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.Tool Call Part
Requests to call external tools:Tool Discovery Call Part
The model asking for tools the request does not declare yet, when the request setstoolDiscovery:
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 optionalmetadata 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.metadatais call-level context for observability and middleware.ReasoningPart.providerMetadata,ToolCallPart.providerMetadata, andToolDiscoveryCallPart.providerMetadataare provider-owned data used to round-trip reasoning blocks, tool calls, and discovery calls with the same provider.- Application-owned
metadataon 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 onesystemInstruction); 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:
'leading' models, send the instruction as part of a user message instead:
getSystemMessagePlacementIssues(). Pass a stricter rule than your current model’s to keep the conversation portable across models:
Helper functions
For message helpers likeresultToMessage() and assistantMessage(), see the utilities reference.
Tool Call Flow
Here’s a complete example of handling tool calls:Best Practices
Next Steps
- Configure generation with Configuration options
- Learn about Error Handling
- Explore Providers for provider-specific features