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) } }