Message Roles
AIDK supports five message roles, each with specific semantics for how they're handled by models and rendered in context.
Role Overview
| Role | Source | Purpose | Persisted? |
|---|---|---|---|
user | Human/client | User messages to the model | Yes |
assistant | Model output | Model-generated responses | Yes |
system | Developer | Instructions, role definition | Yes |
tool | Tool executor | Tool execution results | Yes |
event | Application | User actions, state changes | Yes |
User Messages
Messages from the human user or client application.
<Message role="user">
What's the weather like today?
</Message>
// Or with content blocks
<Message role="user" content={[
{ type: 'text', text: 'Analyze this image:' },
{ type: 'image', source: { type: 'url', url: 'https://...' } }
]} />Characteristics:
- Always visible to the model
- Typically the conversation driver
- Can contain text, images, audio, documents
Assistant Messages
Messages generated by the AI model.
<Message role="assistant">
The weather today is sunny with a high of 72°F.
</Message>
// With tool calls
<Message role="assistant" content={[
{ type: 'text', text: 'Let me check that for you.' },
{ type: 'tool_use', id: 'call_123', name: 'get_weather', input: { city: 'SF' } }
]} />Characteristics:
- Generated by model, not authored by you
- May contain tool calls (
tool_useblocks) - Accumulated in timeline after each tick
System Messages
Instructions and behavioral configuration for the model.
<System>
You are a helpful customer support agent for TechCorp.
Always be professional and concise.
</System>
// Or explicit Message
<Message role="system">
Respond only in JSON format.
</Message>Characteristics:
- Typically at the start of the conversation
- Sets the model's behavior and constraints
- Multiple system messages are consolidated
- Content from
<Section>components becomes system message content
Tool Messages
Results from tool/function executions.
<Message role="tool" content={[
{
type: 'tool_result',
toolUseId: 'call_123',
content: [{ type: 'text', text: '{"temp": 72, "condition": "sunny"}' }]
}
]} />Characteristics:
- Always paired with a
tool_usein an assistant message - Contains
tool_resultcontent blocks toolUseIdmust match the correspondingtool_use.id- Automatically generated by the engine after tool execution
Event Messages
Application events like user actions, state changes, or system notifications.
<Message role="event" content={[
{ type: 'user_action', action: 'clicked_button', target: 'submit' }
]} />
// Using the Event component
<Event>
<UserAction action="uploaded_file" target="document.pdf" />
</Event>Characteristics:
- Represents application-level events, not conversational turns
- Contains event-specific content blocks (
user_action, etc.) - Transformed for models that don't natively support events
Event Content Blocks
// User action
{ type: 'user_action', action: 'clicked', target: 'buy_button' }
// State change
{ type: 'state_change', key: 'cart.items', from: 2, to: 3 }
// System event
{ type: 'system_event', event: 'session_timeout', data: { reason: 'inactivity' } }Model Handling
Different models handle roles differently:
Standard Roles
Most models natively support user, assistant, system, and tool:
// These map directly to model APIs
<Message role="user">Hello</Message> // → user message
<Message role="assistant">Hi!</Message> // → assistant message
<Message role="system">Be helpful</Message> // → system message
<Message role="tool">...</Message> // → tool resultEvent Role Transformation
The event role isn't natively supported by most models. AIDK transforms it:
// Your code
<Event>
<UserAction action="clicked" target="help_button" />
</Event>
// Transformed for model (example)
// Becomes a user message with event delimiters:
// <event type="user_action">
// User clicked: help_button
// </event>The exact transformation is configurable via messageTransformation in model options.
Role Mapping Configuration
Different providers support different roles. AIDK's MessageTransformationConfig controls how non-standard roles are mapped:
messageTransformation: {
roleMapping: {
/**
* Role for event messages:
* - 'user': Most compatible (default)
* - 'developer': Use developer role (Claude, newer OpenAI)
* - 'system': Treat as system context
*/
event: 'user' | 'developer' | 'system',
/**
* Role for ephemeral content (Grounding, Ephemeral):
* - 'user': Most compatible (default)
* - 'developer': Use developer role (Claude, newer OpenAI)
* - 'system': Treat as system context
*/
ephemeral: 'user' | 'developer' | 'system',
},
}Provider-Specific Defaults
| Provider | Event Role | Ephemeral Role | Notes |
|---|---|---|---|
| Anthropic Claude | developer | developer | Native developer role support |
| OpenAI GPT-4+ | developer | developer | Newer models support developer role |
| OpenAI GPT-3.5 | user | user | Use user role fallback |
| Google Gemini | user | user | Use user role |
Configuring Role Mapping
Per-model configuration:
const model = createLanguageModel({
metadata: {
id: 'claude-3',
capabilities: [{
messageTransformation: {
roleMapping: {
event: 'developer',
ephemeral: 'developer',
},
},
}],
},
// ...
});Dynamic configuration based on provider:
messageTransformation: (modelId: string, provider?: string) => ({
roleMapping: {
event: provider === 'anthropic' ? 'developer' : 'user',
ephemeral: provider === 'anthropic' ? 'developer' : 'user',
},
})Per-request override:
const result = await model.generate.call({
messages: [...],
messageTransformation: {
roleMapping: { event: 'user' }, // Override for this request
},
});JSX Components for Messages
Message Component
import { Message } from 'aidk';
// String content
<Message role="user">Hello!</Message>
// Content blocks
<Message
role="assistant"
content={[{ type: 'text', text: 'Hi there!' }]}
/>
// With metadata
<Message
role="user"
id="msg-123"
metadata={{ source: 'web' }}
>
Hello!
</Message>Role-Specific Components
import { User, Assistant, System, Event } from 'aidk';
// Shorthand components
<User>Hello!</User>
<Assistant>Hi there!</Assistant>
<System>You are helpful.</System>
<Event><UserAction action="clicked" target="button" /></Event>Timeline Component
<Timeline>
<System>You are a helpful assistant.</System>
<User>What's 2+2?</User>
<Assistant>2+2 equals 4.</Assistant>
<User>Thanks!</User>
</Timeline>Content Block Types
Messages contain content blocks. Common types:
| Block Type | Used In | Purpose |
|---|---|---|
text | All roles | Plain text content |
image | user, assistant | Image content |
audio | user, assistant | Audio content |
document | user | PDF, documents |
tool_use | assistant | Tool call request |
tool_result | tool | Tool execution result |
user_action | event | User interaction |
state_change | event | State update |
system_event | event | System notification |
interface ContentBlock {
type: 'text' | 'image' | 'audio' | 'tool_use' | 'tool_result' | ...;
// Type-specific fields
}Best Practices
1. Use Semantic Components
// Good: Semantic intent clear
<System>You are a helpful assistant.</System>
<User>{userInput}</User>
// Less good: Generic message
<Message role="system">You are a helpful assistant.</Message>2. Don't Manually Create Assistant Messages
// Bad: Creating assistant messages yourself
<Assistant>I'll help you with that.</Assistant>
// Good: Let the model generate responses
// Assistant messages come from model output3. Use Events for Non-Conversational Actions
// Good: Event for UI action
<Event>
<UserAction action="selected_product" target={productId} />
</Event>
// Less good: User message for non-conversational action
<User>I selected product {productId}</User>4. Consolidate System Instructions
// Good: Single system message with sections
<System>
<Section id="role">You are a customer support agent.</Section>
<Section id="rules">Never promise refunds.</Section>
</System>
// Less good: Multiple system messages
<System>You are a customer support agent.</System>
<System>Never promise refunds.</System>Related
- Timeline - Managing conversation history
- Semantic Primitives - Content formatting
- Ephemeral vs Persisted - What gets saved