From f5be9d8a40c9fed3ec1bdfba13b7c40349b21cb6 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Tue, 30 Jun 2026 15:15:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E9=A6=96=E5=B8=A7=E5=8F=82=E6=95=B0=E5=8F=8A?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F=E6=89=A7=E8=A1=8C=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加 FirstFrame 字段到模型信息结构体,重构视频节点数据转换处理,支持从上游节点获取视频配置并区分输出字段。实现视频结果重新上传至 OSS,修正字段匹配逻辑为精确匹配,并基于 return_last_frame 参数优化顺序执行中的首帧与内容传递。 --- workflow/model/dto/flow/flow_execution_dto.go | 1 + workflow/service/flow/lambda_node.go | 19 +++++++- workflow/service/flow/lambda_node_imp.go | 45 ++++++++++++++----- workflow/service/flow/lambda_node_util.go | 22 +++++++-- 4 files changed, 71 insertions(+), 16 deletions(-) diff --git a/workflow/model/dto/flow/flow_execution_dto.go b/workflow/model/dto/flow/flow_execution_dto.go index 2d79c3b..0bedc82 100644 --- a/workflow/model/dto/flow/flow_execution_dto.go +++ b/workflow/model/dto/flow/flow_execution_dto.go @@ -49,6 +49,7 @@ type GetModelInfoReq struct { type GetModelInfoRes struct { Model struct { + FirstFrame string `json:"firstFrame"` LastFrame string `json:"lastFrame"` ResponseTokenField string `json:"responseTokenField"` ResponseMapping map[string]any `json:"responseMapping"` diff --git a/workflow/service/flow/lambda_node.go b/workflow/service/flow/lambda_node.go index 48e4c8d..64b3115 100644 --- a/workflow/service/flow/lambda_node.go +++ b/workflow/service/flow/lambda_node.go @@ -11,6 +11,7 @@ import ( flowDto "ai-agent/workflow/model/dto/flow" "context" "fmt" + "path/filepath" "strconv" "strings" "sync" @@ -292,7 +293,23 @@ func VideoModelLambda(ctx context.Context, input any) (any, error) { } videoUrl = newS + msg.FileURL } else { - videoUrl = videoURL[0] + var bytes []byte + bytes, err = GetFileBytesFromURL(ctx, videoURL[0]) + if err != nil { + return nil, fmt.Errorf("下载图片失败: %w", err) + } + // 构造文件名 + fileName := fmt.Sprintf("ai_video_%d%s", time.Now().UnixMilli(), strings.ToLower(filepath.Ext(videoURL[0]))) + // 上传到你的OSS(你项目已有的Upload方法) + var upResp *dto.UploadFileBytesRes + upResp, err = Upload(ctx, &dto.UploadFileBytesReq{ + FileName: fileName, + FileBytes: bytes, + }) + if err != nil { + return nil, fmt.Errorf("上传OSS失败: %w", err) + } + videoUrl = upResp.FileURL } outputRes := make([]node.NodeFormField, 0) diff --git a/workflow/service/flow/lambda_node_imp.go b/workflow/service/flow/lambda_node_imp.go index 972310f..fc3eeb5 100644 --- a/workflow/service/flow/lambda_node_imp.go +++ b/workflow/service/flow/lambda_node_imp.go @@ -788,18 +788,41 @@ func VideoOptimizeNode(ctx context.Context, nodeInput *flowDto.NodeExecutionInpu } func DataConversionNode(ctx context.Context, nodeInput *flowDto.NodeExecutionInput, skillName string, form []map[string]any, userForm []map[string]any) ([]node.NodeFormField, error) { - if strings.Contains(nodeInput.Config.Name, "字幕") { + if strings.Contains(nodeInput.Config.Name, "字幕") || strings.Contains(nodeInput.Config.Name, "视频") { jsonStr := `` - for _, field := range nodeInput.Config.OutputConfig { - jsonStr, _ = sjson.Set(jsonStr, field.Field, field.Value) - } outputRes := make([]node.NodeFormField, 0) - outputRes = append(outputRes, node.NodeFormField{ - Field: fmt.Sprintf("data_conversion"), - Value: gconv.Map(jsonStr), - Label: fmt.Sprintf("data_conversion"), - Type: "string", - }) + for _, field := range nodeInput.Config.OutputConfig { + if strings.Contains(nodeInput.Config.Name, "视频") { + for _, item := range nodeInput.Global.ExecutedNodes { + refNode, ok := nodeInput.Global.ConfigMap[item.NodeId] + if !ok { + continue + } + for _, v := range refNode.FormConfig { + if v.Field == field.Value { + jsonStr, _ = sjson.Set(jsonStr, field.Field, v.Value) + } + } + } + } else { + jsonStr, _ = sjson.Set(jsonStr, field.Field, field.Value) + } + } + if strings.Contains(nodeInput.Config.Name, "视频") { + outputRes = append(outputRes, node.NodeFormField{ + Field: fmt.Sprintf("data_content"), + Value: gconv.Map(jsonStr), + Label: fmt.Sprintf("data_content"), + Type: "string", + }) + } else { + outputRes = append(outputRes, node.NodeFormField{ + Field: fmt.Sprintf("data_conversion"), + Value: gconv.Map(jsonStr), + Label: fmt.Sprintf("data_conversion"), + Type: "string", + }) + } return outputRes, nil } @@ -1035,7 +1058,7 @@ func GetNodeContextContent(execInput *flowDto.FlowExecutionInput, nodeEntity *en // 取指定字段 for _, f := range source.Field { for _, v := range refNode.FormConfig { - if strings.Contains(v.Label, f) { + if v.Label == f { input = append(input, v) } } diff --git a/workflow/service/flow/lambda_node_util.go b/workflow/service/flow/lambda_node_util.go index 142ac4d..6af2455 100644 --- a/workflow/service/flow/lambda_node_util.go +++ b/workflow/service/flow/lambda_node_util.go @@ -299,7 +299,22 @@ func GetModelResult(ctx context.Context, sessionId string, nodeInput *flowDto.No if !nodeInput.Global.IsDialogue { sessionId = "" } - + needSequential := false + for _, item := range userForm { + if g.NewVar(item).IsMap() { + valMap := gconv.Map(item) + for _, v := range valMap { + if g.NewVar(v).IsMap() { + vv := gconv.Map(v) + for kk, vvv := range vv { + if kk == "return_last_frame" { + needSequential = vvv.(bool) + } + } + } + } + } + } composeResult, err := GetComposeResult(ctx, buildType, nodeInput.Config.ModelConfig.ModelName, nodeInput.Config.PromptContent, skillName, form, userForm, nodeInput.Global.FileUrl, sessionId, nodeInput.Config.Id, nodeInput.Config.Name) if err != nil { return nil, err @@ -316,13 +331,12 @@ func GetModelResult(ctx context.Context, sessionId string, nodeInput *flowDto.No mapTaskResult = make([]map[string]any, len(composeResult.Messages.Rounds)) var taskResultMap map[string]any - needSequential := false if buildType == 1 { if needSequential { for idx, item := range composeResult.Messages.Rounds { if !g.IsEmpty(taskResultMap) { var set string - set, err = sjson.Set(gconv.String(item), modelInfo.Model.LastFrame, gconv.String(taskResultMap[modelInfo.Model.ResponseBody])) + set, err = sjson.Set(gconv.String(item), modelInfo.Model.FirstFrame, gconv.String(taskResultMap["content"])) if err != nil { return nil, err } @@ -339,7 +353,7 @@ func GetModelResult(ctx context.Context, sessionId string, nodeInput *flowDto.No } if nodeInput.Config.NodeCode == node.NodeTypeVideoModel { - ext := GetFileTypeByPath(gconv.String(taskResult[modelInfo.Model.ResponseBody])) + ext := GetFileTypeByPath(gconv.String(taskResult["content"])) if ext == "image" { taskResultMap = taskResult } else {