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,36 @@
|
|
|
1
|
+
import { I as InputGuardrailContext, a as InputGuardrail } from '../index-CWIb12lh.cjs';
|
|
2
|
+
import 'ai';
|
|
3
|
+
|
|
4
|
+
declare function extractTextContent(context: InputGuardrailContext): {
|
|
5
|
+
prompt: string;
|
|
6
|
+
messages: Array<{
|
|
7
|
+
content?: unknown;
|
|
8
|
+
}>;
|
|
9
|
+
system: string;
|
|
10
|
+
};
|
|
11
|
+
declare function extractMetadata(context: InputGuardrailContext): {
|
|
12
|
+
model?: unknown;
|
|
13
|
+
temperature?: number;
|
|
14
|
+
maxTokens?: number;
|
|
15
|
+
};
|
|
16
|
+
declare const lengthLimit: (maxLength: number) => InputGuardrail;
|
|
17
|
+
declare const blockedWords: (words: string[]) => InputGuardrail;
|
|
18
|
+
declare const contentLengthLimit: (maxLength: number) => InputGuardrail;
|
|
19
|
+
declare const blockedKeywords: (keywords: string[]) => InputGuardrail;
|
|
20
|
+
declare const rateLimiting: (maxRequestsPerMinute: number) => InputGuardrail;
|
|
21
|
+
declare const profanityFilter: (customWords?: string[]) => InputGuardrail;
|
|
22
|
+
declare const customValidation: (name: string, description: string, validator: (input: {
|
|
23
|
+
prompt?: string;
|
|
24
|
+
messages?: unknown[];
|
|
25
|
+
system?: string;
|
|
26
|
+
model?: unknown;
|
|
27
|
+
temperature?: number;
|
|
28
|
+
maxTokens?: number;
|
|
29
|
+
}) => boolean, message: string) => InputGuardrail;
|
|
30
|
+
declare const promptInjectionDetector: () => InputGuardrail;
|
|
31
|
+
declare const piiDetector: () => InputGuardrail;
|
|
32
|
+
declare const toxicityDetector: (threshold?: number) => InputGuardrail;
|
|
33
|
+
declare const mathHomeworkDetector: () => InputGuardrail;
|
|
34
|
+
declare const codeGenerationLimiter: (allowedLanguages?: string[]) => InputGuardrail;
|
|
35
|
+
|
|
36
|
+
export { blockedKeywords, blockedWords, codeGenerationLimiter, contentLengthLimit, customValidation, extractMetadata, extractTextContent, lengthLimit, mathHomeworkDetector, piiDetector, profanityFilter, promptInjectionDetector, rateLimiting, toxicityDetector };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { I as InputGuardrailContext, a as InputGuardrail } from '../index-CWIb12lh.js';
|
|
2
|
+
import 'ai';
|
|
3
|
+
|
|
4
|
+
declare function extractTextContent(context: InputGuardrailContext): {
|
|
5
|
+
prompt: string;
|
|
6
|
+
messages: Array<{
|
|
7
|
+
content?: unknown;
|
|
8
|
+
}>;
|
|
9
|
+
system: string;
|
|
10
|
+
};
|
|
11
|
+
declare function extractMetadata(context: InputGuardrailContext): {
|
|
12
|
+
model?: unknown;
|
|
13
|
+
temperature?: number;
|
|
14
|
+
maxTokens?: number;
|
|
15
|
+
};
|
|
16
|
+
declare const lengthLimit: (maxLength: number) => InputGuardrail;
|
|
17
|
+
declare const blockedWords: (words: string[]) => InputGuardrail;
|
|
18
|
+
declare const contentLengthLimit: (maxLength: number) => InputGuardrail;
|
|
19
|
+
declare const blockedKeywords: (keywords: string[]) => InputGuardrail;
|
|
20
|
+
declare const rateLimiting: (maxRequestsPerMinute: number) => InputGuardrail;
|
|
21
|
+
declare const profanityFilter: (customWords?: string[]) => InputGuardrail;
|
|
22
|
+
declare const customValidation: (name: string, description: string, validator: (input: {
|
|
23
|
+
prompt?: string;
|
|
24
|
+
messages?: unknown[];
|
|
25
|
+
system?: string;
|
|
26
|
+
model?: unknown;
|
|
27
|
+
temperature?: number;
|
|
28
|
+
maxTokens?: number;
|
|
29
|
+
}) => boolean, message: string) => InputGuardrail;
|
|
30
|
+
declare const promptInjectionDetector: () => InputGuardrail;
|
|
31
|
+
declare const piiDetector: () => InputGuardrail;
|
|
32
|
+
declare const toxicityDetector: (threshold?: number) => InputGuardrail;
|
|
33
|
+
declare const mathHomeworkDetector: () => InputGuardrail;
|
|
34
|
+
declare const codeGenerationLimiter: (allowedLanguages?: string[]) => InputGuardrail;
|
|
35
|
+
|
|
36
|
+
export { blockedKeywords, blockedWords, codeGenerationLimiter, contentLengthLimit, customValidation, extractMetadata, extractTextContent, lengthLimit, mathHomeworkDetector, piiDetector, profanityFilter, promptInjectionDetector, rateLimiting, toxicityDetector };
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createInputGuardrail
|
|
3
|
+
} from "../chunk-BNGJDZMX.js";
|
|
4
|
+
|
|
5
|
+
// src/guardrails/input.ts
|
|
6
|
+
function isEmbedParams(context) {
|
|
7
|
+
return "value" in context && !("prompt" in context);
|
|
8
|
+
}
|
|
9
|
+
function hasContextProperty(context) {
|
|
10
|
+
return "context" in context;
|
|
11
|
+
}
|
|
12
|
+
function extractTextContent(context) {
|
|
13
|
+
if (isEmbedParams(context)) {
|
|
14
|
+
const embedContext = context;
|
|
15
|
+
return {
|
|
16
|
+
prompt: String(embedContext.value || ""),
|
|
17
|
+
messages: [],
|
|
18
|
+
system: ""
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const textContext = context;
|
|
22
|
+
return {
|
|
23
|
+
prompt: textContext.prompt || "",
|
|
24
|
+
messages: textContext.messages || [],
|
|
25
|
+
system: textContext.system || ""
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function extractMetadata(context) {
|
|
29
|
+
const contextWithMetadata = context;
|
|
30
|
+
return {
|
|
31
|
+
model: contextWithMetadata.model,
|
|
32
|
+
temperature: contextWithMetadata.temperature,
|
|
33
|
+
maxTokens: contextWithMetadata.maxTokens
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
var lengthLimit = (maxLength) => createInputGuardrail(
|
|
37
|
+
"length-limit",
|
|
38
|
+
"Enforces maximum input length limit",
|
|
39
|
+
(context) => {
|
|
40
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
41
|
+
const { model, temperature, maxTokens } = extractMetadata(context);
|
|
42
|
+
const totalLength = prompt.length + messages.reduce(
|
|
43
|
+
(sum, msg) => sum + String(msg?.content || "").length,
|
|
44
|
+
0
|
|
45
|
+
) + system.length;
|
|
46
|
+
return {
|
|
47
|
+
tripwireTriggered: totalLength > maxLength,
|
|
48
|
+
message: `Input length ${totalLength} exceeds limit of ${maxLength}`,
|
|
49
|
+
severity: "medium",
|
|
50
|
+
metadata: {
|
|
51
|
+
totalLength,
|
|
52
|
+
maxLength,
|
|
53
|
+
model: model ? String(model) : void 0,
|
|
54
|
+
temperature,
|
|
55
|
+
maxTokens,
|
|
56
|
+
messageCount: messages.length
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
var blockedWords = (words) => createInputGuardrail(
|
|
62
|
+
"blocked-words",
|
|
63
|
+
"Blocks input containing specified words",
|
|
64
|
+
(context) => {
|
|
65
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
66
|
+
const allText = [
|
|
67
|
+
prompt,
|
|
68
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
69
|
+
system
|
|
70
|
+
].join(" ").toLowerCase();
|
|
71
|
+
const blockedWord = words.find(
|
|
72
|
+
(word) => allText.includes(word.toLowerCase())
|
|
73
|
+
);
|
|
74
|
+
return {
|
|
75
|
+
tripwireTriggered: !!blockedWord,
|
|
76
|
+
message: blockedWord ? `Blocked word detected: ${blockedWord}` : void 0,
|
|
77
|
+
severity: "high",
|
|
78
|
+
metadata: {
|
|
79
|
+
blockedWord,
|
|
80
|
+
allWords: words
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
var contentLengthLimit = (maxLength) => createInputGuardrail(
|
|
86
|
+
"content-length-limit",
|
|
87
|
+
"Enforces maximum content length limit",
|
|
88
|
+
(context) => {
|
|
89
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
90
|
+
const totalLength = prompt.length + messages.reduce(
|
|
91
|
+
(sum, msg) => sum + String(msg?.content || "").length,
|
|
92
|
+
0
|
|
93
|
+
) + system.length;
|
|
94
|
+
return {
|
|
95
|
+
tripwireTriggered: totalLength > maxLength,
|
|
96
|
+
message: `Content length ${totalLength} exceeds limit of ${maxLength}`,
|
|
97
|
+
severity: "medium",
|
|
98
|
+
metadata: {
|
|
99
|
+
totalLength,
|
|
100
|
+
maxLength
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
var blockedKeywords = (keywords) => createInputGuardrail(
|
|
106
|
+
"blocked-keywords",
|
|
107
|
+
"Blocks input containing specified keywords",
|
|
108
|
+
(context) => {
|
|
109
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
110
|
+
const allText = [
|
|
111
|
+
prompt,
|
|
112
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
113
|
+
system
|
|
114
|
+
].join(" ").toLowerCase();
|
|
115
|
+
const blockedWord = keywords.find(
|
|
116
|
+
(word) => allText.includes(word.toLowerCase())
|
|
117
|
+
);
|
|
118
|
+
return {
|
|
119
|
+
tripwireTriggered: !!blockedWord,
|
|
120
|
+
message: blockedWord ? `Blocked keyword detected: ${blockedWord}` : void 0,
|
|
121
|
+
severity: "high",
|
|
122
|
+
metadata: {
|
|
123
|
+
blockedWord,
|
|
124
|
+
allKeywords: keywords,
|
|
125
|
+
textLength: allText.length
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
var rateLimiting = (maxRequestsPerMinute) => {
|
|
131
|
+
const requestCounts = /* @__PURE__ */ new Map();
|
|
132
|
+
return createInputGuardrail(
|
|
133
|
+
"rate-limiting",
|
|
134
|
+
"Enforces rate limiting per minute",
|
|
135
|
+
(inputContext) => {
|
|
136
|
+
const { prompt } = extractTextContent(inputContext);
|
|
137
|
+
const { model, temperature, maxTokens } = extractMetadata(inputContext);
|
|
138
|
+
let contextData;
|
|
139
|
+
if (hasContextProperty(inputContext)) {
|
|
140
|
+
contextData = inputContext.context;
|
|
141
|
+
}
|
|
142
|
+
const key = contextData?.user?.id || contextData?.request?.ip || "default";
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
const windowMs = 6e4;
|
|
145
|
+
const current = requestCounts.get(key) || {
|
|
146
|
+
count: 0,
|
|
147
|
+
resetTime: now + windowMs
|
|
148
|
+
};
|
|
149
|
+
if (now > current.resetTime) {
|
|
150
|
+
current.count = 0;
|
|
151
|
+
current.resetTime = now + windowMs;
|
|
152
|
+
}
|
|
153
|
+
current.count++;
|
|
154
|
+
requestCounts.set(key, current);
|
|
155
|
+
return {
|
|
156
|
+
tripwireTriggered: current.count > maxRequestsPerMinute,
|
|
157
|
+
message: `Rate limit exceeded: ${current.count}/${maxRequestsPerMinute} requests per minute`,
|
|
158
|
+
severity: "medium",
|
|
159
|
+
metadata: {
|
|
160
|
+
currentCount: current.count,
|
|
161
|
+
maxRequests: maxRequestsPerMinute,
|
|
162
|
+
resetTime: current.resetTime,
|
|
163
|
+
userId: contextData?.user?.id,
|
|
164
|
+
userIp: contextData?.request?.ip,
|
|
165
|
+
model: model ? String(model) : void 0,
|
|
166
|
+
temperature,
|
|
167
|
+
maxTokens,
|
|
168
|
+
promptLength: prompt.length
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
var profanityFilter = (customWords = []) => {
|
|
175
|
+
const defaultProfanity = ["profanity1", "profanity2"];
|
|
176
|
+
const allWords = [...defaultProfanity, ...customWords];
|
|
177
|
+
return createInputGuardrail(
|
|
178
|
+
"profanity-filter",
|
|
179
|
+
"Filters profanity and inappropriate language",
|
|
180
|
+
(context) => {
|
|
181
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
182
|
+
const allText = [
|
|
183
|
+
prompt,
|
|
184
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
185
|
+
system
|
|
186
|
+
].join(" ").toLowerCase();
|
|
187
|
+
const profaneWord = allWords.find(
|
|
188
|
+
(word) => allText.includes(word.toLowerCase())
|
|
189
|
+
);
|
|
190
|
+
return {
|
|
191
|
+
tripwireTriggered: !!profaneWord,
|
|
192
|
+
message: profaneWord ? `Profanity detected: ${profaneWord}` : void 0,
|
|
193
|
+
severity: "high",
|
|
194
|
+
metadata: {
|
|
195
|
+
profaneWord,
|
|
196
|
+
allWords,
|
|
197
|
+
textLength: allText.length
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
var customValidation = (name, description, validator, message) => createInputGuardrail(name, description, (context) => {
|
|
204
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
205
|
+
const { model, temperature, maxTokens } = extractMetadata(context);
|
|
206
|
+
const input = { prompt, messages, system, model, temperature, maxTokens };
|
|
207
|
+
const blocked = validator(input);
|
|
208
|
+
return {
|
|
209
|
+
tripwireTriggered: blocked,
|
|
210
|
+
message: blocked ? message : void 0,
|
|
211
|
+
severity: "medium",
|
|
212
|
+
metadata: {
|
|
213
|
+
validatorName: name,
|
|
214
|
+
inputKeys: Object.keys(input),
|
|
215
|
+
model: model ? String(model) : void 0,
|
|
216
|
+
temperature,
|
|
217
|
+
maxTokens
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
var promptInjectionDetector = () => createInputGuardrail(
|
|
222
|
+
"prompt-injection-detector",
|
|
223
|
+
"Detects potential prompt injection attempts",
|
|
224
|
+
(context) => {
|
|
225
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
226
|
+
const allText = [
|
|
227
|
+
prompt,
|
|
228
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
229
|
+
system
|
|
230
|
+
].join(" ");
|
|
231
|
+
const injectionPatterns = [
|
|
232
|
+
/ignore\s+previous\s+instructions/i,
|
|
233
|
+
/system\s*:\s*you\s+are\s+now/i,
|
|
234
|
+
/forget\s+everything\s+above/i,
|
|
235
|
+
/\bDAN\b.*mode/i,
|
|
236
|
+
/jailbreak/i,
|
|
237
|
+
/act\s+as\s+if\s+you\s+are/i,
|
|
238
|
+
/pretend\s+to\s+be/i,
|
|
239
|
+
/role\s*:\s*system/i
|
|
240
|
+
];
|
|
241
|
+
const detectedPatterns = injectionPatterns.filter(
|
|
242
|
+
(pattern) => pattern.test(allText)
|
|
243
|
+
);
|
|
244
|
+
return {
|
|
245
|
+
tripwireTriggered: detectedPatterns.length > 0,
|
|
246
|
+
message: detectedPatterns.length > 0 ? `Potential prompt injection detected: ${detectedPatterns.length} suspicious patterns found` : void 0,
|
|
247
|
+
severity: "critical",
|
|
248
|
+
metadata: {
|
|
249
|
+
patternsDetected: detectedPatterns.length,
|
|
250
|
+
textLength: allText.length,
|
|
251
|
+
suspiciousPatterns: detectedPatterns.map((p) => p.source)
|
|
252
|
+
},
|
|
253
|
+
suggestion: "Please rephrase your request without system instructions or role-playing elements"
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
var piiDetector = () => createInputGuardrail(
|
|
258
|
+
"pii-detector",
|
|
259
|
+
"Detects personally identifiable information in input",
|
|
260
|
+
(context) => {
|
|
261
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
262
|
+
const allText = [
|
|
263
|
+
prompt,
|
|
264
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
265
|
+
system
|
|
266
|
+
].join(" ");
|
|
267
|
+
const piiPatterns = {
|
|
268
|
+
email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
|
|
269
|
+
phone: /\b(?:\+?1[-.\s]?)?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})\b/g,
|
|
270
|
+
ssn: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
271
|
+
creditCard: /\b(?:\d{4}[\s-]?){3}\d{4}\b/g,
|
|
272
|
+
ipAddress: /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g
|
|
273
|
+
};
|
|
274
|
+
const detectedPII = [];
|
|
275
|
+
const matches = [];
|
|
276
|
+
for (const [type, pattern] of Object.entries(piiPatterns)) {
|
|
277
|
+
const found = allText.match(pattern);
|
|
278
|
+
if (found) {
|
|
279
|
+
detectedPII.push(type);
|
|
280
|
+
matches.push(...found);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
tripwireTriggered: detectedPII.length > 0,
|
|
285
|
+
message: detectedPII.length > 0 ? `PII detected: ${detectedPII.join(", ")}` : void 0,
|
|
286
|
+
severity: "critical",
|
|
287
|
+
metadata: {
|
|
288
|
+
piiTypes: detectedPII,
|
|
289
|
+
matchCount: matches.length,
|
|
290
|
+
textLength: allText.length
|
|
291
|
+
},
|
|
292
|
+
suggestion: "Please remove any personal information (emails, phone numbers, SSNs, etc.) from your input"
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
var toxicityDetector = (threshold = 0.7) => createInputGuardrail(
|
|
297
|
+
"toxicity-detector",
|
|
298
|
+
"Detects toxic and harmful content in input",
|
|
299
|
+
(context) => {
|
|
300
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
301
|
+
const allText = [
|
|
302
|
+
prompt,
|
|
303
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
304
|
+
system
|
|
305
|
+
].join(" ").toLowerCase();
|
|
306
|
+
const toxicWords = [
|
|
307
|
+
"hate",
|
|
308
|
+
"kill",
|
|
309
|
+
"die",
|
|
310
|
+
"stupid",
|
|
311
|
+
"idiot",
|
|
312
|
+
"moron",
|
|
313
|
+
"toxic",
|
|
314
|
+
"harmful"
|
|
315
|
+
];
|
|
316
|
+
const detectedWords = toxicWords.filter((word) => allText.includes(word));
|
|
317
|
+
const toxicityScore = detectedWords.length * 0.3;
|
|
318
|
+
return {
|
|
319
|
+
tripwireTriggered: toxicityScore > threshold,
|
|
320
|
+
message: toxicityScore > threshold ? `Toxic content detected (score: ${toxicityScore})` : void 0,
|
|
321
|
+
severity: toxicityScore > 0.8 ? "critical" : "high",
|
|
322
|
+
metadata: {
|
|
323
|
+
toxicityScore,
|
|
324
|
+
threshold,
|
|
325
|
+
detectedWords,
|
|
326
|
+
textLength: allText.length
|
|
327
|
+
},
|
|
328
|
+
suggestion: "Please use respectful and constructive language"
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
var mathHomeworkDetector = () => createInputGuardrail(
|
|
333
|
+
"math-homework-detector",
|
|
334
|
+
"Detects potential math homework requests",
|
|
335
|
+
(context) => {
|
|
336
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
337
|
+
const allText = [
|
|
338
|
+
prompt,
|
|
339
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
340
|
+
system
|
|
341
|
+
].join(" ");
|
|
342
|
+
const mathKeywords = [
|
|
343
|
+
"solve",
|
|
344
|
+
"calculate",
|
|
345
|
+
"equation",
|
|
346
|
+
"homework",
|
|
347
|
+
"assignment",
|
|
348
|
+
"problem set"
|
|
349
|
+
];
|
|
350
|
+
const mathPatterns = [
|
|
351
|
+
/\b\d+\s*[+\-*/]\s*\d+/g,
|
|
352
|
+
/\b[xy]\s*[+\-*/=]\s*\d+/g,
|
|
353
|
+
/\b(derivative|integral|limit|theorem|proof)/gi,
|
|
354
|
+
/find\s+the\s+(value|solution|answer)/i
|
|
355
|
+
];
|
|
356
|
+
const keywordMatches = mathKeywords.filter(
|
|
357
|
+
(keyword) => allText.toLowerCase().includes(keyword)
|
|
358
|
+
);
|
|
359
|
+
const patternMatches = mathPatterns.filter(
|
|
360
|
+
(pattern) => pattern.test(allText)
|
|
361
|
+
);
|
|
362
|
+
const isMathHomework = keywordMatches.length >= 2 || patternMatches.length > 0;
|
|
363
|
+
return {
|
|
364
|
+
tripwireTriggered: isMathHomework,
|
|
365
|
+
message: isMathHomework ? "Math homework request detected" : void 0,
|
|
366
|
+
severity: "high",
|
|
367
|
+
metadata: {
|
|
368
|
+
keywordMatches,
|
|
369
|
+
patternMatches: patternMatches.length,
|
|
370
|
+
textLength: allText.length,
|
|
371
|
+
confidence: isMathHomework ? 0.85 : 0.15
|
|
372
|
+
},
|
|
373
|
+
suggestion: "Try asking about learning concepts instead of solving specific problems"
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
var codeGenerationLimiter = (allowedLanguages = []) => createInputGuardrail(
|
|
378
|
+
"code-generation-limiter",
|
|
379
|
+
"Limits code generation to specified languages",
|
|
380
|
+
(context) => {
|
|
381
|
+
const { prompt, messages, system } = extractTextContent(context);
|
|
382
|
+
const allText = [
|
|
383
|
+
prompt,
|
|
384
|
+
...messages.map((msg) => String(msg?.content || "")),
|
|
385
|
+
system
|
|
386
|
+
].join(" ").toLowerCase();
|
|
387
|
+
const codeKeywords = [
|
|
388
|
+
"write code",
|
|
389
|
+
"generate code",
|
|
390
|
+
"create function",
|
|
391
|
+
"implement",
|
|
392
|
+
"script"
|
|
393
|
+
];
|
|
394
|
+
const languagePatterns = {
|
|
395
|
+
javascript: /\b(javascript|js|node|react|angular|vue)\b/gi,
|
|
396
|
+
python: /\b(python|py|django|flask|pandas)\b/gi,
|
|
397
|
+
java: /\b(java|spring|hibernate)\b/gi,
|
|
398
|
+
cpp: /\b(c\+\+|cpp|c plus plus)\b/gi,
|
|
399
|
+
csharp: /\b(c#|csharp|dotnet|asp\.net)\b/gi,
|
|
400
|
+
php: /\b(php|laravel|symfony)\b/gi,
|
|
401
|
+
ruby: /\b(ruby|rails|gem)\b/gi,
|
|
402
|
+
go: /\b(golang|go)\b/gi,
|
|
403
|
+
rust: /\b(rust|cargo)\b/gi,
|
|
404
|
+
sql: /\b(sql|mysql|postgresql|oracle)\b/gi
|
|
405
|
+
};
|
|
406
|
+
const hasCodeRequest = codeKeywords.some(
|
|
407
|
+
(keyword) => allText.includes(keyword)
|
|
408
|
+
);
|
|
409
|
+
const detectedLanguages = [];
|
|
410
|
+
for (const [lang, pattern] of Object.entries(languagePatterns)) {
|
|
411
|
+
if (pattern.test(allText)) {
|
|
412
|
+
detectedLanguages.push(lang);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const hasRestrictedLanguage = hasCodeRequest && detectedLanguages.length > 0 && !detectedLanguages.some((lang) => allowedLanguages.includes(lang));
|
|
416
|
+
return {
|
|
417
|
+
tripwireTriggered: hasRestrictedLanguage,
|
|
418
|
+
message: hasRestrictedLanguage ? `Code generation requested for restricted language(s): ${detectedLanguages.join(", ")}` : void 0,
|
|
419
|
+
severity: "medium",
|
|
420
|
+
metadata: {
|
|
421
|
+
hasCodeRequest,
|
|
422
|
+
detectedLanguages,
|
|
423
|
+
allowedLanguages,
|
|
424
|
+
textLength: allText.length
|
|
425
|
+
},
|
|
426
|
+
suggestion: allowedLanguages.length > 0 ? `Please request code only in allowed languages: ${allowedLanguages.join(", ")}` : "Code generation is not allowed"
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
export {
|
|
431
|
+
blockedKeywords,
|
|
432
|
+
blockedWords,
|
|
433
|
+
codeGenerationLimiter,
|
|
434
|
+
contentLengthLimit,
|
|
435
|
+
customValidation,
|
|
436
|
+
extractMetadata,
|
|
437
|
+
extractTextContent,
|
|
438
|
+
lengthLimit,
|
|
439
|
+
mathHomeworkDetector,
|
|
440
|
+
piiDetector,
|
|
441
|
+
profanityFilter,
|
|
442
|
+
promptInjectionDetector,
|
|
443
|
+
rateLimiting,
|
|
444
|
+
toxicityDetector
|
|
445
|
+
};
|