UNPKG

ruv-swarm

Version:

High-performance neural network swarm orchestration in WebAssembly

404 lines (356 loc) 12.9 kB
/** * Progressive WASM Module Loader * Implements on-demand, eager, and progressive loading strategies * for optimal performance and memory usage */ import path from 'path'; import { promises as fs } from 'fs'; class WasmModuleLoader { constructor() { this.modules = new Map(); this.loadingPromises = new Map(); this.loadingStrategy = 'on-demand'; // 'eager', 'on-demand', 'progressive' this.moduleManifest = { core: { path: './wasm/ruv_swarm_wasm_bg.wasm', jsBindings: './wasm/ruv_swarm_wasm.js', size: 512 * 1024, // 512KB priority: 'high', dependencies: [], exists: true, // This module definitely exists type: 'wasm-bindgen', // Uses wasm-bindgen generated bindings }, // Legacy modules - keep for compatibility but mark as optional neural: { path: './wasm/ruv-fann.wasm', size: 1024 * 1024, // 1MB priority: 'medium', dependencies: ['core'], exists: false, // This is a standalone module, not currently built optional: true, }, forecasting: { path: './wasm/neuro-divergent.wasm', size: 1536 * 1024, // 1.5MB priority: 'medium', dependencies: ['core'], exists: false, // This is a standalone module, not currently built optional: true, }, swarm: { path: './wasm/ruv-swarm-orchestration.wasm', size: 768 * 1024, // 768KB priority: 'high', dependencies: ['core'], exists: false, // This functionality is in core module optional: true, }, persistence: { path: './wasm/ruv-swarm-persistence.wasm', size: 256 * 1024, // 256KB priority: 'high', dependencies: ['core'], exists: false, // This functionality is handled by Node.js layer optional: true, }, }; this.baseDir = path.join(new URL('.', import.meta.url).pathname, '..'); } async initialize(strategy = 'progressive') { this.loadingStrategy = strategy; switch (strategy) { case 'eager': return this.loadAllModules(); case 'progressive': return this.loadCoreModules(); case 'on-demand': return this.setupLazyLoading(); default: throw new Error(`Unknown loading strategy: ${strategy}`); } } async loadModule(moduleName) { if (this.modules.has(moduleName)) { return this.modules.get(moduleName); } if (this.loadingPromises.has(moduleName)) { return this.loadingPromises.get(moduleName); } const moduleInfo = this.moduleManifest[moduleName]; if (!moduleInfo) { throw new Error(`Unknown module: ${moduleName}`); } // Check if module is marked as non-existent and optional if (!moduleInfo.exists && moduleInfo.optional) { // Silently use core module for neural and forecasting features // These are integrated into the core module, not separate files if (moduleName === 'neural' || moduleName === 'forecasting') { if (this.modules.has('core')) { const coreModule = this.modules.get('core'); this.modules.set(moduleName, coreModule); // Alias to core module return coreModule; } } else { // Only warn for other optional modules console.warn(`⚠️ Optional module ${moduleName} is not available, functionality will be provided by core module`); } // Return a reference to the core module instead of a placeholder if (moduleName !== 'core' && this.modules.has('core')) { const coreModule = this.modules.get('core'); this.modules.set(moduleName, coreModule); // Alias to core module return coreModule; } throw new Error(`Optional module ${moduleName} not available and core module not loaded`); } // Load dependencies first for (const dep of moduleInfo.dependencies) { await this.loadModule(dep); } const loadingPromise = this.loadWasmModule(moduleName, moduleInfo); this.loadingPromises.set(moduleName, loadingPromise); try { const module = await loadingPromise; this.modules.set(moduleName, module); this.loadingPromises.delete(moduleName); console.log(`✅ Loaded WASM module: ${moduleName} (${this.formatBytes(moduleInfo.size)})`); return module; } catch (error) { this.loadingPromises.delete(moduleName); // If it's an optional module, provide fallback to core functionality if (moduleInfo.optional && this.modules.has('core')) { console.warn(`⚠️ Optional module ${moduleName} failed to load, using core module functionality`); const coreModule = this.modules.get('core'); this.modules.set(moduleName, coreModule); return coreModule; } console.error(`❌ Failed to load WASM module: ${moduleName}`, error); throw error; } } async loadWasmModule(moduleName, moduleInfo) { // Special handling for the core module which uses ES module bindings if (moduleName === 'core') { return this.loadCoreModule(); } // For other modules, load the WASM file directly const wasmPath = path.join(this.baseDir, moduleInfo.path); try { let wasmBuffer; if (typeof window !== 'undefined') { // Browser environment const response = await fetch(wasmPath); if (!response.ok) { throw new Error(`Failed to fetch WASM module: ${response.statusText}`); } wasmBuffer = await response.arrayBuffer(); } else { // Node.js environment try { wasmBuffer = await fs.readFile(wasmPath); } catch (error) { // Fallback: module might not exist yet, return a placeholder console.warn(`Module ${moduleName} not found at ${wasmPath}, using placeholder`); return this.createPlaceholderModule(moduleName); } } const imports = this.getModuleImports(moduleName); const wasmModule = await WebAssembly.instantiate(wasmBuffer, imports); return { instance: wasmModule.instance, module: wasmModule.module, exports: wasmModule.instance.exports, memory: wasmModule.instance.exports.memory, }; } catch (error) { console.warn(`Failed to load ${moduleName}, using placeholder:`, error.message); return this.createPlaceholderModule(moduleName); } } async loadCoreModule() { // Load the core module using ES module bindings try { // Ensure we're using URL-based import for ES modules const wasmJsUrl = new URL('../wasm/ruv_swarm_wasm.js', import.meta.url).href; // Use dynamic import with URL protocol for ES modules const bindings = await import(wasmJsUrl); // Initialize WASM module with file buffer for Node.js if (bindings.default && typeof window === 'undefined') { const wasmPath = path.join(this.baseDir, 'wasm', 'ruv_swarm_wasm_bg.wasm'); try { const wasmBuffer = await fs.readFile(wasmPath); await bindings.default(wasmBuffer); } catch (error) { console.warn('Failed to load WASM file, using bindings defaults:', error); } } return { instance: { exports: bindings }, module: null, exports: bindings, memory: bindings.memory, }; } catch (error) { console.warn('Failed to load core module bindings:', error); return this.createPlaceholderModule('core'); } } getModuleImports(moduleName) { const baseImports = { env: { memory: new WebAssembly.Memory({ initial: 256, maximum: 4096 }), }, wasi_snapshot_preview1: { // Basic WASI imports for compatibility proc_exit: (code) => { throw new Error(`Process exited with code ${code}`); }, fd_write: () => 0, fd_prestat_get: () => 1, fd_prestat_dir_name: () => 1, environ_sizes_get: () => 0, environ_get: () => 0, args_sizes_get: () => 0, args_get: () => 0, clock_time_get: () => Date.now() * 1000000, path_open: () => 1, fd_close: () => 0, fd_read: () => 0, fd_seek: () => 0, random_get: (ptr, len) => { const bytes = new Uint8Array(this.memory.buffer, ptr, len); crypto.getRandomValues(bytes); return 0; }, }, }; // Module-specific imports switch (moduleName) { case 'neural': return { ...baseImports, neural: { log_training_progress: (epoch, loss) => { console.log(`Training progress - Epoch: ${epoch}, Loss: ${loss}`); }, }, }; case 'forecasting': return { ...baseImports, forecasting: { log_forecast: (model, horizon) => { console.log(`Forecasting with model: ${model}, horizon: ${horizon}`); }, }, }; default: return baseImports; } } createPlaceholderModule(moduleName) { // Create a placeholder module with basic functionality console.warn(`Creating placeholder for module: ${moduleName}`); const placeholderExports = { memory: new WebAssembly.Memory({ initial: 1, maximum: 10 }), __wbindgen_malloc: (size) => 0, __wbindgen_realloc: (ptr, oldSize, newSize) => ptr, __wbindgen_free: (ptr, size) => {}, }; // Add module-specific placeholder functions switch (moduleName) { case 'neural': placeholderExports.create_neural_network = () => { console.warn('Neural network module not loaded, using placeholder'); return 0; }; placeholderExports.train_network = () => 0; placeholderExports.forward_pass = () => new Float32Array([0.5]); break; case 'forecasting': placeholderExports.create_forecasting_model = () => { console.warn('Forecasting module not loaded, using placeholder'); return 0; }; placeholderExports.forecast = () => new Float32Array([0.0]); break; case 'swarm': placeholderExports.create_swarm_orchestrator = () => { console.warn('Swarm orchestration module not loaded, using placeholder'); return 0; }; break; } return { instance: { exports: placeholderExports }, module: null, exports: placeholderExports, memory: placeholderExports.memory, isPlaceholder: true, }; } async loadCoreModules() { // Load only the core module - other functionality is included in it await this.loadModule('core'); console.log('🚀 Core WASM module loaded successfully'); return true; } async loadAllModules() { // Only load modules that actually exist const existingModules = Object.keys(this.moduleManifest) .filter(name => this.moduleManifest[name].exists); await Promise.all(existingModules.map(name => this.loadModule(name))); console.log(`🎯 All available WASM modules loaded successfully (${existingModules.length} modules)`); return true; } setupLazyLoading() { // Create proxy objects that load modules on first access const moduleProxies = {}; for (const moduleName of Object.keys(this.moduleManifest)) { moduleProxies[moduleName] = new Proxy({}, { get: (target, prop) => { if (!this.modules.has(moduleName)) { // Trigger module loading this.loadModule(moduleName); throw new Error(`Module ${moduleName} is loading. Please await loadModule('${moduleName}') first.`); } const module = this.modules.get(moduleName); return module.exports[prop]; }, }); } return moduleProxies; } getModuleStatus() { const status = {}; for (const [name, info] of Object.entries(this.moduleManifest)) { status[name] = { loaded: this.modules.has(name), loading: this.loadingPromises.has(name), size: info.size, priority: info.priority, dependencies: info.dependencies, isPlaceholder: this.modules.has(name) && this.modules.get(name).isPlaceholder, }; } return status; } getTotalMemoryUsage() { let totalBytes = 0; for (const module of this.modules.values()) { if (module.memory && module.memory.buffer) { totalBytes += module.memory.buffer.byteLength; } } return totalBytes; } formatBytes(bytes) { if (bytes === 0) { return '0 Bytes'; } const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2)) } ${ sizes[i]}`; } } export { WasmModuleLoader };