Important: This guide provides comprehensive documentation for the composeState method and Providers in v1.x, including comparisons with v0.

Table of Contents


State Management with composeState

The composeState method is the central mechanism for building the context state that powers agent responses. In v1, it has been enhanced with powerful filtering capabilities.

Basic Usage

// v1: Basic state composition
const state = await runtime.composeState(message);

This creates a complete state object containing all available context:

  • Agent information (bio, lore, personality)
  • Conversation history
  • Room and participant details
  • Available actions and evaluators
  • Knowledge and RAG data
  • Provider-generated context

State Filtering

The v1 composeState method introduces filtering capabilities for performance optimization:

// v1: Signature
composeState(
  message: Memory,
  includeList?: string[],    // Keys to include
  onlyInclude?: boolean,     // If true, ONLY include listed keys
  skipCache?: boolean        // Skip caching mechanism
): Promise<State>

Filtering Examples

// Include only specific state keys
const minimalState = await runtime.composeState(
  message,
  ['agentName', 'bio', 'recentMessages'],
  true // onlyInclude = true
);

// Update only specific parts of existing state
const updatedState = await runtime.composeState(
  message,
  ['RECENT_MESSAGES', 'GOALS'] // Update only these
);

// Skip cache for fresh data
const freshState = await runtime.composeState(
  message,
  undefined,
  false,
  true // skipCache = true
);

Available State Keys

Here are the primary state keys you can filter:

Core Agent Information

  • agentId - Agent’s UUID
  • agentName - Agent’s display name
  • bio - Agent biography (string or selected from array)
  • lore - Random selection of lore bits
  • adjective - Random adjective from character
  • topic / topics - Agent’s interests

Conversation Context

  • recentMessages - Formatted recent messages
  • recentMessagesData - Raw message Memory objects
  • recentPosts - Formatted posts in thread
  • attachments - Formatted attachment information

Interaction History

  • recentMessageInteractions - Past interactions as messages
  • recentPostInteractions - Past interactions as posts
  • recentInteractionsData - Raw interaction Memory[]

Character Examples

  • characterPostExamples - Example posts from character
  • characterMessageExamples - Example conversations

Directions & Style

  • messageDirections - Message style guidelines
  • postDirections - Post style guidelines

Room & Participants

  • roomId - Current room UUID
  • actors - Formatted actor information
  • actorsData - Raw Actor[] array
  • senderName - Name of message sender

Goals & Actions

  • goals - Formatted goals string
  • goalsData - Raw Goal[] array
  • actionNames - Available action names
  • actions - Formatted action descriptions
  • actionExamples - Action usage examples

Evaluators

  • evaluators - Formatted evaluator information
  • evaluatorNames - List of evaluator names
  • evaluatorExamples - Evaluator examples
  • evaluatorsData - Raw Evaluator[] array

Knowledge

  • knowledge - Formatted knowledge text
  • knowledgeData - Knowledge items
  • ragKnowledgeData - RAG knowledge items

Providers

  • providers - Additional context from providers

Performance Optimization

Use filtering to optimize performance by only computing needed state:

// Minimal state for simple responses
const quickResponse = await runtime.composeState(
  message,
  ['agentName', 'bio', 'recentMessages', 'messageDirections'],
  true
);

// Full state for complex decision-making
const fullState = await runtime.composeState(message);

// Update pattern for ongoing conversations
let state = await runtime.composeState(message);
// ... later in conversation ...
state = await runtime.composeState(newMessage, ['RECENT_MESSAGES', 'GOALS', 'attachments']);

Providers in v1

Providers supply dynamic contextual information to the agent, acting as the agent’s “senses” for perceiving external data.

Provider Interface

interface Provider {
  // REQUIRED: Unique identifier
  name: string;

  // Optional metadata
  description?: string;
  dynamic?: boolean;
  position?: number;
  private?: boolean;

  // The data retrieval method
  get: (runtime: IAgentRuntime, message: Memory, state: State) => Promise<ProviderResult>;
}

interface ProviderResult {
  values?: {
    [key: string]: any;
  };
  data?: {
    [key: string]: any;
  };
  text?: string;
}

Creating Providers

Simple Text Provider

const weatherProvider: Provider = {
  name: 'weatherProvider',
  description: 'Provides current weather information',
  dynamic: true,

  get: async (runtime, message, state) => {
    const weather = await fetchWeatherData();

    return {
      text: `Current weather: ${weather.temp}°F, ${weather.condition}`,
      values: {
        temperature: weather.temp,
        condition: weather.condition,
      },
    };
  },
};

Complex Data Provider

const marketDataProvider: Provider = {
  name: 'marketDataProvider',
  description: 'Provides real-time market data',
  dynamic: true,
  position: 10, // Higher priority

  get: async (runtime, message, state) => {
    const symbols = extractSymbolsFromMessage(message.content.text);
    const marketData = await fetchMarketData(symbols);

    const summary = formatMarketSummary(marketData);

    return {
      text: summary,
      data: marketData,
      values: {
        mentionedSymbols: symbols,
        marketStatus: marketData.status,
      },
    };
  },
};

Conditional Provider

const contextualProvider: Provider = {
  name: 'contextualProvider',
  description: 'Provides context based on conversation',

  get: async (runtime, message, state) => {
    // Access state to make decisions
    const topic = state.topic;
    const recentTopics = analyzeRecentTopics(state.recentMessagesData);

    if (!topic || !recentTopics.includes(topic)) {
      return { text: '' }; // No additional context needed
    }

    const relevantInfo = await fetchTopicInfo(topic);

    return {
      text: `Relevant ${topic} information: ${relevantInfo}`,
      data: { topic, info: relevantInfo },
    };
  },
};

Provider Options

dynamic Property

Set to true for providers that return different data based on context:

const timeProvider: Provider = {
  name: 'timeProvider',
  dynamic: true, // Time always changes
  get: async () => ({
    text: `Current time: ${new Date().toLocaleString()}`,
    values: { timestamp: Date.now() },
  }),
};

position Property

Controls provider priority (higher = higher priority):

const criticalProvider: Provider = {
  name: 'criticalProvider',
  position: 100, // Will be processed before lower position providers
  get: async () => ({ text: 'Critical information...' }),
};

private Property

Hide from public provider lists:

const internalProvider: Provider = {
  name: 'internalProvider',
  private: true, // Won't appear in provider lists
  get: async () => ({ text: 'Internal data...' }),
};

Provider Best Practices

  1. Always Return ProviderResult
// ❌ Bad - returning raw value
get: async () => 'Some text';

// ✅ Good - returning ProviderResult
get: async () => ({ text: 'Some text' });
  1. Use Appropriate Return Fields
return {
  // Human-readable summary
  text: 'Market is up 2.5% today',

  // Simple key-value pairs for templates
  values: {
    marketChange: 2.5,
    marketStatus: 'bullish'
  },

  // Complex nested data for processing
  data: {
    stocks: [...],
    analysis: {...}
  }
};
  1. Handle Errors Gracefully
get: async (runtime, message, state) => {
  try {
    const data = await fetchExternalData();
    return { text: formatData(data), data };
  } catch (error) {
    elizaLogger.error('Provider error:', error);
    return {
      text: 'Unable to fetch data at this time',
      values: { error: true },
    };
  }
};
  1. Optimize Performance
const cachedProvider: Provider = {
  name: 'cachedProvider',
  dynamic: false, // Indicates static data

  get: async (runtime) => {
    // Check cache first
    const cached = await runtime.getSetting('providerCache');
    if (cached && !isExpired(cached)) {
      return { data: cached.data };
    }

    // Fetch fresh data
    const fresh = await fetchData();
    await runtime.setSetting('providerCache', {
      data: fresh,
      timestamp: Date.now(),
    });

    return { data: fresh };
  },
};

v0 vs v1 Comparison

composeState Changes

Method Signature

// v0: Simple with additional keys
composeState(
  message: Memory,
  additionalKeys?: { [key: string]: unknown }
): Promise<State>

// v1: Advanced with filtering
composeState(
  message: Memory,
  includeList?: string[],
  onlyInclude?: boolean,
  skipCache?: boolean
): Promise<State>

Key Differences:

  1. Filtering: v1 allows selective state composition
  2. Performance: Can request only needed state keys
  3. Caching: Explicit cache control with skipCache
  4. Update Pattern: Use same method for updates with specific keys

Migration Example:

// v0: Update pattern
state = await runtime.updateRecentMessageState(state);

// v1: Update pattern
state = await runtime.composeState(message, ['RECENT_MESSAGES']);

Provider Changes

Interface Changes

// v0: Minimal interface
interface Provider {
  get: (runtime, message, state?) => Promise<any>;
}

// v1: Rich interface
interface Provider {
  name: string; // REQUIRED
  description?: string;
  dynamic?: boolean;
  position?: number;
  private?: boolean;
  get: (runtime, message, state) => Promise<ProviderResult>;
}

Return Type Changes

// v0: Return anything
return 'Some text';
return { data: 'value' };

// v1: Return ProviderResult
return {
  text: 'Human readable',
  values: { key: 'value' },
  data: { complex: 'object' },
};

Key Differences:

  1. Required Name: Every provider must have unique name
  2. Structured Returns: Must return ProviderResult object
  3. Rich Metadata: Can specify behavior with options
  4. State Parameter: No longer optional in get method
  5. Better Organization: Clear separation of text, values, and data

Migration Checklist

  • Add name property to all providers
  • Update return statements to use ProviderResult format
  • Remove optional ? from state parameter in get method
  • Consider adding description for documentation
  • Use dynamic: true for context-dependent providers
  • Replace updateRecentMessageState with filtered composeState
  • Optimize performance by filtering state keys
  • Add error handling with graceful fallbacks
  • Consider caching strategies for expensive operations

Examples & Patterns

State Filtering Pattern

// Initial load - get essential state
const initialState = await runtime.composeState(
  message,
  ['agentName', 'bio', 'recentMessages', 'actions', 'providers'],
  true
);

// Process message...

// Update only what changed
const updatedState = await runtime.composeState(message, [
  'RECENT_MESSAGES',
  'goals',
  'attachments',
]);

Provider Chain Pattern

const providers = [
  weatherProvider, // position: 10
  newsProvider, // position: 5
  marketProvider, // position: 15
  fallbackProvider, // position: 1
];

// Will be processed in order: market, weather, news, fallback
runtime.providers = providers.sort((a, b) => (b.position || 0) - (a.position || 0));

Conditional State Building

const buildState = async (message: Memory, isDetailedResponse: boolean) => {
  const baseKeys = ['agentName', 'bio', 'recentMessages'];

  const keys = isDetailedResponse
    ? [...baseKeys, 'lore', 'topics', 'characterMessageExamples', 'knowledge']
    : baseKeys;

  return runtime.composeState(message, keys, true);
};