1use serde::{Deserialize, Serialize};
7use std::borrow::ToOwned;
8use std::fmt;
9use std::string::{String, ToString};
10use std::vec::Vec;
11use wasm_bindgen::prelude::*;
12
13#[wasm_bindgen]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub enum ErrorCode {
17 E1001 = 1001, E1002 = 1002, E1003 = 1003, E1004 = 1004, E1005 = 1005, E1006 = 1006, E2001 = 2001, E2002 = 2002, E2003 = 2003, E2004 = 2004, E2005 = 2005, E2006 = 2006, E3001 = 3001, E3002 = 3002, E3003 = 3003, E3004 = 3004, E3005 = 3005, E3006 = 3006, E4001 = 4001, E4002 = 4002, E4003 = 4003, E4004 = 4004, E4005 = 4005, E4006 = 4006, E5001 = 5001, E5002 = 5002, E5003 = 5003, E5004 = 5004, E5005 = 5005, E5006 = 5006, E6001 = 6001, E6002 = 6002, E6003 = 6003, E6004 = 6004, E6005 = 6005, E6006 = 6006, E7001 = 7001, E7002 = 7002, E7003 = 7003, E7004 = 7004, E7005 = 7005, E7006 = 7006, E8001 = 8001, E8002 = 8002, E8003 = 8003, E8004 = 8004, E8005 = 8005, E8006 = 8006, E9001 = 9001, E9002 = 9002, E9003 = 9003, E9004 = 9004, E9005 = 9005, E9006 = 9006, }
89
90#[wasm_bindgen]
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93pub enum ErrorSeverity {
94 Fatal = 0,
96 Error = 1,
98 Warning = 2,
100 Info = 3,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, Default)]
106pub struct ErrorContext {
107 pub operation: Option<String>,
108 pub component: Option<String>,
109 pub model_id: Option<String>,
110 pub input_shape: Option<Vec<usize>>,
111 pub device_type: Option<String>,
112 pub memory_usage_mb: Option<f64>,
113 pub additional_info: Option<String>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct TrustformersError {
119 pub code: ErrorCode,
120 pub message: String,
121 pub severity: ErrorSeverity,
122 pub context: ErrorContext,
123 pub timestamp: f64,
124 pub stack_trace: Option<String>,
125 pub recovery_suggestion: Option<String>,
126}
127
128impl TrustformersError {
129 pub fn new(code: ErrorCode, message: &str) -> Self {
131 Self {
132 code,
133 message: message.to_owned(),
134 severity: Self::default_severity(code),
135 context: ErrorContext::default(),
136 timestamp: Self::current_timestamp(),
137 stack_trace: None,
138 recovery_suggestion: Self::default_recovery_suggestion(code),
139 }
140 }
141
142 pub fn with_context(
144 code: ErrorCode,
145 message: &str,
146 severity: ErrorSeverity,
147 context: ErrorContext,
148 ) -> Self {
149 Self {
150 code,
151 message: message.to_owned(),
152 severity,
153 context,
154 timestamp: Self::current_timestamp(),
155 stack_trace: None,
156 recovery_suggestion: Self::default_recovery_suggestion(code),
157 }
158 }
159
160 pub fn with_stack_trace(mut self, stack_trace: String) -> Self {
162 self.stack_trace = Some(stack_trace);
163 self
164 }
165
166 pub fn with_recovery_suggestion(mut self, suggestion: String) -> Self {
168 self.recovery_suggestion = Some(suggestion);
169 self
170 }
171
172 fn default_severity(code: ErrorCode) -> ErrorSeverity {
174 match code {
175 ErrorCode::E4001 | ErrorCode::E4002 | ErrorCode::E8001 | ErrorCode::E8002 => {
177 ErrorSeverity::Fatal
178 },
179
180 ErrorCode::E1001
182 | ErrorCode::E1002
183 | ErrorCode::E2001
184 | ErrorCode::E2002
185 | ErrorCode::E3001
186 | ErrorCode::E3002
187 | ErrorCode::E6001
188 | ErrorCode::E7001 => ErrorSeverity::Error,
189
190 ErrorCode::E1004 | ErrorCode::E2004 | ErrorCode::E3004 | ErrorCode::E4004 => {
192 ErrorSeverity::Warning
193 },
194
195 _ => ErrorSeverity::Error,
197 }
198 }
199
200 fn default_recovery_suggestion(code: ErrorCode) -> Option<String> {
202 let suggestion = match code {
203 ErrorCode::E1001 => "Verify model format is supported and file is not corrupted",
204 ErrorCode::E1002 => "Use a smaller model or enable quantization to reduce memory usage",
205 ErrorCode::E2001 => "Check input tensor shape matches model requirements",
206 ErrorCode::E3001 => "Check device drivers and WebGPU support",
207 ErrorCode::E3002 => "Enable WebGPU in browser settings or fallback to CPU",
208 ErrorCode::E4001 => "Enable quantization or use a smaller model",
209 ErrorCode::E4002 => "Close other applications to free memory",
210 ErrorCode::E5002 => "Check browser compatibility and feature availability",
211 ErrorCode::E6001 => "Check network connectivity and try again",
212 ErrorCode::E7001 => "Clear browser storage or increase storage quota",
213 _ => return None,
214 };
215 Some(suggestion.to_owned())
216 }
217
218 fn current_timestamp() -> f64 {
220 js_sys::Date::now()
221 }
222
223 pub fn user_message(&self) -> String {
225 match self.severity {
226 ErrorSeverity::Fatal => format!(
227 "Fatal Error ({code}): {message}",
228 code = self.code as u32,
229 message = self.message
230 ),
231 ErrorSeverity::Error => format!(
232 "Error ({code}): {message}",
233 code = self.code as u32,
234 message = self.message
235 ),
236 ErrorSeverity::Warning => format!(
237 "Warning ({code}): {message}",
238 code = self.code as u32,
239 message = self.message
240 ),
241 ErrorSeverity::Info => format!(
242 "Info ({code}): {message}",
243 code = self.code as u32,
244 message = self.message
245 ),
246 }
247 }
248
249 pub fn is_recoverable(&self) -> bool {
251 !matches!(self.severity, ErrorSeverity::Fatal)
252 }
253
254 pub fn category(&self) -> &'static str {
256 let code_num = self.code as u32;
257 match code_num {
258 1001..=1999 => "Model",
259 2001..=2999 => "Inference",
260 3001..=3999 => "Device",
261 4001..=4999 => "Memory",
262 5001..=5999 => "Configuration",
263 6001..=6999 => "Network",
264 7001..=7999 => "Storage",
265 8001..=8999 => "WebAssembly",
266 9001..=9999 => "Quantization",
267 _ => "Unknown",
268 }
269 }
270}
271
272impl fmt::Display for TrustformersError {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 write!(f, "{}", self.user_message())
275 }
276}
277
278impl From<TrustformersError> for JsValue {
280 fn from(error: TrustformersError) -> Self {
281 let js_error = js_sys::Error::new(&error.user_message());
283
284 js_sys::Reflect::set(&js_error, &"code".into(), &JsValue::from(error.code as u32)).ok();
286 js_sys::Reflect::set(
287 &js_error,
288 &"severity".into(),
289 &JsValue::from(error.severity as u32),
290 )
291 .ok();
292 js_sys::Reflect::set(
293 &js_error,
294 &"category".into(),
295 &JsValue::from(error.category()),
296 )
297 .ok();
298 js_sys::Reflect::set(
299 &js_error,
300 &"timestamp".into(),
301 &JsValue::from(error.timestamp),
302 )
303 .ok();
304 js_sys::Reflect::set(
305 &js_error,
306 &"isRecoverable".into(),
307 &JsValue::from(error.is_recoverable()),
308 )
309 .ok();
310
311 if let Some(ref suggestion) = error.recovery_suggestion {
312 js_sys::Reflect::set(
313 &js_error,
314 &"recoverySuggestion".into(),
315 &JsValue::from(suggestion),
316 )
317 .ok();
318 }
319
320 if let Some(ref stack) = error.stack_trace {
321 js_sys::Reflect::set(&js_error, &"stackTrace".into(), &JsValue::from(stack)).ok();
322 }
323
324 if let Ok(context_json) = serde_json::to_string(&error.context) {
326 js_sys::Reflect::set(&js_error, &"context".into(), &JsValue::from(context_json)).ok();
327 }
328
329 js_error.into()
330 }
331}
332
333impl From<JsValue> for TrustformersError {
335 fn from(js_value: JsValue) -> Self {
336 let message = if let Some(error) = js_value.dyn_ref::<js_sys::Error>() {
337 error.message().into()
338 } else if js_value.is_string() {
339 js_value.as_string().unwrap_or_else(|| "Unknown error".to_owned())
340 } else {
341 format!("{js_value:?}")
342 };
343
344 TrustformersError::new(ErrorCode::E8006, &message)
345 }
346}
347
348pub struct ErrorBuilder {
350 code: ErrorCode,
351 message: String,
352 severity: Option<ErrorSeverity>,
353 context: ErrorContext,
354}
355
356impl ErrorBuilder {
357 pub fn new(code: ErrorCode, message: &str) -> Self {
358 Self {
359 code,
360 message: message.to_owned(),
361 severity: None,
362 context: ErrorContext::default(),
363 }
364 }
365
366 pub fn severity(mut self, severity: ErrorSeverity) -> Self {
367 self.severity = Some(severity);
368 self
369 }
370
371 pub fn operation(mut self, operation: &str) -> Self {
372 self.context.operation = Some(operation.to_owned());
373 self
374 }
375
376 pub fn component(mut self, component: &str) -> Self {
377 self.context.component = Some(component.to_owned());
378 self
379 }
380
381 pub fn model_id(mut self, model_id: &str) -> Self {
382 self.context.model_id = Some(model_id.to_owned());
383 self
384 }
385
386 pub fn input_shape(mut self, shape: Vec<usize>) -> Self {
387 self.context.input_shape = Some(shape);
388 self
389 }
390
391 pub fn device_type(mut self, device_type: &str) -> Self {
392 self.context.device_type = Some(device_type.to_owned());
393 self
394 }
395
396 pub fn memory_usage_mb(mut self, memory_mb: f64) -> Self {
397 self.context.memory_usage_mb = Some(memory_mb);
398 self
399 }
400
401 pub fn additional_info(mut self, info: &str) -> Self {
402 self.context.additional_info = Some(info.to_owned());
403 self
404 }
405
406 pub fn build(self) -> TrustformersError {
407 if let Some(severity) = self.severity {
408 TrustformersError::with_context(self.code, &self.message, severity, self.context)
409 } else {
410 let mut error = TrustformersError::new(self.code, &self.message);
411 error.context = self.context;
412 error
413 }
414 }
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct ErrorCollection {
420 pub errors: Vec<TrustformersError>,
421 pub has_fatal: bool,
422}
423
424impl ErrorCollection {
425 pub fn new() -> Self {
426 Self {
427 errors: Vec::new(),
428 has_fatal: false,
429 }
430 }
431
432 pub fn add(&mut self, error: TrustformersError) {
433 if matches!(error.severity, ErrorSeverity::Fatal) {
434 self.has_fatal = true;
435 }
436 self.errors.push(error);
437 }
438
439 pub fn is_empty(&self) -> bool {
440 self.errors.is_empty()
441 }
442
443 pub fn len(&self) -> usize {
444 self.errors.len()
445 }
446
447 pub fn has_fatal_errors(&self) -> bool {
448 self.has_fatal
449 }
450
451 pub fn has_errors(&self) -> bool {
452 self.errors
453 .iter()
454 .any(|e| matches!(e.severity, ErrorSeverity::Error | ErrorSeverity::Fatal))
455 }
456
457 pub fn get_fatal_errors(&self) -> Vec<&TrustformersError> {
458 self.errors
459 .iter()
460 .filter(|e| matches!(e.severity, ErrorSeverity::Fatal))
461 .collect()
462 }
463
464 pub fn get_errors(&self) -> Vec<&TrustformersError> {
465 self.errors
466 .iter()
467 .filter(|e| matches!(e.severity, ErrorSeverity::Error | ErrorSeverity::Fatal))
468 .collect()
469 }
470
471 pub fn get_warnings(&self) -> Vec<&TrustformersError> {
472 self.errors
473 .iter()
474 .filter(|e| matches!(e.severity, ErrorSeverity::Warning))
475 .collect()
476 }
477
478 pub fn summary(&self) -> String {
480 if self.is_empty() {
481 return "No errors".to_owned();
482 }
483
484 let fatal_count = self.get_fatal_errors().len();
485 let error_count = self.get_errors().len() - fatal_count;
486 let warning_count = self.get_warnings().len();
487
488 let mut parts = Vec::new();
489 if fatal_count > 0 {
490 parts.push(format!("{fatal_count} fatal error(s)"));
491 }
492 if error_count > 0 {
493 parts.push(format!("{error_count} error(s)"));
494 }
495 if warning_count > 0 {
496 parts.push(format!("{warning_count} warning(s)"));
497 }
498
499 parts.join(", ")
500 }
501}
502
503impl Default for ErrorCollection {
504 fn default() -> Self {
505 Self::new()
506 }
507}
508
509pub type TrustformersResult<T> = Result<T, TrustformersError>;
511
512#[macro_export]
514macro_rules! error {
515 ($code:expr, $msg:expr) => {
516 $crate::error::TrustformersError::new($code, $msg)
517 };
518 ($code:expr, $fmt:expr, $($arg:tt)*) => {
519 $crate::error::TrustformersError::new($code, &format!($fmt, $($arg)*))
520 };
521}
522
523#[macro_export]
524macro_rules! error_builder {
525 ($code:expr, $msg:expr) => {
526 $crate::error::ErrorBuilder::new($code, $msg)
527 };
528}
529
530#[wasm_bindgen]
532pub struct ErrorHandler {
533 collection: ErrorCollection,
534}
535
536#[wasm_bindgen]
537impl ErrorHandler {
538 #[wasm_bindgen(constructor)]
539 pub fn new() -> Self {
540 Self {
541 collection: ErrorCollection::new(),
542 }
543 }
544
545 pub fn add_error(&mut self, code: u32, message: &str, severity: u32) {
547 if let (Ok(error_code), Ok(error_severity)) =
548 (Self::code_from_u32(code), Self::severity_from_u32(severity))
549 {
550 let error = TrustformersError::new(error_code, message);
551 let mut error_with_severity = error;
552 error_with_severity.severity = error_severity;
553 self.collection.add(error_with_severity);
554 }
555 }
556
557 pub fn has_fatal_errors(&self) -> bool {
559 self.collection.has_fatal_errors()
560 }
561
562 pub fn has_errors(&self) -> bool {
564 self.collection.has_errors()
565 }
566
567 pub fn error_count(&self) -> usize {
569 self.collection.len()
570 }
571
572 pub fn summary(&self) -> String {
574 self.collection.summary()
575 }
576
577 pub fn clear(&mut self) {
579 self.collection = ErrorCollection::new();
580 }
581
582 pub fn get_errors_json(&self) -> Result<String, JsValue> {
584 serde_json::to_string(&self.collection.errors)
585 .map_err(|e| JsValue::from_str(&e.to_string()))
586 }
587
588 fn code_from_u32(code: u32) -> Result<ErrorCode, ()> {
589 match code {
590 1001 => Ok(ErrorCode::E1001),
591 1002 => Ok(ErrorCode::E1002),
592 1003 => Ok(ErrorCode::E1003),
593 1004 => Ok(ErrorCode::E1004),
594 1005 => Ok(ErrorCode::E1005),
595 1006 => Ok(ErrorCode::E1006),
596 2001 => Ok(ErrorCode::E2001),
597 2002 => Ok(ErrorCode::E2002),
598 2003 => Ok(ErrorCode::E2003),
599 2004 => Ok(ErrorCode::E2004),
600 2005 => Ok(ErrorCode::E2005),
601 2006 => Ok(ErrorCode::E2006),
602 3001 => Ok(ErrorCode::E3001),
603 3002 => Ok(ErrorCode::E3002),
604 3003 => Ok(ErrorCode::E3003),
605 3004 => Ok(ErrorCode::E3004),
606 3005 => Ok(ErrorCode::E3005),
607 3006 => Ok(ErrorCode::E3006),
608 4001 => Ok(ErrorCode::E4001),
609 4002 => Ok(ErrorCode::E4002),
610 4003 => Ok(ErrorCode::E4003),
611 4004 => Ok(ErrorCode::E4004),
612 4005 => Ok(ErrorCode::E4005),
613 4006 => Ok(ErrorCode::E4006),
614 5001 => Ok(ErrorCode::E5001),
615 5002 => Ok(ErrorCode::E5002),
616 5003 => Ok(ErrorCode::E5003),
617 5004 => Ok(ErrorCode::E5004),
618 5005 => Ok(ErrorCode::E5005),
619 5006 => Ok(ErrorCode::E5006),
620 6001 => Ok(ErrorCode::E6001),
621 6002 => Ok(ErrorCode::E6002),
622 6003 => Ok(ErrorCode::E6003),
623 6004 => Ok(ErrorCode::E6004),
624 6005 => Ok(ErrorCode::E6005),
625 6006 => Ok(ErrorCode::E6006),
626 7001 => Ok(ErrorCode::E7001),
627 7002 => Ok(ErrorCode::E7002),
628 7003 => Ok(ErrorCode::E7003),
629 7004 => Ok(ErrorCode::E7004),
630 7005 => Ok(ErrorCode::E7005),
631 7006 => Ok(ErrorCode::E7006),
632 8001 => Ok(ErrorCode::E8001),
633 8002 => Ok(ErrorCode::E8002),
634 8003 => Ok(ErrorCode::E8003),
635 8004 => Ok(ErrorCode::E8004),
636 8005 => Ok(ErrorCode::E8005),
637 8006 => Ok(ErrorCode::E8006),
638 9001 => Ok(ErrorCode::E9001),
639 9002 => Ok(ErrorCode::E9002),
640 9003 => Ok(ErrorCode::E9003),
641 9004 => Ok(ErrorCode::E9004),
642 9005 => Ok(ErrorCode::E9005),
643 9006 => Ok(ErrorCode::E9006),
644 _ => Err(()),
645 }
646 }
647
648 fn severity_from_u32(severity: u32) -> Result<ErrorSeverity, ()> {
649 match severity {
650 0 => Ok(ErrorSeverity::Fatal),
651 1 => Ok(ErrorSeverity::Error),
652 2 => Ok(ErrorSeverity::Warning),
653 3 => Ok(ErrorSeverity::Info),
654 _ => Err(()),
655 }
656 }
657}
658
659impl Default for ErrorHandler {
660 fn default() -> Self {
661 Self::new()
662 }
663}
664
665#[derive(Debug, Clone)]
668pub struct CircuitBreaker {
669 failure_threshold: u32,
670 recovery_timeout_ms: u64,
671 half_open_max_calls: u32,
672 failure_count: u32,
673 state: CircuitState,
674 last_failure_time: Option<f64>,
675 success_count: u32,
676}
677
678#[derive(Debug, Clone, Copy, PartialEq)]
679pub enum CircuitState {
680 Closed, Open, HalfOpen, }
684
685impl CircuitBreaker {
686 pub fn new(failure_threshold: u32, recovery_timeout_ms: u64, half_open_max_calls: u32) -> Self {
687 Self {
688 failure_threshold,
689 recovery_timeout_ms,
690 half_open_max_calls,
691 failure_count: 0,
692 state: CircuitState::Closed,
693 last_failure_time: None,
694 success_count: 0,
695 }
696 }
697
698 pub fn execute<F, T, E>(&mut self, operation: F) -> Result<T, CircuitBreakerError<E>>
700 where
701 F: FnOnce() -> Result<T, E>,
702 {
703 if !self.can_execute() {
704 return Err(CircuitBreakerError::CircuitOpen);
705 }
706
707 match operation() {
708 Ok(result) => {
709 self.on_success();
710 Ok(result)
711 },
712 Err(error) => {
713 self.on_failure();
714 Err(CircuitBreakerError::OperationFailed(error))
715 },
716 }
717 }
718
719 fn can_execute(&mut self) -> bool {
720 let current_time = Self::current_time();
721
722 match self.state {
723 CircuitState::Closed => true,
724 CircuitState::Open => {
725 if let Some(last_failure) = self.last_failure_time {
726 if current_time - last_failure >= self.recovery_timeout_ms as f64 {
727 self.transition_to_half_open();
728 true
729 } else {
730 false
731 }
732 } else {
733 false
734 }
735 },
736 CircuitState::HalfOpen => self.success_count < self.half_open_max_calls,
737 }
738 }
739
740 fn on_success(&mut self) {
741 match self.state {
742 CircuitState::HalfOpen => {
743 self.success_count += 1;
744 if self.success_count >= self.half_open_max_calls {
745 self.transition_to_closed();
746 }
747 },
748 _ => {
749 if self.failure_count > 0 {
751 self.failure_count = 0;
752 }
753 },
754 }
755 }
756
757 fn on_failure(&mut self) {
758 self.failure_count += 1;
759 self.last_failure_time = Some(Self::current_time());
760
761 match self.state {
762 CircuitState::Closed => {
763 if self.failure_count >= self.failure_threshold {
764 self.transition_to_open();
765 }
766 },
767 CircuitState::HalfOpen => {
768 self.transition_to_open();
769 },
770 CircuitState::Open => {
771 },
773 }
774 }
775
776 fn transition_to_open(&mut self) {
777 self.state = CircuitState::Open;
778 self.success_count = 0;
779 }
780
781 fn transition_to_half_open(&mut self) {
782 self.state = CircuitState::HalfOpen;
783 self.success_count = 0;
784 }
785
786 fn transition_to_closed(&mut self) {
787 self.state = CircuitState::Closed;
788 self.failure_count = 0;
789 self.success_count = 0;
790 self.last_failure_time = None;
791 }
792
793 fn current_time() -> f64 {
794 js_sys::Date::now()
795 }
796
797 pub fn get_state(&self) -> CircuitState {
798 self.state
799 }
800
801 pub fn get_failure_count(&self) -> u32 {
802 self.failure_count
803 }
804}
805
806#[derive(Debug, Clone)]
807pub enum CircuitBreakerError<E> {
808 CircuitOpen,
809 OperationFailed(E),
810}
811
812#[derive(Debug, Clone)]
815pub struct RetryStrategy {
816 max_attempts: u32,
817 base_delay_ms: u64,
818 max_delay_ms: u64,
819 backoff_multiplier: f64,
820 jitter_factor: f64,
821 retryable_errors: Vec<ErrorCode>,
822}
823
824impl RetryStrategy {
825 pub fn new() -> Self {
826 Self {
827 max_attempts: 3,
828 base_delay_ms: 100,
829 max_delay_ms: 30000,
830 backoff_multiplier: 2.0,
831 jitter_factor: 0.1,
832 retryable_errors: vec![
833 ErrorCode::E6001, ErrorCode::E6003, ErrorCode::E3004, ErrorCode::E8006, ErrorCode::E7005, ],
839 }
840 }
841
842 pub fn with_max_attempts(mut self, attempts: u32) -> Self {
843 self.max_attempts = attempts;
844 self
845 }
846
847 pub fn with_base_delay(mut self, delay_ms: u64) -> Self {
848 self.base_delay_ms = delay_ms;
849 self
850 }
851
852 pub fn with_retryable_errors(mut self, errors: Vec<ErrorCode>) -> Self {
853 self.retryable_errors = errors;
854 self
855 }
856
857 pub async fn execute_with_retry<F, T, Fut>(&self, operation: F) -> Result<T, TrustformersError>
859 where
860 F: Fn() -> Fut,
861 Fut: std::future::Future<Output = Result<T, TrustformersError>>,
862 {
863 let mut last_error: Option<TrustformersError> = None;
864
865 for attempt in 1..=self.max_attempts {
866 match operation().await {
867 Ok(result) => return Ok(result),
868 Err(error) => {
869 if !self.is_retryable(&error) || attempt == self.max_attempts {
870 return Err(error);
871 }
872
873 let delay = self.calculate_delay(attempt);
874 self.sleep(delay).await;
875 last_error = Some(error);
876 },
877 }
878 }
879
880 Err(last_error
881 .unwrap_or_else(|| TrustformersError::new(ErrorCode::E2002, "Retry strategy failed")))
882 }
883
884 fn is_retryable(&self, error: &TrustformersError) -> bool {
885 self.retryable_errors.contains(&error.code)
886 }
887
888 fn calculate_delay(&self, attempt: u32) -> u64 {
889 let exponential_delay =
890 (self.base_delay_ms as f64 * self.backoff_multiplier.powi(attempt as i32 - 1)) as u64;
891
892 let capped_delay = exponential_delay.min(self.max_delay_ms);
893
894 let jitter = (capped_delay as f64 * self.jitter_factor * self.random()) as u64;
896 capped_delay + jitter
897 }
898
899 fn random(&self) -> f64 {
900 js_sys::Math::random()
901 }
902
903 async fn sleep(&self, ms: u64) {
904 let promise = js_sys::Promise::new(&mut |resolve, _| {
905 web_sys::window()
906 .expect("window should be available in browser context")
907 .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, ms as i32)
908 .expect("set_timeout should succeed with valid callback");
909 });
910 wasm_bindgen_futures::JsFuture::from(promise)
911 .await
912 .expect("sleep promise should resolve successfully");
913 }
914}
915
916impl Default for RetryStrategy {
917 fn default() -> Self {
918 Self::new()
919 }
920}
921
922#[derive(Debug, Clone)]
925pub struct ErrorRecoverySystem {
926 circuit_breaker: CircuitBreaker,
927 retry_strategy: RetryStrategy,
928 recovery_strategies: std::collections::HashMap<ErrorCode, RecoveryAction>,
929 error_history: Vec<TrustformersError>,
930 max_history_size: usize,
931}
932
933#[derive(Debug, Clone)]
934pub enum RecoveryAction {
935 Retry,
936 FallbackToCPU,
937 ClearCache,
938 ReduceMemoryUsage,
939 ReloadModel,
940 RestartWebGL,
941 SwitchToMinimalMode,
942 NoRecovery,
943}
944
945impl ErrorRecoverySystem {
946 pub fn new() -> Self {
947 let mut recovery_strategies = std::collections::HashMap::new();
948
949 recovery_strategies.insert(ErrorCode::E3001, RecoveryAction::FallbackToCPU);
951 recovery_strategies.insert(ErrorCode::E3002, RecoveryAction::FallbackToCPU);
952 recovery_strategies.insert(ErrorCode::E4001, RecoveryAction::ClearCache);
953 recovery_strategies.insert(ErrorCode::E4002, RecoveryAction::ReduceMemoryUsage);
954 recovery_strategies.insert(ErrorCode::E1004, RecoveryAction::ReloadModel);
955 recovery_strategies.insert(ErrorCode::E6001, RecoveryAction::Retry);
956 recovery_strategies.insert(ErrorCode::E7001, RecoveryAction::ClearCache);
957 recovery_strategies.insert(ErrorCode::E8001, RecoveryAction::RestartWebGL);
958
959 Self {
960 circuit_breaker: CircuitBreaker::new(5, 60000, 3),
961 retry_strategy: RetryStrategy::new(),
962 recovery_strategies,
963 error_history: Vec::new(),
964 max_history_size: 100,
965 }
966 }
967
968 #[allow(clippy::result_large_err)]
970 pub async fn execute_with_recovery<F, T, Fut>(
971 &mut self,
972 operation: F,
973 ) -> Result<T, TrustformersError>
974 where
975 F: Fn() -> Fut + Clone,
976 Fut: std::future::Future<Output = Result<T, TrustformersError>>,
977 {
978 let circuit_breaker = &mut self.circuit_breaker;
979 let operation_clone = operation.clone();
980
981 #[allow(clippy::result_large_err)]
982 match circuit_breaker.execute(|| -> Result<(), TrustformersError> {
983 Ok(())
986 }) {
987 Ok(_) => match self.retry_strategy.execute_with_retry(operation).await {
988 Ok(result) => Ok(result),
989 Err(error) => {
990 self.record_error(error.clone());
991 self.attempt_recovery(error, operation_clone).await
992 },
993 },
994 Err(CircuitBreakerError::CircuitOpen) => Err(TrustformersError::new(
995 ErrorCode::E2004,
996 "Circuit breaker is open - system in failure state",
997 )),
998 Err(CircuitBreakerError::OperationFailed(_)) => {
999 self.retry_strategy.execute_with_retry(operation).await
1000 },
1001 }
1002 }
1003
1004 async fn attempt_recovery<F, T, Fut>(
1005 &mut self,
1006 error: TrustformersError,
1007 operation: F,
1008 ) -> Result<T, TrustformersError>
1009 where
1010 F: Fn() -> Fut,
1011 Fut: std::future::Future<Output = Result<T, TrustformersError>>,
1012 {
1013 if let Some(recovery_action) = self.recovery_strategies.get(&error.code).cloned() {
1014 match self.execute_recovery_action(recovery_action).await {
1015 Ok(_) => {
1016 match operation().await {
1018 Ok(result) => Ok(result),
1019 Err(retry_error) => {
1020 self.record_error(retry_error.clone());
1021 Err(retry_error)
1022 },
1023 }
1024 },
1025 Err(recovery_error) => {
1026 let mut enhanced_error = error;
1028 enhanced_error.recovery_suggestion =
1029 Some(format!("Recovery action failed: {:?}", recovery_error));
1030 Err(enhanced_error)
1031 },
1032 }
1033 } else {
1034 self.apply_intelligent_fallback(error, operation).await
1036 }
1037 }
1038
1039 async fn execute_recovery_action(&self, action: RecoveryAction) -> Result<(), String> {
1040 match action {
1041 RecoveryAction::FallbackToCPU => {
1042 Ok(())
1044 },
1045 RecoveryAction::ClearCache => {
1046 Ok(())
1049 },
1050 RecoveryAction::ReduceMemoryUsage => {
1051 if let Some(window) = web_sys::window() {
1053 if let Some(performance) = window.performance() {
1054 if js_sys::Reflect::has(&performance, &"gc".into()).unwrap_or(false) {
1056 if let Ok(gc_fn) = js_sys::Reflect::get(&performance, &"gc".into()) {
1057 if let Ok(gc_fn) = gc_fn.dyn_into::<js_sys::Function>() {
1058 let _ = gc_fn.call0(&performance);
1059 }
1060 }
1061 }
1062 }
1063 }
1064 Ok(())
1065 },
1066 RecoveryAction::ReloadModel => {
1067 Ok(())
1069 },
1070 RecoveryAction::RestartWebGL => {
1071 Ok(())
1073 },
1074 RecoveryAction::SwitchToMinimalMode => {
1075 Ok(())
1077 },
1078 RecoveryAction::Retry => {
1079 Ok(())
1081 },
1082 RecoveryAction::NoRecovery => Err("No recovery action available".to_string()),
1083 }
1084 }
1085
1086 async fn apply_intelligent_fallback<F, T, Fut>(
1087 &self,
1088 error: TrustformersError,
1089 operation: F,
1090 ) -> Result<T, TrustformersError>
1091 where
1092 F: Fn() -> Fut,
1093 Fut: std::future::Future<Output = Result<T, TrustformersError>>,
1094 {
1095 let error_pattern = self.analyze_error_patterns(&error);
1097
1098 match error_pattern {
1099 ErrorPattern::MemoryRelated => {
1100 self.execute_recovery_action(RecoveryAction::ReduceMemoryUsage).await.ok();
1102 operation().await
1103 },
1104 ErrorPattern::DeviceRelated => {
1105 self.execute_recovery_action(RecoveryAction::FallbackToCPU).await.ok();
1107 operation().await
1108 },
1109 ErrorPattern::NetworkRelated => {
1110 operation().await
1112 },
1113 ErrorPattern::Unknown => Err(error),
1114 }
1115 }
1116
1117 fn analyze_error_patterns(&self, current_error: &TrustformersError) -> ErrorPattern {
1118 let memory_errors = [ErrorCode::E4001, ErrorCode::E4002, ErrorCode::E4003];
1120 let device_errors = [
1121 ErrorCode::E3001,
1122 ErrorCode::E3002,
1123 ErrorCode::E3003,
1124 ErrorCode::E3004,
1125 ];
1126 let network_errors = [ErrorCode::E6001, ErrorCode::E6002, ErrorCode::E6003];
1127
1128 if memory_errors.contains(¤t_error.code) {
1129 ErrorPattern::MemoryRelated
1130 } else if device_errors.contains(¤t_error.code) {
1131 ErrorPattern::DeviceRelated
1132 } else if network_errors.contains(¤t_error.code) {
1133 ErrorPattern::NetworkRelated
1134 } else {
1135 ErrorPattern::Unknown
1136 }
1137 }
1138
1139 fn record_error(&mut self, error: TrustformersError) {
1140 self.error_history.push(error);
1141 if self.error_history.len() > self.max_history_size {
1142 self.error_history.drain(0..self.error_history.len() - self.max_history_size);
1143 }
1144 }
1145
1146 pub fn get_error_statistics(&self) -> ErrorStatistics {
1147 let total_errors = self.error_history.len();
1148 let mut error_counts = std::collections::HashMap::new();
1149
1150 for error in &self.error_history {
1151 *error_counts.entry(error.code).or_insert(0) += 1;
1152 }
1153
1154 let most_common_error = error_counts
1155 .iter()
1156 .max_by_key(|(_, count)| *count)
1157 .map(|(code, count)| (*code, *count));
1158
1159 ErrorStatistics {
1160 total_errors,
1161 error_counts,
1162 most_common_error,
1163 circuit_breaker_state: self.circuit_breaker.get_state(),
1164 }
1165 }
1166}
1167
1168#[derive(Debug, Clone, Copy)]
1169enum ErrorPattern {
1170 MemoryRelated,
1171 DeviceRelated,
1172 NetworkRelated,
1173 Unknown,
1174}
1175
1176#[derive(Debug, Clone)]
1177pub struct ErrorStatistics {
1178 pub total_errors: usize,
1179 pub error_counts: std::collections::HashMap<ErrorCode, u32>,
1180 pub most_common_error: Option<(ErrorCode, u32)>,
1181 pub circuit_breaker_state: CircuitState,
1182}
1183
1184impl Default for ErrorRecoverySystem {
1185 fn default() -> Self {
1186 Self::new()
1187 }
1188}
1189
1190#[cfg(test)]
1191mod tests {
1192 use super::*;
1193
1194 #[test]
1195 #[cfg(target_arch = "wasm32")]
1196 fn test_error_creation() {
1197 let error = TrustformersError::new(ErrorCode::E1001, "Test error");
1198 assert_eq!(error.code, ErrorCode::E1001);
1199 assert_eq!(error.message, "Test error");
1200 assert_eq!(error.severity, ErrorSeverity::Error);
1201 }
1202
1203 #[test]
1204 #[cfg(target_arch = "wasm32")]
1205 fn test_error_builder() {
1206 let error = ErrorBuilder::new(ErrorCode::E2001, "Shape mismatch")
1207 .operation("predict")
1208 .component("inference_session")
1209 .input_shape(vec![1, 2, 3])
1210 .build();
1211
1212 assert_eq!(error.code, ErrorCode::E2001);
1213 assert_eq!(error.context.operation, Some("predict".to_owned()));
1214 assert_eq!(error.context.input_shape, Some(vec![1, 2, 3]));
1215 }
1216
1217 #[test]
1218 #[cfg(target_arch = "wasm32")]
1219 fn test_error_collection() {
1220 let mut collection = ErrorCollection::new();
1221
1222 collection.add(TrustformersError::new(ErrorCode::E1001, "Error 1"));
1223 collection.add(TrustformersError::new(ErrorCode::E4001, "Fatal error"));
1224
1225 assert_eq!(collection.len(), 2);
1226 assert!(collection.has_errors());
1227 assert!(collection.has_fatal_errors());
1228 }
1229
1230 #[test]
1231 #[cfg(target_arch = "wasm32")]
1232 fn test_error_category() {
1233 let error = TrustformersError::new(ErrorCode::E1001, "Model error");
1234 assert_eq!(error.category(), "Model");
1235
1236 let error = TrustformersError::new(ErrorCode::E3001, "Device error");
1237 assert_eq!(error.category(), "Device");
1238 }
1239
1240 #[test]
1241 #[cfg(not(target_arch = "wasm32"))]
1242 fn test_error_codes() {
1243 assert_eq!(ErrorCode::E1001 as u32, 1001);
1245 assert_eq!(ErrorCode::E2001 as u32, 2001);
1246 assert_eq!(ErrorCode::E3001 as u32, 3001);
1247 assert_eq!(ErrorCode::E4001 as u32, 4001);
1248 }
1249
1250 #[test]
1251 #[cfg(not(target_arch = "wasm32"))]
1252 fn test_error_severity() {
1253 use ErrorSeverity::*;
1255 assert_ne!(Info, Error);
1256 assert_ne!(Warning, Fatal);
1257 }
1258}