Skip to content

Plugin SDK

diegosouzapw edited this page Jun 15, 2026 · 1 revision

🌍 View in other languages

OmniRoute Plugin SDK

Quick Start

import { definePlugin } from "omniroute/plugins/sdk";

export default definePlugin({
  name: "my-plugin",
  priority: 50,
  onRequest: async (ctx) => {
    console.log(`Request ${ctx.requestId} for ${ctx.model}`);
  },
  onResponse: async (ctx, response) => {
    console.log(`Response for ${ctx.requestId}`);
    return response;
  },
  onError: async (ctx, error) => {
    console.error(`Error: ${error.message}`);
  },
});

API Reference

definePlugin(def: PluginDefinition): Plugin

Factory function that creates a Plugin object with defaults.

Parameters:

  • name (string, required) — Plugin name in kebab-case
  • priority (number, optional, default: 100) — Lower runs first
  • enabled (boolean, optional, default: true) — Start enabled?
  • onRequest (function, optional) — Runs before chat handler
  • onResponse (function, optional) — Runs after chat handler
  • onError (function, optional) — Runs on handler error

blockRequest(response?): BlockingHookResult

Block the request and optionally return a custom response.

onRequest: (ctx) => {
  if (!ctx.headers["authorization"]) {
    return blockRequest({ error: "Unauthorized", status: 401 });
  }
};

modifyBody(body): PluginResult

Modify the request body before it reaches the provider.

onRequest: (ctx) => {
  return modifyBody({ ...ctx.body, temperature: 0.7 });
};

addMetadata(metadata): PluginResult

Attach metadata to the request context.

onRequest: (ctx) => {
  return addMetadata({ source: "my-plugin", version: "1.0.0" });
};

Plugin Context (PluginContext)

Field Type Description
requestId string Unique request identifier
model string Requested model name
provider string Target provider ID
body Record<string, unknown> Request body
headers Record<string, string> Request headers
metadata Record<string, unknown> Mutable metadata
timestamp number Request timestamp

Manifest (plugin.json)

{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "A sample plugin",
  "author": "your-name",
  "main": "index.js",
  "hooks": {
    "onRequest": { "enabled": true, "priority": 50 },
    "onResponse": true,
    "onError": false
  },
  "requires": {
    "permissions": ["network", "file-read"]
  },
  "enabledByDefault": false,
  "configSchema": {
    "apiKey": { "type": "string", "description": "API key for external service" },
    "maxRetries": { "type": "number", "min": 1, "max": 10, "default": 3 },
    "debug": { "type": "boolean", "default": false },
    "mode": { "type": "string", "enum": ["fast", "slow"], "default": "fast" }
  }
}

Hook Priority

Hooks can be configured with priority (lower = runs first):

{
  "hooks": {
    "onRequest": { "enabled": true, "priority": 10 },
    "onResponse": { "enabled": true, "priority": 100 }
  }
}

Or as simple booleans (default priority 100):

{
  "hooks": {
    "onRequest": true,
    "onResponse": true
  }
}

Permission System

Plugins run in a sandboxed VM context. Access to external resources requires explicit permissions:

Permission Grants
network fetch, AbortController, Headers, Request, Response
file-read fs.readFile, fs.readdir, fs.stat
file-write fs.writeFile, fs.mkdir, fs.rm
env Read-only process.env proxy
exec child_process.exec, child_process.execSync

Without a permission, the corresponding globals are simply not available in the sandbox.

Config Schema

Define configurable settings in configSchema:

{
  "configSchema": {
    "apiKey": { "type": "string", "description": "External API key" },
    "maxRetries": { "type": "number", "min": 1, "max": 10, "default": 3 },
    "debug": { "type": "boolean", "default": false },
    "mode": { "type": "string", "enum": ["fast", "slow"], "default": "fast" }
  }
}

Field types: string, number, boolean, select

Field options: default, min, max, enum, description

Config values are persisted in the database and accessible via the dashboard config page.

Built-in Events

Event When Payload
onRequest Before chat handler Request context
onResponse After chat handler Response data
onError On handler error Error object
onModelSelect Model selected for routing Model info
onComboResolve Combo routing resolved Combo targets
onRateLimit Rate limit hit Limit info
onQuotaExhaust Quota exhausted Quota info
onProviderError Provider returned error Error details
onStreamStart SSE stream started Stream info
onStreamEnd SSE stream ended Stream stats
onInstall Plugin installed { name, version, manifest }
onActivate Plugin activated { name, version, manifest }
onDeactivate Plugin deactivated { name, version, manifest }
onUninstall Plugin uninstalled (before files deleted) { name, version, manifest }

Examples

Request Logger

import { definePlugin } from "omniroute/plugins/sdk";

export default definePlugin({
  name: "request-logger",
  onRequest: async (ctx) => {
    console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.model} -> ${ctx.provider}`);
  },
});

Rate Limiter

import { definePlugin, blockRequest } from "omniroute/plugins/sdk";

const requests = new Map<string, number[]>();

export default definePlugin({
  name: "rate-limiter",
  priority: 10,
  onRequest: async (ctx) => {
    const key = ctx.headers["x-api-key"] || "anonymous";
    const now = Date.now();
    const window = 60000; // 1 minute
    const maxRequests = 100;

    const timestamps = (requests.get(key) || []).filter(t => t > now - window);
    timestamps.push(now);
    requests.set(key, timestamps);

    if (timestamps.length > maxRequests) {
      return blockRequest({ error: "Rate limit exceeded", status: 429 });
    }
  },
});

Response Transformer

import { definePlugin } from "omniroute/plugins/sdk";

export default definePlugin({
  name: "response-transformer",
  onResponse: async (ctx, response) => {
    if (response.choices) {
      response.choices = response.choices.map((c: any) => ({
        ...c,
        message: { ...c.message, content: c.message.content.trim() },
      }));
    }
    return response;
  },
});

Clone this wiki locally