improve:优化视频模型额外参数处理逻辑

This commit is contained in:
qian committed 2026-06-13 09:47:21 +08:00
1 parent 8f3fe333c7
commit 2e7f0beca3
4 files changed
+178 -160

No files matched your search

+37 -44
View File
@@ -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
}
+52
View File
@@ -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
}
+41 -38
View File
@@ -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 -78
View File
@@ -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"
}