fix(runner): 修复转存后残留二进制误判

统一结果二进制探测与上传规则,避免将 thinking_bytes 等上游元数据误判为待转存媒体,同时保留显式媒体字段和签名识别。\n\n补充不含原始内容的安全诊断,并兼容对象存储前缀旧字段;修正真实 OSS 验收脚本使用的正式字段。\n\n验证:Go 全量测试、go vet、迁移安全检查、真实 Gemini 上游响应及阿里云 OSS 转存均通过。
This commit is contained in:
2026-08-04 14:33:04 +08:00
parent 394cc6288f
commit 5c7d6ac9aa
7 changed files with 142 additions and 22 deletions
+53 -1
View File
@@ -10,6 +10,7 @@ import (
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
@@ -106,6 +107,52 @@ func TaskResultHasInlineBinary(result map[string]any) bool {
return localBinaryValueHasPayload(result, "", nil, 0)
}
func taskResultInlineBinaryDiagnostics(result map[string]any) []string {
diagnostics := make([]string, 0, 4)
appendInlineBinaryDiagnostics(result, "", nil, "$", 0, &diagnostics)
return diagnostics
}
func appendInlineBinaryDiagnostics(value any, key string, siblings map[string]any, path string, depth int, diagnostics *[]string) {
if depth >= localBinaryMaxDepth || len(*diagnostics) >= 8 {
return
}
switch typed := value.(type) {
case map[string]any:
if payload, contentType, ok := localBufferObjectBytes(typed); ok {
*diagnostics = append(*diagnostics, fmt.Sprintf("%s buffer bytes=%d contentType=%s", path, len(payload), contentType))
return
}
keys := make([]string, 0, len(typed))
for childKey := range typed {
keys = append(keys, childKey)
}
sort.Strings(keys)
for _, childKey := range keys {
appendInlineBinaryDiagnostics(typed[childKey], childKey, typed, path+"."+childKey, depth+1, diagnostics)
}
case []any:
if localBinaryKey(key) {
if payload, ok := bytesFromNumberArray(typed); ok {
*diagnostics = append(*diagnostics, fmt.Sprintf("%s number-array bytes=%d", path, len(payload)))
return
}
}
for index, child := range typed {
appendInlineBinaryDiagnostics(child, key, siblings, fmt.Sprintf("%s[%d]", path, index), depth+1, diagnostics)
}
case []byte:
if len(typed) > 0 {
*diagnostics = append(*diagnostics, fmt.Sprintf("%s bytes=%d", path, len(typed)))
}
case string:
payload, contentType, encoding, ok := localBinaryStringBytes(key, typed, siblings)
if ok {
*diagnostics = append(*diagnostics, fmt.Sprintf("%s string bytes=%d contentType=%s encoding=%s", path, len(payload), contentType, encoding))
}
}
}
func localBinaryValueHasPayload(value any, key string, siblings map[string]any, depth int) bool {
if depth >= localBinaryMaxDepth {
return false
@@ -518,7 +565,12 @@ func localBinaryStringBytes(key string, value string, siblings map[string]any) (
return nil, "", "", false
}
contentType := firstNonEmptyString(mediaContentTypeFromItem(siblings), defaultContentTypeForRawMediaKey(key))
if !strict && contentType == "" {
// Keys such as thinking_bytes and signature_buffer can carry opaque provider
// metadata. Treat them as generated media only when a sibling content type or
// the payload signature proves that they are media/document bytes. Explicit
// Base64 media keys (b64_json, image_data, binary_data_base64, ...) retain the
// strict behavior expected by compatible image protocols.
if contentType == "" && (!strict || !generatedRawDataMediaPayloadKey(key)) {
contentType = detectGeneratedAssetContentType(payload)
if !generatedContentTypeIsMedia(contentType) && !generatedContentTypeIsDocument(contentType) {
return nil, "", "", false