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 voi: None,
74 }
75 }
76}
77
78#[wasm_bindgen]
80pub struct Candidate {
81 id: String,
82 embedding: Vec<f32>,
83 metadata: String,
84 created_at: i64,
85 access_count: u64,
86 success_rate: f32,
87}
88
89#[wasm_bindgen]
90impl Candidate {
91 #[wasm_bindgen(constructor)]
92 pub fn new(
93 id: String,
94 embedding: Vec<f32>,
95 metadata: String,
96 created_at: i64,
97 access_count: u64,
98 success_rate: f32,
99 ) -> Self {
100 Self {
101 id,
102 embedding,
103 metadata,
104 created_at,
105 access_count,
106 success_rate,
107 }
108 }
109}
110
111impl TryFrom<Candidate> for CoreCandidate {
112 type Error = JsValue;
113
114 fn try_from(candidate: Candidate) -> Result<Self, Self::Error> {
115 let metadata: HashMap<String, serde_json::Value> =
116 serde_json::from_str(&candidate.metadata)
117 .map_err(|e| JsValue::from_str(&format!("Invalid metadata: {}", e)))?;
118
119 Ok(CoreCandidate {
120 id: candidate.id,
121 embedding: candidate.embedding,
122 metadata,
123 created_at: candidate.created_at,
124 access_count: candidate.access_count,
125 success_rate: candidate.success_rate,
126 })
127 }
128}
129
130#[wasm_bindgen]
132pub struct RoutingRequest {
133 query_embedding: Vec<f32>,
134 candidates: Vec<Candidate>,
135 metadata: Option<String>,
136}
137
138#[wasm_bindgen]
139impl RoutingRequest {
140 #[wasm_bindgen(constructor)]
141 pub fn new(query_embedding: Vec<f32>, candidates: Vec<Candidate>) -> Self {
142 Self {
143 query_embedding,
144 candidates,
145 metadata: None,
146 }
147 }
148
149 #[wasm_bindgen(setter)]
150 pub fn set_metadata(&mut self, metadata: String) {
151 self.metadata = Some(metadata);
152 }
153}
154
155impl TryFrom<RoutingRequest> for CoreRoutingRequest {
156 type Error = JsValue;
157
158 fn try_from(request: RoutingRequest) -> Result<Self, Self::Error> {
159 let candidates: Result<Vec<CoreCandidate>, JsValue> = request
160 .candidates
161 .into_iter()
162 .map(|c| c.try_into())
163 .collect();
164
165 let metadata = if let Some(meta_str) = request.metadata {
166 Some(
167 serde_json::from_str(&meta_str)
168 .map_err(|e| JsValue::from_str(&format!("Invalid metadata: {}", e)))?,
169 )
170 } else {
171 None
172 };
173
174 Ok(CoreRoutingRequest {
175 query_embedding: request.query_embedding,
176 candidates: candidates?,
177 metadata,
178 })
179 }
180}
181
182#[wasm_bindgen]
184pub struct RoutingResponse {
185 decisions_json: String,
186 inference_time_us: u64,
187 candidates_processed: usize,
188 feature_time_us: u64,
189}
190
191#[wasm_bindgen]
192impl RoutingResponse {
193 #[wasm_bindgen(getter)]
194 pub fn decisions_json(&self) -> String {
195 self.decisions_json.clone()
196 }
197
198 #[wasm_bindgen(getter)]
199 pub fn inference_time_us(&self) -> u64 {
200 self.inference_time_us
201 }
202
203 #[wasm_bindgen(getter)]
204 pub fn candidates_processed(&self) -> usize {
205 self.candidates_processed
206 }
207
208 #[wasm_bindgen(getter)]
209 pub fn feature_time_us(&self) -> u64 {
210 self.feature_time_us
211 }
212}
213
214impl From<CoreRoutingResponse> for RoutingResponse {
215 fn from(response: CoreRoutingResponse) -> Self {
216 let decisions_json = serde_json::to_string(&response.decisions).unwrap_or_default();
217
218 Self {
219 decisions_json,
220 inference_time_us: response.inference_time_us,
221 candidates_processed: response.candidates_processed,
222 feature_time_us: response.feature_time_us,
223 }
224 }
225}
226
227#[wasm_bindgen]
229pub struct Router {
230 inner: CoreRouter,
231}
232
233#[wasm_bindgen]
234impl Router {
235 #[wasm_bindgen(constructor)]
237 pub fn new(config: RouterConfig) -> Result<Router, JsValue> {
238 let core_config: CoreRouterConfig = config.into();
239 let router = CoreRouter::new(core_config)
240 .map_err(|e| JsValue::from_str(&format!("Failed to create router: {}", e)))?;
241
242 Ok(Router { inner: router })
243 }
244
245 pub fn route(&self, request: RoutingRequest) -> Result<RoutingResponse, JsValue> {
247 let core_request: CoreRoutingRequest = request.try_into()?;
248 let core_response = self
249 .inner
250 .route(core_request)
251 .map_err(|e| JsValue::from_str(&format!("Routing failed: {}", e)))?;
252
253 Ok(core_response.into())
254 }
255
256 pub fn circuit_breaker_status(&self) -> Option<bool> {
258 self.inner.circuit_breaker_status()
259 }
260}
261
262#[wasm_bindgen]
264pub fn version() -> String {
265 env!("CARGO_PKG_VERSION").to_string()
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_version() {
274 assert!(!version().is_empty());
275 }
276}