Skip to main content

potato_type/
error.rs

1use pyo3::exceptions::PyRuntimeError;
2use pyo3::pyclass::PyClassGuardError;
3use pyo3::PyErr;
4use pythonize::PythonizeError;
5use thiserror::Error;
6use tracing::error;
7
8#[derive(Error, Debug)]
9pub enum TypeError {
10    #[error("Error: {0}")]
11    Error(String),
12
13    #[error("Unknown provider: {0}")]
14    UnknownProviderError(String),
15
16    #[error("No provider specified. Pass `provider=` explicitly or set the {0} env var.")]
17    MissingProviderError(&'static str),
18
19    #[error("Unknown model: {0}")]
20    UnknownModelError(String),
21
22    #[error("{0}")]
23    InvalidInput(String),
24
25    #[error(transparent)]
26    UtilError(#[from] potato_util::UtilError),
27
28    #[error(transparent)]
29    SerdeError(#[from] serde_json::Error),
30
31    #[error(transparent)]
32    StdError(#[from] std::io::Error),
33
34    #[error("Failed to create GeminiEmbeddingConfig: {0}")]
35    GeminiEmbeddingConfigError(String),
36
37    #[error("Invalid media type: {0}")]
38    InvalidMediaType(String),
39
40    #[error("Invalid media file: {0}")]
41    InvalidMediaFile(String),
42
43    #[error("Media file '{path}' is too large: {size} bytes exceeds maximum {max_size} bytes")]
44    MediaFileTooLarge {
45        path: String,
46        size: u64,
47        max_size: u64,
48    },
49
50    #[error("media placeholder '${{media:{name}}}' not found in any user message")]
51    MediaPlaceholderNotFound { name: String },
52
53    #[error("media placeholder '${{media:{name}}}' must be the entire content of a text block")]
54    MediaPlaceholderNotIsolated { name: String },
55
56    #[error("provider {provider} does not support media kind {kind:?} with given source")]
57    UnsupportedMediaForProvider {
58        provider: String,
59        kind: crate::prompt::media::MediaKind,
60    },
61
62    #[error("media placeholders are not allowed in system messages")]
63    MediaInSystemMessage,
64
65    #[error("Unsupported prompt content type")]
66    UnsupportedTypeError,
67
68    #[error("Cannot bind non-string content")]
69    CannotBindNonStringContent,
70
71    #[error("Failed to serialize Python object: {0}")]
72    PySerializationError(String),
73
74    #[error("Content type is not supported")]
75    UnsupportedContentType,
76
77    #[error("Invalid model settings provided. ModelSettings expects either OpenAIChatSettings, GeminiSettings, or AnthropicSettings.")]
78    InvalidModelSettings,
79
80    #[error("Expected string, Message, or list of messages")]
81    MessageParseError,
82
83    #[error("Invalid message type in list. Received: {0}")]
84    InvalidMessageTypeInList(String),
85
86    #[error("Either 'auto_mode' or 'manual_mode' must be provided, but not both.")]
87    MissingRoutingConfigMode,
88
89    #[error("Invalid data type for Part. Expected String, Blob, FileData, FunctionCall, FunctionResponse, ExecutableCode, or CodeExecutionResult. Got: {0}")]
90    InvalidDataType(String),
91
92    #[error("Invalid list type for Part. Expected a list of Strings or a list of Message objects. Got: {0}")]
93    InvalidListType(String),
94
95    #[error("Parts must be a string, Part, DataNum variant, or a list of these types")]
96    InvalidPartType,
97
98    #[error("Invalid ranking config provided.")]
99    InvalidRankingConfig,
100
101    #[error("Invalid retrieval source provided.")]
102    InvalidRetrievalSource,
103
104    #[error("Invalid authentication configuration provided.")]
105    InvalidAuthConfig,
106
107    #[error("Invalid OAuth configuration provided.")]
108    InvalidOauthConfig,
109
110    #[error("Invalid OIDC configuration provided.")]
111    InvalidOidcConfig,
112
113    #[error("More than one system instruction provided where only one is allowed.")]
114    MoreThanOneSystemInstruction,
115
116    #[error("Invalid request type for Anthropic provider.")]
117    InvalidRequestTypeForAnthropic,
118
119    #[error("Invalid request type for Gemini provider.")]
120    InvalidRequestTypeForGemini,
121
122    #[error("Invalid request type for OpenAI provider.")]
123    InvalidRequestTypeForOpenAI,
124
125    #[error("Unsupported provider for request creation.")]
126    UnsupportedProviderForRequestCreation,
127
128    #[error("OpenAI response content is empty.")]
129    EmptyOpenAIResponseContent,
130
131    #[error("{0}")]
132    UnsupportedConversion(String),
133
134    #[error("Cannot convert message to self")]
135    CantConvertSelf,
136
137    #[error("Unsupported provider for message conversion")]
138    UnsupportedProviderError,
139
140    #[error("Message is not an OpenAI ChatMessage")]
141    MessageIsNotOpenAIChatMessage,
142
143    #[error("Message is not a Google GeminiContent")]
144    MessageIsNotGoogleGeminiContent,
145
146    #[error("Message is not an Anthropic MessageParam")]
147    MessageIsNotAnthropicMessageParam,
148
149    #[error("Failed to resolve prompt path '{requested_path}'. Tried: {attempted_paths}")]
150    PromptPathNotFound {
151        requested_path: String,
152        attempted_paths: String,
153    },
154
155    #[error("Prompt path '{requested_path}' is ambiguous. Matches: {candidate_paths}")]
156    AmbiguousPromptPath {
157        requested_path: String,
158        candidate_paths: String,
159    },
160
161    #[error(transparent)]
162    SerdeYamlError(#[from] serde_yaml::Error),
163}
164
165impl From<TypeError> for PyErr {
166    fn from(err: TypeError) -> PyErr {
167        let msg = err.to_string();
168        error!("{}", msg);
169        PyRuntimeError::new_err(msg)
170    }
171}
172
173impl From<PythonizeError> for TypeError {
174    fn from(err: PythonizeError) -> Self {
175        TypeError::Error(err.to_string())
176    }
177}
178
179impl From<PyErr> for TypeError {
180    fn from(err: PyErr) -> Self {
181        TypeError::Error(err.to_string())
182    }
183}
184
185impl<'a, 'py> From<PyClassGuardError<'a, 'py>> for TypeError {
186    fn from(err: PyClassGuardError<'a, 'py>) -> Self {
187        TypeError::Error(err.to_string())
188    }
189}
190
191impl<'a, 'py> From<pyo3::CastError<'a, 'py>> for TypeError {
192    fn from(err: pyo3::CastError<'a, 'py>) -> Self {
193        TypeError::Error(err.to_string())
194    }
195}