UNPKG

ai-sdk-guardrails

Version:

Input and output guardrails middleware for Vercel AI SDK.

816 lines (811 loc) 27.9 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/errors.ts var errors_exports = {}; __export(errors_exports, { GuardrailConfigurationError: () => GuardrailConfigurationError, GuardrailExecutionError: () => GuardrailExecutionError, GuardrailTimeoutError: () => GuardrailTimeoutError, GuardrailValidationError: () => GuardrailValidationError, GuardrailsError: () => GuardrailsError, InputBlockedError: () => InputBlockedError, MiddlewareError: () => MiddlewareError, OutputBlockedError: () => OutputBlockedError, extractErrorInfo: () => extractErrorInfo, isGuardrailsError: () => isGuardrailsError }); function isGuardrailsError(error) { return error instanceof GuardrailsError; } function extractErrorInfo(error) { if (isGuardrailsError(error)) { return { name: error.name, message: error.message, code: error.code, metadata: error.metadata }; } if (error instanceof Error) { return { name: error.name, message: error.message }; } return { name: "UnknownError", message: String(error) }; } var GuardrailsError, GuardrailValidationError, GuardrailExecutionError, GuardrailTimeoutError, GuardrailConfigurationError, InputBlockedError, OutputBlockedError, MiddlewareError; var init_errors = __esm({ "src/errors.ts"() { "use strict"; GuardrailsError = class extends Error { timestamp; metadata; constructor(message, metadata = {}) { super(message); this.timestamp = /* @__PURE__ */ new Date(); this.metadata = metadata; Object.setPrototypeOf(this, new.target.prototype); } /** * Convert the error to a serializable object for logging/reporting */ toJSON() { return { name: this.name, code: this.code, message: this.message, timestamp: this.timestamp.toISOString(), metadata: this.metadata, stack: this.stack }; } /** * Check if this error is of a specific type */ is(errorClass) { return this instanceof errorClass; } }; GuardrailValidationError = class extends GuardrailsError { name = "GuardrailValidationError"; code = "GUARDRAIL_VALIDATION_FAILED"; guardrailName; validationErrors; constructor(guardrailName, validationErrors, metadata = {}) { const message = `Guardrail "${guardrailName}" validation failed: ${validationErrors.map((e) => e.message).join(", ")}`; super(message, { ...metadata, guardrailName, validationErrors }); this.guardrailName = guardrailName; this.validationErrors = validationErrors; } }; GuardrailExecutionError = class extends GuardrailsError { name = "GuardrailExecutionError"; code = "GUARDRAIL_EXECUTION_FAILED"; guardrailName; originalError; constructor(guardrailName, originalError, metadata = {}) { const message = originalError ? `Guardrail "${guardrailName}" execution failed: ${originalError.message}` : `Guardrail "${guardrailName}" execution failed`; super(message, { ...metadata, guardrailName, originalError: originalError?.message }); this.guardrailName = guardrailName; this.originalError = originalError; } }; GuardrailTimeoutError = class extends GuardrailsError { name = "GuardrailTimeoutError"; code = "GUARDRAIL_TIMEOUT"; guardrailName; timeoutMs; constructor(guardrailName, timeoutMs, metadata = {}) { const message = `Guardrail "${guardrailName}" timed out after ${timeoutMs}ms`; super(message, { ...metadata, guardrailName, timeoutMs }); this.guardrailName = guardrailName; this.timeoutMs = timeoutMs; } }; GuardrailConfigurationError = class extends GuardrailsError { name = "GuardrailConfigurationError"; code = "GUARDRAIL_CONFIG_INVALID"; configPath; configErrors; constructor(configErrors, configPath, metadata = {}) { const message = `Guardrail configuration error${configPath ? ` in ${configPath}` : ""}: ${configErrors.join(", ")}`; super(message, { ...metadata, configPath, configErrors }); this.configPath = configPath; this.configErrors = configErrors; } }; InputBlockedError = class extends GuardrailsError { name = "InputBlockedError"; code = "INPUT_BLOCKED"; blockedGuardrails; constructor(blockedGuardrails, metadata = {}) { const guardrailNames = blockedGuardrails.map((g) => g.name).join(", "); const message = `Input blocked by guardrail${blockedGuardrails.length > 1 ? "s" : ""}: ${guardrailNames}`; super(message, { ...metadata, blockedGuardrails }); this.blockedGuardrails = blockedGuardrails; } }; OutputBlockedError = class extends GuardrailsError { name = "OutputBlockedError"; code = "OUTPUT_BLOCKED"; blockedGuardrails; constructor(blockedGuardrails, metadata = {}) { const guardrailNames = blockedGuardrails.map((g) => g.name).join(", "); const message = `Output blocked by guardrail${blockedGuardrails.length > 1 ? "s" : ""}: ${guardrailNames}`; super(message, { ...metadata, blockedGuardrails }); this.blockedGuardrails = blockedGuardrails; } }; MiddlewareError = class extends GuardrailsError { name = "MiddlewareError"; code = "MIDDLEWARE_ERROR"; middlewareType; phase; originalError; constructor(middlewareType, phase, originalError, metadata = {}) { const message = originalError ? `${middlewareType} middleware ${phase} error: ${originalError.message}` : `${middlewareType} middleware ${phase} error`; super(message, { ...metadata, middlewareType, phase, originalError: originalError?.message }); this.middlewareType = middlewareType; this.phase = phase; this.originalError = originalError; } }; } }); // src/index.ts var index_exports = {}; __export(index_exports, { GuardrailConfigurationError: () => GuardrailConfigurationError, GuardrailExecutionError: () => GuardrailExecutionError, GuardrailTimeoutError: () => GuardrailTimeoutError, GuardrailValidationError: () => GuardrailValidationError, GuardrailsError: () => GuardrailsError, InputBlockedError: () => InputBlockedError, MiddlewareError: () => MiddlewareError, OutputBlockedError: () => OutputBlockedError, createInputGuardrail: () => createInputGuardrail, createInputGuardrailsMiddleware: () => createInputGuardrailsMiddleware, createOutputGuardrail: () => createOutputGuardrail, createOutputGuardrailsMiddleware: () => createOutputGuardrailsMiddleware, defineInputGuardrail: () => defineInputGuardrail, defineOutputGuardrail: () => defineOutputGuardrail, executeInputGuardrails: () => executeInputGuardrails, executeOutputGuardrails: () => executeOutputGuardrails, extractErrorInfo: () => extractErrorInfo, isGuardrailsError: () => isGuardrailsError, wrapWithGuardrails: () => wrapWithGuardrails, wrapWithInputGuardrails: () => wrapWithInputGuardrails, wrapWithOutputGuardrails: () => wrapWithOutputGuardrails }); module.exports = __toCommonJS(index_exports); // src/core.ts init_errors(); function createInputGuardrail(name, description, execute) { return { name, description, execute }; } function createOutputGuardrail(name, execute) { return { name, execute }; } // src/guardrails.ts var import_ai = require("ai"); function defineInputGuardrail(guardrail) { const enhanced = { enabled: true, priority: "medium", version: "1.0.0", tags: [], ...guardrail, execute: async (params) => { const startTime = Date.now(); const originalExecute = guardrail.execute; try { const result = await originalExecute(params); const executionTime = Date.now() - startTime; return { ...result, context: { guardrailName: guardrail.name, guardrailVersion: guardrail.version, executedAt: /* @__PURE__ */ new Date(), executionTimeMs: executionTime, ...result.context } }; } catch (error) { const executionTime = Date.now() - startTime; return { tripwireTriggered: true, message: `Guardrail execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, severity: "critical", context: { guardrailName: guardrail.name, guardrailVersion: guardrail.version, executedAt: /* @__PURE__ */ new Date(), executionTimeMs: executionTime }, metadata: { error: error instanceof Error ? error.message : String(error) } }; } } }; return enhanced; } async function executeInputGuardrails(guardrails, params, options = {}) { const { parallel = true, timeout = 3e4, // 30 seconds continueOnFailure = true, logLevel = "info" } = options; const enabledGuardrails = guardrails.filter((g) => g.enabled !== false).sort((a, b) => { const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 }; return (priorityOrder[b.priority || "medium"] || 2) - (priorityOrder[a.priority || "medium"] || 2); }); const results = []; const executeWithTimeout = async (guardrail) => { const timeoutPromise = new Promise((_, reject) => { setTimeout(async () => { const { GuardrailTimeoutError: GuardrailTimeoutError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports)); reject(new GuardrailTimeoutError2(guardrail.name, timeout)); }, timeout); }); const executionPromise = guardrail.execute(params); return Promise.race([executionPromise, timeoutPromise]); }; if (parallel) { const promises = enabledGuardrails.map(async (guardrail) => { try { const result = await executeWithTimeout(guardrail); if (result.tripwireTriggered && logLevel !== "none") { console.log( `Input guardrail "${guardrail.name}" triggered: ${result.message}` ); } return result; } catch (error) { if (logLevel !== "none") { console.error( `Error executing input guardrail "${guardrail.name}":`, error ); } return { tripwireTriggered: true, message: `Guardrail execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, severity: "critical", metadata: { error: error instanceof Error ? error.message : String(error) } }; } }); results.push(...await Promise.all(promises)); } else { for (const guardrail of enabledGuardrails) { try { const result = await executeWithTimeout(guardrail); results.push(result); if (result.tripwireTriggered) { if (logLevel !== "none") { console.log( `Input guardrail "${guardrail.name}" triggered: ${result.message}` ); } if (!continueOnFailure) { break; } } } catch (error) { if (logLevel !== "none") { console.error( `Error executing input guardrail "${guardrail.name}":`, error ); } const errorResult = { tripwireTriggered: true, message: `Guardrail execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, severity: "critical", metadata: { error: error instanceof Error ? error.message : String(error) } }; results.push(errorResult); if (!continueOnFailure) { break; } } } } return results; } function defineOutputGuardrail(guardrail) { const enhanced = { enabled: true, priority: "medium", version: "1.0.0", tags: [], ...guardrail, execute: async (params) => { const startTime = Date.now(); const originalExecute = guardrail.execute; try { const result = await originalExecute(params); const executionTime = Date.now() - startTime; return { ...result, context: { guardrailName: guardrail.name, guardrailVersion: guardrail.version, executedAt: /* @__PURE__ */ new Date(), executionTimeMs: executionTime, ...result.context } }; } catch (error) { const executionTime = Date.now() - startTime; return { tripwireTriggered: true, message: `Guardrail execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, severity: "critical", context: { guardrailName: guardrail.name, guardrailVersion: guardrail.version, executedAt: /* @__PURE__ */ new Date(), executionTimeMs: executionTime }, metadata: { error: error instanceof Error ? error.message : String(error) } }; } } }; return enhanced; } async function executeOutputGuardrails(guardrails, params, options = {}) { const { parallel = true, timeout = 3e4, // 30 seconds continueOnFailure = true, logLevel = "info" } = options; const enabledGuardrails = guardrails.filter((g) => g.enabled !== false).sort((a, b) => { const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 }; return (priorityOrder[b.priority || "medium"] || 2) - (priorityOrder[a.priority || "medium"] || 2); }); const results = []; const executeWithTimeout = async (guardrail) => { const timeoutPromise = new Promise((_, reject) => { setTimeout(async () => { const { GuardrailTimeoutError: GuardrailTimeoutError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports)); reject(new GuardrailTimeoutError2(guardrail.name, timeout)); }, timeout); }); const executionPromise = guardrail.execute(params); return Promise.race([executionPromise, timeoutPromise]); }; if (parallel) { const promises = enabledGuardrails.map(async (guardrail) => { try { const result = await executeWithTimeout(guardrail); if (result.tripwireTriggered && logLevel !== "none") { console.log( `Output guardrail "${guardrail.name}" triggered: ${result.message}` ); } return result; } catch (error) { if (logLevel !== "none") { console.error( `Error executing output guardrail "${guardrail.name}":`, error ); } return { tripwireTriggered: true, message: `Guardrail execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, severity: "critical", metadata: { error: error instanceof Error ? error.message : String(error) } }; } }); results.push(...await Promise.all(promises)); } else { for (const guardrail of enabledGuardrails) { try { const result = await executeWithTimeout(guardrail); results.push(result); if (result.tripwireTriggered) { if (logLevel !== "none") { console.log( `Output guardrail "${guardrail.name}" triggered: ${result.message}` ); } if (!continueOnFailure) { break; } } } catch (error) { if (logLevel !== "none") { console.error( `Error executing output guardrail "${guardrail.name}":`, error ); } const errorResult = { tripwireTriggered: true, message: `Guardrail execution failed: ${error instanceof Error ? error.message : "Unknown error"}`, severity: "critical", metadata: { error: error instanceof Error ? error.message : String(error) } }; results.push(errorResult); if (!continueOnFailure) { break; } } } } return results; } function extractTextFromContent(content) { return content.filter((part) => part.type === "text").map((part) => part.text || "").join(""); } function wrapWithInputGuardrails(model, guardrails, options) { const middleware = createInputGuardrailsMiddleware({ inputGuardrails: guardrails, ...options }); return (0, import_ai.wrapLanguageModel)({ model, middleware }); } function wrapWithOutputGuardrails(model, guardrails, options) { const middleware = createOutputGuardrailsMiddleware({ outputGuardrails: guardrails, ...options }); return (0, import_ai.wrapLanguageModel)({ model, middleware }); } function wrapWithGuardrails(model, config) { const { inputGuardrails = [], outputGuardrails = [], throwOnBlocked, executionOptions, onInputBlocked, onOutputBlocked } = config; const middlewares = []; if (inputGuardrails.length > 0) { middlewares.push( createInputGuardrailsMiddleware({ inputGuardrails, throwOnBlocked, executionOptions, onInputBlocked }) ); } if (outputGuardrails.length > 0) { middlewares.push( createOutputGuardrailsMiddleware({ outputGuardrails, throwOnBlocked, executionOptions, onOutputBlocked }) ); } if (middlewares.length === 0) { return model; } return (0, import_ai.wrapLanguageModel)({ model, middleware: middlewares }); } function createInputGuardrailsMiddleware(config) { const { inputGuardrails, executionOptions = {}, onInputBlocked, throwOnBlocked = false } = config; return { transformParams: async ({ params }) => { const enhancedParams = { ...params, guardrailsBlocked: void 0 }; const promptMessages = Array.isArray(enhancedParams.prompt) ? enhancedParams.prompt : []; const systemMessage = promptMessages.find((msg) => msg.role === "system"); const system = systemMessage && Array.isArray(systemMessage.content) ? extractTextFromContent(systemMessage.content) : ""; const messages = promptMessages.filter((msg) => msg.role !== "system").map((msg) => ({ role: msg.role, content: msg.content && Array.isArray(msg.content) ? extractTextFromContent(msg.content) : "" })); const prompt = messages.length === 1 && messages[0]?.role === "user" ? messages[0].content : messages.map((m) => m.content).join(" "); const guardrailContext = { prompt, messages, system, maxOutputTokens: enhancedParams.maxOutputTokens, temperature: enhancedParams.temperature }; const inputResults = await executeInputGuardrails( inputGuardrails, guardrailContext, executionOptions ); const blockedResults = inputResults.filter((r) => r.tripwireTriggered); if (blockedResults.length > 0) { if (onInputBlocked) { onInputBlocked(blockedResults, guardrailContext); } if (throwOnBlocked) { const { InputBlockedError: InputBlockedError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports)); const blockedGuardrails = blockedResults.map((r) => ({ name: r.context?.guardrailName || "unknown", message: r.message || "Blocked", severity: r.severity || "medium" })); throw new InputBlockedError2(blockedGuardrails); } enhancedParams.guardrailsBlocked = blockedResults; } return enhancedParams; }, wrapGenerate: async ({ doGenerate, params }) => { const paramsWithGuardrails = params; if (paramsWithGuardrails.guardrailsBlocked) { const blockedResults = paramsWithGuardrails.guardrailsBlocked; const blockedMessage = blockedResults.map((r) => r.message).join(", "); return { content: [ { type: "text", text: `[Input blocked: ${blockedMessage}]` } ], finishReason: "other", usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }, warnings: [] }; } return doGenerate(); }, wrapStream: async ({ doStream, params }) => { const paramsWithGuardrails = params; if (paramsWithGuardrails.guardrailsBlocked) { const blockedResults = paramsWithGuardrails.guardrailsBlocked; const blockedMessage = blockedResults.map((r) => r.message).join(", "); const stream = new ReadableStream({ start(controller) { controller.enqueue({ type: "text-delta", id: "1", delta: `[Input blocked: ${blockedMessage}]` }); controller.enqueue({ type: "finish", finishReason: "other", usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 } }); controller.close(); } }); return { stream }; } return doStream(); } }; } function createOutputGuardrailsMiddleware(config) { const { outputGuardrails, executionOptions = {}, onOutputBlocked, throwOnBlocked = false } = config; return { wrapGenerate: async ({ doGenerate, params }) => { const result = await doGenerate(); const promptMessages = Array.isArray(params.prompt) ? params.prompt : []; const systemMessage = promptMessages.find((msg) => msg.role === "system"); const system = systemMessage && Array.isArray(systemMessage.content) ? extractTextFromContent(systemMessage.content) : ""; const messages = promptMessages.filter((msg) => msg.role !== "system").map((msg) => ({ role: msg.role, content: msg.content && Array.isArray(msg.content) ? extractTextFromContent(msg.content) : "" })); const prompt = messages.length === 1 && messages[0]?.role === "user" ? messages[0].content : messages.map((m) => m.content).join(" "); const guardrailContext = { prompt, messages, system, maxOutputTokens: params.maxOutputTokens, temperature: params.temperature }; const outputContext = { input: guardrailContext, result }; const outputResults = await executeOutputGuardrails( outputGuardrails, outputContext, executionOptions ); const blockedResults = outputResults.filter((r) => r.tripwireTriggered); if (blockedResults.length > 0) { if (onOutputBlocked) { onOutputBlocked(blockedResults, guardrailContext, result); } if (throwOnBlocked) { const { OutputBlockedError: OutputBlockedError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports)); const blockedGuardrails = blockedResults.map((r) => ({ name: r.context?.guardrailName || "unknown", message: r.message || "Blocked", severity: r.severity || "medium" })); throw new OutputBlockedError2(blockedGuardrails); } } return result; }, wrapStream: async ({ doStream, params }) => { const streamResult = await doStream(); let accumulatedText = ""; const blockedChunks = []; const transformStream = new TransformStream({ transform(chunk) { if (chunk.type === "text-delta") { accumulatedText += chunk.delta || ""; } blockedChunks.push(chunk); }, async flush(controller) { const promptMessages = Array.isArray(params.prompt) ? params.prompt : []; const systemMessage = promptMessages.find( (msg) => msg.role === "system" ); const system = systemMessage && Array.isArray(systemMessage.content) ? extractTextFromContent(systemMessage.content) : ""; const messages = promptMessages.filter((msg) => msg.role !== "system").map((msg) => ({ role: msg.role, content: msg.content && Array.isArray(msg.content) ? extractTextFromContent(msg.content) : "" })); const prompt = messages.length === 1 && messages[0]?.role === "user" ? messages[0].content : messages.map((m) => m.content).join(" "); const guardrailContext = { prompt, messages, system, maxOutputTokens: params.maxOutputTokens, temperature: params.temperature }; const outputContext = { input: guardrailContext, result: { text: accumulatedText } }; const outputResults = await executeOutputGuardrails( outputGuardrails, outputContext, executionOptions ); const blockedResults = outputResults.filter( (r) => r.tripwireTriggered ); if (blockedResults.length > 0) { if (onOutputBlocked) { onOutputBlocked(blockedResults, guardrailContext, { text: accumulatedText }); } if (throwOnBlocked) { controller.error( new Error( `Output guardrails blocked response: ${blockedResults.map((r) => r.message).join(", ")}` ) ); return; } controller.enqueue({ type: "text-delta", id: "1", delta: "[Output blocked by guardrails]" }); controller.enqueue({ type: "finish", finishReason: "error", usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 } }); } else { for (const chunk of blockedChunks) { controller.enqueue(chunk); } } } }); return { stream: streamResult.stream.pipeThrough(transformStream) }; } }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { GuardrailConfigurationError, GuardrailExecutionError, GuardrailTimeoutError, GuardrailValidationError, GuardrailsError, InputBlockedError, MiddlewareError, OutputBlockedError, createInputGuardrail, createInputGuardrailsMiddleware, createOutputGuardrail, createOutputGuardrailsMiddleware, defineInputGuardrail, defineOutputGuardrail, executeInputGuardrails, executeOutputGuardrails, extractErrorInfo, isGuardrailsError, wrapWithGuardrails, wrapWithInputGuardrails, wrapWithOutputGuardrails });