区分请求资源 base64 格式并按平台恢复
This commit is contained in:
@@ -59,8 +59,16 @@ func (s *Service) slimParameterPreprocessingLog(task store.GatewayTask, log para
|
||||
return log
|
||||
}
|
||||
|
||||
func (s *Service) hydrateProviderRequestAssets(ctx context.Context, body map[string]any) (map[string]any, error) {
|
||||
value, err := s.hydrateProviderRequestAssetValue(ctx, body, nil)
|
||||
type requestAssetHydrationStyle string
|
||||
|
||||
const (
|
||||
requestAssetHydrateURL requestAssetHydrationStyle = "url"
|
||||
requestAssetHydrateDataURL requestAssetHydrationStyle = "data_url"
|
||||
requestAssetHydrateRawBase64 requestAssetHydrationStyle = "raw_base64"
|
||||
)
|
||||
|
||||
func (s *Service) hydrateProviderRequestAssets(ctx context.Context, body map[string]any, candidate store.RuntimeModelCandidate) (map[string]any, error) {
|
||||
value, err := s.hydrateProviderRequestAssetValue(ctx, body, nil, candidate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,15 +79,15 @@ func (s *Service) hydrateProviderRequestAssets(ctx context.Context, body map[str
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Service) hydrateProviderRequestAssetValue(ctx context.Context, value any, path []string) (any, error) {
|
||||
func (s *Service) hydrateProviderRequestAssetValue(ctx context.Context, value any, path []string, candidate store.RuntimeModelCandidate) (any, error) {
|
||||
switch typed := value.(type) {
|
||||
case map[string]any:
|
||||
if ref, ok := typed["assetRef"].(map[string]any); ok {
|
||||
return s.hydrateProviderRequestAssetRef(ctx, ref, path)
|
||||
return s.hydrateProviderRequestAssetRef(ctx, ref, path, candidate)
|
||||
}
|
||||
next := make(map[string]any, len(typed))
|
||||
for key, item := range typed {
|
||||
hydrated, err := s.hydrateProviderRequestAssetValue(ctx, item, append(path, key))
|
||||
hydrated, err := s.hydrateProviderRequestAssetValue(ctx, item, append(path, key), candidate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,7 +97,7 @@ func (s *Service) hydrateProviderRequestAssetValue(ctx context.Context, value an
|
||||
case []any:
|
||||
next := make([]any, 0, len(typed))
|
||||
for index, item := range typed {
|
||||
hydrated, err := s.hydrateProviderRequestAssetValue(ctx, item, append(path, fmt.Sprintf("[%d]", index)))
|
||||
hydrated, err := s.hydrateProviderRequestAssetValue(ctx, item, append(path, fmt.Sprintf("[%d]", index)), candidate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -101,12 +109,23 @@ func (s *Service) hydrateProviderRequestAssetValue(ctx context.Context, value an
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) hydrateProviderRequestAssetRef(ctx context.Context, ref map[string]any, path []string) (any, error) {
|
||||
func (s *Service) hydrateProviderRequestAssetRef(ctx context.Context, ref map[string]any, path []string, candidate store.RuntimeModelCandidate) (any, error) {
|
||||
asset, err := s.resolveRequestAsset(ctx, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if providerFieldNeedsBase64(path) {
|
||||
switch requestAssetHydrationForField(path, candidate) {
|
||||
case requestAssetHydrateDataURL:
|
||||
payload, err := s.readRequestAssetBytes(ctx, asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentType := strings.TrimSpace(asset.ContentType)
|
||||
if contentType == "" {
|
||||
contentType = "application/octet-stream"
|
||||
}
|
||||
return "data:" + contentType + ";base64," + base64.StdEncoding.EncodeToString(payload), nil
|
||||
case requestAssetHydrateRawBase64:
|
||||
payload, err := s.readRequestAssetBytes(ctx, asset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -213,15 +232,28 @@ func (s *Service) localPathFromRequestAssetURL(value string) string {
|
||||
return filepath.Join(storageDir, fileName)
|
||||
}
|
||||
|
||||
func providerFieldNeedsBase64(path []string) bool {
|
||||
if len(path) == 0 {
|
||||
return false
|
||||
func requestAssetHydrationForField(path []string, candidate store.RuntimeModelCandidate) requestAssetHydrationStyle {
|
||||
if providerFieldNeedsRawBase64(path) {
|
||||
return requestAssetHydrateRawBase64
|
||||
}
|
||||
key := strings.ToLower(strings.Trim(path[len(path)-1], "[]"))
|
||||
parent := ""
|
||||
if len(path) > 1 {
|
||||
parent = strings.ToLower(strings.Trim(path[len(path)-2], "[]"))
|
||||
if mediaURLFieldNeedsHydration(path) {
|
||||
if style := configuredRequestAssetMediaURLHydration(candidate, requestAssetMediaURLKind(path)); style != "" {
|
||||
return style
|
||||
}
|
||||
if providerMediaURLNeedsDataURL(candidate) {
|
||||
return requestAssetHydrateDataURL
|
||||
}
|
||||
}
|
||||
return requestAssetHydrateURL
|
||||
}
|
||||
|
||||
func mediaURLFieldNeedsHydration(path []string) bool {
|
||||
key, parent := requestAssetFieldPath(path)
|
||||
return key == "url" && (parent == "image_url" || parent == "audio_url" || parent == "video_url" || parent == "file_url")
|
||||
}
|
||||
|
||||
func providerFieldNeedsRawBase64(path []string) bool {
|
||||
key, parent := requestAssetFieldPath(path)
|
||||
return key == "b64_json" ||
|
||||
key == "base64" ||
|
||||
key == "b64" ||
|
||||
@@ -230,6 +262,116 @@ func providerFieldNeedsBase64(path []string) bool {
|
||||
(parent == "input_audio" && key == "data")
|
||||
}
|
||||
|
||||
func requestAssetMediaURLKind(path []string) string {
|
||||
_, parent := requestAssetFieldPath(path)
|
||||
switch parent {
|
||||
case "image_url":
|
||||
return "image"
|
||||
case "audio_url":
|
||||
return "audio"
|
||||
case "video_url":
|
||||
return "video"
|
||||
case "file_url":
|
||||
return "file"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func configuredRequestAssetMediaURLHydration(candidate store.RuntimeModelCandidate, kind string) requestAssetHydrationStyle {
|
||||
keys := []string{}
|
||||
switch kind {
|
||||
case "image":
|
||||
keys = append(keys, "requestAssetImageURLFormat", "request_asset_image_url_format")
|
||||
case "audio":
|
||||
keys = append(keys, "requestAssetAudioURLFormat", "request_asset_audio_url_format")
|
||||
case "video":
|
||||
keys = append(keys, "requestAssetVideoURLFormat", "request_asset_video_url_format")
|
||||
case "file":
|
||||
keys = append(keys, "requestAssetFileURLFormat", "request_asset_file_url_format")
|
||||
}
|
||||
keys = append(keys,
|
||||
"requestAssetMediaURLFormat",
|
||||
"request_asset_media_url_format",
|
||||
"mediaURLAssetFormat",
|
||||
"media_url_asset_format",
|
||||
)
|
||||
for _, key := range keys {
|
||||
if style := requestAssetHydrationStyleFromString(stringFromAny(candidate.PlatformConfig[key])); style != "" {
|
||||
return style
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func requestAssetHydrationStyleFromString(value string) requestAssetHydrationStyle {
|
||||
normalized := strings.ToLower(strings.TrimSpace(value))
|
||||
normalized = strings.ReplaceAll(normalized, "-", "_")
|
||||
normalized = strings.ReplaceAll(normalized, " ", "_")
|
||||
switch normalized {
|
||||
case "url", "remote_url", "public_url":
|
||||
return requestAssetHydrateURL
|
||||
case "data_url", "dataurl", "prefixed_base64", "base64_with_prefix", "base64_with_data_url_prefix":
|
||||
return requestAssetHydrateDataURL
|
||||
case "raw_base64", "base64", "bare_base64", "naked_base64":
|
||||
return requestAssetHydrateRawBase64
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func providerMediaURLNeedsDataURL(candidate store.RuntimeModelCandidate) bool {
|
||||
for _, name := range []string{candidate.Provider, candidate.SpecType, candidate.PlatformKey} {
|
||||
switch normalizeProviderKey(name) {
|
||||
case "openai", "volces", "volces_openai", "gemini", "vidu":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func normalizeProviderKey(value string) string {
|
||||
normalized := strings.ToLower(strings.TrimSpace(value))
|
||||
normalized = strings.ReplaceAll(normalized, "-", "_")
|
||||
normalized = strings.ReplaceAll(normalized, " ", "_")
|
||||
return normalized
|
||||
}
|
||||
|
||||
func requestAssetFieldPath(path []string) (string, string) {
|
||||
if len(path) == 0 {
|
||||
return "", ""
|
||||
}
|
||||
names := make([]string, 0, len(path))
|
||||
for _, segment := range path {
|
||||
name := strings.ToLower(strings.TrimSpace(strings.Trim(segment, "[]")))
|
||||
if name == "" || requestAssetPathSegmentIsIndex(name) {
|
||||
continue
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
if len(names) == 0 {
|
||||
return "", ""
|
||||
}
|
||||
key := names[len(names)-1]
|
||||
parent := ""
|
||||
if len(names) > 1 {
|
||||
parent = names[len(names)-2]
|
||||
}
|
||||
return key, parent
|
||||
}
|
||||
|
||||
func requestAssetPathSegmentIsIndex(value string) bool {
|
||||
if value == "" {
|
||||
return false
|
||||
}
|
||||
for _, char := range value {
|
||||
if char < '0' || char > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func requestAssetIsExpired(asset store.RequestAsset, now time.Time) bool {
|
||||
if asset.ExpiredAt != nil {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user