//! Error types for RTX-NLG operations use std::fmt; /// Result type for RTX-NLG operations pub type Result = std::result::Result; /// Errors that can occur during NLG operations #[derive(thiserror::Error, Debug)] pub enum NlgError { /// Model-related errors #[error("Model not found: {0}")] ModelNotFound(String), #[error("Model loading failed: {0}")] ModelLoadError(String), #[error("Model inference failed: {0}")] InferenceError(String), /// Generation errors #[error("Generation failed: {0}")] GenerationError(String), #[error("Invalid generation configuration: {0}")] InvalidConfig(String), #[error("Generation timeout after {timeout_ms}ms")] GenerationTimeout { timeout_ms: u64 }, /// Tokenization errors #[error("Tokenization failed: {0}")] TokenizationError(String), #[error("Invalid token sequence")] InvalidTokenSequence, /// Quality control errors #[error("Content filtered by quality control: {reason}")] ContentFiltered { reason: String }, #[error("Toxicity detected: {score}")] ToxicityDetected { score: f32 }, #[error("Factuality check failed: {0}")] FactualityError(String), /// Resource errors #[error("Out of memory during generation")] OutOfMemory, #[error("Device error: {0}")] DeviceError(String), #[error("Cache error: {0}")] CacheError(String), /// I/O and serialization errors #[error("I/O error: {0}")] IoError(#[from] std::io::Error), #[error("Serialization error: {0}")] SerializationError(#[from] serde_json::Error), /// Tensor operations #[error("Tensor operation failed: {0}")] TensorError(String), /// Configuration errors #[error("Invalid parameter: {param} = {value}")] InvalidParameter { param: String, value: String }, #[error("Missing required parameter: {0}")] MissingParameter(String), /// Network and serving errors #[error("Network error: {0}")] NetworkError(String), #[error("Server overloaded: {current_load}/{max_load}")] ServerOverloaded { current_load: usize, max_load: usize, }, #[error("Request timeout")] RequestTimeout, /// Translation-specific errors #[error("Translation failed: {0}")] TranslationError(String), #[error("Unsupported language pair: {source_lang} to {target_lang}")] UnsupportedLanguagePair { source_lang: String, target_lang: String, }, /// Summarization-specific errors #[error("Summarization failed: {0}")] SummarizationError(String), #[error("Document too short for summarization")] DocumentTooShort, #[error("Document too long: {length} > {max_length}")] DocumentTooLong { length: usize, max_length: usize }, /// Question answering errors #[error("Question answering failed: {0}")] QuestionAnsweringError(String), #[error("No answer found for question")] NoAnswerFound, #[error("Context too short for answering")] ContextTooShort, /// Dialogue system errors #[error("Dialogue error: {0}")] DialogueError(String), #[error("Conversation context too long")] ConversationTooLong, #[error("Invalid conversation state")] InvalidConversationState, /// Optimization errors #[error("KV cache error: {0}")] KvCacheError(String), #[error("Speculative decoding failed: {0}")] SpeculativeDecodingError(String), #[error("Model parallelism error: {0}")] ModelParallelismError(String), /// Template and batch processing errors #[error("Template validation failed for field '{field}': {message}")] ValidationFailed { field: String, message: String }, #[error("Invalid pattern '{pattern}': {error}")] InvalidPattern { pattern: String, error: String }, #[error("Batch queue is full")] BatchQueueFull, #[error("Batch request timeout")] BatchTimeout, #[error("Batch request was cancelled")] BatchRequestCancelled, /// Generic error for wrapping other error types #[error("Internal error: {0}")] Internal(#[from] anyhow::Error), } impl NlgError { /// Create a model not found error pub fn model_not_found(model_id: impl Into) -> Self { Self::ModelNotFound(model_id.into()) } /// Create a generation error pub fn generation_error(msg: impl Into) -> Self { Self::GenerationError(msg.into()) } /// Create an invalid configuration error pub fn invalid_config(msg: impl Into) -> Self { Self::InvalidConfig(msg.into()) } /// Create a tokenization error pub fn tokenization_error(msg: impl Into) -> Self { Self::TokenizationError(msg.into()) } /// Create a content filtered error pub fn content_filtered(reason: impl Into) -> Self { Self::ContentFiltered { reason: reason.into(), } } /// Create a toxicity detected error pub fn toxicity_detected(score: f32) -> Self { Self::ToxicityDetected { score } } /// Create a tensor error pub fn tensor_error(msg: impl Into) -> Self { Self::TensorError(msg.into()) } /// Create an invalid parameter error pub fn invalid_parameter(param: impl Into, value: impl Into) -> Self { Self::InvalidParameter { param: param.into(), value: value.into(), } } /// Create a missing parameter error pub fn missing_parameter(param: impl Into) -> Self { Self::MissingParameter(param.into()) } /// Create a server overloaded error pub fn server_overloaded(current_load: usize, max_load: usize) -> Self { Self::ServerOverloaded { current_load, max_load, } } /// Create a translation error pub fn translation_error(msg: impl Into) -> Self { Self::TranslationError(msg.into()) } /// Create an unsupported language pair error pub fn unsupported_language_pair(source: impl Into, target: impl Into) -> Self { Self::UnsupportedLanguagePair { source_lang: source.into(), target_lang: target.into(), } } /// Create a summarization error pub fn summarization_error(msg: impl Into) -> Self { Self::SummarizationError(msg.into()) } /// Create a document too long error pub fn document_too_long(length: usize, max_length: usize) -> Self { Self::DocumentTooLong { length, max_length } } /// Create a question answering error pub fn question_answering_error(msg: impl Into) -> Self { Self::QuestionAnsweringError(msg.into()) } /// Create a dialogue error pub fn dialogue_error(msg: impl Into) -> Self { Self::DialogueError(msg.into()) } /// Check if error is recoverable (can retry) pub fn is_recoverable(&self) -> bool { matches!( self, Self::GenerationTimeout { .. } | Self::OutOfMemory | Self::NetworkError(_) | Self::ServerOverloaded { .. } | Self::RequestTimeout ) } /// Check if error is related to content safety pub fn is_safety_error(&self) -> bool { matches!( self, Self::ContentFiltered { .. } | Self::ToxicityDetected { .. } | Self::FactualityError(_) ) } /// Check if error is a configuration issue pub fn is_config_error(&self) -> bool { matches!( self, Self::InvalidConfig(_) | Self::InvalidParameter { .. } | Self::MissingParameter(_) ) } /// Get error category for metrics and logging pub fn category(&self) -> ErrorCategory { match self { Self::ModelNotFound(_) | Self::ModelLoadError(_) | Self::InferenceError(_) => { ErrorCategory::Model } Self::GenerationError(_) | Self::GenerationTimeout { .. } => ErrorCategory::Generation, Self::TokenizationError(_) | Self::InvalidTokenSequence => ErrorCategory::Tokenization, Self::ContentFiltered { .. } | Self::ToxicityDetected { .. } | Self::FactualityError(_) => ErrorCategory::Safety, Self::InvalidConfig(_) | Self::InvalidParameter { .. } | Self::MissingParameter(_) => { ErrorCategory::Configuration } Self::OutOfMemory | Self::DeviceError(_) | Self::CacheError(_) => { ErrorCategory::Resource } Self::NetworkError(_) | Self::ServerOverloaded { .. } | Self::RequestTimeout => { ErrorCategory::Network } Self::TranslationError(_) | Self::UnsupportedLanguagePair { .. } => { ErrorCategory::Translation } Self::SummarizationError(_) | Self::DocumentTooShort | Self::DocumentTooLong { .. } => { ErrorCategory::Summarization } Self::QuestionAnsweringError(_) | Self::NoAnswerFound | Self::ContextTooShort => { ErrorCategory::QuestionAnswering } Self::DialogueError(_) | Self::ConversationTooLong | Self::InvalidConversationState => { ErrorCategory::Dialogue } Self::KvCacheError(_) | Self::SpeculativeDecodingError(_) | Self::ModelParallelismError(_) => ErrorCategory::Optimization, Self::ValidationFailed { .. } | Self::InvalidPattern { .. } => { ErrorCategory::Configuration } Self::BatchQueueFull | Self::BatchTimeout | Self::BatchRequestCancelled => { ErrorCategory::Network } _ => ErrorCategory::Internal, } } } /// Error categories for metrics and monitoring #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ErrorCategory { Model, Generation, Tokenization, Safety, Configuration, Resource, Network, Translation, Summarization, QuestionAnswering, Dialogue, Optimization, Internal, } impl fmt::Display for ErrorCategory { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let name = match self { Self::Model => "model", Self::Generation => "generation", Self::Tokenization => "tokenization", Self::Safety => "safety", Self::Configuration => "configuration", Self::Resource => "resource", Self::Network => "network", Self::Translation => "translation", Self::Summarization => "summarization", Self::QuestionAnswering => "question_answering", Self::Dialogue => "dialogue", Self::Optimization => "optimization", Self::Internal => "internal", }; write!(f, "{name}") } } /// Error context for debugging and monitoring #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct ErrorContext { pub category: String, pub recoverable: bool, pub safety_related: bool, pub timestamp: chrono::DateTime, pub request_id: Option, pub model_id: Option, pub user_id: Option, } impl ErrorContext { /// Create error context from NlgError pub fn from_error(error: &NlgError) -> Self { Self { category: error.category().to_string(), recoverable: error.is_recoverable(), safety_related: error.is_safety_error(), timestamp: chrono::Utc::now(), request_id: None, model_id: None, user_id: None, } } /// Add request ID to context pub fn with_request_id(mut self, request_id: impl Into) -> Self { self.request_id = Some(request_id.into()); self } /// Add model ID to context pub fn with_model_id(mut self, model_id: impl Into) -> Self { self.model_id = Some(model_id.into()); self } /// Add user ID to context pub fn with_user_id(mut self, user_id: impl Into) -> Self { self.user_id = Some(user_id.into()); self } } // Implement From for rtx-tensor errors to allow ? operator impl From for NlgError { fn from(err: rtx_tensor::TensorError) -> Self { Self::TensorError(err.to_string()) } } // Implement From for String to allow easier error creation impl From for NlgError { fn from(s: String) -> Self { Self::GenerationError(s) } } // Implement From for &str to allow easier error creation impl From<&str> for NlgError { fn from(s: &str) -> Self { Self::GenerationError(s.to_string()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_error_creation() { let error = NlgError::model_not_found("test_model"); assert!(matches!(error, NlgError::ModelNotFound(_))); assert_eq!(error.category(), ErrorCategory::Model); assert!(!error.is_recoverable()); assert!(!error.is_safety_error()); } #[test] fn test_safety_error() { let error = NlgError::toxicity_detected(0.9); assert!(matches!(error, NlgError::ToxicityDetected { .. })); assert_eq!(error.category(), ErrorCategory::Safety); assert!(error.is_safety_error()); } #[test] fn test_recoverable_error() { let error = NlgError::GenerationTimeout { timeout_ms: 5000 }; assert!(error.is_recoverable()); assert_eq!(error.category(), ErrorCategory::Generation); } #[test] fn test_error_context() { let error = NlgError::generation_error("test error"); let context = ErrorContext::from_error(&error) .with_request_id("req_123") .with_model_id("model_456"); assert_eq!(context.category, "generation"); assert_eq!(context.request_id, Some("req_123".to_string())); assert_eq!(context.model_id, Some("model_456".to_string())); } #[test] fn test_error_display() { let error = NlgError::server_overloaded(100, 50); let error_str = format!("{}", error); assert!(error_str.contains("Server overloaded")); assert!(error_str.contains("100")); assert!(error_str.contains("50")); } }