feat: 支持模板子流程递归拷贝与段级续跑优化
* 模板拷贝用户流程时递归复制子流程,并重写 sub_flow 节点引用 * 段级续跑改用列表位置作为段身份,移除对 segment_index 的依赖 * 段结果保存移到每段生成完成时立即落库,降低崩溃丢失风险 * 移除视频分段续跑设计与对应测试
This commit is contained in:
@@ -77,12 +77,13 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
var totalCost float64
|
||||
if len(paramsList) > 1 {
|
||||
// 段级续跑仅在"多段 + 视频模型"启用;非视频分段(批量文本等)走原逻辑零影响。
|
||||
// 段级复用/落库还要求各段带互不重复的 segment_index(目前仅 split_shots_pipeline 产出):
|
||||
// 视频节点若被批量拆分(如 split_batch_model_params),paramsList>1 但无 segment_index,
|
||||
// 此时不得启用段级续跑,否则续跑会拿同一段结果拼出 N 段重复视频(静默损坏)。
|
||||
// segVideo 为 false 时走既有非段级合并路径(全量生成、不落库、不复用),全新执行行为不变,
|
||||
// 段身份 = 列表位置(0-based):paramsList 顺序即段序,concat 按列表顺序拼接;
|
||||
// 位置互不重复且跨 reExecute 稳定(参数一致 → 段数/顺序不变)。不依赖 params 里的
|
||||
// segment_index——真实链路(上游 split_shots_pipeline 转写 → 下游 split_segment 按
|
||||
// __segment_fields 拆分,invokePreTool 剥离 __ 内部键)下 paramsList 只有模型参数。
|
||||
// segVideo=false 时走既有非段级合并路径(全量生成、不落库、不复用),全新执行行为不变,
|
||||
// 后续自动 concat 判断(独立的 isVideoModel 调用)仍正常执行。
|
||||
segVideo := isVideoModel(ctx, nodeInput.Config.ModelConfig.ModelId) && hasDistinctSegmentIndex(paramsList)
|
||||
segVideo := isVideoModel(ctx, nodeInput.Config.ModelConfig.ModelId)
|
||||
|
||||
// 续跑(!ForceNewRun)时读取该节点已成功段;全新执行不查(BuildExecution 已清旧段),saved 为 nil → 全量重生成
|
||||
var saved map[int]entity.SegmentRef
|
||||
@@ -98,6 +99,7 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
results := make([][]map[string]any, len(paramsList))
|
||||
tokenRes := make([]*gateway.ModelCallRes, len(paramsList))
|
||||
errs := make([]error, len(paramsList))
|
||||
saveErrs := make([]error, len(paramsList))
|
||||
isInference := make([]bool, len(paramsList))
|
||||
var wg sync.WaitGroup
|
||||
for i, params := range paramsList {
|
||||
@@ -114,31 +116,29 @@ func ModelLambda(ctx context.Context, input any) (any, error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// 每段成功立即落库:该段刚成功即持久化,其他段仍在跑/重试时已成功段也不丢;
|
||||
// 后续段失败或进程崩溃(panic/OOM/kill)时,已完成段已在库中,reExecute 可直接复用
|
||||
if segVideo && errs[i] == nil {
|
||||
for _, rec := range results[i] {
|
||||
key := media.FindVideoKey(rec)
|
||||
url := media.FindVideoURL(ctx, rec)
|
||||
if key == "" || url == "" {
|
||||
continue
|
||||
}
|
||||
if err := flowDao.FlowSegmentResultDao.Save(ctx, nodeInput.Global.ExecutionId, nodeInput.Config.Id, idxList[i], key, url); err != nil {
|
||||
saveErrs[i] = err
|
||||
}
|
||||
}
|
||||
}
|
||||
}(i, params)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// 每段成功立即落库:即使后续某段失败导致节点失败,成功段也保留供下次续跑复用
|
||||
if segVideo {
|
||||
for i := range results {
|
||||
if !needGen[i] || errs[i] != nil {
|
||||
continue
|
||||
}
|
||||
for _, rec := range results[i] {
|
||||
key := media.FindVideoKey(rec)
|
||||
url := media.FindVideoURL(ctx, rec)
|
||||
if key == "" || url == "" {
|
||||
continue
|
||||
}
|
||||
if err := flowDao.FlowSegmentResultDao.Save(ctx, nodeInput.Global.ExecutionId, nodeInput.Config.Id, idxList[i], key, url); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 仍有失败段 → 节点失败(成功段已落库,供下次 reExecute 复用)
|
||||
// 仍有失败段或落库失败 → 节点失败(成功段已立即落库,供下次 reExecute 复用)
|
||||
for i := range results {
|
||||
if saveErrs[i] != nil {
|
||||
return nil, saveErrs[i]
|
||||
}
|
||||
if needGen[i] && errs[i] != nil {
|
||||
return nil, errs[i]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user