UNPKG

ai-sdk-guardrails

Version:

Input and output guardrails middleware for Vercel AI SDK.

398 lines (390 loc) 14.8 kB
import { ToolSet } from 'ai'; import { R as RequestContext, O as OutputGuardrail, e as GuardrailExecutionSummary } from './types-C7t6e3EI.cjs'; /** * Tool Parameter Validation Guardrails * * Validates and sanitizes tool inputs BEFORE execution to prevent * dangerous operations like SQL injection, path traversal, etc. */ /** * Context provided to tool parameter validators */ interface ToolValidationContext<TContext = Record<string, unknown>> { /** Name of the tool being called */ toolName: string; /** The tool call ID */ toolCallId?: string; /** Request-scoped context (user, session, permissions) */ requestContext?: RequestContext<TContext>; } /** * Result of tool parameter validation */ interface ToolValidationResult<T = unknown> { /** Whether the input is valid */ valid: boolean; /** Optionally return sanitized/modified input */ sanitizedInput?: T; /** Error/block message if invalid */ message?: string; /** Whether to completely block the tool call */ block?: boolean; /** Severity of the issue */ severity?: 'low' | 'medium' | 'high' | 'critical'; /** Additional metadata about the validation */ metadata?: Record<string, unknown>; } /** * Tool parameter guardrail definition */ interface ToolParameterGuardrail<TInput = unknown, TContext = Record<string, unknown>> { /** Unique name for this guardrail */ name: string; /** Description of what this guardrail validates */ description?: string; /** Tool name(s) this guardrail applies to */ toolName: string | RegExp | string[]; /** Validation function */ validateInput: (input: TInput, context: ToolValidationContext<TContext>) => Promise<ToolValidationResult<TInput>> | ToolValidationResult<TInput>; } /** * Options for tool parameter guardrails wrapper */ interface ToolParameterGuardrailsOptions<TContext = Record<string, unknown>> { /** Request-scoped context */ requestContext?: RequestContext<TContext>; /** Whether to throw on validation failure (default: true) */ throwOnInvalid?: boolean; /** Callback when validation fails */ onValidationFailed?: (toolName: string, input: unknown, results: ToolValidationResult[]) => void; } /** * Wraps tools with parameter validation guardrails. * * This function intercepts tool calls and validates/sanitizes their inputs * BEFORE the tool executes, preventing dangerous operations. * * @example * ```typescript * const protectedTools = withToolParameterGuardrails( * { * executeSQL: tool({ * description: 'Execute SQL query', * parameters: z.object({ query: z.string() }), * execute: async ({ query }) => db.execute(query) * }), * readFile: tool({ * description: 'Read a file', * parameters: z.object({ path: z.string() }), * execute: async ({ path }) => fs.readFile(path, 'utf-8') * }) * }, * [ * { * name: 'sql-injection-prevention', * toolName: 'executeSQL', * validateInput: async (input) => { * if (containsSQLInjection(input.query)) { * return { valid: false, block: true, message: 'SQL injection detected' }; * } * return { valid: true, sanitizedInput: { query: escapeSql(input.query) } }; * } * }, * { * name: 'path-traversal-prevention', * toolName: 'readFile', * validateInput: async (input) => { * if (input.path.includes('..')) { * return { valid: false, block: true, message: 'Path traversal detected' }; * } * return { valid: true }; * } * } * ] * ); * ``` * * @deprecated For gating tool calls, prefer {@link guardrailApproval} with the * `toolApproval` option of `generateText` / `streamText` / `Agent` (AI SDK v7). * It runs inside the agent loop, so it can pause for human-in-the-loop approval * and resume — which wrapping the tool cannot. The same `ToolParameterGuardrail` * objects work with both. Keep using `withToolParameterGuardrails` only when you * need to *rewrite* tool input via `sanitizedInput` before execution, which * `toolApproval` (allow/deny only) does not do. This wrapper will be removed in a * future major. */ declare function withToolParameterGuardrails<TOOLS extends ToolSet, TContext = Record<string, unknown>>(tools: TOOLS, guardrails: ToolParameterGuardrail<unknown, TContext>[], options?: ToolParameterGuardrailsOptions<TContext>): TOOLS; /** * Error thrown when tool parameter validation fails */ declare class ToolParameterValidationError extends Error { readonly toolName: string; readonly guardrailName: string; readonly severity?: "low" | "medium" | "high" | "critical" | undefined; constructor(toolName: string, guardrailName: string, message: string, severity?: "low" | "medium" | "high" | "critical" | undefined); } /** * Creates a guardrail that prevents SQL injection attacks */ declare function sqlInjectionGuardrail(options?: { toolName?: string | string[]; patterns?: RegExp[]; }): ToolParameterGuardrail<{ query?: string; sql?: string; }>; /** * Creates a guardrail that prevents path traversal attacks */ declare function pathTraversalGuardrail(options?: { toolName?: string | string[]; allowedPaths?: string[]; blockedPatterns?: RegExp[]; }): ToolParameterGuardrail<{ path?: string; file?: string; filename?: string; }>; /** * Creates a guardrail that enforces parameter length limits */ declare function parameterLengthGuardrail(options?: { toolName?: string | string[]; maxLength?: number; fields?: string[]; }): ToolParameterGuardrail<Record<string, unknown>>; /** * Creates a role-based access control guardrail for tools */ declare function toolRBACGuardrail<TContext = Record<string, unknown>>(options: { toolName: string | string[]; requiredPermissions: string[]; mode?: 'any' | 'all'; }): ToolParameterGuardrail<unknown, TContext>; /** * System-prompt leak detection. Catches a model echoing, quoting, or * paraphrasing its own system prompt back to the user by measuring n-gram and * word overlap between the output and the system prompt, then optionally * redacting the leaked fragments. * * Distinct from `sensitiveDataFilter` / `secretRedaction` (which look for * secrets/PII): this looks for the *instructions themselves* leaking out. * * Detection algorithm adapted for this guardrail runtime. */ interface SystemPromptLeakResult { leaked: boolean; /** 0..1 confidence that the output reproduces the system prompt. */ confidence: number; /** Overlapping fragments found in the output. */ fragments: string[]; /** The output with leaked fragments replaced (when redaction runs). */ sanitized: string; } interface SystemPromptLeakOptions { /** * The system prompt to protect. When omitted, it is read from the guardrail * context (`context.input.system`), so the common case needs no config. */ systemPrompt?: string; /** Contiguous-token window used for exact-fragment matching. Default 4. */ ngramSize?: number; /** Confidence at or above which a match counts as a leak. Default 0.7. */ threshold?: number; /** Jaccard word-overlap that counts as a leak on its own. Default 0.25. */ wordOverlapThreshold?: number; /** Replacement for leaked fragments in `metadata.sanitized`. Default `[REDACTED]`. */ redactionText?: string; /** Severity reported when a leak trips. Default `high`. */ severity?: 'low' | 'medium' | 'high' | 'critical'; } type SystemPromptLeakMetadata = { confidence: number; fragments: string[]; /** Output with leaked fragments redacted — use this to replace the response. */ sanitized: string; }; /** * Pure detection: does `output` reproduce `systemPrompt`? Use this when you * want the verdict (and a redacted copy) without wiring a guardrail. */ declare function detectSystemPromptLeak(output: string, systemPrompt: string, options?: SystemPromptLeakOptions): SystemPromptLeakResult; /** * Output guardrail that trips when the model leaks its own system prompt. The * system prompt is taken from the call context by default, so a bare * `systemPromptLeakDetector()` works. The redacted output is provided on * `metadata.sanitized` — pair with `replaceOnBlocked` to swap it in. * * @example * const model = withGuardrails({ * model: openai('gpt-4o'), * outputGuardrails: [systemPromptLeakDetector()], * }); */ declare function systemPromptLeakDetector(options?: SystemPromptLeakOptions): OutputGuardrail<SystemPromptLeakMetadata>; /** * Utility functions for creating common guardrail-based stop conditions * Similar to AI SDK's stepCountIs, toolCallCountIs, etc. */ type GuardrailViolation = { step: number; summary: GuardrailExecutionSummary; } | { chunkIndex: number; summary: GuardrailExecutionSummary; }; /** * Creates a stop condition that triggers when a critical severity violation occurs * * Note: Only explicit 'critical' severity triggers this condition. Undefined severity * defaults to 'medium' and will not trigger. * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [piiGuardrail], * stopOnGuardrailViolation: hasCriticalViolation(), // guardrail violations * stopWhen: stepCountIs(10), // composed with the guardrail stop condition * }), * tools: { search: searchTool }, * }); * ``` */ declare function hasCriticalViolation(): (violations: GuardrailViolation[]) => boolean; /** * Creates a stop condition that triggers after N guardrail violations * * @param count - Number of violations before stopping * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [qualityGuardrail], * stopOnGuardrailViolation: isViolationCount(3), * }), * tools: { search: searchTool }, * }); * ``` */ declare function isViolationCount(count: number): (violations: GuardrailViolation[]) => boolean; /** * Creates a stop condition that triggers when violations of a specific severity occur * * Note: Guardrail severity defaults to 'medium' when not specified. This helper * treats undefined severity as 'medium' to match the documented behavior. * * @param severity - The severity level to check for * @param minCount - Minimum number of violations of this severity (default: 1) * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [securityGuardrail], * stopOnGuardrailViolation: hasViolationSeverity('high', 2), * }), * tools: { search: searchTool }, * }); * ``` */ declare function hasViolationSeverity(severity: 'low' | 'medium' | 'high' | 'critical', minCount?: number): (violations: GuardrailViolation[]) => boolean; /** * Creates a stop condition that triggers when a specific guardrail is violated * * @param guardrailName - The name of the guardrail to watch for * @param minCount - Minimum number of violations of this guardrail (default: 1) * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [piiGuardrail, qualityGuardrail], * stopOnGuardrailViolation: hasGuardrailViolation('pii-detection'), * }), * tools: { search: searchTool }, * }); * ``` */ declare function hasGuardrailViolation(guardrailName: string, minCount?: number): (violations: GuardrailViolation[]) => boolean; /** * Creates a stop condition that triggers when violations occur in consecutive steps * * @param consecutiveCount - Number of consecutive violations before stopping * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [qualityGuardrail], * stopOnGuardrailViolation: hasConsecutiveViolations(2), * }), * tools: { search: searchTool }, * }); * ``` */ declare function hasConsecutiveViolations(consecutiveCount: number): (violations: GuardrailViolation[]) => boolean; /** * Combines multiple stop conditions with OR logic * * @param conditions - Array of stop condition functions * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [piiGuardrail, qualityGuardrail], * stopOnGuardrailViolation: anyOf([ * hasCriticalViolation(), * isViolationCount(5), * hasConsecutiveViolations(3), * ]), * }), * tools: { search: searchTool }, * }); * ``` */ declare function anyOf(conditions: Array<(violations: GuardrailViolation[]) => boolean>): (violations: GuardrailViolation[]) => boolean; /** * Combines multiple stop conditions with AND logic * * @param conditions - Array of stop condition functions * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [piiGuardrail, qualityGuardrail], * stopOnGuardrailViolation: allOf([ * isViolationCount(3), * hasViolationSeverity('high'), * ]), * }), * tools: { search: searchTool }, * }); * ``` */ declare function allOf(conditions: Array<(violations: GuardrailViolation[]) => boolean>): (violations: GuardrailViolation[]) => boolean; /** * Creates a stop condition with a custom predicate * Useful for complex logic not covered by other helpers * * @param predicate - Custom function that receives violations and returns boolean * * @example * ```typescript * const agent = new ToolLoopAgent({ * ...agentGuardrails({ model, * outputGuardrails: [piiGuardrail, qualityGuardrail], * stopOnGuardrailViolation: custom((violations) => { * const avgSeverity = calculateAverageSeverity(violations); * return avgSeverity > 0.7; * }), * }), * tools: { search: searchTool }, * }); * ``` */ declare function custom(predicate: (violations: GuardrailViolation[]) => boolean): (violations: GuardrailViolation[]) => boolean; export { type GuardrailViolation as G, type SystemPromptLeakOptions as S, type ToolParameterGuardrail as T, type ToolValidationResult as a, type SystemPromptLeakMetadata as b, sqlInjectionGuardrail as c, parameterLengthGuardrail as d, type ToolValidationContext as e, hasViolationSeverity as f, hasGuardrailViolation as g, hasCriticalViolation as h, isViolationCount as i, hasConsecutiveViolations as j, anyOf as k, allOf as l, custom as m, detectSystemPromptLeak as n, type SystemPromptLeakResult as o, pathTraversalGuardrail as p, ToolParameterValidationError as q, type ToolParameterGuardrailsOptions as r, systemPromptLeakDetector as s, toolRBACGuardrail as t, withToolParameterGuardrails as w };