improve:优化视频模型额外参数处理逻辑
This commit is contained in:
1 parent
8f3fe333c7
commit
2e7f0beca3
4 files changed
+178
-160
No files matched your search
@@ -44,6 +44,19 @@ type dashScopeVideoParams struct {
|
||||
Seed *int `json:"seed,omitempty"`
|
||||
}
|
||||
|
||||
// aliMetadata Ali DashScope metadata 参数结构体(用于 UnmarshalMetadata 映射)
|
||||
type aliMetadata struct {
|
||||
NegativePrompt string `json:"negative_prompt,omitempty"`
|
||||
AudioURL string `json:"audio_url,omitempty"`
|
||||
Resolution string `json:"resolution,omitempty"`
|
||||
Ratio string `json:"ratio,omitempty"`
|
||||
Size string `json:"size,omitempty"`
|
||||
Duration *int `json:"duration,omitempty"`
|
||||
PromptExtend *bool `json:"prompt_extend,omitempty"`
|
||||
Watermark *bool `json:"watermark,omitempty"`
|
||||
Seed *int `json:"seed,omitempty"`
|
||||
}
|
||||
|
||||
// dashScopeImageRequest DashScope 图片生成请求
|
||||
type dashScopeImageRequest struct {
|
||||
Model string `json:"model"`
|
||||
@@ -130,10 +143,12 @@ func (a *AliAdaptor) EstimateBilling(_ context.Context, _ *common.RelayInfo, bod
|
||||
|
||||
if a.isVideo {
|
||||
// duration 影响计费
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
if v, ok := metadata["duration"].(float64); ok && v > 0 {
|
||||
ratios["duration"] = v
|
||||
}
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
var meta struct {
|
||||
Duration *int `json:"duration"`
|
||||
}
|
||||
if taskchannel.UnmarshalMetadata(metadata, &meta) == nil && meta.Duration != nil && *meta.Duration > 0 {
|
||||
ratios["duration"] = float64(*meta.Duration)
|
||||
}
|
||||
if seconds, ok := req["seconds"].(string); ok {
|
||||
if d, err := parseInt(seconds); err == nil && d > 0 {
|
||||
@@ -202,55 +217,33 @@ func (a *AliAdaptor) buildVideoRequest(info *common.RelayInfo, req map[string]an
|
||||
}
|
||||
|
||||
params := &dashScopeVideoParams{}
|
||||
hasParams := false
|
||||
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
if v, ok := metadata["negative_prompt"].(string); ok {
|
||||
dsReq.Input.NegativePrompt = v
|
||||
}
|
||||
if v, ok := metadata["audio_url"].(string); ok {
|
||||
dsReq.Input.AudioURL = v
|
||||
}
|
||||
if v, ok := metadata["resolution"].(string); ok {
|
||||
params.Resolution = v
|
||||
hasParams = true
|
||||
}
|
||||
if v, ok := metadata["ratio"].(string); ok {
|
||||
params.Ratio = v
|
||||
hasParams = true
|
||||
}
|
||||
if v, ok := metadata["size"].(string); ok {
|
||||
params.Size = strings.ReplaceAll(v, "x", "*")
|
||||
hasParams = true
|
||||
}
|
||||
if v, ok := metadata["duration"].(float64); ok && v > 0 {
|
||||
d := int(v)
|
||||
params.Duration = &d
|
||||
hasParams = true
|
||||
}
|
||||
if v, ok := metadata["prompt_extend"].(bool); ok {
|
||||
params.PromptExtend = &v
|
||||
hasParams = true
|
||||
}
|
||||
if v, ok := metadata["watermark"].(bool); ok {
|
||||
params.Watermark = &v
|
||||
hasParams = true
|
||||
}
|
||||
if v, ok := metadata["seed"].(float64); ok {
|
||||
s := int(v)
|
||||
params.Seed = &s
|
||||
hasParams = true
|
||||
}
|
||||
// 从 metadata 提取参数
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
var meta aliMetadata
|
||||
if err := taskchannel.UnmarshalMetadata(metadata, &meta); err != nil {
|
||||
return nil, fmt.Errorf("parse ali metadata: %w", err)
|
||||
}
|
||||
dsReq.Input.NegativePrompt = meta.NegativePrompt
|
||||
dsReq.Input.AudioURL = meta.AudioURL
|
||||
params.Resolution = meta.Resolution
|
||||
params.Ratio = meta.Ratio
|
||||
if meta.Size != "" {
|
||||
params.Size = strings.ReplaceAll(meta.Size, "x", "*")
|
||||
}
|
||||
params.Duration = meta.Duration
|
||||
params.PromptExtend = meta.PromptExtend
|
||||
params.Watermark = meta.Watermark
|
||||
params.Seed = meta.Seed
|
||||
|
||||
if seconds, ok := req["seconds"].(string); ok {
|
||||
if d, err := parseInt(seconds); err == nil && d > 0 {
|
||||
params.Duration = &d
|
||||
hasParams = true
|
||||
}
|
||||
}
|
||||
|
||||
if hasParams {
|
||||
if params.Resolution != "" || params.Ratio != "" || params.Size != "" ||
|
||||
params.Duration != nil || params.PromptExtend != nil || params.Watermark != nil || params.Seed != nil {
|
||||
dsReq.Parameters = params
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package taskchannel
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ExtractMetadata 从已解析的请求 map 中提取 metadata 子 map。
|
||||
// 同时删除 "model" 字段防止通过 metadata 覆盖计费模型。
|
||||
func ExtractMetadata(req map[string]any) map[string]any {
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
meta, ok := req["metadata"].(map[string]any)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
delete(meta, "model")
|
||||
return meta
|
||||
}
|
||||
|
||||
// UnmarshalMetadata 将 metadata map 通过 JSON 往返反序列化到目标结构体。
|
||||
// metadata 为 nil 时直接返回 nil,不修改 target。
|
||||
func UnmarshalMetadata(metadata map[string]any, target any) error {
|
||||
if len(metadata) == 0 {
|
||||
return nil
|
||||
}
|
||||
metaBytes, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal metadata failed: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(metaBytes, target); err != nil {
|
||||
return fmt.Errorf("unmarshal metadata failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultString 返回 val(非空时),否则返回 fallback。
|
||||
func DefaultString(val, fallback string) string {
|
||||
if val == "" {
|
||||
return fallback
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// DefaultInt 返回 val(非零时),否则返回 fallback。
|
||||
func DefaultInt(val, fallback int) int {
|
||||
if val == 0 {
|
||||
return fallback
|
||||
}
|
||||
return val
|
||||
}
|
||||
@@ -51,6 +51,19 @@ type klingRequest struct {
|
||||
CameraControl map[string]any `json:"camera_control,omitempty"`
|
||||
}
|
||||
|
||||
// klingMetadata 可灵 metadata 参数结构体(用于 UnmarshalMetadata 映射)
|
||||
type klingMetadata struct {
|
||||
NegativePrompt string `json:"negative_prompt,omitempty"`
|
||||
Mode string `json:"mode,omitempty"`
|
||||
Duration any `json:"duration,omitempty"` // string 或 number
|
||||
AspectRatio string `json:"aspect_ratio,omitempty"`
|
||||
CfgScale *float64 `json:"cfg_scale,omitempty"`
|
||||
Image string `json:"image,omitempty"`
|
||||
ImageTail string `json:"image_tail,omitempty"`
|
||||
Sound string `json:"sound,omitempty"`
|
||||
CameraControl map[string]any `json:"camera_control,omitempty"`
|
||||
}
|
||||
|
||||
// klingSubmitResponse 可灵任务提交响应
|
||||
type klingSubmitResponse struct {
|
||||
Code int `json:"code"`
|
||||
@@ -116,10 +129,12 @@ func (a *KlingAdaptor) EstimateBilling(_ context.Context, _ *common.RelayInfo, b
|
||||
}
|
||||
|
||||
// 时长加价:10s = 2x
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
if v, ok := metadata["duration"].(float64); ok && v >= 10 {
|
||||
ratios["duration_multiplier"] = 2.0
|
||||
}
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
var meta struct {
|
||||
Duration float64 `json:"duration"`
|
||||
}
|
||||
if taskchannel.UnmarshalMetadata(metadata, &meta) == nil && meta.Duration >= 10 {
|
||||
ratios["duration_multiplier"] = 2.0
|
||||
}
|
||||
if seconds, ok := req["seconds"].(string); ok {
|
||||
if d, err := parseInt(seconds); err == nil && d >= 10 {
|
||||
@@ -143,10 +158,9 @@ func determineTaskType(req map[string]any) string {
|
||||
}
|
||||
}
|
||||
// 有图片输入则 image2video
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
if _, hasImg := metadata["image"]; hasImg {
|
||||
return "image2video"
|
||||
}
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
if _, hasImg := metadata["image"]; hasImg {
|
||||
return "image2video"
|
||||
}
|
||||
if _, hasImg := req["images"].([]any); hasImg {
|
||||
return "image2video"
|
||||
@@ -204,36 +218,25 @@ func (a *KlingAdaptor) BuildRequestBody(_ context.Context, info *common.RelayInf
|
||||
}
|
||||
|
||||
// 从 metadata 提取参数
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
if v, ok := metadata["negative_prompt"].(string); ok {
|
||||
kReq.NegativePrompt = v
|
||||
}
|
||||
if v, ok := metadata["mode"].(string); ok {
|
||||
kReq.Mode = v
|
||||
}
|
||||
if v, ok := metadata["duration"].(string); ok {
|
||||
kReq.Duration = v
|
||||
} else if v, ok := metadata["duration"].(float64); ok {
|
||||
kReq.Duration = fmt.Sprintf("%d", int(v))
|
||||
}
|
||||
if v, ok := metadata["aspect_ratio"].(string); ok {
|
||||
kReq.AspectRatio = v
|
||||
}
|
||||
if v, ok := metadata["cfg_scale"].(float64); ok {
|
||||
kReq.CfgScale = &v
|
||||
}
|
||||
if v, ok := metadata["image"].(string); ok {
|
||||
kReq.Image = v
|
||||
}
|
||||
if v, ok := metadata["image_tail"].(string); ok {
|
||||
kReq.ImageTail = v
|
||||
}
|
||||
if v, ok := metadata["sound"].(string); ok {
|
||||
kReq.Sound = v
|
||||
}
|
||||
if v, ok := metadata["camera_control"].(map[string]any); ok {
|
||||
kReq.CameraControl = v
|
||||
}
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
var meta klingMetadata
|
||||
if err := taskchannel.UnmarshalMetadata(metadata, &meta); err != nil {
|
||||
return nil, fmt.Errorf("parse kling metadata: %w", err)
|
||||
}
|
||||
kReq.NegativePrompt = meta.NegativePrompt
|
||||
kReq.Mode = meta.Mode
|
||||
kReq.AspectRatio = meta.AspectRatio
|
||||
kReq.CfgScale = meta.CfgScale
|
||||
kReq.Image = meta.Image
|
||||
kReq.ImageTail = meta.ImageTail
|
||||
kReq.Sound = meta.Sound
|
||||
kReq.CameraControl = meta.CameraControl
|
||||
// duration 支持 string 或 number 两种类型
|
||||
switch v := meta.Duration.(type) {
|
||||
case string:
|
||||
kReq.Duration = v
|
||||
case float64:
|
||||
kReq.Duration = fmt.Sprintf("%d", int(v))
|
||||
}
|
||||
|
||||
// seconds 字段映射到 duration
|
||||
|
||||
@@ -48,6 +48,18 @@ type submitRequest struct {
|
||||
ReturnLastFrame *bool `json:"return_last_frame,omitempty"`
|
||||
}
|
||||
|
||||
// volcengineMetadata 火山引擎 metadata 参数结构体(用于 UnmarshalMetadata 映射)
|
||||
type volcengineMetadata struct {
|
||||
Content []contentItem `json:"content,omitempty"`
|
||||
Resolution string `json:"resolution,omitempty"`
|
||||
Ratio string `json:"ratio,omitempty"`
|
||||
Duration *int `json:"duration,omitempty"`
|
||||
Seed *int `json:"seed,omitempty"`
|
||||
Watermark *bool `json:"watermark,omitempty"`
|
||||
GenerateAudio *bool `json:"generate_audio,omitempty"`
|
||||
ReturnLastFrame *bool `json:"return_last_frame,omitempty"`
|
||||
}
|
||||
|
||||
// submitResponse 提交任务响应
|
||||
type submitResponse struct {
|
||||
ID string `json:"id"`
|
||||
@@ -186,65 +198,20 @@ func (a *VolcengineVideoAdaptor) BuildRequestBody(_ context.Context, info *commo
|
||||
}
|
||||
|
||||
// 从 metadata 中提取额外参数
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
// content 数组中的额外项(如 video_url、audio_url)
|
||||
if contentRaw, ok := metadata["content"].([]any); ok {
|
||||
for _, item := range contentRaw {
|
||||
if itemMap, ok := item.(map[string]any); ok {
|
||||
ci := contentItem{}
|
||||
if t, ok := itemMap["type"].(string); ok {
|
||||
ci.Type = t
|
||||
}
|
||||
if t, ok := itemMap["text"].(string); ok {
|
||||
ci.Text = t
|
||||
}
|
||||
if imgURL, ok := itemMap["image_url"].(map[string]any); ok {
|
||||
if u, ok := imgURL["url"].(string); ok {
|
||||
ci.ImageURL = &mediaURL{URL: u}
|
||||
}
|
||||
}
|
||||
if vidURL, ok := itemMap["video_url"].(map[string]any); ok {
|
||||
if u, ok := vidURL["url"].(string); ok {
|
||||
ci.VideoURL = &mediaURL{URL: u}
|
||||
}
|
||||
}
|
||||
if audURL, ok := itemMap["audio_url"].(map[string]any); ok {
|
||||
if u, ok := audURL["url"].(string); ok {
|
||||
ci.AudioURL = &mediaURL{URL: u}
|
||||
}
|
||||
}
|
||||
if ci.Type != "" || ci.Text != "" {
|
||||
volcReq.Content = append(volcReq.Content, ci)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 其他参数
|
||||
if v, ok := metadata["resolution"].(string); ok {
|
||||
volcReq.Resolution = v
|
||||
}
|
||||
if v, ok := metadata["ratio"].(string); ok {
|
||||
volcReq.Ratio = v
|
||||
}
|
||||
if v, ok := metadata["duration"].(float64); ok && v > 0 {
|
||||
d := int(v)
|
||||
volcReq.Duration = &d
|
||||
}
|
||||
if v, ok := metadata["seed"].(float64); ok {
|
||||
s := int(v)
|
||||
volcReq.Seed = &s
|
||||
}
|
||||
if v, ok := metadata["watermark"].(bool); ok {
|
||||
volcReq.Watermark = &v
|
||||
}
|
||||
if v, ok := metadata["generate_audio"].(bool); ok {
|
||||
volcReq.GenerateAudio = &v
|
||||
}
|
||||
if v, ok := metadata["return_last_frame"].(bool); ok {
|
||||
volcReq.ReturnLastFrame = &v
|
||||
}
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
var meta volcengineMetadata
|
||||
if err := taskchannel.UnmarshalMetadata(metadata, &meta); err != nil {
|
||||
return nil, fmt.Errorf("parse volcengine metadata: %w", err)
|
||||
}
|
||||
// content 数组中的额外项(如 video_url、audio_url)
|
||||
volcReq.Content = append(volcReq.Content, meta.Content...)
|
||||
volcReq.Resolution = meta.Resolution
|
||||
volcReq.Ratio = meta.Ratio
|
||||
volcReq.Duration = meta.Duration
|
||||
volcReq.Seed = meta.Seed
|
||||
volcReq.Watermark = meta.Watermark
|
||||
volcReq.GenerateAudio = meta.GenerateAudio
|
||||
volcReq.ReturnLastFrame = meta.ReturnLastFrame
|
||||
|
||||
// 从根层级提取 resolution(支持 input.resolution 格式)
|
||||
if v, ok := req["resolution"].(string); ok && v != "" && volcReq.Resolution == "" {
|
||||
@@ -410,20 +377,19 @@ func (a *VolcengineVideoAdaptor) GetChannelName() string {
|
||||
|
||||
// hasVideoInput 检测请求是否包含视频输入
|
||||
func hasVideoInput(req map[string]any) bool {
|
||||
// 检查 metadata.content 中是否有 video_url 类型
|
||||
metadata, _ := req["metadata"].(map[string]any)
|
||||
if metadata == nil {
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
if len(metadata) == 0 {
|
||||
return false
|
||||
}
|
||||
content, _ := metadata["content"].([]any)
|
||||
for _, item := range content {
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
if m["type"] == "video_url" {
|
||||
return true
|
||||
}
|
||||
if _, has := m["video_url"]; has {
|
||||
return true
|
||||
}
|
||||
var meta struct {
|
||||
Content []contentItem `json:"content"`
|
||||
}
|
||||
if taskchannel.UnmarshalMetadata(metadata, &meta) != nil {
|
||||
return false
|
||||
}
|
||||
for _, c := range meta.Content {
|
||||
if c.Type == "video_url" || c.VideoURL != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -449,10 +415,12 @@ func extractDuration(req map[string]any) int {
|
||||
}
|
||||
}
|
||||
// 从 metadata 提取
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
if d, ok := metadata["duration"].(float64); ok && d > 0 {
|
||||
return int(d)
|
||||
}
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
var meta struct {
|
||||
Duration *int `json:"duration"`
|
||||
}
|
||||
if taskchannel.UnmarshalMetadata(metadata, &meta) == nil && meta.Duration != nil && *meta.Duration > 0 {
|
||||
return *meta.Duration
|
||||
}
|
||||
if s, ok := req["seconds"].(string); ok {
|
||||
if d, err := parseInt(s); err == nil && d > 0 {
|
||||
@@ -469,10 +437,12 @@ func extractResolution(req map[string]any) string {
|
||||
return r
|
||||
}
|
||||
// 从 metadata 提取
|
||||
if metadata, ok := req["metadata"].(map[string]any); ok {
|
||||
if r, ok := metadata["resolution"].(string); ok && r != "" {
|
||||
return r
|
||||
}
|
||||
metadata := taskchannel.ExtractMetadata(req)
|
||||
var meta struct {
|
||||
Resolution string `json:"resolution"`
|
||||
}
|
||||
if taskchannel.UnmarshalMetadata(metadata, &meta) == nil && meta.Resolution != "" {
|
||||
return meta.Resolution
|
||||
}
|
||||
return "720p"
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user