use pyo3::exceptions::PyRuntimeError;
use pyo3::pyclass::PyClassGuardError;
use pyo3::PyErr;
use pythonize::PythonizeError;
use thiserror::Error;
use tracing::error;
#[derive(Error, Debug)]
pub enum TypeError {
#[error("Error: {0}")]
Error(String),
#[error("Unknown provider: {0}")]
UnknownProviderError(String),
#[error("No provider specified. Pass `provider=` explicitly or set the {0} env var.")]
MissingProviderError(&'static str),
#[error("Unknown model: {0}")]
UnknownModelError(String),
#[error("{0}")]
InvalidInput(String),
#[error(transparent)]
UtilError(#[from] potato_util::UtilError),
#[error(transparent)]
SerdeError(#[from] serde_json::Error),
#[error(transparent)]
StdError(#[from] std::io::Error),
#[error("Failed to create GeminiEmbeddingConfig: {0}")]
GeminiEmbeddingConfigError(String),
#[error("Invalid media type: {0}")]
InvalidMediaType(String),
#[error("Invalid media file: {0}")]
InvalidMediaFile(String),
#[error("Media file '{path}' is too large: {size} bytes exceeds maximum {max_size} bytes")]
MediaFileTooLarge {
path: String,
size: u64,
max_size: u64,
},
#[error("media placeholder '${{media:{name}}}' not found in any user message")]
MediaPlaceholderNotFound { name: String },
#[error("media placeholder '${{media:{name}}}' must be the entire content of a text block")]
MediaPlaceholderNotIsolated { name: String },
#[error("provider {provider} does not support media kind {kind:?} with given source")]
UnsupportedMediaForProvider {
provider: String,
kind: crate::prompt::media::MediaKind,
},
#[error("media placeholders are not allowed in system messages")]
MediaInSystemMessage,
#[error("Unsupported prompt content type")]
UnsupportedTypeError,
#[error("Cannot bind non-string content")]
CannotBindNonStringContent,
#[error("Failed to serialize Python object: {0}")]
PySerializationError(String),
#[error("Content type is not supported")]
UnsupportedContentType,
#[error("Invalid model settings provided. ModelSettings expects either OpenAIChatSettings, GeminiSettings, or AnthropicSettings.")]
InvalidModelSettings,
#[error("Expected string, Message, or list of messages")]
MessageParseError,
#[error("Invalid message type in list. Received: {0}")]
InvalidMessageTypeInList(String),
#[error("Either 'auto_mode' or 'manual_mode' must be provided, but not both.")]
MissingRoutingConfigMode,
#[error("Invalid data type for Part. Expected String, Blob, FileData, FunctionCall, FunctionResponse, ExecutableCode, or CodeExecutionResult. Got: {0}")]
InvalidDataType(String),
#[error("Invalid list type for Part. Expected a list of Strings or a list of Message objects. Got: {0}")]
InvalidListType(String),
#[error("Parts must be a string, Part, DataNum variant, or a list of these types")]
InvalidPartType,
#[error("Invalid ranking config provided.")]
InvalidRankingConfig,
#[error("Invalid retrieval source provided.")]
InvalidRetrievalSource,
#[error("Invalid authentication configuration provided.")]
InvalidAuthConfig,
#[error("Invalid OAuth configuration provided.")]
InvalidOauthConfig,
#[error("Invalid OIDC configuration provided.")]
InvalidOidcConfig,
#[error("More than one system instruction provided where only one is allowed.")]
MoreThanOneSystemInstruction,
#[error("Invalid request type for Anthropic provider.")]
InvalidRequestTypeForAnthropic,
#[error("Invalid request type for Gemini provider.")]
InvalidRequestTypeForGemini,
#[error("Invalid request type for OpenAI provider.")]
InvalidRequestTypeForOpenAI,
#[error("Unsupported provider for request creation.")]
UnsupportedProviderForRequestCreation,
#[error("OpenAI response content is empty.")]
EmptyOpenAIResponseContent,
#[error("{0}")]
UnsupportedConversion(String),
#[error("Cannot convert message to self")]
CantConvertSelf,
#[error("Unsupported provider for message conversion")]
UnsupportedProviderError,
#[error("Message is not an OpenAI ChatMessage")]
MessageIsNotOpenAIChatMessage,
#[error("Message is not a Google GeminiContent")]
MessageIsNotGoogleGeminiContent,
#[error("Message is not an Anthropic MessageParam")]
MessageIsNotAnthropicMessageParam,
#[error("Failed to resolve prompt path '{requested_path}'. Tried: {attempted_paths}")]
PromptPathNotFound {
requested_path: String,
attempted_paths: String,
},
#[error("Prompt path '{requested_path}' is ambiguous. Matches: {candidate_paths}")]
AmbiguousPromptPath {
requested_path: String,
candidate_paths: String,
},
#[error(transparent)]
SerdeYamlError(#[from] serde_yaml::Error),
}
impl From<TypeError> for PyErr {
fn from(err: TypeError) -> PyErr {
let msg = err.to_string();
error!("{}", msg);
PyRuntimeError::new_err(msg)
}
}
impl From<PythonizeError> for TypeError {
fn from(err: PythonizeError) -> Self {
TypeError::Error(err.to_string())
}
}
impl From<PyErr> for TypeError {
fn from(err: PyErr) -> Self {
TypeError::Error(err.to_string())
}
}
impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for TypeError {
fn from(err: PyClassGuardError<'a, 'py>) -> Self {
TypeError::Error(err.to_string())
}
}
impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for TypeError {
fn from(err: pyo3::CastError<'a, 'py>) -> Self {
TypeError::Error(err.to_string())
}
}