use pyo3::exceptions::PyRuntimeError;
use pyo3::pyclass::PyClassGuardError;
use pyo3::PyErr;
use pythonize::PythonizeError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum UtilError {
#[error("Failed to get parent path")]
GetParentPathError,
#[error("Failed to create directory")]
CreateDirectoryError,
#[error("Failed to read to create path")]
CreatePathError,
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
}
impl From<UtilError> for PyErr {
fn from(err: UtilError) -> PyErr {
let msg = err.to_string();
PyRuntimeError::new_err(msg)
}
}
#[derive(Error, Debug)]
pub enum TypeError {
#[error("{0}")]
Error(String),
#[error("Start time must be before end time")]
StartTimeError,
#[error("Invalid schedule")]
InvalidScheduleError,
#[error("Invalid PSI threshold configuration")]
InvalidPsiThresholdError,
#[error("Invalid time interval for converting to start and end times")]
InvalidTimeIntervalError,
#[error("Invalid alert dispatch configuration")]
InvalidDispatchConfigError,
#[error("Invalid equal width binning method")]
InvalidEqualWidthBinningMethodError,
#[error("Missing space argument")]
MissingSpaceError,
#[error("Missing name argument")]
MissingNameError,
#[error("Missing version argument")]
MissingVersionError,
#[error("Missing uid argument")]
MissingUidError,
#[error("Missing alert_config argument")]
MissingAlertConfigError,
#[error("No metrics found")]
NoMetricsError,
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
#[error("Invalid number")]
InvalidNumberError,
#[error("Root must be an object")]
RootMustBeObject,
#[error("Unsupported type: {0}")]
UnsupportedType(String),
#[error("Failed to downcast Python object: {0}")]
DowncastError(String),
#[error("Invalid data type")]
InvalidDataType,
#[error("Missing value for string feature")]
MissingStringValueError,
#[error("{0}")]
PyError(String),
#[error(
"Unsupported feature type. Feature must be an integer, float or string. Received: {0}"
)]
UnsupportedFeatureTypeError(String),
#[error("Unsupported features type. Features must be a list of Feature instances or a dictionary of key value pairs. Received: {0}")]
UnsupportedFeaturesTypeError(String),
#[error("Unsupported metrics type. Metrics must be a list of Metric instances or a dictionary of key value pairs. Received: {0}")]
UnsupportedMetricsTypeError(String),
#[error("{0}")]
InvalidParameterError(String),
#[error("{0}")]
InvalidBinCountError(String),
#[error("{0}")]
InvalidValueError(String),
#[error("Empty Array Detected: {0}")]
EmptyArrayError(String),
#[error("Invalid binning strategy")]
InvalidBinningStrategyError,
#[error("Unsupported status. Status must be one of: All, Pending or Processed. Received: {0}")]
InvalidStatusError(String),
#[error("Failed to supply either input or response for the genai record")]
MissingInputOrResponse,
#[error("Invalid context type. Context must be a PyDict or a Pydantic BaseModel")]
MustBeDictOrBaseModel,
#[error("Failed to check if the context is a Pydantic BaseModel. Error: {0}")]
FailedToCheckPydanticModel(String),
#[error("Failed to import pydantic module. Error: {0}")]
FailedToImportPydantic(String),
#[error("Unsupported Python object type for conversion")]
UnsupportedPyObjectType,
#[error("Invalid dictionary key type. Dictionary keys must be strings, int, float or bool")]
InvalidDictKeyType,
#[error("Invalid compressions type")]
InvalidCompressionTypeError,
#[error("Invalid evaluation task type: {0}")]
InvalidEvalType(String),
#[error("Compression type not supported: {0}")]
CompressionTypeNotSupported(String),
#[error("Missing dependency: {0}")]
MissingDependency(String),
#[error("Expected a Python dict")]
ExpectedPyDict,
#[error("List contains an item that is neither AssertionTask nor LLMJudgeTask")]
InvalidAssertionTaskType,
#[error("{0}")]
FailedToCreateProfile(String),
#[error("Duplicate task IDs found in evaluation tasks")]
DuplicateTaskIds,
#[error("Key not found: {key}")]
KeyNotFound { key: String },
#[error("{0}")]
InvalidLength(String),
#[error(transparent)]
FromHexError(#[from] hex::FromHexError),
#[error(transparent)]
StdIOError(#[from] std::io::Error),
#[error(transparent)]
SerdeYamlError(#[from] serde_yaml::Error),
#[error("Array {index} out of bounds for length {length}")]
IndexOutOfBounds { index: isize, length: usize },
#[error("Expected an integer index or a slice")]
IndexOrSliceExpected,
#[error(transparent)]
PotatoTypeError(#[from] potato_head::TypeError),
}
impl From<pythonize::PythonizeError> for TypeError {
fn from(err: PythonizeError) -> Self {
TypeError::PyError(err.to_string())
}
}
impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for TypeError {
fn from(err: pyo3::CastError<'a, 'py>) -> Self {
TypeError::DowncastError(err.to_string())
}
}
impl From<TypeError> for PyErr {
fn from(err: TypeError) -> PyErr {
let msg = err.to_string();
PyRuntimeError::new_err(msg)
}
}
impl From<PyErr> for TypeError {
fn from(err: PyErr) -> TypeError {
TypeError::PyError(err.to_string())
}
}
impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for TypeError {
fn from(err: PyClassGuardError<'a, 'py>) -> Self {
TypeError::PyError(err.to_string())
}
}
#[derive(Error, Debug)]
pub enum ContractError {
#[error(transparent)]
TypeError(#[from] TypeError),
#[error("{0}")]
PyError(String),
}
impl From<ContractError> for PyErr {
fn from(err: ContractError) -> PyErr {
let msg = err.to_string();
PyRuntimeError::new_err(msg)
}
}
impl From<PyErr> for ContractError {
fn from(err: PyErr) -> ContractError {
ContractError::PyError(err.to_string())
}
}
#[derive(Error, Debug)]
pub enum RecordError {
#[error("Unable to extract record into any known ServerRecord variant")]
ExtractionError,
#[error("No server records found")]
EmptyServerRecordsError,
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
#[error("Unexpected record type")]
InvalidDriftTypeError,
#[error("{0}")]
PyError(String),
#[error("Failed to supply either input or response for the genai record")]
MissingInputOrResponse,
#[error(transparent)]
PotatoUtilError(#[from] potato_head::UtilError),
#[error(transparent)]
TypeError(#[from] TypeError),
#[error("Invalid context type. Context must be dictionary or Pydantic BaseModel")]
MustBeDictOrBaseModel,
#[error("Failed to downcast Python object: {0}")]
DowncastError(String),
#[error("{0}")]
SliceError(String),
#[error(transparent)]
FromHexError(#[from] hex::FromHexError),
}
impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for RecordError {
fn from(err: pyo3::CastError) -> Self {
RecordError::DowncastError(err.to_string())
}
}
impl From<pythonize::PythonizeError> for RecordError {
fn from(err: PythonizeError) -> Self {
RecordError::PyError(err.to_string())
}
}
impl From<RecordError> for PyErr {
fn from(err: RecordError) -> PyErr {
let msg = err.to_string();
PyRuntimeError::new_err(msg)
}
}
impl From<PyErr> for RecordError {
fn from(err: PyErr) -> RecordError {
RecordError::PyError(err.to_string())
}
}
impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for RecordError {
fn from(err: PyClassGuardError<'a, 'py>) -> Self {
RecordError::PyError(err.to_string())
}
}
#[derive(Error, Debug)]
pub enum ProfileError {
#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
#[error("Features and array are not the same length")]
FeatureArrayLengthError,
#[error("Unexpected record type")]
InvalidDriftTypeError,
#[error(transparent)]
UtilError(#[from] UtilError),
#[error(transparent)]
TypeError(#[from] TypeError),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error("Missing sample argument")]
MissingSampleError,
#[error("Missing sample size argument")]
MissingSampleSizeError,
#[error("Custom alert thresholds have not been set")]
CustomThresholdNotSetError,
#[error("Custom alert threshold not found")]
CustomAlertThresholdNotFound,
#[error("{0}")]
PyError(String),
#[error("Invalid binning strategy")]
InvalidBinningStrategyError,
#[error("Missing evaluation workflow")]
MissingWorkflowError,
#[error("Invalid argument for workflow. Argument must be a Workflow object")]
InvalidWorkflowType,
#[error(transparent)]
AgentError(#[from] potato_head::AgentError),
#[error(transparent)]
WorkflowError(#[from] potato_head::WorkflowError),
#[error("Invalid metric name found: {0}")]
InvalidMetricNameError(String),
#[error("No AssertionTasks or LLMJudgeTasks found in the workflow")]
EmptyTaskList,
#[error("LLM Metric requires at least one bound parameter")]
NeedAtLeastOneBoundParameterError(String),
#[error(
"Missing prompt in LLM Metric. If providing a list of metrics, prompt must be present"
)]
MissingPromptError(String),
#[error("No tasks found in the workflow when validating: {0}")]
NoTasksFoundError(String),
#[error("No metrics found for the output task: {0}")]
MetricNotFoundForOutputTask(String),
#[error("Metric not found in profile LLM metrics: {0}")]
MetricNotFound(String),
#[error(transparent)]
PotatoTypeError(#[from] potato_head::TypeError),
#[error("Invalid task type. Expected either AssertionTask or LLMJudgeTask: {0}")]
InvalidTaskType(String),
#[error("Detected circular dependency in evaluation tasks")]
CircularDependency,
}
impl From<ProfileError> for PyErr {
fn from(err: ProfileError) -> PyErr {
let msg = err.to_string();
PyRuntimeError::new_err(msg)
}
}
impl From<PyErr> for ProfileError {
fn from(err: PyErr) -> ProfileError {
ProfileError::PyError(err.to_string())
}
}
impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for ProfileError {
fn from(err: PyClassGuardError<'a, 'py>) -> Self {
ProfileError::PyError(err.to_string())
}
}
impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for ProfileError {
fn from(err: pyo3::CastError) -> Self {
ProfileError::PyError(err.to_string())
}
}