ai-sdk-guardrails 1.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/LICENSE +21 -0
- package/README.md +729 -0
- package/dist/chunk-BNGJDZMX.js +218 -0
- package/dist/guardrails/input.cjs +487 -0
- package/dist/guardrails/input.d.cts +36 -0
- package/dist/guardrails/input.d.ts +36 -0
- package/dist/guardrails/input.js +445 -0
- package/dist/guardrails/output.cjs +674 -0
- package/dist/guardrails/output.d.cts +46 -0
- package/dist/guardrails/output.d.ts +46 -0
- package/dist/guardrails/output.js +628 -0
- package/dist/index-CWIb12lh.d.cts +121 -0
- package/dist/index-CWIb12lh.d.ts +121 -0
- package/dist/index.cjs +245 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +20 -0
- package/package.json +91 -0
package/README.md
ADDED
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
# AI SDK Guardrails
|
|
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.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/ai-sdk-guardrails)
|
|
6
|
+
[](https://www.npmjs.com/package/ai-sdk-guardrails)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
## Why AI SDK Guardrails?
|
|
11
|
+
|
|
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.
|
|
13
|
+
|
|
14
|
+
### 🛡️ Complete Protection
|
|
15
|
+
|
|
16
|
+
Validate inputs before they reach your model and outputs before they reach users. Catch harmful content, PII, prompt injections, and more.
|
|
17
|
+
|
|
18
|
+
### ⚡ Real-time Streaming Safety
|
|
19
|
+
|
|
20
|
+
The only guardrails library with true streaming support. Monitor and stop streams in real-time when safety violations occur.
|
|
21
|
+
|
|
22
|
+
### 🎯 Built for Vercel AI SDK
|
|
23
|
+
|
|
24
|
+
First-class support for all AI SDK functions: `generateText`, `generateObject`, `streamText`, `streamObject`, and `embed`.
|
|
25
|
+
|
|
26
|
+
### 🔧 Developer Friendly
|
|
27
|
+
|
|
28
|
+
Simple API with TypeScript autocompletion, helpful error messages, and sensible defaults. Start safe in minutes, not hours.
|
|
29
|
+
|
|
30
|
+
## How It Works
|
|
31
|
+
|
|
32
|
+
```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
|
|
50
|
+
|
|
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
|
|
58
|
+
|
|
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
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm install ai-sdk-guardrails
|
|
86
|
+
# or
|
|
87
|
+
pnpm add ai-sdk-guardrails
|
|
88
|
+
# or
|
|
89
|
+
yarn add ai-sdk-guardrails
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
|
|
94
|
+
Get started with production-ready guardrails in just a few lines:
|
|
95
|
+
|
|
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';
|
|
101
|
+
|
|
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
|
+
);
|
|
112
|
+
|
|
113
|
+
console.log(result.text);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Core Concepts
|
|
117
|
+
|
|
118
|
+
### Input Guardrails
|
|
119
|
+
|
|
120
|
+
Input guardrails validate prompts before they reach your AI model. Use them to:
|
|
121
|
+
|
|
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
|
|
127
|
+
|
|
128
|
+
```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
|
+
];
|
|
141
|
+
|
|
142
|
+
const isHomework = homeworkPatterns.some((pattern) =>
|
|
143
|
+
pattern.test(prompt || ''),
|
|
144
|
+
);
|
|
145
|
+
|
|
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
|
+
};
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Output Guardrails
|
|
158
|
+
|
|
159
|
+
Output guardrails validate AI responses before they reach users. Use them to:
|
|
160
|
+
|
|
161
|
+
- Ensure response quality
|
|
162
|
+
- Block sensitive information
|
|
163
|
+
- Enforce formatting requirements
|
|
164
|
+
- Validate against schemas
|
|
165
|
+
- Check confidence levels
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { createOutputGuardrail } from 'ai-sdk-guardrails';
|
|
169
|
+
|
|
170
|
+
const sensitiveInfoFilter = createOutputGuardrail(
|
|
171
|
+
'sensitive-info-filter',
|
|
172
|
+
(context) => {
|
|
173
|
+
const { text } = context.result;
|
|
174
|
+
const sensitivePatterns = [
|
|
175
|
+
/password:\s*\w+/i,
|
|
176
|
+
/api[_-]?key:\s*\w+/i,
|
|
177
|
+
/\b\d{3}-\d{2}-\d{4}\b/, // SSN format
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
const hasSensitive = sensitivePatterns.some((pattern) =>
|
|
181
|
+
pattern.test(text || ''),
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
tripwireTriggered: hasSensitive,
|
|
186
|
+
message: hasSensitive
|
|
187
|
+
? 'Response contains sensitive information'
|
|
188
|
+
: undefined,
|
|
189
|
+
severity: hasSensitive ? 'critical' : 'low',
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Streaming Guardrails
|
|
196
|
+
|
|
197
|
+
Protect streaming responses in real-time. The stream automatically stops if guardrails detect violations:
|
|
198
|
+
|
|
199
|
+
```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
|
+
},
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// The stream will automatically stop if guardrails trigger
|
|
219
|
+
for await (const chunk of stream.textStream) {
|
|
220
|
+
process.stdout.write(chunk);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Built-in Guardrails
|
|
225
|
+
|
|
226
|
+
### Input Guardrails
|
|
227
|
+
|
|
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
|
+
```
|
|
258
|
+
|
|
259
|
+
### Output Guardrails
|
|
260
|
+
|
|
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
|
+
```
|
|
301
|
+
|
|
302
|
+
## Integration with Autoevals
|
|
303
|
+
|
|
304
|
+
Use [Autoevals](https://github.com/braintrust-data/autoevals) for sophisticated AI quality evaluation as guardrails:
|
|
305
|
+
|
|
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);
|
|
320
|
+
|
|
321
|
+
const evalResult = await Factuality({
|
|
322
|
+
output: text,
|
|
323
|
+
expected,
|
|
324
|
+
input: prompt || '',
|
|
325
|
+
model: MODEL_NAME,
|
|
326
|
+
});
|
|
327
|
+
|
|
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
|
+
}
|
|
348
|
+
|
|
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
|
+
);
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Error Handling
|
|
367
|
+
|
|
368
|
+
Guardrails provide rich error information to help you handle violations gracefully:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
import { GuardrailError } from 'ai-sdk-guardrails';
|
|
372
|
+
|
|
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
|
+
});
|
|
388
|
+
|
|
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
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Advanced Usage
|
|
400
|
+
|
|
401
|
+
### Custom Validation Logic
|
|
402
|
+
|
|
403
|
+
Create sophisticated guardrails for your specific needs:
|
|
404
|
+
|
|
405
|
+
```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,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
tripwireTriggered: !validationResult.isValid,
|
|
420
|
+
message: validationResult.error,
|
|
421
|
+
metadata: validationResult.details,
|
|
422
|
+
severity: validationResult.severity,
|
|
423
|
+
suggestion: validationResult.suggestion,
|
|
424
|
+
};
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Composing Guardrails
|
|
430
|
+
|
|
431
|
+
Combine multiple guardrail configurations for different scenarios:
|
|
432
|
+
|
|
433
|
+
```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
|
+
```
|
|
452
|
+
|
|
453
|
+
### Object Generation with Schema Validation
|
|
454
|
+
|
|
455
|
+
Validate structured outputs with custom business logic:
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
import { z } from 'zod';
|
|
459
|
+
|
|
460
|
+
const userSchema = z.object({
|
|
461
|
+
name: z.string(),
|
|
462
|
+
age: z.number().min(0).max(120),
|
|
463
|
+
email: z.string().email(),
|
|
464
|
+
});
|
|
465
|
+
|
|
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) {
|
|
474
|
+
return {
|
|
475
|
+
tripwireTriggered: true,
|
|
476
|
+
message: 'Schema validation failed',
|
|
477
|
+
severity: 'high',
|
|
478
|
+
metadata: { validationError: error.message },
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
);
|
|
483
|
+
|
|
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],
|
|
492
|
+
},
|
|
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
|
+
};
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### 2. Handle Errors Gracefully
|
|
527
|
+
|
|
528
|
+
Provide helpful feedback when guardrails trigger:
|
|
529
|
+
|
|
530
|
+
```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)**
|
|
545
|
+
|
|
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:
|
|
555
|
+
|
|
556
|
+
- Ask you what type of change (patch/minor/major)
|
|
557
|
+
- Let you write a summary of changes
|
|
558
|
+
- Create a changeset file
|
|
559
|
+
|
|
560
|
+
#### **Step 2: Version and Publish**
|
|
561
|
+
|
|
562
|
+
You have two options:
|
|
563
|
+
|
|
564
|
+
**Option A: Manual Control**
|
|
565
|
+
|
|
566
|
+
```bash
|
|
567
|
+
# 1. Run CI to ensure everything passes
|
|
568
|
+
npm run ci
|
|
569
|
+
|
|
570
|
+
# 2. Version the package (reads changesets and updates version)
|
|
571
|
+
npx changeset version
|
|
572
|
+
|
|
573
|
+
# 3. Publish to npm
|
|
574
|
+
npx changeset publish
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Option B: Use Your Local Release Script**
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
npm run local-release
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
This runs: `npm run ci && changeset version && changeset publish`
|
|
584
|
+
|
|
585
|
+
### 4. **First Release Steps**
|
|
586
|
+
|
|
587
|
+
For your first release, I recommend:
|
|
588
|
+
|
|
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**:
|
|
596
|
+
|
|
597
|
+
```bash
|
|
598
|
+
npx changeset
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
- Select "major" (since this is 0.0.1 → 1.0.0)
|
|
602
|
+
- Write: "Initial release of AI SDK Guardrails"
|
|
603
|
+
|
|
604
|
+
3. **Release it**:
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
npm run local-release
|
|
608
|
+
```
|
|
609
|
+
|
|
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
|
|
619
|
+
|
|
620
|
+
### 6. **Additional Recommendations**
|
|
621
|
+
|
|
622
|
+
Consider these npm badges for your README:
|
|
623
|
+
|
|
624
|
+
```markdown
|
|
625
|
+
<code_block_to_apply_changes_from>
|
|
626
|
+
[](https://www.npmjs.com/package/ai-sdk-guardrails)
|
|
627
|
+
[](https://www.npmjs.com/package/ai-sdk-guardrails)
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### 7. **GitHub Release Integration**
|
|
631
|
+
|
|
632
|
+
After publishing to npm, you might want to:
|
|
633
|
+
|
|
634
|
+
- Create GitHub releases that match your npm versions
|
|
635
|
+
- Set up GitHub Actions for automated publishing (optional)
|
|
636
|
+
|
|
637
|
+
**Would you like me to help you run through the first release process now?**
|
|
638
|
+
|
|
639
|
+
## All AI SDK Functions Supported
|
|
640
|
+
|
|
641
|
+
The library provides guarded versions of all AI SDK functions:
|
|
642
|
+
|
|
643
|
+
```typescript
|
|
644
|
+
import {
|
|
645
|
+
generateTextWithGuardrails,
|
|
646
|
+
generateObjectWithGuardrails,
|
|
647
|
+
streamTextWithGuardrails,
|
|
648
|
+
streamObjectWithGuardrails,
|
|
649
|
+
embedWithGuardrails,
|
|
650
|
+
} from 'ai-sdk-guardrails';
|
|
651
|
+
|
|
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
|
+
);
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
## TypeScript Support
|
|
684
|
+
|
|
685
|
+
Full TypeScript support with intelligent type inference:
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
// Types are automatically inferred
|
|
689
|
+
const result = await generateTextWithGuardrails(
|
|
690
|
+
{
|
|
691
|
+
model: openai('gpt-4-turbo'),
|
|
692
|
+
prompt: 'Hello',
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
inputGuardrails: [
|
|
696
|
+
/* your guardrails */
|
|
697
|
+
],
|
|
698
|
+
},
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
// result is fully typed as GenerateTextResult
|
|
702
|
+
console.log(result.text);
|
|
703
|
+
console.log(result.usage);
|
|
704
|
+
console.log(result.finishReason);
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
## Examples
|
|
708
|
+
|
|
709
|
+
Explore our comprehensive examples that demonstrate real-world usage patterns:
|
|
710
|
+
|
|
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
|
|
716
|
+
|
|
717
|
+
Each example is fully functional and demonstrates different aspects of the library with practical use cases.
|
|
718
|
+
|
|
719
|
+
## Contributing
|
|
720
|
+
|
|
721
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
722
|
+
|
|
723
|
+
## License
|
|
724
|
+
|
|
725
|
+
MIT © [Jag Reehal](https://github.com/jagreehal)
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
Built with ❤️ for the AI community. Star the repo if you find it helpful!
|