//! WASM bindings for MidStream Lean Agentic Learning System //! //! Provides WebSocket, SSE, and HTTP streaming support for browser and Node.js use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::JsFuture; use web_sys::{WebSocket, EventSource, Request, RequestInit, Response}; use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::rc::Rc; use js_sys::{Function, Promise, Uint8Array}; // Re-export types from midstream use midstream::{ TemporalComparator, RealtimeScheduler, SchedulingPolicy, Priority, AttractorAnalyzer, TemporalNeuralSolver, MetaLearner, MetaLevel, ComparisonAlgorithm, TemporalFormula, TemporalTrace, TemporalState, }; #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_namespace = console)] fn log(s: &str); #[wasm_bindgen(js_namespace = console)] fn error(s: &str); } /// Initialize panic hook for better error messages #[wasm_bindgen(start)] pub fn init() { console_error_panic_hook::set_once(); log("MidStream WASM initialized"); } // ============================================================================ // WebSocket Client // ============================================================================ #[wasm_bindgen] pub struct WebSocketClient { socket: WebSocket, on_message: Rc>>, on_error: Rc>>, on_close: Rc>>, } #[wasm_bindgen] impl WebSocketClient { #[wasm_bindgen(constructor)] pub fn new(url: &str) -> Result { let socket = WebSocket::new(url)?; socket.set_binary_type(web_sys::BinaryType::Arraybuffer); Ok(WebSocketClient { socket, on_message: Rc::new(RefCell::new(None)), on_error: Rc::new(RefCell::new(None)), on_close: Rc::new(RefCell::new(None)), }) } pub fn connect(&self) -> Result<(), JsValue> { // Set up message handler let on_message_cb = self.on_message.clone(); let onmessage_callback = Closure::wrap(Box::new(move |e: web_sys::MessageEvent| { if let Some(callback) = on_message_cb.borrow().as_ref() { if let Ok(txt) = e.data().dyn_into::() { let _ = callback.call1(&JsValue::NULL, &txt); } else if let Ok(array) = e.data().dyn_into::() { let uint8_array = Uint8Array::new(&array); let _ = callback.call1(&JsValue::NULL, &uint8_array); } } }) as Box); self.socket.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref())); onmessage_callback.forget(); // Set up error handler let on_error_cb = self.on_error.clone(); let onerror_callback = Closure::wrap(Box::new(move |e: web_sys::ErrorEvent| { if let Some(callback) = on_error_cb.borrow().as_ref() { let _ = callback.call1(&JsValue::NULL, &e.into()); } }) as Box); self.socket.set_onerror(Some(onerror_callback.as_ref().unchecked_ref())); onerror_callback.forget(); // Set up close handler let on_close_cb = self.on_close.clone(); let onclose_callback = Closure::wrap(Box::new(move |e: web_sys::CloseEvent| { if let Some(callback) = on_close_cb.borrow().as_ref() { let _ = callback.call1(&JsValue::NULL, &e.into()); } }) as Box); self.socket.set_onclose(Some(onclose_callback.as_ref().unchecked_ref())); onclose_callback.forget(); Ok(()) } pub fn send(&self, data: &str) -> Result<(), JsValue> { self.socket.send_with_str(data) } pub fn send_bytes(&self, data: &[u8]) -> Result<(), JsValue> { self.socket.send_with_u8_array(data) } pub fn close(&self) -> Result<(), JsValue> { self.socket.close() } pub fn set_on_message(&self, callback: Function) { *self.on_message.borrow_mut() = Some(callback); } pub fn set_on_error(&self, callback: Function) { *self.on_error.borrow_mut() = Some(callback); } pub fn set_on_close(&self, callback: Function) { *self.on_close.borrow_mut() = Some(callback); } pub fn ready_state(&self) -> u16 { self.socket.ready_state() } } // ============================================================================ // SSE Client // ============================================================================ #[wasm_bindgen] pub struct SSEClient { event_source: EventSource, on_message: Rc>>, on_error: Rc>>, } #[wasm_bindgen] impl SSEClient { #[wasm_bindgen(constructor)] pub fn new(url: &str) -> Result { let event_source = EventSource::new(url)?; Ok(SSEClient { event_source, on_message: Rc::new(RefCell::new(None)), on_error: Rc::new(RefCell::new(None)), }) } pub fn connect(&self) -> Result<(), JsValue> { // Set up message handler let on_message_cb = self.on_message.clone(); let onmessage_callback = Closure::wrap(Box::new(move |e: web_sys::MessageEvent| { if let Some(callback) = on_message_cb.borrow().as_ref() { if let Ok(data) = e.data().dyn_into::() { let _ = callback.call1(&JsValue::NULL, &data); } } }) as Box); self.event_source.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref())); onmessage_callback.forget(); // Set up error handler let on_error_cb = self.on_error.clone(); let onerror_callback = Closure::wrap(Box::new(move |e: web_sys::Event| { if let Some(callback) = on_error_cb.borrow().as_ref() { let _ = callback.call1(&JsValue::NULL, &e); } }) as Box); self.event_source.set_onerror(Some(onerror_callback.as_ref().unchecked_ref())); onerror_callback.forget(); Ok(()) } pub fn close(&self) { self.event_source.close(); } pub fn set_on_message(&self, callback: Function) { *self.on_message.borrow_mut() = Some(callback); } pub fn set_on_error(&self, callback: Function) { *self.on_error.borrow_mut() = Some(callback); } pub fn ready_state(&self) -> u16 { self.event_source.ready_state() } } // ============================================================================ // HTTP Streaming Client // ============================================================================ #[wasm_bindgen] pub struct StreamingHTTPClient; #[wasm_bindgen] impl StreamingHTTPClient { #[wasm_bindgen(constructor)] pub fn new() -> StreamingHTTPClient { StreamingHTTPClient } pub async fn stream(&self, url: &str, on_chunk: Function) -> Result<(), JsValue> { let mut opts = RequestInit::new(); opts.method("GET"); let request = Request::new_with_str_and_init(url, &opts)?; let window = web_sys::window().ok_or("No window object")?; let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?; let resp: Response = resp_value.dyn_into()?; if let Some(body) = resp.body() { let reader = body.get_reader(); loop { let result = JsFuture::from(reader.read()).await?; let done = js_sys::Reflect::get(&result, &JsValue::from_str("done"))?; if done.as_bool().unwrap_or(false) { break; } let value = js_sys::Reflect::get(&result, &JsValue::from_str("value"))?; if let Ok(uint8_array) = value.dyn_into::() { let _ = on_chunk.call1(&JsValue::NULL, &uint8_array); } } } Ok(()) } } // ============================================================================ // Lean Agentic System WASM Bindings // ============================================================================ #[wasm_bindgen] pub struct TemporalComparatorWasm { inner: TemporalComparator, } #[wasm_bindgen] impl TemporalComparatorWasm { #[wasm_bindgen(constructor)] pub fn new() -> TemporalComparatorWasm { TemporalComparatorWasm { inner: TemporalComparator::new(), } } pub fn compare(&mut self, seq1: JsValue, seq2: JsValue, algorithm: &str) -> Result { let seq1_vec: Vec = serde_wasm_bindgen::from_value(seq1)?; let seq2_vec: Vec = serde_wasm_bindgen::from_value(seq2)?; let algo = match algorithm { "dtw" | "DTW" => ComparisonAlgorithm::DTW, "lcs" | "LCS" => ComparisonAlgorithm::LCS, "edit" | "EditDistance" => ComparisonAlgorithm::EditDistance, "corr" | "Correlation" => ComparisonAlgorithm::Correlation, _ => return Err(JsValue::from_str("Unknown algorithm")), }; Ok(self.inner.compare(&seq1_vec, &seq2_vec, algo)) } pub fn detect_pattern(&self, sequence: JsValue, pattern: JsValue) -> Result { let seq_vec: Vec = serde_wasm_bindgen::from_value(sequence)?; let pat_vec: Vec = serde_wasm_bindgen::from_value(pattern)?; let positions = self.inner.detect_pattern(&seq_vec, &pat_vec); serde_wasm_bindgen::to_value(&positions).map_err(|e| JsValue::from_str(&e.to_string())) } pub fn cache_stats(&self) -> Result { let stats = self.inner.cache_stats(); serde_wasm_bindgen::to_value(&stats).map_err(|e| JsValue::from_str(&e.to_string())) } } #[wasm_bindgen] pub struct AttractorAnalyzerWasm { inner: AttractorAnalyzer, } #[wasm_bindgen] impl AttractorAnalyzerWasm { #[wasm_bindgen(constructor)] pub fn new(embedding_dim: usize, time_delay: usize) -> AttractorAnalyzerWasm { AttractorAnalyzerWasm { inner: AttractorAnalyzer::new(embedding_dim, time_delay), } } pub fn analyze(&self, data: JsValue) -> Result { let data_vec: Vec = serde_wasm_bindgen::from_value(data)?; let result = self.inner.analyze(&data_vec) .map_err(|e| JsValue::from_str(&e))?; serde_wasm_bindgen::to_value(&result).map_err(|e| JsValue::from_str(&e.to_string())) } } #[wasm_bindgen] pub struct MetaLearnerWasm { inner: MetaLearner, } #[wasm_bindgen] impl MetaLearnerWasm { #[wasm_bindgen(constructor)] pub fn new(max_history: usize) -> MetaLearnerWasm { MetaLearnerWasm { inner: MetaLearner::new(max_history), } } pub fn learn(&mut self, content: &str, reward: f64) { self.inner.learn(content.to_string(), reward); } pub fn ascend(&mut self) -> Result { self.inner.ascend() .map(|level| format!("{:?}", level)) .map_err(|e| JsValue::from_str(&e)) } pub fn descend(&mut self) -> Result { self.inner.descend() .map(|level| format!("{:?}", level)) .map_err(|e| JsValue::from_str(&e)) } pub fn get_summary(&self) -> Result { let summary = self.inner.get_summary(); serde_wasm_bindgen::to_value(&summary).map_err(|e| JsValue::from_str(&e.to_string())) } pub fn current_level(&self) -> String { format!("{:?}", self.inner.current_level()) } } // ============================================================================ // MidStream Agent // ============================================================================ #[derive(Serialize, Deserialize)] pub struct AgentConfig { pub max_history: usize, pub embedding_dim: usize, pub scheduling_policy: String, } impl Default for AgentConfig { fn default() -> Self { Self { max_history: 1000, embedding_dim: 3, scheduling_policy: "EDF".to_string(), } } } #[wasm_bindgen] pub struct MidStreamAgent { temporal: TemporalComparator, attractor: AttractorAnalyzer, meta_learner: MetaLearner, config: AgentConfig, } #[wasm_bindgen] impl MidStreamAgent { #[wasm_bindgen(constructor)] pub fn new(config: JsValue) -> Result { let config: AgentConfig = if config.is_undefined() { AgentConfig::default() } else { serde_wasm_bindgen::from_value(config)? }; Ok(MidStreamAgent { temporal: TemporalComparator::new(), attractor: AttractorAnalyzer::new(config.embedding_dim, 1), meta_learner: MetaLearner::new(config.max_history), config, }) } pub fn process_message(&mut self, message: &str) -> Result { // Simple message processing with learning self.meta_learner.learn(message.to_string(), 0.8); let summary = self.meta_learner.get_summary(); serde_wasm_bindgen::to_value(&summary).map_err(|e| JsValue::from_str(&e.to_string())) } pub fn analyze_conversation(&mut self, messages: JsValue) -> Result { let msg_vec: Vec = serde_wasm_bindgen::from_value(messages)?; let mut result = serde_json::json!({ "message_count": msg_vec.len(), "patterns": [], "meta_learning": self.meta_learner.get_summary(), }); serde_wasm_bindgen::to_value(&result).map_err(|e| JsValue::from_str(&e.to_string())) } pub fn get_status(&self) -> Result { let status = serde_json::json!({ "meta_level": format!("{:?}", self.meta_learner.current_level()), "config": { "max_history": self.config.max_history, "embedding_dim": self.config.embedding_dim, } }); serde_wasm_bindgen::to_value(&status).map_err(|e| JsValue::from_str(&e.to_string())) } } // ============================================================================ // Benchmarking utilities // ============================================================================ #[wasm_bindgen] pub fn benchmark_dtw(size: usize, iterations: usize) -> f64 { let mut comparator = TemporalComparator::::new(); let seq1: Vec = (0..size as i32).collect(); let seq2: Vec = (0..size as i32).map(|x| x + 1).collect(); let start = js_sys::Date::now(); for _ in 0..iterations { comparator.compare(&seq1, &seq2, ComparisonAlgorithm::DTW); } let end = js_sys::Date::now(); (end - start) / iterations as f64 } #[wasm_bindgen] pub fn benchmark_lcs(size: usize, iterations: usize) -> f64 { let mut comparator = TemporalComparator::::new(); let seq1: Vec = (0..size as i32).collect(); let seq2: Vec = (0..size as i32).map(|x| x + 1).collect(); let start = js_sys::Date::now(); for _ in 0..iterations { comparator.compare(&seq1, &seq2, ComparisonAlgorithm::LCS); } let end = js_sys::Date::now(); (end - start) / iterations as f64 } #[wasm_bindgen] pub fn get_version() -> String { env!("CARGO_PKG_VERSION").to_string() }