Skip to main content

scouter_drift/
error.rs

1use futures::io;
2use potato_head::error::WorkflowError;
3use pyo3::exceptions::PyRuntimeError;
4use pyo3::pyclass::PyClassGuardError;
5use pyo3::PyErr;
6use scouter_dispatch::error::DispatchError;
7#[cfg(feature = "sql")]
8use scouter_sql::sql::error::SqlError;
9use thiserror::Error;
10#[derive(Error, Debug)]
11pub enum DriftError {
12    #[error("Failed to compute mean")]
13    ComputeMeanError,
14
15    #[error("At least 10 values needed to compute deciles")]
16    NotEnoughDecileValuesError,
17
18    #[error("Failed to convert deciles to array")]
19    ConvertDecileToArray,
20
21    #[error("Failed to compute deciles")]
22    ComputeDecilesError,
23
24    #[error("{0}")]
25    RunTimeError(String),
26
27    #[error("Feature and array length mismatch")]
28    FeatureLengthError,
29
30    #[error("Feature does not exist")]
31    FeatureNotExistError,
32
33    #[error(transparent)]
34    ShapeError(#[from] ndarray::ShapeError),
35
36    #[cfg(feature = "sql")]
37    #[error(transparent)]
38    SqlError(#[from] SqlError),
39
40    #[error(transparent)]
41    UtilError(#[from] potato_head::UtilError),
42
43    #[error("SPC rule length is not 8")]
44    SpcRuleLengthError,
45
46    #[error(transparent)]
47    ParseIntError(#[from] std::num::ParseIntError),
48
49    #[error(transparent)]
50    DispatchError(#[from] DispatchError),
51
52    #[error("Failed to process alerts")]
53    ProcessAlertError,
54
55    #[error("Invalid configuration provided for drifter. Please check that the configuration type matches the drifter type")]
56    InvalidConfigError,
57
58    #[error("Not implemented")]
59    NotImplemented,
60
61    #[error("Data type not supported: {0}")]
62    UnsupportedDataTypeError(String),
63
64    #[error("Failed to downcast Python object: {0}")]
65    DowncastError(String),
66
67    #[error(transparent)]
68    ProfileError(#[from] scouter_types::error::ProfileError),
69
70    #[error("Invalid drift type")]
71    InvalidDriftType,
72
73    #[error("Error processing alert: {0}")]
74    AlertProcessingError(String),
75
76    #[error("Feature to monitor: {0}, not present in data")]
77    FeatureToMonitorMissingError(String),
78
79    #[error("Categorical feature specified in drift config: {0}, not present in data")]
80    CategoricalFeatureMissingError(String),
81
82    #[error("Empty Array Detected: {0}")]
83    EmptyArrayError(String),
84
85    #[error("Failed to compute binning edges: {0}")]
86    BinningError(String),
87
88    #[error("Failed to deserialize: {0}")]
89    SerdeJsonError(#[from] serde_json::Error),
90
91    #[error("Context is not a valid JSON object. Should be a Map<String, Value>")]
92    InvalidContextFormat,
93
94    #[error(transparent)]
95    WorkflowError(#[from] WorkflowError),
96
97    #[error("Incorrect method called: {0}")]
98    WrongMethodError(String),
99
100    #[error("Invalid content type. Expected a json string or value")]
101    InvalidContentTypeError,
102
103    #[error("Failed to setup tokio runtime for computing GenAI drift: {0}")]
104    SetupTokioRuntimeError(#[source] io::Error),
105
106    #[error("{0}")]
107    InvalidDataConfiguration(String),
108
109    #[error("Workflow is missing for GenAI drift evaluation")]
110    MissingWorkflow,
111
112    #[error("Failed to acquire write lock on workflow")]
113    WriteLockAcquireError,
114
115    #[error("Task execution error: {0}")]
116    TaskExecutionError(String),
117
118    #[error(transparent)]
119    EvaluationError(#[from] scouter_evaluate::error::EvaluationError),
120
121    #[error("Expected a list of AssertionTask or LLMJudgeTask. Received {0}")]
122    ExpectedListOfAssertionOrLLMJudgeTasks(String),
123
124    #[error("Expected a list of GenAIEvalRecords. Received {0}")]
125    ExpectedListOfGenAIEvalRecords(String),
126
127    #[error("Failed to process GenAI evaluation: {0}")]
128    GenAIEvaluatorError(String),
129
130    #[error(transparent)]
131    TypeError(#[from] scouter_types::error::TypeError),
132
133    #[error("Trace spans not available for task: {0}")]
134    TraceSpansNotAvailable(String),
135
136    #[error(transparent)]
137    OutOfRangeError(#[from] chrono::OutOfRangeError),
138}
139
140impl From<DriftError> for PyErr {
141    fn from(err: DriftError) -> PyErr {
142        let msg = err.to_string();
143        PyRuntimeError::new_err(msg)
144    }
145}
146
147impl From<PyErr> for DriftError {
148    fn from(err: PyErr) -> DriftError {
149        DriftError::RunTimeError(err.to_string())
150    }
151}
152
153impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for DriftError {
154    fn from(err: PyClassGuardError<'a, 'py>) -> Self {
155        DriftError::RunTimeError(err.to_string())
156    }
157}
158
159impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for DriftError {
160    fn from(err: pyo3::CastError<'a, 'py>) -> Self {
161        DriftError::DowncastError(err.to_string())
162    }
163}