| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
|
|
|
|
| import { logger } from "../../../open-sse/utils/logger.ts";
|
|
|
| const log = logger("PLUGINS");
|
|
|
| export interface PluginContext {
|
|
|
| requestId: string;
|
|
|
| body: any;
|
|
|
| model: string;
|
|
|
| provider?: string;
|
|
|
| apiKeyInfo?: any;
|
|
|
| metadata: Record<string, any>;
|
| }
|
|
|
| export interface PluginResult {
|
|
|
| blocked?: boolean;
|
|
|
| response?: any;
|
|
|
| body?: any;
|
|
|
| metadata?: Record<string, any>;
|
| }
|
|
|
| export interface Plugin {
|
|
|
| name: string;
|
|
|
| priority?: number;
|
|
|
| enabled?: boolean;
|
|
|
| onRequest?: (ctx: PluginContext) => Promise<PluginResult | void> | PluginResult | void;
|
|
|
| onResponse?: (ctx: PluginContext, response: any) => Promise<any | void> | any | void;
|
|
|
| onError?: (ctx: PluginContext, error: Error) => Promise<any | void> | any | void;
|
| }
|
|
|
|
|
|
|
| const _plugins: Plugin[] = [];
|
|
|
| |
| |
|
|
| export function registerPlugin(plugin: Plugin): void {
|
|
|
| plugin.priority = plugin.priority ?? 100;
|
| plugin.enabled = plugin.enabled ?? true;
|
|
|
|
|
| const idx = _plugins.findIndex((p) => p.name === plugin.name);
|
| if (idx !== -1) _plugins.splice(idx, 1);
|
|
|
| _plugins.push(plugin);
|
| _plugins.sort((a, b) => (a.priority || 100) - (b.priority || 100));
|
|
|
| log.info("plugin.registered", {
|
| name: plugin.name,
|
| priority: plugin.priority,
|
| enabled: plugin.enabled,
|
| });
|
| }
|
|
|
| |
| |
|
|
| export function unregisterPlugin(name: string): boolean {
|
| const idx = _plugins.findIndex((p) => p.name === name);
|
| if (idx === -1) return false;
|
| _plugins.splice(idx, 1);
|
| return true;
|
| }
|
|
|
| |
| |
|
|
| export function setPluginEnabled(name: string, enabled: boolean): boolean {
|
| const plugin = _plugins.find((p) => p.name === name);
|
| if (!plugin) return false;
|
| plugin.enabled = enabled;
|
| return true;
|
| }
|
|
|
| |
| |
|
|
| export function listPlugins(): Array<{
|
| name: string;
|
| priority: number;
|
| enabled: boolean;
|
| hooks: string[];
|
| }> {
|
| return _plugins.map((p) => ({
|
| name: p.name,
|
| priority: p.priority || 100,
|
| enabled: p.enabled !== false,
|
| hooks: [
|
| p.onRequest ? "onRequest" : "",
|
| p.onResponse ? "onResponse" : "",
|
| p.onError ? "onError" : "",
|
| ].filter(Boolean),
|
| }));
|
| }
|
|
|
|
|
|
|
| |
| |
| |
|
|
| export async function runOnRequest(
|
| ctx: PluginContext
|
| ): Promise<{ blocked: boolean; response?: any; ctx: PluginContext }> {
|
| let currentCtx = { ...ctx };
|
|
|
| for (const plugin of _plugins) {
|
| if (!plugin.enabled || !plugin.onRequest) continue;
|
|
|
| try {
|
| const result = await plugin.onRequest(currentCtx);
|
| if (result) {
|
| if (result.blocked) {
|
| log.info("plugin.request_blocked", { name: plugin.name });
|
| return { blocked: true, response: result.response, ctx: currentCtx };
|
| }
|
| if (result.body) currentCtx.body = result.body;
|
| if (result.metadata) {
|
| currentCtx.metadata = { ...currentCtx.metadata, ...result.metadata };
|
| }
|
| }
|
| } catch (err: any) {
|
| log.error("plugin.onRequest_error", {
|
| name: plugin.name,
|
| error: err instanceof Error ? err.message : String(err),
|
| });
|
|
|
| }
|
| }
|
|
|
| return { blocked: false, ctx: currentCtx };
|
| }
|
|
|
| |
| |
|
|
| export async function runOnResponse(ctx: PluginContext, response: any): Promise<any> {
|
| let currentResponse = response;
|
|
|
| for (const plugin of _plugins) {
|
| if (!plugin.enabled || !plugin.onResponse) continue;
|
|
|
| try {
|
| const modified = await plugin.onResponse(ctx, currentResponse);
|
| if (modified !== undefined && modified !== null) {
|
| currentResponse = modified;
|
| }
|
| } catch (err: any) {
|
| log.error("plugin.onResponse_error", {
|
| name: plugin.name,
|
| error: err instanceof Error ? err.message : String(err),
|
| });
|
| }
|
| }
|
|
|
| return currentResponse;
|
| }
|
|
|
| |
| |
| |
|
|
| export async function runOnError(ctx: PluginContext, error: Error): Promise<any | null> {
|
| for (const plugin of _plugins) {
|
| if (!plugin.enabled || !plugin.onError) continue;
|
|
|
| try {
|
| const recovery = await plugin.onError(ctx, error);
|
| if (recovery !== undefined && recovery !== null) {
|
| log.info("plugin.error_recovered", { name: plugin.name });
|
| return recovery;
|
| }
|
| } catch (err: any) {
|
| log.error("plugin.onError_error", {
|
| name: plugin.name,
|
| error: err instanceof Error ? err.message : String(err),
|
| });
|
| }
|
| }
|
|
|
| return null;
|
| }
|
|
|
| |
| |
|
|
| export function resetPlugins(): void {
|
| _plugins.length = 0;
|
| }
|
|
|