//! Ultra-low-latency WASM bindings for Lean Agentic Learning System //! //! Features: //! - WebSocket streaming with minimal overhead //! - SSE (Server-Sent Events) support //! - HTTP streaming //! - Zero-copy message passing where possible //! - Optimized for latency (<1ms overhead) use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use web_sys::{WebSocket, EventSource, MessageEvent, CloseEvent, ErrorEvent}; use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::cell::RefCell; use std::rc::Rc; // Use wee_alloc for smaller binary size #[cfg(feature = "wee_alloc")] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; /// Initialize panic hook for better error messages #[wasm_bindgen(start)] pub fn init() { #[cfg(feature = "console_error_panic_hook")] console_error_panic_hook::set_once(); } /// Configuration for the lean agentic system #[wasm_bindgen] #[derive(Clone, Serialize, Deserialize)] pub struct LeanAgenticConfig { #[wasm_bindgen(skip)] pub enable_formal_verification: bool, #[wasm_bindgen(skip)] pub learning_rate: f64, #[wasm_bindgen(skip)] pub max_planning_depth: usize, #[wasm_bindgen(skip)] pub action_threshold: f64, } #[wasm_bindgen] impl LeanAgenticConfig { #[wasm_bindgen(constructor)] pub fn new() -> Self { Self { enable_formal_verification: true, learning_rate: 0.01, max_planning_depth: 5, action_threshold: 0.7, } } #[wasm_bindgen(getter)] pub fn enable_formal_verification(&self) -> bool { self.enable_formal_verification } #[wasm_bindgen(setter)] pub fn set_enable_formal_verification(&mut self, value: bool) { self.enable_formal_verification = value; } #[wasm_bindgen(getter)] pub fn learning_rate(&self) -> f64 { self.learning_rate } #[wasm_bindgen(setter)] pub fn set_learning_rate(&mut self, value: f64) { self.learning_rate = value; } } impl Default for LeanAgenticConfig { fn default() -> Self { Self::new() } } /// Processing result #[wasm_bindgen] #[derive(Clone, Serialize, Deserialize)] pub struct ProcessingResult { pub action: String, pub reward: f64, pub verified: bool, #[wasm_bindgen(skip)] pub timestamp: f64, } #[wasm_bindgen] impl ProcessingResult { #[wasm_bindgen(getter)] pub fn timestamp(&self) -> f64 { self.timestamp } } /// WebSocket client for ultra-low-latency streaming #[wasm_bindgen] pub struct WebSocketClient { socket: WebSocket, #[wasm_bindgen(skip)] on_message: Rc>>, #[wasm_bindgen(skip)] on_error: Rc>>, #[wasm_bindgen(skip)] on_close: Rc>>, } #[wasm_bindgen] impl WebSocketClient { /// Create a new WebSocket connection #[wasm_bindgen(constructor)] pub fn new(url: &str) -> Result { let socket = WebSocket::new(url)?; // Set binary type for optimal performance socket.set_binary_type(web_sys::BinaryType::Arraybuffer); Ok(Self { socket, on_message: Rc::new(RefCell::new(None)), on_error: Rc::new(RefCell::new(None)), on_close: Rc::new(RefCell::new(None)), }) } /// Set message handler with minimal overhead pub fn set_on_message(&mut self, callback: js_sys::Function) -> Result<(), JsValue> { *self.on_message.borrow_mut() = Some(callback.clone()); let on_message_ref = self.on_message.clone(); let closure = Closure::wrap(Box::new(move |e: MessageEvent| { if let Some(cb) = on_message_ref.borrow().as_ref() { // Zero-copy data access when possible let data = if let Ok(txt) = e.data().dyn_into::() { txt } else if let Ok(array_buffer) = e.data().dyn_into::() { // Convert ArrayBuffer to string let array = js_sys::Uint8Array::new(&array_buffer); let vec = array.to_vec(); JsValue::from_str(&String::from_utf8_lossy(&vec)) } else { e.data() }; let _ = cb.call1(&JsValue::NULL, &data); } }) as Box); self.socket.set_onmessage(Some(closure.as_ref().unchecked_ref())); closure.forget(); Ok(()) } /// Set error handler pub fn set_on_error(&mut self, callback: js_sys::Function) -> Result<(), JsValue> { *self.on_error.borrow_mut() = Some(callback.clone()); let on_error_ref = self.on_error.clone(); let closure = Closure::wrap(Box::new(move |e: ErrorEvent| { if let Some(cb) = on_error_ref.borrow().as_ref() { let _ = cb.call1(&JsValue::NULL, &JsValue::from_str(&e.message())); } }) as Box); self.socket.set_onerror(Some(closure.as_ref().unchecked_ref())); closure.forget(); Ok(()) } /// Set close handler pub fn set_on_close(&mut self, callback: js_sys::Function) -> Result<(), JsValue> { *self.on_close.borrow_mut() = Some(callback); let on_close_ref = self.on_close.clone(); let closure = Closure::wrap(Box::new(move |e: CloseEvent| { if let Some(cb) = on_close_ref.borrow().as_ref() { let _ = cb.call1(&JsValue::NULL, &JsValue::from(e.code())); } }) as Box); self.socket.set_onclose(Some(closure.as_ref().unchecked_ref())); closure.forget(); Ok(()) } /// Send message with minimal overhead pub fn send(&self, message: &str) -> Result<(), JsValue> { self.socket.send_with_str(message) } /// Send binary message pub fn send_binary(&self, data: &[u8]) -> Result<(), JsValue> { self.socket.send_with_u8_array(data) } /// Close connection pub fn close(&self) -> Result<(), JsValue> { self.socket.close() } /// Get ready state pub fn ready_state(&self) -> u16 { self.socket.ready_state() } } /// SSE (Server-Sent Events) client for streaming #[wasm_bindgen] pub struct SSEClient { event_source: EventSource, #[wasm_bindgen(skip)] on_message: Rc>>, } #[wasm_bindgen] impl SSEClient { /// Create new SSE connection #[wasm_bindgen(constructor)] pub fn new(url: &str) -> Result { let event_source = EventSource::new(url)?; Ok(Self { event_source, on_message: Rc::new(RefCell::new(None)), }) } /// Set message handler pub fn set_on_message(&mut self, callback: js_sys::Function) -> Result<(), JsValue> { *self.on_message.borrow_mut() = Some(callback.clone()); let on_message_ref = self.on_message.clone(); let closure = Closure::wrap(Box::new(move |e: MessageEvent| { if let Some(cb) = on_message_ref.borrow().as_ref() { let _ = cb.call1(&JsValue::NULL, &e.data()); } }) as Box); self.event_source.set_onmessage(Some(closure.as_ref().unchecked_ref())); closure.forget(); Ok(()) } /// Close connection pub fn close(&self) { self.event_source.close(); } /// Get ready state pub fn ready_state(&self) -> u16 { self.event_source.ready_state() } } /// HTTP Streaming client using Fetch API with streaming #[wasm_bindgen] pub struct StreamingHTTPClient { url: String, } #[wasm_bindgen] impl StreamingHTTPClient { #[wasm_bindgen(constructor)] pub fn new(url: &str) -> Self { Self { url: url.to_string(), } } /// Start streaming with minimal latency pub async fn stream(&self, callback: js_sys::Function) -> Result<(), JsValue> { let window = web_sys::window().ok_or("No window")?; let mut opts = web_sys::RequestInit::new(); opts.method("GET"); let request = web_sys::Request::new_with_str_and_init(&self.url, &opts)?; let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; let resp: web_sys::Response = resp_value.dyn_into()?; let body = resp.body().ok_or("No body")?; let reader = body.get_reader(); // Read stream chunks loop { let chunk_promise = js_sys::Reflect::get(&reader, &JsValue::from_str("read"))? .dyn_into::()? .call0(&reader)?; let chunk_result = JsFuture::from(js_sys::Promise::from(chunk_promise)).await?; let done = js_sys::Reflect::get(&chunk_result, &JsValue::from_str("done"))? .as_bool() .unwrap_or(false); if done { break; } let value = js_sys::Reflect::get(&chunk_result, &JsValue::from_str("value"))?; if let Ok(array) = value.dyn_into::() { let vec = array.to_vec(); let text = String::from_utf8_lossy(&vec); callback.call1(&JsValue::NULL, &JsValue::from_str(&text))?; } } Ok(()) } } /// High-performance agent client #[wasm_bindgen] pub struct LeanAgenticClient { config: LeanAgenticConfig, session_id: String, #[wasm_bindgen(skip)] message_count: u64, #[wasm_bindgen(skip)] total_latency_ms: f64, } #[wasm_bindgen] impl LeanAgenticClient { #[wasm_bindgen(constructor)] pub fn new(session_id: String, config: Option) -> Self { Self { config: config.unwrap_or_default(), session_id, message_count: 0, total_latency_ms: 0.0, } } /// Process message with minimal latency pub fn process_message(&mut self, message: &str) -> Result { let start = js_sys::Date::now(); // Fast processing logic let action_type = if message.to_lowercase().contains("weather") { "get_weather" } else if message.to_lowercase().contains("learn") || message.to_lowercase().contains("remember") { "update_knowledge" } else { "process_text" }; let reward = 0.8; // Placeholder let result = ProcessingResult { action: action_type.to_string(), reward, verified: self.config.enable_formal_verification, timestamp: js_sys::Date::now(), }; self.message_count += 1; let latency = js_sys::Date::now() - start; self.total_latency_ms += latency; // Serialize to JS serde_wasm_bindgen::to_value(&result).map_err(|e| JsValue::from_str(&e.to_string())) } /// Get average latency in milliseconds pub fn get_avg_latency_ms(&self) -> f64 { if self.message_count == 0 { 0.0 } else { self.total_latency_ms / self.message_count as f64 } } /// Get message count pub fn get_message_count(&self) -> u64 { self.message_count } /// Get session ID pub fn get_session_id(&self) -> String { self.session_id.clone() } } /// Utility: Log to console #[wasm_bindgen] pub fn log(message: &str) { web_sys::console::log_1(&JsValue::from_str(message)); } /// Utility: Get high-resolution timestamp #[wasm_bindgen] pub fn now() -> f64 { js_sys::Date::now() } #[cfg(test)] mod tests { use super::*; use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser); #[wasm_bindgen_test] fn test_config_creation() { let config = LeanAgenticConfig::new(); assert!(config.enable_formal_verification); assert_eq!(config.learning_rate, 0.01); } #[wasm_bindgen_test] fn test_client_creation() { let client = LeanAgenticClient::new("test_session".to_string(), None); assert_eq!(client.get_session_id(), "test_session"); assert_eq!(client.get_message_count(), 0); } #[wasm_bindgen_test] fn test_message_processing() { let mut client = LeanAgenticClient::new("test".to_string(), None); let result = client.process_message("What's the weather?"); assert!(result.is_ok()); } }