package providers import ( "encoding/json" "errors" "log" "net/url" "regexp" "strconv" "strings" "unicode/utf8" ) var ( minimaxDiagnosticURL = regexp.MustCompile(`(?i)(?:https?://|data:)[^\s"'<>]+`) minimaxDiagnosticCredential = regexp.MustCompile(`(?i)(?:bearer\s+|(?:authorization|api[_-]?key|access[_-]?key|secret|token|password|signature|credential)\s*["']?\s*[:=]\s*["']?(?:bearer\s+)?)[^\s,"';}]+`) minimaxDiagnosticQuoted = regexp.MustCompile("\"(?:[^\"\\\\]|\\\\.)*\"|'[^']*'|`[^`]*`") minimaxDiagnosticLongToken = regexp.MustCompile(`[A-Za-z0-9_+/=.-]{32,}`) ) // Preserve the upstream validation explanation, but remove request content, // credentials, URLs and arbitrary quoted values before it reaches logs or DB. func minimaxSafeFailureDetail(responseBody, requestBody []byte, apiKey string) string { root := record(responseBody) message := stringValue(object(root["error"])["message"], root["message"]) if message == "" { return "" } privateValues := []string{apiKey} request := record(requestBody) content, _ := request["content"].([]any) for _, item := range content { entry := object(item) privateValues = append(privateValues, stringValue(entry["text"])) for _, kind := range []string{"image_url", "video_url", "audio_url"} { privateValues = append(privateValues, stringValue(object(entry[kind])["url"])) } } 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 { message = string(runes[:512]) + "...[truncated]" } return message } func logMinimaxFailedRequest(request Request, config Config, err error) { var failure *ProviderError if !errors.As(err, &failure) { return } // All strings except the request ID are fixed enums. In particular, never // log custom endpoints, prompts or even the hosts of reference image URLs. endpointClass := "custom" basePathClass := "custom" if endpoint, parseErr := url.Parse(config.BaseURL); parseErr == nil { if endpoint.Hostname() == "api.minimax.cn" { endpointClass = "official-cn" } switch strings.Trim(endpoint.Path, "/") { case "": basePathClass = "root" case "v1", "v2": basePathClass = "version-prefix" } } imageItems := 0 if len(request.Materials) > 0 || len(request.InputURLs) > 0 { imageItems = 1 } duration := "invalid" if encoded, encodeErr := json.Marshal(request.Settings["duration"]); encodeErr == nil { if value, parseErr := strconv.Atoi(string(encoded)); parseErr == nil && value >= 4 && value <= 15 { duration = strconv.Itoa(value) } } log.Printf( "zhinian-api MiniMax submit request summary requestId=%q model=%q resolution=%q duration=%s ratio=%q textItems=1 promptChars=%d imageItems=%d endpointClass=%s basePathClass=%s", failure.RequestID, minimaxDiagnosticValue(requestModel(request, config.Model)), minimaxDiagnosticValue(stringValue(request.Settings["resolution"])), duration, minimaxDiagnosticValue(stringValue(request.Settings["ratio"])), utf8.RuneCountInString(request.Prompt), imageItems, endpointClass, basePathClass, ) } func minimaxDiagnosticValue(value string) string { switch value { case MinimaxH3Model, "MiniMax-H3-Max", "768P", "2K", "480P", "adaptive", "21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "model", "content", "text", "prompt", "resolution", "duration", "ratio", "image_url", "video_url", "audio_url", "role", "first_frame", "last_frame", "aigc_watermark": return value default: return "unrecognized" } }