diff --git a/go.mod b/go.mod index b5da2e5..38f1b67 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,14 @@ go 1.26.0 require ( gitea.redpowerfuture.com/red-future/common v0.0.29 - github.com/cloudwego/eino v0.9.5 + github.com/cloudwego/eino v0.9.12 + github.com/cloudwego/eino-examples v0.0.0-20260630021147-171220631fb7 github.com/cloudwego/eino-ext/components/model/qwen v0.1.9 github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2 github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2 github.com/gogf/gf/v2 v2.10.2 github.com/google/uuid v1.6.0 + github.com/stretchr/testify v1.11.1 github.com/tidwall/gjson v1.19.0 github.com/tidwall/sjson v1.2.5 go.opentelemetry.io/otel/trace v1.44.0 @@ -29,6 +31,7 @@ require ( github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/cloudwego/eino-ext/libs/acl/openai v0.1.17 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/badger/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -65,7 +68,7 @@ require ( github.com/hashicorp/serf v0.10.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mailru/easyjson v0.9.0 // indirect @@ -81,15 +84,16 @@ require ( github.com/olekukonko/errors v1.1.0 // indirect github.com/olekukonko/ll v0.0.9 // indirect github.com/olekukonko/tablewriter v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.9 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/r3labs/diff/v2 v2.15.1 // indirect - github.com/redis/go-redis/v9 v9.12.1 // indirect + github.com/redis/go-redis/v9 v9.17.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/tiger1103/gfast-token v1.0.10 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/vcaesar/cedar v0.30.0 // indirect @@ -105,8 +109,8 @@ require ( go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect - golang.org/x/arch v0.11.0 // indirect - golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect + golang.org/x/arch v0.19.0 // indirect + golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect diff --git a/go.sum b/go.sum index 4e45841..971b734 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,10 @@ github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cloudwego/eino v0.9.5 h1:0Nftjx9gPek/2S/hzm38LVxSjk5/6mqRr3I9VKrKvm4= github.com/cloudwego/eino v0.9.5/go.mod h1:OBD1mrkfkt/pJa4rkg1P0VnaMeOVl7l8IAdEqY//3IQ= +github.com/cloudwego/eino v0.9.12 h1:mHAMo5k7GdvnVD8Lc2sLyfpkxEm0S/y3PkEMhsSYt78= +github.com/cloudwego/eino v0.9.12/go.mod h1:OBD1mrkfkt/pJa4rkg1P0VnaMeOVl7l8IAdEqY//3IQ= +github.com/cloudwego/eino-examples v0.0.0-20260630021147-171220631fb7 h1:HU60xk+OI3g7b/C73vl8fyEnD1rLqOGHiVQAPBiDDG8= +github.com/cloudwego/eino-examples v0.0.0-20260630021147-171220631fb7/go.mod h1:pSszHeBqB4AuF0EY05eh6eUpVcJtDtsCTwbrSh8LnH0= github.com/cloudwego/eino-ext/components/model/qwen v0.1.9 h1:xCz/mp43JeWqupjPR3zLRArmwC6P29/6lTwbwh1yzYM= github.com/cloudwego/eino-ext/components/model/qwen v0.1.9/go.mod h1:slTGTuhzkzhNavf+1UtUg1FvUSA31iNAF+rq1mT4SnI= github.com/cloudwego/eino-ext/libs/acl/openai v0.1.17 h1:EeVcR1TslRA2IdNW1h/2LaGbPlffwGhQm99jM3zWZiI= @@ -235,6 +239,8 @@ github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uq github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -306,10 +312,14 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f h1:lJqhwddJVYAkyp72a4pwzMClI20xTwL7miDdm2W/KBM= +github.com/pkg/errors v0.9.2-0.20201214064552-5dd12d0cfe7f/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -331,6 +341,8 @@ github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI= +github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -376,6 +388,8 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s= @@ -425,6 +439,8 @@ go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU= +golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= @@ -435,6 +451,8 @@ golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= +golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= +golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/workflow/consts/node/node_template.go b/workflow/consts/node/node_template.go index ae5a104..f4e82ea 100644 --- a/workflow/consts/node/node_template.go +++ b/workflow/consts/node/node_template.go @@ -106,6 +106,7 @@ type NodeItem struct { NodeCode NodeType `json:"nodeCode"` ModelType int `json:"modelType"` NodeName string `json:"nodeName"` // 从常量来 + PatchLayout bool `json:"patchLayout"` SkillOption bool `json:"skillOption"` PromptOption bool `json:"promptOption"` IsSaveFile bool `json:"isSaveFile"` diff --git a/workflow/controller/flow/flow_execution_controller.go b/workflow/controller/flow/flow_execution_controller.go index dd45492..87ab274 100644 --- a/workflow/controller/flow/flow_execution_controller.go +++ b/workflow/controller/flow/flow_execution_controller.go @@ -38,3 +38,17 @@ func (c *flowExecution) Get(ctx context.Context, req *flowDto.GetFlowExecutionRe func (c *flowExecution) List(ctx context.Context, req *flowDto.ListFlowExecutionReq) (res *flowDto.ListFlowExecutionTreeRes, err error) { return flowService.FlowExecutionService.List(ctx, req) } + +func (c *flowExecution) GetSessionList(ctx context.Context, req *flowDto.GetSessionListReq) (res *flowDto.ListFlowExecutionRes, err error) { + return flowService.FlowExecutionService.GetSessionList(ctx, req) +} + +func (c *flowExecution) DeleteResult(ctx context.Context, req *flowDto.DeleteResultReq) (res *beans.ResponseEmpty, err error) { + err = flowService.FlowExecutionService.DeleteResult(ctx, req) + return +} + +func (c *flowExecution) DeleteSession(ctx context.Context, req *flowDto.DeleteSessionReq) (res *beans.ResponseEmpty, err error) { + err = flowService.FlowExecutionService.DeleteSession(ctx, req) + return +} diff --git a/workflow/dao/flow/flow_execution_dao.go b/workflow/dao/flow/flow_execution_dao.go index 78276df..ebc26fd 100644 --- a/workflow/dao/flow/flow_execution_dao.go +++ b/workflow/dao/flow/flow_execution_dao.go @@ -51,6 +51,11 @@ func (d *flowExecutionDao) Get(ctx context.Context, req *flowDto.GetFlowExecutio func (d *flowExecutionDao) List(ctx context.Context, req *flowDto.ListFlowExecutionReq, fields ...string) (res []*entity.FlowExecution, total int, err error) { model := gfdb.DB(ctx, public.DbNameBlackDeacon).Model(ctx, public.TableNameFlowExecution).Fields(fields).OmitEmpty() model.Where(entity.FlowExecutionCol.Creator, req.Creator) + model.Where(entity.FlowExecutionCol.ResultDel, req.ResultDel) + model.Where(entity.FlowExecutionCol.SessionDel, req.SessionDel) + if req.IsResult { + model.WhereNot(entity.FlowExecutionCol.OutputParams, "[]") + } model.OrderDesc(entity.FlowExecutionCol.CreatedAt) if req.Page != nil { model.Page(int(req.Page.PageNum), int(req.Page.PageSize)) diff --git a/workflow/model/dto/flow/flow_execution_dto.go b/workflow/model/dto/flow/flow_execution_dto.go index 8fb2a8a..766dce1 100644 --- a/workflow/model/dto/flow/flow_execution_dto.go +++ b/workflow/model/dto/flow/flow_execution_dto.go @@ -30,6 +30,7 @@ type FlowExecutionInput struct { ExecutionId int64 `json:"executionId"` ConfigMap map[string]*entity.FlowNode `json:"configMap"` SessionId string `json:"sessionId" dc:"会话ID"` + Templates []map[string]any `json:"templates"` Desc string `json:"desc"` SkillName string `json:"skillName"` FileUrl []string `json:"fileUrl"` @@ -184,6 +185,7 @@ type ExecuteReq struct { FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"` NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"` SessionId string `json:"sessionId" dc:"会话ID"` + Templates []map[string]any `json:"templates"` Desc string `json:"desc"` SkillName string `json:"skillName"` FileUrl []string `json:"fileUrl"` @@ -213,6 +215,7 @@ type CreateFlowExecutionReq struct { ErrorMessage string `json:"errorMessage" description:"错误信息"` TraceId string `json:"traceId" description:"跟踪ID"` SessionId string `json:"sessionId" dc:"会话ID"` + Extension map[string]interface{} `json:"extension"` } type CreateFlowExecutionRes struct { @@ -220,15 +223,33 @@ type CreateFlowExecutionRes struct { } type UpdateFlowExecutionReq struct { - Id int64 `json:"id" v:"required#ID不能为空"` - NodeGroupId string `json:"nodeGroupId"` - DurationMs int64 `json:"durationMs" description:"执行时长(毫秒)"` - Status flow.FlowExecutionStatus `json:"status" description:"状态:1-运行中,2-成功,3-失败"` - OutputParams []map[string]interface{} `json:"outputParams" description:"输出参数"` - ErrorMessage string `json:"errorMessage" description:"错误信息"` - TraceId string `json:"traceId" description:"跟踪ID"` - TotalTokens int `json:"totalTokens" description:"总token"` - TotalFee float64 `json:"totalFee" description:"总费用"` + Id int64 `json:"id" v:"required#ID不能为空"` + NodeGroupId string `json:"nodeGroupId"` + DurationMs int64 `json:"durationMs" description:"执行时长(毫秒)"` + Status flow.FlowExecutionStatus `json:"status" description:"状态:1-运行中,2-成功,3-失败"` + FlowContent *entity.FlowInfo `json:"flowContent" description:"流程内容"` + NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"` + OutputParams []map[string]interface{} `json:"outputParams" description:"输出参数"` + ErrorMessage string `json:"errorMessage" description:"错误信息"` + TraceId string `json:"traceId" description:"跟踪ID"` + TotalTokens int `json:"totalTokens" description:"总token"` + TotalFee float64 `json:"totalFee" description:"总费用"` + SessionDel bool `json:"sessionDel" description:"会话是否删除"` + ResultDel bool `json:"resultDel" description:"结果是否删除"` + Extension map[string]interface{} `json:"extension"` +} + +type DeleteResultReq struct { + g.Meta `path:"/deleteResult" method:"delete" tags:"任务管理" summary:"删除结果" dc:"删除结果"` + + Id int64 `json:"id" v:"required#ID不能为空"` + Content string `json:"content"` +} + +type DeleteSessionReq struct { + g.Meta `path:"/deleteSession" method:"delete" tags:"任务管理" summary:"删除会话" dc:"删除会话"` + + Id int64 `json:"id" v:"required#ID不能为空"` } type GetFlowExecutionReq struct { @@ -238,11 +259,20 @@ type GetFlowExecutionReq struct { SessionId string `json:"sessionId" dc:"会话ID"` } +type GetSessionListReq struct { + g.Meta `path:"/sessionList" method:"get" tags:"任务管理" summary:"会话列表" dc:"会话列表"` + + *beans.Page `json:"page"` +} + type ListFlowExecutionReq struct { g.Meta `path:"/list" method:"get" tags:"任务管理" summary:"任务列表" dc:"任务列表"` - Page *beans.Page `json:"page"` - Creator string `json:"creator"` + *beans.Page `json:"page"` + Creator string `json:"creator"` + SessionDel bool `json:"sessionDel"` + ResultDel bool `json:"resultDel"` + IsResult bool `json:"isResult"` } type ListFlowExecutionRes struct { @@ -261,6 +291,7 @@ type VOFlowExecution struct { NodeInputParams []*entity.FlowNode `json:"nodeInputParams" description:"节点输入参数"` OutputParams []map[string]interface{} `json:"outputParams" description:"输出参数"` ErrorMessage string `json:"errorMessage" description:"错误信息"` + Extension map[string]interface{} `json:"extension"` TraceId string `json:"traceId" description:"跟踪ID"` SessionId string `json:"sessionId" dc:"会话ID"` CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"` @@ -269,22 +300,17 @@ type VOFlowExecution struct { } // ========== 核心:构建树状结构 ========== -// 定义树结构 type OutputItem struct { + Id int64 `json:"id,string" description:"ID"` Timestamp string `json:"timestamp" description:"时间戳key"` Content string `json:"content" description:"内容值"` Type string `json:"type" description:"类型"` Label string `json:"label" description:"后缀+数字标号"` } -type FlowNode struct { - FlowName string `json:"flowName" description:"流程名称"` - Id int64 `json:"Id,string" description:"任务ID"` - SessionId string `json:"sessionId" description:"会话ID"` - Items []OutputItem `json:"items" description:"输出项列表"` -} + type DateNode struct { - CreateDate string `json:"createDate" description:"创建日期"` - Flows []FlowNode `json:"flows" description:"流程列表"` + CreateDate string `json:"createDate" description:"创建日期"` + Items []OutputItem `json:"items" description:"直接是结果项列表"` } type ListFlowExecutionTreeRes struct { diff --git a/workflow/model/entity/flow_execution.go b/workflow/model/entity/flow_execution.go index 0bbdc3f..ace3dfa 100644 --- a/workflow/model/entity/flow_execution.go +++ b/workflow/model/entity/flow_execution.go @@ -23,6 +23,9 @@ type FlowExecution struct { SessionId string `orm:"session_id" json:"sessionId" description:"会话ID"` TotalTokens int `orm:"total_tokens" json:"totalTokens" description:"总token消耗"` TotalFee int `orm:"total_fee" json:"totalFee" description:"总费用"` + SessionDel bool `orm:"session_del" json:"sessionDel" description:"会话是否删除"` + ResultDel bool `orm:"result_del" json:"resultDel" description:"结果是否删除"` + Extension map[string]interface{} `orm:"extension" json:"extension" description:"扩展字段"` } type flowExecutionCol struct { @@ -41,6 +44,9 @@ type flowExecutionCol struct { SessionId string TotalTokens string TotalFee string + SessionDel string + ResultDel string + Extension string } var FlowExecutionCol = flowExecutionCol{ @@ -59,4 +65,7 @@ var FlowExecutionCol = flowExecutionCol{ SessionId: "session_id", TotalTokens: "total_tokens", TotalFee: "total_fee", + SessionDel: "session_del", + ResultDel: "result_del", + Extension: "extension", } diff --git a/workflow/model/entity/flow_user.go b/workflow/model/entity/flow_user.go index 4cb344f..71c7ae1 100644 --- a/workflow/model/entity/flow_user.go +++ b/workflow/model/entity/flow_user.go @@ -19,6 +19,7 @@ type FlowNode struct { NodeCode node.NodeType `json:"nodeCode"` Name string `json:"name"` Config map[string]interface{} `json:"config"` + PatchLayout bool `json:"patchLayout"` SkillName string `json:"skillName"` PromptContent string `json:"promptContent"` IsSaveFile bool `json:"isSaveFile"` diff --git a/workflow/service/flow/flow_execution_service.go b/workflow/service/flow/flow_execution_service.go index 63d9a6c..9909d45 100644 --- a/workflow/service/flow/flow_execution_service.go +++ b/workflow/service/flow/flow_execution_service.go @@ -16,14 +16,12 @@ import ( "fmt" "sort" "strconv" - "strings" "sync" "time" "gitea.redpowerfuture.com/red-future/common/utils" "github.com/cloudwego/eino/compose" "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" "github.com/google/uuid" "go.opentelemetry.io/otel/trace" @@ -47,54 +45,100 @@ func (s *flowExecutionService) Get(ctx context.Context, req *flowDto.GetFlowExec return res, err } +func (s *flowExecutionService) DeleteResult(ctx context.Context, req *flowDto.DeleteResultReq) (err error) { + r, err := flowDao.FlowExecutionDao.Get(ctx, &flowDto.GetFlowExecutionReq{Id: req.Id}) + if err != nil { + return err + } + // 遍历并剔除值等于 req.Url 的数据 + newOutputParams := make([]map[string]any, 0) + for _, paramMap := range r.OutputParams { + // 单个 paramMap 过滤 + cleanMap := make(map[string]any) + for k, v := range paramMap { + // 转为字符串对比 + if gconv.String(v) != req.Content { + cleanMap[k] = v + } + } + // 只保留非空 map,避免出现空层级 + if len(cleanMap) > 0 { + newOutputParams = append(newOutputParams, cleanMap) + } + } + // 赋值回原数据 + r.OutputParams = newOutputParams + // 执行更新:更新 OutputParams + 标记删除 + flowUpdateReq := new(flowDto.UpdateFlowExecutionReq) + flowUpdateReq.Id = req.Id + flowUpdateReq.OutputParams = r.OutputParams + _, err = flowDao.FlowExecutionDao.Update(ctx, flowUpdateReq) + return +} + +func (s *flowExecutionService) DeleteSession(ctx context.Context, req *flowDto.DeleteSessionReq) (err error) { + flowUpdateReq := new(flowDto.UpdateFlowExecutionReq) + flowUpdateReq.Id = req.Id + flowUpdateReq.SessionDel = true + _, err = flowDao.FlowExecutionDao.Update(ctx, flowUpdateReq) + return +} + +func (s *flowExecutionService) GetSessionList(ctx context.Context, req *flowDto.GetSessionListReq) (res *flowDto.ListFlowExecutionRes, err error) { + user, err := utils.GetUserInfo(ctx) + if err != nil { + return + } + flowReq := new(flowDto.ListFlowExecutionReq) + flowReq.Page = req.Page + flowReq.Creator = user.UserName + flowReq.SessionDel = false + list, total, err := flowDao.FlowExecutionDao.List(ctx, flowReq) + if err != nil { + return nil, err + } + res = &flowDto.ListFlowExecutionRes{ + Total: total, + } + err = gconv.Struct(list, &res.List) + return res, err +} + func (s *flowExecutionService) List(ctx context.Context, req *flowDto.ListFlowExecutionReq) (res *flowDto.ListFlowExecutionTreeRes, err error) { user, err := utils.GetUserInfo(ctx) if err != nil { return } req.Creator = user.UserName + req.IsResult = true list, _, err := flowDao.FlowExecutionDao.List(ctx, req) if err != nil { return nil, err } - // ===================== 核心修复:只统计【有数据】的执行记录,空的直接跳过 ===================== - + // ========== 只统计【有数据】的执行记录,空的直接跳过 ========== executionNumber := make(map[int64]int) // executionId -> 倒序编号(最新=1) - - // 第一次遍历:只处理【有输出参数】的记录,统计并分配编号 - var validList []*entity.FlowExecution // 只存有效(非空)记录 + var validList []*entity.FlowExecution for _, execution := range list { if g.IsEmpty(execution.OutputParams) { - continue // 空数据直接过滤,不参与编号、不展示 + continue } validList = append(validList, execution) } - - // 给有效记录分配【时间倒序编号】(最新=1) totalValid := len(validList) for idx, execution := range validList { executionNumber[execution.Id] = totalValid - idx } - // 2. 分组映射:日期 -> 流程节点 - type flowWrap struct { - flowNode flowDto.FlowNode - createdAt *gtime.Time - } - dateMap := make(map[string]*[]flowWrap) + // ========== 分组:日期 -> 合并所有 OutputItem ========== + dateMap := make(map[string][]flowDto.OutputItem) - // 遍历【有效数据】构建结构 for _, execution := range validList { createDate := execution.CreatedAt.Format("Y-m-d") - flowName := execution.FlowName + execID := execution.Id // 拿到执行ID outputParams := execution.OutputParams - // 编号只算有效数据,不会把空的算进去 - num := executionNumber[execution.Id] - displayFlowName := fmt.Sprintf("会话-%d(%s)", num, flowName) - - // 3. 解析 outputParams + // 解析 outputParams var tempItems []flowDto.OutputItem for _, paramMap := range outputParams { for tsKey, value := range paramMap { @@ -102,13 +146,12 @@ func (s *flowExecutionService) List(ctx context.Context, req *flowDto.ListFlowEx continue } tempItems = append(tempItems, flowDto.OutputItem{ + Id: execID, Timestamp: tsKey, Content: gconv.String(value), }) } } - - // ===================== 修复1:如果解析后依然为空,直接跳过,不生成第二层节点 ===================== if len(tempItems) == 0 { continue } @@ -126,21 +169,17 @@ func (s *flowExecutionService) List(ctx context.Context, req *flowDto.ListFlowEx item := &tempItems[idx] val := item.Content suffix := "内容" - ext := "" - ext = GetFileTypeByPath(val) - if ext == "image" { + ext := GetFileTypeByPath(val) + switch ext { + case "image": suffix = "图片" - } - if ext == "video" { + case "video": suffix = "视频" - } - if ext == "audio" { + case "audio": suffix = "音频" - } - if ext == "text" { + case "text": suffix = "文案" - } - if ext == "html" { + case "html": suffix = "HTML" } suffixCount[suffix]++ @@ -148,48 +187,23 @@ func (s *flowExecutionService) List(ctx context.Context, req *flowDto.ListFlowEx item.Label = fmt.Sprintf("%s_%d", suffix, suffixCount[suffix]) } - // 组装节点 - flowNode := flowDto.FlowNode{ - FlowName: displayFlowName, - Id: execution.Id, - SessionId: gconv.String(execution.SessionId), - Items: tempItems, - } - - if dateMap[createDate] == nil { - dateMap[createDate] = &[]flowWrap{} - } - *dateMap[createDate] = append(*dateMap[createDate], flowWrap{ - flowNode: flowNode, - createdAt: execution.CreatedAt, - }) + // 直接追加到对应日期下(不再包一层 FlowNode) + dateMap[createDate] = append(dateMap[createDate], tempItems...) } - // 6. 构建树 + 排序 + // ========== 构建树并排序 ========== var tree []flowDto.DateNode - for date, wraps := range dateMap { - // 第二层按创建时间倒序(最新在前) - sort.Slice(*wraps, func(i, j int) bool { - return (*wraps)[i].createdAt.After((*wraps)[j].createdAt) - }) - - var flowNodes []flowDto.FlowNode - for _, w := range *wraps { - flowNodes = append(flowNodes, w.flowNode) - } - - // ===================== 修复2:日期下没有流程,也过滤掉 ===================== - if len(flowNodes) == 0 { + for date, items := range dateMap { + if len(items) == 0 { continue } - tree = append(tree, flowDto.DateNode{ CreateDate: date, - Flows: flowNodes, + Items: items, }) } - // 第一层日期倒序 + // 日期倒序 sort.Slice(tree, func(i, j int) bool { return tree[i].CreateDate > tree[j].CreateDate }) @@ -294,17 +308,24 @@ func (s *flowExecutionService) Execute(ctx context.Context, req *flowDto.Execute var executionId int64 var isDialogue bool var nodeGroupId = uuid.NewString() + flowName := req.FlowName + if !g.IsEmpty(req.Desc) { + flowName = req.Desc + } + isDialogue = false if flowInfo == nil { - isDialogue = false var r = new(flowDto.CreateFlowExecutionReq) r.FlowUserId = req.FlowId - r.FlowName = req.FlowName + r.FlowName = flowName r.NodeGroupId = nodeGroupId r.TriggerType = flow.FlowExecutionTriggerTypeManual.Code() r.FlowContent = req.FlowContent r.NodeInputParams = req.NodeInputParams r.SessionId = req.SessionId r.Status = flow.FlowExecutionStatusRunning.Code() + r.Extension = map[string]any{ + "templates": req.Templates, + } span := trace.SpanFromContext(ctx) if span != nil && span.SpanContext().HasTraceID() { r.TraceId = span.SpanContext().TraceID().String() @@ -316,7 +337,6 @@ func (s *flowExecutionService) Execute(ctx context.Context, req *flowDto.Execute return } } else { - isDialogue = true executionId = flowInfo.Id span := trace.SpanFromContext(ctx) if span != nil && span.SpanContext().HasTraceID() { @@ -324,10 +344,15 @@ func (s *flowExecutionService) Execute(ctx context.Context, req *flowDto.Execute cancelMap.Store(traceId, cancel) } executionReq := flowDto.UpdateFlowExecutionReq{ - Id: executionId, - NodeGroupId: nodeGroupId, - Status: flow.FlowExecutionStatusRunning.Code(), - TraceId: traceId, + Id: executionId, + NodeGroupId: nodeGroupId, + FlowContent: req.FlowContent, + NodeInputParams: req.NodeInputParams, + Extension: map[string]any{ + "templates": req.Templates, + }, + Status: flow.FlowExecutionStatusRunning.Code(), + TraceId: traceId, } _, err = flowDao.FlowExecutionDao.Update(ctx, &executionReq) if err != nil { @@ -349,20 +374,20 @@ func (s *flowExecutionService) Execute(ctx context.Context, req *flowDto.Execute } } - if isDialogue && !g.IsEmpty(flowInfo) && !g.IsEmpty(req.ResultUrl) { - req.NodeGroupId = nodeGroupId - if strings.HasSuffix(gconv.String(req.ResultUrl), ".inc") { - err = TextModelSingleLambda(ctx, req, flowInfo) - return - } else if strings.HasSuffix(gconv.String(req.ResultUrl), ".png") { - err = ImgModelSingleLambda(ctx, req, flowInfo) - return - } else if strings.HasSuffix(gconv.String(req.ResultUrl), ".html") { - err = TextImgModelSingleLambda(ctx, req, flowInfo) - return - } - return nil, errors.New("文件格式不支持") - } + //if isDialogue && !g.IsEmpty(flowInfo) && !g.IsEmpty(req.ResultUrl) { + // req.NodeGroupId = nodeGroupId + // if strings.HasSuffix(gconv.String(req.ResultUrl), ".inc") { + // err = TextModelSingleLambda(ctx, req, flowInfo) + // return + // } else if strings.HasSuffix(gconv.String(req.ResultUrl), ".png") { + // err = ImgModelSingleLambda(ctx, req, flowInfo) + // return + // } else if strings.HasSuffix(gconv.String(req.ResultUrl), ".html") { + // err = TextImgModelSingleLambda(ctx, req, flowInfo) + // return + // } + // return nil, errors.New("文件格式不支持") + //} // ========================================================================= // ✅【第1步】给所有判断节点自动生成意图识别节点 @@ -443,6 +468,7 @@ func (s *flowExecutionService) Execute(ctx context.Context, req *flowDto.Execute IsDialogue: isDialogue, ExecutionId: executionId, ConfigMap: configMap, + Templates: req.Templates, SessionId: req.SessionId, Desc: req.Desc, SkillName: req.SkillName, diff --git a/workflow/service/flow/lambda_node.go b/workflow/service/flow/lambda_node.go index ae91c0a..570dae4 100644 --- a/workflow/service/flow/lambda_node.go +++ b/workflow/service/flow/lambda_node.go @@ -663,7 +663,7 @@ func SummaryLambda(ctx context.Context, input any) (any, error) { // 生成 毫秒时间戳 作为 KEY timeKey := strconv.FormatInt(time.Now().UnixMilli(), 10) item := make(map[string]interface{}) - item[timeKey] = field.Value + item[timeKey] = ProcessPath(ctx, gconv.String(field.Value)) summaryResult = append(summaryResult, item) } } diff --git a/workflow/service/flow/lambda_node_imp.go b/workflow/service/flow/lambda_node_imp.go index fc3eeb5..7db11b1 100644 --- a/workflow/service/flow/lambda_node_imp.go +++ b/workflow/service/flow/lambda_node_imp.go @@ -898,7 +898,7 @@ func HttpNode(ctx context.Context, nodeInput *flowDto.NodeExecutionInput) ([]nod } // 构建请求参数 - newBody := BuildNestedJson(body, nodeInput.Global.ConfigMap) + newBody := BuildNestedJson(body, nodeInput.Global) // 1. 自己生成唯一 taskId(不用前端给) taskId := "my_task_" + uuid.New().String() // 自己生成唯一ID if responseType == "callback" { diff --git a/workflow/service/flow/lambda_node_util.go b/workflow/service/flow/lambda_node_util.go index 125277e..1fefe23 100644 --- a/workflow/service/flow/lambda_node_util.go +++ b/workflow/service/flow/lambda_node_util.go @@ -413,7 +413,9 @@ func GetModelResult(ctx context.Context, sessionId string, nodeInput *flowDto.No // 加锁写入map,解决并发竞态 mu.Lock() + fmt.Println("taskResult======================", idx, taskResult) mapTaskResult[idx] = taskResult + fmt.Println("mapTaskResult======================", mapTaskResult) mu.Unlock() //updateTokenCount(ctx, nodeInput.NodeExecutionId, modelInfo.Model.ResponseTokenField, taskResult) @@ -440,13 +442,17 @@ func GetModelResult(ctx context.Context, sessionId string, nodeInput *flowDto.No //updateTokenCount(ctx, nodeInput.NodeExecutionId, modelInfo.Model.ResponseTokenField, item) } } - + fmt.Println("mapTaskResult--------------------------------------", mapTaskResult) return mapTaskResult, nil } -func BuildNestedJson(body g.Map, mockConfigMap map[string]*entity.FlowNode) g.Map { +func BuildNestedJson(body g.Map, global *flowDto.FlowExecutionInput) g.Map { jsonStr := "{}" for originKey, originItem := range body { + if originKey == "templates" && !g.IsEmpty(global.Templates) { + jsonStr, _ = sjson.Set(jsonStr, originKey, global.Templates) + continue + } bodyItemMap := gconv.Map(originItem) val := bodyItemMap["value"] if v, ok := bodyItemMap["value"]; ok { @@ -457,7 +463,7 @@ func BuildNestedJson(body g.Map, mockConfigMap map[string]*entity.FlowNode) g.Ma valMap := gconv.Map(val) nodeId := gconv.String(valMap["nodeId"]) fieldName := gconv.String(valMap["field"]) - if configValue, ok := mockConfigMap[nodeId]; ok { + if configValue, ok := global.ConfigMap[nodeId]; ok { if !g.IsEmpty(configValue.OutputResult) { for _, v := range configValue.OutputResult { if strings.Contains(v.Field, fieldName) { @@ -659,6 +665,30 @@ func GetUrlSuffix(rawUrl string, withDot bool) string { return suffix } +// ProcessPath 处理请求路径 +// 1. 判断是否为合法完整请求路径(以/开头) +// 2. 包含 tenantId-1 则截断其及前面所有内容 +func ProcessPath(ctx context.Context, path string) string { + // 判断是否是完整请求路径:以 / 开头 + if strings.HasPrefix(path, "/") { + return path + } + + target, err := utils.GetBucketName(ctx) + if err != nil { + return path + } + idx := strings.Index(path, target) + if idx == -1 { + // 不包含目标字符串,原样返回 + return path + } + + // 截取 tenantId-1 后面的内容 + newPath := path[idx+len(target):] + return newPath +} + func BuildText(text string) string { // 生成单条HTML var htmlBuilder strings.Builder diff --git a/workflow/service/node/node_library_service.go b/workflow/service/node/node_library_service.go index b47d4a7..60b0ffb 100644 --- a/workflow/service/node/node_library_service.go +++ b/workflow/service/node/node_library_service.go @@ -67,6 +67,7 @@ func (s *nodeLibraryService) GetNodeLibrary(ctx context.Context, req *nodeDto.Wo NodeCode: node.NodeTypeVideoModel, NodeName: node.NodeNameVideoModel, ModelType: node.ModelTypeVideo, + PatchLayout: true, SkillOption: false, PromptOption: true, IsSaveFile: true,