254 lines
7.6 KiB
Markdown
254 lines
7.6 KiB
Markdown
---
|
|
title: "OmniRoute Plugin SDK"
|
|
version: 3.8.40
|
|
lastUpdated: 2026-06-28
|
|
---
|
|
|
|
# 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`)
|
|
|
|
| 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`)
|
|
|
|
```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:
|
|
|
|
| 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`:
|
|
|
|
```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
|
|
|
|
| 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
|
|
|
|
```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;
|
|
},
|
|
});
|
|
```
|