use futures::io;
use potato_head::error::WorkflowError;
use pyo3::exceptions::PyRuntimeError;
use pyo3::pyclass::PyClassGuardError;
use pyo3::PyErr;
use scouter_dispatch::error::DispatchError;
#[cfg(feature = "sql")]
use scouter_sql::sql::error::SqlError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DriftError {
#[error("Failed to compute mean")]
ComputeMeanError,
#[error("At least 10 values needed to compute deciles")]
NotEnoughDecileValuesError,
#[error("Failed to convert deciles to array")]
ConvertDecileToArray,
#[error("Failed to compute deciles")]
ComputeDecilesError,
#[error("{0}")]
RunTimeError(String),
#[error("Feature and array length mismatch")]
FeatureLengthError,
#[error("Feature does not exist")]
FeatureNotExistError,
#[error(transparent)]
ShapeError(#[from] ndarray::ShapeError),
#[cfg(feature = "sql")]
#[error(transparent)]
SqlError(#[from] SqlError),
#[error(transparent)]
UtilError(#[from] potato_head::UtilError),
#[error("SPC rule length is not 8")]
SpcRuleLengthError,
#[error(transparent)]
ParseIntError(#[from] std::num::ParseIntError),
#[error(transparent)]
DispatchError(#[from] DispatchError),
#[error("Failed to process alerts")]
ProcessAlertError,
#[error("Invalid configuration provided for drifter. Please check that the configuration type matches the drifter type")]
InvalidConfigError,
#[error("Not implemented")]
NotImplemented,
#[error("Data type not supported: {0}")]
UnsupportedDataTypeError(String),
#[error("Failed to downcast Python object: {0}")]
DowncastError(String),
#[error(transparent)]
ProfileError(#[from] scouter_types::error::ProfileError),
#[error("Invalid drift type")]
InvalidDriftType,
#[error("Error processing alert: {0}")]
AlertProcessingError(String),
#[error("Feature to monitor: {0}, not present in data")]
FeatureToMonitorMissingError(String),
#[error("Categorical feature specified in drift config: {0}, not present in data")]
CategoricalFeatureMissingError(String),
#[error("Empty Array Detected: {0}")]
EmptyArrayError(String),
#[error("Failed to compute binning edges: {0}")]
BinningError(String),
#[error("Failed to deserialize: {0}")]
SerdeJsonError(#[from] serde_json::Error),
#[error("Context is not a valid JSON object. Should be a Map<String, Value>")]
InvalidContextFormat,
#[error(transparent)]
WorkflowError(#[from] WorkflowError),
#[error("Incorrect method called: {0}")]
WrongMethodError(String),
#[error("Invalid content type. Expected a json string or value")]
InvalidContentTypeError,
#[error("Failed to setup tokio runtime for computing GenAI drift: {0}")]
SetupTokioRuntimeError(#[source] io::Error),
#[error("{0}")]
InvalidDataConfiguration(String),
#[error("Workflow is missing for GenAI drift evaluation")]
MissingWorkflow,
#[error("Failed to acquire write lock on workflow")]
WriteLockAcquireError,
#[error("Task execution error: {0}")]
TaskExecutionError(String),
#[error(transparent)]
EvaluationError(#[from] scouter_evaluate::error::EvaluationError),
#[error("Expected a list of AssertionTask or LLMJudgeTask. Received {0}")]
ExpectedListOfAssertionOrLLMJudgeTasks(String),
#[error("Expected a list of EvalRecords. Received {0}")]
ExpectedListOfEvalRecords(String),
#[error("Failed to process GenAI evaluation: {0}")]
GenAIEvaluatorError(String),
#[error(transparent)]
TypeError(#[from] scouter_types::error::TypeError),
#[error("Trace spans not available for task: {0}")]
TraceSpansNotAvailable(String),
#[error(transparent)]
OutOfRangeError(#[from] chrono::OutOfRangeError),
}
impl From<DriftError> for PyErr {
fn from(err: DriftError) -> PyErr {
let msg = err.to_string();
PyRuntimeError::new_err(msg)
}
}
impl From<PyErr> for DriftError {
fn from(err: PyErr) -> DriftError {
DriftError::RunTimeError(err.to_string())
}
}
impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for DriftError {
fn from(err: PyClassGuardError<'a, 'py>) -> Self {
DriftError::RunTimeError(err.to_string())
}
}
impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for DriftError {
fn from(err: pyo3::CastError<'a, 'py>) -> Self {
DriftError::DowncastError(err.to_string())
}
}