package node import ( "ai-agent/workflow/consts/node" nodeDto "ai-agent/workflow/model/dto/node" "context" "fmt" "gitea.redpowerfuture.com/red-future/common/beans" commonHttp "gitea.redpowerfuture.com/red-future/common/http" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/frame/g" ) var NodeLibraryService = &nodeLibraryService{} type nodeLibraryService struct{} func GetModelType(ctx context.Context) (mainTypeMap map[int]string, err error) { headers := make(map[string]string) if r := g.RequestFromCtx(ctx); r != nil { for k, v := range r.Request.Header { if len(v) > 0 { headers[k] = v[0] } } } res := new(nodeDto.ModelTypeResponse) err = commonHttp.Get(ctx, "model-gateway/model/listType", headers, res, nil) // 通用过滤:只保留 能被 100 整除的主类型(100/200/300...) mainTypeMap = make(map[int]string) for typ, name := range res.Type { if typ%100 == 0 { mainTypeMap[typ] = name } } return } func (s *nodeLibraryService) GetNodeLibrary(ctx context.Context, req *nodeDto.WorkflowNodeTreeReq) (*nodeDto.WorkflowNodeTreeRes, error) { WorkflowNodeGroups := []node.NodeGroupItem{ { Group: node.NodeGroupComponent, Label: node.NodeGroupNameComponent, Items: []node.NodeItem{ { NodeCode: node.NodeTypeTextModel, NodeName: node.NodeNameTextModel, ModelType: node.ModelTypeText, SkillOption: false, PromptOption: true, IsSaveFile: true, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeImageModel, NodeName: node.NodeNameImageModel, ModelType: node.ModelTypeImage, SkillOption: false, PromptOption: true, IsSaveFile: true, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeVideoModel, NodeName: node.NodeNameVideoModel, ModelType: node.ModelTypeVideo, SkillOption: false, PromptOption: true, IsSaveFile: true, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeAudioModel, NodeName: node.NodeNameAudioModel, ModelType: node.ModelTypeAudio, SkillOption: false, PromptOption: true, IsSaveFile: true, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeBatchModel, NodeName: node.NodeNameBatchModel, ModelType: node.ModelTypeText, SkillOption: false, PromptOption: true, IsSaveFile: true, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, //{ // NodeCode: node.NodeTypeSenseOptimizeModel, // NodeName: node.NodeNameSenseOptimizeModel, // ModelType: node.ModelTypeText, // SkillOption: false, // FormConfig: []node.NodeFormField{}, // ModelConfig: []node.ModelItem{}, //}, //{ // NodeCode: node.NodeTypeStoryOptimizeModel, // NodeName: node.NodeNameStoryOptimizeModel, // ModelType: node.ModelTypeText, // SkillOption: false, // FormConfig: []node.NodeFormField{}, // ModelConfig: []node.ModelItem{}, //}, //{ // NodeCode: node.NodeTypeScriptOptimizeModel, // NodeName: node.NodeNameScriptOptimizeModel, // ModelType: node.ModelTypeText, // SkillOption: false, // FormConfig: []node.NodeFormField{}, // ModelConfig: []node.ModelItem{}, //}, }, }, { Group: node.NodeGroupBase, Label: node.NodeGroupNameBase, Items: []node.NodeItem{ { NodeCode: node.NodeTypeDataConversionModel, NodeName: node.NodeNameDataConversionModel, ModelType: node.ModelTypeText, SkillOption: false, PromptOption: true, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeMerge, NodeName: node.NodeNameMerge, SkillOption: false, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeDataMerge, NodeName: node.NodeNameDataMerge, SkillOption: false, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeJudge, NodeName: node.NodeNameJudge, SkillOption: false, FormConfig: []node.NodeFormField{ {Field: "condition", Label: node.FormLabelCondition, Type: "input", Required: true}, }, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeForm, NodeName: node.NodeNameForm, SkillOption: false, FormConfig: []node.NodeFormField{}, ModelConfig: []node.ModelItem{}, }, { NodeCode: node.NodeTypeHttp, NodeName: node.NodeNameHttp, SkillOption: false, IsSaveFile: true, FormConfig: []node.NodeFormField{ { Field: "method", Label: "请求方式", Type: "select", Required: true, Options: []node.SelectOption{ {Label: "GET", Value: "GET"}, {Label: "POST", Value: "POST"}, {Label: "PUT", Value: "PUT"}, {Label: "DELETE", Value: "DELETE"}, }, }, { Field: "url", Label: "请求地址", Type: "input", Required: true, }, { Field: "headers", Label: "请求头(支持Authorization鉴权)", Type: "keyValue", Required: false, }, { Field: "bodyType", Label: "请求体类型", Type: "select", Required: true, Options: []node.SelectOption{ {Label: "无", Value: "None"}, {Label: "JSON", Value: "JSON"}, //{Label: "表单", Value: "FormUrlEncoded"}, //{Label: "文件上传", Value: "FormData"}, //{Label: "原生文本", Value: "Raw"}, }, }, { Field: "body", Label: "请求体内容", Type: "keyValue", Required: false, }, { Field: "response", Label: "结果返回结构", Type: "keyValue", Required: false, }, { Field: "responseType", Label: "结果返回方式", Type: "select", Required: true, Options: []node.SelectOption{ {Label: "同步返回", Value: "sync"}, {Label: "等候回调", Value: "callback"}, {Label: "主动拉取", Value: "pull"}, }, Expand: []node.NodeFormField{ { Field: "method", Label: "请求方式", Type: "select", Required: true, Options: []node.SelectOption{ {Label: "GET", Value: "GET"}, {Label: "POST", Value: "POST"}, {Label: "PUT", Value: "PUT"}, {Label: "DELETE", Value: "DELETE"}, }, }, { Field: "url", Label: "请求地址", Type: "input", Required: true, }, { Field: "headers", Label: "请求头(支持Authorization鉴权)", Type: "keyValue", Required: false, }, { Field: "bodyType", Label: "请求体类型", Type: "select", Required: true, Options: []node.SelectOption{ {Label: "无", Value: "None"}, {Label: "JSON", Value: "JSON"}, //{Label: "表单", Value: "FormUrlEncoded"}, //{Label: "文件上传", Value: "FormData"}, //{Label: "原生文本", Value: "Raw"}, }, }, { Field: "body", Label: "请求体内容", Type: "keyValue", Required: false, }, { Field: "response", Label: "结果返回结构", Type: "keyValue", Required: false, }, { Field: "timeout", Label: "超时时间(秒)", Type: "inputNumber", Required: false, Default: 30, }, { Field: "insecureSkipVerify", Label: "跳过HTTPS证书校验", Type: "switch", Required: false, Default: false, }, }, }, { Field: "callbackUrl", Label: "回调地址(只需要填写字段名称)", Type: "input", Required: false, Default: "", }, { Field: "timeout", Label: "超时时间(秒)", Type: "inputNumber", Required: false, Default: 30, }, { Field: "insecureSkipVerify", Label: "跳过HTTPS证书校验", Type: "switch", Required: false, Default: false, }, }, ModelConfig: []node.ModelItem{}, }, //{ // NodeCode: node.NodeTypeModel, // NodeName: node.NodeNameModel, // SkillOption: true, // FormConfig: []node.NodeFormField{}, // ModelConfig: []node.ModelItem{}, //}, }, }, //{ // Group: node.NodeGroupCustom, // Label: node.NodeGroupNameCustom, // Items: []node.NodeItem{ // { // NodeCode: node.NodeTypeCustomNode, // NodeName: node.NodeNameCustomNode, // SkillOption: true, // FormConfig: []node.NodeFormField{ // {Field: "nodeName", Label: node.FormLabelApiKey, Type: "input", Required: true}, // {Field: "nodeType", Label: node.FormLabelModel, Type: "input", Required: true}, // }, // ModelConfig: []node.ModelItem{}, // }, // }, //}, } tree := &nodeDto.WorkflowNodeTreeRes{ Groups: WorkflowNodeGroups, } // 3. 遍历分组,根据 typeId=1 给【文本模型节点】追加固定表单 for gIdx := range tree.Groups { group := &tree.Groups[gIdx] // 遍历分组下的每个节点 for itemIdx := range group.Items { item := &group.Items[itemIdx] if item.NodeCode == node.NodeTypeTextModel || item.NodeCode == node.NodeTypeImageModel || item.NodeCode == node.NodeTypeVideoModel || item.NodeCode == node.NodeTypeAudioModel || item.NodeCode == node.NodeTypeBatchModel || item.NodeCode == node.NodeTypeDataConversionModel || item.NodeCode == node.NodeTypeSenseOptimizeModel || item.NodeCode == node.NodeTypeStoryOptimizeModel || item.NodeCode == node.NodeTypeScriptOptimizeModel { item.ModelConfig = append(item.ModelConfig, node.ModelItem{ ModelName: "自定义", }) } } } return tree, nil } // SetUserInfo 设置用户信息 func (s *nodeLibraryService) SetUserInfo(ctx context.Context, creator string, tenantId uint64) (headers map[string]string, err error) { // 创建完整的用户信息 userInfo := &beans.User{ UserName: creator, TenantId: tenantId, } ctx = context.WithValue(ctx, "user", *userInfo) // 提取并保存请求头(在连接升级前) headers = make(map[string]string) // 提取其他headers if r := g.RequestFromCtx(ctx); r != nil { for k, v := range r.Request.Header { if len(v) > 0 { headers[k] = v[0] } } } // 将完整用户信息序列化为JSON,放到X-User-Info请求头 userInfoJson, err := gjson.Encode(userInfo) if err != nil { return nil, fmt.Errorf("用户信息序列化失败: %w", err) } headers["X-User-Info"] = string(userInfoJson) return }