Back to Omniroute

OmniRoute Plugin SDK

docs/frameworks/PLUGIN_SDK.md

3.8.497.5 KB
Original Source

OmniRoute Plugin SDK

Quick Start

ts
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.

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

modifyBody(body): PluginResult

Modify the request body before it reaches the provider.

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

addMetadata(metadata): PluginResult

Attach metadata to the request context.

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

Plugin Context (PluginContext)

FieldTypeDescription
requestIdstringUnique request identifier
modelstringRequested model name
providerstringTarget provider ID
bodyRecord<string, unknown>Request body
headersRecord<string, string>Request headers
metadataRecord<string, unknown>Mutable metadata
timestampnumberRequest timestamp

Manifest (plugin.json)

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):

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

Or as simple booleans (default priority 100):

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

Permission System

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

PermissionGrants
networkfetch, AbortController, Headers, Request, Response
file-readfs.readFile, fs.readdir, fs.stat
file-writefs.writeFile, fs.mkdir, fs.rm
envRead-only process.env proxy
execchild_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:

json
{
  "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

EventWhenPayload
onRequestBefore chat handlerRequest context
onResponseAfter chat handlerResponse data
onErrorOn handler errorError object
onModelSelectModel selected for routingModel info
onComboResolveCombo routing resolvedCombo targets
onRateLimitRate limit hitLimit info
onQuotaExhaustQuota exhaustedQuota info
onProviderErrorProvider returned errorError details
onStreamStartSSE stream startedStream info
onStreamEndSSE stream endedStream stats
onInstallPlugin installed{ name, version, manifest }
onActivatePlugin activated{ name, version, manifest }
onDeactivatePlugin deactivated{ name, version, manifest }
onUninstallPlugin uninstalled (before files deleted){ name, version, manifest }

Examples

Request Logger

ts
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

ts
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

ts
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;
  },
});