| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- package types
- import (
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "strings"
- "github.com/QuantumNous/new-api/common"
- )
- type OpenAIError struct {
- Message string `json:"message"`
- Type string `json:"type"`
- Param string `json:"param"`
- Code any `json:"code"`
- Metadata json.RawMessage `json:"metadata,omitempty"`
- }
- type ClaudeError struct {
- Type string `json:"type,omitempty"`
- Message string `json:"message,omitempty"`
- }
- type ErrorType string
- const (
- ErrorTypeNewAPIError ErrorType = "new_api_error"
- ErrorTypeOpenAIError ErrorType = "openai_error"
- ErrorTypeClaudeError ErrorType = "claude_error"
- ErrorTypeMidjourneyError ErrorType = "midjourney_error"
- ErrorTypeGeminiError ErrorType = "gemini_error"
- ErrorTypeRerankError ErrorType = "rerank_error"
- ErrorTypeUpstreamError ErrorType = "upstream_error"
- )
- type ErrorCode string
- const (
- ErrorCodeInvalidRequest ErrorCode = "invalid_request"
- ErrorCodeSensitiveWordsDetected ErrorCode = "sensitive_words_detected"
- // new api error
- ErrorCodeCountTokenFailed ErrorCode = "count_token_failed"
- ErrorCodeModelPriceError ErrorCode = "model_price_error"
- ErrorCodeInvalidApiType ErrorCode = "invalid_api_type"
- ErrorCodeJsonMarshalFailed ErrorCode = "json_marshal_failed"
- ErrorCodeDoRequestFailed ErrorCode = "do_request_failed"
- ErrorCodeGetChannelFailed ErrorCode = "get_channel_failed"
- ErrorCodeGenRelayInfoFailed ErrorCode = "gen_relay_info_failed"
- // channel error
- ErrorCodeChannelNoAvailableKey ErrorCode = "channel:no_available_key"
- ErrorCodeChannelParamOverrideInvalid ErrorCode = "channel:param_override_invalid"
- ErrorCodeChannelHeaderOverrideInvalid ErrorCode = "channel:header_override_invalid"
- ErrorCodeChannelModelMappedError ErrorCode = "channel:model_mapped_error"
- ErrorCodeChannelAwsClientError ErrorCode = "channel:aws_client_error"
- ErrorCodeChannelInvalidKey ErrorCode = "channel:invalid_key"
- ErrorCodeChannelResponseTimeExceeded ErrorCode = "channel:response_time_exceeded"
- // client request error
- ErrorCodeReadRequestBodyFailed ErrorCode = "read_request_body_failed"
- ErrorCodeConvertRequestFailed ErrorCode = "convert_request_failed"
- ErrorCodeAccessDenied ErrorCode = "access_denied"
- // request error
- ErrorCodeBadRequestBody ErrorCode = "bad_request_body"
- // response error
- ErrorCodeReadResponseBodyFailed ErrorCode = "read_response_body_failed"
- ErrorCodeBadResponseStatusCode ErrorCode = "bad_response_status_code"
- ErrorCodeBadResponse ErrorCode = "bad_response"
- ErrorCodeBadResponseBody ErrorCode = "bad_response_body"
- ErrorCodeEmptyResponse ErrorCode = "empty_response"
- ErrorCodeAwsInvokeError ErrorCode = "aws_invoke_error"
- ErrorCodeModelNotFound ErrorCode = "model_not_found"
- ErrorCodePromptBlocked ErrorCode = "prompt_blocked"
- // sql error
- ErrorCodeQueryDataError ErrorCode = "query_data_error"
- ErrorCodeUpdateDataError ErrorCode = "update_data_error"
- // quota error
- ErrorCodeInsufficientUserQuota ErrorCode = "insufficient_user_quota"
- ErrorCodePreConsumeTokenQuotaFailed ErrorCode = "pre_consume_token_quota_failed"
- )
- type NewAPIError struct {
- Err error
- RelayError any
- skipRetry bool
- recordErrorLog *bool
- errorType ErrorType
- errorCode ErrorCode
- StatusCode int
- Metadata json.RawMessage
- }
- // Unwrap enables errors.Is / errors.As to work with NewAPIError by exposing the underlying error.
- func (e *NewAPIError) Unwrap() error {
- if e == nil {
- return nil
- }
- return e.Err
- }
- func (e *NewAPIError) GetErrorCode() ErrorCode {
- if e == nil {
- return ""
- }
- return e.errorCode
- }
- func (e *NewAPIError) GetErrorType() ErrorType {
- if e == nil {
- return ""
- }
- return e.errorType
- }
- func (e *NewAPIError) Error() string {
- if e == nil {
- return ""
- }
- if e.Err == nil {
- // fallback message when underlying error is missing
- return string(e.errorCode)
- }
- return e.Err.Error()
- }
- func (e *NewAPIError) MaskSensitiveError() string {
- if e == nil {
- return ""
- }
- if e.Err == nil {
- return string(e.errorCode)
- }
- errStr := e.Err.Error()
- if e.errorCode == ErrorCodeCountTokenFailed {
- return errStr
- }
- return common.MaskSensitiveInfo(errStr)
- }
- func (e *NewAPIError) SetMessage(message string) {
- e.Err = errors.New(message)
- }
- func (e *NewAPIError) ToOpenAIError() OpenAIError {
- var result OpenAIError
- switch e.errorType {
- case ErrorTypeOpenAIError:
- if openAIError, ok := e.RelayError.(OpenAIError); ok {
- result = openAIError
- }
- case ErrorTypeClaudeError:
- if claudeError, ok := e.RelayError.(ClaudeError); ok {
- result = OpenAIError{
- Message: e.Error(),
- Type: claudeError.Type,
- Param: "",
- Code: e.errorCode,
- }
- }
- default:
- result = OpenAIError{
- Message: e.Error(),
- Type: string(e.errorType),
- Param: "",
- Code: e.errorCode,
- }
- }
- if e.errorCode != ErrorCodeCountTokenFailed {
- result.Message = common.MaskSensitiveInfo(result.Message)
- }
- if result.Message == "" {
- result.Message = string(e.errorType)
- }
- return result
- }
- func (e *NewAPIError) ToClaudeError() ClaudeError {
- var result ClaudeError
- switch e.errorType {
- case ErrorTypeOpenAIError:
- if openAIError, ok := e.RelayError.(OpenAIError); ok {
- result = ClaudeError{
- Message: e.Error(),
- Type: fmt.Sprintf("%v", openAIError.Code),
- }
- }
- case ErrorTypeClaudeError:
- if claudeError, ok := e.RelayError.(ClaudeError); ok {
- result = claudeError
- }
- default:
- result = ClaudeError{
- Message: e.Error(),
- Type: string(e.errorType),
- }
- }
- if e.errorCode != ErrorCodeCountTokenFailed {
- result.Message = common.MaskSensitiveInfo(result.Message)
- }
- if result.Message == "" {
- result.Message = string(e.errorType)
- }
- return result
- }
- type NewAPIErrorOptions func(*NewAPIError)
- func NewError(err error, errorCode ErrorCode, ops ...NewAPIErrorOptions) *NewAPIError {
- var newErr *NewAPIError
- // 保留深层传递的 new err
- if errors.As(err, &newErr) {
- for _, op := range ops {
- op(newErr)
- }
- return newErr
- }
- e := &NewAPIError{
- Err: err,
- RelayError: nil,
- errorType: ErrorTypeNewAPIError,
- StatusCode: http.StatusInternalServerError,
- errorCode: errorCode,
- }
- for _, op := range ops {
- op(e)
- }
- return e
- }
- func NewOpenAIError(err error, errorCode ErrorCode, statusCode int, ops ...NewAPIErrorOptions) *NewAPIError {
- var newErr *NewAPIError
- // 保留深层传递的 new err
- if errors.As(err, &newErr) {
- if newErr.RelayError == nil {
- openaiError := OpenAIError{
- Message: newErr.Error(),
- Type: string(errorCode),
- Code: errorCode,
- }
- newErr.RelayError = openaiError
- }
- for _, op := range ops {
- op(newErr)
- }
- return newErr
- }
- openaiError := OpenAIError{
- Message: err.Error(),
- Type: string(errorCode),
- Code: errorCode,
- }
- return WithOpenAIError(openaiError, statusCode, ops...)
- }
- func InitOpenAIError(errorCode ErrorCode, statusCode int, ops ...NewAPIErrorOptions) *NewAPIError {
- openaiError := OpenAIError{
- Type: string(errorCode),
- Code: errorCode,
- }
- return WithOpenAIError(openaiError, statusCode, ops...)
- }
- func NewErrorWithStatusCode(err error, errorCode ErrorCode, statusCode int, ops ...NewAPIErrorOptions) *NewAPIError {
- e := &NewAPIError{
- Err: err,
- RelayError: OpenAIError{
- Message: err.Error(),
- Type: string(errorCode),
- },
- errorType: ErrorTypeNewAPIError,
- StatusCode: statusCode,
- errorCode: errorCode,
- }
- for _, op := range ops {
- op(e)
- }
- return e
- }
- func WithOpenAIError(openAIError OpenAIError, statusCode int, ops ...NewAPIErrorOptions) *NewAPIError {
- code, ok := openAIError.Code.(string)
- if !ok {
- if openAIError.Code != nil {
- code = fmt.Sprintf("%v", openAIError.Code)
- } else {
- code = "unknown_error"
- }
- }
- if openAIError.Type == "" {
- openAIError.Type = "upstream_error"
- }
- e := &NewAPIError{
- RelayError: openAIError,
- errorType: ErrorTypeOpenAIError,
- StatusCode: statusCode,
- Err: errors.New(openAIError.Message),
- errorCode: ErrorCode(code),
- }
- // OpenRouter
- if len(openAIError.Metadata) > 0 {
- openAIError.Message = fmt.Sprintf("%s (%s)", openAIError.Message, openAIError.Metadata)
- e.Metadata = openAIError.Metadata
- e.RelayError = openAIError
- e.Err = errors.New(openAIError.Message)
- }
- for _, op := range ops {
- op(e)
- }
- return e
- }
- func WithClaudeError(claudeError ClaudeError, statusCode int, ops ...NewAPIErrorOptions) *NewAPIError {
- if claudeError.Type == "" {
- claudeError.Type = "upstream_error"
- }
- e := &NewAPIError{
- RelayError: claudeError,
- errorType: ErrorTypeClaudeError,
- StatusCode: statusCode,
- Err: errors.New(claudeError.Message),
- errorCode: ErrorCode(claudeError.Type),
- }
- for _, op := range ops {
- op(e)
- }
- return e
- }
- func IsChannelError(err *NewAPIError) bool {
- if err == nil {
- return false
- }
- return strings.HasPrefix(string(err.errorCode), "channel:")
- }
- func IsSkipRetryError(err *NewAPIError) bool {
- if err == nil {
- return false
- }
- return err.skipRetry
- }
- func ErrOptionWithSkipRetry() NewAPIErrorOptions {
- return func(e *NewAPIError) {
- e.skipRetry = true
- }
- }
- func ErrOptionWithNoRecordErrorLog() NewAPIErrorOptions {
- return func(e *NewAPIError) {
- e.recordErrorLog = common.GetPointer(false)
- }
- }
- func ErrOptionWithHideErrMsg(replaceStr string) NewAPIErrorOptions {
- return func(e *NewAPIError) {
- if common.DebugEnabled {
- fmt.Printf("ErrOptionWithHideErrMsg: %s, origin error: %s", replaceStr, e.Err)
- }
- e.Err = errors.New(replaceStr)
- }
- }
- func IsRecordErrorLog(e *NewAPIError) bool {
- if e == nil {
- return false
- }
- if e.recordErrorLog == nil {
- // default to true if not set
- return true
- }
- return *e.recordErrorLog
- }
|