error.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "math"
  9. "net/http"
  10. "strconv"
  11. "strings"
  12. "github.com/QuantumNous/new-api/common"
  13. "github.com/QuantumNous/new-api/dto"
  14. "github.com/QuantumNous/new-api/logger"
  15. "github.com/QuantumNous/new-api/types"
  16. )
  17. func MidjourneyErrorWrapper(code int, desc string) *dto.MidjourneyResponse {
  18. return &dto.MidjourneyResponse{
  19. Code: code,
  20. Description: desc,
  21. }
  22. }
  23. func MidjourneyErrorWithStatusCodeWrapper(code int, desc string, statusCode int) *dto.MidjourneyResponseWithStatusCode {
  24. return &dto.MidjourneyResponseWithStatusCode{
  25. StatusCode: statusCode,
  26. Response: *MidjourneyErrorWrapper(code, desc),
  27. }
  28. }
  29. //// OpenAIErrorWrapper wraps an error into an OpenAIErrorWithStatusCode
  30. //func OpenAIErrorWrapper(err error, code string, statusCode int) *dto.OpenAIErrorWithStatusCode {
  31. // text := err.Error()
  32. // lowerText := strings.ToLower(text)
  33. // if !strings.HasPrefix(lowerText, "get file base64 from url") && !strings.HasPrefix(lowerText, "mime type is not supported") {
  34. // if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") {
  35. // common.SysLog(fmt.Sprintf("error: %s", text))
  36. // text = "请求上游地址失败"
  37. // }
  38. // }
  39. // openAIError := dto.OpenAIError{
  40. // Message: text,
  41. // Type: "new_api_error",
  42. // Code: code,
  43. // }
  44. // return &dto.OpenAIErrorWithStatusCode{
  45. // Error: openAIError,
  46. // StatusCode: statusCode,
  47. // }
  48. //}
  49. //
  50. //func OpenAIErrorWrapperLocal(err error, code string, statusCode int) *dto.OpenAIErrorWithStatusCode {
  51. // openaiErr := OpenAIErrorWrapper(err, code, statusCode)
  52. // openaiErr.LocalError = true
  53. // return openaiErr
  54. //}
  55. func ClaudeErrorWrapper(err error, code string, statusCode int) *dto.ClaudeErrorWithStatusCode {
  56. text := err.Error()
  57. lowerText := strings.ToLower(text)
  58. if !strings.HasPrefix(lowerText, "get file base64 from url") {
  59. if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") {
  60. common.SysLog(fmt.Sprintf("error: %s", text))
  61. text = "请求上游地址失败"
  62. }
  63. }
  64. claudeError := types.ClaudeError{
  65. Message: text,
  66. Type: "new_api_error",
  67. }
  68. return &dto.ClaudeErrorWithStatusCode{
  69. Error: claudeError,
  70. StatusCode: statusCode,
  71. }
  72. }
  73. func ClaudeErrorWrapperLocal(err error, code string, statusCode int) *dto.ClaudeErrorWithStatusCode {
  74. claudeErr := ClaudeErrorWrapper(err, code, statusCode)
  75. claudeErr.LocalError = true
  76. return claudeErr
  77. }
  78. func RelayErrorHandler(ctx context.Context, resp *http.Response, showBodyWhenFail bool) (newApiErr *types.NewAPIError) {
  79. newApiErr = types.InitOpenAIError(types.ErrorCodeBadResponseStatusCode, resp.StatusCode)
  80. responseBody, err := io.ReadAll(resp.Body)
  81. if err != nil {
  82. return
  83. }
  84. CloseResponseBodyGracefully(resp)
  85. var errResponse dto.GeneralErrorResponse
  86. buildErrWithBody := func(message string) error {
  87. if message == "" {
  88. return fmt.Errorf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody))
  89. }
  90. return fmt.Errorf("bad response status code %d, message: %s, body: %s", resp.StatusCode, message, string(responseBody))
  91. }
  92. err = common.Unmarshal(responseBody, &errResponse)
  93. if err != nil {
  94. if showBodyWhenFail {
  95. newApiErr.Err = buildErrWithBody("")
  96. } else {
  97. logger.LogError(ctx, fmt.Sprintf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody)))
  98. newApiErr.Err = fmt.Errorf("bad response status code %d", resp.StatusCode)
  99. }
  100. return
  101. }
  102. if common.GetJsonType(errResponse.Error) == "object" {
  103. // General format error (OpenAI, Anthropic, Gemini, etc.)
  104. oaiError := errResponse.TryToOpenAIError()
  105. if oaiError != nil {
  106. newApiErr = types.WithOpenAIError(*oaiError, resp.StatusCode)
  107. if showBodyWhenFail {
  108. newApiErr.Err = buildErrWithBody(newApiErr.Error())
  109. }
  110. return
  111. }
  112. }
  113. newApiErr = types.NewOpenAIError(errors.New(errResponse.ToMessage()), types.ErrorCodeBadResponseStatusCode, resp.StatusCode)
  114. if showBodyWhenFail {
  115. newApiErr.Err = buildErrWithBody(newApiErr.Error())
  116. }
  117. return
  118. }
  119. func ResetStatusCode(newApiErr *types.NewAPIError, statusCodeMappingStr string) {
  120. if newApiErr == nil {
  121. return
  122. }
  123. if statusCodeMappingStr == "" || statusCodeMappingStr == "{}" {
  124. return
  125. }
  126. statusCodeMapping := make(map[string]any)
  127. err := common.Unmarshal([]byte(statusCodeMappingStr), &statusCodeMapping)
  128. if err != nil {
  129. return
  130. }
  131. if newApiErr.StatusCode == http.StatusOK {
  132. return
  133. }
  134. codeStr := strconv.Itoa(newApiErr.StatusCode)
  135. if value, ok := statusCodeMapping[codeStr]; ok {
  136. intCode, ok := parseStatusCodeMappingValue(value)
  137. if !ok {
  138. return
  139. }
  140. newApiErr.StatusCode = intCode
  141. }
  142. }
  143. func parseStatusCodeMappingValue(value any) (int, bool) {
  144. switch v := value.(type) {
  145. case string:
  146. if v == "" {
  147. return 0, false
  148. }
  149. statusCode, err := strconv.Atoi(v)
  150. if err != nil {
  151. return 0, false
  152. }
  153. return statusCode, true
  154. case float64:
  155. if v != math.Trunc(v) {
  156. return 0, false
  157. }
  158. return int(v), true
  159. case int:
  160. return v, true
  161. case json.Number:
  162. statusCode, err := strconv.Atoi(v.String())
  163. if err != nil {
  164. return 0, false
  165. }
  166. return statusCode, true
  167. default:
  168. return 0, false
  169. }
  170. }
  171. func TaskErrorWrapperLocal(err error, code string, statusCode int) *dto.TaskError {
  172. openaiErr := TaskErrorWrapper(err, code, statusCode)
  173. openaiErr.LocalError = true
  174. return openaiErr
  175. }
  176. func TaskErrorWrapper(err error, code string, statusCode int) *dto.TaskError {
  177. text := err.Error()
  178. lowerText := strings.ToLower(text)
  179. if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") {
  180. common.SysLog(fmt.Sprintf("error: %s", text))
  181. //text = "请求上游地址失败"
  182. text = common.MaskSensitiveInfo(text)
  183. }
  184. //避免暴露内部错误
  185. taskError := &dto.TaskError{
  186. Code: code,
  187. Message: text,
  188. StatusCode: statusCode,
  189. Error: err,
  190. }
  191. return taskError
  192. }