@ldsg/workflow 0.3.10 → 0.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/__tests__/unit/evaluate-variables.prefixItems.test.d.ts +2 -0
  2. package/dist/__tests__/unit/evaluate-variables.prefixItems.test.d.ts.map +1 -0
  3. package/dist/__tests__/unit/evaluate-variables.prefixItems.test.js +391 -0
  4. package/dist/__tests__/unit/resource.portId.test.d.ts +2 -0
  5. package/dist/__tests__/unit/resource.portId.test.d.ts.map +1 -0
  6. package/dist/__tests__/unit/resource.portId.test.js +275 -0
  7. package/dist/resource.d.ts.map +1 -1
  8. package/dist/resource.js +12 -72
  9. package/dist/types/json_schema_with_expression_extension.d.ts +1 -0
  10. package/dist/types/json_schema_with_expression_extension.d.ts.map +1 -1
  11. package/dist/types/node.d.ts +4 -0
  12. package/dist/types/node.d.ts.map +1 -1
  13. package/dist/types/workflow.d.ts +5 -0
  14. package/dist/types/workflow.d.ts.map +1 -1
  15. package/dist/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/resolve-json-schema.d.ts.map +1 -1
  16. package/dist/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/resolve-json-schema.js +25 -2
  17. package/dist/utils/execute-workflow.d.ts +22 -0
  18. package/dist/utils/execute-workflow.d.ts.map +1 -0
  19. package/dist/utils/execute-workflow.js +212 -0
  20. package/package.json +9 -7
  21. package/dist/utils/evaluate-variables/index.test.d.ts +0 -2
  22. package/dist/utils/evaluate-variables/index.test.d.ts.map +0 -1
  23. package/dist/utils/evaluate-variables/index.test.js +0 -219
  24. package/dist/utils/get-execution-levels.d.ts +0 -17
  25. package/dist/utils/get-execution-levels.d.ts.map +0 -1
  26. package/dist/utils/get-execution-levels.js +0 -136
  27. package/dist/utils/get-execution-levels.test.d.ts +0 -2
  28. package/dist/utils/get-execution-levels.test.d.ts.map +0 -1
  29. package/dist/utils/get-execution-levels.test.js +0 -228
package/dist/resource.js CHANGED
@@ -6,8 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.WorkflowResource = void 0;
7
7
  const resource_1 = require("@ldsg/resource");
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
- const evaluate_variables_1 = require("./utils/evaluate-variables");
10
- const get_execution_levels_1 = require("./utils/get-execution-levels");
9
+ const execute_workflow_1 = require("./utils/execute-workflow");
11
10
  class WorkflowResource extends resource_1.Resource {
12
11
  constructor() {
13
12
  super(...arguments);
@@ -57,80 +56,21 @@ class WorkflowResource extends resource_1.Resource {
57
56
  return res;
58
57
  };
59
58
  this.execute = async (params) => {
60
- const { context, presetWorkflowVariables = {}, variables: workflowInputVariables } = params;
59
+ const { context, presetWorkflowVariables, variables, traceContext } = params;
61
60
  const { getWorkflowData, settings: { startWorkflowNodeResourceId, endWorkflowNodeResourceId } } = this;
62
- const workflowVariablesMap = new Map(Object.entries(presetWorkflowVariables));
63
61
  const { workflowData } = getWorkflowData();
64
- const { nodes } = workflowData;
65
- const startNode = nodes.find((value) => value.id === startWorkflowNodeResourceId);
66
- const endNode = nodes.find((value) => value.id === endWorkflowNodeResourceId);
67
- if (!startNode || !endNode) {
68
- throw new Error('invalid workflow nodes');
69
- }
70
- const { executionLevels } = (0, get_execution_levels_1.getExecutionLevels)({
62
+ const { nodes, edges } = workflowData;
63
+ return (0, execute_workflow_1.executeWorkflow)({
64
+ context,
65
+ endWorkflowNodeResourceId,
66
+ nodes,
67
+ edges,
68
+ presetWorkflowVariables,
69
+ startWorkflowNodeResourceId,
70
+ variables,
71
71
  workflowData,
72
- startNode,
73
- endNode
72
+ traceContext
74
73
  });
75
- if (executionLevels.length < 1) {
76
- throw new Error('invalid workflow execution levels');
77
- }
78
- let workflowOutputVariables;
79
- // 按层级执行,同一层级的节点可以并行执行
80
- for (let levelIndex = 0; levelIndex < executionLevels.length; levelIndex++) {
81
- const currentLevel = executionLevels[levelIndex];
82
- // 并行执行当前层级的所有节点
83
- const levelPromises = currentLevel.map(async (node) => {
84
- const { id, propertiesSchema, execute: nodeExecute } = node;
85
- const workflowVariables = Object.fromEntries(workflowVariablesMap);
86
- // 输入变量从工作流变量中使用表达式获取
87
- const workflowNodePropertiesInputSchema = lodash_1.default.get(propertiesSchema, 'properties.input');
88
- const workflowNodePropertiesOutputSchema = lodash_1.default.get(propertiesSchema, 'properties.output');
89
- if (!lodash_1.default.isObject(workflowNodePropertiesInputSchema) ||
90
- !lodash_1.default.isObject(workflowNodePropertiesOutputSchema)) {
91
- throw new Error('invalid workflow node properties schema');
92
- }
93
- /**
94
- * 对于开始节点,不忽略 input 表达式,但替换工作流内部变量为工作流输入变量。
95
- */
96
- const workflowNodeInputExpressionEvaluateInput = levelIndex === 0 ? workflowInputVariables : workflowVariables;
97
- const { variables: workflowNodeInputVariables } = await (0, evaluate_variables_1.evaluateVariables)({
98
- jsonSchema: workflowNodePropertiesInputSchema,
99
- variables: workflowNodeInputExpressionEvaluateInput
100
- });
101
- const { variables: workflowNodeOutputExpressionEvaluateInput } = await nodeExecute({
102
- context,
103
- variables: workflowNodeInputVariables
104
- });
105
- /**
106
- * 输出变量从结果中使用表达式获取
107
- * 对于结束节点,不忽略 output 表达式,但表达式固定为取返回变量。
108
- */
109
- const { variables: workflowNodeOutputVariables } = await (0, evaluate_variables_1.evaluateVariables)({
110
- jsonSchema: workflowNodePropertiesOutputSchema,
111
- variables: workflowNodeOutputExpressionEvaluateInput
112
- });
113
- const res = {
114
- nodeId: id,
115
- variables: workflowNodeOutputVariables
116
- };
117
- return res;
118
- });
119
- // 等待当前层级的所有节点执行完成
120
- const levelResults = await Promise.all(levelPromises);
121
- // 将当前层级的结果更新到工作流变量映射中
122
- levelResults.forEach((result) => {
123
- workflowVariablesMap.set(result.nodeId, result.variables);
124
- // 如果是结束节点,保存输出变量
125
- if (result.nodeId === endNode.id) {
126
- workflowOutputVariables = result.variables;
127
- }
128
- });
129
- }
130
- const res = {
131
- variables: workflowOutputVariables
132
- };
133
- return res;
134
74
  };
135
75
  this.getWorkflowInfo = () => {
136
76
  const { execute } = this;
@@ -9,6 +9,7 @@ export interface JSONSchemaWithExpressionExtension extends Omit<JSONSchema7, 'ty
9
9
  [key: string]: JSONSchemaWithExpressionExtension;
10
10
  };
11
11
  items?: JSONSchemaWithExpressionExtension;
12
+ prefixItems?: JSONSchemaWithExpressionExtension[];
12
13
  'x-expression'?: string;
13
14
  'x-expression-const'?: string;
14
15
  'x-expression-default'?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"json_schema_with_expression_extension.d.ts","sourceRoot":"","sources":["../../src/types/json_schema_with_expression_extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,MAAM,qCAAqC,GAAG,mBAAmB,GAAG,KAAK,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,iCACf,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,YAAY,CAAC;IAC1D,IAAI,CAAC,EAAE,qCAAqC,CAAC;IAE7C,UAAU,CAAC,EAAE;QACX,CAAC,GAAG,EAAE,MAAM,GAAG,iCAAiC,CAAC;KAClD,CAAC;IAEF,KAAK,CAAC,EAAE,iCAAiC,CAAC;IAE1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC"}
1
+ {"version":3,"file":"json_schema_with_expression_extension.d.ts","sourceRoot":"","sources":["../../src/types/json_schema_with_expression_extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,MAAM,qCAAqC,GAAG,mBAAmB,GAAG,KAAK,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,iCACf,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,YAAY,CAAC;IAC1D,IAAI,CAAC,EAAE,qCAAqC,CAAC;IAE7C,UAAU,CAAC,EAAE;QACX,CAAC,GAAG,EAAE,MAAM,GAAG,iCAAiC,CAAC;KAClD,CAAC;IAEF,KAAK,CAAC,EAAE,iCAAiC,CAAC;IAE1C,WAAW,CAAC,EAAE,iCAAiC,EAAE,CAAC;IAElD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC"}
@@ -17,6 +17,10 @@ export interface WorkflowNodeExecuteRes<Variables = any> {
17
17
  * 由工作流节点向工作流内输入
18
18
  */
19
19
  variables: Variables;
20
+ /**
21
+ * (输出)端口 ID
22
+ */
23
+ portId?: string;
20
24
  }
21
25
  /**
22
26
  * 工作流执行方法
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/types/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAE5F,MAAM,MAAM,2BAA2B,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,MAAM,WAAW,yBAAyB,CAAC,SAAS,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG;IACvE;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB,CAAC,SAAS,GAAG,GAAG;IACrD;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,CAAC,SAAS,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CAChE,MAAM,EAAE,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC,KAClD,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,gBAAgB,EAAE,iCAAiC,CAAC;IAEpD;;;OAGG;IACH,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/types/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAE5F,MAAM,MAAM,2BAA2B,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,MAAM,WAAW,yBAAyB,CAAC,SAAS,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG;IACvE;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB,CAAC,SAAS,GAAG,GAAG;IACrD;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC;IAErB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,CAAC,SAAS,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CAChE,MAAM,EAAE,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC,KAClD,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,gBAAgB,EAAE,iCAAiC,CAAC;IAEpD;;;OAGG;IACH,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,sBAAsB,CAAC"}
@@ -18,6 +18,11 @@ export interface WorkflowExecuteParams {
18
18
  * 预设工作流变量
19
19
  */
20
20
  presetWorkflowVariables?: Record<string, WorkflowVariables>;
21
+ /**
22
+ * OpenTelemetry 追踪上下文
23
+ * 用于将工作流执行与请求 trace 关联
24
+ */
25
+ traceContext?: any;
21
26
  }
22
27
  export interface WorkflowExecuteRes {
23
28
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/types/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;IAE7B;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,qBAAqB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAE7F,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,eAAe,CAAC;CAC1B"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/types/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;IAE7B;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAE5D;;;OAGG;IACH,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,qBAAqB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAE7F,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,eAAe,CAAC;CAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-json-schema.d.ts","sourceRoot":"","sources":["../../../../../src/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/resolve-json-schema.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iCAAiC,EAAE,MAAM,mBAAmB,CAAC;AAEtE,UAAU,uBAAuB;IAC/B;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;IAEX;;OAEG;IACH,UAAU,EAAE,iCAAiC,CAAC;IAE9C;;OAEG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC;CACd;AAED,UAAU,oBAAoB;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,KAAK,iBAAiB,GAAG,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAE5F,eAAO,MAAM,iBAAiB,EAAE,iBAkF/B,CAAC"}
1
+ {"version":3,"file":"resolve-json-schema.d.ts","sourceRoot":"","sources":["../../../../../src/utils/evaluate-variables/utils/get-data-with-json-schema-and-input/resolve-json-schema.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iCAAiC,EAAE,MAAM,mBAAmB,CAAC;AAEtE,UAAU,uBAAuB;IAC/B;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;IAEX;;OAEG;IACH,UAAU,EAAE,iCAAiC,CAAC;IAE9C;;OAEG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC;CACd;AAED,UAAU,oBAAoB;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,KAAK,iBAAiB,GAAG,CAAC,MAAM,EAAE,uBAAuB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAE5F,eAAO,MAAM,iBAAiB,EAAE,iBAiH/B,CAAC"}
@@ -9,7 +9,7 @@ const lodash_1 = __importDefault(require("lodash"));
9
9
  const resolveJsonSchema = async (params) => {
10
10
  var _a;
11
11
  const { input, jsonSchema, source } = params;
12
- const { type: jsonSchemaType, 'x-expression': jsonSchemaExpression, const: jsonSchemaConst } = jsonSchema;
12
+ const { type: jsonSchemaType, 'x-expression': jsonSchemaExpression, const: jsonSchemaConst, prefixItems: jsonSchemaPrefixItems } = jsonSchema;
13
13
  let data;
14
14
  /**
15
15
  * 若常量值存在,则直接返回
@@ -51,7 +51,30 @@ const resolveJsonSchema = async (params) => {
51
51
  break;
52
52
  }
53
53
  case 'array': {
54
- throw new Error('unsupported array type');
54
+ /**
55
+ * 如果 prefixItems 存在,则转为数组结果
56
+ */
57
+ if (jsonSchemaPrefixItems) {
58
+ if (jsonSchemaPrefixItems.length > 0) {
59
+ const itemsDataPromises = jsonSchemaPrefixItems.map(async (prefixItem) => {
60
+ const resolveJsonSchemaRes = await (0, exports.resolveJsonSchema)({
61
+ jsonSchema: prefixItem,
62
+ input,
63
+ source
64
+ });
65
+ return resolveJsonSchemaRes.data;
66
+ });
67
+ const itemsData = await Promise.all(itemsDataPromises);
68
+ data = itemsData;
69
+ }
70
+ else {
71
+ /**
72
+ * 空数组时,初始化为空数组
73
+ */
74
+ data = [];
75
+ }
76
+ }
77
+ break;
55
78
  }
56
79
  case 'boolean':
57
80
  case 'integer':
@@ -0,0 +1,22 @@
1
+ import { WorkflowData, WorkflowExecuteRes, WorkflowNodeInfo, WorkflowVariables } from '../types';
2
+ export interface ExecuteWorkflowParams {
3
+ context: any;
4
+ endWorkflowNodeResourceId: string;
5
+ nodes: WorkflowNodeInfo[];
6
+ edges: WorkflowData['edges'];
7
+ presetWorkflowVariables?: Record<string, WorkflowVariables>;
8
+ startWorkflowNodeResourceId: string;
9
+ variables: any;
10
+ workflowData: WorkflowData;
11
+ traceContext?: any;
12
+ }
13
+ export interface NodeExecutionResult {
14
+ nodeId: string;
15
+ variables: any;
16
+ portId?: string;
17
+ }
18
+ /**
19
+ * 执行工作流
20
+ */
21
+ export declare function executeWorkflow(params: ExecuteWorkflowParams): Promise<WorkflowExecuteRes>;
22
+ //# sourceMappingURL=execute-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute-workflow.d.ts","sourceRoot":"","sources":["../../src/utils/execute-workflow.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAElB,MAAM,UAAU,CAAC;AAqBlB,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,GAAG,CAAC;IACb,yBAAyB,EAAE,MAAM,CAAC;IAClC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC5D,2BAA2B,EAAE,MAAM,CAAC;IACpC,SAAS,EAAE,GAAG,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgKD;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAsHhG"}
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.executeWorkflow = executeWorkflow;
7
+ const api_1 = __importDefault(require("@opentelemetry/api"));
8
+ const lodash_1 = __importDefault(require("lodash"));
9
+ const evaluate_variables_1 = require("./evaluate-variables");
10
+ const tracer = api_1.default.trace.getTracer('workspace');
11
+ /**
12
+ * 安全的 JSON 序列化,处理循环引用
13
+ */
14
+ function safeStringify(obj) {
15
+ const seen = new WeakSet();
16
+ return JSON.stringify(obj, (key, value) => {
17
+ if (typeof value === 'object' && value !== null) {
18
+ if (seen.has(value)) {
19
+ return '[Circular Reference]';
20
+ }
21
+ seen.add(value);
22
+ }
23
+ return value;
24
+ });
25
+ }
26
+ /**
27
+ * 执行单个工作流节点
28
+ */
29
+ async function executeNode(node, context, workflowVariablesMap, isFirstLevel, workflowInputVariables) {
30
+ const { id, propertiesSchema, execute: nodeExecute } = node;
31
+ const workflowVariables = Object.fromEntries(workflowVariablesMap);
32
+ // 输入变量从工作流变量中使用表达式获取
33
+ const workflowNodePropertiesInputSchema = lodash_1.default.get(propertiesSchema, 'properties.input');
34
+ const workflowNodePropertiesOutputSchema = lodash_1.default.get(propertiesSchema, 'properties.output');
35
+ /**
36
+ * 对于开始节点,不忽略 input 表达式,但替换工作流内部变量为工作流输入变量。
37
+ */
38
+ const workflowNodeInputExpressionEvaluateInput = isFirstLevel ? workflowInputVariables : workflowVariables;
39
+ let workflowNodeInputVariables = {};
40
+ if (workflowNodePropertiesInputSchema) {
41
+ const { variables } = await (0, evaluate_variables_1.evaluateVariables)({
42
+ jsonSchema: workflowNodePropertiesInputSchema,
43
+ variables: workflowNodeInputExpressionEvaluateInput
44
+ });
45
+ workflowNodeInputVariables = variables;
46
+ }
47
+ const { variables: workflowNodeOutputExpressionEvaluateInput, portId } = await nodeExecute({
48
+ context,
49
+ variables: workflowNodeInputVariables
50
+ });
51
+ /**
52
+ * 输出变量从结果中使用表达式获取
53
+ * 对于结束节点,不忽略 output 表达式,但表达式固定为取返回变量。
54
+ */
55
+ let workflowNodeOutputVariables = {};
56
+ if (workflowNodePropertiesOutputSchema) {
57
+ const { variables } = await (0, evaluate_variables_1.evaluateVariables)({
58
+ jsonSchema: workflowNodePropertiesOutputSchema,
59
+ variables: workflowNodeOutputExpressionEvaluateInput
60
+ });
61
+ workflowNodeOutputVariables = variables;
62
+ }
63
+ return {
64
+ nodeId: id,
65
+ variables: workflowNodeOutputVariables,
66
+ portId
67
+ };
68
+ }
69
+ /**
70
+ * 查找下一个需要执行的节点集合
71
+ */
72
+ function findNextNodeIds(levelResults, edges, visitedNodes, completedNodePredecessors, activatedEdges, endNodeId) {
73
+ const nextNodeIds = [];
74
+ const nextLevelNodesSet = new Set();
75
+ let workflowOutputVariables;
76
+ levelResults.forEach((result) => {
77
+ if (!result) {
78
+ return;
79
+ }
80
+ const { nodeId, variables, portId } = result;
81
+ // 标记节点为已访问
82
+ visitedNodes.add(nodeId);
83
+ // 保存变量
84
+ // workflowVariablesMap.set(nodeId, variables);
85
+ // 如果是结束节点,保存输出变量
86
+ if (nodeId === endNodeId) {
87
+ workflowOutputVariables = variables;
88
+ }
89
+ // 查找从当前节点出发的连线
90
+ const outgoingEdges = edges.filter((edge) => edge.sourceWorkflowNodeResourceId === nodeId);
91
+ // 根据 portId 过滤连线
92
+ const matchedEdges = portId
93
+ ? outgoingEdges.filter((edge) => edge.sourceWorkflowNodePortId === portId)
94
+ : outgoingEdges.filter((edge) => !edge.sourceWorkflowNodePortId);
95
+ // 处理匹配的连线的目标节点
96
+ matchedEdges.forEach((edge) => {
97
+ const targetNodeId = edge.targetWorkflowNodeResourceId;
98
+ // 如果目标节点已访问,跳过
99
+ if (visitedNodes.has(targetNodeId)) {
100
+ return;
101
+ }
102
+ // 记录该目标节点已完成的前驱节点
103
+ if (!completedNodePredecessors.has(targetNodeId)) {
104
+ completedNodePredecessors.set(targetNodeId, new Set());
105
+ }
106
+ completedNodePredecessors.get(targetNodeId).add(nodeId);
107
+ // 记录激活的边(源节点 -> 目标节点)
108
+ if (!activatedEdges.has(targetNodeId)) {
109
+ activatedEdges.set(targetNodeId, new Set());
110
+ }
111
+ activatedEdges.get(targetNodeId).add(nodeId);
112
+ // 查找目标节点的所有可能的入边(用于计算需要等待多少个前驱节点)
113
+ const incomingEdges = edges.filter((edge) => edge.targetWorkflowNodeResourceId === targetNodeId);
114
+ // 计算实际需要等待的前驱节点数量
115
+ // 对于每个前驱节点,检查是否有至少一条激活的入边
116
+ const allPredecessorIds = new Set(incomingEdges.map((edge) => edge.sourceWorkflowNodeResourceId));
117
+ let requiredPredecessorCount = 0;
118
+ allPredecessorIds.forEach((predecessorId) => {
119
+ // 检查该前驱节点是否有激活的入边指向当前节点
120
+ const hasActivatedEdge = incomingEdges.some((e) => {
121
+ var _a;
122
+ return e.sourceWorkflowNodeResourceId === predecessorId &&
123
+ ((_a = activatedEdges.get(targetNodeId)) === null || _a === void 0 ? void 0 : _a.has(predecessorId));
124
+ });
125
+ if (hasActivatedEdge) {
126
+ requiredPredecessorCount++;
127
+ }
128
+ });
129
+ // 只有当所有需要等待的前驱节点都完成时,才加入下一轮执行
130
+ const completedPredecessors = completedNodePredecessors.get(targetNodeId);
131
+ if (completedPredecessors.size === requiredPredecessorCount) {
132
+ // 避免重复添加同一个节点到下一层
133
+ if (!nextLevelNodesSet.has(targetNodeId)) {
134
+ nextLevelNodesSet.add(targetNodeId);
135
+ nextNodeIds.push(targetNodeId);
136
+ }
137
+ }
138
+ });
139
+ });
140
+ return { nextNodeIds, workflowOutputVariables };
141
+ }
142
+ /**
143
+ * 执行工作流
144
+ */
145
+ async function executeWorkflow(params) {
146
+ const { context, endWorkflowNodeResourceId, nodes, edges, presetWorkflowVariables = {}, startWorkflowNodeResourceId, variables: workflowInputVariables, traceContext } = params;
147
+ // 使用传入的 traceContext 或当前活跃的 context 来创建 span
148
+ const parentContext = traceContext || api_1.default.context.active();
149
+ const executeWorkflowSpan = tracer.startSpan('execute-workflow', undefined, parentContext);
150
+ const workflowVariablesMap = new Map(Object.entries(presetWorkflowVariables));
151
+ const startNode = nodes.find((value) => value.id === startWorkflowNodeResourceId);
152
+ const endNode = nodes.find((value) => value.id === endWorkflowNodeResourceId);
153
+ if (!startNode || !endNode) {
154
+ throw new Error('invalid workflow nodes');
155
+ }
156
+ let workflowOutputVariables;
157
+ // 使用动态执行,根据 portId 决定下一步执行哪些节点
158
+ let currentNodeIds = [startNode.id];
159
+ const visitedNodes = new Set();
160
+ const completedNodePredecessors = new Map();
161
+ // 记录每个节点被激活的入边(源节点ID -> 目标节点ID的集合)
162
+ const activatedEdges = new Map();
163
+ let isFirstLevel = true;
164
+ while (currentNodeIds.length > 0) {
165
+ // 去重当前节点集合(避免重复执行)
166
+ const uniqueNodeIds = [...new Set(currentNodeIds)];
167
+ // 并行执行当前节点集合
168
+ const levelPromises = uniqueNodeIds.map(async (nodeId) => {
169
+ // 跳过已访问的节点(避免循环执行)
170
+ if (visitedNodes.has(nodeId)) {
171
+ return null;
172
+ }
173
+ const ctx = api_1.default.trace.setSpan(parentContext, executeWorkflowSpan);
174
+ const span = tracer.startSpan(nodeId, undefined, ctx);
175
+ const node = nodes.find((value) => value.id === nodeId);
176
+ if (!node) {
177
+ throw new Error(`node not found: ${nodeId}`);
178
+ }
179
+ span.addEvent('execution workflow node');
180
+ const res = await executeNode(node, context, workflowVariablesMap, isFirstLevel, workflowInputVariables);
181
+ const { portId: outputPortId } = res;
182
+ span.setAttributes({
183
+ currentNodeId: nodeId,
184
+ outputPortId
185
+ });
186
+ span.end();
187
+ return res;
188
+ });
189
+ // 等待当前节点集合执行完成,如果任何节点失败则抛出异常
190
+ const levelResults = await Promise.all(levelPromises);
191
+ // 保存执行结果中的变量到 workflowVariablesMap
192
+ levelResults.forEach((result) => {
193
+ if (result) {
194
+ workflowVariablesMap.set(result.nodeId, result.variables);
195
+ }
196
+ });
197
+ // 根据执行结果和 portId 查找下一个节点集合
198
+ const { nextNodeIds, workflowOutputVariables: tempOutputVariables } = findNextNodeIds(levelResults, edges, visitedNodes, completedNodePredecessors, activatedEdges, endNode.id);
199
+ if (tempOutputVariables !== undefined) {
200
+ workflowOutputVariables = tempOutputVariables;
201
+ }
202
+ // 更新当前节点集合
203
+ currentNodeIds = nextNodeIds;
204
+ // 标记已过第一轮执行
205
+ isFirstLevel = false;
206
+ }
207
+ const res = {
208
+ variables: workflowOutputVariables
209
+ };
210
+ executeWorkflowSpan.end();
211
+ return res;
212
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ldsg/workflow",
3
- "version": "0.3.10",
3
+ "version": "0.3.17",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -11,14 +11,15 @@
11
11
  "dist/"
12
12
  ],
13
13
  "dependencies": {
14
+ "@opentelemetry/api": "~1.9.0",
14
15
  "json-schema": "0.4.0",
15
16
  "json-schema-default": "^1.0.2",
16
17
  "jsonata": "^2.0.6",
17
18
  "lodash": "^4.17.21",
18
- "@ldsg/resource": "0.3.10",
19
- "@ldsg/resource-definition": "0.3.10",
20
- "@ldsg/constants": "0.3.10",
21
- "@ldsg/handler": "0.3.10"
19
+ "@ldsg/constants": "0.3.17",
20
+ "@ldsg/handler": "0.3.17",
21
+ "@ldsg/resource": "0.3.17",
22
+ "@ldsg/resource-definition": "0.3.17"
22
23
  },
23
24
  "devDependencies": {
24
25
  "@types/fs-extra": "^11.0.4",
@@ -31,13 +32,14 @@
31
32
  "ts-jest": "^29.1.5",
32
33
  "ts-node": "^10.9.2",
33
34
  "typescript": "^5.8.3",
34
- "@ldsg/types": "0.3.10"
35
+ "@ldsg/types": "0.3.17"
35
36
  },
36
37
  "publishConfig": {
37
38
  "access": "public"
38
39
  },
39
40
  "scripts": {
40
41
  "build": "tsc --project tsconfig.build.json",
41
- "clean": "rm -rf dist"
42
+ "clean": "rm -rf dist",
43
+ "test": "jest"
42
44
  }
43
45
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/utils/evaluate-variables/index.test.ts"],"names":[],"mappings":""}