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