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