| package service |
|
|
| import ( |
| "context" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "io" |
| "math" |
| "net/http" |
| "strconv" |
| "strings" |
|
|
| "github.com/QuantumNous/new-api/common" |
| "github.com/QuantumNous/new-api/dto" |
| "github.com/QuantumNous/new-api/logger" |
| "github.com/QuantumNous/new-api/types" |
| ) |
|
|
| func MidjourneyErrorWrapper(code int, desc string) *dto.MidjourneyResponse { |
| return &dto.MidjourneyResponse{ |
| Code: code, |
| Description: desc, |
| } |
| } |
|
|
| func MidjourneyErrorWithStatusCodeWrapper(code int, desc string, statusCode int) *dto.MidjourneyResponseWithStatusCode { |
| return &dto.MidjourneyResponseWithStatusCode{ |
| StatusCode: statusCode, |
| Response: *MidjourneyErrorWrapper(code, desc), |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| func ClaudeErrorWrapper(err error, code string, statusCode int) *dto.ClaudeErrorWithStatusCode { |
| text := err.Error() |
| lowerText := strings.ToLower(text) |
| if !strings.HasPrefix(lowerText, "get file base64 from url") { |
| if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") { |
| common.SysLog(fmt.Sprintf("error: %s", text)) |
| text = "请求上游地址失败" |
| } |
| } |
| claudeError := types.ClaudeError{ |
| Message: text, |
| Type: "new_api_error", |
| } |
| return &dto.ClaudeErrorWithStatusCode{ |
| Error: claudeError, |
| StatusCode: statusCode, |
| } |
| } |
|
|
| func ClaudeErrorWrapperLocal(err error, code string, statusCode int) *dto.ClaudeErrorWithStatusCode { |
| claudeErr := ClaudeErrorWrapper(err, code, statusCode) |
| claudeErr.LocalError = true |
| return claudeErr |
| } |
|
|
| func RelayErrorHandler(ctx context.Context, resp *http.Response, showBodyWhenFail bool) (newApiErr *types.NewAPIError) { |
| newApiErr = types.InitOpenAIError(types.ErrorCodeBadResponseStatusCode, resp.StatusCode) |
|
|
| responseBody, err := io.ReadAll(resp.Body) |
| if err != nil { |
| return |
| } |
| CloseResponseBodyGracefully(resp) |
| var errResponse dto.GeneralErrorResponse |
| buildErrWithBody := func(message string) error { |
| if message == "" { |
| return fmt.Errorf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody)) |
| } |
| return fmt.Errorf("bad response status code %d, message: %s, body: %s", resp.StatusCode, message, string(responseBody)) |
| } |
|
|
| err = common.Unmarshal(responseBody, &errResponse) |
| if err != nil { |
| if showBodyWhenFail { |
| newApiErr.Err = buildErrWithBody("") |
| } else { |
| logger.LogError(ctx, fmt.Sprintf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody))) |
| newApiErr.Err = fmt.Errorf("bad response status code %d", resp.StatusCode) |
| } |
| return |
| } |
|
|
| if common.GetJsonType(errResponse.Error) == "object" { |
| |
| oaiError := errResponse.TryToOpenAIError() |
| if oaiError != nil { |
| newApiErr = types.WithOpenAIError(*oaiError, resp.StatusCode) |
| if showBodyWhenFail { |
| newApiErr.Err = buildErrWithBody(newApiErr.Error()) |
| } |
| return |
| } |
| } |
| newApiErr = types.NewOpenAIError(errors.New(errResponse.ToMessage()), types.ErrorCodeBadResponseStatusCode, resp.StatusCode) |
| if showBodyWhenFail { |
| newApiErr.Err = buildErrWithBody(newApiErr.Error()) |
| } |
| return |
| } |
|
|
| func ResetStatusCode(newApiErr *types.NewAPIError, statusCodeMappingStr string) { |
| if newApiErr == nil { |
| return |
| } |
| if statusCodeMappingStr == "" || statusCodeMappingStr == "{}" { |
| return |
| } |
| statusCodeMapping := make(map[string]any) |
| err := common.Unmarshal([]byte(statusCodeMappingStr), &statusCodeMapping) |
| if err != nil { |
| return |
| } |
| if newApiErr.StatusCode == http.StatusOK { |
| return |
| } |
| codeStr := strconv.Itoa(newApiErr.StatusCode) |
| if value, ok := statusCodeMapping[codeStr]; ok { |
| intCode, ok := parseStatusCodeMappingValue(value) |
| if !ok { |
| return |
| } |
| newApiErr.StatusCode = intCode |
| } |
| } |
|
|
| func parseStatusCodeMappingValue(value any) (int, bool) { |
| switch v := value.(type) { |
| case string: |
| if v == "" { |
| return 0, false |
| } |
| statusCode, err := strconv.Atoi(v) |
| if err != nil { |
| return 0, false |
| } |
| return statusCode, true |
| case float64: |
| if v != math.Trunc(v) { |
| return 0, false |
| } |
| return int(v), true |
| case int: |
| return v, true |
| case json.Number: |
| statusCode, err := strconv.Atoi(v.String()) |
| if err != nil { |
| return 0, false |
| } |
| return statusCode, true |
| default: |
| return 0, false |
| } |
| } |
|
|
| func TaskErrorWrapperLocal(err error, code string, statusCode int) *dto.TaskError { |
| openaiErr := TaskErrorWrapper(err, code, statusCode) |
| openaiErr.LocalError = true |
| return openaiErr |
| } |
|
|
| func TaskErrorWrapper(err error, code string, statusCode int) *dto.TaskError { |
| text := err.Error() |
| lowerText := strings.ToLower(text) |
| if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") { |
| common.SysLog(fmt.Sprintf("error: %s", text)) |
| |
| text = common.MaskSensitiveInfo(text) |
| } |
| |
| taskError := &dto.TaskError{ |
| Code: code, |
| Message: text, |
| StatusCode: statusCode, |
| Error: err, |
| } |
|
|
| return taskError |
| } |
|
|
| |
| func TaskErrorFromAPIError(apiErr *types.NewAPIError) *dto.TaskError { |
| if apiErr == nil { |
| return nil |
| } |
| return &dto.TaskError{ |
| Code: string(apiErr.GetErrorCode()), |
| Message: apiErr.Err.Error(), |
| StatusCode: apiErr.StatusCode, |
| Error: apiErr.Err, |
| } |
| } |
|
|