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}