diff --git a/crates/ruvector-attention-node/npm/darwin-x64/package.json b/crates/ruvector-attention-node/npm/darwin-x64/package.json index 3ba4b4a6..d028d38a 100644 --- a/crates/ruvector-attention-node/npm/darwin-x64/package.json +++ b/crates/ruvector-attention-node/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/attention-darwin-x64", - "version": "0.1.1", + "version": "0.1.3", "os": [ "darwin" ], diff --git a/crates/ruvector-attention-node/npm/linux-x64-gnu/package.json b/crates/ruvector-attention-node/npm/linux-x64-gnu/package.json index 99506dec..0e2bc8b2 100644 --- a/crates/ruvector-attention-node/npm/linux-x64-gnu/package.json +++ b/crates/ruvector-attention-node/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/attention-linux-x64-gnu", - "version": "0.1.1", + "version": "0.1.3", "os": [ "linux" ], diff --git a/crates/ruvector-attention-node/npm/win32-x64-msvc/package.json b/crates/ruvector-attention-node/npm/win32-x64-msvc/package.json index 8b608052..e4661393 100644 --- a/crates/ruvector-attention-node/npm/win32-x64-msvc/package.json +++ b/crates/ruvector-attention-node/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/attention-win32-x64-msvc", - "version": "0.1.1", + "version": "0.1.3", "os": [ "win32" ], diff --git a/crates/ruvector-attention-node/package.json b/crates/ruvector-attention-node/package.json index 087fd55a..2fe0dfd8 100644 --- a/crates/ruvector-attention-node/package.json +++ b/crates/ruvector-attention-node/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/attention", - "version": "0.1.1", + "version": "0.1.3", "description": "High-performance attention mechanisms for Node.js", "main": "index.js", "types": "index.d.ts", @@ -53,11 +53,13 @@ "access": "public" }, "optionalDependencies": { - "@ruvector/attention-win32-x64-msvc": "0.1.1", - "@ruvector/attention-darwin-x64": "0.1.1", - "@ruvector/attention-linux-x64-gnu": "0.1.1" + "@ruvector/attention-win32-x64-msvc": "0.1.2", + "@ruvector/attention-darwin-x64": "0.1.2", + "@ruvector/attention-darwin-arm64": "0.1.1", + "@ruvector/attention-linux-x64-gnu": "0.1.2", + "@ruvector/attention-linux-arm64-gnu": "0.1.1" }, "devDependencies": { "@napi-rs/cli": "^2.18.0" } -} \ No newline at end of file +} diff --git a/crates/ruvector-gnn-node/npm/linux-arm64-gnu/package.json b/crates/ruvector-gnn-node/npm/linux-arm64-gnu/package.json index 1875e750..1db6c430 100644 --- a/crates/ruvector-gnn-node/npm/linux-arm64-gnu/package.json +++ b/crates/ruvector-gnn-node/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/gnn-linux-arm64-gnu", - "version": "0.1.19", + "version": "0.1.21", "os": [ "linux" ], diff --git a/crates/ruvector-gnn-node/npm/linux-x64-gnu/package.json b/crates/ruvector-gnn-node/npm/linux-x64-gnu/package.json index 1315ff09..d8fc9f9b 100644 --- a/crates/ruvector-gnn-node/npm/linux-x64-gnu/package.json +++ b/crates/ruvector-gnn-node/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/gnn-linux-x64-gnu", - "version": "0.1.19", + "version": "0.1.21", "os": [ "linux" ], diff --git a/crates/ruvector-gnn-node/package.json b/crates/ruvector-gnn-node/package.json index d09a035f..9e3481f8 100644 --- a/crates/ruvector-gnn-node/package.json +++ b/crates/ruvector-gnn-node/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/gnn", - "version": "0.1.19", + "version": "0.1.21", "description": "Graph Neural Network capabilities for Ruvector - Node.js bindings", "main": "index.js", "types": "index.d.ts", @@ -59,4 +59,4 @@ "@ruvector/gnn-linux-arm64-musl": "0.1.19", "@ruvector/gnn-darwin-arm64": "0.1.19" } -} \ No newline at end of file +} diff --git a/crates/sona/Cargo.toml b/crates/sona/Cargo.toml index 109378af..3fb475ea 100644 --- a/crates/sona/Cargo.toml +++ b/crates/sona/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ruvector-sona" -version = "0.1.3" +version = "0.1.4" edition = "2021" rust-version = "1.70" authors = ["RuVector Team "] @@ -62,6 +62,9 @@ features = [ "Window", ] +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2", features = ["js"] } + [dev-dependencies] criterion = "0.5" rand = "0.8" diff --git a/crates/sona/src/training/federated.rs b/crates/sona/src/training/federated.rs index 301e060d..eaabdf09 100644 --- a/crates/sona/src/training/federated.rs +++ b/crates/sona/src/training/federated.rs @@ -186,8 +186,53 @@ impl EphemeralAgent { } /// Force local learning - pub fn force_learn(&self) { - self.engine.force_learn(); + pub fn force_learn(&self) -> String { + self.engine.force_learn() + } + + /// Simple process task method + pub fn process_task(&mut self, embedding: Vec, quality: f32) { + self.process_trajectory(embedding.clone(), embedding, quality, None, vec![]); + } + + /// Process task with route information + pub fn process_task_with_route(&mut self, embedding: Vec, quality: f32, route: &str) { + self.process_trajectory(embedding.clone(), embedding, quality, Some(route.to_string()), vec![]); + } + + /// Get average quality (alias for avg_quality) + pub fn average_quality(&self) -> f32 { + self.avg_quality() + } + + /// Get uptime in seconds + pub fn uptime_seconds(&self) -> u64 { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64; + (now - self.start_time) / 1000 + } + + /// Get agent stats + pub fn stats(&self) -> AgentExportStats { + let engine_stats = self.engine.stats(); + AgentExportStats { + total_trajectories: self.trajectories.len(), + avg_quality: self.avg_quality(), + patterns_learned: engine_stats.patterns_stored, + } + } + + /// Clear trajectories (after export) + pub fn clear(&mut self) { + self.trajectories.clear(); + self.quality_samples.clear(); + } + + /// Get learned patterns from agent + pub fn get_patterns(&self) -> Vec { + self.engine.find_patterns(&[], 0) } /// Export agent state for federation @@ -407,6 +452,39 @@ impl FederatedCoordinator { pub fn metrics(&self) -> &TrainingMetrics { &self.metrics } + + /// Get total number of contributing agents + pub fn agent_count(&self) -> usize { + self.contributions.len() + } + + /// Get total trajectories aggregated + pub fn total_trajectories(&self) -> usize { + self.total_trajectories + } + + /// Find similar patterns + pub fn find_patterns(&self, query: &[f32], k: usize) -> Vec { + self.master_engine.find_patterns(query, k) + } + + /// Apply coordinator's LoRA to input + pub fn apply_lora(&self, input: &[f32]) -> Vec { + let mut output = vec![0.0; input.len()]; + self.master_engine.apply_micro_lora(input, &mut output); + output + } + + /// Consolidate learning (alias for force_consolidate) + pub fn consolidate(&self) -> String { + self.force_consolidate() + } + + /// Clear all contributions + pub fn clear(&mut self) { + self.contributions.clear(); + self.total_trajectories = 0; + } } /// Result of aggregating an agent export diff --git a/crates/sona/src/types.rs b/crates/sona/src/types.rs index d0d98d32..507bda0e 100644 --- a/crates/sona/src/types.rs +++ b/crates/sona/src/types.rs @@ -462,6 +462,48 @@ impl SonaConfig { enable_simd: true, } } + + /// Create config for ephemeral agents (~5MB footprint) + /// + /// Optimized for lightweight federated learning nodes that collect + /// trajectories locally before aggregation. + pub fn for_ephemeral() -> Self { + Self { + hidden_dim: 256, + embedding_dim: 256, + micro_lora_rank: 2, + base_lora_rank: 4, // Small base for memory efficiency + micro_lora_lr: 0.002, + base_lora_lr: 0.0001, + ewc_lambda: 1000.0, + pattern_clusters: 50, // Fewer clusters for memory + trajectory_capacity: 500, // Local buffer before aggregation + background_interval_ms: 60000, // 1 minute for quick local updates + quality_threshold: 0.3, + enable_simd: true, + } + } + + /// Create config for federated coordinator (central aggregation) + /// + /// Optimized for aggregating trajectories from multiple ephemeral agents + /// with larger capacity and pattern storage. + pub fn for_coordinator() -> Self { + Self { + hidden_dim: 256, + embedding_dim: 256, + micro_lora_rank: 2, + base_lora_rank: 16, // Higher rank for aggregated learning + micro_lora_lr: 0.001, // Conservative for stability + base_lora_lr: 0.0005, // Moderate base learning + ewc_lambda: 2000.0, // Strong forgetting prevention + pattern_clusters: 200, // More clusters for diverse patterns + trajectory_capacity: 50000, // Large capacity for aggregation + background_interval_ms: 300000, // 5 minutes consolidation + quality_threshold: 0.4, // Higher threshold for quality filtering + enable_simd: true, + } + } } #[cfg(test)] diff --git a/crates/sona/src/wasm.rs b/crates/sona/src/wasm.rs index 6dc517cb..20c55cce 100644 --- a/crates/sona/src/wasm.rs +++ b/crates/sona/src/wasm.rs @@ -351,6 +351,339 @@ pub fn wasm_init() { web_sys::console::log_1(&"SONA WASM module initialized".into()); } +// ============================================================================ +// Federated Learning WASM Bindings +// ============================================================================ + +use crate::training::{ + EphemeralAgent as RustEphemeralAgent, + FederatedCoordinator as RustFederatedCoordinator, + FederatedTopology, +}; + +/// WASM-compatible Ephemeral Agent for federated learning +/// +/// Lightweight agent wrapper (~5MB footprint) for distributed training. +/// Agents process tasks, collect trajectories, and export state for aggregation. +/// +/// # Example +/// ```javascript +/// const agent = new WasmEphemeralAgent("agent-1"); +/// +/// // Process tasks +/// const embedding = new Float32Array(256).fill(0.1); +/// agent.process_task(embedding, 0.85); +/// +/// // Export state for coordinator +/// const state = agent.export_state(); +/// ``` +#[wasm_bindgen] +pub struct WasmEphemeralAgent { + inner: RustEphemeralAgent, +} + +#[wasm_bindgen] +impl WasmEphemeralAgent { + /// Create a new ephemeral agent with default config + /// + /// # Arguments + /// * `agent_id` - Unique identifier for this agent + /// + /// # Example + /// ```javascript + /// const agent = new WasmEphemeralAgent("agent-1"); + /// ``` + #[wasm_bindgen(constructor)] + pub fn new(agent_id: &str) -> Result { + let config = SonaConfig::for_ephemeral(); + Ok(Self { + inner: RustEphemeralAgent::new(agent_id, config), + }) + } + + /// Create agent with custom configuration + /// + /// # Arguments + /// * `agent_id` - Unique identifier + /// * `config` - JSON configuration object + /// + /// # Example + /// ```javascript + /// const config = { + /// hidden_dim: 256, + /// trajectory_capacity: 500, + /// pattern_clusters: 25 + /// }; + /// const agent = WasmEphemeralAgent.with_config("agent-1", config); + /// ``` + #[wasm_bindgen(js_name = withConfig)] + pub fn with_config(agent_id: &str, config: JsValue) -> Result { + let config: SonaConfig = serde_wasm_bindgen::from_value(config)?; + Ok(Self { + inner: RustEphemeralAgent::new(agent_id, config), + }) + } + + /// Process a task and record trajectory + /// + /// # Arguments + /// * `embedding` - Query embedding as Float32Array + /// * `quality` - Task quality score [0.0, 1.0] + /// + /// # Example + /// ```javascript + /// const embedding = new Float32Array(256).fill(0.1); + /// agent.process_task(embedding, 0.85); + /// ``` + #[wasm_bindgen(js_name = processTask)] + pub fn process_task(&mut self, embedding: Vec, quality: f32) { + self.inner.process_task(embedding, quality); + } + + /// Process task with model route information + /// + /// # Arguments + /// * `embedding` - Query embedding + /// * `quality` - Quality score + /// * `route` - Model route used (e.g., "gpt-4", "claude-3") + #[wasm_bindgen(js_name = processTaskWithRoute)] + pub fn process_task_with_route(&mut self, embedding: Vec, quality: f32, route: &str) { + self.inner.process_task_with_route(embedding, quality, route); + } + + /// Export agent state for coordinator aggregation + /// + /// # Returns + /// JSON object containing agent state, trajectories, and statistics + /// + /// # Example + /// ```javascript + /// const state = agent.export_state(); + /// console.log('Trajectories:', state.trajectories.length); + /// coordinator.aggregate(state); + /// ``` + #[wasm_bindgen(js_name = exportState)] + pub fn export_state(&self) -> JsValue { + let export = self.inner.export_state(); + serde_wasm_bindgen::to_value(&export).unwrap_or(JsValue::NULL) + } + + /// Get agent statistics + /// + /// # Returns + /// JSON object with trajectory count, quality stats, uptime + #[wasm_bindgen(js_name = getStats)] + pub fn get_stats(&self) -> JsValue { + let stats = self.inner.stats(); + serde_wasm_bindgen::to_value(&stats).unwrap_or(JsValue::NULL) + } + + /// Get number of collected trajectories + #[wasm_bindgen(js_name = trajectoryCount)] + pub fn trajectory_count(&self) -> usize { + self.inner.trajectory_count() + } + + /// Get average quality of collected trajectories + #[wasm_bindgen(js_name = averageQuality)] + pub fn average_quality(&self) -> f32 { + self.inner.average_quality() + } + + /// Get agent uptime in seconds + #[wasm_bindgen(js_name = uptimeSeconds)] + pub fn uptime_seconds(&self) -> u64 { + self.inner.uptime_seconds() + } + + /// Clear collected trajectories (after export) + #[wasm_bindgen] + pub fn clear(&mut self) { + self.inner.clear(); + } + + /// Force learning cycle on agent's engine + #[wasm_bindgen(js_name = forceLearn)] + pub fn force_learn(&self) -> String { + self.inner.force_learn() + } + + /// Get learned patterns from agent + #[wasm_bindgen(js_name = getPatterns)] + pub fn get_patterns(&self) -> JsValue { + let patterns = self.inner.get_patterns(); + serde_wasm_bindgen::to_value(&patterns).unwrap_or(JsValue::NULL) + } +} + +/// WASM-compatible Federated Coordinator +/// +/// Central aggregator for federated learning with quality filtering. +/// Coordinates multiple ephemeral agents using star topology. +/// +/// # Example +/// ```javascript +/// const coordinator = new WasmFederatedCoordinator("central"); +/// +/// // Aggregate agent exports +/// const agentState = agent.export_state(); +/// const result = coordinator.aggregate(agentState); +/// +/// // Check stats +/// const stats = coordinator.get_stats(); +/// console.log('Total agents:', stats.total_agents); +/// ``` +#[wasm_bindgen] +pub struct WasmFederatedCoordinator { + inner: RustFederatedCoordinator, +} + +#[wasm_bindgen] +impl WasmFederatedCoordinator { + /// Create a new federated coordinator with default config + /// + /// # Arguments + /// * `coordinator_id` - Unique identifier for this coordinator + /// + /// # Example + /// ```javascript + /// const coordinator = new WasmFederatedCoordinator("central"); + /// ``` + #[wasm_bindgen(constructor)] + pub fn new(coordinator_id: &str) -> Result { + let config = SonaConfig::for_coordinator(); + Ok(Self { + inner: RustFederatedCoordinator::new(coordinator_id, config), + }) + } + + /// Create coordinator with custom configuration + /// + /// # Arguments + /// * `coordinator_id` - Unique identifier + /// * `config` - JSON configuration object + /// + /// # Example + /// ```javascript + /// const config = { + /// hidden_dim: 256, + /// trajectory_capacity: 50000, + /// pattern_clusters: 200, + /// ewc_lambda: 2000.0 + /// }; + /// const coordinator = WasmFederatedCoordinator.with_config("central", config); + /// ``` + #[wasm_bindgen(js_name = withConfig)] + pub fn with_config(coordinator_id: &str, config: JsValue) -> Result { + let config: SonaConfig = serde_wasm_bindgen::from_value(config)?; + Ok(Self { + inner: RustFederatedCoordinator::new(coordinator_id, config), + }) + } + + /// Set quality threshold for accepting trajectories + /// + /// # Arguments + /// * `threshold` - Minimum quality [0.0, 1.0], default 0.4 + #[wasm_bindgen(js_name = setQualityThreshold)] + pub fn set_quality_threshold(&mut self, threshold: f32) { + self.inner.set_quality_threshold(threshold); + } + + /// Aggregate agent export into coordinator + /// + /// # Arguments + /// * `agent_export` - JSON export from agent.export_state() + /// + /// # Returns + /// JSON aggregation result with accepted/rejected counts + /// + /// # Example + /// ```javascript + /// const agentState = agent.export_state(); + /// const result = coordinator.aggregate(agentState); + /// console.log('Accepted:', result.accepted); + /// ``` + #[wasm_bindgen] + pub fn aggregate(&mut self, agent_export: JsValue) -> JsValue { + use crate::training::AgentExport; + + match serde_wasm_bindgen::from_value::(agent_export) { + Ok(export) => { + let result = self.inner.aggregate(export); + serde_wasm_bindgen::to_value(&result).unwrap_or(JsValue::NULL) + } + Err(e) => { + web_sys::console::error_1(&format!("Failed to parse agent export: {:?}", e).into()); + JsValue::NULL + } + } + } + + /// Consolidate learning from all aggregated trajectories + /// + /// Should be called periodically after aggregating multiple agents. + /// + /// # Returns + /// Learning result as JSON string + #[wasm_bindgen] + pub fn consolidate(&self) -> String { + self.inner.consolidate() + } + + /// Get coordinator statistics + /// + /// # Returns + /// JSON object with agent count, trajectory count, quality stats + #[wasm_bindgen(js_name = getStats)] + pub fn get_stats(&self) -> JsValue { + let stats = self.inner.stats(); + serde_wasm_bindgen::to_value(&stats).unwrap_or(JsValue::NULL) + } + + /// Get total number of contributing agents + #[wasm_bindgen(js_name = agentCount)] + pub fn agent_count(&self) -> usize { + self.inner.agent_count() + } + + /// Get total trajectories aggregated + #[wasm_bindgen(js_name = totalTrajectories)] + pub fn total_trajectories(&self) -> usize { + self.inner.total_trajectories() + } + + /// Get all learned patterns from coordinator + #[wasm_bindgen(js_name = getPatterns)] + pub fn get_patterns(&self) -> JsValue { + let patterns = self.inner.get_all_patterns(); + serde_wasm_bindgen::to_value(&patterns).unwrap_or(JsValue::NULL) + } + + /// Find similar patterns to query + /// + /// # Arguments + /// * `query_embedding` - Query vector + /// * `k` - Number of patterns to return + #[wasm_bindgen(js_name = findPatterns)] + pub fn find_patterns(&self, query_embedding: Vec, k: usize) -> JsValue { + let patterns = self.inner.find_patterns(&query_embedding, k); + serde_wasm_bindgen::to_value(&patterns).unwrap_or(JsValue::NULL) + } + + /// Apply coordinator's learned LoRA to input + #[wasm_bindgen(js_name = applyLora)] + pub fn apply_lora(&self, input: Vec) -> Vec { + self.inner.apply_lora(&input) + } + + /// Clear all agent contributions (reset coordinator) + #[wasm_bindgen] + pub fn clear(&mut self) { + self.inner.clear(); + } +} + // Additional helper for serde support #[cfg(feature = "wasm")] mod serde_wasm_bindgen {