substrai-guardrailgraph 0.1.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/dist/checks/cost.d.ts +14 -0
- package/dist/checks/cost.js +28 -0
- package/dist/checks/index.d.ts +5 -0
- package/dist/checks/index.js +13 -0
- package/dist/checks/injection.d.ts +12 -0
- package/dist/checks/injection.js +80 -0
- package/dist/checks/pii.d.ts +14 -0
- package/dist/checks/pii.js +63 -0
- package/dist/checks/topics.d.ts +14 -0
- package/dist/checks/topics.js +36 -0
- package/dist/checks/toxicity.d.ts +12 -0
- package/dist/checks/toxicity.js +50 -0
- package/dist/core/actions.d.ts +12 -0
- package/dist/core/actions.js +22 -0
- package/dist/core/check.d.ts +40 -0
- package/dist/core/check.js +102 -0
- package/dist/core/context.d.ts +11 -0
- package/dist/core/context.js +16 -0
- package/dist/core/pipeline.d.ts +41 -0
- package/dist/core/pipeline.js +167 -0
- package/dist/core/result.d.ts +28 -0
- package/dist/core/result.js +29 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +75 -0
- package/dist/middleware/index.d.ts +37 -0
- package/dist/middleware/index.js +49 -0
- package/dist/packs/financial.d.ts +8 -0
- package/dist/packs/financial.js +59 -0
- package/dist/packs/hipaa.d.ts +9 -0
- package/dist/packs/hipaa.js +55 -0
- package/dist/packs/index.d.ts +2 -0
- package/dist/packs/index.js +38 -0
- package/package.json +47 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in cost limiting check.
|
|
3
|
+
*/
|
|
4
|
+
import { Action } from "../core/actions";
|
|
5
|
+
import { Check } from "../core/check";
|
|
6
|
+
export interface CostCheckOptions {
|
|
7
|
+
maxTokensPerRequest?: number;
|
|
8
|
+
maxCostPerRequest?: number;
|
|
9
|
+
costPer1kInputTokens?: number;
|
|
10
|
+
action?: Action;
|
|
11
|
+
threshold?: number;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function costCheck(options?: CostCheckOptions): Check;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in cost limiting check.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.costCheck = costCheck;
|
|
7
|
+
const actions_1 = require("../core/actions");
|
|
8
|
+
const check_1 = require("../core/check");
|
|
9
|
+
function costCheck(options = {}) {
|
|
10
|
+
const { maxTokensPerRequest = 4000, maxCostPerRequest = 0.10, costPer1kInputTokens = 0.00025, action = actions_1.Action.BLOCK, threshold = 0.5, name = "cost-limit", } = options;
|
|
11
|
+
return new check_1.Check((text) => {
|
|
12
|
+
const estimatedTokens = Math.max(1, Math.floor(text.length / 4));
|
|
13
|
+
const estimatedCost = (estimatedTokens / 1000) * costPer1kInputTokens;
|
|
14
|
+
const tokenExceeded = estimatedTokens > maxTokensPerRequest;
|
|
15
|
+
const costExceeded = estimatedCost > maxCostPerRequest;
|
|
16
|
+
const detected = tokenExceeded || costExceeded;
|
|
17
|
+
return {
|
|
18
|
+
detected,
|
|
19
|
+
confidence: detected ? 1.0 : 0.0,
|
|
20
|
+
estimatedTokens,
|
|
21
|
+
estimatedCostUsd: estimatedCost,
|
|
22
|
+
maxTokens: maxTokensPerRequest,
|
|
23
|
+
maxCostUsd: maxCostPerRequest,
|
|
24
|
+
tokenExceeded,
|
|
25
|
+
costExceeded,
|
|
26
|
+
};
|
|
27
|
+
}, { name, action, threshold });
|
|
28
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { piiCheck, PiiCheckOptions } from "./pii";
|
|
2
|
+
export { toxicityCheck, ToxicityCheckOptions } from "./toxicity";
|
|
3
|
+
export { topicCheck, TopicCheckOptions } from "./topics";
|
|
4
|
+
export { injectionCheck, InjectionCheckOptions } from "./injection";
|
|
5
|
+
export { costCheck, CostCheckOptions } from "./cost";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.costCheck = exports.injectionCheck = exports.topicCheck = exports.toxicityCheck = exports.piiCheck = void 0;
|
|
4
|
+
var pii_1 = require("./pii");
|
|
5
|
+
Object.defineProperty(exports, "piiCheck", { enumerable: true, get: function () { return pii_1.piiCheck; } });
|
|
6
|
+
var toxicity_1 = require("./toxicity");
|
|
7
|
+
Object.defineProperty(exports, "toxicityCheck", { enumerable: true, get: function () { return toxicity_1.toxicityCheck; } });
|
|
8
|
+
var topics_1 = require("./topics");
|
|
9
|
+
Object.defineProperty(exports, "topicCheck", { enumerable: true, get: function () { return topics_1.topicCheck; } });
|
|
10
|
+
var injection_1 = require("./injection");
|
|
11
|
+
Object.defineProperty(exports, "injectionCheck", { enumerable: true, get: function () { return injection_1.injectionCheck; } });
|
|
12
|
+
var cost_1 = require("./cost");
|
|
13
|
+
Object.defineProperty(exports, "costCheck", { enumerable: true, get: function () { return cost_1.costCheck; } });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in prompt injection detection check.
|
|
3
|
+
*/
|
|
4
|
+
import { Action } from "../core/actions";
|
|
5
|
+
import { Check } from "../core/check";
|
|
6
|
+
export interface InjectionCheckOptions {
|
|
7
|
+
sensitivity?: "low" | "medium" | "high";
|
|
8
|
+
action?: Action;
|
|
9
|
+
threshold?: number;
|
|
10
|
+
name?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function injectionCheck(options?: InjectionCheckOptions): Check;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in prompt injection detection check.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.injectionCheck = injectionCheck;
|
|
7
|
+
const actions_1 = require("../core/actions");
|
|
8
|
+
const check_1 = require("../core/check");
|
|
9
|
+
const INJECTION_PATTERNS = [
|
|
10
|
+
{
|
|
11
|
+
name: "instruction_override",
|
|
12
|
+
patterns: [
|
|
13
|
+
/ignore\s+(all\s+)?(previous|prior|above)\s+(instructions?|prompts?|rules?)/i,
|
|
14
|
+
/disregard\s+(all\s+)?(previous|prior|above)/i,
|
|
15
|
+
/forget\s+(everything|all|your)\s+(instructions?|rules?|training)/i,
|
|
16
|
+
/override\s+(your|the|all)\s+(instructions?|rules?|constraints?)/i,
|
|
17
|
+
/new\s+instructions?\s*:/i,
|
|
18
|
+
],
|
|
19
|
+
severity: 0.95,
|
|
20
|
+
category: "override",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "role_manipulation",
|
|
24
|
+
patterns: [
|
|
25
|
+
/you\s+are\s+now\s+(?:a\s+)?(?:DAN|evil|unrestricted|jailbroken)/i,
|
|
26
|
+
/pretend\s+(?:you\s+are|to\s+be)\s+(?:a\s+)?(?:different|evil|unrestricted)/i,
|
|
27
|
+
/act\s+as\s+(?:if|though)\s+you\s+(?:have\s+)?no\s+(?:rules|restrictions|limits)/i,
|
|
28
|
+
/enter\s+(?:DAN|developer|admin|god)\s+mode/i,
|
|
29
|
+
],
|
|
30
|
+
severity: 0.9,
|
|
31
|
+
category: "role_play",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "system_prompt_extraction",
|
|
35
|
+
patterns: [
|
|
36
|
+
/(?:show|reveal|display|print|output)\s+(?:me\s+)?(?:your|the)\s+(?:system\s+)?(?:prompt|instructions)/i,
|
|
37
|
+
/what\s+(?:are|is)\s+your\s+(?:system\s+)?(?:prompt|instructions|rules)/i,
|
|
38
|
+
/repeat\s+(?:your|the)\s+(?:system\s+)?(?:prompt|instructions)\s+(?:back|verbatim)/i,
|
|
39
|
+
],
|
|
40
|
+
severity: 0.8,
|
|
41
|
+
category: "extraction",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "delimiter_injection",
|
|
45
|
+
patterns: [
|
|
46
|
+
/<\/?system>/i,
|
|
47
|
+
/\[\/?INST\]/i,
|
|
48
|
+
/```\s*system/i,
|
|
49
|
+
/<\|(?:im_start|im_end|system|endoftext)\|>/i,
|
|
50
|
+
],
|
|
51
|
+
severity: 0.85,
|
|
52
|
+
category: "delimiter",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
function injectionCheck(options = {}) {
|
|
56
|
+
const { sensitivity = "high", action = actions_1.Action.BLOCK, threshold = 0.6, name = "prompt-injection", } = options;
|
|
57
|
+
const thresholdMap = { low: 0.9, medium: 0.75, high: 0.6 };
|
|
58
|
+
const sensitivityThreshold = thresholdMap[sensitivity] || 0.75;
|
|
59
|
+
return new check_1.Check((text) => {
|
|
60
|
+
const matches = [];
|
|
61
|
+
let maxSeverity = 0;
|
|
62
|
+
for (const group of INJECTION_PATTERNS) {
|
|
63
|
+
for (const pattern of group.patterns) {
|
|
64
|
+
if (pattern.test(text)) {
|
|
65
|
+
matches.push({ name: group.name, category: group.category, severity: group.severity });
|
|
66
|
+
maxSeverity = Math.max(maxSeverity, group.severity);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const detected = maxSeverity >= sensitivityThreshold;
|
|
72
|
+
return {
|
|
73
|
+
detected,
|
|
74
|
+
confidence: maxSeverity,
|
|
75
|
+
matches,
|
|
76
|
+
matchCount: matches.length,
|
|
77
|
+
categories: [...new Set(matches.map((m) => m.category))],
|
|
78
|
+
};
|
|
79
|
+
}, { name, action, threshold });
|
|
80
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in PII detection and redaction check.
|
|
3
|
+
*/
|
|
4
|
+
import { Action } from "../core/actions";
|
|
5
|
+
import { Check } from "../core/check";
|
|
6
|
+
export interface PiiCheckOptions {
|
|
7
|
+
entityTypes?: string[];
|
|
8
|
+
redactionChar?: string;
|
|
9
|
+
sensitivity?: string;
|
|
10
|
+
action?: Action;
|
|
11
|
+
threshold?: number;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function piiCheck(options?: PiiCheckOptions): Check;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in PII detection and redaction check.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.piiCheck = piiCheck;
|
|
7
|
+
const actions_1 = require("../core/actions");
|
|
8
|
+
const check_1 = require("../core/check");
|
|
9
|
+
const PII_PATTERNS = {
|
|
10
|
+
SSN: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
11
|
+
PHONE: /\b(?:\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
12
|
+
EMAIL: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
|
|
13
|
+
CREDIT_CARD: /\b(?:\d{4}[-\s]?){3}\d{4}\b/g,
|
|
14
|
+
IP_ADDRESS: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
|
|
15
|
+
DATE_OF_BIRTH: /\b(?:0[1-9]|1[0-2])[/-](?:0[1-9]|[12]\d|3[01])[/-](?:19|20)\d{2}\b/g,
|
|
16
|
+
};
|
|
17
|
+
function detectPii(text, entityTypes) {
|
|
18
|
+
const entities = [];
|
|
19
|
+
const patterns = entityTypes
|
|
20
|
+
? Object.entries(PII_PATTERNS).filter(([k]) => entityTypes.includes(k))
|
|
21
|
+
: Object.entries(PII_PATTERNS);
|
|
22
|
+
for (const [type, pattern] of patterns) {
|
|
23
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
24
|
+
let match;
|
|
25
|
+
while ((match = regex.exec(text)) !== null) {
|
|
26
|
+
entities.push({
|
|
27
|
+
type,
|
|
28
|
+
value: match[0],
|
|
29
|
+
start: match.index,
|
|
30
|
+
end: match.index + match[0].length,
|
|
31
|
+
confidence: 0.95,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return entities.sort((a, b) => a.start - b.start);
|
|
36
|
+
}
|
|
37
|
+
function redactText(text, entities) {
|
|
38
|
+
let result = text;
|
|
39
|
+
const sorted = [...entities].sort((a, b) => b.start - a.start);
|
|
40
|
+
for (const entity of sorted) {
|
|
41
|
+
result = result.slice(0, entity.start) + `[${entity.type}]` + result.slice(entity.end);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function piiCheck(options = {}) {
|
|
46
|
+
const { entityTypes, action = actions_1.Action.REDACT, threshold = 0.5, name = "pii-detection" } = options;
|
|
47
|
+
return new check_1.Check((text) => {
|
|
48
|
+
const entities = detectPii(text, entityTypes);
|
|
49
|
+
if (entities.length === 0) {
|
|
50
|
+
return { detected: false, confidence: 0 };
|
|
51
|
+
}
|
|
52
|
+
const maxConfidence = Math.max(...entities.map((e) => e.confidence));
|
|
53
|
+
const redacted = redactText(text, entities);
|
|
54
|
+
return {
|
|
55
|
+
detected: true,
|
|
56
|
+
confidence: maxConfidence,
|
|
57
|
+
entities: entities.map((e) => ({ type: e.type, value: e.value })),
|
|
58
|
+
redactedText: redacted,
|
|
59
|
+
entityCount: entities.length,
|
|
60
|
+
entityTypes: [...new Set(entities.map((e) => e.type))],
|
|
61
|
+
};
|
|
62
|
+
}, { name, action, threshold });
|
|
63
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in topic restriction check.
|
|
3
|
+
*/
|
|
4
|
+
import { Action } from "../core/actions";
|
|
5
|
+
import { Check } from "../core/check";
|
|
6
|
+
export interface TopicCheckOptions {
|
|
7
|
+
blockedTopics?: string[];
|
|
8
|
+
allowedTopics?: string[];
|
|
9
|
+
mode?: "blocklist" | "allowlist";
|
|
10
|
+
action?: Action;
|
|
11
|
+
threshold?: number;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function topicCheck(options?: TopicCheckOptions): Check;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in topic restriction check.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.topicCheck = topicCheck;
|
|
7
|
+
const actions_1 = require("../core/actions");
|
|
8
|
+
const check_1 = require("../core/check");
|
|
9
|
+
function topicCheck(options = {}) {
|
|
10
|
+
const { blockedTopics = [], allowedTopics = [], mode = "blocklist", action = actions_1.Action.BLOCK, threshold = 0.5, name = "topic-restriction", } = options;
|
|
11
|
+
return new check_1.Check((text) => {
|
|
12
|
+
const textLower = text.toLowerCase();
|
|
13
|
+
if (mode === "blocklist") {
|
|
14
|
+
const matched = blockedTopics.filter((t) => textLower.includes(t.toLowerCase()));
|
|
15
|
+
return {
|
|
16
|
+
detected: matched.length > 0,
|
|
17
|
+
confidence: matched.length > 0 ? 1.0 : 0.0,
|
|
18
|
+
matchedTopics: matched,
|
|
19
|
+
mode: "blocklist",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const matched = allowedTopics.filter((t) => textLower.includes(t.toLowerCase()));
|
|
24
|
+
if (matched.length > 0) {
|
|
25
|
+
return { detected: false, confidence: 0, matchedTopics: matched, mode: "allowlist" };
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
detected: true,
|
|
29
|
+
confidence: 0.8,
|
|
30
|
+
matchedTopics: [],
|
|
31
|
+
mode: "allowlist",
|
|
32
|
+
reason: "Content does not match any allowed topic",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}, { name, action, threshold });
|
|
36
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in toxicity detection check.
|
|
3
|
+
*/
|
|
4
|
+
import { Action } from "../core/actions";
|
|
5
|
+
import { Check } from "../core/check";
|
|
6
|
+
export interface ToxicityCheckOptions {
|
|
7
|
+
categories?: string[];
|
|
8
|
+
threshold?: number;
|
|
9
|
+
action?: Action;
|
|
10
|
+
name?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function toxicityCheck(options?: ToxicityCheckOptions): Check;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in toxicity detection check.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.toxicityCheck = toxicityCheck;
|
|
7
|
+
const actions_1 = require("../core/actions");
|
|
8
|
+
const check_1 = require("../core/check");
|
|
9
|
+
const TOXICITY_KEYWORDS = {
|
|
10
|
+
hate: ["hate", "racist", "bigot", "supremacist", "inferior race", "ethnic slur", "discrimination"],
|
|
11
|
+
violence: ["kill", "murder", "attack", "bomb", "shoot", "stab", "assault", "destroy", "weapon", "explosive"],
|
|
12
|
+
sexual: ["explicit", "pornographic", "nude", "sexual act"],
|
|
13
|
+
self_harm: ["suicide", "self-harm", "cut myself", "end my life", "kill myself", "overdose"],
|
|
14
|
+
harassment: ["threaten", "bully", "stalk", "intimidate", "harass", "doxx", "blackmail"],
|
|
15
|
+
};
|
|
16
|
+
const CATEGORY_WEIGHTS = {
|
|
17
|
+
hate: 0.9,
|
|
18
|
+
violence: 0.95,
|
|
19
|
+
sexual: 0.7,
|
|
20
|
+
self_harm: 1.0,
|
|
21
|
+
harassment: 0.85,
|
|
22
|
+
};
|
|
23
|
+
function toxicityCheck(options = {}) {
|
|
24
|
+
const { categories, threshold = 0.7, action = actions_1.Action.BLOCK, name = "toxicity", } = options;
|
|
25
|
+
const activeCategories = categories || Object.keys(TOXICITY_KEYWORDS);
|
|
26
|
+
return new check_1.Check((text) => {
|
|
27
|
+
const textLower = text.toLowerCase();
|
|
28
|
+
const categoryScores = {};
|
|
29
|
+
const matchedTerms = {};
|
|
30
|
+
for (const category of activeCategories) {
|
|
31
|
+
const keywords = TOXICITY_KEYWORDS[category] || [];
|
|
32
|
+
const matches = keywords.filter((kw) => textLower.includes(kw));
|
|
33
|
+
if (matches.length > 0) {
|
|
34
|
+
const weight = CATEGORY_WEIGHTS[category] || 0.8;
|
|
35
|
+
categoryScores[category] = Math.min(matches.length / 3.0, 1.0) * weight;
|
|
36
|
+
matchedTerms[category] = matches;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
categoryScores[category] = 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const overallScore = Math.max(...Object.values(categoryScores), 0);
|
|
43
|
+
return {
|
|
44
|
+
detected: overallScore >= threshold,
|
|
45
|
+
confidence: overallScore,
|
|
46
|
+
categoryScores,
|
|
47
|
+
matchedTerms,
|
|
48
|
+
};
|
|
49
|
+
}, { name, action, threshold });
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action definitions for guardrail check outcomes.
|
|
3
|
+
*/
|
|
4
|
+
export declare enum Action {
|
|
5
|
+
PASS = "pass",
|
|
6
|
+
BLOCK = "block",
|
|
7
|
+
REDACT = "redact",
|
|
8
|
+
FLAG_FOR_REVIEW = "flag_for_review",
|
|
9
|
+
LOG = "log"
|
|
10
|
+
}
|
|
11
|
+
export declare function isBlocking(action: Action): boolean;
|
|
12
|
+
export declare function isModifying(action: Action): boolean;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Action definitions for guardrail check outcomes.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Action = void 0;
|
|
7
|
+
exports.isBlocking = isBlocking;
|
|
8
|
+
exports.isModifying = isModifying;
|
|
9
|
+
var Action;
|
|
10
|
+
(function (Action) {
|
|
11
|
+
Action["PASS"] = "pass";
|
|
12
|
+
Action["BLOCK"] = "block";
|
|
13
|
+
Action["REDACT"] = "redact";
|
|
14
|
+
Action["FLAG_FOR_REVIEW"] = "flag_for_review";
|
|
15
|
+
Action["LOG"] = "log";
|
|
16
|
+
})(Action || (exports.Action = Action = {}));
|
|
17
|
+
function isBlocking(action) {
|
|
18
|
+
return action === Action.BLOCK;
|
|
19
|
+
}
|
|
20
|
+
function isModifying(action) {
|
|
21
|
+
return action === Action.REDACT;
|
|
22
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Check class and check() factory — turns functions into guardrail checks.
|
|
3
|
+
*/
|
|
4
|
+
import { Action } from "./actions";
|
|
5
|
+
import { CheckContext } from "./context";
|
|
6
|
+
import { CheckResult } from "./result";
|
|
7
|
+
export type CheckFunction = (text: string, context?: CheckContext) => CheckFunctionResult | Promise<CheckFunctionResult>;
|
|
8
|
+
export type CheckFunctionResult = {
|
|
9
|
+
detected: boolean;
|
|
10
|
+
confidence: number;
|
|
11
|
+
redactedText?: string;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
} | boolean;
|
|
14
|
+
export interface CheckOptions {
|
|
15
|
+
name?: string;
|
|
16
|
+
action?: Action;
|
|
17
|
+
threshold?: number;
|
|
18
|
+
description?: string;
|
|
19
|
+
tags?: string[];
|
|
20
|
+
dependsOn?: string[];
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class Check {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly action: Action;
|
|
26
|
+
readonly threshold: number;
|
|
27
|
+
readonly description: string;
|
|
28
|
+
readonly tags: string[];
|
|
29
|
+
readonly dependsOn: string[];
|
|
30
|
+
readonly timeoutMs?: number;
|
|
31
|
+
private readonly fn;
|
|
32
|
+
constructor(fn: CheckFunction, options?: CheckOptions);
|
|
33
|
+
execute(text: string, context?: CheckContext): Promise<CheckResult>;
|
|
34
|
+
executeSync(text: string, context?: CheckContext): CheckResult;
|
|
35
|
+
private normalizeResult;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Factory function to create a Check instance.
|
|
39
|
+
*/
|
|
40
|
+
export declare function check(options?: CheckOptions): (fn: CheckFunction) => Check;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The Check class and check() factory — turns functions into guardrail checks.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Check = void 0;
|
|
7
|
+
exports.check = check;
|
|
8
|
+
const actions_1 = require("./actions");
|
|
9
|
+
const context_1 = require("./context");
|
|
10
|
+
class Check {
|
|
11
|
+
constructor(fn, options = {}) {
|
|
12
|
+
this.fn = fn;
|
|
13
|
+
this.name = options.name || fn.name || "unnamed-check";
|
|
14
|
+
this.action = options.action || actions_1.Action.BLOCK;
|
|
15
|
+
this.threshold = options.threshold ?? 0.5;
|
|
16
|
+
this.description = options.description || "";
|
|
17
|
+
this.tags = options.tags || [];
|
|
18
|
+
this.dependsOn = options.dependsOn || [];
|
|
19
|
+
this.timeoutMs = options.timeoutMs;
|
|
20
|
+
}
|
|
21
|
+
async execute(text, context) {
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
const ctx = context || (0, context_1.createContext)();
|
|
24
|
+
try {
|
|
25
|
+
const raw = await this.fn(text, ctx);
|
|
26
|
+
const result = this.normalizeResult(raw);
|
|
27
|
+
result.latencyMs = Date.now() - start;
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return {
|
|
32
|
+
name: this.name,
|
|
33
|
+
detected: false,
|
|
34
|
+
confidence: 0,
|
|
35
|
+
action: actions_1.Action.PASS,
|
|
36
|
+
details: {},
|
|
37
|
+
latencyMs: Date.now() - start,
|
|
38
|
+
error: `Check failed: ${err.message || err}`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
executeSync(text, context) {
|
|
43
|
+
// For sync contexts, we run the function directly if it's not async
|
|
44
|
+
const start = Date.now();
|
|
45
|
+
const ctx = context || (0, context_1.createContext)();
|
|
46
|
+
try {
|
|
47
|
+
const raw = this.fn(text, ctx);
|
|
48
|
+
if (raw instanceof Promise) {
|
|
49
|
+
throw new Error("Cannot run async check synchronously. Use execute() instead.");
|
|
50
|
+
}
|
|
51
|
+
const result = this.normalizeResult(raw);
|
|
52
|
+
result.latencyMs = Date.now() - start;
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return {
|
|
57
|
+
name: this.name,
|
|
58
|
+
detected: false,
|
|
59
|
+
confidence: 0,
|
|
60
|
+
action: actions_1.Action.PASS,
|
|
61
|
+
details: {},
|
|
62
|
+
latencyMs: Date.now() - start,
|
|
63
|
+
error: `Check failed: ${err.message || err}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
normalizeResult(raw) {
|
|
68
|
+
if (typeof raw === "boolean") {
|
|
69
|
+
return {
|
|
70
|
+
name: this.name,
|
|
71
|
+
detected: raw,
|
|
72
|
+
confidence: raw ? 1.0 : 0.0,
|
|
73
|
+
action: raw ? this.action : actions_1.Action.PASS,
|
|
74
|
+
details: {},
|
|
75
|
+
latencyMs: 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const detected = raw.detected && raw.confidence >= this.threshold;
|
|
79
|
+
const { detected: _d, confidence: _c, redactedText, ...rest } = raw;
|
|
80
|
+
return {
|
|
81
|
+
name: this.name,
|
|
82
|
+
detected,
|
|
83
|
+
confidence: raw.confidence,
|
|
84
|
+
action: detected ? this.action : actions_1.Action.PASS,
|
|
85
|
+
details: rest,
|
|
86
|
+
latencyMs: 0,
|
|
87
|
+
redactedText: redactedText,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.Check = Check;
|
|
92
|
+
/**
|
|
93
|
+
* Factory function to create a Check instance.
|
|
94
|
+
*/
|
|
95
|
+
function check(options = {}) {
|
|
96
|
+
return (fn) => {
|
|
97
|
+
return new Check(fn, {
|
|
98
|
+
...options,
|
|
99
|
+
name: options.name || fn.name?.replace(/_/g, "-") || "unnamed-check",
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check execution context — provides runtime info to checks.
|
|
3
|
+
*/
|
|
4
|
+
export interface CheckContext {
|
|
5
|
+
pipelineName: string;
|
|
6
|
+
environment: string;
|
|
7
|
+
config: Record<string, any>;
|
|
8
|
+
metadata: Record<string, any>;
|
|
9
|
+
shared: Record<string, any>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createContext(partial?: Partial<CheckContext>): CheckContext;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Check execution context — provides runtime info to checks.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createContext = createContext;
|
|
7
|
+
function createContext(partial) {
|
|
8
|
+
return {
|
|
9
|
+
pipelineName: "",
|
|
10
|
+
environment: "dev",
|
|
11
|
+
config: {},
|
|
12
|
+
metadata: {},
|
|
13
|
+
shared: {},
|
|
14
|
+
...partial,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline builder and DAG executor — the orchestration engine.
|
|
3
|
+
*/
|
|
4
|
+
import { Check } from "./check";
|
|
5
|
+
import { CheckContext } from "./context";
|
|
6
|
+
import { PipelineResult } from "./result";
|
|
7
|
+
export interface PipelineOptions {
|
|
8
|
+
name?: string;
|
|
9
|
+
checks?: Check[];
|
|
10
|
+
packs?: Array<{
|
|
11
|
+
checks: Check[];
|
|
12
|
+
} | Check[]>;
|
|
13
|
+
mode?: "fail-closed" | "fail-open" | "log-only";
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
onBlock?: (result: PipelineResult) => void;
|
|
16
|
+
onFlag?: (result: PipelineResult) => void;
|
|
17
|
+
onPass?: (result: PipelineResult) => void;
|
|
18
|
+
parallel?: boolean;
|
|
19
|
+
metadata?: Record<string, any>;
|
|
20
|
+
}
|
|
21
|
+
export declare class Pipeline {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
checks: Check[];
|
|
24
|
+
readonly mode: string;
|
|
25
|
+
readonly timeoutMs: number;
|
|
26
|
+
readonly onBlock?: (result: PipelineResult) => void;
|
|
27
|
+
readonly onFlag?: (result: PipelineResult) => void;
|
|
28
|
+
readonly onPass?: (result: PipelineResult) => void;
|
|
29
|
+
readonly parallel: boolean;
|
|
30
|
+
readonly metadata: Record<string, any>;
|
|
31
|
+
constructor(options?: PipelineOptions);
|
|
32
|
+
addCheck(check: Check): this;
|
|
33
|
+
removeCheck(name: string): this;
|
|
34
|
+
run(text: string, context?: CheckContext, metadata?: Record<string, any>): Promise<PipelineResult>;
|
|
35
|
+
private buildExecutionPlan;
|
|
36
|
+
private determineFinalAction;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create a guardrail pipeline.
|
|
40
|
+
*/
|
|
41
|
+
export declare function pipeline(options?: PipelineOptions): Pipeline;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pipeline builder and DAG executor — the orchestration engine.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Pipeline = void 0;
|
|
7
|
+
exports.pipeline = pipeline;
|
|
8
|
+
const actions_1 = require("./actions");
|
|
9
|
+
const context_1 = require("./context");
|
|
10
|
+
class Pipeline {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.name = options.name || "default";
|
|
13
|
+
this.checks = [];
|
|
14
|
+
this.mode = options.mode || "fail-closed";
|
|
15
|
+
this.timeoutMs = options.timeoutMs || 5000;
|
|
16
|
+
this.onBlock = options.onBlock;
|
|
17
|
+
this.onFlag = options.onFlag;
|
|
18
|
+
this.onPass = options.onPass;
|
|
19
|
+
this.parallel = options.parallel !== false;
|
|
20
|
+
this.metadata = options.metadata || {};
|
|
21
|
+
// Register checks
|
|
22
|
+
if (options.checks) {
|
|
23
|
+
for (const c of options.checks) {
|
|
24
|
+
this.addCheck(c);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Register packs
|
|
28
|
+
if (options.packs) {
|
|
29
|
+
for (const pack of options.packs) {
|
|
30
|
+
const packChecks = Array.isArray(pack) ? pack : pack.checks;
|
|
31
|
+
for (const c of packChecks) {
|
|
32
|
+
this.addCheck(c);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
addCheck(check) {
|
|
38
|
+
this.checks.push(check);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
removeCheck(name) {
|
|
42
|
+
this.checks = this.checks.filter((c) => c.name !== name);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
async run(text, context, metadata) {
|
|
46
|
+
const start = Date.now();
|
|
47
|
+
const ctx = context || (0, context_1.createContext)({ pipelineName: this.name });
|
|
48
|
+
if (metadata) {
|
|
49
|
+
Object.assign(ctx.metadata, metadata);
|
|
50
|
+
}
|
|
51
|
+
// Build execution layers
|
|
52
|
+
const layers = this.buildExecutionPlan();
|
|
53
|
+
const allResults = [];
|
|
54
|
+
let currentText = text;
|
|
55
|
+
let blocked = false;
|
|
56
|
+
for (const layer of layers) {
|
|
57
|
+
if (blocked && this.mode === "fail-closed")
|
|
58
|
+
break;
|
|
59
|
+
let layerResults;
|
|
60
|
+
if (this.parallel && layer.length > 1) {
|
|
61
|
+
layerResults = await Promise.all(layer.map((check) => check.execute(currentText, ctx)));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
layerResults = [];
|
|
65
|
+
for (const check of layer) {
|
|
66
|
+
const result = await check.execute(currentText, ctx);
|
|
67
|
+
layerResults.push(result);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const result of layerResults) {
|
|
71
|
+
allResults.push(result);
|
|
72
|
+
if (result.detected && result.action === actions_1.Action.REDACT && result.redactedText) {
|
|
73
|
+
currentText = result.redactedText;
|
|
74
|
+
}
|
|
75
|
+
if (result.detected && (0, actions_1.isBlocking)(result.action) && this.mode !== "log-only") {
|
|
76
|
+
blocked = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const finalAction = this.determineFinalAction(allResults);
|
|
81
|
+
const allowed = this.mode === "log-only" ? true : !blocked;
|
|
82
|
+
const pipelineResult = {
|
|
83
|
+
allowed,
|
|
84
|
+
action: finalAction,
|
|
85
|
+
checkResults: allResults,
|
|
86
|
+
modifiedText: currentText !== text ? currentText : undefined,
|
|
87
|
+
originalText: text,
|
|
88
|
+
totalLatencyMs: Date.now() - start,
|
|
89
|
+
pipelineName: this.name,
|
|
90
|
+
metadata: this.metadata,
|
|
91
|
+
};
|
|
92
|
+
// Fire callbacks
|
|
93
|
+
if (!allowed && this.onBlock)
|
|
94
|
+
this.onBlock(pipelineResult);
|
|
95
|
+
else if (allResults.some((r) => r.detected && r.action === actions_1.Action.FLAG_FOR_REVIEW) &&
|
|
96
|
+
this.onFlag) {
|
|
97
|
+
this.onFlag(pipelineResult);
|
|
98
|
+
}
|
|
99
|
+
else if (allowed && this.onPass) {
|
|
100
|
+
this.onPass(pipelineResult);
|
|
101
|
+
}
|
|
102
|
+
return pipelineResult;
|
|
103
|
+
}
|
|
104
|
+
buildExecutionPlan() {
|
|
105
|
+
if (this.checks.length === 0)
|
|
106
|
+
return [];
|
|
107
|
+
const checkMap = new Map(this.checks.map((c) => [c.name, c]));
|
|
108
|
+
const inDegree = new Map(this.checks.map((c) => [c.name, 0]));
|
|
109
|
+
const dependents = new Map();
|
|
110
|
+
for (const c of this.checks) {
|
|
111
|
+
for (const dep of c.dependsOn) {
|
|
112
|
+
if (checkMap.has(dep)) {
|
|
113
|
+
inDegree.set(c.name, (inDegree.get(c.name) || 0) + 1);
|
|
114
|
+
const deps = dependents.get(dep) || [];
|
|
115
|
+
deps.push(c.name);
|
|
116
|
+
dependents.set(dep, deps);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const layers = [];
|
|
121
|
+
let ready = [...inDegree.entries()]
|
|
122
|
+
.filter(([_, deg]) => deg === 0)
|
|
123
|
+
.map(([name]) => name);
|
|
124
|
+
while (ready.length > 0) {
|
|
125
|
+
layers.push(ready.map((name) => checkMap.get(name)));
|
|
126
|
+
const nextReady = [];
|
|
127
|
+
for (const name of ready) {
|
|
128
|
+
for (const depName of dependents.get(name) || []) {
|
|
129
|
+
const newDeg = (inDegree.get(depName) || 1) - 1;
|
|
130
|
+
inDegree.set(depName, newDeg);
|
|
131
|
+
if (newDeg === 0)
|
|
132
|
+
nextReady.push(depName);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
ready = nextReady;
|
|
136
|
+
}
|
|
137
|
+
return layers;
|
|
138
|
+
}
|
|
139
|
+
determineFinalAction(results) {
|
|
140
|
+
const severity = {
|
|
141
|
+
[actions_1.Action.BLOCK]: 4,
|
|
142
|
+
[actions_1.Action.FLAG_FOR_REVIEW]: 3,
|
|
143
|
+
[actions_1.Action.REDACT]: 2,
|
|
144
|
+
[actions_1.Action.LOG]: 1,
|
|
145
|
+
[actions_1.Action.PASS]: 0,
|
|
146
|
+
};
|
|
147
|
+
let maxAction = actions_1.Action.PASS;
|
|
148
|
+
let maxSeverity = 0;
|
|
149
|
+
for (const result of results) {
|
|
150
|
+
if (result.detected) {
|
|
151
|
+
const s = severity[result.action] || 0;
|
|
152
|
+
if (s > maxSeverity) {
|
|
153
|
+
maxSeverity = s;
|
|
154
|
+
maxAction = result.action;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return maxAction;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.Pipeline = Pipeline;
|
|
162
|
+
/**
|
|
163
|
+
* Create a guardrail pipeline.
|
|
164
|
+
*/
|
|
165
|
+
function pipeline(options = {}) {
|
|
166
|
+
return new Pipeline(options);
|
|
167
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result types for check execution and pipeline outcomes.
|
|
3
|
+
*/
|
|
4
|
+
import { Action } from "./actions";
|
|
5
|
+
export interface CheckResult {
|
|
6
|
+
name: string;
|
|
7
|
+
detected: boolean;
|
|
8
|
+
confidence: number;
|
|
9
|
+
action: Action;
|
|
10
|
+
details: Record<string, any>;
|
|
11
|
+
latencyMs: number;
|
|
12
|
+
redactedText?: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PipelineResult {
|
|
16
|
+
allowed: boolean;
|
|
17
|
+
action: Action;
|
|
18
|
+
checkResults: CheckResult[];
|
|
19
|
+
modifiedText?: string;
|
|
20
|
+
originalText?: string;
|
|
21
|
+
totalLatencyMs: number;
|
|
22
|
+
pipelineName: string;
|
|
23
|
+
metadata: Record<string, any>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createCheckResult(partial: Partial<CheckResult> & {
|
|
26
|
+
name: string;
|
|
27
|
+
}): CheckResult;
|
|
28
|
+
export declare function createPipelineResult(partial: Partial<PipelineResult>): PipelineResult;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Result types for check execution and pipeline outcomes.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createCheckResult = createCheckResult;
|
|
7
|
+
exports.createPipelineResult = createPipelineResult;
|
|
8
|
+
const actions_1 = require("./actions");
|
|
9
|
+
function createCheckResult(partial) {
|
|
10
|
+
return {
|
|
11
|
+
detected: false,
|
|
12
|
+
confidence: 0,
|
|
13
|
+
action: actions_1.Action.PASS,
|
|
14
|
+
details: {},
|
|
15
|
+
latencyMs: 0,
|
|
16
|
+
...partial,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function createPipelineResult(partial) {
|
|
20
|
+
return {
|
|
21
|
+
allowed: true,
|
|
22
|
+
action: actions_1.Action.PASS,
|
|
23
|
+
checkResults: [],
|
|
24
|
+
totalLatencyMs: 0,
|
|
25
|
+
pipelineName: "",
|
|
26
|
+
metadata: {},
|
|
27
|
+
...partial,
|
|
28
|
+
};
|
|
29
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GuardrailGraph — Composable AI safety pipeline framework.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
export { Action, isBlocking, isModifying } from "./core/actions";
|
|
7
|
+
export { Check, check, CheckOptions, CheckFunction, CheckFunctionResult } from "./core/check";
|
|
8
|
+
export { Pipeline, pipeline, PipelineOptions } from "./core/pipeline";
|
|
9
|
+
export { CheckResult, PipelineResult, createCheckResult, createPipelineResult } from "./core/result";
|
|
10
|
+
export { CheckContext, createContext } from "./core/context";
|
|
11
|
+
export { piiCheck, PiiCheckOptions } from "./checks/pii";
|
|
12
|
+
export { toxicityCheck, ToxicityCheckOptions } from "./checks/toxicity";
|
|
13
|
+
export { topicCheck, TopicCheckOptions } from "./checks/topics";
|
|
14
|
+
export { injectionCheck, InjectionCheckOptions } from "./checks/injection";
|
|
15
|
+
export { costCheck, CostCheckOptions } from "./checks/cost";
|
|
16
|
+
export * as hipaa from "./packs/hipaa";
|
|
17
|
+
export * as financial from "./packs/financial";
|
|
18
|
+
export { GuardrailMiddleware, MiddlewareOptions, wrapLlmCall } from "./middleware";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GuardrailGraph — Composable AI safety pipeline framework.
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.wrapLlmCall = exports.GuardrailMiddleware = exports.financial = exports.hipaa = exports.costCheck = exports.injectionCheck = exports.topicCheck = exports.toxicityCheck = exports.piiCheck = exports.createContext = exports.createPipelineResult = exports.createCheckResult = exports.pipeline = exports.Pipeline = exports.check = exports.Check = exports.isModifying = exports.isBlocking = exports.Action = void 0;
|
|
42
|
+
// Core
|
|
43
|
+
var actions_1 = require("./core/actions");
|
|
44
|
+
Object.defineProperty(exports, "Action", { enumerable: true, get: function () { return actions_1.Action; } });
|
|
45
|
+
Object.defineProperty(exports, "isBlocking", { enumerable: true, get: function () { return actions_1.isBlocking; } });
|
|
46
|
+
Object.defineProperty(exports, "isModifying", { enumerable: true, get: function () { return actions_1.isModifying; } });
|
|
47
|
+
var check_1 = require("./core/check");
|
|
48
|
+
Object.defineProperty(exports, "Check", { enumerable: true, get: function () { return check_1.Check; } });
|
|
49
|
+
Object.defineProperty(exports, "check", { enumerable: true, get: function () { return check_1.check; } });
|
|
50
|
+
var pipeline_1 = require("./core/pipeline");
|
|
51
|
+
Object.defineProperty(exports, "Pipeline", { enumerable: true, get: function () { return pipeline_1.Pipeline; } });
|
|
52
|
+
Object.defineProperty(exports, "pipeline", { enumerable: true, get: function () { return pipeline_1.pipeline; } });
|
|
53
|
+
var result_1 = require("./core/result");
|
|
54
|
+
Object.defineProperty(exports, "createCheckResult", { enumerable: true, get: function () { return result_1.createCheckResult; } });
|
|
55
|
+
Object.defineProperty(exports, "createPipelineResult", { enumerable: true, get: function () { return result_1.createPipelineResult; } });
|
|
56
|
+
var context_1 = require("./core/context");
|
|
57
|
+
Object.defineProperty(exports, "createContext", { enumerable: true, get: function () { return context_1.createContext; } });
|
|
58
|
+
// Built-in checks
|
|
59
|
+
var pii_1 = require("./checks/pii");
|
|
60
|
+
Object.defineProperty(exports, "piiCheck", { enumerable: true, get: function () { return pii_1.piiCheck; } });
|
|
61
|
+
var toxicity_1 = require("./checks/toxicity");
|
|
62
|
+
Object.defineProperty(exports, "toxicityCheck", { enumerable: true, get: function () { return toxicity_1.toxicityCheck; } });
|
|
63
|
+
var topics_1 = require("./checks/topics");
|
|
64
|
+
Object.defineProperty(exports, "topicCheck", { enumerable: true, get: function () { return topics_1.topicCheck; } });
|
|
65
|
+
var injection_1 = require("./checks/injection");
|
|
66
|
+
Object.defineProperty(exports, "injectionCheck", { enumerable: true, get: function () { return injection_1.injectionCheck; } });
|
|
67
|
+
var cost_1 = require("./checks/cost");
|
|
68
|
+
Object.defineProperty(exports, "costCheck", { enumerable: true, get: function () { return cost_1.costCheck; } });
|
|
69
|
+
// Industry packs
|
|
70
|
+
exports.hipaa = __importStar(require("./packs/hipaa"));
|
|
71
|
+
exports.financial = __importStar(require("./packs/financial"));
|
|
72
|
+
// Middleware
|
|
73
|
+
var middleware_1 = require("./middleware");
|
|
74
|
+
Object.defineProperty(exports, "GuardrailMiddleware", { enumerable: true, get: function () { return middleware_1.GuardrailMiddleware; } });
|
|
75
|
+
Object.defineProperty(exports, "wrapLlmCall", { enumerable: true, get: function () { return middleware_1.wrapLlmCall; } });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware layer — integrate guardrails with any LLM provider.
|
|
3
|
+
*/
|
|
4
|
+
import { Pipeline } from "../core/pipeline";
|
|
5
|
+
import { CheckContext } from "../core/context";
|
|
6
|
+
import { PipelineResult } from "../core/result";
|
|
7
|
+
export interface MiddlewareOptions {
|
|
8
|
+
pipeline: Pipeline;
|
|
9
|
+
applyTo?: "input" | "output" | "both";
|
|
10
|
+
onBlockResponse?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class GuardrailMiddleware {
|
|
13
|
+
private pipeline;
|
|
14
|
+
private applyTo;
|
|
15
|
+
private onBlockResponse;
|
|
16
|
+
constructor(options: MiddlewareOptions);
|
|
17
|
+
processInput(text: string, context?: CheckContext): Promise<PipelineResult>;
|
|
18
|
+
processOutput(text: string, context?: CheckContext): Promise<PipelineResult>;
|
|
19
|
+
wrap<T extends (text: string, ...args: any[]) => any>(llmCall: T): (text: string, ...args: any[]) => Promise<{
|
|
20
|
+
blocked: boolean;
|
|
21
|
+
response: string;
|
|
22
|
+
guardrailResult: PipelineResult;
|
|
23
|
+
} | {
|
|
24
|
+
blocked: boolean;
|
|
25
|
+
response: string;
|
|
26
|
+
guardrailResult?: undefined;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export declare function wrapLlmCall<T extends (text: string, ...args: any[]) => any>(llmCall: T, pipeline: Pipeline, applyTo?: "input" | "output" | "both"): (text: string, ...args: any[]) => Promise<{
|
|
30
|
+
blocked: boolean;
|
|
31
|
+
response: string;
|
|
32
|
+
guardrailResult: PipelineResult;
|
|
33
|
+
} | {
|
|
34
|
+
blocked: boolean;
|
|
35
|
+
response: string;
|
|
36
|
+
guardrailResult?: undefined;
|
|
37
|
+
}>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Middleware layer — integrate guardrails with any LLM provider.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GuardrailMiddleware = void 0;
|
|
7
|
+
exports.wrapLlmCall = wrapLlmCall;
|
|
8
|
+
const actions_1 = require("../core/actions");
|
|
9
|
+
class GuardrailMiddleware {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.pipeline = options.pipeline;
|
|
12
|
+
this.applyTo = options.applyTo || "both";
|
|
13
|
+
this.onBlockResponse = options.onBlockResponse || "I cannot process this request due to content policy.";
|
|
14
|
+
}
|
|
15
|
+
async processInput(text, context) {
|
|
16
|
+
if (this.applyTo === "input" || this.applyTo === "both") {
|
|
17
|
+
return this.pipeline.run(text, context);
|
|
18
|
+
}
|
|
19
|
+
return { allowed: true, action: actions_1.Action.PASS, checkResults: [], totalLatencyMs: 0, pipelineName: this.pipeline.name, metadata: {} };
|
|
20
|
+
}
|
|
21
|
+
async processOutput(text, context) {
|
|
22
|
+
if (this.applyTo === "output" || this.applyTo === "both") {
|
|
23
|
+
return this.pipeline.run(text, context);
|
|
24
|
+
}
|
|
25
|
+
return { allowed: true, action: actions_1.Action.PASS, checkResults: [], totalLatencyMs: 0, pipelineName: this.pipeline.name, metadata: {} };
|
|
26
|
+
}
|
|
27
|
+
wrap(llmCall) {
|
|
28
|
+
const middleware = this;
|
|
29
|
+
return async (text, ...args) => {
|
|
30
|
+
const inputResult = await middleware.processInput(text);
|
|
31
|
+
if (!inputResult.allowed) {
|
|
32
|
+
return { blocked: true, response: middleware.onBlockResponse, guardrailResult: inputResult };
|
|
33
|
+
}
|
|
34
|
+
const effectiveText = inputResult.modifiedText || text;
|
|
35
|
+
const llmResponse = await llmCall(effectiveText, ...args);
|
|
36
|
+
const responseText = typeof llmResponse === "string" ? llmResponse : String(llmResponse);
|
|
37
|
+
const outputResult = await middleware.processOutput(responseText);
|
|
38
|
+
if (!outputResult.allowed) {
|
|
39
|
+
return { blocked: true, response: middleware.onBlockResponse, guardrailResult: outputResult };
|
|
40
|
+
}
|
|
41
|
+
return { blocked: false, response: outputResult.modifiedText || responseText };
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.GuardrailMiddleware = GuardrailMiddleware;
|
|
46
|
+
function wrapLlmCall(llmCall, pipeline, applyTo = "both") {
|
|
47
|
+
const middleware = new GuardrailMiddleware({ pipeline, applyTo });
|
|
48
|
+
return middleware.wrap(llmCall);
|
|
49
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Financial Compliance Pack — SOX guardrails.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.sox = sox;
|
|
7
|
+
const actions_1 = require("../core/actions");
|
|
8
|
+
const check_1 = require("../core/check");
|
|
9
|
+
const FINANCIAL_ADVICE_KEYWORDS = [
|
|
10
|
+
"you should invest", "buy this stock", "sell your",
|
|
11
|
+
"guaranteed returns", "financial advice", "investment recommendation",
|
|
12
|
+
"i recommend buying", "this stock will", "market prediction",
|
|
13
|
+
"insider tip", "sure thing", "can't lose",
|
|
14
|
+
];
|
|
15
|
+
const INSIDER_KEYWORDS = [
|
|
16
|
+
"insider information", "non-public", "material information",
|
|
17
|
+
"before the announcement", "confidential deal", "merger talks",
|
|
18
|
+
"earnings surprise", "undisclosed", "tip from",
|
|
19
|
+
];
|
|
20
|
+
function financialAdviceDetection() {
|
|
21
|
+
return new check_1.Check((text) => {
|
|
22
|
+
const textLower = text.toLowerCase();
|
|
23
|
+
const matched = FINANCIAL_ADVICE_KEYWORDS.filter((kw) => textLower.includes(kw));
|
|
24
|
+
if (matched.length === 0)
|
|
25
|
+
return { detected: false, confidence: 0 };
|
|
26
|
+
return {
|
|
27
|
+
detected: true,
|
|
28
|
+
confidence: Math.min(matched.length / 2.0, 1.0),
|
|
29
|
+
matchedKeywords: matched,
|
|
30
|
+
category: "financial_advice",
|
|
31
|
+
};
|
|
32
|
+
}, { name: "financial-advice-detection", action: actions_1.Action.BLOCK, threshold: 0.6 });
|
|
33
|
+
}
|
|
34
|
+
function insiderInfoDetection() {
|
|
35
|
+
return new check_1.Check((text) => {
|
|
36
|
+
const textLower = text.toLowerCase();
|
|
37
|
+
const matched = INSIDER_KEYWORDS.filter((kw) => textLower.includes(kw));
|
|
38
|
+
if (matched.length === 0)
|
|
39
|
+
return { detected: false, confidence: 0 };
|
|
40
|
+
return {
|
|
41
|
+
detected: true,
|
|
42
|
+
confidence: Math.min(matched.length / 2.0, 1.0),
|
|
43
|
+
matchedKeywords: matched,
|
|
44
|
+
category: "insider_information",
|
|
45
|
+
};
|
|
46
|
+
}, { name: "insider-info-detection", action: actions_1.Action.BLOCK, threshold: 0.7 });
|
|
47
|
+
}
|
|
48
|
+
function soxAuditLogging() {
|
|
49
|
+
return new check_1.Check((text) => ({
|
|
50
|
+
detected: true,
|
|
51
|
+
confidence: 1.0,
|
|
52
|
+
auditRecord: { timestamp: Date.now(), textLength: text.length, framework: "SOX" },
|
|
53
|
+
}), { name: "sox-audit-log", action: actions_1.Action.LOG, threshold: 0 });
|
|
54
|
+
}
|
|
55
|
+
function sox() {
|
|
56
|
+
return {
|
|
57
|
+
checks: [financialAdviceDetection(), insiderInfoDetection(), soxAuditLogging()],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HIPAA Compliance Pack — healthcare AI safety guardrails.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.full = full;
|
|
7
|
+
exports.basic = basic;
|
|
8
|
+
const actions_1 = require("../core/actions");
|
|
9
|
+
const check_1 = require("../core/check");
|
|
10
|
+
const pii_1 = require("../checks/pii");
|
|
11
|
+
const MEDICAL_CLAIM_KEYWORDS = [
|
|
12
|
+
"you have", "you are diagnosed", "your diagnosis is",
|
|
13
|
+
"i recommend", "you should take", "prescribe",
|
|
14
|
+
"your condition", "treatment plan", "prognosis",
|
|
15
|
+
"medical advice", "clinical recommendation",
|
|
16
|
+
];
|
|
17
|
+
function phiDetection() {
|
|
18
|
+
return (0, pii_1.piiCheck)({
|
|
19
|
+
entityTypes: ["SSN", "PHONE", "EMAIL", "DATE_OF_BIRTH", "IP_ADDRESS"],
|
|
20
|
+
action: actions_1.Action.REDACT,
|
|
21
|
+
name: "phi-detection",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function medicalClaimDetection() {
|
|
25
|
+
return new check_1.Check((text) => {
|
|
26
|
+
const textLower = text.toLowerCase();
|
|
27
|
+
const matched = MEDICAL_CLAIM_KEYWORDS.filter((kw) => textLower.includes(kw));
|
|
28
|
+
if (matched.length === 0) {
|
|
29
|
+
return { detected: false, confidence: 0 };
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
detected: true,
|
|
33
|
+
confidence: Math.min(matched.length / 3.0, 1.0),
|
|
34
|
+
matchedClaims: matched,
|
|
35
|
+
claimCount: matched.length,
|
|
36
|
+
};
|
|
37
|
+
}, { name: "medical-claim-detection", action: actions_1.Action.FLAG_FOR_REVIEW, threshold: 0.6 });
|
|
38
|
+
}
|
|
39
|
+
function auditLogging() {
|
|
40
|
+
return new check_1.Check((text) => ({
|
|
41
|
+
detected: true,
|
|
42
|
+
confidence: 1.0,
|
|
43
|
+
auditRecord: { timestamp: Date.now(), textLength: text.length, action: "logged" },
|
|
44
|
+
}), { name: "hipaa-audit-log", action: actions_1.Action.LOG, threshold: 0 });
|
|
45
|
+
}
|
|
46
|
+
function full() {
|
|
47
|
+
return {
|
|
48
|
+
checks: [phiDetection(), medicalClaimDetection(), auditLogging()],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function basic() {
|
|
52
|
+
return {
|
|
53
|
+
checks: [phiDetection(), auditLogging()],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.financial = exports.hipaa = void 0;
|
|
37
|
+
exports.hipaa = __importStar(require("./hipaa"));
|
|
38
|
+
exports.financial = __importStar(require("./financial"));
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "substrai-guardrailgraph",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Composable AI safety pipeline framework with industry compliance packs (HIPAA, SOX, GDPR, FedRAMP)",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "node --test dist/tests/",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai-safety",
|
|
17
|
+
"guardrails",
|
|
18
|
+
"llm",
|
|
19
|
+
"compliance",
|
|
20
|
+
"hipaa",
|
|
21
|
+
"sox",
|
|
22
|
+
"gdpr",
|
|
23
|
+
"serverless",
|
|
24
|
+
"aws-lambda",
|
|
25
|
+
"dag",
|
|
26
|
+
"pipeline",
|
|
27
|
+
"pii",
|
|
28
|
+
"toxicity",
|
|
29
|
+
"prompt-injection"
|
|
30
|
+
],
|
|
31
|
+
"author": "Gaurav Kumar Sinha <gaurav@substrai.dev>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/substrai/guardrailgraph"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/substrai/guardrailgraph",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/substrai/guardrailgraph/issues"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=16.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"typescript": "^5.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|