odin-protocol-core
Version:
The world's first standardized AI-to-AI communication infrastructure for JavaScript/TypeScript - 100% functional with 57K+ msgs/sec throughput
365 lines (364 loc) • 12.7 kB
JavaScript
"use strict";
/**
* HEL (Human-Enhanced Logic) Rule Engine Implementation
* Advanced rule evaluation system for AI coordination
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.HELRuleEngine = void 0;
const events_1 = require("events");
const HELTypes_1 = require("../types/HELTypes");
const OdinTypes_1 = require("../types/OdinTypes");
class HELRuleEngine extends events_1.EventEmitter {
constructor(options = {}) {
super();
this.rules = new Map();
this.activeRules = new Set();
this.ruleHistory = [];
this.maxHistorySize = 1000;
this.maxHistorySize = options.maxHistorySize || 1000;
}
/**
* Add a new rule to the engine
*/
addRule(rule) {
if (!rule.id || !rule.name) {
throw new Error('Rule must have an ID and name');
}
if (!rule.conditions || rule.conditions.length === 0) {
throw new Error('Rule must have at least one condition');
}
if (!rule.actions || rule.actions.length === 0) {
throw new Error('Rule must have at least one action');
}
this.rules.set(rule.id, { ...rule });
if (rule.status === HELTypes_1.HELRuleStatus.ACTIVE) {
this.activeRules.add(rule.id);
}
this.emit('ruleAdded', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: { ruleId: rule.id, ruleName: rule.name }
});
}
/**
* Remove a rule from the engine
*/
removeRule(ruleId) {
const removed = this.rules.delete(ruleId);
this.activeRules.delete(ruleId);
if (removed) {
this.emit('ruleRemoved', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: { ruleId }
});
}
return removed;
}
/**
* Enable a rule
*/
enableRule(ruleId) {
const rule = this.rules.get(ruleId);
if (!rule) {
return false;
}
rule.status = HELTypes_1.HELRuleStatus.ACTIVE;
this.activeRules.add(ruleId);
this.emit('ruleEnabled', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: { ruleId }
});
return true;
}
/**
* Disable a rule
*/
disableRule(ruleId) {
const rule = this.rules.get(ruleId);
if (!rule) {
return false;
}
rule.status = HELTypes_1.HELRuleStatus.INACTIVE;
this.activeRules.delete(ruleId);
this.emit('ruleDisabled', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: { ruleId }
});
return true;
}
/**
* Evaluate all active rules against a context
*/
async evaluateRules(context) {
const results = [];
const startTime = Date.now();
this.emit('evaluationStarted', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: { context, activeRulesCount: this.activeRules.size }
});
for (const ruleId of this.activeRules) {
const rule = this.rules.get(ruleId);
if (!rule)
continue;
try {
const result = await this.evaluateRule(rule, context);
results.push(result);
// Add to history
this.addToHistory(result);
// Execute actions if rule fired
if (result.fired) {
await this.executeActions(rule.actions, context, result);
}
}
catch (error) {
const errorResult = {
ruleId: rule.id,
ruleName: rule.name,
fired: false,
timestamp: new Date(),
executionTime: Date.now() - startTime,
context,
error: error.message,
conditionResults: []
};
results.push(errorResult);
this.addToHistory(errorResult);
this.emit('evaluationError', {
type: OdinTypes_1.OdinEventType.ERROR,
timestamp: new Date(),
data: { ruleId: rule.id, error: error.message }
});
}
}
this.emit('evaluationCompleted', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: {
resultsCount: results.length,
firedCount: results.filter(r => r.fired).length,
totalExecutionTime: Date.now() - startTime
}
});
return results;
}
/**
* Evaluate a single rule against a context
*/
async evaluateRule(rule, context) {
const startTime = Date.now();
const conditionResults = [];
// Evaluate each condition
for (const condition of rule.conditions) {
const result = this.evaluateCondition(condition, context);
conditionResults.push(result);
}
// Apply rule logic (all conditions must be true for now)
const fired = conditionResults.every(result => result);
const evaluationResult = {
ruleId: rule.id,
ruleName: rule.name,
fired,
timestamp: new Date(),
executionTime: Date.now() - startTime,
context,
conditionResults,
triggeredActions: fired ? rule.actions.map(a => a.type) : []
};
return evaluationResult;
}
/**
* Evaluate a single condition
*/
evaluateCondition(condition, context) {
const contextValue = this.getContextValue(condition.field, context);
const expectedValue = condition.value;
switch (condition.operator) {
case HELTypes_1.HELOperator.EQUALS:
return contextValue === expectedValue;
case HELTypes_1.HELOperator.NOT_EQUALS:
return contextValue !== expectedValue;
case HELTypes_1.HELOperator.GREATER_THAN:
return Number(contextValue) > Number(expectedValue);
case HELTypes_1.HELOperator.LESS_THAN:
return Number(contextValue) < Number(expectedValue);
case HELTypes_1.HELOperator.GREATER_THAN_OR_EQUAL:
return Number(contextValue) >= Number(expectedValue);
case HELTypes_1.HELOperator.LESS_THAN_OR_EQUAL:
return Number(contextValue) <= Number(expectedValue);
case HELTypes_1.HELOperator.CONTAINS:
return String(contextValue).includes(String(expectedValue));
case HELTypes_1.HELOperator.NOT_CONTAINS:
return !String(contextValue).includes(String(expectedValue));
case HELTypes_1.HELOperator.REGEX_MATCH:
try {
const regex = new RegExp(String(expectedValue));
return regex.test(String(contextValue));
}
catch {
return false;
}
case HELTypes_1.HELOperator.IN_ARRAY:
return Array.isArray(expectedValue) && expectedValue.includes(contextValue);
case HELTypes_1.HELOperator.NOT_IN_ARRAY:
return Array.isArray(expectedValue) && !expectedValue.includes(contextValue);
default:
return false;
}
}
/**
* Get a value from context using dot notation
*/
getContextValue(field, context) {
const keys = field.split('.');
let value = context.data;
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key];
}
else {
return undefined;
}
}
return value;
}
/**
* Execute actions when a rule fires
*/
async executeActions(actions, context, result) {
for (const action of actions) {
try {
await this.executeAction(action, context, result);
}
catch (error) {
this.emit('actionError', {
type: OdinTypes_1.OdinEventType.ERROR,
timestamp: new Date(),
data: {
ruleId: result.ruleId,
actionType: action.type,
error: error.message
}
});
}
}
}
/**
* Execute a single action
*/
async executeAction(action, context, result) {
this.emit('actionExecuting', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: {
ruleId: result.ruleId,
actionType: action.type,
parameters: action.parameters
}
});
switch (action.type) {
case HELTypes_1.HELActionType.LOG:
console.log(`[HEL Rule ${result.ruleId}]`, action.parameters?.message || 'Rule fired');
break;
case HELTypes_1.HELActionType.EMIT_EVENT:
this.emit('ruleEvent', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: {
ruleId: result.ruleId,
eventType: action.parameters?.eventType,
eventData: action.parameters?.eventData
}
});
break;
case HELTypes_1.HELActionType.SET_VARIABLE:
if (action.parameters?.variable && context.variables) {
context.variables[action.parameters.variable] = action.parameters.value;
}
break;
case HELTypes_1.HELActionType.CALL_FUNCTION:
if (action.parameters?.functionName && typeof action.parameters.functionName === 'string') {
// Emit an event for external function calls
this.emit('functionCall', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: {
ruleId: result.ruleId,
functionName: action.parameters.functionName,
arguments: action.parameters.arguments || []
}
});
}
break;
default:
console.warn(`Unknown action type: ${action.type}`);
}
this.emit('actionExecuted', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: {
ruleId: result.ruleId,
actionType: action.type
}
});
}
/**
* Get rule statistics
*/
getStatistics() {
const totalEvaluations = this.ruleHistory.length;
const errorEvaluations = this.ruleHistory.filter(r => r.error).length;
return {
totalRules: this.rules.size,
activeRules: this.activeRules.size,
inactiveRules: this.rules.size - this.activeRules.size,
totalEvaluations,
successfulEvaluations: totalEvaluations - errorEvaluations,
errorEvaluations
};
}
/**
* Get rule history
*/
getHistory(limit) {
const history = [...this.ruleHistory].reverse();
return limit ? history.slice(0, limit) : history;
}
/**
* Clear rule history
*/
clearHistory() {
this.ruleHistory = [];
this.emit('historyCleaned', {
type: OdinTypes_1.OdinEventType.SYSTEM,
timestamp: new Date(),
data: {}
});
}
/**
* Get all rules
*/
getRules() {
return Array.from(this.rules.values());
}
/**
* Get a specific rule
*/
getRule(ruleId) {
return this.rules.get(ruleId);
}
/**
* Add result to history with size limit
*/
addToHistory(result) {
this.ruleHistory.push(result);
// Maintain history size limit
if (this.ruleHistory.length > this.maxHistorySize) {
this.ruleHistory.shift();
}
}
}
exports.HELRuleEngine = HELRuleEngine;