Creating Custom Providers for Eliza

This guide will walk you through creating your own custom provider and action in Eliza. We'll build something practical: a news digest system that can fetch and summarize news from your favorite sources. I've used this action and provider with my own agent, arjux.

What you'll learn:

1. Setting Up Your Project

First, let's create a new repository from the eliza-starter template:

  1. Go to https://github.com/ai16z/eliza-starter
  2. Click the "Use this template" button
  3. Create your new repository
  4. Clone your new repository:
git clone https://github.com/YOUR_USERNAME/YOUR_REPO_NAME
cd YOUR_REPO_NAME
pnpm install

2. Creating Your News Provider

Let's create a provider that fetches news from your chosen RSS feeds. Make sure you replace the source URL's with your own.

// src/providers/newsDigest.provider.ts
import { Provider, IAgentRuntime, Memory, State } from "@ai16z/eliza";
import Parser from 'rss-parser';
import * as cheerio from 'cheerio';

interface NewsItem {
  title: string;
  link: string;
  pubDate: string;
  content: string;
  source: string;
}

// Add your favorite news sources here!
const RSS_FEEDS = [
  { title: "Your Tech News Source", url: "https://your-tech-news-source.com/feed" },
  { title: "Another News Site", url: "https://another-news-site.com/rss" },
  // Add more feeds as needed
];

const parser = new Parser();

const newsDigestProvider: Provider = {
  get: async (_runtime: IAgentRuntime, message: Memory, _state?: State): Promise => {
    try {
      // Check if this is a news-related request
      const text = message.content.text.toLowerCase().trim();
      const hasNews = text.includes('news');
      const hasSecurity = text.includes('security') || text.includes('cyber');
      
      if (!hasNews || !hasSecurity) {
        return "";
      }

      // You can customize these keywords based on what you want to filter out
      const unwantedKeywords = ['trending', 'price', 'stocks'];
      if (unwantedKeywords.some(keyword => text.includes(keyword))) {
        return "";
      }
      
      const last24Hours = Date.now() - 24 * 60 * 60 * 1000;
      const allNews: NewsItem[] = [];

      // Fetch from your RSS feeds
      for (const feed of RSS_FEEDS) {
        try {
          const feedData = await parser.parseURL(feed.url);
          const item = feedData.items[0];
          
          if (item) {
            const pubDate = new Date(item.pubDate || '').getTime();
            if (pubDate > last24Hours) {
              // Get the full article
              const response = await fetch(item.link || '');
              const html = await response.text();
              const $ = cheerio.load(html);
              
              // Customize this selector based on your news sources
              const content = $('.article-content').text();

              allNews.push({
                title: item.title || '',
                link: item.link || '',
                pubDate: item.pubDate || '',
                content: content.trim(),
                source: feed.title
              });
            }
          }
        } catch (error) {
          console.error(`Error fetching ${feed.title}:`, error);
          // Continue with other feeds
        }
      }

      // Get the latest stories
      const topStories = allNews
        .sort((a, b) => new Date(b.pubDate).getTime() - new Date(a.pubDate).getTime())
        .slice(0, 3);

      // Format your digest however you like!
      return topStories.map(story => `
📰 ${story.title}
Source: ${story.source}
Published: ${new Date(story.pubDate).toLocaleString()}
Summary: ${story.content.substring(0, 250)}...

Read more: ${story.link}
      `).join('\n\n');
    } catch (error) {
      console.error('Error in newsDigest provider:', error);
      return 'Unable to fetch news stories right now. Please try again later.';
    }
  }
};

3. Building Your Action

Now let's create an action that handles user requests. You can customize the keywords and responses.

// src/actions/newsDigest.action.ts
import { Action, IAgentRuntime, Memory } from "@ai16z/eliza";
import { newsDigestProvider } from "../providers/newsDigest.provider.js";

export const newsDigestAction: Action = {
  name: "NEWS_DIGEST",
  // Add more variations of how users might ask for news
  similes: ["NEWS_UPDATE", "LATEST_NEWS", "WHATS_NEW"],
  description: "Fetch and summarize the latest news from your favorite sources",
  examples: [
    [
      {
        user: "{{user1}}",
        content: { text: "What's happening in tech today?" }
      },
      {
        user: "{{user2}}",
        content: { text: "I'll fetch the latest tech news for you", action: "NEWS_DIGEST" }
      }
    ]
  ],
  
  validate: async (_runtime: IAgentRuntime, message: Memory) => {
    const text = message.content.text.toLowerCase().trim();
    
    // Customize these filters for your use case
    const unwantedKeywords = ['weather', 'sports'];
    if (unwantedKeywords.some(keyword => text.includes(keyword))) {
      return false;
    }
    
    // Add your own keyword combinations
    const hasNews = text.includes('news') || text.includes('update');
    const hasTopic = text.includes('tech') || text.includes('security');
    
    return hasNews && hasTopic;
  },
  
  handler: async (runtime: IAgentRuntime, message: Memory) => {
    try {
      const news = await newsDigestProvider.get(runtime, message);
      
      await runtime.messageManager.createMemory({
        ...message,
        content: {
          text: news,
          action: "NEWS_DIGEST"
        }
      });
      
      return true;
    } catch (error) {
      console.error("Error in news digest action:", error);
      return false;
    }
  }
};

4. Bringing It All Together

Finally, let's register your provider and action with your Eliza agent:

// src/index.ts
import { createAgent } from '@ai16z/eliza';
import { newsDigestProvider } from './providers/newsDigest.provider';
import { newsDigestAction } from './actions/newsDigest.action';

const agent = createAgent({
  providers: [
    ['newsDigest', newsDigestProvider],
    // Add your other providers here
  ],
  actions: [
    newsDigestAction,
    // Add your other actions here
  ]
});

5. Building and Running Your Agent

Now that we have our provider and action registered, let's build and start our agent:

# Build the project
pnpm run build src/index.ts

# Start the agent with the Eliza character
pnpm start --character="characters/eliza.character.json"

Making It Your Own

Customization Points

Pro Tips

Conclusion

Now you have your own custom news digest system! You can extend this further by:

The possibilities are endless - other actions and providers I have integrated include: a eliza repo commit history explanation, clanker revenue tracking, and more. Extend your agents capabilities with your own custom actions and providers.