ai-sdk-guardrails 1.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,729 +1,651 @@
1
1
  # AI SDK Guardrails
2
2
 
3
- The safest way to build production AI applications with the Vercel AI SDK. A comprehensive TypeScript library that protects your AI systems with intelligent input and output validation, streaming safety, and enterprise-grade reliability.
3
+ A powerful middleware for the Vercel AI SDK that adds safety, quality control, and cost management to your AI applications by intercepting prompts and responses.
4
4
 
5
- [![npm version](https://badge.fury.io/js/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
6
- [![Downloads](https://img.shields.io/npm/dm/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
7
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ Block harmful inputs, filter low-quality outputs, and gain observability, all in just a few lines of code.
9
6
 
10
- ## Why AI SDK Guardrails?
7
+ ![Guardrails Demo](./media/guardrail-example.gif)
11
8
 
12
- Building AI applications is exciting, but deploying them safely in production requires careful consideration. This library provides the safety net you need without sacrificing developer experience or performance.
9
+ ## TL;DR
13
10
 
14
- ### 🛡️ Complete Protection
11
+ Quickly add input and output validation to any AI SDK-compatible model.
15
12
 
16
- Validate inputs before they reach your model and outputs before they reach users. Catch harmful content, PII, prompt injections, and more.
13
+ ```typescript
14
+ import { openai } from '@ai-sdk/openai';
15
+ import { generateText } from 'ai';
16
+ import {
17
+ wrapWithGuardrails,
18
+ defineInputGuardrail,
19
+ defineOutputGuardrail,
20
+ } from 'ai-sdk-guardrails';
17
21
 
18
- ### Real-time Streaming Safety
22
+ // 1. Define your guardrails
23
+ const inputGuard = defineInputGuardrail({
24
+ name: 'length-check',
25
+ execute: async ({ prompt }) =>
26
+ prompt.length > 100
27
+ ? { tripwireTriggered: true, message: 'Input too long' }
28
+ : { tripwireTriggered: false },
29
+ });
19
30
 
20
- The only guardrails library with true streaming support. Monitor and stop streams in real-time when safety violations occur.
31
+ const outputGuard = defineOutputGuardrail({
32
+ name: 'quality-check',
33
+ execute: async ({ result }) =>
34
+ result.text.length < 10
35
+ ? { tripwireTriggered: true, message: 'Response too short' }
36
+ : { tripwireTriggered: false },
37
+ });
21
38
 
22
- ### 🎯 Built for Vercel AI SDK
39
+ // 2. Wrap your model
40
+ const guardedModel = wrapWithGuardrails(openai('gpt-4o'), {
41
+ inputGuardrails: [inputGuard],
42
+ outputGuardrails: [outputGuard],
43
+ });
23
44
 
24
- First-class support for all AI SDK functions: `generateText`, `generateObject`, `streamText`, `streamObject`, and `embed`.
45
+ // 3. Use it! Guardrails will run automatically.
46
+ const { text } = await generateText({
47
+ model: guardedModel,
48
+ prompt: 'A prompt that is definitely not too long.',
49
+ });
50
+ ```
25
51
 
26
- ### 🔧 Developer Friendly
52
+ ## How It Works
27
53
 
28
- Simple API with TypeScript autocompletion, helpful error messages, and sensible defaults. Start safe in minutes, not hours.
54
+ ### Without Guardrails (Inefficient, Poor Quality)
29
55
 
30
- ## How It Works
56
+ ```mermaid
57
+ flowchart LR
58
+ A[User Input<br/>'hello'] --> B[AI Model] --> C[Response<br/>⚠️ Wastes resources<br/>😞 Often useless]
59
+ ```
60
+
61
+ ### With Input Guardrails (Save Resources)
31
62
 
32
63
  ```mermaid
33
- graph TB
34
- A[User Input] --> B{Input Guardrails}
35
- B -->|✅ Pass| C[AI Model]
36
- B -->|❌ Block| D[GuardrailError]
37
-
38
- C --> E[AI Response]
39
- E --> F{Output Guardrails}
40
- F -->|✅ Pass| G[Safe Response]
41
- F -->|❌ Block| H[GuardrailError]
42
-
43
- subgraph "Input Guardrails"
44
- I1[Length Limit]
45
- I2[Blocked Keywords]
46
- I3[PII Detection]
47
- I4[Prompt Injection]
48
- I5[Rate Limiting]
49
- end
64
+ flowchart LR
65
+ A[User Input<br/>'hello'] --> B[Input Guardrails] --> C[❌ STOPPED<br/>✅ No API call made]
66
+ ```
50
67
 
51
- subgraph "Output Guardrails"
52
- O1[Quality Check]
53
- O2[Toxicity Filter]
54
- O3[Privacy Leakage]
55
- O4[Schema Validation]
56
- O5[Confidence Threshold]
57
- end
68
+ ### With Output Guardrails (Ensure Quality)
58
69
 
59
- B -.-> I1
60
- B -.-> I2
61
- B -.-> I3
62
- B -.-> I4
63
- B -.-> I5
64
-
65
- F -.-> O1
66
- F -.-> O2
67
- F -.-> O3
68
- F -.-> O4
69
- F -.-> O5
70
-
71
- classDef inputNode fill:#e1f5fe
72
- classDef outputNode fill:#f3e5f5
73
- classDef blockNode fill:#ffebee
74
- classDef passNode fill:#e8f5e8
75
-
76
- class I1,I2,I3,I4,I5 inputNode
77
- class O1,O2,O3,O4,O5 outputNode
78
- class D,H blockNode
79
- class G passNode
70
+ ```mermaid
71
+ flowchart LR
72
+ A[AI Response<br/>'Here's my SSN: 123-45-6789'] --> B[Output Guardrails] --> C[❌ BLOCKED<br/>🛡️ Privacy protected]
80
73
  ```
81
74
 
82
- ## Installation
75
+ ### Complete Protection
83
76
 
84
- ```bash
85
- npm install ai-sdk-guardrails
86
- # or
87
- pnpm add ai-sdk-guardrails
88
- # or
89
- yarn add ai-sdk-guardrails
77
+ ```mermaid
78
+ flowchart LR
79
+ A[User Input] --> B[Input Guardrails] --> C[AI Model] --> D[Output Guardrails] --> E[Clean Response]
90
80
  ```
91
81
 
92
- ## Quick Start
82
+ That's it! Input guardrails optimize resource usage by stopping inefficient requests. Output guardrails ensure quality by filtering responses.
93
83
 
94
- Get started with production-ready guardrails in just a few lines:
84
+ ## 📦 Installation
95
85
 
96
- ```typescript
97
- import { generateTextWithGuardrails } from 'ai-sdk-guardrails';
98
- import { blockedKeywords } from 'ai-sdk-guardrails/guardrails/input';
99
- import { outputLengthLimit } from 'ai-sdk-guardrails/guardrails/output';
100
- import { openai } from '@ai-sdk/openai';
86
+ ```bash
87
+ npm install ai-sdk-guardrails
101
88
 
102
- const result = await generateTextWithGuardrails(
103
- {
104
- model: openai('gpt-4-turbo'),
105
- prompt: 'Write a helpful response about web security',
106
- },
107
- {
108
- inputGuardrails: [blockedKeywords(['hack', 'exploit', 'vulnerability'])],
109
- outputGuardrails: [outputLengthLimit(500)],
110
- },
111
- );
89
+ # or
112
90
 
113
- console.log(result.text);
114
- ```
91
+ yarn add ai-sdk-guardrails
92
+
93
+ # or
115
94
 
116
- ## Core Concepts
95
+ pnpm add ai-sdk-guardrails
96
+ ```
117
97
 
118
- ### Input Guardrails
98
+ ## 🚀 Quick Start
119
99
 
120
- Input guardrails validate prompts before they reach your AI model. Use them to:
100
+ Add smart validation to your AI applications in just 3 steps:
121
101
 
122
- - Block harmful or inappropriate content
123
- - Enforce length limits
124
- - Detect prompt injection attempts
125
- - Remove personally identifiable information (PII)
126
- - Implement rate limiting
102
+ ### 1. Prevent Unnecessary AI Calls
127
103
 
128
104
  ```typescript
129
- import { createInputGuardrail } from 'ai-sdk-guardrails';
130
-
131
- const mathHomeworkDetector = createInputGuardrail(
132
- 'math-homework-detector',
133
- 'Prevents direct homework solving requests',
134
- (context) => {
135
- const { prompt } = context;
136
- const homeworkPatterns = [
137
- /solve this equation/i,
138
- /what is \d+ [\+\-\*\/] \d+/i,
139
- /calculate the answer/i,
140
- ];
105
+ import { generateText } from 'ai';
106
+ import { openai } from '@ai-sdk/openai';
107
+ import {
108
+ wrapWithInputGuardrails,
109
+ defineInputGuardrail,
110
+ } from 'ai-sdk-guardrails';
111
+ import { extractTextContent } from 'ai-sdk-guardrails/guardrails/input';
141
112
 
142
- const isHomework = homeworkPatterns.some((pattern) =>
143
- pattern.test(prompt || ''),
113
+ // Block inefficient requests before calling the AI model
114
+ const lengthGuard = defineInputGuardrail({
115
+ name: 'blocked-keywords',
116
+ execute: async (context) => {
117
+ const { prompt } = extractTextContent(context);
118
+ const blockedWords = ['spam', 'test', 'hello'];
119
+
120
+ const foundWord = blockedWords.find((word) =>
121
+ prompt.toLowerCase().includes(word.toLowerCase()),
144
122
  );
145
123
 
146
- return {
147
- tripwireTriggered: isHomework,
148
- message: isHomework ? 'Direct homework solving detected' : undefined,
149
- suggestion: isHomework
150
- ? 'Try asking about the concepts instead'
151
- : undefined,
152
- };
124
+ if (foundWord) {
125
+ return {
126
+ tripwireTriggered: true,
127
+ message: `Blocked keyword detected: ${foundWord}`,
128
+ severity: 'medium',
129
+ };
130
+ }
131
+
132
+ return { tripwireTriggered: false };
153
133
  },
154
- );
155
- ```
134
+ });
156
135
 
157
- ### Output Guardrails
136
+ const optimizedModel = wrapWithInputGuardrails(openai('gpt-4'), {
137
+ inputGuardrails: [lengthGuard],
138
+ });
158
139
 
159
- Output guardrails validate AI responses before they reach users. Use them to:
140
+ // This would normally waste an API call for a useless response
141
+ try {
142
+ const result = await generateText({
143
+ model: optimizedModel,
144
+ prompt: 'hello', // ❌ Blocked - prevents unnecessary API call
145
+ });
146
+ } catch (error) {
147
+ console.log('Blocked request, saved money!');
148
+ }
149
+
150
+ // This generates valuable content
151
+ const goodResult = await generateText({
152
+ model: optimizedModel,
153
+ prompt: 'Write a product description for our new software', // ✅ This creates value
154
+ });
155
+ ```
160
156
 
161
- - Ensure response quality
162
- - Block sensitive information
163
- - Enforce formatting requirements
164
- - Validate against schemas
165
- - Check confidence levels
157
+ ### 2. Ensure Quality Output
166
158
 
167
159
  ```typescript
168
- import { createOutputGuardrail } from 'ai-sdk-guardrails';
160
+ import {
161
+ wrapWithOutputGuardrails,
162
+ defineOutputGuardrail,
163
+ } from 'ai-sdk-guardrails';
164
+ import { extractContent } from 'ai-sdk-guardrails/guardrails/output';
165
+
166
+ const qualityGuard = defineOutputGuardrail({
167
+ name: 'sensitive-info-detector',
168
+ execute: async (context) => {
169
+ const { text } = extractContent(context.result);
169
170
 
170
- const sensitiveInfoFilter = createOutputGuardrail(
171
- 'sensitive-info-filter',
172
- (context) => {
173
- const { text } = context.result;
171
+ // Simple sensitive info patterns
174
172
  const sensitivePatterns = [
175
- /password:\s*\w+/i,
176
- /api[_-]?key:\s*\w+/i,
177
- /\b\d{3}-\d{2}-\d{4}\b/, // SSN format
173
+ /\b\d{3}-\d{2}-\d{4}\b/, // SSN
174
+ /\b[\w\.-]+@[\w\.-]+\.\w+\b/, // Email
175
+ /\b\d{3}-\d{3}-\d{4}\b/, // Phone
178
176
  ];
179
177
 
180
- const hasSensitive = sensitivePatterns.some((pattern) =>
181
- pattern.test(text || ''),
178
+ const foundPattern = sensitivePatterns.find((pattern) =>
179
+ pattern.test(text),
182
180
  );
183
181
 
184
- return {
185
- tripwireTriggered: hasSensitive,
186
- message: hasSensitive
187
- ? 'Response contains sensitive information'
188
- : undefined,
189
- severity: hasSensitive ? 'critical' : 'low',
190
- };
182
+ if (foundPattern) {
183
+ return {
184
+ tripwireTriggered: true,
185
+ message: 'Sensitive information detected in response',
186
+ severity: 'high',
187
+ };
188
+ }
189
+
190
+ return { tripwireTriggered: false };
191
191
  },
192
- );
193
- ```
192
+ });
194
193
 
195
- ### Streaming Guardrails
194
+ const qualityModel = wrapWithOutputGuardrails(openai('gpt-4'), {
195
+ outputGuardrails: [qualityGuard],
196
+ onOutputBlocked: (results) => {
197
+ console.log('Prevented sensitive data leak:', results[0]?.message);
198
+ },
199
+ });
200
+
201
+ const result = await generateText({
202
+ model: qualityModel,
203
+ prompt: 'Create a user profile example',
204
+ });
205
+ // Automatically blocks responses containing emails, phone numbers, or SSNs
206
+ ```
196
207
 
197
- Protect streaming responses in real-time. The stream automatically stops if guardrails detect violations:
208
+ ### 3. Custom Business Logic
198
209
 
199
210
  ```typescript
200
- import { streamTextWithGuardrails } from 'ai-sdk-guardrails';
201
-
202
- const stream = await streamTextWithGuardrails(
203
- {
204
- model: openai('gpt-4-turbo'),
205
- prompt: 'Generate a long story...',
206
- },
207
- {
208
- outputGuardrails: [
209
- outputLengthLimit(1000),
210
- blockedOutputContent(['inappropriate', 'offensive']),
211
- ],
212
- onOutputBlocked: (error) => {
213
- console.error('Stream blocked:', error.reason);
214
- },
211
+ const businessHoursGuard = defineInputGuardrail({
212
+ name: 'business-hours-only',
213
+ execute: async () => {
214
+ const hour = new Date().getUTCHours();
215
+ // Only allow requests between 9 AM and 5 PM UTC
216
+ if (hour < 9 || hour > 17) {
217
+ return {
218
+ tripwireTriggered: true,
219
+ message:
220
+ 'Requests are only permitted during business hours (9:00-17:00 UTC).',
221
+ severity: 'low',
222
+ };
223
+ }
224
+ return { tripwireTriggered: false };
215
225
  },
216
- );
226
+ });
217
227
 
218
- // The stream will automatically stop if guardrails trigger
219
- for await (const chunk of stream.textStream) {
220
- process.stdout.write(chunk);
221
- }
228
+ const smartEducationModel = wrapWithInputGuardrails(openai('gpt-4'), {
229
+ inputGuardrails: [businessHoursGuard],
230
+ });
222
231
  ```
223
232
 
224
- ## Built-in Guardrails
233
+ **That's it!** Your AI application now optimizes resource usage, ensures quality, and prevents inappropriate responses automatically.
225
234
 
226
- ### Input Guardrails
235
+ ## Features
227
236
 
228
- ```typescript
229
- import {
230
- lengthLimit,
231
- blockedWords,
232
- blockedKeywords,
233
- profanityFilter,
234
- promptInjectionDetector,
235
- piiDetector,
236
- toxicityDetector,
237
- mathHomeworkDetector,
238
- codeGenerationLimiter,
239
- } from 'ai-sdk-guardrails/guardrails/input';
240
-
241
- // Content filters
242
- const contentGuardrails = [
243
- lengthLimit(1000),
244
- blockedWords(['spam', 'hack']),
245
- profanityFilter(['custom', 'words']),
246
- toxicityDetector(0.7), // threshold
247
- ];
248
-
249
- // Security guardrails
250
- const securityGuardrails = [promptInjectionDetector(), piiDetector()];
251
-
252
- // Educational guardrails
253
- const educationGuardrails = [
254
- mathHomeworkDetector(),
255
- codeGenerationLimiter(['javascript', 'python']),
256
- ];
257
- ```
237
+ - 🛡️ **Input & Output Guardrails**: Enforce custom safety, compliance, and quality policies on both prompts and LLM responses.
238
+ - 💰 **Cost Control**: Block invalid or wasteful prompts before they are sent to your LLM provider, saving you money.
239
+ - 🎯 **Quality Improvement**: Automatically filter, flag, or retry low-quality or irrelevant model outputs.
240
+ - 🔄 **Streaming Support**: Works seamlessly with both streaming (streamText) and standard (generateText) API responses.
241
+ - 📊 **Observability Hooks**: Built-in callbacks (onInputBlocked, onOutputBlocked, etc.) for logging and monitoring.
242
+ - ⚙️ **Configurable Execution**: Run guardrails in parallel or sequentially and set custom timeouts.
243
+ - 🚀 **AI SDK Native**: Designed from the ground up to integrate cleanly with AI SDK middleware patterns.
258
244
 
259
- ### Output Guardrails
245
+ ## 📚 API Overview
260
246
 
261
- ```typescript
262
- import {
263
- lengthLimit,
264
- blockedContent,
265
- jsonValidation,
266
- confidenceThreshold,
267
- toxicityFilter,
268
- schemaValidation,
269
- tokenUsageLimit,
270
- performanceMonitor,
271
- hallucinationDetector,
272
- biasDetector,
273
- factualAccuracyChecker,
274
- privacyLeakageDetector,
275
- contentConsistencyChecker,
276
- complianceChecker,
277
- } from 'ai-sdk-guardrails/guardrails/output';
278
-
279
- // Quality guardrails
280
- const qualityGuardrails = [
281
- lengthLimit(500),
282
- confidenceThreshold(0.8),
283
- hallucinationDetector(0.7),
284
- factualAccuracyChecker(true), // require sources
285
- ];
286
-
287
- // Safety guardrails
288
- const safetyGuardrails = [
289
- toxicityFilter(0.5),
290
- biasDetector(),
291
- privacyLeakageDetector(),
292
- complianceChecker(['GDPR', 'HIPAA']),
293
- ];
294
-
295
- // Performance guardrails
296
- const performanceGuardrails = [
297
- tokenUsageLimit(1000),
298
- performanceMonitor(5000), // max 5 seconds
299
- ];
300
- ```
247
+ | Function | Description |
248
+ | ---------------------------- | ----------------------------------------------------------------------------- |
249
+ | `defineInputGuardrail()` | Creates a guardrail to validate, inspect, or block prompts. |
250
+ | `defineOutputGuardrail()` | Creates a guardrail to validate, filter, or re-route LLM outputs. |
251
+ | `wrapWithGuardrails()` | ⭐ **Recommended** - The easiest way to add both input and output guardrails. |
252
+ | `wrapWithInputGuardrails()` | Attaches input-only guardrails to a model. |
253
+ | `wrapWithOutputGuardrails()` | Attaches output-only guardrails to a model. |
254
+ | `InputBlockedError`, etc. | Custom, structured error types for easy try/catch handling. |
301
255
 
302
- ## Integration with Autoevals
256
+ ## 🧠 Design Philosophy
303
257
 
304
- Use [Autoevals](https://github.com/braintrust-data/autoevals) for sophisticated AI quality evaluation as guardrails:
258
+ - ✅ **Helper-First**: Simple, chainable utility functions provide a great developer experience for fast adoption.
259
+ - 🧩 **Composable**: Multiple guardrails can be chained together and will run in your specified order (or in parallel).
260
+ - 🧾 **Type-Safe**: Full TypeScript support with contextual typing for guardrail inputs, outputs, and metadata.
261
+ - 🧪 **Sensible Defaults**: Get started quickly with zero-config default behaviors that can be easily overridden.
305
262
 
306
- ```typescript
307
- import { Factuality, init } from 'autoevals';
308
- import { createOutputGuardrail } from 'ai-sdk-guardrails';
309
-
310
- function createFactualityGuardrail({
311
- expected,
312
- minScore,
313
- }: {
314
- expected: string;
315
- minScore: number;
316
- }) {
317
- return createOutputGuardrail('factuality-check', async (context) => {
318
- const { text } = extractContent(context.result);
319
- const { prompt } = extractTextContent(context.input);
263
+ ## Architecture Overview
320
264
 
321
- const evalResult = await Factuality({
322
- output: text,
323
- expected,
324
- input: prompt || '',
325
- model: MODEL_NAME,
326
- });
265
+ The library leverages the Vercel AI SDK's middleware architecture to provide composable guardrails that integrate seamlessly with your existing AI applications:
327
266
 
328
- const isFactual = (evalResult.score || 0) >= minScore;
329
-
330
- return {
331
- tripwireTriggered: !isFactual,
332
- message: isFactual
333
- ? `Factual content (score: ${evalResult.score})`
334
- : `Factual accuracy too low (score: ${evalResult.score}, required: ${minScore})`,
335
- severity: isFactual ? 'low' : 'high',
336
- metadata: {
337
- factualityScore: evalResult.score,
338
- rationale: evalResult.metadata?.rationale,
339
- expected,
340
- minScore,
341
- },
342
- suggestion: isFactual
343
- ? undefined
344
- : 'Please provide more accurate information',
345
- };
346
- });
347
- }
267
+ ```mermaid
268
+ graph TB
269
+ subgraph "Your Application"
270
+ App[Your App Code]
271
+ Config[Guardrail Configuration]
272
+ end
348
273
 
349
- // Usage
350
- const result = await generateTextWithGuardrails(
351
- {
352
- model: openai('gpt-4-turbo'),
353
- prompt: 'Which country has the highest population?',
354
- },
355
- {
356
- outputGuardrails: [
357
- createFactualityGuardrail({
358
- expected: 'China',
359
- minScore: 0.7,
360
- }),
361
- ],
362
- },
363
- );
274
+ subgraph "AI SDK Guardrails Middleware"
275
+ InputMW[Input Guardrails Middleware]
276
+ OutputMW[Output Guardrails Middleware]
277
+
278
+ subgraph "Input Guardrails Layer"
279
+ Length[Length Validation]
280
+ Spam[Spam Detection]
281
+ PII[PII Detection]
282
+ Business[Business Rules]
283
+ Custom1[Custom Guards]
284
+ end
285
+
286
+ subgraph "Output Guardrails Layer"
287
+ Quality[Quality Assurance]
288
+ Sensitive[Sensitive Info Filter]
289
+ Professional[Professional Tone]
290
+ Factual[Factual Validation]
291
+ Custom2[Custom Guards]
292
+ end
293
+ end
294
+
295
+ subgraph "AI SDK Core"
296
+ Wrapper[wrapLanguageModel]
297
+ Generator[generateText/Object/Stream]
298
+ end
299
+
300
+ subgraph "External Services"
301
+ AI[AI Model Provider]
302
+ Log[Logging & Telemetry]
303
+ end
304
+
305
+ App --> Config
306
+ Config --> InputMW
307
+ InputMW --> Length
308
+ InputMW --> Spam
309
+ InputMW --> PII
310
+ InputMW --> Business
311
+ InputMW --> Custom1
312
+
313
+ InputMW -->|Valid Request| Wrapper
314
+ InputMW -->|Blocked Request| Log
315
+
316
+ Wrapper --> Generator
317
+ Generator --> AI
318
+ AI --> OutputMW
319
+
320
+ OutputMW --> Quality
321
+ OutputMW --> Sensitive
322
+ OutputMW --> Professional
323
+ OutputMW --> Factual
324
+ OutputMW --> Custom2
325
+
326
+ OutputMW -->|Clean Response| App
327
+ OutputMW -->|Quality Issues| Log
328
+
329
+ style InputMW fill:#e1f5fe
330
+ style OutputMW fill:#f3e5f5
331
+ style AI fill:#fff3e0
332
+ style App fill:#e8f5e8
364
333
  ```
365
334
 
366
- ## Error Handling
335
+ ## 🍳 Recipes & Use Cases
367
336
 
368
- Guardrails provide rich error information to help you handle violations gracefully:
337
+ Guardrails can enforce any custom logic. Here are a few common patterns.
369
338
 
370
- ```typescript
371
- import { GuardrailError } from 'ai-sdk-guardrails';
339
+ ### Rate Limiting
372
340
 
373
- try {
374
- const result = await generateTextWithGuardrails(params, guardrails);
375
- } catch (error) {
376
- if (error instanceof GuardrailError) {
377
- // Access detailed error information
378
- console.log('Guardrail triggered:', error.guardrailName);
379
- console.log('Reason:', error.reason);
380
- console.log('Type:', error.type); // 'input' or 'output'
381
-
382
- // Get all issues
383
- error.issues.forEach((issue) => {
384
- console.log(`${issue.guardrail}: ${issue.message}`);
385
- console.log('Severity:', issue.severity);
386
- console.log('Suggestion:', issue.suggestion);
387
- });
341
+ Pass a userId in the metadata of your generateText call to enforce per-user rate limits.
388
342
 
389
- // Use helper methods
390
- const summary = error.getSummary();
391
- console.log(`Total issues: ${summary.totalIssues}`);
392
- console.log(
393
- `Guardrails triggered: ${summary.guardrailsTriggered.join(', ')}`,
394
- );
395
- }
396
- }
343
+ ```typescript
344
+ const rateLimitGuard = defineInputGuardrail({
345
+ name: 'user-rate-limit',
346
+ execute: async ({ metadata }) => {
347
+ const userId = metadata?.userId ?? 'anonymous';
348
+ const allowed = await checkRateLimit(userId); // Your rate-limiting logic
349
+
350
+ return allowed
351
+ ? { tripwireTriggered: false }
352
+ : {
353
+ tripwireTriggered: true,
354
+ message: `Rate limit exceeded for user: ${userId}`,
355
+ };
356
+ },
357
+ });
397
358
  ```
398
359
 
399
- ## Advanced Usage
400
-
401
- ### Custom Validation Logic
360
+ ### LLM-as-Judge for Quality Scoring
402
361
 
403
- Create sophisticated guardrails for your specific needs:
362
+ Use a cheaper, faster model to "judge" the output of a more powerful one.
404
363
 
405
364
  ```typescript
406
- const businessLogicGuardrail = createInputGuardrail(
407
- 'business-logic-validator',
408
- 'Ensures requests meet business requirements',
409
- async (context) => {
410
- const { prompt, messages } = context;
411
-
412
- // Implement your custom logic
413
- const validationResult = await validateBusinessRules({
414
- content: prompt,
415
- history: messages,
365
+ const qualityJudge = defineOutputGuardrail({
366
+ name: 'llm-quality-judge',
367
+ execute: async ({ result }) => {
368
+ // Use a cheap model to score the primary model's output
369
+ const judgement = await generateText({
370
+ model: openai('gpt-3.5-turbo'),
371
+ prompt: `Is the following response helpful and safe? Answer YES or NO. \n\nResponse: "${result.text}"`,
416
372
  });
417
373
 
418
- return {
419
- tripwireTriggered: !validationResult.isValid,
420
- message: validationResult.error,
421
- metadata: validationResult.details,
422
- severity: validationResult.severity,
423
- suggestion: validationResult.suggestion,
424
- };
374
+ const isSafe = judgement.text.includes('YES');
375
+ return isSafe
376
+ ? { tripwireTriggered: false }
377
+ : {
378
+ tripwireTriggered: true,
379
+ message: `Output failed LLM-as-judge quality check.`,
380
+ metadata: { originalText: result.text },
381
+ };
425
382
  },
426
- );
383
+ });
427
384
  ```
428
385
 
429
- ### Composing Guardrails
430
-
431
- Combine multiple guardrail configurations for different scenarios:
386
+ ### Advanced Input Validation
432
387
 
433
388
  ```typescript
434
- // Development guardrails (more lenient)
435
- const devGuardrails = {
436
- inputGuardrails: [lengthLimit(2000)],
437
- throwOnBlocked: false,
438
- onInputBlocked: (error) => console.warn('Dev warning:', error),
439
- };
440
-
441
- // Production guardrails (strict)
442
- const prodGuardrails = {
443
- inputGuardrails: [lengthLimit(500), blockedKeywords(['test', 'debug'])],
444
- outputGuardrails: [confidenceThreshold(0.9), complianceChecker(['GDPR'])],
445
- throwOnBlocked: true,
446
- };
447
-
448
- // Use based on environment
449
- const guardrails =
450
- process.env.NODE_ENV === 'production' ? prodGuardrails : devGuardrails;
451
- ```
389
+ import { extractTextContent } from 'ai-sdk-guardrails/guardrails/input';
452
390
 
453
- ### Object Generation with Schema Validation
391
+ const comprehensiveInputGuard = defineInputGuardrail({
392
+ name: 'comprehensive-input-validation',
393
+ execute: async (context) => {
394
+ const { prompt } = extractTextContent(context);
454
395
 
455
- Validate structured outputs with custom business logic:
396
+ // Length validation
397
+ if (prompt.length < 10) {
398
+ return {
399
+ tripwireTriggered: true,
400
+ message: 'Input too short - likely to produce low-value response',
401
+ severity: 'medium',
402
+ suggestion: 'Please provide more detailed input for better results',
403
+ };
404
+ }
456
405
 
457
- ```typescript
458
- import { z } from 'zod';
406
+ if (prompt.length > 4000) {
407
+ return {
408
+ tripwireTriggered: true,
409
+ message: 'Input too long - may exceed token limits',
410
+ severity: 'high',
411
+ suggestion: 'Break your request into smaller, focused parts',
412
+ };
413
+ }
459
414
 
460
- const userSchema = z.object({
461
- name: z.string(),
462
- age: z.number().min(0).max(120),
463
- email: z.string().email(),
464
- });
415
+ // Content quality checks
416
+ const spamPatterns = [
417
+ /^(.)\1{10,}$/, // Repeated characters
418
+ /^(test|hello|hi|hey)$/i, // Common spam words
419
+ ];
465
420
 
466
- const schemaValidator = createOutputGuardrail(
467
- 'schema-validator',
468
- async (context) => {
469
- const { object } = extractContent(context.result);
470
- try {
471
- userSchema.parse(object);
472
- return { tripwireTriggered: false };
473
- } catch (error) {
421
+ const foundSpam = spamPatterns.find((pattern) => pattern.test(prompt));
422
+ if (foundSpam) {
474
423
  return {
475
424
  tripwireTriggered: true,
476
- message: 'Schema validation failed',
425
+ message: 'Low-quality input detected',
477
426
  severity: 'high',
478
- metadata: { validationError: error.message },
479
427
  };
480
428
  }
481
- },
482
- );
483
429
 
484
- const result = await generateObjectWithGuardrails(
485
- {
486
- model: openai('gpt-4-turbo'),
487
- prompt: 'Create a user profile for John Doe, age 30',
488
- schema: userSchema,
489
- },
490
- {
491
- outputGuardrails: [schemaValidator],
430
+ return { tripwireTriggered: false };
492
431
  },
493
- );
494
- ```
495
-
496
- ## Best Practices
497
-
498
- ### 1. Layer Your Defence
499
-
500
- Use multiple guardrails for comprehensive protection:
501
-
502
- ```typescript
503
- const guardrails = {
504
- inputGuardrails: [
505
- // First line: Block obvious threats
506
- promptInjectionDetector(),
507
- blockedKeywords(['malicious', 'exploit']),
508
-
509
- // Second line: Quality control
510
- lengthLimit(1000),
511
-
512
- // Third line: Business logic
513
- customBusinessRules(),
514
- ],
515
- outputGuardrails: [
516
- // Ensure quality
517
- confidenceThreshold(0.7),
518
-
519
- // Ensure safety
520
- toxicityFilter(),
521
- privacyLeakageDetector(),
522
- ],
523
- };
432
+ });
524
433
  ```
525
434
 
526
- ### 2. Handle Errors Gracefully
527
-
528
- Provide helpful feedback when guardrails trigger:
435
+ ### Professional Output Quality Control
529
436
 
530
437
  ```typescript
531
- onInputBlocked: (error) => {
532
- // Map technical errors to user-friendly messages
533
- const userMessage =
534
- {
535
- 'content-length-limit': 'Your message is too long. Please shorten it.',
536
- 'blocked-keywords': 'Your request contains restricted content.',
537
- 'pii-detector': 'Please remove personal information from your request.',
538
- }[error.guardrailName] || 'Your request could not be processed.';
539
-
540
- return userMessage;
541
- };
542
- ```
543
-
544
- ### 3. **Publishing Process (Using Changesets)**
438
+ import { extractContent } from 'ai-sdk-guardrails/guardrails/output';
545
439
 
546
- You're already set up with **Changesets** which is the recommended approach. Here's the process:
547
-
548
- #### **Step 1: Create a Changeset**
549
-
550
- ```bash
551
- npx changeset
552
- ```
553
-
554
- This will:
440
+ const professionalQualityGuard = defineOutputGuardrail({
441
+ name: 'professional-quality-control',
442
+ execute: async (context) => {
443
+ const { text } = extractContent(context.result);
555
444
 
556
- - Ask you what type of change (patch/minor/major)
557
- - Let you write a summary of changes
558
- - Create a changeset file
445
+ const qualityIssues = [];
559
446
 
560
- #### **Step 2: Version and Publish**
447
+ // Check for unprofessional language
448
+ const unprofessionalTerms = ['lol', 'wtf', 'omg', 'ur', 'u r'];
449
+ const hasUnprofessional = unprofessionalTerms.some((term) =>
450
+ text.toLowerCase().includes(term),
451
+ );
561
452
 
562
- You have two options:
453
+ if (hasUnprofessional) {
454
+ qualityIssues.push('Contains unprofessional language');
455
+ }
563
456
 
564
- **Option A: Manual Control**
457
+ // Check for placeholder text
458
+ const placeholders = ['[insert', '[add', '[your', 'TODO:', 'FIXME:'];
459
+ const hasPlaceholders = placeholders.some((placeholder) =>
460
+ text.includes(placeholder),
461
+ );
565
462
 
566
- ```bash
567
- # 1. Run CI to ensure everything passes
568
- npm run ci
463
+ if (hasPlaceholders) {
464
+ qualityIssues.push('Contains placeholder text - incomplete response');
465
+ }
569
466
 
570
- # 2. Version the package (reads changesets and updates version)
571
- npx changeset version
467
+ // Check for excessive repetition
468
+ const sentences = text.split(/[.!?]+/).filter((s) => s.trim());
469
+ const uniqueSentences = new Set(
470
+ sentences.map((s) => s.trim().toLowerCase()),
471
+ );
472
+ const repetitionRatio = uniqueSentences.size / sentences.length;
572
473
 
573
- # 3. Publish to npm
574
- npx changeset publish
575
- ```
474
+ if (sentences.length > 3 && repetitionRatio < 0.6) {
475
+ qualityIssues.push('Excessive repetition detected');
476
+ }
576
477
 
577
- **Option B: Use Your Local Release Script**
478
+ if (qualityIssues.length > 0) {
479
+ return {
480
+ tripwireTriggered: true,
481
+ message: `Quality issues found: ${qualityIssues.join(', ')}`,
482
+ severity: 'medium',
483
+ suggestion: 'Request a more professional, complete response',
484
+ metadata: {
485
+ issues: qualityIssues,
486
+ quality_score: repetitionRatio,
487
+ },
488
+ };
489
+ }
578
490
 
579
- ```bash
580
- npm run local-release
491
+ return { tripwireTriggered: false };
492
+ },
493
+ });
581
494
  ```
582
495
 
583
- This runs: `npm run ci && changeset version && changeset publish`
584
-
585
- ### 4. **First Release Steps**
496
+ ## 🔄 Streaming Support
586
497
 
587
- For your first release, I recommend:
498
+ Guardrails work with streams out-of-the-box. Output guardrails will run after the complete response has been streamed and generated.
588
499
 
589
- 1. **Fix any remaining issues** (like the prettier formatting):
590
-
591
- ```bash
592
- npm run format
593
- ```
594
-
595
- 2. **Create your first changeset**:
500
+ ```typescript
501
+ import { streamText } from 'ai';
596
502
 
597
- ```bash
598
- npx changeset
599
- ```
503
+ const guardedModel = wrapWithGuardrails(openai('gpt-4o'), {
504
+ outputGuardrails: [qualityJudge],
505
+ });
600
506
 
601
- - Select "major" (since this is 0.0.1 → 1.0.0)
602
- - Write: "Initial release of AI SDK Guardrails"
507
+ const { textStream } = await streamText({
508
+ model: guardedModel,
509
+ prompt: 'Tell me a short story about a robot.',
510
+ });
603
511
 
604
- 3. **Release it**:
512
+ // Stream the response to the client
513
+ for await (const delta of textStream) {
514
+ process.stdout.write(delta);
515
+ }
605
516
 
606
- ```bash
607
- npm run local-release
517
+ // The qualityJudge guardrail will run after the stream is complete.
608
518
  ```
609
519
 
610
- ### 5. **Package Name Assessment**
611
-
612
- **✅ `ai-sdk-guardrails` is excellent because:**
613
-
614
- - Available on npm
615
- - Descriptive and searchable
616
- - Follows naming conventions
617
- - Clearly indicates it's for AI SDK
618
- - Professional sounding
520
+ ## 🛠️ Error Handling
619
521
 
620
- ### 6. **Additional Recommendations**
522
+ When `throwOnBlocked: true` (the default), you can catch structured errors to handle blocks gracefully.
621
523
 
622
- Consider these npm badges for your README:
524
+ ```typescript
525
+ import { generateText } from 'ai';
526
+ import { isGuardrailsError } from 'ai-sdk-guardrails';
623
527
 
624
- ```markdown
625
- <code_block_to_apply_changes_from>
626
- [![npm version](https://badge.fury.io/js/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
627
- [![Downloads](https://img.shields.io/npm/dm/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
528
+ try {
529
+ const result = await generateText({
530
+ model: guardedModel,
531
+ prompt: 'A prompt that might be blocked...',
532
+ });
533
+ } catch (error) {
534
+ if (isGuardrailsError(error)) {
535
+ // Error was thrown by one of our guardrails
536
+ console.error('Guardrail check failed:', error.message);
537
+ console.error('Triggered Guards:', error.results);
538
+ } else {
539
+ // Some other error occurred
540
+ console.error('An unexpected error occurred:', error);
541
+ }
542
+ }
628
543
  ```
629
544
 
630
- ### 7. **GitHub Release Integration**
631
-
632
- After publishing to npm, you might want to:
545
+ ### User-Friendly Error Messages
633
546
 
634
- - Create GitHub releases that match your npm versions
635
- - Set up GitHub Actions for automated publishing (optional)
547
+ Transform technical guardrail messages into user-friendly guidance:
636
548
 
637
- **Would you like me to help you run through the first release process now?**
549
+ ```typescript
550
+ function createUserFriendlyMessage(guardrailResult): string {
551
+ const guardrailName = guardrailResult.context?.guardrailName;
638
552
 
639
- ## All AI SDK Functions Supported
553
+ switch (guardrailName) {
554
+ case 'content-length-limit':
555
+ return 'Your message is too long. Please keep it under 500 characters for the best response.';
640
556
 
641
- The library provides guarded versions of all AI SDK functions:
557
+ case 'blocked-keywords':
558
+ return "I can't help with that topic. Try asking about something else I can assist with.";
642
559
 
643
- ```typescript
644
- import {
645
- generateTextWithGuardrails,
646
- generateObjectWithGuardrails,
647
- streamTextWithGuardrails,
648
- streamObjectWithGuardrails,
649
- embedWithGuardrails,
650
- } from 'ai-sdk-guardrails';
560
+ case 'user-rate-limit':
561
+ return "You're sending requests too quickly. Please wait a moment before trying again.";
651
562
 
652
- // Generate text with guardrails
653
- const textResult = await generateTextWithGuardrails(
654
- { model, prompt: 'Hello' },
655
- { inputGuardrails: [lengthLimit(100)] },
656
- );
657
-
658
- // Generate structured objects with guardrails
659
- const objectResult = await generateObjectWithGuardrails(
660
- { model, prompt: 'Create user', schema: userSchema },
661
- { outputGuardrails: [schemaValidation(userSchema)] },
662
- );
663
-
664
- // Stream text with real-time guardrails
665
- const textStream = await streamTextWithGuardrails(
666
- { model, prompt: 'Long response' },
667
- { outputGuardrails: [outputLengthLimit(1000)] },
668
- );
669
-
670
- // Stream objects with guardrails
671
- const objectStream = await streamObjectWithGuardrails(
672
- { model, prompt: 'Stream data', schema: dataSchema },
673
- { outputGuardrails: [schemaValidation(dataSchema)] },
674
- );
675
-
676
- // Embed with input validation
677
- const embedResult = await embedWithGuardrails(
678
- { model, value: 'Text to embed' },
679
- { inputGuardrails: [piiDetector()] },
680
- );
563
+ default:
564
+ return (
565
+ guardrailResult.suggestion ||
566
+ 'Please refine your request and try again.'
567
+ );
568
+ }
569
+ }
681
570
  ```
682
571
 
683
- ## TypeScript Support
572
+ ## Complete AI SDK Integration
684
573
 
685
- Full TypeScript support with intelligent type inference:
574
+ The library seamlessly integrates with all AI SDK functions:
686
575
 
687
576
  ```typescript
688
- // Types are automatically inferred
689
- const result = await generateTextWithGuardrails(
690
- {
691
- model: openai('gpt-4-turbo'),
692
- prompt: 'Hello',
577
+ // Create your production-ready model once
578
+ const productionModel = wrapWithGuardrails(openai('gpt-4'), {
579
+ inputGuardrails: [lengthGuard, spamGuard, rateLimitGuard],
580
+ outputGuardrails: [qualityGuard, sensitiveInfoGuard],
581
+ throwOnBlocked: false,
582
+ onInputBlocked: (results) => {
583
+ console.log('Input blocked:', results[0]?.message);
693
584
  },
694
- {
695
- inputGuardrails: [
696
- /* your guardrails */
697
- ],
585
+ onOutputBlocked: (results) => {
586
+ console.log('Output filtered:', results[0]?.message);
698
587
  },
699
- );
588
+ });
589
+
590
+ // Use with any AI SDK function
591
+ const textResult = await generateText({
592
+ model: productionModel,
593
+ prompt: 'Write a professional email response',
594
+ });
595
+
596
+ const objectResult = await generateObject({
597
+ model: productionModel,
598
+ prompt: 'Create a user profile',
599
+ schema: userProfileSchema,
600
+ });
700
601
 
701
- // result is fully typed as GenerateTextResult
702
- console.log(result.text);
703
- console.log(result.usage);
704
- console.log(result.finishReason);
602
+ const textStream = await streamText({
603
+ model: productionModel,
604
+ prompt: 'Explain our product features',
605
+ });
705
606
  ```
706
607
 
707
608
  ## Examples
708
609
 
709
- Explore our comprehensive examples that demonstrate real-world usage patterns:
610
+ Explore focused examples that demonstrate practical performance optimization and quality assurance:
611
+
612
+ ### Core Examples
613
+
614
+ - **[Basic Composition](examples/basic-composition.ts)** - Simple input/output validation for efficiency and quality
615
+ - **[Basic Guardrails](examples/basic-guardrails.ts)** - Foundation patterns for input/output validation
616
+ - **[Business Logic](examples/business-logic.ts)** - Custom business rules, work hours, and professional standards
617
+ - **[LLM-as-Judge](examples/llm-as-judge.ts)** - AI-powered quality evaluation and scoring
710
618
 
711
- - **[Basic Guardrails](examples/basic-guardrails.ts)** - Simple input/output validation with math homework detection
712
- - **[Streaming Guardrails](examples/streaming-guardrails.ts)** - Real-time stream protection with content filtering
713
- - **[Object Guardrails](examples/object-guardrails.ts)** - Schema validation and custom object validation
714
- - **[Autoevals Integration](examples/autoevals-guardrails.ts)** - AI quality evaluation with factuality checking
715
- - **[Kitchen Sink](examples/kitchen-sink.ts)** - Advanced patterns, composition, and LLM-as-judge
619
+ ### Additional Examples
716
620
 
717
- Each example is fully functional and demonstrates different aspects of the library with practical use cases.
621
+ - **[Object Guardrails](examples/object-guardrails.ts)** - Schema validation and structured output quality
622
+ - **[Streaming Guardrails](examples/streaming-guardrails.ts)** - Real-time quality monitoring
623
+ - **[Rate Limiting](examples/rate-limit-guardrail.ts)** - Smart rate limiting that prevents resource overuse
624
+ - **[Autoevals Integration](examples/autoevals-guardrails.ts)** - Advanced AI-powered evaluation
718
625
 
719
- ## Contributing
626
+ ### Running Examples
627
+
628
+ ```bash
629
+ # Install dependencies
630
+ pnpm install
631
+
632
+ # Interactive examples with better UX
633
+ tsx examples/basic-composition.ts # Start here - simplest example
634
+ tsx examples/basic-guardrails.ts # Core patterns with 8 examples
635
+ tsx examples/business-logic.ts # Business-specific rules
636
+ tsx examples/llm-as-judge.ts # AI-powered quality control
637
+
638
+ # Or run specific examples directly
639
+ tsx examples/basic-guardrails.ts 1 # Run first example only
640
+ tsx examples/streaming-guardrails.ts 3 # Run third streaming example
641
+ ```
720
642
 
721
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
643
+ All examples feature interactive menus with arrow key navigation, multi-selection with checkboxes, and automatic return to the main menu.
722
644
 
723
- ## License
645
+ ## 🤝 Contributing
724
646
 
725
- MIT © [Jag Reehal](https://github.com/jagreehal)
647
+ Contributions of all sizes are welcome! Please open issues and pull requests on [GitHub](https://github.com/jagreehal/ai-sdk-guardrails).
726
648
 
727
- ---
649
+ ## 📄 License
728
650
 
729
- Built with ❤️ for the AI community. Star the repo if you find it helpful!
651
+ MIT © [Jag Reehal](https://github.com/jagreehal) See LICENSE for full details.