@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
244 lines (243 loc) • 6.26 kB
JavaScript
class GuardrailEngine {
rules;
config;
state;
constructor(config) {
this.rules = config.rules || [];
this.config = {
stopOnFatal: true,
enableStreaming: true,
checkInterval: 100,
...config
};
this.state = this.createInitialState();
}
/**
* Create initial guardrail state
*/
createInitialState() {
return {
violations: [],
violationsByRule: /* @__PURE__ */ new Map(),
hasFatalViolations: false,
hasErrorViolations: false,
violationCount: 0
};
}
/**
* Generate a unique callback ID
*/
generateCallbackId() {
return `cb_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
}
/**
* Execute all rules against context
* @param context - Guardrail context
* @returns Guardrail result
*/
check(context) {
const violations = [];
const timestamp = Date.now();
const phase = context.completed ? "post" : "pre";
const phaseStartTime = Date.now();
if (this.config.onPhaseStart) {
this.config.onPhaseStart(phase, this.rules.length, context.tokenCount);
}
let ruleIndex = 0;
for (const rule of this.rules) {
if (rule.streaming && !this.config.enableStreaming && !context.completed) {
continue;
}
if (!rule.streaming && !context.completed) {
continue;
}
const callbackId = this.generateCallbackId();
const ruleStartTime = Date.now();
if (this.config.onRuleStart) {
this.config.onRuleStart(ruleIndex, rule.name, callbackId);
}
try {
const ruleViolations = rule.check({
...context,
previousViolations: this.state.violations
});
for (const violation of ruleViolations) {
violations.push({
...violation,
timestamp
});
}
if (ruleViolations.length > 0) {
const existing = this.state.violationsByRule.get(rule.name) || [];
this.state.violationsByRule.set(rule.name, [
...existing,
...ruleViolations
]);
}
const ruleDurationMs = Date.now() - ruleStartTime;
if (this.config.onRuleEnd) {
this.config.onRuleEnd(
ruleIndex,
rule.name,
ruleViolations.length === 0,
callbackId,
ruleDurationMs
);
}
if (this.config.stopOnFatal && ruleViolations.some((v) => v.severity === "fatal")) {
break;
}
} catch (error) {
violations.push({
rule: rule.name,
message: `Rule execution failed: ${error instanceof Error ? error.message : "Unknown error"}`,
severity: "warning",
recoverable: true,
timestamp
});
const ruleDurationMs = Date.now() - ruleStartTime;
if (this.config.onRuleEnd) {
this.config.onRuleEnd(
ruleIndex,
rule.name,
false,
callbackId,
ruleDurationMs
);
}
}
ruleIndex++;
}
const phaseDurationMs = Date.now() - phaseStartTime;
if (this.config.onPhaseEnd) {
this.config.onPhaseEnd(
phase,
violations.length === 0,
violations,
phaseDurationMs
);
}
this.state.violations.push(...violations);
this.state.violationCount = this.state.violations.length;
this.state.hasFatalViolations = violations.some(
(v) => v.severity === "fatal"
);
this.state.hasErrorViolations = violations.some(
(v) => v.severity === "error"
);
this.state.lastCheckTime = timestamp;
if (this.config.onViolation) {
for (const violation of violations) {
this.config.onViolation(violation);
}
}
const result = {
passed: violations.length === 0,
violations,
shouldRetry: this.shouldRetry(violations),
shouldHalt: this.shouldHalt(violations),
summary: {
total: violations.length,
fatal: violations.filter((v) => v.severity === "fatal").length,
errors: violations.filter((v) => v.severity === "error").length,
warnings: violations.filter((v) => v.severity === "warning").length
}
};
return result;
}
/**
* Determine if violations should trigger a retry
*/
shouldRetry(violations) {
return violations.some(
(v) => v.recoverable && (v.severity === "error" || v.severity === "fatal")
);
}
/**
* Determine if violations should halt execution
*/
shouldHalt(violations) {
if (violations.some((v) => v.severity === "fatal")) {
return true;
}
if (violations.some((v) => !v.recoverable && v.severity === "error")) {
return true;
}
return false;
}
/**
* Get current state
*/
getState() {
return { ...this.state };
}
/**
* Reset state
*/
reset() {
this.state = this.createInitialState();
}
/**
* Add a rule to the engine
*/
addRule(rule) {
this.rules.push(rule);
}
/**
* Remove a rule from the engine
*/
removeRule(ruleName) {
const index = this.rules.findIndex((r) => r.name === ruleName);
if (index !== -1) {
this.rules.splice(index, 1);
return true;
}
return false;
}
/**
* Get violations for a specific rule
*/
getViolationsByRule(ruleName) {
return this.state.violationsByRule.get(ruleName) || [];
}
/**
* Get all violations
*/
getAllViolations() {
return [...this.state.violations];
}
/**
* Check if any violations exist
*/
hasViolations() {
return this.state.violationCount > 0;
}
/**
* Check if any fatal violations exist
*/
hasFatalViolations() {
return this.state.hasFatalViolations;
}
/**
* Check if any error violations exist
*/
hasErrorViolations() {
return this.state.hasErrorViolations;
}
}
function createGuardrailEngine(rules, options) {
return new GuardrailEngine({
rules,
...options
});
}
function checkGuardrails(context, rules) {
const engine = createGuardrailEngine(rules);
return engine.check(context);
}
export {
GuardrailEngine,
checkGuardrails,
createGuardrailEngine
};
//# sourceMappingURL=engine.js.map