ruvector_tiny_dancer_wasm/
lib.rs1use ruvector_tiny_dancer_core::{
4 types::{
5 Candidate as CoreCandidate, RouterConfig as CoreRouterConfig,
6 RoutingRequest as CoreRoutingRequest, RoutingResponse as CoreRoutingResponse,
7 },
8 Router as CoreRouter,
9};
10use std::collections::HashMap;
11use wasm_bindgen::prelude::*;
12
13#[wasm_bindgen(start)]
15pub fn init() {
16 #[cfg(feature = "console_error_panic_hook")]
17 console_error_panic_hook::set_once();
18}
19
20#[wasm_bindgen]
22#[derive(Clone)]
23pub struct RouterConfig {
24 model_path: String,
25 confidence_threshold: f32,
26 max_uncertainty: f32,
27 enable_circuit_breaker: bool,
28 circuit_breaker_threshold: u32,
29 enable_quantization: bool,
30}
31
32#[wasm_bindgen]
33impl RouterConfig {
34 #[wasm_bindgen(constructor)]
35 pub fn new() -> Self {
36 Self {
37 model_path: "./models/fastgrnn.safetensors".to_string(),
38 confidence_threshold: 0.85,
39 max_uncertainty: 0.15,
40 enable_circuit_breaker: true,
41 circuit_breaker_threshold: 5,
42 enable_quantization: true,
43 }
44 }
45
46 #[wasm_bindgen(setter)]
47 pub fn set_model_path(&mut self, path: String) {
48 self.model_path = path;
49 }
50
51 #[wasm_bindgen(setter)]
52 pub fn set_confidence_threshold(&mut self, threshold: f32) {
53 self.confidence_threshold = threshold;
54 }
55
56 #[wasm_bindgen(setter)]
57 pub fn set_max_uncertainty(&mut self, uncertainty: f32) {
58 self.max_uncertainty = uncertainty;
59 }
60}
61
62impl From<RouterConfig> for CoreRouterConfig {
63 fn from(config: RouterConfig) -> Self {
64 CoreRouterConfig {
65 model_path: config.model_path,
66 confidence_threshold: config.confidence_threshold,
67 max_uncertainty: config.max_uncertainty,
68 enable_circuit_breaker: config.enable_circuit_breaker,
69 circuit_breaker_threshold: config.circuit_breaker_threshold,
70 enable_quantization: config.enable_quantization,
71 database_path: None,
72 }
73 }
74}
75
76#[wasm_bindgen]
78pub struct Candidate {
79 id: String,
80 embedding: Vec<f32>,
81 metadata: String,
82 created_at: i64,
83 access_count: u64,
84 success_rate: f32,
85}
86
87#[wasm_bindgen]
88impl Candidate {
89 #[wasm_bindgen(constructor)]
90 pub fn new(
91 id: String,
92 embedding: Vec<f32>,
93 metadata: String,
94 created_at: i64,
95 access_count: u64,
96 success_rate: f32,
97 ) -> Self {
98 Self {
99 id,
100 embedding,
101 metadata,
102 created_at,
103 access_count,
104 success_rate,
105 }
106 }
107}
108
109impl TryFrom<Candidate> for CoreCandidate {
110 type Error = JsValue;
111
112 fn try_from(candidate: Candidate) -> Result<Self, Self::Error> {
113 let metadata: HashMap<String, serde_json::Value> =
114 serde_json::from_str(&candidate.metadata)
115 .map_err(|e| JsValue::from_str(&format!("Invalid metadata: {}", e)))?;
116
117 Ok(CoreCandidate {
118 id: candidate.id,
119 embedding: candidate.embedding,
120 metadata,
121 created_at: candidate.created_at,
122 access_count: candidate.access_count,
123 success_rate: candidate.success_rate,
124 })
125 }
126}
127
128#[wasm_bindgen]
130pub struct RoutingRequest {
131 query_embedding: Vec<f32>,
132 candidates: Vec<Candidate>,
133 metadata: Option<String>,
134}
135
136#[wasm_bindgen]
137impl RoutingRequest {
138 #[wasm_bindgen(constructor)]
139 pub fn new(query_embedding: Vec<f32>, candidates: Vec<Candidate>) -> Self {
140 Self {
141 query_embedding,
142 candidates,
143 metadata: None,
144 }
145 }
146
147 #[wasm_bindgen(setter)]
148 pub fn set_metadata(&mut self, metadata: String) {
149 self.metadata = Some(metadata);
150 }
151}
152
153impl TryFrom<RoutingRequest> for CoreRoutingRequest {
154 type Error = JsValue;
155
156 fn try_from(request: RoutingRequest) -> Result<Self, Self::Error> {
157 let candidates: Result<Vec<CoreCandidate>, JsValue> = request
158 .candidates
159 .into_iter()
160 .map(|c| c.try_into())
161 .collect();
162
163 let metadata = if let Some(meta_str) = request.metadata {
164 Some(
165 serde_json::from_str(&meta_str)
166 .map_err(|e| JsValue::from_str(&format!("Invalid metadata: {}", e)))?,
167 )
168 } else {
169 None
170 };
171
172 Ok(CoreRoutingRequest {
173 query_embedding: request.query_embedding,
174 candidates: candidates?,
175 metadata,
176 })
177 }
178}
179
180#[wasm_bindgen]
182pub struct RoutingResponse {
183 decisions_json: String,
184 inference_time_us: u64,
185 candidates_processed: usize,
186 feature_time_us: u64,
187}
188
189#[wasm_bindgen]
190impl RoutingResponse {
191 #[wasm_bindgen(getter)]
192 pub fn decisions_json(&self) -> String {
193 self.decisions_json.clone()
194 }
195
196 #[wasm_bindgen(getter)]
197 pub fn inference_time_us(&self) -> u64 {
198 self.inference_time_us
199 }
200
201 #[wasm_bindgen(getter)]
202 pub fn candidates_processed(&self) -> usize {
203 self.candidates_processed
204 }
205
206 #[wasm_bindgen(getter)]
207 pub fn feature_time_us(&self) -> u64 {
208 self.feature_time_us
209 }
210}
211
212impl From<CoreRoutingResponse> for RoutingResponse {
213 fn from(response: CoreRoutingResponse) -> Self {
214 let decisions_json = serde_json::to_string(&response.decisions).unwrap_or_default();
215
216 Self {
217 decisions_json,
218 inference_time_us: response.inference_time_us,
219 candidates_processed: response.candidates_processed,
220 feature_time_us: response.feature_time_us,
221 }
222 }
223}
224
225#[wasm_bindgen]
227pub struct Router {
228 inner: CoreRouter,
229}
230
231#[wasm_bindgen]
232impl Router {
233 #[wasm_bindgen(constructor)]
235 pub fn new(config: RouterConfig) -> Result<Router, JsValue> {
236 let core_config: CoreRouterConfig = config.into();
237 let router = CoreRouter::new(core_config)
238 .map_err(|e| JsValue::from_str(&format!("Failed to create router: {}", e)))?;
239
240 Ok(Router { inner: router })
241 }
242
243 pub fn route(&self, request: RoutingRequest) -> Result<RoutingResponse, JsValue> {
245 let core_request: CoreRoutingRequest = request.try_into()?;
246 let core_response = self
247 .inner
248 .route(core_request)
249 .map_err(|e| JsValue::from_str(&format!("Routing failed: {}", e)))?;
250
251 Ok(core_response.into())
252 }
253
254 pub fn circuit_breaker_status(&self) -> Option<bool> {
256 self.inner.circuit_breaker_status()
257 }
258}
259
260#[wasm_bindgen]
262pub fn version() -> String {
263 env!("CARGO_PKG_VERSION").to_string()
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_version() {
272 assert!(!version().is_empty());
273 }
274}