errors.go

  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}