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}