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 +499 -577
- package/dist/chunk-HHQ3CIFN.js +12 -0
- package/dist/chunk-LLCOPUS6.js +159 -0
- package/dist/errors-BTTWMQEI.js +24 -0
- package/dist/guardrails/input.cjs +15 -9
- package/dist/guardrails/input.d.cts +3 -3
- package/dist/guardrails/input.d.ts +3 -3
- package/dist/guardrails/input.js +17 -9
- package/dist/guardrails/output.cjs +31 -7
- package/dist/guardrails/output.d.cts +2 -2
- package/dist/guardrails/output.d.ts +2 -2
- package/dist/guardrails/output.js +33 -7
- package/dist/index.cjs +756 -186
- package/dist/index.d.cts +271 -1
- package/dist/index.d.ts +271 -1
- package/dist/index.js +601 -14
- package/dist/{index-CWIb12lh.d.cts → types-B9h_0Gyl.d.cts} +31 -31
- package/dist/{index-CWIb12lh.d.ts → types-B9h_0Gyl.d.ts} +31 -31
- package/package.json +19 -15
- package/dist/chunk-BNGJDZMX.js +0 -218
package/README.md
CHANGED
|
@@ -1,729 +1,651 @@
|
|
|
1
1
|
# AI SDK Guardrails
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
6
|
-
[](https://www.npmjs.com/package/ai-sdk-guardrails)
|
|
7
|
-
[](https://www.typescriptlang.org/)
|
|
8
|
-
[](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
|
-
|
|
7
|
+

|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
## ⚡ TL;DR
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
Quickly add input and output validation to any AI SDK-compatible model.
|
|
15
12
|
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
+
// 2. Wrap your model
|
|
40
|
+
const guardedModel = wrapWithGuardrails(openai('gpt-4o'), {
|
|
41
|
+
inputGuardrails: [inputGuard],
|
|
42
|
+
outputGuardrails: [outputGuard],
|
|
43
|
+
});
|
|
23
44
|
|
|
24
|
-
|
|
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
|
-
|
|
52
|
+
## How It Works
|
|
27
53
|
|
|
28
|
-
|
|
54
|
+
### Without Guardrails (Inefficient, Poor Quality)
|
|
29
55
|
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
A[User Input] --> B
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
B
|
|
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
|
-
|
|
75
|
+
### Complete Protection
|
|
83
76
|
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
82
|
+
That's it! Input guardrails optimize resource usage by stopping inefficient requests. Output guardrails ensure quality by filtering responses.
|
|
93
83
|
|
|
94
|
-
|
|
84
|
+
## 📦 Installation
|
|
95
85
|
|
|
96
|
-
```
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
91
|
+
yarn add ai-sdk-guardrails
|
|
92
|
+
|
|
93
|
+
# or
|
|
115
94
|
|
|
116
|
-
|
|
95
|
+
pnpm add ai-sdk-guardrails
|
|
96
|
+
```
|
|
117
97
|
|
|
118
|
-
|
|
98
|
+
## 🚀 Quick Start
|
|
119
99
|
|
|
120
|
-
|
|
100
|
+
Add smart validation to your AI applications in just 3 steps:
|
|
121
101
|
|
|
122
|
-
|
|
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 {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
136
|
+
const optimizedModel = wrapWithInputGuardrails(openai('gpt-4'), {
|
|
137
|
+
inputGuardrails: [lengthGuard],
|
|
138
|
+
});
|
|
158
139
|
|
|
159
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
171
|
-
'sensitive-info-filter',
|
|
172
|
-
(context) => {
|
|
173
|
-
const { text } = context.result;
|
|
171
|
+
// Simple sensitive info patterns
|
|
174
172
|
const sensitivePatterns = [
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
/\b\d{3}-\d{
|
|
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
|
|
181
|
-
pattern.test(text
|
|
178
|
+
const foundPattern = sensitivePatterns.find((pattern) =>
|
|
179
|
+
pattern.test(text),
|
|
182
180
|
);
|
|
183
181
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
:
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
+
### 3. Custom Business Logic
|
|
198
209
|
|
|
199
210
|
```typescript
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
228
|
+
const smartEducationModel = wrapWithInputGuardrails(openai('gpt-4'), {
|
|
229
|
+
inputGuardrails: [businessHoursGuard],
|
|
230
|
+
});
|
|
222
231
|
```
|
|
223
232
|
|
|
224
|
-
|
|
233
|
+
**That's it!** Your AI application now optimizes resource usage, ensures quality, and prevents inappropriate responses automatically.
|
|
225
234
|
|
|
226
|
-
|
|
235
|
+
## ✨ Features
|
|
227
236
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
245
|
+
## 📚 API Overview
|
|
260
246
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
##
|
|
256
|
+
## 🧠 Design Philosophy
|
|
303
257
|
|
|
304
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
##
|
|
335
|
+
## 🍳 Recipes & Use Cases
|
|
367
336
|
|
|
368
|
-
Guardrails
|
|
337
|
+
Guardrails can enforce any custom logic. Here are a few common patterns.
|
|
369
338
|
|
|
370
|
-
|
|
371
|
-
import { GuardrailError } from 'ai-sdk-guardrails';
|
|
339
|
+
### Rate Limiting
|
|
372
340
|
|
|
373
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
400
|
-
|
|
401
|
-
### Custom Validation Logic
|
|
360
|
+
### LLM-as-Judge for Quality Scoring
|
|
402
361
|
|
|
403
|
-
|
|
362
|
+
Use a cheaper, faster model to "judge" the output of a more powerful one.
|
|
404
363
|
|
|
405
364
|
```typescript
|
|
406
|
-
const
|
|
407
|
-
'
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
###
|
|
430
|
-
|
|
431
|
-
Combine multiple guardrail configurations for different scenarios:
|
|
386
|
+
### Advanced Input Validation
|
|
432
387
|
|
|
433
388
|
```typescript
|
|
434
|
-
|
|
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
|
-
|
|
391
|
+
const comprehensiveInputGuard = defineInputGuardrail({
|
|
392
|
+
name: 'comprehensive-input-validation',
|
|
393
|
+
execute: async (context) => {
|
|
394
|
+
const { prompt } = extractTextContent(context);
|
|
454
395
|
|
|
455
|
-
|
|
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
|
-
|
|
458
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
|
467
|
-
|
|
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: '
|
|
425
|
+
message: 'Low-quality input detected',
|
|
477
426
|
severity: 'high',
|
|
478
|
-
metadata: { validationError: error.message },
|
|
479
427
|
};
|
|
480
428
|
}
|
|
481
|
-
},
|
|
482
|
-
);
|
|
483
429
|
|
|
484
|
-
|
|
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
|
-
###
|
|
527
|
-
|
|
528
|
-
Provide helpful feedback when guardrails trigger:
|
|
435
|
+
### Professional Output Quality Control
|
|
529
436
|
|
|
530
437
|
```typescript
|
|
531
|
-
|
|
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
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
|
|
557
|
-
- Let you write a summary of changes
|
|
558
|
-
- Create a changeset file
|
|
445
|
+
const qualityIssues = [];
|
|
559
446
|
|
|
560
|
-
|
|
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
|
-
|
|
453
|
+
if (hasUnprofessional) {
|
|
454
|
+
qualityIssues.push('Contains unprofessional language');
|
|
455
|
+
}
|
|
563
456
|
|
|
564
|
-
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
463
|
+
if (hasPlaceholders) {
|
|
464
|
+
qualityIssues.push('Contains placeholder text - incomplete response');
|
|
465
|
+
}
|
|
569
466
|
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
|
|
574
|
-
|
|
575
|
-
|
|
474
|
+
if (sentences.length > 3 && repetitionRatio < 0.6) {
|
|
475
|
+
qualityIssues.push('Excessive repetition detected');
|
|
476
|
+
}
|
|
576
477
|
|
|
577
|
-
|
|
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
|
-
|
|
580
|
-
|
|
491
|
+
return { tripwireTriggered: false };
|
|
492
|
+
},
|
|
493
|
+
});
|
|
581
494
|
```
|
|
582
495
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
### 4. **First Release Steps**
|
|
496
|
+
## 🔄 Streaming Support
|
|
586
497
|
|
|
587
|
-
|
|
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
|
-
|
|
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
|
-
|
|
598
|
-
|
|
599
|
-
|
|
503
|
+
const guardedModel = wrapWithGuardrails(openai('gpt-4o'), {
|
|
504
|
+
outputGuardrails: [qualityJudge],
|
|
505
|
+
});
|
|
600
506
|
|
|
601
|
-
|
|
602
|
-
|
|
507
|
+
const { textStream } = await streamText({
|
|
508
|
+
model: guardedModel,
|
|
509
|
+
prompt: 'Tell me a short story about a robot.',
|
|
510
|
+
});
|
|
603
511
|
|
|
604
|
-
|
|
512
|
+
// Stream the response to the client
|
|
513
|
+
for await (const delta of textStream) {
|
|
514
|
+
process.stdout.write(delta);
|
|
515
|
+
}
|
|
605
516
|
|
|
606
|
-
|
|
607
|
-
npm run local-release
|
|
517
|
+
// The qualityJudge guardrail will run after the stream is complete.
|
|
608
518
|
```
|
|
609
519
|
|
|
610
|
-
|
|
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
|
-
|
|
522
|
+
When `throwOnBlocked: true` (the default), you can catch structured errors to handle blocks gracefully.
|
|
621
523
|
|
|
622
|
-
|
|
524
|
+
```typescript
|
|
525
|
+
import { generateText } from 'ai';
|
|
526
|
+
import { isGuardrailsError } from 'ai-sdk-guardrails';
|
|
623
527
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
###
|
|
631
|
-
|
|
632
|
-
After publishing to npm, you might want to:
|
|
545
|
+
### User-Friendly Error Messages
|
|
633
546
|
|
|
634
|
-
|
|
635
|
-
- Set up GitHub Actions for automated publishing (optional)
|
|
547
|
+
Transform technical guardrail messages into user-friendly guidance:
|
|
636
548
|
|
|
637
|
-
|
|
549
|
+
```typescript
|
|
550
|
+
function createUserFriendlyMessage(guardrailResult): string {
|
|
551
|
+
const guardrailName = guardrailResult.context?.guardrailName;
|
|
638
552
|
|
|
639
|
-
|
|
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
|
-
|
|
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
|
-
|
|
644
|
-
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
|
|
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
|
-
##
|
|
572
|
+
## Complete AI SDK Integration
|
|
684
573
|
|
|
685
|
-
|
|
574
|
+
The library seamlessly integrates with all AI SDK functions:
|
|
686
575
|
|
|
687
576
|
```typescript
|
|
688
|
-
//
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
|
|
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
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
643
|
+
All examples feature interactive menus with arrow key navigation, multi-selection with checkboxes, and automatic return to the main menu.
|
|
722
644
|
|
|
723
|
-
##
|
|
645
|
+
## 🤝 Contributing
|
|
724
646
|
|
|
725
|
-
|
|
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
|
-
|
|
651
|
+
MIT © [Jag Reehal](https://github.com/jagreehal) – See LICENSE for full details.
|