package providers import ( "encoding/json" "net/http" "regexp" "strings" ) var providerRequestIDInMessage = regexp.MustCompile(`(?i)\brequest[ _-]?id\s*[:=]\s*([A-Za-z0-9_.:-]{3,128})\b`) // SanitizeFailureDetail retains a short provider explanation while removing // submitted text, media links, credentials, and unrecognized quoted values. // It is safe to call again after the original request has been discarded. func SanitizeFailureDetail(message string, requestBody []byte, secrets ...string) string { if strings.TrimSpace(message) == "" { return "" } privateValues := append([]string(nil), secrets...) var request any if json.Unmarshal(requestBody, &request) == nil { collectPrivateRequestValues(request, "", &privateValues) } for _, value := range privateValues { if value == "" { continue } message = strings.ReplaceAll(message, value, "[redacted]") encoded, _ := json.Marshal(value) if len(encoded) > 2 { message = strings.ReplaceAll(message, string(encoded[1:len(encoded)-1]), "[redacted]") } } message = minimaxDiagnosticURL.ReplaceAllString(message, "[redacted]") message = minimaxDiagnosticCredential.ReplaceAllString(message, "[redacted]") message = minimaxDiagnosticQuoted.ReplaceAllStringFunc(message, func(quoted string) string { value := quoted[1 : len(quoted)-1] if minimaxDiagnosticValue(value) != "unrecognized" { return quoted } return "[redacted]" }) message = minimaxDiagnosticLongToken.ReplaceAllString(message, "[redacted]") message = strings.Join(strings.Fields(message), " ") runes := []rune(message) if len(runes) > 512 { return string(runes[:512]) + "...[truncated]" } return message } func collectPrivateRequestValues(value any, key string, values *[]string) { switch typed := value.(type) { case map[string]any: for childKey, childValue := range typed { collectPrivateRequestValues(childValue, strings.ToLower(childKey), values) } case []any: for _, child := range typed { collectPrivateRequestValues(child, key, values) } case string: if strings.Contains(key, "prompt") || strings.Contains(key, "text") || strings.Contains(key, "url") || strings.Contains(key, "image") || strings.Contains(key, "video") || strings.Contains(key, "audio") || strings.Contains(key, "token") || strings.Contains(key, "secret") || strings.Contains(key, "key") || strings.Contains(key, "signature") || strings.Contains(key, "password") || strings.Contains(key, "credential") { *values = append(*values, typed) } } } func requestIDFromMessage(message string) string { match := providerRequestIDInMessage.FindStringSubmatch(message) if len(match) < 2 { return "" } return safeHTTPProviderDiagnosticToken(match[1], 128) } func safeProviderResponseDetail(raw, requestBody []byte, secrets ...string) string { root := record(raw) providerError := object(first(root["error"], root["Error"])) output := object(root["output"]) data := object(root["data"]) task := object(first(root["task"], data["task"])) nestedError := object(first(data["error"], task["error"], output["error"])) metadataError := object(first(object(first(root["ResponseMetadata"], root["response_metadata"]))["Error"], object(first(root["ResponseMetadata"], root["response_metadata"]))["error"])) message := stringValue(providerError["message"], providerError["Message"], nestedError["message"], nestedError["Message"], task["message"], output["message"], data["message"], metadataError["Message"], metadataError["message"], root["message"], root["Message"]) return SanitizeFailureDetail(message, requestBody, secrets...) } func successBusinessCode(code string) bool { switch strings.ToLower(strings.TrimSpace(code)) { case "", "0", "200", "ok", "success", "10000": return true } return false } // Only explicit top-level or output error indicators count as a business // rejection. A successful task can carry a normal informational message. func inspectProviderBusinessRejection(raw []byte, headers http.Header, requestBody []byte, secrets ...string) (code, requestID, detail string, rejected bool) { root := record(raw) providerError := object(first(root["error"], root["Error"])) output := object(root["output"]) data := object(root["data"]) task := object(first(root["task"], data["task"])) nestedError := object(first(data["error"], task["error"], output["error"])) code, requestID, _ = inspectHTTPProviderFailure(raw, headers) message := stringValue(providerError["message"], providerError["Message"], nestedError["message"], nestedError["Message"], output["message"], data["message"], root["message"]) if (len(providerError) > 0 || len(nestedError) > 0) && (code != "" || message != "") { rejected = true } else if rootCode := safeHTTPProviderDiagnosticToken(first(root["code"], root["Code"]), 64); rootCode != "" && !successBusinessCode(rootCode) { rejected = true } else if outputCode := safeHTTPProviderDiagnosticToken(first(output["code"], output["Code"]), 64); outputCode != "" && !successBusinessCode(outputCode) { rejected = true } else if strings.EqualFold(stringValue(root["type"]), "error") && message != "" { rejected = true } if !rejected { return "", "", "", false } return code, requestID, SanitizeFailureDetail(message, requestBody, secrets...), true } func annotateTerminalFailure(result Result, raw []byte, headers http.Header, requestBody []byte, secrets ...string) Result { if result.Status != StatusFailed && result.Status != StatusExpired && result.Status != StatusCancelled { return result } root := record(raw) business := object(first(root["Result"], root["result"])) data := object(root["data"]) businessData := object(first(business["data"], business["Data"])) output := object(root["output"]) task := object(root["task"]) providerError := object(first(task["error"], data["error"], businessData["error"], businessData["Error"], business["error"], business["Error"], output["error"], root["error"], root["Error"])) code, requestID, _ := inspectHTTPProviderFailure(raw, headers) if nestedCode := firstSafeHTTPProviderDiagnosticToken(64, providerError["code"], providerError["Code"], task["error_code"], data["error_code"], businessData["error_code"], business["error_code"]); nestedCode != "" { code = nestedCode } if nestedID := firstSafeHTTPProviderDiagnosticToken(128, providerError["request_id"], providerError["requestId"], providerError["RequestId"], task["request_id"], data["request_id"], businessData["request_id"], business["request_id"]); nestedID != "" { requestID = nestedID } message := stringValue(providerError["message"], providerError["Message"], task["error_message"], task["message"], output["message"], data["error_message"], data["message"], businessData["error_message"], businessData["message"], business["message"], root["message"], result.ErrorMessage) if requestID == "" { requestID = requestIDFromMessage(message) } result.ErrorCode = code result.RequestID = requestID result.ErrorDetail = SanitizeFailureDetail(message, requestBody, secrets...) result.ErrorMessage = SanitizeFailureDetail(result.ErrorMessage, requestBody, secrets...) return result }