UNPKG

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
"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;