veryfront 0.1.699 → 0.1.701
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.
- package/esm/deno.js +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/esm/src/workflow/executor/dag/index.d.ts +1 -2
- package/esm/src/workflow/executor/dag/index.d.ts.map +1 -1
- package/esm/src/workflow/executor/dag/index.js +37 -216
- package/esm/src/workflow/executor/dag/loop-node-strategy.d.ts +13 -0
- package/esm/src/workflow/executor/dag/loop-node-strategy.d.ts.map +1 -0
- package/esm/src/workflow/executor/dag/loop-node-strategy.js +134 -0
- package/esm/src/workflow/executor/dag/map-node-strategy.d.ts +13 -0
- package/esm/src/workflow/executor/dag/map-node-strategy.d.ts.map +1 -0
- package/esm/src/workflow/executor/dag/map-node-strategy.js +78 -0
- package/esm/src/workflow/executor/dag/node-strategy-types.d.ts +11 -0
- package/esm/src/workflow/executor/dag/node-strategy-types.d.ts.map +1 -0
- package/esm/src/workflow/executor/dag/node-strategy-types.js +1 -0
- package/esm/src/workflow/worker/run-manager.d.ts.map +1 -1
- package/esm/src/workflow/worker/run-manager.js +1 -0
- package/package.json +1 -1
package/esm/deno.js
CHANGED
|
@@ -15,11 +15,10 @@ export declare class DAGExecutor {
|
|
|
15
15
|
private executeNode;
|
|
16
16
|
private executeStepNode;
|
|
17
17
|
private executeParallelNode;
|
|
18
|
-
private executeMapNode;
|
|
19
18
|
private executeBranchNode;
|
|
20
19
|
private executeWaitNode;
|
|
21
20
|
private executeSubWorkflowNode;
|
|
22
|
-
private executeLoopNode;
|
|
23
21
|
private checkpoint;
|
|
22
|
+
private executeChildGraph;
|
|
24
23
|
}
|
|
25
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAQV,YAAY,EAEZ,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAIxB,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE7F,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAOpB,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,iBAAiB;IAQ/B,OAAO,CACX,KAAK,EAAE,YAAY,EAAE,EACrB,GAAG,EAAE,WAAW,EAChB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,kBAAkB,CAAC;YAsGhB,WAAW;YAgEX,eAAe;YA0Bf,mBAAmB;YA4CnB,iBAAiB;YA+DjB,eAAe;YA8Bf,sBAAsB;YA+DtB,UAAU;YAqBV,iBAAiB;CAiBhC"}
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module ai/workflow/executor/dag
|
|
7
7
|
*/
|
|
8
|
-
import { generateId
|
|
8
|
+
import { generateId } from "../../types.js";
|
|
9
9
|
import { INVALID_ARGUMENT, NOT_SUPPORTED } from "../../../errors/index.js";
|
|
10
|
-
import { deriveNodeStatus, shouldCheckpoint
|
|
10
|
+
import { deriveNodeStatus, shouldCheckpoint } from "./utils.js";
|
|
11
11
|
import { buildGraph, getReadyNodes, hasCycle, updateInDegreesForCompletedNodes } from "./graph.js";
|
|
12
|
+
import { executeLoopNodeStrategy } from "./loop-node-strategy.js";
|
|
13
|
+
import { executeMapNodeStrategy } from "./map-node-strategy.js";
|
|
12
14
|
export class DAGExecutor {
|
|
13
15
|
config;
|
|
14
16
|
constructor(config) {
|
|
@@ -118,7 +120,16 @@ export class DAGExecutor {
|
|
|
118
120
|
case "parallel":
|
|
119
121
|
return this.executeParallelNode(node, config, context, nodeStates);
|
|
120
122
|
case "map":
|
|
121
|
-
return
|
|
123
|
+
return executeMapNodeStrategy({
|
|
124
|
+
node,
|
|
125
|
+
config,
|
|
126
|
+
context,
|
|
127
|
+
nodeStates,
|
|
128
|
+
runtime: {
|
|
129
|
+
executeChildGraph: (nodes, run, options) => this.executeChildGraph(nodes, run, options),
|
|
130
|
+
onNodeComplete: this.config.onNodeComplete,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
122
133
|
case "branch":
|
|
123
134
|
return this.executeBranchNode(node, config, context, nodeStates);
|
|
124
135
|
case "wait":
|
|
@@ -126,7 +137,16 @@ export class DAGExecutor {
|
|
|
126
137
|
case "subWorkflow":
|
|
127
138
|
return this.executeSubWorkflowNode(node, config, context);
|
|
128
139
|
case "loop":
|
|
129
|
-
return
|
|
140
|
+
return executeLoopNodeStrategy({
|
|
141
|
+
node,
|
|
142
|
+
config,
|
|
143
|
+
context,
|
|
144
|
+
nodeStates,
|
|
145
|
+
runtime: {
|
|
146
|
+
executeChildGraph: (nodes, run) => this.executeChildGraph(nodes, run),
|
|
147
|
+
onNodeComplete: this.config.onNodeComplete,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
130
150
|
default:
|
|
131
151
|
throw INVALID_ARGUMENT.create({
|
|
132
152
|
detail: `Unknown node type "${config.type}" for node "${node.id}". ` +
|
|
@@ -186,85 +206,6 @@ export class DAGExecutor {
|
|
|
186
206
|
waiting: result.waiting,
|
|
187
207
|
};
|
|
188
208
|
}
|
|
189
|
-
async executeMapNode(node, config, context, nodeStates) {
|
|
190
|
-
const startTime = Date.now();
|
|
191
|
-
const items = typeof config.items === "function" ? await config.items(context) : config.items;
|
|
192
|
-
if (!Array.isArray(items)) {
|
|
193
|
-
throw INVALID_ARGUMENT.create({ detail: `Map node "${node.id}" items must be an array` });
|
|
194
|
-
}
|
|
195
|
-
if (items.length === 0) {
|
|
196
|
-
const state = {
|
|
197
|
-
nodeId: node.id,
|
|
198
|
-
status: "completed",
|
|
199
|
-
output: [],
|
|
200
|
-
attempt: 1,
|
|
201
|
-
startedAt: new Date(startTime),
|
|
202
|
-
completedAt: new Date(),
|
|
203
|
-
};
|
|
204
|
-
return { state, contextUpdates: { [node.id]: [] }, waiting: false };
|
|
205
|
-
}
|
|
206
|
-
const isWorkflowDef = (p) => typeof p === "object" && p !== null && "steps" in p;
|
|
207
|
-
const childNodes = items.map((item, i) => {
|
|
208
|
-
const childId = `${node.id}_${i}`;
|
|
209
|
-
if (isWorkflowDef(config.processor)) {
|
|
210
|
-
return {
|
|
211
|
-
id: childId,
|
|
212
|
-
config: {
|
|
213
|
-
type: "subWorkflow",
|
|
214
|
-
workflow: config.processor,
|
|
215
|
-
input: item,
|
|
216
|
-
retry: config.retry,
|
|
217
|
-
checkpoint: false,
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
const processorConfig = { ...config.processor.config };
|
|
222
|
-
if (processorConfig.type === "step") {
|
|
223
|
-
processorConfig.input = item;
|
|
224
|
-
}
|
|
225
|
-
return { id: childId, config: processorConfig };
|
|
226
|
-
});
|
|
227
|
-
const originalConcurrency = this.config.maxConcurrency;
|
|
228
|
-
if (config.concurrency) {
|
|
229
|
-
this.config.maxConcurrency = config.concurrency;
|
|
230
|
-
}
|
|
231
|
-
try {
|
|
232
|
-
const result = await this.execute(childNodes, {
|
|
233
|
-
id: `${node.id}_map`,
|
|
234
|
-
workflowId: "",
|
|
235
|
-
status: "running",
|
|
236
|
-
input: context.input,
|
|
237
|
-
// Carry already-accumulated child states so completed children are
|
|
238
|
-
// skipped on resume instead of re-executing (H8).
|
|
239
|
-
nodeStates,
|
|
240
|
-
currentNodes: [],
|
|
241
|
-
context: { ...context },
|
|
242
|
-
checkpoints: [],
|
|
243
|
-
pendingApprovals: [],
|
|
244
|
-
createdAt: new Date(),
|
|
245
|
-
});
|
|
246
|
-
Object.assign(nodeStates, result.nodeStates);
|
|
247
|
-
const outputs = childNodes.map((child) => result.nodeStates[child.id]?.output);
|
|
248
|
-
const state = {
|
|
249
|
-
nodeId: node.id,
|
|
250
|
-
status: deriveNodeStatus(result.completed, result.waiting),
|
|
251
|
-
output: outputs,
|
|
252
|
-
error: result.error,
|
|
253
|
-
attempt: 1,
|
|
254
|
-
startedAt: new Date(startTime),
|
|
255
|
-
completedAt: result.completed ? new Date() : undefined,
|
|
256
|
-
};
|
|
257
|
-
this.config.onNodeComplete?.(node.id, state);
|
|
258
|
-
return {
|
|
259
|
-
state,
|
|
260
|
-
contextUpdates: result.completed ? { [node.id]: outputs } : {},
|
|
261
|
-
waiting: result.waiting,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
finally {
|
|
265
|
-
this.config.maxConcurrency = originalConcurrency;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
209
|
async executeBranchNode(node, config, context, nodeStates) {
|
|
269
210
|
const startTime = Date.now();
|
|
270
211
|
const conditionResult = await config.condition(context);
|
|
@@ -383,139 +324,6 @@ export class DAGExecutor {
|
|
|
383
324
|
waiting: result.waiting,
|
|
384
325
|
};
|
|
385
326
|
}
|
|
386
|
-
async executeLoopNode(node, config, context, nodeStates) {
|
|
387
|
-
const startTime = Date.now();
|
|
388
|
-
const previousResults = [];
|
|
389
|
-
let iteration = 0;
|
|
390
|
-
let exitReason = "condition";
|
|
391
|
-
let lastError;
|
|
392
|
-
const existingLoopState = context[`${node.id}_loop_state`];
|
|
393
|
-
// Child node states for the in-flight (resumed) iteration, so its already
|
|
394
|
-
// completed steps are not re-executed on resume (H9).
|
|
395
|
-
let resumeIterationNodeStates;
|
|
396
|
-
let resumeIteration;
|
|
397
|
-
if (existingLoopState) {
|
|
398
|
-
iteration = existingLoopState.iteration;
|
|
399
|
-
previousResults.push(...existingLoopState.previousResults);
|
|
400
|
-
resumeIterationNodeStates = existingLoopState.iterationNodeStates;
|
|
401
|
-
resumeIteration = existingLoopState.iteration;
|
|
402
|
-
}
|
|
403
|
-
while (iteration < config.maxIterations) {
|
|
404
|
-
const loopContext = {
|
|
405
|
-
iteration,
|
|
406
|
-
totalIterations: iteration,
|
|
407
|
-
previousResults: [...previousResults],
|
|
408
|
-
isFirstIteration: iteration === 0,
|
|
409
|
-
isLastAllowedIteration: iteration === config.maxIterations - 1,
|
|
410
|
-
};
|
|
411
|
-
if (!(await config.while(context, loopContext))) {
|
|
412
|
-
exitReason = "condition";
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
const steps = typeof config.steps === "function"
|
|
416
|
-
? config.steps(context, loopContext)
|
|
417
|
-
: config.steps;
|
|
418
|
-
// On resume, rehydrate the in-flight iteration's child node states so its
|
|
419
|
-
// already-completed steps are skipped instead of re-executed (H9).
|
|
420
|
-
const iterationNodeStates = resumeIteration === iteration && resumeIterationNodeStates
|
|
421
|
-
? { ...resumeIterationNodeStates }
|
|
422
|
-
: {};
|
|
423
|
-
// Only rehydrate once; subsequent iterations start fresh.
|
|
424
|
-
resumeIterationNodeStates = undefined;
|
|
425
|
-
const result = await this.execute(steps, {
|
|
426
|
-
id: `${node.id}_iter_${iteration}`,
|
|
427
|
-
workflowId: "",
|
|
428
|
-
status: "running",
|
|
429
|
-
input: context.input,
|
|
430
|
-
nodeStates: iterationNodeStates,
|
|
431
|
-
currentNodes: [],
|
|
432
|
-
context: { ...context, _loop: loopContext },
|
|
433
|
-
checkpoints: [],
|
|
434
|
-
pendingApprovals: [],
|
|
435
|
-
createdAt: new Date(),
|
|
436
|
-
});
|
|
437
|
-
if (result.waiting) {
|
|
438
|
-
Object.assign(nodeStates, result.nodeStates);
|
|
439
|
-
const state = {
|
|
440
|
-
nodeId: node.id,
|
|
441
|
-
status: "running",
|
|
442
|
-
output: { iteration, waiting: true, previousResults },
|
|
443
|
-
attempt: 1,
|
|
444
|
-
startedAt: new Date(startTime),
|
|
445
|
-
};
|
|
446
|
-
return {
|
|
447
|
-
state,
|
|
448
|
-
contextUpdates: {
|
|
449
|
-
...result.context,
|
|
450
|
-
[`${node.id}_loop_state`]: {
|
|
451
|
-
iteration,
|
|
452
|
-
previousResults,
|
|
453
|
-
// Persist the in-flight iteration's child states so completed
|
|
454
|
-
// steps are not re-executed when this iteration resumes (H9).
|
|
455
|
-
iterationNodeStates: result.nodeStates,
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
waiting: true,
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
if (result.error) {
|
|
462
|
-
lastError = result.error;
|
|
463
|
-
exitReason = "error";
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
previousResults.push(result.context);
|
|
467
|
-
Object.assign(context, result.context);
|
|
468
|
-
Object.assign(nodeStates, result.nodeStates);
|
|
469
|
-
if (config.delay && iteration < config.maxIterations - 1) {
|
|
470
|
-
const delayMs = typeof config.delay === "number"
|
|
471
|
-
? config.delay
|
|
472
|
-
: parseDuration(config.delay);
|
|
473
|
-
await sleep(delayMs);
|
|
474
|
-
}
|
|
475
|
-
iteration++;
|
|
476
|
-
}
|
|
477
|
-
if (iteration >= config.maxIterations && exitReason !== "condition") {
|
|
478
|
-
exitReason = "maxIterations";
|
|
479
|
-
}
|
|
480
|
-
const finalLoopContext = {
|
|
481
|
-
iteration,
|
|
482
|
-
totalIterations: iteration,
|
|
483
|
-
previousResults,
|
|
484
|
-
isFirstIteration: false,
|
|
485
|
-
isLastAllowedIteration: true,
|
|
486
|
-
};
|
|
487
|
-
let completionUpdates = {};
|
|
488
|
-
if (exitReason === "maxIterations" && config.onMaxIterations) {
|
|
489
|
-
completionUpdates = await config.onMaxIterations(context, finalLoopContext);
|
|
490
|
-
}
|
|
491
|
-
else if (exitReason === "condition" && config.onComplete) {
|
|
492
|
-
completionUpdates = await config.onComplete(context, finalLoopContext);
|
|
493
|
-
}
|
|
494
|
-
const output = {
|
|
495
|
-
exitReason,
|
|
496
|
-
iterations: iteration,
|
|
497
|
-
previousResults,
|
|
498
|
-
...completionUpdates,
|
|
499
|
-
};
|
|
500
|
-
const state = {
|
|
501
|
-
nodeId: node.id,
|
|
502
|
-
status: exitReason === "error" ? "failed" : "completed",
|
|
503
|
-
output,
|
|
504
|
-
error: lastError,
|
|
505
|
-
attempt: 1,
|
|
506
|
-
startedAt: new Date(startTime),
|
|
507
|
-
completedAt: new Date(),
|
|
508
|
-
};
|
|
509
|
-
this.config.onNodeComplete?.(node.id, state);
|
|
510
|
-
return {
|
|
511
|
-
state,
|
|
512
|
-
contextUpdates: {
|
|
513
|
-
[node.id]: output,
|
|
514
|
-
...completionUpdates,
|
|
515
|
-
},
|
|
516
|
-
waiting: false,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
327
|
async checkpoint(runId, nodeId, context, nodeStates) {
|
|
520
328
|
if (!this.config.checkpointManager) {
|
|
521
329
|
return;
|
|
@@ -529,4 +337,17 @@ export class DAGExecutor {
|
|
|
529
337
|
};
|
|
530
338
|
await this.config.checkpointManager.save(runId, checkpoint);
|
|
531
339
|
}
|
|
340
|
+
async executeChildGraph(nodes, run, options) {
|
|
341
|
+
if (!options?.maxConcurrency) {
|
|
342
|
+
return await this.execute(nodes, run);
|
|
343
|
+
}
|
|
344
|
+
const originalConcurrency = this.config.maxConcurrency;
|
|
345
|
+
this.config.maxConcurrency = options.maxConcurrency;
|
|
346
|
+
try {
|
|
347
|
+
return await this.execute(nodes, run);
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
this.config.maxConcurrency = originalConcurrency;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
532
353
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LoopNodeConfig, NodeState, WorkflowContext, WorkflowNode } from "../../types.js";
|
|
2
|
+
import type { NodeExecutionResult } from "./types.js";
|
|
3
|
+
import type { NodeStrategyRuntime } from "./node-strategy-types.js";
|
|
4
|
+
interface ExecuteLoopNodeStrategyInput {
|
|
5
|
+
node: WorkflowNode;
|
|
6
|
+
config: LoopNodeConfig;
|
|
7
|
+
context: WorkflowContext;
|
|
8
|
+
nodeStates: Record<string, NodeState>;
|
|
9
|
+
runtime: NodeStrategyRuntime;
|
|
10
|
+
}
|
|
11
|
+
export declare function executeLoopNodeStrategy(input: ExecuteLoopNodeStrategyInput): Promise<NodeExecutionResult>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=loop-node-strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-node-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/loop-node-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,EACd,SAAS,EACT,eAAe,EACf,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,UAAU,4BAA4B;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAQD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAwJ9B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { parseDuration } from "../../types.js";
|
|
2
|
+
import { sleep } from "./utils.js";
|
|
3
|
+
export async function executeLoopNodeStrategy(input) {
|
|
4
|
+
const { node, config, context, nodeStates, runtime } = input;
|
|
5
|
+
const startTime = Date.now();
|
|
6
|
+
const previousResults = [];
|
|
7
|
+
let iteration = 0;
|
|
8
|
+
let exitReason = "condition";
|
|
9
|
+
let lastError;
|
|
10
|
+
const existingLoopState = context[`${node.id}_loop_state`];
|
|
11
|
+
// Child node states for the in-flight (resumed) iteration, so its already
|
|
12
|
+
// completed steps are not re-executed on resume (H9).
|
|
13
|
+
let resumeIterationNodeStates;
|
|
14
|
+
let resumeIteration;
|
|
15
|
+
if (existingLoopState) {
|
|
16
|
+
iteration = existingLoopState.iteration;
|
|
17
|
+
previousResults.push(...existingLoopState.previousResults);
|
|
18
|
+
resumeIterationNodeStates = existingLoopState.iterationNodeStates;
|
|
19
|
+
resumeIteration = existingLoopState.iteration;
|
|
20
|
+
}
|
|
21
|
+
while (iteration < config.maxIterations) {
|
|
22
|
+
const loopContext = {
|
|
23
|
+
iteration,
|
|
24
|
+
totalIterations: iteration,
|
|
25
|
+
previousResults: [...previousResults],
|
|
26
|
+
isFirstIteration: iteration === 0,
|
|
27
|
+
isLastAllowedIteration: iteration === config.maxIterations - 1,
|
|
28
|
+
};
|
|
29
|
+
if (!(await config.while(context, loopContext))) {
|
|
30
|
+
exitReason = "condition";
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
const steps = typeof config.steps === "function"
|
|
34
|
+
? config.steps(context, loopContext)
|
|
35
|
+
: config.steps;
|
|
36
|
+
// On resume, rehydrate the in-flight iteration's child node states so its
|
|
37
|
+
// already-completed steps are skipped instead of re-executed (H9).
|
|
38
|
+
const iterationNodeStates = resumeIteration === iteration && resumeIterationNodeStates
|
|
39
|
+
? { ...resumeIterationNodeStates }
|
|
40
|
+
: {};
|
|
41
|
+
// Only rehydrate once; subsequent iterations start fresh.
|
|
42
|
+
resumeIterationNodeStates = undefined;
|
|
43
|
+
const result = await runtime.executeChildGraph(steps, {
|
|
44
|
+
id: `${node.id}_iter_${iteration}`,
|
|
45
|
+
workflowId: "",
|
|
46
|
+
status: "running",
|
|
47
|
+
input: context.input,
|
|
48
|
+
nodeStates: iterationNodeStates,
|
|
49
|
+
currentNodes: [],
|
|
50
|
+
context: { ...context, _loop: loopContext },
|
|
51
|
+
checkpoints: [],
|
|
52
|
+
pendingApprovals: [],
|
|
53
|
+
createdAt: new Date(),
|
|
54
|
+
});
|
|
55
|
+
if (result.waiting) {
|
|
56
|
+
Object.assign(nodeStates, result.nodeStates);
|
|
57
|
+
const state = {
|
|
58
|
+
nodeId: node.id,
|
|
59
|
+
status: "running",
|
|
60
|
+
output: { iteration, waiting: true, previousResults },
|
|
61
|
+
attempt: 1,
|
|
62
|
+
startedAt: new Date(startTime),
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
state,
|
|
66
|
+
contextUpdates: {
|
|
67
|
+
...result.context,
|
|
68
|
+
[`${node.id}_loop_state`]: {
|
|
69
|
+
iteration,
|
|
70
|
+
previousResults,
|
|
71
|
+
// Persist the in-flight iteration's child states so completed
|
|
72
|
+
// steps are not re-executed when this iteration resumes (H9).
|
|
73
|
+
iterationNodeStates: result.nodeStates,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
waiting: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (result.error) {
|
|
80
|
+
lastError = result.error;
|
|
81
|
+
exitReason = "error";
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
previousResults.push(result.context);
|
|
85
|
+
Object.assign(context, result.context);
|
|
86
|
+
Object.assign(nodeStates, result.nodeStates);
|
|
87
|
+
if (config.delay && iteration < config.maxIterations - 1) {
|
|
88
|
+
const delayMs = typeof config.delay === "number" ? config.delay : parseDuration(config.delay);
|
|
89
|
+
await sleep(delayMs);
|
|
90
|
+
}
|
|
91
|
+
iteration++;
|
|
92
|
+
}
|
|
93
|
+
if (iteration >= config.maxIterations && exitReason !== "condition") {
|
|
94
|
+
exitReason = "maxIterations";
|
|
95
|
+
}
|
|
96
|
+
const finalLoopContext = {
|
|
97
|
+
iteration,
|
|
98
|
+
totalIterations: iteration,
|
|
99
|
+
previousResults,
|
|
100
|
+
isFirstIteration: false,
|
|
101
|
+
isLastAllowedIteration: true,
|
|
102
|
+
};
|
|
103
|
+
let completionUpdates = {};
|
|
104
|
+
if (exitReason === "maxIterations" && config.onMaxIterations) {
|
|
105
|
+
completionUpdates = await config.onMaxIterations(context, finalLoopContext);
|
|
106
|
+
}
|
|
107
|
+
else if (exitReason === "condition" && config.onComplete) {
|
|
108
|
+
completionUpdates = await config.onComplete(context, finalLoopContext);
|
|
109
|
+
}
|
|
110
|
+
const output = {
|
|
111
|
+
exitReason,
|
|
112
|
+
iterations: iteration,
|
|
113
|
+
previousResults,
|
|
114
|
+
...completionUpdates,
|
|
115
|
+
};
|
|
116
|
+
const state = {
|
|
117
|
+
nodeId: node.id,
|
|
118
|
+
status: exitReason === "error" ? "failed" : "completed",
|
|
119
|
+
output,
|
|
120
|
+
error: lastError,
|
|
121
|
+
attempt: 1,
|
|
122
|
+
startedAt: new Date(startTime),
|
|
123
|
+
completedAt: new Date(),
|
|
124
|
+
};
|
|
125
|
+
runtime.onNodeComplete?.(node.id, state);
|
|
126
|
+
return {
|
|
127
|
+
state,
|
|
128
|
+
contextUpdates: {
|
|
129
|
+
[node.id]: output,
|
|
130
|
+
...completionUpdates,
|
|
131
|
+
},
|
|
132
|
+
waiting: false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MapNodeConfig, NodeState, WorkflowContext, WorkflowNode } from "../../types.js";
|
|
2
|
+
import type { NodeExecutionResult } from "./types.js";
|
|
3
|
+
import type { NodeStrategyRuntime } from "./node-strategy-types.js";
|
|
4
|
+
interface ExecuteMapNodeStrategyInput {
|
|
5
|
+
node: WorkflowNode;
|
|
6
|
+
config: MapNodeConfig;
|
|
7
|
+
context: WorkflowContext;
|
|
8
|
+
nodeStates: Record<string, NodeState>;
|
|
9
|
+
runtime: NodeStrategyRuntime;
|
|
10
|
+
}
|
|
11
|
+
export declare function executeMapNodeStrategy(input: ExecuteMapNodeStrategyInput): Promise<NodeExecutionResult>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=map-node-strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-node-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/map-node-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,eAAe,EAEf,YAAY,EAEb,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,UAAU,2BAA2B;IACnC,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAqCD,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAgE9B"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { INVALID_ARGUMENT } from "../../../errors/index.js";
|
|
2
|
+
import { deriveNodeStatus } from "./utils.js";
|
|
3
|
+
function isWorkflowDefinition(processor) {
|
|
4
|
+
return typeof processor === "object" && processor !== null && "steps" in processor;
|
|
5
|
+
}
|
|
6
|
+
function createMapChildNodes(node, config, items) {
|
|
7
|
+
return items.map((item, i) => {
|
|
8
|
+
const childId = `${node.id}_${i}`;
|
|
9
|
+
if (isWorkflowDefinition(config.processor)) {
|
|
10
|
+
return {
|
|
11
|
+
id: childId,
|
|
12
|
+
config: {
|
|
13
|
+
type: "subWorkflow",
|
|
14
|
+
workflow: config.processor,
|
|
15
|
+
input: item,
|
|
16
|
+
retry: config.retry,
|
|
17
|
+
checkpoint: false,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const processorConfig = { ...config.processor.config };
|
|
22
|
+
if (processorConfig.type === "step") {
|
|
23
|
+
processorConfig.input = item;
|
|
24
|
+
}
|
|
25
|
+
return { id: childId, config: processorConfig };
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export async function executeMapNodeStrategy(input) {
|
|
29
|
+
const { node, config, context, nodeStates, runtime } = input;
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
const items = typeof config.items === "function" ? await config.items(context) : config.items;
|
|
32
|
+
if (!Array.isArray(items)) {
|
|
33
|
+
throw INVALID_ARGUMENT.create({ detail: `Map node "${node.id}" items must be an array` });
|
|
34
|
+
}
|
|
35
|
+
if (items.length === 0) {
|
|
36
|
+
const state = {
|
|
37
|
+
nodeId: node.id,
|
|
38
|
+
status: "completed",
|
|
39
|
+
output: [],
|
|
40
|
+
attempt: 1,
|
|
41
|
+
startedAt: new Date(startTime),
|
|
42
|
+
completedAt: new Date(),
|
|
43
|
+
};
|
|
44
|
+
return { state, contextUpdates: { [node.id]: [] }, waiting: false };
|
|
45
|
+
}
|
|
46
|
+
const childNodes = createMapChildNodes(node, config, items);
|
|
47
|
+
const result = await runtime.executeChildGraph(childNodes, {
|
|
48
|
+
id: `${node.id}_map`,
|
|
49
|
+
workflowId: "",
|
|
50
|
+
status: "running",
|
|
51
|
+
input: context.input,
|
|
52
|
+
// Carry already-accumulated child states so completed children are
|
|
53
|
+
// skipped on resume instead of re-executing (H8).
|
|
54
|
+
nodeStates,
|
|
55
|
+
currentNodes: [],
|
|
56
|
+
context: { ...context },
|
|
57
|
+
checkpoints: [],
|
|
58
|
+
pendingApprovals: [],
|
|
59
|
+
createdAt: new Date(),
|
|
60
|
+
}, config.concurrency ? { maxConcurrency: config.concurrency } : undefined);
|
|
61
|
+
Object.assign(nodeStates, result.nodeStates);
|
|
62
|
+
const outputs = childNodes.map((child) => result.nodeStates[child.id]?.output);
|
|
63
|
+
const state = {
|
|
64
|
+
nodeId: node.id,
|
|
65
|
+
status: deriveNodeStatus(result.completed, result.waiting),
|
|
66
|
+
output: outputs,
|
|
67
|
+
error: result.error,
|
|
68
|
+
attempt: 1,
|
|
69
|
+
startedAt: new Date(startTime),
|
|
70
|
+
completedAt: result.completed ? new Date() : undefined,
|
|
71
|
+
};
|
|
72
|
+
runtime.onNodeComplete?.(node.id, state);
|
|
73
|
+
return {
|
|
74
|
+
state,
|
|
75
|
+
contextUpdates: result.completed ? { [node.id]: outputs } : {},
|
|
76
|
+
waiting: result.waiting,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NodeState, WorkflowNode, WorkflowRun } from "../../types.js";
|
|
2
|
+
import type { DAGExecutionResult } from "./types.js";
|
|
3
|
+
export interface ChildGraphExecutionOptions {
|
|
4
|
+
maxConcurrency?: number;
|
|
5
|
+
}
|
|
6
|
+
export type ExecuteChildGraph = (nodes: WorkflowNode[], run: WorkflowRun, options?: ChildGraphExecutionOptions) => Promise<DAGExecutionResult>;
|
|
7
|
+
export interface NodeStrategyRuntime {
|
|
8
|
+
executeChildGraph: ExecuteChildGraph;
|
|
9
|
+
onNodeComplete?: (nodeId: string, state: NodeState) => void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=node-strategy-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-strategy-types.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/node-strategy-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,YAAY,EAAE,EACrB,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE,0BAA0B,KACjC,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-manager.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/worker/run-manager.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG9F,OAAO,KAAK,EAAsB,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAehG,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE9F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,OAAO,EAAE,eAAe,CAAC;IAEzB,6DAA6D;IAC7D,QAAQ,EAAE,WAAW,CAAC;IAEtB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,wCAAwC;IACxC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,EAAE,IAAI,CAAC;CACjB;AAOD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,WAAW,CAAC,CAAwC;IAC5D,OAAO,CAAC,gBAAgB,CAAuC;IAC/D,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,wBAAwB;IAuB5C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B3B;;OAEG;IACH,QAAQ,IAAI,YAAY;IAIxB;;OAEG;IACH,mBAAmB,IAAI,gBAAgB,EAAE;IAIzC;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;YACW,IAAI;IAyGlB;;OAEG;YACW,wBAAwB;IAuCtC;;OAEG;YACW,0BAA0B;
|
|
1
|
+
{"version":3,"file":"run-manager.d.ts","sourceRoot":"","sources":["../../../../src/src/workflow/worker/run-manager.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAoC,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG9F,OAAO,KAAK,EAAsB,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAehG,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE9F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,OAAO,EAAE,eAAe,CAAC;IAEzB,6DAA6D;IAC7D,QAAQ,EAAE,WAAW,CAAC;IAEtB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,wCAAwC;IACxC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,EAAE,IAAI,CAAC;CACjB;AAOD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,WAAW,CAAC,CAAwC;IAC5D,OAAO,CAAC,gBAAgB,CAAuC;IAC/D,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,wBAAwB;IAuB5C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B3B;;OAEG;IACH,QAAQ,IAAI,YAAY;IAIxB;;OAEG;IACH,mBAAmB,IAAI,gBAAgB,EAAE;IAIzC;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;YACW,IAAI;IAyGlB;;OAEG;YACW,wBAAwB;IAuCtC;;OAEG;YACW,0BAA0B;IAqDxC;;OAEG;IACH,OAAO,CAAC,WAAW;CAIpB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,kBAAkB,CAEpB"}
|
|
@@ -303,6 +303,7 @@ export class WorkflowRunManager {
|
|
|
303
303
|
}
|
|
304
304
|
catch (error) {
|
|
305
305
|
logger.error(`Failed to create run execution for ${run.id}:`, error);
|
|
306
|
+
this.stats.executionsFailed++;
|
|
306
307
|
// Mark workflow as failed
|
|
307
308
|
await this.config.backend.updateRun(run.id, {
|
|
308
309
|
status: "failed",
|