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
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/guardrails/output.ts
|
|
21
|
+
var output_exports = {};
|
|
22
|
+
__export(output_exports, {
|
|
23
|
+
biasDetector: () => biasDetector,
|
|
24
|
+
blockedContent: () => blockedContent,
|
|
25
|
+
blockedOutputContent: () => blockedOutputContent,
|
|
26
|
+
complianceChecker: () => complianceChecker,
|
|
27
|
+
confidenceThreshold: () => confidenceThreshold,
|
|
28
|
+
contentConsistencyChecker: () => contentConsistencyChecker,
|
|
29
|
+
customValidation: () => customValidation,
|
|
30
|
+
extractContent: () => extractContent,
|
|
31
|
+
factualAccuracyChecker: () => factualAccuracyChecker,
|
|
32
|
+
hallucinationDetector: () => hallucinationDetector,
|
|
33
|
+
jsonValidation: () => jsonValidation,
|
|
34
|
+
lengthLimit: () => lengthLimit,
|
|
35
|
+
outputLengthLimit: () => outputLengthLimit,
|
|
36
|
+
performanceMonitor: () => performanceMonitor,
|
|
37
|
+
privacyLeakageDetector: () => privacyLeakageDetector,
|
|
38
|
+
schemaValidation: () => schemaValidation,
|
|
39
|
+
tokenUsageLimit: () => tokenUsageLimit,
|
|
40
|
+
toxicityFilter: () => toxicityFilter
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(output_exports);
|
|
43
|
+
|
|
44
|
+
// src/core.ts
|
|
45
|
+
var import_ai = require("ai");
|
|
46
|
+
function createOutputGuardrail(name, execute) {
|
|
47
|
+
return { name, execute };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/guardrails/output.ts
|
|
51
|
+
function extractContent(result) {
|
|
52
|
+
if ("object" in result && result.object != null) {
|
|
53
|
+
const objectResult = result;
|
|
54
|
+
return {
|
|
55
|
+
text: objectResult.text || "",
|
|
56
|
+
object: objectResult.object,
|
|
57
|
+
usage: objectResult.usage,
|
|
58
|
+
finishReason: objectResult.finishReason,
|
|
59
|
+
generationTimeMs: objectResult.experimental_providerMetadata?.generationTimeMs,
|
|
60
|
+
reasoning: objectResult.reasoning || objectResult.experimental_providerMetadata?.reasoning
|
|
61
|
+
};
|
|
62
|
+
} else if ("text" in result) {
|
|
63
|
+
const textResult = result;
|
|
64
|
+
return {
|
|
65
|
+
text: textResult.text || "",
|
|
66
|
+
object: null,
|
|
67
|
+
usage: textResult.usage,
|
|
68
|
+
finishReason: textResult.finishReason,
|
|
69
|
+
generationTimeMs: textResult.experimental_providerMetadata?.generationTimeMs,
|
|
70
|
+
reasoning: textResult.reasoning || textResult.experimental_providerMetadata?.reasoning
|
|
71
|
+
};
|
|
72
|
+
} else if ("textStream" in result) {
|
|
73
|
+
return {
|
|
74
|
+
text: "",
|
|
75
|
+
object: null,
|
|
76
|
+
usage: void 0,
|
|
77
|
+
finishReason: void 0,
|
|
78
|
+
generationTimeMs: void 0
|
|
79
|
+
};
|
|
80
|
+
} else if ("objectStream" in result) {
|
|
81
|
+
return {
|
|
82
|
+
text: "",
|
|
83
|
+
object: null,
|
|
84
|
+
usage: void 0,
|
|
85
|
+
finishReason: void 0,
|
|
86
|
+
generationTimeMs: void 0
|
|
87
|
+
};
|
|
88
|
+
} else if ("embeddings" in result || "then" in result) {
|
|
89
|
+
return {
|
|
90
|
+
text: "",
|
|
91
|
+
object: null,
|
|
92
|
+
usage: void 0,
|
|
93
|
+
finishReason: void 0,
|
|
94
|
+
generationTimeMs: void 0
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
text: "",
|
|
99
|
+
object: null,
|
|
100
|
+
usage: void 0,
|
|
101
|
+
finishReason: void 0,
|
|
102
|
+
generationTimeMs: void 0
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
var lengthLimit = (maxLength) => createOutputGuardrail(
|
|
106
|
+
"output-length-limit",
|
|
107
|
+
(context, accumulatedText) => {
|
|
108
|
+
const { text, object, usage, finishReason, generationTimeMs } = extractContent(context.result);
|
|
109
|
+
const content = accumulatedText || text || (object ? JSON.stringify(object) : "");
|
|
110
|
+
return {
|
|
111
|
+
tripwireTriggered: content.length > maxLength,
|
|
112
|
+
message: `Output length ${content.length} exceeds limit of ${maxLength}`,
|
|
113
|
+
severity: "medium",
|
|
114
|
+
metadata: {
|
|
115
|
+
contentLength: content.length,
|
|
116
|
+
maxLength,
|
|
117
|
+
hasObject: !!object,
|
|
118
|
+
usage,
|
|
119
|
+
finishReason,
|
|
120
|
+
generationTimeMs,
|
|
121
|
+
tokensPerMs: usage?.totalTokens && generationTimeMs ? usage.totalTokens / generationTimeMs : void 0
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
var blockedContent = (words) => createOutputGuardrail(
|
|
127
|
+
"blocked-content",
|
|
128
|
+
(context) => {
|
|
129
|
+
const { text, object } = extractContent(context.result);
|
|
130
|
+
const content = (text || (object ? JSON.stringify(object) : "")).toLowerCase();
|
|
131
|
+
const blockedWord = words.find(
|
|
132
|
+
(word) => content.includes(word.toLowerCase())
|
|
133
|
+
);
|
|
134
|
+
return {
|
|
135
|
+
tripwireTriggered: !!blockedWord,
|
|
136
|
+
message: blockedWord ? `Blocked content detected: ${blockedWord}` : void 0,
|
|
137
|
+
severity: "high",
|
|
138
|
+
metadata: {
|
|
139
|
+
blockedWord,
|
|
140
|
+
allWords: words,
|
|
141
|
+
contentLength: content.length
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
var outputLengthLimit = (maxLength) => createOutputGuardrail(
|
|
147
|
+
"output-length-limit",
|
|
148
|
+
(context, accumulatedText) => {
|
|
149
|
+
const { text, object } = extractContent(context.result);
|
|
150
|
+
const content = accumulatedText || text || (object ? JSON.stringify(object) : "");
|
|
151
|
+
return {
|
|
152
|
+
tripwireTriggered: content.length > maxLength,
|
|
153
|
+
message: `Output length ${content.length} exceeds limit of ${maxLength}`,
|
|
154
|
+
severity: "medium",
|
|
155
|
+
metadata: {
|
|
156
|
+
contentLength: content.length,
|
|
157
|
+
maxLength,
|
|
158
|
+
hasObject: !!object
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
var blockedOutputContent = (words) => createOutputGuardrail(
|
|
164
|
+
"blocked-output-content",
|
|
165
|
+
(context) => {
|
|
166
|
+
const { text, object } = extractContent(context.result);
|
|
167
|
+
const content = (text || (object ? JSON.stringify(object) : "")).toLowerCase();
|
|
168
|
+
const blockedWord = words.find(
|
|
169
|
+
(word) => content.includes(word.toLowerCase())
|
|
170
|
+
);
|
|
171
|
+
return {
|
|
172
|
+
tripwireTriggered: !!blockedWord,
|
|
173
|
+
message: blockedWord ? `Blocked output content detected: ${blockedWord}` : void 0,
|
|
174
|
+
severity: "high",
|
|
175
|
+
metadata: {
|
|
176
|
+
blockedWord,
|
|
177
|
+
allWords: words,
|
|
178
|
+
contentLength: content.length
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
var jsonValidation = () => createOutputGuardrail(
|
|
184
|
+
"json-validation",
|
|
185
|
+
(context) => {
|
|
186
|
+
const { text, object } = extractContent(context.result);
|
|
187
|
+
if (object) return { tripwireTriggered: false };
|
|
188
|
+
try {
|
|
189
|
+
JSON.parse(text);
|
|
190
|
+
return { tripwireTriggered: false };
|
|
191
|
+
} catch (error) {
|
|
192
|
+
return {
|
|
193
|
+
tripwireTriggered: true,
|
|
194
|
+
message: "Output is not valid JSON",
|
|
195
|
+
severity: "medium",
|
|
196
|
+
metadata: {
|
|
197
|
+
error: error instanceof Error ? error.message : String(error),
|
|
198
|
+
textLength: text.length
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
var confidenceThreshold = (minConfidence) => createOutputGuardrail(
|
|
205
|
+
"confidence-threshold",
|
|
206
|
+
(context) => {
|
|
207
|
+
const { text, object, usage, finishReason, generationTimeMs, reasoning } = extractContent(context.result);
|
|
208
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
209
|
+
const hasUncertainty = content.toLowerCase().includes("i think") || content.toLowerCase().includes("maybe") || content.toLowerCase().includes("probably") || content.toLowerCase().includes("uncertain") || content.toLowerCase().includes("not sure");
|
|
210
|
+
const finishReasonPenalty = finishReason === "length" ? 0.2 : 0;
|
|
211
|
+
const baseConfidence = hasUncertainty ? 0.5 : 0.9;
|
|
212
|
+
const confidence = Math.max(0, baseConfidence - finishReasonPenalty);
|
|
213
|
+
return {
|
|
214
|
+
tripwireTriggered: confidence < minConfidence,
|
|
215
|
+
message: `Output confidence ${confidence} below threshold ${minConfidence}`,
|
|
216
|
+
severity: "medium",
|
|
217
|
+
metadata: {
|
|
218
|
+
confidence,
|
|
219
|
+
minConfidence,
|
|
220
|
+
hasUncertainty,
|
|
221
|
+
textLength: content.length,
|
|
222
|
+
usage,
|
|
223
|
+
finishReason,
|
|
224
|
+
generationTimeMs,
|
|
225
|
+
finishReasonPenalty,
|
|
226
|
+
reasoning
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
var toxicityFilter = (threshold = 0.7) => createOutputGuardrail(
|
|
232
|
+
"toxicity-filter",
|
|
233
|
+
(context) => {
|
|
234
|
+
const { text, object } = extractContent(context.result);
|
|
235
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
236
|
+
const toxicWords = ["toxic", "harmful", "offensive", "inappropriate"];
|
|
237
|
+
const detectedWords = toxicWords.filter(
|
|
238
|
+
(word) => content.toLowerCase().includes(word)
|
|
239
|
+
);
|
|
240
|
+
const toxicityScore = detectedWords.length * 0.3;
|
|
241
|
+
return {
|
|
242
|
+
tripwireTriggered: toxicityScore > threshold,
|
|
243
|
+
message: `Content toxicity score ${toxicityScore} exceeds threshold ${threshold}`,
|
|
244
|
+
severity: "high",
|
|
245
|
+
metadata: {
|
|
246
|
+
toxicityScore,
|
|
247
|
+
threshold,
|
|
248
|
+
detectedWords,
|
|
249
|
+
contentLength: content.length
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
var customValidation = (name, validator, message) => createOutputGuardrail(name, (context) => {
|
|
255
|
+
const { text, object, usage, finishReason, generationTimeMs } = extractContent(context.result);
|
|
256
|
+
const validatorInput = {
|
|
257
|
+
text,
|
|
258
|
+
object,
|
|
259
|
+
usage,
|
|
260
|
+
finishReason,
|
|
261
|
+
generationTimeMs
|
|
262
|
+
};
|
|
263
|
+
const blocked = validator(validatorInput);
|
|
264
|
+
return {
|
|
265
|
+
tripwireTriggered: blocked,
|
|
266
|
+
message: blocked ? message : void 0,
|
|
267
|
+
severity: "medium",
|
|
268
|
+
metadata: {
|
|
269
|
+
validatorName: name,
|
|
270
|
+
hasText: !!text,
|
|
271
|
+
hasObject: !!object,
|
|
272
|
+
usage,
|
|
273
|
+
finishReason,
|
|
274
|
+
generationTimeMs
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
var schemaValidation = (schema) => createOutputGuardrail(
|
|
279
|
+
"schema-validation",
|
|
280
|
+
(context) => {
|
|
281
|
+
const { object, usage, finishReason, generationTimeMs } = extractContent(
|
|
282
|
+
context.result
|
|
283
|
+
);
|
|
284
|
+
if (!object) {
|
|
285
|
+
return {
|
|
286
|
+
tripwireTriggered: true,
|
|
287
|
+
message: "No object to validate",
|
|
288
|
+
severity: "medium",
|
|
289
|
+
metadata: {
|
|
290
|
+
hasObject: false,
|
|
291
|
+
usage,
|
|
292
|
+
finishReason,
|
|
293
|
+
generationTimeMs
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
schema.parse(object);
|
|
299
|
+
return {
|
|
300
|
+
tripwireTriggered: false,
|
|
301
|
+
metadata: {
|
|
302
|
+
hasObject: true,
|
|
303
|
+
validationPassed: true,
|
|
304
|
+
usage,
|
|
305
|
+
finishReason,
|
|
306
|
+
generationTimeMs
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
} catch (error) {
|
|
310
|
+
return {
|
|
311
|
+
tripwireTriggered: true,
|
|
312
|
+
message: `Schema validation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
313
|
+
severity: "high",
|
|
314
|
+
metadata: {
|
|
315
|
+
hasObject: true,
|
|
316
|
+
validationPassed: false,
|
|
317
|
+
error: error instanceof Error ? error.message : String(error),
|
|
318
|
+
usage,
|
|
319
|
+
finishReason,
|
|
320
|
+
generationTimeMs
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
var tokenUsageLimit = (maxTokens) => createOutputGuardrail(
|
|
327
|
+
"token-usage-limit",
|
|
328
|
+
(context) => {
|
|
329
|
+
const { text, object, usage, generationTimeMs } = extractContent(
|
|
330
|
+
context.result
|
|
331
|
+
);
|
|
332
|
+
const totalTokens = usage?.totalTokens || 0;
|
|
333
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
334
|
+
return {
|
|
335
|
+
tripwireTriggered: totalTokens > maxTokens,
|
|
336
|
+
message: `Token usage ${totalTokens} exceeds limit of ${maxTokens}`,
|
|
337
|
+
severity: "medium",
|
|
338
|
+
metadata: {
|
|
339
|
+
totalTokens,
|
|
340
|
+
maxTokens,
|
|
341
|
+
promptTokens: usage?.promptTokens,
|
|
342
|
+
completionTokens: usage?.completionTokens,
|
|
343
|
+
contentLength: content.length,
|
|
344
|
+
generationTimeMs,
|
|
345
|
+
tokensPerMs: totalTokens && generationTimeMs ? totalTokens / generationTimeMs : void 0
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
var performanceMonitor = (maxGenerationTimeMs) => createOutputGuardrail(
|
|
351
|
+
"performance-monitor",
|
|
352
|
+
(context) => {
|
|
353
|
+
const { text, object, usage, generationTimeMs } = extractContent(
|
|
354
|
+
context.result
|
|
355
|
+
);
|
|
356
|
+
const actualGenerationTimeMs = generationTimeMs || 0;
|
|
357
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
358
|
+
return {
|
|
359
|
+
tripwireTriggered: actualGenerationTimeMs > maxGenerationTimeMs,
|
|
360
|
+
message: `Generation time ${actualGenerationTimeMs}ms exceeds limit of ${maxGenerationTimeMs}ms`,
|
|
361
|
+
severity: "low",
|
|
362
|
+
metadata: {
|
|
363
|
+
generationTimeMs: actualGenerationTimeMs,
|
|
364
|
+
maxGenerationTimeMs,
|
|
365
|
+
contentLength: content.length,
|
|
366
|
+
usage,
|
|
367
|
+
tokensPerMs: usage?.totalTokens && actualGenerationTimeMs ? usage.totalTokens / actualGenerationTimeMs : void 0,
|
|
368
|
+
charactersPerMs: actualGenerationTimeMs ? content.length / actualGenerationTimeMs : void 0
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
374
|
+
var hallucinationDetector = (confidenceThreshold2 = 0.7) => createOutputGuardrail(
|
|
375
|
+
"hallucination-detector",
|
|
376
|
+
(context, accumulatedText) => {
|
|
377
|
+
const { text, object, usage, finishReason, generationTimeMs } = extractContent(context.result);
|
|
378
|
+
const content = accumulatedText || (object && isObject(object) ? JSON.stringify(object) : text || "");
|
|
379
|
+
const uncertaintyIndicators = [
|
|
380
|
+
"i think",
|
|
381
|
+
"i believe",
|
|
382
|
+
"probably",
|
|
383
|
+
"likely",
|
|
384
|
+
"might be",
|
|
385
|
+
"could be",
|
|
386
|
+
"not sure",
|
|
387
|
+
"uncertain",
|
|
388
|
+
"possibly",
|
|
389
|
+
"perhaps",
|
|
390
|
+
"maybe",
|
|
391
|
+
"seems like",
|
|
392
|
+
"appears to be",
|
|
393
|
+
"my understanding is",
|
|
394
|
+
"if i recall correctly"
|
|
395
|
+
];
|
|
396
|
+
const factualClaims = [
|
|
397
|
+
"according to",
|
|
398
|
+
"studies show",
|
|
399
|
+
"research indicates",
|
|
400
|
+
"data suggests",
|
|
401
|
+
"statistics show",
|
|
402
|
+
"proven fact",
|
|
403
|
+
"scientific evidence",
|
|
404
|
+
"documented",
|
|
405
|
+
"confirmed by",
|
|
406
|
+
"established that"
|
|
407
|
+
];
|
|
408
|
+
const uncertaintyCount = uncertaintyIndicators.filter(
|
|
409
|
+
(indicator) => content.toLowerCase().includes(indicator)
|
|
410
|
+
).length;
|
|
411
|
+
const factualClaimCount = factualClaims.filter(
|
|
412
|
+
(claim) => content.toLowerCase().includes(claim)
|
|
413
|
+
).length;
|
|
414
|
+
const hallucinationScore = uncertaintyCount * 0.3 + factualClaimCount * 0.2;
|
|
415
|
+
const isHallucination = hallucinationScore > confidenceThreshold2;
|
|
416
|
+
return {
|
|
417
|
+
tripwireTriggered: isHallucination,
|
|
418
|
+
message: isHallucination ? `Potential hallucination detected (score: ${hallucinationScore})` : void 0,
|
|
419
|
+
severity: hallucinationScore > 0.8 ? "high" : "medium",
|
|
420
|
+
metadata: {
|
|
421
|
+
hallucinationScore,
|
|
422
|
+
confidenceThreshold: confidenceThreshold2,
|
|
423
|
+
uncertaintyCount,
|
|
424
|
+
factualClaimCount,
|
|
425
|
+
contentLength: content.length,
|
|
426
|
+
usage,
|
|
427
|
+
finishReason,
|
|
428
|
+
generationTimeMs
|
|
429
|
+
},
|
|
430
|
+
suggestion: "Please verify factual claims and consider requesting sources"
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
var biasDetector = () => createOutputGuardrail("bias-detector", (context) => {
|
|
435
|
+
const { text, object } = extractContent(context.result);
|
|
436
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
437
|
+
const lowerContent = content.toLowerCase();
|
|
438
|
+
const biasPatterns = {
|
|
439
|
+
gender: [
|
|
440
|
+
"men are better at",
|
|
441
|
+
"women are better at",
|
|
442
|
+
"typical male",
|
|
443
|
+
"typical female",
|
|
444
|
+
"boys will be boys",
|
|
445
|
+
"women should",
|
|
446
|
+
"men should",
|
|
447
|
+
"ladies",
|
|
448
|
+
"gentlemen"
|
|
449
|
+
],
|
|
450
|
+
racial: [
|
|
451
|
+
"people of that race",
|
|
452
|
+
"those people",
|
|
453
|
+
"their culture",
|
|
454
|
+
"natural talent",
|
|
455
|
+
"genetic predisposition",
|
|
456
|
+
"inherent ability",
|
|
457
|
+
"cultural background"
|
|
458
|
+
],
|
|
459
|
+
age: [
|
|
460
|
+
"young people today",
|
|
461
|
+
"older people can't",
|
|
462
|
+
"millennials are",
|
|
463
|
+
"boomers are",
|
|
464
|
+
"too old to",
|
|
465
|
+
"too young to"
|
|
466
|
+
],
|
|
467
|
+
socioeconomic: [
|
|
468
|
+
"poor people are",
|
|
469
|
+
"rich people are",
|
|
470
|
+
"welfare recipients",
|
|
471
|
+
"privileged class",
|
|
472
|
+
"working class",
|
|
473
|
+
"upper class"
|
|
474
|
+
]
|
|
475
|
+
};
|
|
476
|
+
const detectedBias = [];
|
|
477
|
+
const matches = [];
|
|
478
|
+
for (const [category, patterns] of Object.entries(biasPatterns)) {
|
|
479
|
+
const found = patterns.filter(
|
|
480
|
+
(pattern) => lowerContent.includes(pattern)
|
|
481
|
+
);
|
|
482
|
+
if (found.length > 0) {
|
|
483
|
+
detectedBias.push(category);
|
|
484
|
+
matches.push(...found);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
tripwireTriggered: detectedBias.length > 0,
|
|
489
|
+
message: detectedBias.length > 0 ? `Potential bias detected in categories: ${detectedBias.join(", ")}` : void 0,
|
|
490
|
+
severity: "medium",
|
|
491
|
+
metadata: {
|
|
492
|
+
biasCategories: detectedBias,
|
|
493
|
+
biasPatterns: matches,
|
|
494
|
+
contentLength: content.length
|
|
495
|
+
},
|
|
496
|
+
suggestion: "Consider reviewing content for potential bias and using more inclusive language"
|
|
497
|
+
};
|
|
498
|
+
});
|
|
499
|
+
var factualAccuracyChecker = (requireSources = false) => createOutputGuardrail(
|
|
500
|
+
"factual-accuracy-checker",
|
|
501
|
+
(context) => {
|
|
502
|
+
const { text, object, generationTimeMs } = extractContent(context.result);
|
|
503
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
504
|
+
const factualClaims = [
|
|
505
|
+
"according to",
|
|
506
|
+
"studies show",
|
|
507
|
+
"research indicates",
|
|
508
|
+
"data suggests",
|
|
509
|
+
"statistics show",
|
|
510
|
+
"proven fact",
|
|
511
|
+
"scientific evidence",
|
|
512
|
+
"documented",
|
|
513
|
+
"confirmed by",
|
|
514
|
+
"established that",
|
|
515
|
+
"published in",
|
|
516
|
+
"survey found"
|
|
517
|
+
];
|
|
518
|
+
const sourceCitations = [
|
|
519
|
+
"source:",
|
|
520
|
+
"reference:",
|
|
521
|
+
"citation:",
|
|
522
|
+
"published in",
|
|
523
|
+
"journal of",
|
|
524
|
+
"university of",
|
|
525
|
+
"institute of",
|
|
526
|
+
"doi:",
|
|
527
|
+
"isbn:",
|
|
528
|
+
"url:",
|
|
529
|
+
"http"
|
|
530
|
+
];
|
|
531
|
+
const factualClaimCount = factualClaims.filter(
|
|
532
|
+
(claim) => content.toLowerCase().includes(claim)
|
|
533
|
+
).length;
|
|
534
|
+
const sourceCitationCount = sourceCitations.filter(
|
|
535
|
+
(source) => content.toLowerCase().includes(source)
|
|
536
|
+
).length;
|
|
537
|
+
const hasUnfoundedClaims = requireSources && factualClaimCount > 0 && sourceCitationCount === 0;
|
|
538
|
+
return {
|
|
539
|
+
tripwireTriggered: hasUnfoundedClaims,
|
|
540
|
+
message: hasUnfoundedClaims ? `Factual claims detected without sources (${factualClaimCount} claims, ${sourceCitationCount} sources)` : void 0,
|
|
541
|
+
severity: "medium",
|
|
542
|
+
metadata: {
|
|
543
|
+
factualClaimCount,
|
|
544
|
+
sourceCitationCount,
|
|
545
|
+
requireSources,
|
|
546
|
+
contentLength: content.length,
|
|
547
|
+
generationTimeMs
|
|
548
|
+
},
|
|
549
|
+
suggestion: "Please provide sources for factual claims or clarify that claims are general knowledge"
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
var privacyLeakageDetector = () => createOutputGuardrail(
|
|
554
|
+
"privacy-leakage-detector",
|
|
555
|
+
(context) => {
|
|
556
|
+
const { text, object } = extractContent(context.result);
|
|
557
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
558
|
+
const privacyPatterns = {
|
|
559
|
+
personal: /\b(john|jane|smith|doe|password|secret|private|confidential)\b/gi,
|
|
560
|
+
contact: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
|
|
561
|
+
phone: /\b\d{3}-\d{3}-\d{4}\b/g,
|
|
562
|
+
financial: /\b(credit card|ssn|social security|bank account|routing number)\b/gi,
|
|
563
|
+
location: /\b(address|street|apartment|zip code|postal code)\b/gi
|
|
564
|
+
};
|
|
565
|
+
const detectedLeakage = [];
|
|
566
|
+
const matches = [];
|
|
567
|
+
for (const [category, pattern] of Object.entries(privacyPatterns)) {
|
|
568
|
+
const found = content.match(pattern);
|
|
569
|
+
if (found) {
|
|
570
|
+
detectedLeakage.push(category);
|
|
571
|
+
matches.push(...found);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return {
|
|
575
|
+
tripwireTriggered: detectedLeakage.length > 0,
|
|
576
|
+
message: detectedLeakage.length > 0 ? `Potential privacy leakage detected: ${detectedLeakage.join(", ")}` : void 0,
|
|
577
|
+
severity: "critical",
|
|
578
|
+
metadata: {
|
|
579
|
+
leakageCategories: detectedLeakage,
|
|
580
|
+
matchCount: matches.length,
|
|
581
|
+
contentLength: content.length
|
|
582
|
+
},
|
|
583
|
+
suggestion: "Review output for any personal or sensitive information that should be removed"
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
);
|
|
587
|
+
var contentConsistencyChecker = (referenceContent) => createOutputGuardrail(
|
|
588
|
+
"content-consistency-checker",
|
|
589
|
+
(context) => {
|
|
590
|
+
const { text, object } = extractContent(context.result);
|
|
591
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
592
|
+
if (!referenceContent) {
|
|
593
|
+
return { tripwireTriggered: false };
|
|
594
|
+
}
|
|
595
|
+
const contentWords = content.toLowerCase().split(/\s+/);
|
|
596
|
+
const referenceWords = referenceContent.toLowerCase().split(/\s+/);
|
|
597
|
+
const commonWords = contentWords.filter(
|
|
598
|
+
(word) => referenceWords.includes(word)
|
|
599
|
+
);
|
|
600
|
+
const consistencyScore = commonWords.length / Math.max(contentWords.length, referenceWords.length);
|
|
601
|
+
const isInconsistent = consistencyScore < 0.3;
|
|
602
|
+
return {
|
|
603
|
+
tripwireTriggered: isInconsistent,
|
|
604
|
+
message: isInconsistent ? `Content consistency score too low: ${consistencyScore.toFixed(2)}` : void 0,
|
|
605
|
+
severity: "medium",
|
|
606
|
+
metadata: {
|
|
607
|
+
consistencyScore,
|
|
608
|
+
contentLength: content.length,
|
|
609
|
+
referenceLength: referenceContent.length,
|
|
610
|
+
commonWordCount: commonWords.length
|
|
611
|
+
},
|
|
612
|
+
suggestion: "Ensure output maintains consistency with reference content"
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
var complianceChecker = (regulations = []) => createOutputGuardrail(
|
|
617
|
+
"compliance-checker",
|
|
618
|
+
(context) => {
|
|
619
|
+
const { text, object } = extractContent(context.result);
|
|
620
|
+
const content = text || (object ? JSON.stringify(object) : "");
|
|
621
|
+
const compliancePatterns = {
|
|
622
|
+
gdpr: ["personal data", "data processing", "consent", "data subject"],
|
|
623
|
+
hipaa: ["patient", "medical", "health information", "protected health"],
|
|
624
|
+
pci: ["credit card", "payment", "cardholder", "card number"],
|
|
625
|
+
sox: ["financial", "audit", "internal controls", "financial reporting"],
|
|
626
|
+
coppa: ["children", "under 13", "parental consent", "minor"]
|
|
627
|
+
};
|
|
628
|
+
const violations = [];
|
|
629
|
+
for (const regulation of regulations) {
|
|
630
|
+
const patterns = compliancePatterns[regulation.toLowerCase()];
|
|
631
|
+
if (patterns) {
|
|
632
|
+
const found = patterns.some(
|
|
633
|
+
(pattern) => content.toLowerCase().includes(pattern)
|
|
634
|
+
);
|
|
635
|
+
if (found) {
|
|
636
|
+
violations.push(regulation.toUpperCase());
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
tripwireTriggered: violations.length > 0,
|
|
642
|
+
message: violations.length > 0 ? `Potential compliance violations detected: ${violations.join(", ")}` : void 0,
|
|
643
|
+
severity: "high",
|
|
644
|
+
metadata: {
|
|
645
|
+
violations,
|
|
646
|
+
regulations,
|
|
647
|
+
contentLength: content.length,
|
|
648
|
+
environment: context.input?.environment
|
|
649
|
+
},
|
|
650
|
+
suggestion: "Review output for compliance with applicable regulations"
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
);
|
|
654
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
655
|
+
0 && (module.exports = {
|
|
656
|
+
biasDetector,
|
|
657
|
+
blockedContent,
|
|
658
|
+
blockedOutputContent,
|
|
659
|
+
complianceChecker,
|
|
660
|
+
confidenceThreshold,
|
|
661
|
+
contentConsistencyChecker,
|
|
662
|
+
customValidation,
|
|
663
|
+
extractContent,
|
|
664
|
+
factualAccuracyChecker,
|
|
665
|
+
hallucinationDetector,
|
|
666
|
+
jsonValidation,
|
|
667
|
+
lengthLimit,
|
|
668
|
+
outputLengthLimit,
|
|
669
|
+
performanceMonitor,
|
|
670
|
+
privacyLeakageDetector,
|
|
671
|
+
schemaValidation,
|
|
672
|
+
tokenUsageLimit,
|
|
673
|
+
toxicityFilter
|
|
674
|
+
});
|