feat(workflow): 增加工作流计费与执行生命周期管理
- 新增计费模块:执行开始建单、终态结算/取消/失败处理,支持按条/按秒/按token计费 - 新增执行生命周期跟踪:优雅关停时取消运行中执行并等待落库 - 新增异步任务等待/通知机制(Wait/Notify) - 重构执行记录落库与进度上报,统一失败分类与重试语义 - 重命名文件:async_task.go→async.go、flow_checkpoint_store.go→exec_checkpoint.go、flow_graph_util.go→exec_record.go - 更新 .gitignore 与数据库密码配置
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"ai-agent/gateway"
|
||||
"ai-agent/workflow/consts/model"
|
||||
"ai-agent/workflow/service/flow/processor"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// isVideoModel 判断模型是否为视频模型(模型类型 TypeVideo=600),用于视频节点多视频自动合成判断
|
||||
func isVideoModel(ctx context.Context, modelId int64) bool {
|
||||
modelInfo, err := gateway.GetModelInfoById(ctx, &gateway.GetModelInfoByIdReq{ModelId: modelId})
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "查询模型配置失败,跳过自动视频合成 modelId=%d err=%v", modelId, err)
|
||||
return false
|
||||
}
|
||||
return modelInfo.ModelManage.ModelType != nil && *modelInfo.ModelManage.ModelType == model.TypeVideo
|
||||
}
|
||||
|
||||
// invokePreTool 执行前置处理器,把模型请求参数转换为模型调用入参列表。
|
||||
// 前置处理器契约:入参即模型请求参数本体;返回值:
|
||||
// - map[string]any 一次模型调用,入参为返回值
|
||||
// - []map[string]any 多次模型调用,逐个入参请求
|
||||
// - nil 视为异常,节点失败(不允许静默跳过模型调用)
|
||||
func invokePreTool(ctx context.Context, processorName string, modelParams map[string]any) (paramsList []map[string]any, err error) {
|
||||
if processorName == "" {
|
||||
return []map[string]any{stripInternalKeys(modelParams)}, nil
|
||||
}
|
||||
data, err := processor.Call(ctx, processorName, modelParams)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("执行前置处理器[%s]失败: %v", processorName, err)
|
||||
}
|
||||
switch v := data.(type) {
|
||||
case nil:
|
||||
return nil, fmt.Errorf("前置处理器[%s]返回空", processorName)
|
||||
case map[string]any:
|
||||
return []map[string]any{stripInternalKeys(v)}, nil
|
||||
case []map[string]any:
|
||||
list := make([]map[string]any, 0, len(v))
|
||||
for _, m := range v {
|
||||
list = append(list, stripInternalKeys(m))
|
||||
}
|
||||
return list, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("前置处理器[%s]返回类型不支持: %T", processorName, data)
|
||||
}
|
||||
}
|
||||
|
||||
// stripInternalKeys 剥离 __ 前缀的内部键(如 __segment_fields/__produced),
|
||||
// 模型网关做参数严格校验(CheckParams strictUnknown)会拒绝未知字段,内部标记不得随请求体下发。
|
||||
func stripInternalKeys(params map[string]any) map[string]any {
|
||||
if params == nil {
|
||||
return params
|
||||
}
|
||||
for k := range params {
|
||||
if strings.HasPrefix(k, "__") {
|
||||
delete(params, k)
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// invokePostTool 执行后置处理器,加工模型调用结果。
|
||||
// 后置处理器契约:入参 {"output": 模型输出结果列表, "request": 原始模型请求参数}(列表须包成对象传入);返回值:
|
||||
// - []map[string]any 替换模型输出
|
||||
// - map[string]any 替换为单条输出
|
||||
// - nil 保留原输出
|
||||
func invokePostTool(ctx context.Context, processorName string, outputRes []map[string]any, requestParams map[string]any) ([]map[string]any, error) {
|
||||
if processorName == "" {
|
||||
return outputRes, nil
|
||||
}
|
||||
data, err := processor.Call(ctx, processorName, map[string]any{"output": outputRes, "request": requestParams})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("执行后置处理器[%s]失败: %v", processorName, err)
|
||||
}
|
||||
switch v := data.(type) {
|
||||
case nil:
|
||||
return outputRes, nil
|
||||
case []map[string]any:
|
||||
return v, nil
|
||||
case map[string]any:
|
||||
return []map[string]any{v}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("后置处理器[%s]返回类型不支持: %T", processorName, data)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user