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 represents a language model.
194	ModelTypeLanguage ModelType = "languageModel"
195	// ModelTypeTextEmbedding represents a text embedding model.
196	ModelTypeTextEmbedding ModelType = "textEmbeddingModel"
197	// ModelTypeImage represents an image model.
198	ModelTypeImage ModelType = "imageModel"
199	// ModelTypeTranscription represents a transcription model.
200	ModelTypeTranscription ModelType = "transcriptionModel"
201	// ModelTypeSpeech represents a speech model.
202	ModelTypeSpeech ModelType = "speechModel"
203)
204
205// NoSuchModelError represents an error when a model is not found.
206type NoSuchModelError struct {
207	*AIError
208	ModelID   string
209	ModelType ModelType
210}
211
212// NewNoSuchModelError creates a new no such model error.
213func NewNoSuchModelError(modelID string, modelType ModelType, message string) *NoSuchModelError {
214	if message == "" {
215		message = fmt.Sprintf("No such %s: %s", modelType, modelID)
216	}
217	return &NoSuchModelError{
218		AIError:   NewAIError("AI_NoSuchModelError", message, nil),
219		ModelID:   modelID,
220		ModelType: modelType,
221	}
222}
223
224// TooManyEmbeddingValuesForCallError represents an error when too many values are provided for embedding.
225type TooManyEmbeddingValuesForCallError struct {
226	*AIError
227	Provider             string
228	ModelID              string
229	MaxEmbeddingsPerCall int
230	Values               []any
231}
232
233// NewTooManyEmbeddingValuesForCallError creates a new too many embedding values error.
234func NewTooManyEmbeddingValuesForCallError(provider, modelID string, maxEmbeddingsPerCall int, values []any) *TooManyEmbeddingValuesForCallError {
235	message := fmt.Sprintf(
236		"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.",
237		provider, modelID, maxEmbeddingsPerCall, len(values),
238	)
239	return &TooManyEmbeddingValuesForCallError{
240		AIError:              NewAIError("AI_TooManyEmbeddingValuesForCallError", message, nil),
241		Provider:             provider,
242		ModelID:              modelID,
243		MaxEmbeddingsPerCall: maxEmbeddingsPerCall,
244		Values:               values,
245	}
246}
247
248// TypeValidationError represents a type validation error.
249type TypeValidationError struct {
250	*AIError
251	Value any
252}
253
254// NewTypeValidationError creates a new type validation error.
255func NewTypeValidationError(value any, cause error) *TypeValidationError {
256	valueJSON, _ := json.Marshal(value)
257	message := fmt.Sprintf(
258		"Type validation failed: Value: %s.\nError message: %s",
259		string(valueJSON), GetErrorMessage(cause),
260	)
261	return &TypeValidationError{
262		AIError: NewAIError("AI_TypeValidationError", message, cause),
263		Value:   value,
264	}
265}
266
267// WrapTypeValidationError wraps an error into a TypeValidationError.
268func WrapTypeValidationError(value any, cause error) *TypeValidationError {
269	if tvErr, ok := cause.(*TypeValidationError); ok && tvErr.Value == value {
270		return tvErr
271	}
272	return NewTypeValidationError(value, cause)
273}
274
275// UnsupportedFunctionalityError represents an unsupported functionality error.
276type UnsupportedFunctionalityError struct {
277	*AIError
278	Functionality string
279}
280
281// NewUnsupportedFunctionalityError creates a new unsupported functionality error.
282func NewUnsupportedFunctionalityError(functionality, message string) *UnsupportedFunctionalityError {
283	if message == "" {
284		message = fmt.Sprintf("'%s' functionality not supported.", functionality)
285	}
286	return &UnsupportedFunctionalityError{
287		AIError:       NewAIError("AI_UnsupportedFunctionalityError", message, nil),
288		Functionality: functionality,
289	}
290}
291
292// GetErrorMessage extracts a message from an error.
293func GetErrorMessage(err error) string {
294	if err == nil {
295		return "unknown error"
296	}
297	return err.Error()
298}