115 lines
4.1 KiB
Go
115 lines
4.1 KiB
Go
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"
|
|
}
|
|
}
|