potato-type 0.26.0

Toppings for your potatoes
Documentation
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())
    }
}