package httpapi import ( "net/http" "strings" "github.com/easyai/easyai-ai-gateway/apps/api/internal/publicerror" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func publicGatewayTask(task store.GatewayTask) store.GatewayTask { if task.Status == "failed" || task.Status == "cancelled" || task.ErrorCode != "" || task.ErrorMessage != "" { value := publicerror.FromFields(task.ErrorCode, firstNonEmpty(task.ErrorMessage, task.Error), taskErrorHTTPStatus(task), false) if task.PublicError != nil && task.PublicError.Code != "" && task.PublicError.Source != "" { value = *task.PublicError } value = publicerror.WithIDs(value, task.RequestID, task.ID) publicerror.Observe(value) task.PublicError = &value task.ErrorCode = value.Code task.ErrorMessage = value.Message task.Error = value.Message } task.Attempts = append([]store.TaskAttempt(nil), task.Attempts...) for index := range task.Attempts { attempt := &task.Attempts[index] if attempt.ErrorCode == "" && attempt.ErrorMessage == "" { continue } status := attempt.StatusCode if status <= 0 { status = http.StatusBadGateway } value := publicerror.FromFields(attempt.ErrorCode, attempt.ErrorMessage, status, attempt.Retryable) if attempt.PublicError != nil && attempt.PublicError.Code != "" && attempt.PublicError.Source != "" { value = *attempt.PublicError } value = publicerror.WithIDs(value, attempt.RequestID, task.ID) attempt.PublicError = &value attempt.ErrorCode = value.Code attempt.ErrorMessage = value.Message } return task } func publicTaskError(task store.GatewayTask) publicerror.Error { if task.PublicError != nil && task.PublicError.Code != "" && task.PublicError.Source != "" { return publicerror.WithIDs(*task.PublicError, task.RequestID, task.ID) } status := taskErrorHTTPStatus(task) if status <= 0 { status = http.StatusBadGateway } return publicerror.WithIDs(publicerror.FromFields(task.ErrorCode, firstNonEmpty(task.ErrorMessage, task.Error, task.Message), status, false), task.RequestID, task.ID) } func taskErrorHTTPStatus(task store.GatewayTask) int { if task.PublicError != nil && task.PublicError.Source != "" && task.PublicError.HTTPStatus >= 400 { return task.PublicError.HTTPStatus } for index := len(task.Attempts) - 1; index >= 0; index-- { if status := task.Attempts[index].StatusCode; status >= 400 && status <= 599 { return status } } if status := publicErrorInt(task.Metrics["statusCode"]); status >= 400 && status <= 599 { return status } return storedTaskErrorStatus(task.ErrorCode) } func publicErrorInt(value any) int { switch typed := value.(type) { case int: return typed case int32: return int(typed) case int64: return int(typed) case float32: return int(typed) case float64: return int(typed) default: return 0 } } func publicTaskList(items []store.GatewayTask) []store.GatewayTask { out := make([]store.GatewayTask, len(items)) for index, task := range items { out[index] = publicGatewayTask(task) } return out } func publicErrorMap(value publicerror.Error) map[string]any { out := map[string]any{ "code": value.Code, "message": value.Message, "category": value.Category, "source": value.Source, "httpStatus": value.HTTPStatus, "retryable": value.Retryable, "action": value.Action, "version": value.Version, } if value.RetryAfterSeconds > 0 { out["retryAfterSeconds"] = value.RetryAfterSeconds } if requestID := strings.TrimSpace(value.RequestID); requestID != "" { out["requestId"] = requestID } if taskID := strings.TrimSpace(value.TaskID); taskID != "" { out["taskId"] = taskID } return out } func publicErrorWithRetryAfter(value publicerror.Error, details map[string]any) publicerror.Error { if value.RetryAfterSeconds > 0 || details == nil { return value } switch typed := details["retryAfterSeconds"].(type) { case int: value.RetryAfterSeconds = typed case int32: value.RetryAfterSeconds = int(typed) case int64: value.RetryAfterSeconds = int(typed) case float64: value.RetryAfterSeconds = int(typed) } if value.RetryAfterSeconds < 0 { value.RetryAfterSeconds = 0 } return value } func safePublicErrorDetails(details map[string]any, value publicerror.Error, includePublicError bool) map[string]any { out := map[string]any{} for _, key := range []string{"param", "retryAfterSeconds", "recoveryAt", "rateLimit", "pricing"} { if item, ok := details[key]; ok && item != nil { out[key] = item } } if value.Category == "request" { for _, key := range []string{"reason", "diagnosticId"} { if item, ok := details[key]; ok && item != nil { out[key] = item } } } if includePublicError { out["publicError"] = value } return out }