1package fantasy
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7)
8
9// markerSymbol is used for identifying AI SDK Error instances.
10var markerSymbol = "fantasy.error"
11
12// AIError is a custom error type for AI SDK related errors.
13type AIError struct {
14 Name string
15 Message string
16 Cause error
17 marker string
18}
19
20// Error implements the error interface.
21func (e *AIError) Error() string {
22 return e.Message
23}
24
25// Unwrap returns the underlying cause of the error.
26func (e *AIError) Unwrap() error {
27 return e.Cause
28}
29
30// NewAIError creates a new AI SDK Error.
31func NewAIError(name, message string, cause error) *AIError {
32 return &AIError{
33 Name: name,
34 Message: message,
35 Cause: cause,
36 marker: markerSymbol,
37 }
38}
39
40// IsAIError checks if the given error is an AI SDK Error.
41func IsAIError(err error) bool {
42 var sdkErr *AIError
43 return errors.As(err, &sdkErr) && sdkErr.marker == markerSymbol
44}
45
46// APICallError represents an error from an API call.
47type APICallError struct {
48 *AIError
49 URL string
50 RequestDump string
51 StatusCode int
52 ResponseHeaders map[string]string
53 ResponseDump string
54 IsRetryable bool
55}
56
57// NewAPICallError creates a new API call error.
58func NewAPICallError(message, url string, requestDump string, statusCode int, responseHeaders map[string]string, responseDump string, cause error, isRetryable bool) *APICallError {
59 if !isRetryable && statusCode != 0 {
60 isRetryable = statusCode == 408 || statusCode == 409 || statusCode == 429 || statusCode >= 500
61 }
62
63 return &APICallError{
64 AIError: NewAIError("AI_APICallError", message, cause),
65 URL: url,
66 RequestDump: requestDump,
67 StatusCode: statusCode,
68 ResponseHeaders: responseHeaders,
69 ResponseDump: responseDump,
70 IsRetryable: isRetryable,
71 }
72}
73
74// EmptyResponseBodyError represents an empty response body error.
75type EmptyResponseBodyError struct {
76 *AIError
77}
78
79// NewEmptyResponseBodyError creates a new empty response body error.
80func NewEmptyResponseBodyError(message string) *EmptyResponseBodyError {
81 if message == "" {
82 message = "Empty response body"
83 }
84 return &EmptyResponseBodyError{
85 AIError: NewAIError("AI_EmptyResponseBodyError", message, nil),
86 }
87}
88
89// InvalidArgumentError represents an invalid function argument error.
90type InvalidArgumentError struct {
91 *AIError
92 Argument string
93}
94
95// NewInvalidArgumentError creates a new invalid argument error.
96func NewInvalidArgumentError(argument, message string, cause error) *InvalidArgumentError {
97 return &InvalidArgumentError{
98 AIError: NewAIError("AI_InvalidArgumentError", message, cause),
99 Argument: argument,
100 }
101}
102
103// InvalidPromptError represents an invalid prompt error.
104type InvalidPromptError struct {
105 *AIError
106 Prompt any
107}
108
109// NewInvalidPromptError creates a new invalid prompt error.
110func NewInvalidPromptError(prompt any, message string, cause error) *InvalidPromptError {
111 return &InvalidPromptError{
112 AIError: NewAIError("AI_InvalidPromptError", fmt.Sprintf("Invalid prompt: %s", message), cause),
113 Prompt: prompt,
114 }
115}
116
117// InvalidResponseDataError represents invalid response data from the server.
118type InvalidResponseDataError struct {
119 *AIError
120 Data any
121}
122
123// NewInvalidResponseDataError creates a new invalid response data error.
124func NewInvalidResponseDataError(data any, message string) *InvalidResponseDataError {
125 if message == "" {
126 dataJSON, _ := json.Marshal(data)
127 message = fmt.Sprintf("Invalid response data: %s.", string(dataJSON))
128 }
129 return &InvalidResponseDataError{
130 AIError: NewAIError("AI_InvalidResponseDataError", message, nil),
131 Data: data,
132 }
133}
134
135// JSONParseError represents a JSON parsing error.
136type JSONParseError struct {
137 *AIError
138 Text string
139}
140
141// NewJSONParseError creates a new JSON parse error.
142func NewJSONParseError(text string, cause error) *JSONParseError {
143 message := fmt.Sprintf("JSON parsing failed: Text: %s.\nError message: %s", text, GetErrorMessage(cause))
144 return &JSONParseError{
145 AIError: NewAIError("AI_JSONParseError", message, cause),
146 Text: text,
147 }
148}
149
150// LoadAPIKeyError represents an error loading an API key.
151type LoadAPIKeyError struct {
152 *AIError
153}
154
155// NewLoadAPIKeyError creates a new load API key error.
156func NewLoadAPIKeyError(message string) *LoadAPIKeyError {
157 return &LoadAPIKeyError{
158 AIError: NewAIError("AI_LoadAPIKeyError", message, nil),
159 }
160}
161
162// LoadSettingError represents an error loading a setting.
163type LoadSettingError struct {
164 *AIError
165}
166
167// NewLoadSettingError creates a new load setting error.
168func NewLoadSettingError(message string) *LoadSettingError {
169 return &LoadSettingError{
170 AIError: NewAIError("AI_LoadSettingError", message, nil),
171 }
172}
173
174// NoContentGeneratedError is thrown when the AI provider fails to generate any content.
175type NoContentGeneratedError struct {
176 *AIError
177}
178
179// NewNoContentGeneratedError creates a new no content generated error.
180func NewNoContentGeneratedError(message string) *NoContentGeneratedError {
181 if message == "" {
182 message = "No content generated."
183 }
184 return &NoContentGeneratedError{
185 AIError: NewAIError("AI_NoContentGeneratedError", message, nil),
186 }
187}
188
189// ModelType represents the type of model.
190type ModelType string
191
192const (
193 ModelTypeLanguage ModelType = "languageModel"
194 ModelTypeTextEmbedding ModelType = "textEmbeddingModel"
195 ModelTypeImage ModelType = "imageModel"
196 ModelTypeTranscription ModelType = "transcriptionModel"
197 ModelTypeSpeech ModelType = "speechModel"
198)
199
200// NoSuchModelError represents an error when a model is not found.
201type NoSuchModelError struct {
202 *AIError
203 ModelID string
204 ModelType ModelType
205}
206
207// NewNoSuchModelError creates a new no such model error.
208func NewNoSuchModelError(modelID string, modelType ModelType, message string) *NoSuchModelError {
209 if message == "" {
210 message = fmt.Sprintf("No such %s: %s", modelType, modelID)
211 }
212 return &NoSuchModelError{
213 AIError: NewAIError("AI_NoSuchModelError", message, nil),
214 ModelID: modelID,
215 ModelType: modelType,
216 }
217}
218
219// TooManyEmbeddingValuesForCallError represents an error when too many values are provided for embedding.
220type TooManyEmbeddingValuesForCallError struct {
221 *AIError
222 Provider string
223 ModelID string
224 MaxEmbeddingsPerCall int
225 Values []any
226}
227
228// NewTooManyEmbeddingValuesForCallError creates a new too many embedding values error.
229func NewTooManyEmbeddingValuesForCallError(provider, modelID string, maxEmbeddingsPerCall int, values []any) *TooManyEmbeddingValuesForCallError {
230 message := fmt.Sprintf(
231 "Too many values for a single embedding call. The %s model \"%s\" can only embed up to %d values per call, but %d values were provided.",
232 provider, modelID, maxEmbeddingsPerCall, len(values),
233 )
234 return &TooManyEmbeddingValuesForCallError{
235 AIError: NewAIError("AI_TooManyEmbeddingValuesForCallError", message, nil),
236 Provider: provider,
237 ModelID: modelID,
238 MaxEmbeddingsPerCall: maxEmbeddingsPerCall,
239 Values: values,
240 }
241}
242
243// TypeValidationError represents a type validation error.
244type TypeValidationError struct {
245 *AIError
246 Value any
247}
248
249// NewTypeValidationError creates a new type validation error.
250func NewTypeValidationError(value any, cause error) *TypeValidationError {
251 valueJSON, _ := json.Marshal(value)
252 message := fmt.Sprintf(
253 "Type validation failed: Value: %s.\nError message: %s",
254 string(valueJSON), GetErrorMessage(cause),
255 )
256 return &TypeValidationError{
257 AIError: NewAIError("AI_TypeValidationError", message, cause),
258 Value: value,
259 }
260}
261
262// WrapTypeValidationError wraps an error into a TypeValidationError.
263func WrapTypeValidationError(value any, cause error) *TypeValidationError {
264 if tvErr, ok := cause.(*TypeValidationError); ok && tvErr.Value == value {
265 return tvErr
266 }
267 return NewTypeValidationError(value, cause)
268}
269
270// UnsupportedFunctionalityError represents an unsupported functionality error.
271type UnsupportedFunctionalityError struct {
272 *AIError
273 Functionality string
274}
275
276// NewUnsupportedFunctionalityError creates a new unsupported functionality error.
277func NewUnsupportedFunctionalityError(functionality, message string) *UnsupportedFunctionalityError {
278 if message == "" {
279 message = fmt.Sprintf("'%s' functionality not supported.", functionality)
280 }
281 return &UnsupportedFunctionalityError{
282 AIError: NewAIError("AI_UnsupportedFunctionalityError", message, nil),
283 Functionality: functionality,
284 }
285}
286
287// GetErrorMessage extracts a message from an error.
288func GetErrorMessage(err error) string {
289 if err == nil {
290 return "unknown error"
291 }
292 return err.Error()
293}