Skip to main content

scouter_evaluate/
error.rs

1use pyo3::exceptions::PyRuntimeError;
2use pyo3::pyclass::PyClassGuardError;
3use pyo3::PyErr;
4use pythonize::PythonizeError;
5use thiserror::Error;
6use tracing::error;
7#[derive(Error, Debug)]
8pub enum EvaluationError {
9    #[error("Invalid response type. Expected Score")]
10    InvalidResponseError,
11
12    #[error(transparent)]
13    WorkflowError(#[from] potato_head::WorkflowError),
14
15    #[error("{0}")]
16    DowncastError(String),
17
18    #[error(transparent)]
19    JoinError(#[from] tokio::task::JoinError),
20
21    #[error(transparent)]
22    RegexError(#[from] regex::Error),
23
24    #[error("Missing key: {0}")]
25    MissingKeyError(String),
26
27    #[error("Invalid context type. Context must be a PyDict or a Pydantic BaseModel")]
28    MustBeDictOrBaseModel,
29
30    #[error(transparent)]
31    IoError(#[from] std::io::Error),
32
33    #[error(transparent)]
34    TypeError(#[from] scouter_types::error::TypeError),
35
36    #[error("Invalid embedder type. Expected an instance of Embedder")]
37    InvalidEmbedderType,
38
39    #[error("No results found in evaluation results")]
40    NoResultsFound,
41
42    #[error(transparent)]
43    ShapeError(#[from] ndarray::ShapeError),
44
45    #[error(transparent)]
46    DataProfileError(#[from] scouter_profile::error::DataProfileError),
47
48    #[error(transparent)]
49    SerdeError(#[from] serde_json::Error),
50
51    #[error("Field '{0}' not found")]
52    FieldNotFound(String),
53
54    #[error("Index {0} not found")]
55    IndexNotFound(usize),
56
57    #[error("Invalid array index: {0}")]
58    InvalidArrayIndex(String),
59
60    #[error("Empty context path provided")]
61    EmptyFieldPath,
62
63    #[error("{0}")]
64    PyError(String),
65
66    #[error("Cannot compare non-numeric values")]
67    CannotCompareNonNumericValues,
68
69    #[error("Contains operation requires string or list")]
70    InvalidContainsOperation,
71
72    #[error("StartsWith operation requires strings")]
73    InvalidStartsWithOperation,
74
75    #[error("EndsWith operation requires strings")]
76    InvalidEndsWithOperation,
77
78    #[error("Regex match requires strings")]
79    InvalidRegexOperation,
80
81    #[error("Invalid number format")]
82    InvalidNumberFormat,
83
84    #[error("Cannot convert object to AssertionValue")]
85    CannotConvertObjectToAssertionValue,
86
87    #[error("Cannot get length of object")]
88    CannotGetLengthOfObject,
89
90    #[error("Expected value for length must be an integer")]
91    ExpectedLengthMustBeInteger,
92
93    #[error("Invalid assertion value type")]
94    InvalidAssertionValueType,
95
96    #[error("Invalid task type for evaluation")]
97    InvalidTaskType,
98
99    #[error("Cannot get length: {0}")]
100    CannotGetLength(String),
101
102    #[error(transparent)]
103    ProfileError(#[from] scouter_types::error::ProfileError),
104
105    #[error("Failed to process GenAI drift record: {0}")]
106    GenAIEvaluatorError(String),
107
108    #[error("Task not found: {0}")]
109    TaskNotFound(String),
110
111    #[error("Failed to acquire read lock on workflow")]
112    ReadLockAcquireError,
113
114    #[error("Invalid email validation operation")]
115    InvalidEmailOperation,
116
117    #[error("Invalid URL validation operation")]
118    InvalidUrlOperation,
119
120    #[error("Invalid UUID validation operation")]
121    InvalidUuidOperation,
122
123    #[error("Invalid ISO8601 validation operation")]
124    InvalidIso8601Operation,
125
126    #[error("Invalid JSON validation operation")]
127    InvalidJsonOperation,
128
129    #[error("Invalid range format - expected [min, max] array")]
130    InvalidRangeFormat,
131
132    #[error("Invalid tolerance format - expected [value, tolerance] array")]
133    InvalidToleranceFormat,
134
135    #[error("Invalid ContainsAll operation")]
136    InvalidContainsAllOperation,
137
138    #[error("Invalid ContainsAny operation")]
139    InvalidContainsAnyOperation,
140
141    #[error("Invalid ContainsNone operation")]
142    InvalidContainsNoneOperation,
143
144    #[error("Invalid empty check operation")]
145    InvalidEmptyOperation,
146
147    #[error("Invalid unique items check operation")]
148    InvalidUniqueItemsOperation,
149
150    #[error("Invalid alphabetic check operation")]
151    InvalidAlphabeticOperation,
152
153    #[error("Invalid alphanumeric check operation")]
154    InvalidAlphanumericOperation,
155
156    #[error("Invalid case check operation")]
157    InvalidCaseOperation,
158
159    #[error("Invalid contains word operation")]
160    InvalidContainsWordOperation,
161
162    #[error(transparent)]
163    RecordError(#[from] scouter_types::error::RecordError),
164
165    #[error("Array {index} out of bounds for length {length}")]
166    IndexOutOfBounds { index: isize, length: usize },
167
168    #[error("Expected an integer index or a slice")]
169    IndexOrSliceExpected,
170
171    #[error("Invalid sequence matches operation")]
172    InvalidSequenceMatchesOperation,
173
174    #[error("Invalid filter: {0}")]
175    InvalidFilter(String),
176
177    #[error("Trace data has no spans")]
178    NoRootSpan,
179
180    #[error("Attribute '{0}' not found in span")]
181    AttributeNotFound(String),
182}
183
184impl From<pythonize::PythonizeError> for EvaluationError {
185    fn from(err: PythonizeError) -> Self {
186        EvaluationError::PyError(err.to_string())
187    }
188}
189
190impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for EvaluationError {
191    fn from(err: pyo3::CastError) -> Self {
192        EvaluationError::DowncastError(err.to_string())
193    }
194}
195
196impl From<EvaluationError> for PyErr {
197    fn from(err: EvaluationError) -> PyErr {
198        let msg = err.to_string();
199        error!("{}", msg);
200        PyRuntimeError::new_err(msg)
201    }
202}
203
204impl From<PyErr> for EvaluationError {
205    fn from(err: PyErr) -> EvaluationError {
206        EvaluationError::PyError(err.to_string())
207    }
208}
209
210impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for EvaluationError {
211    fn from(err: PyClassGuardError<'a, 'py>) -> Self {
212        EvaluationError::PyError(err.to_string())
213    }
214}