elizaOS

Plugin Templates & Examples

Ready-to-use templates and examples for creating ElizaOS plugins

This guide provides ready-to-use templates and examples for creating different types of ElizaOS plugins. Use these as starting points for your own plugin development.

Basic Plugin Template

Minimal Plugin Structure

// src/index.ts
import { Plugin } from "@elizaos/core";

export const basicPlugin: Plugin = {
  name: "basic-plugin",
  description: "A basic plugin template",

  // Optional initialization
  init: async (config, runtime) => {
    console.log("Plugin initialized");
  },

  // Optional configuration
  config: {
    PLUGIN_ENABLED: process.env.PLUGIN_ENABLED || "true",
  },
};

export default basicPlugin;

Package.json Template

{
  "name": "@elizaos/plugin-basic",
  "version": "1.0.0",
  "description": "A basic ElizaOS plugin",
  "main": "dist/index.js",
  "type": "module",
  "scripts": {
    "build": "tsup",
    "dev": "tsup --watch",
    "test": "vitest"
  },
  "keywords": ["eliza", "plugin", "basic"],
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "@elizaos/core": "workspace:*"
  },
  "devDependencies": {
    "tsup": "^8.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
  },
  "agentConfig": {
    "pluginType": "elizaos:plugin:1.0.0",
    "pluginParameters": {
      "PLUGIN_ENABLED": {
        "type": "boolean",
        "description": "Enable or disable the plugin"
      }
    }
  }
}

Action Plugin Template

Simple Action Plugin

// src/actions/greetAction.ts
import { Action, IAgentRuntime, Memory, State } from "@elizaos/core";

export const greetAction: Action = {
  name: "GREET",
  similes: ["hello", "hi", "greet", "greeting"],
  description: "Greets the user with a friendly message",

  validate: async (runtime: IAgentRuntime, message: Memory) => {
    // Validate if action should be triggered
    const text = message.content.text?.toLowerCase();
    return text?.includes("hello") || text?.includes("hi") || text?.includes("greet");
  },

  handler: async (runtime: IAgentRuntime, message: Memory, state: State) => {
    const userName = message.userId || "friend";

    return {
      text: `Hello ${userName}! How can I help you today?`,
      actions: ["GREET"],
    };
  },

  examples: [
    [
      {
        user: "user",
        content: { text: "Hello!" },
      },
      {
        user: "agent",
        content: { text: "Hello friend! How can I help you today?" },
      },
    ],
  ],
};

Plugin with Action

// src/index.ts
import { Plugin } from "@elizaos/core";

import { greetAction } from "./actions/greetAction";

export const greetPlugin: Plugin = {
  name: "greet-plugin",
  description: "A plugin that greets users",
  actions: [greetAction],
};

export default greetPlugin;

Service Plugin Template

Background Service Plugin

// src/services/NotificationService.ts
import { IAgentRuntime, Service, logger } from "@elizaos/core";

export class NotificationService extends Service {
  static serviceType = "notification";

  capabilityDescription = "Handles notifications and alerts";

  private intervalId?: NodeJS.Timeout;

  constructor(protected runtime: IAgentRuntime) {
    super(runtime);
  }

  static async start(runtime: IAgentRuntime): Promise<NotificationService> {
    logger.info("Starting notification service");
    const service = new NotificationService(runtime);

    // Start background process
    service.intervalId = setInterval(() => {
      service.checkNotifications();
    }, 60000); // Check every minute

    return service;
  }

  static async stop(runtime: IAgentRuntime) {
    logger.info("Stopping notification service");
    const service = runtime.getService(NotificationService.serviceType);
    if (service) {
      await service.stop();
    }
  }

  async stop() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
    }
    logger.info("Notification service stopped");
  }

  private async checkNotifications() {
    // Check for notifications logic
    logger.debug("Checking notifications...");
  }
}

Service Plugin

// src/index.ts
import { Plugin } from "@elizaos/core";

import { NotificationService } from "./services/NotificationService";

export const notificationPlugin: Plugin = {
  name: "notification-plugin",
  description: "Handles notifications and alerts",
  services: [NotificationService],
};

export default notificationPlugin;

Provider Plugin Template

Data Provider Plugin

// src/providers/weatherProvider.ts
import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core";

export const weatherProvider: Provider = {
  name: "WEATHER_PROVIDER",
  description: "Provides current weather information",

  get: async (runtime: IAgentRuntime, message: Memory, state: State) => {
    const location = extractLocation(message.content.text) || "New York";

    try {
      const weather = await fetchWeatherData(location);

      return {
        text: `Current weather in ${location}: ${weather.temperature}°F, ${weather.conditions}`,
        values: {
          location,
          temperature: weather.temperature,
          conditions: weather.conditions,
          humidity: weather.humidity,
        },
        data: weather,
      };
    } catch (error) {
      return {
        text: `Unable to fetch weather data for ${location}`,
        values: { error: error.message },
        data: null,
      };
    }
  },
};

function extractLocation(text: string): string | null {
  // Simple location extraction logic
  const locationMatch = text.match(/weather in (.+)/i);
  return locationMatch ? locationMatch[1].trim() : null;
}

async function fetchWeatherData(location: string) {
  // Mock weather API call
  return {
    temperature: 72,
    conditions: "Sunny",
    humidity: 45,
  };
}

Provider Plugin

// src/index.ts
import { Plugin } from "@elizaos/core";

import { weatherProvider } from "./providers/weatherProvider";

export const weatherPlugin: Plugin = {
  name: "weather-plugin",
  description: "Provides weather information",
  providers: [weatherProvider],

  config: {
    WEATHER_API_KEY: process.env.WEATHER_API_KEY,
  },
};

export default weatherPlugin;

API Integration Plugin Template

REST API Plugin

// src/services/ApiService.ts
import { IAgentRuntime, Service, logger } from "@elizaos/core";

export class ApiService extends Service {
  static serviceType = "api-service";

  capabilityDescription = "Integrates with external REST APIs";

  private baseUrl: string;
  private apiKey: string;

  constructor(protected runtime: IAgentRuntime) {
    super(runtime);
    this.baseUrl = runtime.getSetting("API_BASE_URL") || "https://api.example.com";
    this.apiKey = runtime.getSetting("API_KEY");
  }

  static async start(runtime: IAgentRuntime): Promise<ApiService> {
    logger.info("Starting API service");
    const service = new ApiService(runtime);

    // Test API connection
    await service.testConnection();

    return service;
  }

  static async stop(runtime: IAgentRuntime) {
    logger.info("Stopping API service");
    const service = runtime.getService(ApiService.serviceType);
    if (service) {
      await service.stop();
    }
  }

  async stop() {
    logger.info("API service stopped");
  }

  private async testConnection() {
    try {
      const response = await fetch(`${this.baseUrl}/health`, {
        headers: {
          Authorization: `Bearer ${this.apiKey}`,
          "Content-Type": "application/json",
        },
      });

      if (!response.ok) {
        throw new Error(`API connection failed: ${response.status}`);
      }

      logger.info("API connection successful");
    } catch (error) {
      logger.error("API connection failed:", error);
      throw error;
    }
  }

  async makeRequest(
    endpoint: string,
    method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
    data?: any
  ) {
    const url = `${this.baseUrl}${endpoint}`;
    const options: RequestInit = {
      method,
      headers: {
        Authorization: `Bearer ${this.apiKey}`,
        "Content-Type": "application/json",
      },
    };

    if (data && method !== "GET") {
      options.body = JSON.stringify(data);
    }

    try {
      const response = await fetch(url, options);

      if (!response.ok) {
        throw new Error(`API request failed: ${response.status} ${response.statusText}`);
      }

      return await response.json();
    } catch (error) {
      logger.error(`API request failed for ${endpoint}:`, error);
      throw error;
    }
  }
}

Action Using API Service

// src/actions/apiAction.ts
import { Action, IAgentRuntime, Memory, State } from "@elizaos/core";

import { ApiService } from "../services/ApiService";

export const apiAction: Action = {
  name: "API_CALL",
  similes: ["call api", "fetch data", "get information"],
  description: "Makes API calls to external services",

  validate: async (runtime: IAgentRuntime, message: Memory) => {
    const text = message.content.text?.toLowerCase();
    return text?.includes("api") || text?.includes("fetch") || text?.includes("get data");
  },

  handler: async (runtime: IAgentRuntime, message: Memory, state: State) => {
    const apiService = runtime.getService(ApiService.serviceType) as ApiService;

    if (!apiService) {
      return {
        text: "API service is not available",
        actions: ["API_CALL"],
      };
    }

    try {
      const endpoint = extractEndpoint(message.content.text) || "/data";
      const data = await apiService.makeRequest(endpoint);

      return {
        text: `API data retrieved: ${JSON.stringify(data, null, 2)}`,
        actions: ["API_CALL"],
        data,
      };
    } catch (error) {
      return {
        text: `Failed to fetch API data: ${error.message}`,
        actions: ["API_CALL"],
      };
    }
  },

  examples: [
    [
      {
        user: "user",
        content: { text: "Can you fetch the latest data from the API?" },
      },
      {
        user: "agent",
        content: { text: "I'll fetch the latest data for you." },
      },
    ],
  ],
};

function extractEndpoint(text: string): string | null {
  const endpointMatch = text.match(/endpoint\s+(.+)/i);
  return endpointMatch ? endpointMatch[1].trim() : null;
}

Complex Plugin Template

Multi-Component Plugin

// src/index.ts
import { Plugin } from "@elizaos/core";

import { apiAction } from "./actions/apiAction";
import { dataEvaluator } from "./evaluators/dataEvaluator";
import { dataProvider } from "./providers/dataProvider";
import { ApiService } from "./services/ApiService";

export const complexPlugin: Plugin = {
  name: "complex-plugin",
  description: "A complex plugin with multiple components",

  // Configuration
  config: {
    API_KEY: process.env.API_KEY,
    API_BASE_URL: process.env.API_BASE_URL || "https://api.example.com",
    CACHE_ENABLED: process.env.CACHE_ENABLED === "true",
  },

  // Initialization
  init: async (config, runtime) => {
    if (!config.API_KEY) {
      throw new Error("API_KEY is required for complex-plugin");
    }

    // Initialize cache if enabled
    if (config.CACHE_ENABLED) {
      await initializeCache(runtime);
    }
  },

  // Components
  services: [ApiService],
  actions: [apiAction],
  providers: [dataProvider],
  evaluators: [dataEvaluator],

  // HTTP Routes
  routes: [
    {
      name: "plugin-status",
      path: "/api/complex-plugin/status",
      type: "GET",
      handler: async (req, res) => {
        res.json({
          status: "running",
          timestamp: new Date().toISOString(),
        });
      },
    },
    {
      name: "plugin-data",
      path: "/api/complex-plugin/data",
      type: "POST",
      handler: async (req, res) => {
        // Handle data submission
        const data = req.body;
        res.json({ received: data });
      },
    },
  ],

  // Event Handlers
  events: {
    MESSAGE_RECEIVED: [
      async (params) => {
        // Log all received messages
        console.log("Message received:", params.message.content.text);
      },
    ],
    ACTION_COMPLETED: [
      async (params) => {
        // Track action completions
        console.log("Action completed:", params.action.name);
      },
    ],
  },

  // Test Suite
  tests: [
    {
      name: "complex_plugin_test_suite",
      description: "Test suite for complex plugin",
      tests: [
        {
          name: "service_initialization",
          fn: async (runtime) => {
            const service = runtime.getService(ApiService.serviceType);
            expect(service).toBeDefined();
          },
        },
        {
          name: "action_validation",
          fn: async (runtime) => {
            const action = runtime.actions?.find((a) => a.name === "API_CALL");
            expect(action).toBeDefined();

            const isValid = await action.validate(runtime, {
              content: { text: "call api" },
            });
            expect(isValid).toBe(true);
          },
        },
      ],
    },
  ],

  // Dependencies
  dependencies: ["@elizaos/plugin-bootstrap"],

  // Priority
  priority: 10,
};

async function initializeCache(runtime: IAgentRuntime) {
  // Initialize caching system
  console.log("Initializing cache for complex plugin");
}

export default complexPlugin;

Plugin Testing Template

Test Suite Template

// src/__tests__/plugin.test.ts
import { IAgentRuntime } from "@elizaos/core";
import { beforeEach, describe, expect, it } from "vitest";

import { complexPlugin } from "../index";

// Mock runtime
const mockRuntime = {
  getSetting: (key: string) => {
    const settings = {
      API_KEY: "test-api-key",
      API_BASE_URL: "https://test-api.com",
    };
    return settings[key];
  },
  getService: (type: string) => null,
  actions: [],
  providers: [],
  evaluators: [],
} as unknown as IAgentRuntime;

describe("Complex Plugin", () => {
  beforeEach(() => {
    // Reset mocks
  });

  it("should initialize with valid configuration", async () => {
    const config = {
      API_KEY: "test-key",
      API_BASE_URL: "https://test.com",
    };

    await expect(complexPlugin.init(config, mockRuntime)).resolves.not.toThrow();
  });

  it("should throw error without API key", async () => {
    const config = {};

    await expect(complexPlugin.init(config, mockRuntime)).rejects.toThrow("API_KEY is required");
  });

  it("should have required components", () => {
    expect(complexPlugin.name).toBe("complex-plugin");
    expect(complexPlugin.services).toBeDefined();
    expect(complexPlugin.actions).toBeDefined();
    expect(complexPlugin.providers).toBeDefined();
    expect(complexPlugin.evaluators).toBeDefined();
  });
});

E2E Test Template

// src/__tests__/e2e/plugin-e2e.test.ts
import { TestSuite } from "@elizaos/core";

export const ComplexPluginE2ETestSuite: TestSuite = {
  name: "complex_plugin_e2e_test_suite",
  description: "End-to-end tests for complex plugin",

  tests: [
    {
      name: "full_workflow_test",
      fn: async (runtime) => {
        // Test complete workflow
        const service = runtime.getService("api-service");
        expect(service).toBeDefined();

        const action = runtime.actions?.find((a) => a.name === "API_CALL");
        expect(action).toBeDefined();

        const provider = runtime.providers?.find((p) => p.name === "DATA_PROVIDER");
        expect(provider).toBeDefined();
      },
    },
    {
      name: "integration_test",
      fn: async (runtime) => {
        // Test integration between components
        const testMessage = {
          content: { text: "call api endpoint /test" },
          userId: "test-user",
        };

        const action = runtime.actions?.find((a) => a.name === "API_CALL");
        const result = await action.handler(runtime, testMessage, {});

        expect(result).toBeDefined();
        expect(result.text).toContain("API data retrieved");
      },
    },
  ],
};

Usage Examples

Installing and Using Templates

# Create new plugin from template
elizaos create my-new-plugin --type plugin --template complex

# Or use CLI generator
elizaos plugins generate --template api-integration

# Install dependencies
cd plugin-my-new-plugin
bun install

# Start development
elizaos dev

Customizing Templates

  1. Modify the plugin name and description
  2. Update environment variables and configuration
  3. Customize actions, providers, and services
  4. Add your specific business logic
  5. Update tests to match your functionality
  6. Add proper documentation

Best Practices

  • Use TypeScript for type safety
  • Include comprehensive tests for all components
  • Document all configuration options
  • Handle errors gracefully
  • Follow naming conventions
  • Keep dependencies minimal
  • Use environment variables for configuration
  • Implement proper logging

These templates provide a solid foundation for creating ElizaOS plugins. Customize them based on your specific needs and requirements.