package node import ( "context" "strconv" "ai-agent/gateway" "ai-agent/workflow/consts/model" "ai-agent/workflow/consts/node" nodeDto "ai-agent/workflow/model/dto/node" "ai-agent/workflow/service/flow/processor" "github.com/gogf/gf/v2/frame/g" ) var NodeLibraryService = &nodeLibraryService{} type nodeLibraryService struct{} func (s *nodeLibraryService) GetNodeLibrary(ctx context.Context, req *nodeDto.WorkflowNodeTreeReq) (*nodeDto.WorkflowNodeTreeRes, error) { tree := node.GetFilterNodeTree([]node.NodeGroup{node.NodeGroupBase}) if opts, err := videoModelOptions(ctx); err != nil { g.Log().Warningf(ctx, "加载视频模型选项失败,节点库降级返回: %v", err) } else { applyVideoModelOptions(tree, opts) } // 前置/后置方法下拉填充处理器注册表数据 if opts, err := processorOptions(ctx); err != nil { g.Log().Warningf(ctx, "加载工作流前后置处理器选项失败,节点库降级返回: %v", err) } else { applyProcessorOptions(tree, opts) } return &nodeDto.WorkflowNodeTreeRes{ Groups: tree, }, nil } // processorOptions 从处理器注册表构建前置/后置方法下拉选项:key=处理器名,value=描述(无描述回落名称)。 func processorOptions(ctx context.Context) ([]node.SelectOption, error) { list, err := processor.List(ctx) if err != nil { return nil, err } opts := make([]node.SelectOption, 0, len(list)) for _, p := range list { if p == nil || p.Name == "" || !p.IsShow { continue } value := p.Description if value == "" { value = p.Name } opts = append(opts, node.SelectOption{ Key: p.Name, Value: value, }) } return opts, nil } // applyProcessorOptions 把处理器选项填充进模型节点的 preTool/postTool 下拉。 // 深拷贝 PreToolOption/PostToolOption 后再改,避免改写全局 NodeTypeMetaList。 func applyProcessorOptions(tree []node.NodeGroupTree, opts []node.SelectOption) { for gi := range tree { for ni := range tree[gi].Nodes { n := &tree[gi].Nodes[ni] if n.Key != node.NodeTypeModel { continue } n.PreToolOption = withProcessorOptions(n.PreToolOption, opts) n.PostToolOption = withProcessorOptions(n.PostToolOption, opts) return } } } // withProcessorOptions 返回 preTool/postTool 字段列表的深拷贝,并把 select 类型字段的 Options 替换为处理器选项。 func withProcessorOptions(fields []node.NodePresetField, opts []node.SelectOption) []node.NodePresetField { out := append([]node.NodePresetField(nil), fields...) for i := range out { if out[i].Type == "select" { out[i].Options = opts } } return out } // videoModelOptions 按视频模型类型(600)查模型网关,转成 modelId 下拉选项:key=模型ID,value=模型名称。 func videoModelOptions(ctx context.Context) ([]node.SelectOption, error) { res, err := gateway.ListModelManage(ctx, &gateway.ListModelManageReq{ModelType: model.TypeVideo}) if err != nil { return nil, err } opts := make([]node.SelectOption, 0, len(res.List)) for _, item := range res.List { if item == nil || item.Id <= 0 || item.ModelName == "" { continue } opts = append(opts, node.SelectOption{ Key: strconv.FormatInt(item.Id, 10), Value: item.ModelName, }) } return opts, nil } // applyVideoModelOptions 把 script_transcribe 节点的 modelId 预设下拉替换为视频模型选项。 // 深拷贝 PresetOption 后再改,避免改写全局 NodeTypeMetaList。 func applyVideoModelOptions(tree []node.NodeGroupTree, opts []node.SelectOption) { for gi := range tree { for ni := range tree[gi].Nodes { n := &tree[gi].Nodes[ni] if n.Key != node.NodeTypeScriptTranscribe { continue } presets := append([]node.NodePresetField(nil), n.PresetOption...) for pi := range presets { if presets[pi].Field == "modelId" { presets[pi].Options = opts break } } n.PresetOption = presets return } } }