UNPKG

rcc-virtual-model-rules

Version:

RCC Virtual Model Rules Module - Claude Code Router rules implementation

864 lines 32 kB
"use strict"; // Main Virtual Model Rules Module for RCC var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.VirtualModelRulesModule = void 0; const rcc_basemodule_1 = require("rcc-basemodule"); /** * Virtual Model Rules Module for RCC * Manages intelligent routing rules, model scheduling, and request processing * based on Claude Code Router patterns and virtual model capabilities */ class VirtualModelRulesModule extends rcc_basemodule_1.BaseModule { constructor() { const moduleInfo = { id: 'VirtualModelRulesModule', name: 'RCC Virtual Model Rules Module', version: '1.0.0', description: 'Virtual model routing rules and scheduling based on Claude Code Router patterns', type: 'virtual-model-rules', capabilities: ['rule-engine', 'model-scheduling', 'intelligent-routing', 'request-evaluation'], dependencies: ['rcc-basemodule'], config: {}, metadata: { author: 'RCC Development Team', license: 'MIT', repository: 'https://github.com/rcc/rcc-virtual-model-rules' } }; super(moduleInfo); this.rules = new Map(); this.schedules = new Map(); this.ruleMetrics = new Map(); this.evaluationContext = null; this.isInitialized = false; this.ruleExecutionCache = new Map(); this.cacheCleanupInterval = null; } /** * Configure the virtual model rules module */ configure(config) { super.configure(config); this.logInfo('Virtual Model Rules module configured', 'configure'); } /** * Initialize the virtual model rules module */ async initialize() { if (this.isInitialized) { this.warn('Virtual Model Rules module is already initialized', 'initialize'); return; } this.log('Initializing Virtual Model Rules Module', {}, 'initialize'); try { // Call parent initialize first await super.initialize(); // Initialize rule engine this.initializeRuleEngine(); // Setup message handlers this.setupMessageHandlers(); // Start cache cleanup this.startCacheCleanup(); this.isInitialized = true; this.logInfo('Virtual Model Rules Module initialized successfully', 'initialize'); // Notify initialization complete this.broadcastMessage('virtual-model-rules-initialized', { ruleCount: this.rules.size, scheduleCount: this.schedules.size }); } catch (error) { this.error('Failed to initialize Virtual Model Rules Module [initialize]', error instanceof Error ? error.message : String(error)); throw error; } } /** * Register a virtual model rule */ async registerRule(rule) { this.log('Registering virtual model rule', { ruleId: rule.id }, 'registerRule'); // Validate rule configuration this.validateRule(rule); // Add to rules registry this.rules.set(rule.id, rule); // Initialize metrics for the rule this.ruleMetrics.set(rule.id, { ruleId: rule.id, totalEvaluations: 0, successfulMatches: 0, failedMatches: 0, matchRate: 0, avgEvaluationTime: 0, avgConfidence: 0, lastEvaluation: 0, actionSuccessRate: 0, errorCount: 0 }); this.logInfo('Virtual model rule registered successfully', 'registerRule'); // Notify rule registered this.broadcastMessage('virtual-model-rule-registered', { rule }); } /** * Unregister a virtual model rule */ async unregisterRule(ruleId) { this.log('Unregistering virtual model rule', { ruleId }, 'unregisterRule'); if (!this.rules.has(ruleId)) { throw new Error(`Rule '${ruleId}' not found`); } // Remove from registries this.rules.delete(ruleId); this.ruleMetrics.delete(ruleId); // Clear cache entries for this rule this.clearRuleCache(ruleId); this.logInfo('Virtual model rule unregistered successfully', 'unregisterRule'); // Notify rule unregistered this.broadcastMessage('virtual-model-rule-unregistered', { ruleId }); } /** * Get a rule by ID */ getRule(ruleId) { return this.rules.get(ruleId); } /** * Get all rules */ getRules() { return Array.from(this.rules.values()); } /** * Get rules for a specific model */ getRulesForModel(modelId) { return Array.from(this.rules.values()).filter(rule => !rule.modelId || rule.modelId === modelId); } /** * Evaluate rules for a given context */ async evaluateRules(context) { this.log('Evaluating rules', { requestId: context.requestId }, 'evaluateRules'); const results = []; const applicableRules = this.getApplicableRules(context); for (const rule of applicableRules) { try { const startTime = Date.now(); const result = await this.evaluateRule(rule, context); const evaluationTime = Date.now() - startTime; // Update metrics this.updateRuleMetrics(rule.id, result, evaluationTime); results.push(result); } catch (error) { this.error('Rule evaluation failed [evaluateRules]', `Rule ${rule.id} failed: ${error instanceof Error ? error.message : String(error)}`); // Create error result results.push({ ruleId: rule.id, matched: false, confidence: 0, executionResults: { actions: [], totalExecutionTime: 0, allSuccess: false }, timestamp: Date.now(), metrics: { conditionEvalTime: 0, conditionsEvaluated: 0, conditionsMatched: 0 } }); } } // Sort results by confidence (highest first) results.sort((a, b) => b.confidence - a.confidence); this.log('Rules evaluation completed', { requestId: context.requestId, resultCount: results.length }, 'evaluateRules'); return results; } /** * Get rule metrics */ getRuleMetrics(ruleId) { if (ruleId) { const metrics = this.ruleMetrics.get(ruleId); return metrics ? [metrics] : []; } return Array.from(this.ruleMetrics.values()); } /** * Load rules from configuration file */ async loadRules(rulesPath) { this.log('Loading rules from configuration file', { rulesPath }, 'loadRules'); // Mark as under construction feature const underConstruction = new (await Promise.resolve().then(() => __importStar(require('rcc-underconstruction')))).UnderConstruction(); await underConstruction.initialize(); underConstruction.callUnderConstructionFeature('load-rules-from-config', { caller: 'VirtualModelRulesModule.loadRules', parameters: { rulesPath }, purpose: '从配置文件加载规则' }); // Placeholder implementation this.logInfo('Rules loading feature is under construction', 'loadRules'); } /** * Add a new virtual model rule (alias for registerRule) */ async addRule(rule) { return await this.registerRule(rule); } /** * Remove a virtual model rule (alias for unregisterRule) */ async removeRule(ruleId) { return await this.unregisterRule(ruleId); } /** * Update an existing rule */ async updateRule(ruleId, updates) { this.log('Updating virtual model rule', { ruleId }, 'updateRule'); const existingRule = this.rules.get(ruleId); if (!existingRule) { throw new Error(`Rule '${ruleId}' not found`); } // Merge updates with existing rule const updatedRule = { ...existingRule, ...updates }; // Remove and re-add the rule await this.unregisterRule(ruleId); await this.registerRule(updatedRule); this.logInfo('Virtual model rule updated successfully', 'updateRule'); } /** * Get model schedule (alias for getSchedule) */ getModelSchedule(modelId) { return this.getSchedule(modelId); } /** * Set model schedule (alias for registerSchedule) */ async setModelSchedule(modelId, schedule) { // Ensure schedule has the correct modelId schedule.modelId = modelId; await this.registerSchedule(schedule); } /** * Get active rules for model */ getActiveRules(modelId) { return this.getRulesForModel(modelId).filter(rule => rule.enabled); } /** * Enable/disable rule */ async setRuleEnabled(ruleId, enabled) { this.log('Setting rule enabled state', { ruleId, enabled }, 'setRuleEnabled'); const rule = this.rules.get(ruleId); if (!rule) { throw new Error(`Rule '${ruleId}' not found`); } rule.enabled = enabled; this.logInfo('Rule enabled state updated successfully', 'setRuleEnabled'); // Notify rule state changed this.broadcastMessage('rule-state-changed', { ruleId, enabled }); } /** * Register a model schedule */ async registerSchedule(schedule) { this.log('Registering model schedule', { modelId: schedule.modelId }, 'registerSchedule'); // Validate schedule configuration this.validateSchedule(schedule); // Add to schedules registry this.schedules.set(schedule.modelId, schedule); this.logInfo('Model schedule registered successfully', 'registerSchedule'); // Notify schedule registered this.broadcastMessage('model-schedule-registered', { schedule }); } /** * Unregister a model schedule */ async unregisterSchedule(modelId) { this.log('Unregistering model schedule', { modelId }, 'unregisterSchedule'); if (!this.schedules.has(modelId)) { throw new Error(`Schedule for model '${modelId}' not found`); } // Remove from schedules registry this.schedules.delete(modelId); this.logInfo('Model schedule unregistered successfully', 'unregisterSchedule'); // Notify schedule unregistered this.broadcastMessage('model-schedule-unregistered', { modelId }); } /** * Get schedule for a model */ getSchedule(modelId) { return this.schedules.get(modelId); } /** * Get all schedules */ getSchedules() { return Array.from(this.schedules.values()); } /** * Check if a model is currently scheduled to run */ async isModelScheduled(modelId) { const schedule = this.schedules.get(modelId); if (!schedule || !schedule.enabled) { return false; } // Check time windows and constraints return this.isScheduleActive(schedule); } /** * Get configuration */ getConfig() { return super.getConfig(); } /** * Update configuration */ async updateConfig(config) { this.log('Updating virtual model rules configuration', config, 'updateConfig'); // Call parent update super.configure(config); this.logInfo('Virtual model rules configuration updated successfully', 'updateConfig'); } /** * Handle incoming messages */ async handleMessage(message) { this.log('Handling message', { type: message?.type, source: message?.source }, 'handleMessage'); switch (message?.type) { case 'rule-evaluation-request': return await this.handleRuleEvaluation(message); case 'schedule-status-request': return await this.handleScheduleStatus(message); case 'rule-metrics-request': return await this.handleRuleMetrics(message); default: return await super.handleMessage(message); } } /** * Cleanup resources */ async destroy() { this.log('Cleaning up Virtual Model Rules Module', {}, 'destroy'); try { // Clear registries this.rules.clear(); this.schedules.clear(); this.ruleMetrics.clear(); this.ruleExecutionCache.clear(); this.evaluationContext = null; this.isInitialized = false; // Clear cache cleanup interval if (this.cacheCleanupInterval) { clearInterval(this.cacheCleanupInterval); this.cacheCleanupInterval = null; } await super.destroy(); } catch (error) { this.error('Error during cleanup [destroy]', error instanceof Error ? error.message : String(error)); throw error; } } /** * Initialize rule engine */ initializeRuleEngine() { this.log('Initializing rule engine', {}, 'initializeRuleEngine'); // Initialize with default rules if needed // This could load rules from configuration files this.logInfo('Rule engine initialized successfully', 'initializeRuleEngine'); } /** * Setup message handlers */ setupMessageHandlers() { this.log('Setting up message handlers', {}, 'setupMessageHandlers'); // Message handling is done in handleMessage method } /** * Start cache cleanup */ startCacheCleanup() { // Clean up expired cache entries every minute const intervalId = setInterval(() => { this.cleanupCache(); }, 60000); // Store interval ID for cleanup this.cacheCleanupInterval = intervalId; this.log('Cache cleanup started', {}, 'startCacheCleanup'); } /** * Validate rule configuration */ validateRule(rule) { if (!rule.id || !rule.name || !rule.priority) { throw new Error('Rule configuration missing required fields: id, name, priority'); } 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'); } // Validate priority is one of the allowed values const validPriorities = ['low', 'medium', 'high', 'critical']; if (!validPriorities.includes(rule.priority)) { throw new Error('Rule priority must be one of: low, medium, high, critical'); } } /** * Validate schedule configuration */ validateSchedule(schedule) { if (!schedule.modelId || !schedule.priority) { throw new Error('Schedule configuration missing required fields: modelId, priority'); } if (schedule.maxExecutionTime && schedule.maxExecutionTime < 1000) { throw new Error('Max execution time must be at least 1000ms'); } } /** * Get applicable rules for a context */ getApplicableRules(context) { const applicableRules = Array.from(this.rules.values()) .filter(rule => rule.enabled) .filter(rule => !rule.modelId || rule.modelId === context.model?.modelId) .sort((a, b) => { // Sort by priority (higher priority first) const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 }; return priorityOrder[b.priority] - priorityOrder[a.priority]; }); this.trace('Found applicable rules [getApplicableRules]', { requestId: context.requestId, ruleCount: applicableRules.length }); return applicableRules; } /** * Evaluate a single rule */ async evaluateRule(rule, context) { const startTime = Date.now(); // Check cache first const cacheKey = this.getCacheKey(rule, context); const cachedResult = this.ruleExecutionCache.get(cacheKey); if (cachedResult && Date.now() - cachedResult.timestamp < cachedResult.ttl) { this.trace('Using cached rule result [evaluateRule]', { ruleId: rule.id }); return this.createEvaluationResult(rule, true, cachedResult.result, 0.95); } // Evaluate conditions const conditionStartTime = Date.now(); const { matched, confidence, conditionsEvaluated, conditionsMatched } = await this.evaluateConditions(rule, context); const conditionEvalTime = Date.now() - conditionStartTime; if (!matched) { // Cache negative result this.ruleExecutionCache.set(cacheKey, { result: false, timestamp: Date.now(), ttl: 30000 // 30 seconds for negative results }); return this.createEvaluationResult(rule, false, false, 0, conditionEvalTime, conditionsEvaluated, conditionsMatched); } // Execute actions const executionResults = await this.executeActions(rule, context); // Cache positive result this.ruleExecutionCache.set(cacheKey, { result: true, timestamp: Date.now(), ttl: 60000 // 60 seconds for positive results }); const totalExecutionTime = Date.now() - startTime; return this.createEvaluationResult(rule, true, true, confidence, conditionEvalTime, conditionsEvaluated, conditionsMatched, executionResults, totalExecutionTime); } /** * Evaluate rule conditions */ async evaluateConditions(rule, context) { let conditionsEvaluated = 0; let conditionsMatched = 0; let totalWeight = 0; let matchedWeight = 0; for (const condition of rule.conditions) { conditionsEvaluated++; try { const result = await this.evaluateCondition(condition, context); const weight = condition.weight || 1; totalWeight += weight; if (result) { conditionsMatched++; matchedWeight += weight; } } catch (error) { this.warn('Condition evaluation failed [evaluateConditions]', `Rule ${rule.id}, field ${condition.field}: ${error instanceof Error ? error.message : String(error)}`); } } const matched = conditionsMatched > 0; const confidence = totalWeight > 0 ? matchedWeight / totalWeight : 0; return { matched, confidence, conditionsEvaluated, conditionsMatched }; } /** * Evaluate a single condition */ async evaluateCondition(condition, context) { const fieldValue = this.getFieldValue(context, condition.field); switch (condition.operator) { case 'equals': return fieldValue === condition.value; case 'not_equals': return fieldValue !== condition.value; case 'contains': return typeof fieldValue === 'string' && fieldValue.includes(condition.value); case 'not_contains': return typeof fieldValue === 'string' && !fieldValue.includes(condition.value); case 'greater_than': return typeof fieldValue === 'number' && fieldValue > condition.value; case 'less_than': return typeof fieldValue === 'number' && fieldValue < condition.value; case 'in': return Array.isArray(condition.value) && condition.value.includes(fieldValue); case 'not_in': return Array.isArray(condition.value) && !condition.value.includes(fieldValue); case 'regex': return typeof fieldValue === 'string' && new RegExp(condition.value).test(fieldValue); default: return false; } } /** * Get field value from context */ getFieldValue(context, field) { const parts = field.split('.'); let value = context; for (const part of parts) { if (value && typeof value === 'object') { value = value[part]; } else { return undefined; } } return value; } /** * Execute rule actions */ async executeActions(rule, context) { const actions = []; let totalExecutionTime = 0; let allSuccess = true; for (const action of rule.actions) { const startTime = Date.now(); try { const result = await this.executeAction(action, context); const executionTime = Date.now() - startTime; actions.push({ actionId: action.id || 'unknown', success: true, result, executionTime }); totalExecutionTime += executionTime; } catch (error) { const executionTime = Date.now() - startTime; actions.push({ actionId: action.id || 'unknown', success: false, error: error instanceof Error ? error.message : String(error), executionTime }); totalExecutionTime += executionTime; allSuccess = false; } } return { actions, totalExecutionTime, allSuccess }; } /** * Execute a single action */ async executeAction(action, context) { this.trace('Executing action [executeAction]', { type: action.type }); switch (action.type) { case 'route_to_model': return this.executeRouteToModel(action, context); case 'set_priority': return this.executeSetPriority(action, context); case 'add_tag': return this.executeAddTag(action, context); case 'remove_tag': return this.executeRemoveTag(action, context); case 'log_event': return this.executeLogEvent(action, context); default: throw new Error(`Unknown action type: ${action.type}`); } } /** * Execute route to model action */ async executeRouteToModel(action, context) { this.trace('Routing to model [executeRouteToModel]', action.parameters); return { routed: true, modelId: action.parameters.modelId }; } /** * Execute set priority action */ async executeSetPriority(action, context) { this.trace('Setting priority [executeSetPriority]', action.parameters); return { priority: action.parameters.priority }; } /** * Execute add tag action */ async executeAddTag(action, context) { this.trace('Adding tag [executeAddTag]', action.parameters); return { tagAdded: true, tag: action.parameters.tag }; } /** * Execute remove tag action */ async executeRemoveTag(action, context) { this.trace('Removing tag [executeRemoveTag]', action.parameters); return { tagRemoved: true, tag: action.parameters.tag }; } /** * Execute log event action */ async executeLogEvent(action, context) { this.log('Event logged', { event: action.parameters.event }, 'executeLogEvent'); return { logged: true }; } /** * Create evaluation result */ createEvaluationResult(rule, matched, result, confidence, conditionEvalTime = 0, conditionsEvaluated = 0, conditionsMatched = 0, executionResults, totalExecutionTime = 0) { return { ruleId: rule.id, matched, confidence, executionResults: executionResults || { actions: [], totalExecutionTime: 0, allSuccess: false }, timestamp: Date.now(), metrics: { conditionEvalTime, conditionsEvaluated, conditionsMatched } }; } /** * Update rule metrics */ updateRuleMetrics(ruleId, result, evaluationTime) { const metrics = this.ruleMetrics.get(ruleId); if (!metrics) return; metrics.totalEvaluations++; metrics.lastEvaluation = Date.now(); metrics.avgEvaluationTime = (metrics.avgEvaluationTime * (metrics.totalEvaluations - 1) + evaluationTime) / metrics.totalEvaluations; if (result.matched) { metrics.successfulMatches++; metrics.avgConfidence = (metrics.avgConfidence * (metrics.successfulMatches - 1) + result.confidence) / metrics.successfulMatches; } else { metrics.failedMatches++; } metrics.matchRate = metrics.successfulMatches / metrics.totalEvaluations; if (result.executionResults.allSuccess) { const successCount = metrics.actionSuccessRate * (metrics.totalEvaluations - 1); metrics.actionSuccessRate = (successCount + 1) / metrics.totalEvaluations; } if (!result.executionResults.allSuccess) { metrics.errorCount++; } } /** * Get cache key for rule evaluation */ getCacheKey(rule, context) { return `${rule.id}_${context.requestId}_${context.model?.modelId || 'unknown'}_${JSON.stringify(context.request)}`; } /** * Clear cache entries for a rule */ clearRuleCache(ruleId) { const keysToDelete = []; for (const [key, value] of this.ruleExecutionCache.entries()) { if (key.startsWith(`${ruleId}_`)) { keysToDelete.push(key); } } for (const key of keysToDelete) { this.ruleExecutionCache.delete(key); } } /** * Cleanup expired cache entries */ cleanupCache() { const now = Date.now(); const keysToDelete = []; for (const [key, value] of this.ruleExecutionCache.entries()) { if (now - value.timestamp > value.ttl) { keysToDelete.push(key); } } for (const key of keysToDelete) { this.ruleExecutionCache.delete(key); } if (keysToDelete.length > 0) { this.trace('Cache cleanup completed [cleanupCache]', { entriesRemoved: keysToDelete.length }); } } /** * Check if schedule is currently active */ isScheduleActive(schedule) { const now = new Date(); const currentTime = now.getHours() * 60 + now.getMinutes(); // Check time windows if (schedule.timeWindows) { const { startTime, endTime } = schedule.timeWindows; if (startTime && endTime) { const [startHour, startMinute] = startTime.split(':').map(Number); const [endHour, endMinute] = endTime.split(':').map(Number); const startTimeMinutes = startHour * 60 + startMinute; const endTimeMinutes = endHour * 60 + endMinute; if (currentTime < startTimeMinutes || currentTime > endTimeMinutes) { return false; } } // Check days of week if (schedule.timeWindows.daysOfWeek) { const currentDay = now.getDay(); if (!schedule.timeWindows.daysOfWeek.includes(currentDay)) { return false; } } } return true; } /** * Handle rule evaluation message */ async handleRuleEvaluation(message) { this.log('Handling rule evaluation request', {}, 'handleRuleEvaluation'); const context = message.payload?.context; if (!context) { return { messageId: message.id, correlationId: message.correlationId || '', success: false, error: 'Missing evaluation context', timestamp: Date.now() }; } try { const results = await this.evaluateRules(context); return { messageId: message.id, correlationId: message.correlationId || '', success: true, data: { results }, timestamp: Date.now() }; } catch (error) { return { messageId: message.id, correlationId: message.correlationId || '', success: false, error: error instanceof Error ? error.message : String(error), timestamp: Date.now() }; } } /** * Handle schedule status message */ async handleScheduleStatus(message) { this.log('Handling schedule status request', {}, 'handleScheduleStatus'); const modelId = message.payload?.modelId; const scheduleStatus = {}; if (modelId) { scheduleStatus[modelId] = await this.isModelScheduled(modelId); } else { for (const modelId of this.schedules.keys()) { scheduleStatus[modelId] = await this.isModelScheduled(modelId); } } return { messageId: message.id, correlationId: message.correlationId || '', success: true, data: { scheduleStatus }, timestamp: Date.now() }; } /** * Handle rule metrics message */ async handleRuleMetrics(message) { this.log('Handling rule metrics request', {}, 'handleRuleMetrics'); const ruleId = message.payload?.ruleId; const metrics = this.getRuleMetrics(ruleId); return { messageId: message.id, correlationId: message.correlationId || '', success: true, data: { metrics }, timestamp: Date.now() }; } } exports.VirtualModelRulesModule = VirtualModelRulesModule; //# sourceMappingURL=VirtualModelRulesModule.js.map