UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

529 lines 20.4 kB
/** * Enhanced Model Router with Agent Booster AST Integration * * Implements ADR-026: 3-tier intelligent model routing: * - Tier 1: Agent Booster (WASM) - <1ms, $0 for simple transforms * - Tier 2: Haiku - ~500ms for low complexity * - Tier 3: Sonnet/Opus - 2-5s for high complexity * * @module enhanced-model-router */ import { existsSync, readFileSync } from 'fs'; import { extname } from 'path'; import { getModelRouter } from './model-router.js'; // ============================================================================ // Intent Detection Patterns // ============================================================================ /** * Pattern definitions for Agent Booster intent detection */ const INTENT_PATTERNS = { 'var-to-const': { patterns: [ /convert\s+var\s+to\s+const/i, /change\s+var\s+to\s+const/i, /change\s+var\s+declarations?\s+to\s+const/i, /replace\s+var\s+with\s+const/i, /var\s*(?:→|->|to)\s*const/i, /use\s+const\s+instead\s+of\s+var/i, ], weight: 1.0, description: 'Convert var declarations to const/let', }, 'add-types': { patterns: [ /add\s+type\s+annotations?/i, /add\s+typescript\s+types?/i, /type\s+this\s+function/i, /add\s+types?\s+to/i, /annotate\s+with\s+types?/i, ], weight: 0.9, description: 'Add TypeScript type annotations', }, 'add-error-handling': { patterns: [ /add\s+error\s+handling/i, /wrap\s+in\s+try\s*[/-]?\s*catch/i, /add\s+try\s*[/-]?\s*catch/i, /handle\s+errors?/i, /add\s+exception\s+handling/i, ], weight: 0.7, // Lower weight - often needs more context description: 'Wrap code in try/catch blocks', }, 'async-await': { patterns: [ /convert\s+to\s+async\s*[/-]?\s*await/i, /convert\s+\w+\s+to\s+async/i, /use\s+async\s*[/-]?\s*await/i, /change\s+promises?\s+to\s+async/i, /refactor\s+to\s+async/i, /\.then\s*(?:→|->|to)\s*await/i, /callback\s+to\s+async/i, /callbacks?\s+to\s+async/i, ], weight: 0.8, description: 'Convert callbacks/promises to async/await', }, 'add-logging': { patterns: [ /add\s+logging/i, /add\s+console\.log/i, /add\s+debug\s+logs?/i, /log\s+this\s+function/i, /add\s+trace\s+logging/i, ], weight: 0.85, description: 'Add console.log or logging statements', }, 'remove-console': { patterns: [ /remove\s+(?:all\s+)?console\.log/i, /remove\s+(?:all\s+)?console\s+statements?/i, /delete\s+(?:all\s+)?console\s+statements?/i, /strip\s+console/i, /clean\s+up\s+console/i, /clean\s+up\s+debug\s+logs?/i, /remove\s+(?:all\s+)?debug\s+logs?/i, /delete\s+(?:all\s+)?console\.log/i, ], weight: 0.95, description: 'Remove console.* calls', }, }; /** * File path extraction patterns */ const FILE_PATH_PATTERNS = [ /(?:in|from|to|file|path)\s+[`"']?([a-zA-Z0-9_./\\-]+\.[a-zA-Z]+)[`"']?/i, /[`"']([a-zA-Z0-9_./\\-]+\.[a-zA-Z]+)[`"']/, /(\S+\.[tj]sx?)\b/i, /(\S+\.(?:js|ts|jsx|tsx|py|rb|go|rs|java|kt|swift|c|cpp|h))\b/i, ]; /** * Language detection by extension */ /** * High-complexity keywords that indicate Tier 3 (Opus) routing * These tasks require deep reasoning and architectural understanding */ const TIER3_KEYWORDS = [ // Architecture & Design /\b(microservices?|architecture|system\s+design|distributed)\b/i, /\b(design|architect|plan)\s+(a|an|the|complex)\b/i, /\b(design)\s+\w+\s+(schema|system|architecture)\b/i, // Security /\b(oauth2?|pkce|jwt|rbac|authentication\s+system|security\s+audit)\b/i, /\b(refresh\s+token|token\s+rotation|role-based|permission|authorization)\b/i, /\b(encryption|cryptograph|certificate|ssl|tls)\b/i, /\b(end-to-end\s+encryption|key\s+rotation|secure\s+channel)\b/i, // Distributed Systems /\b(consensus|distributed|byzantine|raft|paxos)\b/i, /\b(replication|sharding|partitioning|eventual\s+consistency)\b/i, /\b(load\s+balanc|fault[- ]toleran|high\s+availability)\b/i, /\b(message\s+queue|event\s+sourc|cqrs|saga)\b/i, // Complex Algorithms /\b(algorithm|machine\s+learning|neural|optimization)\b/i, /\b(graph\s+algorithm|tree\s+traversal|dynamic\s+programming)\b/i, // Database Design /\b(schema\s+design|database\s+architect|data\s+model)\b/i, /\b(database\s+schema|multi[- ]tenant)\b/i, /\b(normalization|denormalization|index\s+strateg)\b/i, // Performance Critical /\b(performance\s+critical|low\s+latency|high\s+throughput)\b/i, /\b(memory\s+optimi|cache\s+strateg|concurrent)\b/i, ]; const LANGUAGE_MAP = { '.js': 'javascript', '.jsx': 'javascript', '.ts': 'typescript', '.tsx': 'typescript', '.py': 'python', '.rb': 'ruby', '.go': 'go', '.rs': 'rust', '.java': 'java', '.kt': 'kotlin', '.swift': 'swift', '.c': 'c', '.cpp': 'cpp', '.h': 'c', }; // ============================================================================ // Enhanced Model Router Implementation // ============================================================================ /** * Enhanced Model Router with Agent Booster AST integration * * Provides intelligent 3-tier routing: * - Tier 1: Agent Booster for simple code transforms (352x faster, $0) * - Tier 2: Haiku for low complexity tasks * - Tier 3: Sonnet/Opus for complex reasoning tasks */ export class EnhancedModelRouter { config; tinyDancerRouter; constructor(config) { this.config = { agentBoosterEnabled: true, agentBoosterConfidenceThreshold: 0.7, enabledIntents: [ 'var-to-const', 'add-types', 'add-error-handling', 'async-await', 'add-logging', 'remove-console', ], complexityThresholds: { haiku: 0.3, sonnet: 0.6, opus: 1.0, }, preferCost: false, preferQuality: false, ...config, }; this.tinyDancerRouter = getModelRouter(); } /** * Detect code editing intent from task description */ detectIntent(task) { const taskLower = task.toLowerCase(); let bestIntent = null; let bestScore = 0; for (const [intentType, config] of Object.entries(INTENT_PATTERNS)) { if (!this.config.enabledIntents.includes(intentType)) { continue; } for (const pattern of config.patterns) { if (pattern.test(taskLower)) { const score = config.weight; if (score > bestScore) { bestScore = score; bestIntent = { type: intentType, confidence: score, description: config.description, }; } } } } // Extract file path if intent found if (bestIntent) { const filePath = this.extractFilePath(task); if (filePath) { bestIntent.filePath = filePath; bestIntent.language = this.detectLanguage(filePath); // Boost confidence if file exists if (existsSync(filePath)) { bestIntent.confidence = Math.min(1.0, bestIntent.confidence + 0.1); } } } return bestIntent; } /** * Extract file path from task description */ extractFilePath(task) { for (const pattern of FILE_PATH_PATTERNS) { const match = task.match(pattern); if (match && match[1]) { return match[1]; } } return null; } /** * Detect language from file extension */ detectLanguage(filePath) { const ext = extname(filePath).toLowerCase(); return LANGUAGE_MAP[ext] || 'javascript'; } /** * Check if task contains Tier 3 (Opus) keywords */ containsTier3Keywords(task) { let count = 0; for (const pattern of TIER3_KEYWORDS) { if (pattern.test(task)) { count++; } } return { matches: count > 0, count }; } /** * Route a task to the optimal tier and handler */ async route(task, context) { // Step 1: Try Agent Booster intent detection if (this.config.agentBoosterEnabled) { const intent = this.detectIntent(task); if (intent && intent.confidence >= this.config.agentBoosterConfidenceThreshold) { return { tier: 1, handler: 'agent-booster', confidence: intent.confidence, reasoning: `Agent Booster can handle "${intent.type}" with ${(intent.confidence * 100).toFixed(0)}% confidence`, agentBoosterIntent: intent, canSkipLLM: true, estimatedLatencyMs: 1, estimatedCost: 0, }; } } // Step 2: Check for Tier 3 keywords (architecture, security, distributed) const tier3Check = this.containsTier3Keywords(task); if (tier3Check.matches && tier3Check.count >= 2) { // Strong signal for Opus - multiple complex keywords return { tier: 3, handler: 'opus', model: 'opus', confidence: Math.min(0.95, 0.7 + tier3Check.count * 0.1), complexity: 0.8 + tier3Check.count * 0.05, reasoning: `High complexity task (${tier3Check.count} architectural keywords) - using opus`, canSkipLLM: false, estimatedLatencyMs: 5000, estimatedCost: 0.015, }; } // Step 3: AST complexity analysis (if file path provided) let astComplexity; const targetFile = context?.filePath || this.extractFilePath(task); if (targetFile && existsSync(targetFile)) { try { astComplexity = await this.analyzeASTComplexity(targetFile); } catch { // AST analysis not available, continue with text-based routing } } // Step 4: Text-based complexity + tiny-dancer routing const tinyDancerResult = await this.tinyDancerRouter.route(task); // Step 5: Combine AST complexity with tiny-dancer result // Also boost if single tier3 keyword found let finalComplexity = astComplexity !== undefined ? (astComplexity + tinyDancerResult.complexity) / 2 : tinyDancerResult.complexity; // Boost complexity if tier3 keywords found (even just one) if (tier3Check.matches) { finalComplexity = Math.min(1.0, finalComplexity + 0.25); } // Step 6: Determine tier based on complexity const { haiku, sonnet } = this.config.complexityThresholds; if (finalComplexity < haiku) { return { tier: 2, handler: 'haiku', model: 'haiku', confidence: tinyDancerResult.confidence, complexity: finalComplexity, reasoning: `Low complexity (${(finalComplexity * 100).toFixed(0)}%) - using haiku`, canSkipLLM: false, estimatedLatencyMs: 500, estimatedCost: 0.0002, }; } if (finalComplexity < sonnet) { return { tier: 2, handler: 'sonnet', model: 'sonnet', confidence: tinyDancerResult.confidence, complexity: finalComplexity, reasoning: `Medium complexity (${(finalComplexity * 100).toFixed(0)}%) - using sonnet`, canSkipLLM: false, estimatedLatencyMs: 2000, estimatedCost: 0.003, }; } return { tier: 3, handler: 'opus', model: 'opus', confidence: tinyDancerResult.confidence, complexity: finalComplexity, reasoning: `High complexity (${(finalComplexity * 100).toFixed(0)}%) - using opus`, canSkipLLM: false, estimatedLatencyMs: 5000, estimatedCost: 0.015, }; } /** * Analyze AST complexity of a file * Returns normalized complexity score (0-1) */ async analyzeASTComplexity(filePath) { try { const content = readFileSync(filePath, 'utf-8'); const lines = content.split('\n'); // Simple heuristics for complexity let complexity = 0; // Line count contribution complexity += Math.min(0.3, lines.length / 1000); // Nesting depth estimation (count indentation) const avgIndent = lines .filter((l) => l.trim().length > 0) .map((l) => l.match(/^(\s*)/)?.[1].length || 0) .reduce((sum, indent) => sum + indent, 0) / Math.max(1, lines.length); complexity += Math.min(0.2, avgIndent / 20); // Control flow complexity (count keywords) const controlFlowCount = (content.match(/\b(if|else|for|while|switch|case|try|catch|async|await)\b/g) || []).length; complexity += Math.min(0.3, controlFlowCount / 100); // Function/class count const functionCount = (content.match(/\b(function|class|=>)\b/g) || []).length; complexity += Math.min(0.2, functionCount / 50); return Math.min(1, complexity); } catch { return 0.5; // Default to medium complexity on error } } /** * Execute task using the appropriate tier * Returns the result and routing information */ async execute(task, context) { const routeResult = await this.route(task, context); if (routeResult.tier === 1 && routeResult.agentBoosterIntent) { // Try to execute with Agent Booster const abResult = await this.tryAgentBooster(routeResult.agentBoosterIntent, context); if (abResult.success) { return { result: { applied: true, confidence: abResult.confidence }, routeResult, }; } // Agent Booster failed, fall back to LLM routeResult.tier = 2; routeResult.handler = 'sonnet'; routeResult.model = 'sonnet'; routeResult.canSkipLLM = false; routeResult.reasoning += ' (Agent Booster fallback to LLM)'; } // Return routing result - caller handles LLM invocation return { result: routeResult.reasoning, routeResult }; } /** * Try to apply edit using Agent Booster */ async tryAgentBooster(intent, context) { try { const filePath = intent.filePath || context?.filePath; if (!filePath || !existsSync(filePath)) { return { success: false, confidence: 0 }; } const originalCode = context?.originalCode || readFileSync(filePath, 'utf-8'); const intentToInstruction = { 'var-to-const': 'Convert all var declarations to const', 'add-types': 'Add TypeScript type annotations', 'add-error-handling': 'Wrap in try/catch blocks', 'async-await': 'Convert to async/await', 'add-logging': 'Add console.log statements', 'remove-console': 'Remove all console.* statements', }; const instruction = intentToInstruction[intent.type]; const language = intent.language || 'javascript'; // Try local agentic-flow agent-booster (v3 — no npx needed) // Note: agent-booster export declared but dist missing in alpha.1; use intelligence path as fallback const boosterModule = await import('agentic-flow/agent-booster') .catch(() => import(/* @vite-ignore */ 'agentic-flow/intelligence/agent-booster-enhanced')) .catch(() => null); if (boosterModule?.enhancedApply) { const result = await boosterModule.enhancedApply({ code: originalCode, edit: instruction, language, }); if (result && result.confidence >= this.config.agentBoosterConfidenceThreshold) { return { success: true, confidence: result.confidence, output: result.output }; } return { success: false, confidence: result?.confidence ?? 0 }; } // Fallback: shell out to npx agent-booster // Sanitize language to prevent command injection (whitelist only) const SAFE_LANGUAGES = ['javascript', 'typescript', 'python', 'rust', 'go', 'java', 'c', 'cpp', 'ruby', 'swift', 'kotlin']; const safeLang = SAFE_LANGUAGES.includes(language) ? language : 'javascript'; const { execSync } = await import('child_process'); const cmd = `npx --yes agent-booster@0.2.2 apply --language ${safeLang}`; const result = execSync(cmd, { encoding: 'utf-8', input: JSON.stringify({ code: originalCode, edit: instruction }), maxBuffer: 10 * 1024 * 1024, timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], }); const parsed = JSON.parse(result); if (parsed.confidence >= this.config.agentBoosterConfidenceThreshold) { return { success: true, confidence: parsed.confidence, output: parsed.output }; } return { success: false, confidence: parsed.confidence ?? 0 }; } catch { // Agent Booster not available or failed return { success: false, confidence: 0 }; } } /** * Get router statistics */ getStats() { return { config: { ...this.config }, tinyDancerStats: this.tinyDancerRouter.getStats(), }; } } // ============================================================================ // Singleton & Factory Functions // ============================================================================ let enhancedRouterInstance = null; /** * Get or create the singleton EnhancedModelRouter instance */ export function getEnhancedModelRouter(config) { if (!enhancedRouterInstance) { enhancedRouterInstance = new EnhancedModelRouter(config); } return enhancedRouterInstance; } /** * Reset the singleton instance */ export function resetEnhancedModelRouter() { enhancedRouterInstance = null; } /** * Create a new EnhancedModelRouter instance (non-singleton) */ export function createEnhancedModelRouter(config) { return new EnhancedModelRouter(config); } // ============================================================================ // Convenience Functions // ============================================================================ /** * Quick route function with enhanced routing */ export async function enhancedRouteToModel(task, context) { const router = getEnhancedModelRouter(); return router.route(task, context); } /** * Detect if a task can be handled by Agent Booster */ export function canUseAgentBooster(task) { const router = getEnhancedModelRouter(); const intent = router.detectIntent(task); if (intent && intent.confidence >= 0.7) { return { canUse: true, intent }; } return { canUse: false }; } //# sourceMappingURL=enhanced-model-router.js.map