@workglow/task-graph 0.0.114 → 0.0.116

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 (39) hide show
  1. package/dist/Workflow-7eps4dfz.js +31 -0
  2. package/dist/Workflow-7eps4dfz.js.map +9 -0
  3. package/dist/Workflow-v7wec8kc.js +31 -0
  4. package/dist/Workflow-v7wec8kc.js.map +9 -0
  5. package/dist/Workflow-wrs2y87g.js +32 -0
  6. package/dist/Workflow-wrs2y87g.js.map +9 -0
  7. package/dist/browser-bmjr6xz5.js +4245 -0
  8. package/dist/browser-bmjr6xz5.js.map +28 -0
  9. package/dist/browser.js +482 -4059
  10. package/dist/browser.js.map +11 -28
  11. package/dist/bun-tgs39x49.js +4240 -0
  12. package/dist/bun-tgs39x49.js.map +28 -0
  13. package/dist/bun.js +482 -4059
  14. package/dist/bun.js.map +11 -28
  15. package/dist/common.d.ts +1 -0
  16. package/dist/common.d.ts.map +1 -1
  17. package/dist/node-mthgc8tr.js +4240 -0
  18. package/dist/node-mthgc8tr.js.map +28 -0
  19. package/dist/node.js +482 -4059
  20. package/dist/node.js.map +11 -28
  21. package/dist/task/GraphAsTask.d.ts +14 -1
  22. package/dist/task/GraphAsTask.d.ts.map +1 -1
  23. package/dist/task/ITask.d.ts.map +1 -1
  24. package/dist/task/IteratorTaskRunner.d.ts.map +1 -1
  25. package/dist/task/MapTask.d.ts.map +1 -1
  26. package/dist/task/Task.d.ts.map +1 -1
  27. package/dist/task/TaskRunner.d.ts +5 -5
  28. package/dist/task/TaskRunner.d.ts.map +1 -1
  29. package/dist/task-graph/Conversions.d.ts +0 -15
  30. package/dist/task-graph/Conversions.d.ts.map +1 -1
  31. package/dist/task-graph/GraphToWorkflowCode.d.ts +46 -0
  32. package/dist/task-graph/GraphToWorkflowCode.d.ts.map +1 -0
  33. package/dist/task-graph/IWorkflow.d.ts +6 -1
  34. package/dist/task-graph/IWorkflow.d.ts.map +1 -1
  35. package/dist/task-graph/TaskGraphRunner.d.ts +5 -5
  36. package/dist/task-graph/TaskGraphRunner.d.ts.map +1 -1
  37. package/dist/task-graph/Workflow.d.ts +19 -3
  38. package/dist/task-graph/Workflow.d.ts.map +1 -1
  39. package/package.json +7 -7
package/dist/bun.js CHANGED
@@ -1,4065 +1,432 @@
1
1
  // @bun
2
- // src/task-graph/Dataflow.ts
3
- import { areSemanticallyCompatible, EventEmitter } from "@workglow/util";
4
-
5
- // src/task/TaskTypes.ts
6
- var TaskStatus = {
7
- PENDING: "PENDING",
8
- DISABLED: "DISABLED",
9
- PROCESSING: "PROCESSING",
10
- STREAMING: "STREAMING",
11
- COMPLETED: "COMPLETED",
12
- ABORTING: "ABORTING",
13
- FAILED: "FAILED"
14
- };
15
- var TaskConfigSchema = {
16
- type: "object",
17
- properties: {
18
- id: {
19
- "x-ui-hidden": true
20
- },
21
- title: { type: "string" },
22
- description: { type: "string" },
23
- cacheable: { type: "boolean" },
24
- timeout: { type: "number", description: "Max execution time in milliseconds" },
25
- inputSchema: {
26
- type: "object",
27
- properties: {},
28
- additionalProperties: true,
29
- "x-ui-hidden": true
30
- },
31
- outputSchema: {
32
- type: "object",
33
- properties: {},
34
- additionalProperties: true,
35
- "x-ui-hidden": true
36
- },
37
- extras: {
38
- type: "object",
39
- additionalProperties: true,
40
- "x-ui-hidden": true
41
- }
42
- },
43
- additionalProperties: false
44
- };
45
-
46
- // src/task-graph/Dataflow.ts
47
- var DATAFLOW_ALL_PORTS = "*";
48
- var DATAFLOW_ERROR_PORT = "[error]";
49
-
50
- class Dataflow {
51
- sourceTaskId;
52
- sourceTaskPortId;
53
- targetTaskId;
54
- targetTaskPortId;
55
- constructor(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId) {
56
- this.sourceTaskId = sourceTaskId;
57
- this.sourceTaskPortId = sourceTaskPortId;
58
- this.targetTaskId = targetTaskId;
59
- this.targetTaskPortId = targetTaskPortId;
60
- }
61
- static createId(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId) {
62
- return `${sourceTaskId}[${sourceTaskPortId}] ==> ${targetTaskId}[${targetTaskPortId}]`;
63
- }
64
- get id() {
65
- return Dataflow.createId(this.sourceTaskId, this.sourceTaskPortId, this.targetTaskId, this.targetTaskPortId);
66
- }
67
- value = undefined;
68
- status = TaskStatus.PENDING;
69
- error;
70
- stream = undefined;
71
- setStream(stream) {
72
- this.stream = stream;
73
- }
74
- getStream() {
75
- return this.stream;
76
- }
77
- async awaitStreamValue() {
78
- if (!this.stream)
79
- return;
80
- const reader = this.stream.getReader();
81
- let lastSnapshotData = undefined;
82
- let finishData = undefined;
83
- let streamError;
84
- try {
85
- while (true) {
86
- const { done, value: event } = await reader.read();
87
- if (done)
88
- break;
89
- switch (event.type) {
90
- case "snapshot":
91
- lastSnapshotData = event.data;
92
- break;
93
- case "finish":
94
- finishData = event.data;
95
- break;
96
- case "error":
97
- streamError = event.error;
98
- break;
99
- }
100
- }
101
- } finally {
102
- reader.releaseLock();
103
- this.stream = undefined;
104
- }
105
- if (streamError) {
106
- this.error = streamError;
107
- this.setStatus(TaskStatus.FAILED);
108
- throw streamError;
109
- }
110
- if (lastSnapshotData !== undefined) {
111
- this.setPortData(lastSnapshotData);
112
- } else if (finishData !== undefined) {
113
- this.setPortData(finishData);
114
- }
115
- }
116
- reset() {
117
- this.status = TaskStatus.PENDING;
118
- this.error = undefined;
119
- this.value = undefined;
120
- this.stream = undefined;
121
- this.emit("reset");
122
- this.emit("status", this.status);
123
- }
124
- setStatus(status) {
125
- if (status === this.status)
126
- return;
127
- this.status = status;
128
- switch (status) {
129
- case TaskStatus.PROCESSING:
130
- this.emit("start");
131
- break;
132
- case TaskStatus.STREAMING:
133
- this.emit("streaming");
134
- break;
135
- case TaskStatus.COMPLETED:
136
- this.emit("complete");
137
- break;
138
- case TaskStatus.ABORTING:
139
- this.emit("abort");
140
- break;
141
- case TaskStatus.PENDING:
142
- this.emit("reset");
143
- break;
144
- case TaskStatus.FAILED:
145
- this.emit("error", this.error);
146
- break;
147
- case TaskStatus.DISABLED:
148
- this.emit("disabled");
149
- break;
150
- }
151
- this.emit("status", this.status);
152
- }
153
- setPortData(entireDataBlock) {
154
- if (this.sourceTaskPortId === DATAFLOW_ALL_PORTS) {
155
- this.value = entireDataBlock;
156
- } else if (this.sourceTaskPortId === DATAFLOW_ERROR_PORT) {
157
- this.error = entireDataBlock;
158
- } else {
159
- this.value = entireDataBlock[this.sourceTaskPortId];
160
- }
161
- }
162
- getPortData() {
163
- let result;
164
- if (this.targetTaskPortId === DATAFLOW_ALL_PORTS) {
165
- result = this.value;
166
- } else if (this.targetTaskPortId === DATAFLOW_ERROR_PORT) {
167
- result = { [DATAFLOW_ERROR_PORT]: this.error };
168
- } else {
169
- result = { [this.targetTaskPortId]: this.value };
170
- }
171
- return result;
172
- }
173
- toJSON() {
174
- return {
175
- sourceTaskId: this.sourceTaskId,
176
- sourceTaskPortId: this.sourceTaskPortId,
177
- targetTaskId: this.targetTaskId,
178
- targetTaskPortId: this.targetTaskPortId
179
- };
180
- }
181
- semanticallyCompatible(graph, dataflow) {
182
- const targetSchema = graph.getTask(dataflow.targetTaskId).inputSchema();
183
- const sourceSchema = graph.getTask(dataflow.sourceTaskId).outputSchema();
184
- if (typeof targetSchema === "boolean") {
185
- if (targetSchema === false) {
186
- return "incompatible";
187
- }
188
- return "static";
189
- }
190
- if (typeof sourceSchema === "boolean") {
191
- if (sourceSchema === false) {
192
- return "incompatible";
193
- }
194
- return "runtime";
195
- }
196
- let targetSchemaProperty = DATAFLOW_ALL_PORTS === dataflow.targetTaskPortId ? true : targetSchema.properties?.[dataflow.targetTaskPortId];
197
- if (targetSchemaProperty === undefined && targetSchema.additionalProperties === true) {
198
- targetSchemaProperty = true;
199
- }
200
- let sourceSchemaProperty = DATAFLOW_ALL_PORTS === dataflow.sourceTaskPortId ? true : sourceSchema.properties?.[dataflow.sourceTaskPortId];
201
- if (sourceSchemaProperty === undefined && sourceSchema.additionalProperties === true) {
202
- sourceSchemaProperty = true;
203
- }
204
- const semanticallyCompatible = areSemanticallyCompatible(sourceSchemaProperty, targetSchemaProperty);
205
- return semanticallyCompatible;
206
- }
207
- get events() {
208
- if (!this._events) {
209
- this._events = new EventEmitter;
210
- }
211
- return this._events;
212
- }
213
- _events;
214
- subscribe(name, fn) {
215
- return this.events.subscribe(name, fn);
216
- }
217
- on(name, fn) {
218
- this.events.on(name, fn);
219
- }
220
- off(name, fn) {
221
- this.events.off(name, fn);
222
- }
223
- once(name, fn) {
224
- this.events.once(name, fn);
225
- }
226
- waitOn(name) {
227
- return this.events.waitOn(name);
228
- }
229
- emit(name, ...args) {
230
- this._events?.emit(name, ...args);
231
- }
232
- }
233
-
234
- class DataflowArrow extends Dataflow {
235
- constructor(dataflow) {
236
- const pattern = /^([a-zA-Z0-9-]+?)\[([a-zA-Z0-9-]+?)\] ==> ([a-zA-Z0-9-]+?)\[([a-zA-Z0-9-]+?)\]$/;
237
- const match = dataflow.match(pattern);
238
- if (!match) {
239
- throw new Error(`Invalid dataflow format: ${dataflow}`);
240
- }
241
- const [, sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId] = match;
242
- super(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId);
243
- }
244
- }
245
- // src/task-graph/GraphSchemaUtils.ts
246
- import { uuid4 } from "@workglow/util";
247
- function calculateNodeDepths(graph) {
248
- const depths = new Map;
249
- const tasks = graph.getTasks();
250
- for (const task of tasks) {
251
- depths.set(task.id, 0);
252
- }
253
- const sortedTasks = graph.topologicallySortedNodes();
254
- for (const task of sortedTasks) {
255
- const currentDepth = depths.get(task.id) || 0;
256
- const targetTasks = graph.getTargetTasks(task.id);
257
- for (const targetTask of targetTasks) {
258
- const targetDepth = depths.get(targetTask.id) || 0;
259
- depths.set(targetTask.id, Math.max(targetDepth, currentDepth + 1));
260
- }
261
- }
262
- return depths;
263
- }
264
- function computeGraphInputSchema(graph, options) {
265
- const trackOrigins = options?.trackOrigins ?? false;
266
- const properties = {};
267
- const required = [];
268
- const propertyOrigins = {};
269
- const tasks = graph.getTasks();
270
- const startingNodes = tasks.filter((task) => graph.getSourceDataflows(task.id).length === 0);
271
- for (const task of startingNodes) {
272
- const taskInputSchema = task.inputSchema();
273
- if (typeof taskInputSchema === "boolean") {
274
- if (taskInputSchema === false) {
275
- continue;
276
- }
277
- if (taskInputSchema === true) {
278
- properties[DATAFLOW_ALL_PORTS] = {};
279
- continue;
280
- }
281
- }
282
- const taskProperties = taskInputSchema.properties || {};
283
- for (const [inputName, inputProp] of Object.entries(taskProperties)) {
284
- if (!properties[inputName]) {
285
- properties[inputName] = inputProp;
286
- if (taskInputSchema.required && taskInputSchema.required.includes(inputName)) {
287
- required.push(inputName);
288
- }
289
- if (trackOrigins) {
290
- propertyOrigins[inputName] = [task.id];
291
- }
292
- } else if (trackOrigins) {
293
- propertyOrigins[inputName].push(task.id);
294
- }
295
- }
296
- }
297
- const sourceIds = new Set(startingNodes.map((t) => t.id));
298
- for (const task of tasks) {
299
- if (sourceIds.has(task.id))
300
- continue;
301
- const taskInputSchema = task.inputSchema();
302
- if (typeof taskInputSchema === "boolean")
303
- continue;
304
- const requiredKeys = new Set(taskInputSchema.required || []);
305
- if (requiredKeys.size === 0)
306
- continue;
307
- const connectedPorts = new Set(graph.getSourceDataflows(task.id).map((df) => df.targetTaskPortId));
308
- for (const key of requiredKeys) {
309
- if (connectedPorts.has(key))
310
- continue;
311
- if (properties[key]) {
312
- if (trackOrigins) {
313
- propertyOrigins[key].push(task.id);
314
- }
315
- continue;
316
- }
317
- if (task.defaults && task.defaults[key] !== undefined)
318
- continue;
319
- const prop = (taskInputSchema.properties || {})[key];
320
- if (!prop || typeof prop === "boolean")
321
- continue;
322
- properties[key] = prop;
323
- if (!required.includes(key)) {
324
- required.push(key);
325
- }
326
- if (trackOrigins) {
327
- propertyOrigins[key] = [task.id];
328
- }
329
- }
330
- }
331
- if (trackOrigins) {
332
- for (const [propName, origins] of Object.entries(propertyOrigins)) {
333
- const prop = properties[propName];
334
- if (!prop || typeof prop === "boolean")
335
- continue;
336
- if (origins.length === 1) {
337
- properties[propName] = { ...prop, "x-source-task-id": origins[0] };
338
- } else {
339
- properties[propName] = { ...prop, "x-source-task-ids": origins };
340
- }
341
- }
342
- }
343
- return {
344
- type: "object",
345
- properties,
346
- ...required.length > 0 ? { required } : {},
347
- additionalProperties: false
348
- };
349
- }
350
- function computeGraphOutputSchema(graph, options) {
351
- const trackOrigins = options?.trackOrigins ?? false;
352
- const properties = {};
353
- const required = [];
354
- const propertyOrigins = {};
355
- const tasks = graph.getTasks();
356
- const endingNodes = tasks.filter((task) => graph.getTargetDataflows(task.id).length === 0);
357
- const depths = calculateNodeDepths(graph);
358
- const maxDepth = Math.max(...endingNodes.map((task) => depths.get(task.id) || 0));
359
- const lastLevelNodes = endingNodes.filter((task) => depths.get(task.id) === maxDepth);
360
- const propertyCount = {};
361
- const propertySchema = {};
362
- for (const task of lastLevelNodes) {
363
- const taskOutputSchema = task.outputSchema();
364
- if (typeof taskOutputSchema === "boolean") {
365
- if (taskOutputSchema === false) {
366
- continue;
367
- }
368
- if (taskOutputSchema === true) {
369
- properties[DATAFLOW_ALL_PORTS] = {};
370
- continue;
371
- }
372
- }
373
- const taskProperties = taskOutputSchema.properties || {};
374
- for (const [outputName, outputProp] of Object.entries(taskProperties)) {
375
- propertyCount[outputName] = (propertyCount[outputName] || 0) + 1;
376
- if (!propertySchema[outputName]) {
377
- propertySchema[outputName] = outputProp;
378
- }
379
- if (trackOrigins) {
380
- if (!propertyOrigins[outputName]) {
381
- propertyOrigins[outputName] = [task.id];
382
- } else {
383
- propertyOrigins[outputName].push(task.id);
384
- }
385
- }
386
- }
387
- }
388
- for (const [outputName] of Object.entries(propertyCount)) {
389
- const outputProp = propertySchema[outputName];
390
- if (lastLevelNodes.length === 1) {
391
- properties[outputName] = outputProp;
392
- } else {
393
- properties[outputName] = {
394
- type: "array",
395
- items: outputProp
396
- };
397
- }
398
- }
399
- if (trackOrigins) {
400
- for (const [propName, origins] of Object.entries(propertyOrigins)) {
401
- const prop = properties[propName];
402
- if (!prop || typeof prop === "boolean")
403
- continue;
404
- if (origins.length === 1) {
405
- properties[propName] = { ...prop, "x-source-task-id": origins[0] };
406
- } else {
407
- properties[propName] = { ...prop, "x-source-task-ids": origins };
408
- }
409
- }
410
- }
411
- return {
412
- type: "object",
413
- properties,
414
- ...required.length > 0 ? { required } : {},
415
- additionalProperties: false
416
- };
417
- }
418
- function stripOriginAnnotations(schema) {
419
- if (typeof schema === "boolean" || !schema || typeof schema !== "object")
420
- return schema;
421
- const properties = schema.properties;
422
- if (!properties)
423
- return schema;
424
- const strippedProperties = {};
425
- for (const [key, prop] of Object.entries(properties)) {
426
- if (!prop || typeof prop !== "object") {
427
- strippedProperties[key] = prop;
428
- continue;
429
- }
430
- const {
431
- "x-source-task-id": _id,
432
- "x-source-task-ids": _ids,
433
- ...rest
434
- } = prop;
435
- strippedProperties[key] = rest;
436
- }
437
- return { ...schema, properties: strippedProperties };
438
- }
439
- function getOriginTaskIds(prop) {
440
- if (prop["x-source-task-ids"]) {
441
- return prop["x-source-task-ids"];
442
- }
443
- if (prop["x-source-task-id"] !== undefined) {
444
- return [prop["x-source-task-id"]];
445
- }
446
- return [];
447
- }
448
- function addBoundaryNodesToGraphJson(json, graph) {
449
- const hasInputTask = json.tasks.some((t) => t.type === "InputTask");
450
- const hasOutputTask = json.tasks.some((t) => t.type === "OutputTask");
451
- if (hasInputTask && hasOutputTask) {
452
- return json;
453
- }
454
- const inputSchema = !hasInputTask ? computeGraphInputSchema(graph, { trackOrigins: true }) : undefined;
455
- const outputSchema = !hasOutputTask ? computeGraphOutputSchema(graph, { trackOrigins: true }) : undefined;
456
- const prependTasks = [];
457
- const appendTasks = [];
458
- const inputDataflows = [];
459
- const outputDataflows = [];
460
- if (!hasInputTask && inputSchema) {
461
- const inputTaskId = uuid4();
462
- const strippedInputSchema = stripOriginAnnotations(inputSchema);
463
- prependTasks.push({
464
- id: inputTaskId,
465
- type: "InputTask",
466
- config: {
467
- inputSchema: strippedInputSchema,
468
- outputSchema: strippedInputSchema
469
- }
470
- });
471
- if (typeof inputSchema !== "boolean" && inputSchema.properties) {
472
- for (const [propName, prop] of Object.entries(inputSchema.properties)) {
473
- if (!prop || typeof prop === "boolean")
474
- continue;
475
- const origins = getOriginTaskIds(prop);
476
- for (const originId of origins) {
477
- inputDataflows.push({
478
- sourceTaskId: inputTaskId,
479
- sourceTaskPortId: propName,
480
- targetTaskId: originId,
481
- targetTaskPortId: propName
482
- });
483
- }
484
- }
485
- }
486
- }
487
- if (!hasOutputTask && outputSchema) {
488
- const outputTaskId = uuid4();
489
- const strippedOutputSchema = stripOriginAnnotations(outputSchema);
490
- appendTasks.push({
491
- id: outputTaskId,
492
- type: "OutputTask",
493
- config: {
494
- inputSchema: strippedOutputSchema,
495
- outputSchema: strippedOutputSchema
496
- }
497
- });
498
- if (typeof outputSchema !== "boolean" && outputSchema.properties) {
499
- for (const [propName, prop] of Object.entries(outputSchema.properties)) {
500
- if (!prop || typeof prop === "boolean")
501
- continue;
502
- const origins = getOriginTaskIds(prop);
503
- for (const originId of origins) {
504
- outputDataflows.push({
505
- sourceTaskId: originId,
506
- sourceTaskPortId: propName,
507
- targetTaskId: outputTaskId,
508
- targetTaskPortId: propName
509
- });
510
- }
511
- }
512
- }
513
- }
514
- return {
515
- tasks: [...prependTasks, ...json.tasks, ...appendTasks],
516
- dataflows: [...inputDataflows, ...json.dataflows, ...outputDataflows]
517
- };
518
- }
519
- function addBoundaryNodesToDependencyJson(items, graph) {
520
- const hasInputTask = items.some((t) => t.type === "InputTask");
521
- const hasOutputTask = items.some((t) => t.type === "OutputTask");
522
- if (hasInputTask && hasOutputTask) {
523
- return items;
524
- }
525
- const prependItems = [];
526
- const appendItems = [];
527
- if (!hasInputTask) {
528
- const inputSchema = computeGraphInputSchema(graph, { trackOrigins: true });
529
- const inputTaskId = uuid4();
530
- const strippedInputSchema = stripOriginAnnotations(inputSchema);
531
- prependItems.push({
532
- id: inputTaskId,
533
- type: "InputTask",
534
- config: {
535
- inputSchema: strippedInputSchema,
536
- outputSchema: strippedInputSchema
537
- }
538
- });
539
- if (typeof inputSchema !== "boolean" && inputSchema.properties) {
540
- for (const [propName, prop] of Object.entries(inputSchema.properties)) {
541
- if (!prop || typeof prop === "boolean")
542
- continue;
543
- const origins = getOriginTaskIds(prop);
544
- for (const originId of origins) {
545
- const targetItem = items.find((item) => item.id === originId);
546
- if (!targetItem)
547
- continue;
548
- if (!targetItem.dependencies) {
549
- targetItem.dependencies = {};
550
- }
551
- const existing = targetItem.dependencies[propName];
552
- const dep = { id: inputTaskId, output: propName };
553
- if (!existing) {
554
- targetItem.dependencies[propName] = dep;
555
- } else if (Array.isArray(existing)) {
556
- existing.push(dep);
557
- } else {
558
- targetItem.dependencies[propName] = [existing, dep];
559
- }
560
- }
561
- }
562
- }
563
- }
564
- if (!hasOutputTask) {
565
- const outputSchema = computeGraphOutputSchema(graph, { trackOrigins: true });
566
- const outputTaskId = uuid4();
567
- const strippedOutputSchema = stripOriginAnnotations(outputSchema);
568
- const outputDependencies = {};
569
- if (typeof outputSchema !== "boolean" && outputSchema.properties) {
570
- for (const [propName, prop] of Object.entries(outputSchema.properties)) {
571
- if (!prop || typeof prop === "boolean")
572
- continue;
573
- const origins = getOriginTaskIds(prop);
574
- if (origins.length === 1) {
575
- outputDependencies[propName] = { id: origins[0], output: propName };
576
- } else if (origins.length > 1) {
577
- outputDependencies[propName] = origins.map((id) => ({ id, output: propName }));
578
- }
579
- }
580
- }
581
- appendItems.push({
582
- id: outputTaskId,
583
- type: "OutputTask",
584
- config: {
585
- inputSchema: strippedOutputSchema,
586
- outputSchema: strippedOutputSchema
587
- },
588
- ...Object.keys(outputDependencies).length > 0 ? { dependencies: outputDependencies } : {}
589
- });
590
- }
591
- return [...prependItems, ...items, ...appendItems];
592
- }
593
- // src/task-graph/TaskGraph.ts
594
- import { DirectedAcyclicGraph, EventEmitter as EventEmitter5, uuid4 as uuid45 } from "@workglow/util";
595
-
596
- // src/task/GraphAsTask.ts
597
- import { compileSchema as compileSchema2 } from "@workglow/util";
598
-
599
- // src/task-graph/TaskGraphRunner.ts
600
- import {
601
- collectPropertyValues,
602
- getLogger as getLogger3,
603
- globalServiceRegistry as globalServiceRegistry2,
604
- ServiceRegistry as ServiceRegistry2,
605
- uuid4 as uuid43
606
- } from "@workglow/util";
607
-
608
- // src/storage/TaskOutputRepository.ts
609
- import { createServiceToken, EventEmitter as EventEmitter2 } from "@workglow/util";
610
- var TASK_OUTPUT_REPOSITORY = createServiceToken("taskgraph.taskOutputRepository");
611
-
612
- class TaskOutputRepository {
613
- outputCompression;
614
- constructor({ outputCompression = true }) {
615
- this.outputCompression = outputCompression;
616
- }
617
- get events() {
618
- if (!this._events) {
619
- this._events = new EventEmitter2;
620
- }
621
- return this._events;
622
- }
623
- _events;
624
- on(name, fn) {
625
- this.events.on(name, fn);
626
- }
627
- off(name, fn) {
628
- this.events.off(name, fn);
629
- }
630
- waitOn(name) {
631
- return this.events.waitOn(name);
632
- }
633
- emit(name, ...args) {
634
- this._events?.emit(name, ...args);
635
- }
636
- }
637
-
638
- // src/task/ConditionalTask.ts
639
- import { getLogger as getLogger2 } from "@workglow/util";
640
-
641
- // src/task/ConditionUtils.ts
642
- function evaluateCondition(fieldValue, operator, compareValue) {
643
- if (fieldValue === null || fieldValue === undefined) {
644
- switch (operator) {
645
- case "is_empty":
646
- return true;
647
- case "is_not_empty":
648
- return false;
649
- case "is_true":
650
- return false;
651
- case "is_false":
652
- return true;
653
- default:
654
- return false;
655
- }
656
- }
657
- const strValue = String(fieldValue);
658
- const numValue = Number(fieldValue);
659
- switch (operator) {
660
- case "equals":
661
- if (!isNaN(numValue) && !isNaN(Number(compareValue))) {
662
- return numValue === Number(compareValue);
663
- }
664
- return strValue === compareValue;
665
- case "not_equals":
666
- if (!isNaN(numValue) && !isNaN(Number(compareValue))) {
667
- return numValue !== Number(compareValue);
668
- }
669
- return strValue !== compareValue;
670
- case "greater_than":
671
- return numValue > Number(compareValue);
672
- case "greater_or_equal":
673
- return numValue >= Number(compareValue);
674
- case "less_than":
675
- return numValue < Number(compareValue);
676
- case "less_or_equal":
677
- return numValue <= Number(compareValue);
678
- case "contains":
679
- return strValue.toLowerCase().includes(compareValue.toLowerCase());
680
- case "starts_with":
681
- return strValue.toLowerCase().startsWith(compareValue.toLowerCase());
682
- case "ends_with":
683
- return strValue.toLowerCase().endsWith(compareValue.toLowerCase());
684
- case "is_empty":
685
- return strValue === "" || Array.isArray(fieldValue) && fieldValue.length === 0;
686
- case "is_not_empty":
687
- return strValue !== "" && !(Array.isArray(fieldValue) && fieldValue.length === 0);
688
- case "is_true":
689
- return Boolean(fieldValue) === true;
690
- case "is_false":
691
- return Boolean(fieldValue) === false;
692
- default:
693
- return false;
694
- }
695
- }
696
- function getNestedValue(obj, path) {
697
- const parts = path.split(".");
698
- let current = obj;
699
- for (const part of parts) {
700
- if (current === null || current === undefined || typeof current !== "object") {
701
- return;
702
- }
703
- current = current[part];
704
- }
705
- return current;
706
- }
707
-
708
- // src/task/Task.ts
709
- import {
710
- compileSchema,
711
- deepEqual,
712
- EventEmitter as EventEmitter3,
713
- uuid4 as uuid42
714
- } from "@workglow/util";
715
-
716
- // src/task/TaskError.ts
717
- import { BaseError } from "@workglow/util";
718
-
719
- class TaskError extends BaseError {
720
- static type = "TaskError";
721
- constructor(message) {
722
- super(message);
723
- }
724
- }
725
-
726
- class TaskConfigurationError extends TaskError {
727
- static type = "TaskConfigurationError";
728
- constructor(message) {
729
- super(message);
730
- }
731
- }
732
-
733
- class WorkflowError extends TaskError {
734
- static type = "WorkflowError";
735
- constructor(message) {
736
- super(message);
737
- }
738
- }
739
-
740
- class TaskAbortedError extends TaskError {
741
- static type = "TaskAbortedError";
742
- constructor(message = "Task aborted") {
743
- super(message);
744
- }
745
- }
746
-
747
- class TaskTimeoutError extends TaskAbortedError {
748
- static type = "TaskTimeoutError";
749
- constructor(timeoutMs) {
750
- super(timeoutMs ? `Task timed out after ${timeoutMs}ms` : "Task timed out");
751
- }
752
- }
753
-
754
- class TaskFailedError extends TaskError {
755
- static type = "TaskFailedError";
756
- constructor(message = "Task failed") {
757
- super(message);
758
- }
759
- }
760
-
761
- class JobTaskFailedError extends TaskFailedError {
762
- static type = "JobTaskFailedError";
763
- jobError;
764
- constructor(err) {
765
- super(String(err));
766
- this.jobError = err;
767
- }
768
- }
769
-
770
- class TaskJSONError extends TaskError {
771
- static type = "TaskJSONError";
772
- constructor(message = "Error converting JSON to a Task") {
773
- super(message);
774
- }
775
- }
776
-
777
- class TaskInvalidInputError extends TaskError {
778
- static type = "TaskInvalidInputError";
779
- constructor(message = "Invalid input data") {
780
- super(message);
781
- }
782
- }
783
-
784
- // src/task/TaskRunner.ts
785
- import { getLogger, globalServiceRegistry } from "@workglow/util";
786
-
787
- // src/task/InputResolver.ts
788
- import { getInputResolvers } from "@workglow/util";
789
- function getSchemaFormat(schema) {
790
- if (typeof schema !== "object" || schema === null)
791
- return;
792
- const s = schema;
793
- if (typeof s.format === "string")
794
- return s.format;
795
- const variants = s.oneOf ?? s.anyOf;
796
- if (Array.isArray(variants)) {
797
- for (const variant of variants) {
798
- if (typeof variant === "object" && variant !== null) {
799
- const v = variant;
800
- if (typeof v.format === "string")
801
- return v.format;
802
- }
803
- }
804
- }
805
- return;
806
- }
807
- function getObjectSchema(schema) {
808
- if (typeof schema !== "object" || schema === null)
809
- return;
810
- const s = schema;
811
- if (s.type === "object" && s.properties && typeof s.properties === "object") {
812
- return s;
813
- }
814
- const variants = s.oneOf ?? s.anyOf;
815
- if (Array.isArray(variants)) {
816
- for (const variant of variants) {
817
- if (typeof variant === "object" && variant !== null) {
818
- const v = variant;
819
- if (v.type === "object" && v.properties && typeof v.properties === "object") {
820
- return v;
821
- }
822
- }
823
- }
824
- }
825
- return;
826
- }
827
- function getFormatPrefix(format) {
828
- const colonIndex = format.indexOf(":");
829
- return colonIndex >= 0 ? format.substring(0, colonIndex) : format;
830
- }
831
- async function resolveSchemaInputs(input, schema, config) {
832
- if (typeof schema === "boolean")
833
- return input;
834
- const properties = schema.properties;
835
- if (!properties || typeof properties !== "object")
836
- return input;
837
- const resolvers = getInputResolvers();
838
- const resolved = { ...input };
839
- for (const [key, propSchema] of Object.entries(properties)) {
840
- let value = resolved[key];
841
- const format = getSchemaFormat(propSchema);
842
- if (format) {
843
- let resolver = resolvers.get(format);
844
- if (!resolver) {
845
- const prefix = getFormatPrefix(format);
846
- resolver = resolvers.get(prefix);
847
- }
848
- if (resolver) {
849
- if (typeof value === "string") {
850
- value = await resolver(value, format, config.registry);
851
- resolved[key] = value;
852
- } else if (Array.isArray(value) && value.some((item) => typeof item === "string")) {
853
- const results = await Promise.all(value.map((item) => typeof item === "string" ? resolver(item, format, config.registry) : item));
854
- value = results.filter((result) => result !== undefined);
855
- resolved[key] = value;
856
- }
857
- }
858
- }
859
- if (value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value)) {
860
- const objectSchema = getObjectSchema(propSchema);
861
- if (objectSchema) {
862
- resolved[key] = await resolveSchemaInputs(value, objectSchema, config);
863
- }
864
- }
865
- }
866
- return resolved;
867
- }
868
-
869
- // src/task/StreamTypes.ts
870
- function getPortStreamMode(schema, portId) {
871
- if (typeof schema === "boolean")
872
- return "none";
873
- const prop = schema.properties?.[portId];
874
- if (!prop || typeof prop === "boolean")
875
- return "none";
876
- const xStream = prop["x-stream"];
877
- if (xStream === "append" || xStream === "replace" || xStream === "object")
878
- return xStream;
879
- return "none";
880
- }
881
- function getStreamingPorts(schema) {
882
- if (typeof schema === "boolean")
883
- return [];
884
- const props = schema.properties;
885
- if (!props)
886
- return [];
887
- const result = [];
888
- for (const [name, prop] of Object.entries(props)) {
889
- if (!prop || typeof prop === "boolean")
890
- continue;
891
- const xStream = prop["x-stream"];
892
- if (xStream === "append" || xStream === "replace" || xStream === "object") {
893
- result.push({ port: name, mode: xStream });
894
- }
895
- }
896
- return result;
897
- }
898
- function getOutputStreamMode(outputSchema) {
899
- const ports = getStreamingPorts(outputSchema);
900
- if (ports.length === 0)
901
- return "none";
902
- const mode = ports[0].mode;
903
- for (let i = 1;i < ports.length; i++) {
904
- if (ports[i].mode !== mode) {
905
- return "mixed";
906
- }
907
- }
908
- return mode;
909
- }
910
- function isTaskStreamable(task) {
911
- if (typeof task.executeStream !== "function")
912
- return false;
913
- return getOutputStreamMode(task.outputSchema()) !== "none";
914
- }
915
- function getAppendPortId(schema) {
916
- if (typeof schema === "boolean")
917
- return;
918
- const props = schema.properties;
919
- if (!props)
920
- return;
921
- for (const [name, prop] of Object.entries(props)) {
922
- if (!prop || typeof prop === "boolean")
923
- continue;
924
- if (prop["x-stream"] === "append")
925
- return name;
926
- }
927
- return;
928
- }
929
- function edgeNeedsAccumulation(sourceSchema, sourcePort, targetSchema, targetPort) {
930
- const sourceMode = getPortStreamMode(sourceSchema, sourcePort);
931
- if (sourceMode === "none")
932
- return false;
933
- const targetMode = getPortStreamMode(targetSchema, targetPort);
934
- return sourceMode !== targetMode;
935
- }
936
- function getObjectPortId(schema) {
937
- if (typeof schema === "boolean")
938
- return;
939
- const props = schema.properties;
940
- if (!props)
941
- return;
942
- for (const [name, prop] of Object.entries(props)) {
943
- if (!prop || typeof prop === "boolean")
944
- continue;
945
- if (prop["x-stream"] === "object")
946
- return name;
947
- }
948
- return;
949
- }
950
- function getStructuredOutputSchemas(schema) {
951
- const result = new Map;
952
- if (typeof schema === "boolean")
953
- return result;
954
- const props = schema.properties;
955
- if (!props)
956
- return result;
957
- for (const [name, prop] of Object.entries(props)) {
958
- if (!prop || typeof prop === "boolean")
959
- continue;
960
- if (prop["x-structured-output"] === true) {
961
- result.set(name, prop);
962
- }
963
- }
964
- return result;
965
- }
966
- function hasStructuredOutput(schema) {
967
- return getStructuredOutputSchemas(schema).size > 0;
968
- }
969
-
970
- // src/task/TaskRunner.ts
971
- class TaskRunner {
972
- running = false;
973
- reactiveRunning = false;
974
- task;
975
- abortController;
976
- outputCache;
977
- registry = globalServiceRegistry;
978
- inputStreams;
979
- timeoutTimer;
980
- pendingTimeoutError;
981
- shouldAccumulate = true;
982
- constructor(task) {
983
- this.task = task;
984
- this.own = this.own.bind(this);
985
- this.handleProgress = this.handleProgress.bind(this);
986
- }
987
- get timerLabel() {
988
- return `task:${this.task.type}:${this.task.config.id}`;
989
- }
990
- async run(overrides = {}, config = {}) {
991
- await this.handleStart(config);
992
- try {
993
- this.task.setInput(overrides);
994
- const schema = this.task.constructor.inputSchema();
995
- this.task.runInputData = await resolveSchemaInputs(this.task.runInputData, schema, { registry: this.registry });
996
- const isValid = await this.task.validateInput(this.task.runInputData);
997
- if (!isValid) {
998
- throw new TaskInvalidInputError("Invalid input data");
999
- }
1000
- if (this.abortController?.signal.aborted) {
1001
- await this.handleAbort();
1002
- throw new TaskAbortedError("Promise for task created and aborted before run");
1003
- }
1004
- const inputs = this.task.runInputData;
1005
- let outputs;
1006
- const isStreamable = isTaskStreamable(this.task);
1007
- if (this.task.cacheable) {
1008
- outputs = await this.outputCache?.getOutput(this.task.type, inputs);
1009
- if (outputs) {
1010
- if (isStreamable) {
1011
- this.task.runOutputData = outputs;
1012
- this.task.emit("stream_start");
1013
- this.task.emit("stream_chunk", { type: "finish", data: outputs });
1014
- this.task.emit("stream_end", outputs);
1015
- this.task.runOutputData = await this.executeTaskReactive(inputs, outputs);
1016
- } else {
1017
- this.task.runOutputData = await this.executeTaskReactive(inputs, outputs);
1018
- }
1019
- }
1020
- }
1021
- if (!outputs) {
1022
- if (isStreamable) {
1023
- outputs = await this.executeStreamingTask(inputs);
1024
- } else {
1025
- outputs = await this.executeTask(inputs);
1026
- }
1027
- if (this.task.cacheable && outputs !== undefined) {
1028
- await this.outputCache?.saveOutput(this.task.type, inputs, outputs);
1029
- }
1030
- this.task.runOutputData = outputs ?? {};
1031
- }
1032
- await this.handleComplete();
1033
- return this.task.runOutputData;
1034
- } catch (err) {
1035
- await this.handleError(err);
1036
- throw this.task.error instanceof TaskTimeoutError ? this.task.error : err;
1037
- }
1038
- }
1039
- async runReactive(overrides = {}) {
1040
- if (this.task.status === TaskStatus.PROCESSING) {
1041
- return this.task.runOutputData;
1042
- }
1043
- this.task.setInput(overrides);
1044
- const schema = this.task.constructor.inputSchema();
1045
- this.task.runInputData = await resolveSchemaInputs(this.task.runInputData, schema, { registry: this.registry });
1046
- await this.handleStartReactive();
1047
- try {
1048
- const isValid = await this.task.validateInput(this.task.runInputData);
1049
- if (!isValid) {
1050
- throw new TaskInvalidInputError("Invalid input data");
1051
- }
1052
- const resultReactive = await this.executeTaskReactive(this.task.runInputData, this.task.runOutputData);
1053
- this.task.runOutputData = resultReactive;
1054
- await this.handleCompleteReactive();
1055
- } catch (err) {
1056
- await this.handleErrorReactive();
1057
- } finally {
1058
- return this.task.runOutputData;
1059
- }
1060
- }
1061
- abort() {
1062
- if (this.task.hasChildren()) {
1063
- this.task.subGraph.abort();
1064
- }
1065
- this.abortController?.abort();
1066
- }
1067
- own(i) {
1068
- const task = ensureTask(i, { isOwned: true });
1069
- this.task.subGraph.addTask(task);
1070
- return i;
1071
- }
1072
- async executeTask(input) {
1073
- const result = await this.task.execute(input, {
1074
- signal: this.abortController.signal,
1075
- updateProgress: this.handleProgress.bind(this),
1076
- own: this.own,
1077
- registry: this.registry
1078
- });
1079
- return await this.executeTaskReactive(input, result || {});
1080
- }
1081
- async executeTaskReactive(input, output) {
1082
- const reactiveResult = await this.task.executeReactive(input, output, { own: this.own });
1083
- return Object.assign({}, output, reactiveResult ?? {});
1084
- }
1085
- async executeStreamingTask(input) {
1086
- const streamMode = getOutputStreamMode(this.task.outputSchema());
1087
- if (streamMode === "append") {
1088
- const ports = getStreamingPorts(this.task.outputSchema());
1089
- if (ports.length === 0) {
1090
- throw new TaskError(`Task ${this.task.type} declares append streaming but no output port has x-stream: "append"`);
1091
- }
1092
- }
1093
- if (streamMode === "object") {
1094
- const ports = getStreamingPorts(this.task.outputSchema());
1095
- if (ports.length === 0) {
1096
- throw new TaskError(`Task ${this.task.type} declares object streaming but no output port has x-stream: "object"`);
1097
- }
1098
- }
1099
- const accumulated = this.shouldAccumulate ? new Map : undefined;
1100
- const accumulatedObjects = this.shouldAccumulate ? new Map : undefined;
1101
- let chunkCount = 0;
1102
- let finalOutput;
1103
- this.task.emit("stream_start");
1104
- const stream = this.task.executeStream(input, {
1105
- signal: this.abortController.signal,
1106
- updateProgress: this.handleProgress.bind(this),
1107
- own: this.own,
1108
- registry: this.registry,
1109
- inputStreams: this.inputStreams
1110
- });
1111
- for await (const event of stream) {
1112
- chunkCount++;
1113
- if (chunkCount === 1) {
1114
- this.task.status = TaskStatus.STREAMING;
1115
- this.task.emit("status", this.task.status);
1116
- }
1117
- if (event.type === "snapshot") {
1118
- this.task.runOutputData = event.data;
1119
- }
1120
- switch (event.type) {
1121
- case "text-delta": {
1122
- if (accumulated) {
1123
- accumulated.set(event.port, (accumulated.get(event.port) ?? "") + event.textDelta);
1124
- }
1125
- this.task.emit("stream_chunk", event);
1126
- const progress = Math.min(99, Math.round(100 * (1 - Math.exp(-0.05 * chunkCount))));
1127
- await this.handleProgress(progress);
1128
- break;
1129
- }
1130
- case "object-delta": {
1131
- if (accumulatedObjects) {
1132
- accumulatedObjects.set(event.port, event.objectDelta);
1133
- }
1134
- this.task.runOutputData = {
1135
- ...this.task.runOutputData,
1136
- [event.port]: event.objectDelta
1137
- };
1138
- this.task.emit("stream_chunk", event);
1139
- const progress = Math.min(99, Math.round(100 * (1 - Math.exp(-0.05 * chunkCount))));
1140
- await this.handleProgress(progress);
1141
- break;
1142
- }
1143
- case "snapshot": {
1144
- this.task.emit("stream_chunk", event);
1145
- const progress = Math.min(99, Math.round(100 * (1 - Math.exp(-0.05 * chunkCount))));
1146
- await this.handleProgress(progress);
1147
- break;
1148
- }
1149
- case "finish": {
1150
- if (accumulated || accumulatedObjects) {
1151
- const merged = { ...event.data || {} };
1152
- if (accumulated) {
1153
- for (const [port, text] of accumulated) {
1154
- if (text.length > 0)
1155
- merged[port] = text;
1156
- }
1157
- }
1158
- if (accumulatedObjects) {
1159
- for (const [port, obj] of accumulatedObjects) {
1160
- merged[port] = obj;
1161
- }
1162
- }
1163
- finalOutput = merged;
1164
- this.task.emit("stream_chunk", { type: "finish", data: merged });
1165
- } else {
1166
- finalOutput = event.data;
1167
- this.task.emit("stream_chunk", event);
1168
- }
1169
- break;
1170
- }
1171
- case "error": {
1172
- throw event.error;
1173
- }
1174
- }
1175
- }
1176
- if (this.abortController?.signal.aborted) {
1177
- throw new TaskAbortedError("Task aborted during streaming");
1178
- }
1179
- if (finalOutput !== undefined) {
1180
- this.task.runOutputData = finalOutput;
1181
- }
1182
- this.task.emit("stream_end", this.task.runOutputData);
1183
- const reactiveResult = await this.executeTaskReactive(input, this.task.runOutputData || {});
1184
- return reactiveResult;
1185
- }
1186
- async handleStart(config = {}) {
1187
- if (this.task.status === TaskStatus.PROCESSING)
1188
- return;
1189
- this.running = true;
1190
- this.task.startedAt = new Date;
1191
- this.task.progress = 0;
1192
- this.task.status = TaskStatus.PROCESSING;
1193
- this.abortController = new AbortController;
1194
- this.abortController.signal.addEventListener("abort", () => {
1195
- this.handleAbort();
1196
- });
1197
- const cache = config.outputCache ?? this.task.runConfig?.outputCache;
1198
- if (cache === true) {
1199
- let instance = globalServiceRegistry.get(TASK_OUTPUT_REPOSITORY);
1200
- this.outputCache = instance;
1201
- } else if (cache === false) {
1202
- this.outputCache = undefined;
1203
- } else if (cache instanceof TaskOutputRepository) {
1204
- this.outputCache = cache;
1205
- }
1206
- this.shouldAccumulate = config.shouldAccumulate !== false;
1207
- const timeout = this.task.config.timeout;
1208
- if (timeout !== undefined && timeout > 0) {
1209
- this.pendingTimeoutError = new TaskTimeoutError(timeout);
1210
- this.timeoutTimer = setTimeout(() => {
1211
- this.abort();
1212
- }, timeout);
1213
- }
1214
- if (config.updateProgress) {
1215
- this.updateProgress = config.updateProgress;
1216
- }
1217
- if (config.registry) {
1218
- this.registry = config.registry;
1219
- }
1220
- getLogger().time(this.timerLabel, { taskType: this.task.type, taskId: this.task.config.id });
1221
- this.task.emit("start");
1222
- this.task.emit("status", this.task.status);
1223
- }
1224
- updateProgress = async (task, progress, message, ...args) => {};
1225
- async handleStartReactive() {
1226
- this.reactiveRunning = true;
1227
- }
1228
- clearTimeoutTimer() {
1229
- if (this.timeoutTimer !== undefined) {
1230
- clearTimeout(this.timeoutTimer);
1231
- this.timeoutTimer = undefined;
1232
- }
1233
- }
1234
- async handleAbort() {
1235
- if (this.task.status === TaskStatus.ABORTING)
1236
- return;
1237
- this.clearTimeoutTimer();
1238
- getLogger().timeEnd(this.timerLabel, { taskType: this.task.type, taskId: this.task.config.id });
1239
- this.task.status = TaskStatus.ABORTING;
1240
- this.task.progress = 100;
1241
- this.task.error = this.pendingTimeoutError ?? new TaskAbortedError;
1242
- this.pendingTimeoutError = undefined;
1243
- this.task.emit("abort", this.task.error);
1244
- this.task.emit("status", this.task.status);
1245
- }
1246
- async handleAbortReactive() {
1247
- this.reactiveRunning = false;
1248
- }
1249
- async handleComplete() {
1250
- if (this.task.status === TaskStatus.COMPLETED)
1251
- return;
1252
- this.clearTimeoutTimer();
1253
- this.pendingTimeoutError = undefined;
1254
- getLogger().timeEnd(this.timerLabel, { taskType: this.task.type, taskId: this.task.config.id });
1255
- this.task.completedAt = new Date;
1256
- this.task.progress = 100;
1257
- this.task.status = TaskStatus.COMPLETED;
1258
- this.abortController = undefined;
1259
- this.task.emit("complete");
1260
- this.task.emit("status", this.task.status);
1261
- }
1262
- async handleCompleteReactive() {
1263
- this.reactiveRunning = false;
1264
- }
1265
- async handleDisable() {
1266
- if (this.task.status === TaskStatus.DISABLED)
1267
- return;
1268
- this.task.status = TaskStatus.DISABLED;
1269
- this.task.progress = 100;
1270
- this.task.completedAt = new Date;
1271
- this.abortController = undefined;
1272
- this.task.emit("disabled");
1273
- this.task.emit("status", this.task.status);
1274
- }
1275
- async disable() {
1276
- await this.handleDisable();
1277
- }
1278
- async handleError(err) {
1279
- if (err instanceof TaskAbortedError)
1280
- return this.handleAbort();
1281
- if (this.task.status === TaskStatus.FAILED)
1282
- return;
1283
- this.clearTimeoutTimer();
1284
- this.pendingTimeoutError = undefined;
1285
- getLogger().timeEnd(this.timerLabel, { taskType: this.task.type, taskId: this.task.config.id });
1286
- if (this.task.hasChildren()) {
1287
- this.task.subGraph.abort();
1288
- }
1289
- this.task.completedAt = new Date;
1290
- this.task.progress = 100;
1291
- this.task.status = TaskStatus.FAILED;
1292
- this.task.error = err instanceof TaskError ? err : new TaskFailedError(err?.message || "Task failed");
1293
- this.abortController = undefined;
1294
- this.task.emit("error", this.task.error);
1295
- this.task.emit("status", this.task.status);
1296
- }
1297
- async handleErrorReactive() {
1298
- this.reactiveRunning = false;
1299
- }
1300
- async handleProgress(progress, message, ...args) {
1301
- this.task.progress = progress;
1302
- await this.updateProgress(this.task, progress, message, ...args);
1303
- this.task.emit("progress", progress, message, ...args);
1304
- }
1305
- }
1306
-
1307
- // src/task/Task.ts
1308
- class Task {
1309
- static type = "Task";
1310
- static category = "Hidden";
1311
- static title = "";
1312
- static description = "";
1313
- static cacheable = true;
1314
- static hasDynamicSchemas = false;
1315
- static passthroughInputsToOutputs = false;
1316
- static customizable = false;
1317
- static inputSchema() {
1318
- return {
1319
- type: "object",
1320
- properties: {},
1321
- additionalProperties: false
1322
- };
1323
- }
1324
- static outputSchema() {
1325
- return {
1326
- type: "object",
1327
- properties: {},
1328
- additionalProperties: false
1329
- };
1330
- }
1331
- static configSchema() {
1332
- return TaskConfigSchema;
1333
- }
1334
- async execute(_input, context) {
1335
- if (context.signal?.aborted) {
1336
- throw new TaskAbortedError("Task aborted");
1337
- }
1338
- return;
1339
- }
1340
- async executeReactive(_input, output, _context) {
1341
- return output;
1342
- }
1343
- _runner;
1344
- get runner() {
1345
- if (!this._runner) {
1346
- this._runner = new TaskRunner(this);
1347
- }
1348
- return this._runner;
1349
- }
1350
- async run(overrides = {}, runConfig = {}) {
1351
- return this.runner.run(overrides, { ...this.runConfig, ...runConfig });
1352
- }
1353
- async runReactive(overrides = {}) {
1354
- return this.runner.runReactive(overrides);
1355
- }
1356
- abort() {
1357
- this.runner.abort();
1358
- }
1359
- async disable() {
1360
- await this.runner.disable();
1361
- }
1362
- inputSchema() {
1363
- return this.constructor.inputSchema();
1364
- }
1365
- outputSchema() {
1366
- return this.constructor.outputSchema();
1367
- }
1368
- configSchema() {
1369
- return this.constructor.configSchema();
1370
- }
1371
- get type() {
1372
- return this.constructor.type;
1373
- }
1374
- get category() {
1375
- return this.constructor.category;
1376
- }
1377
- get title() {
1378
- return this.config?.title ?? this.constructor.title;
1379
- }
1380
- get description() {
1381
- return this.config?.description ?? this.constructor.description;
1382
- }
1383
- get cacheable() {
1384
- return this.runConfig?.cacheable ?? this.config?.cacheable ?? this.constructor.cacheable;
1385
- }
1386
- defaults;
1387
- runInputData = {};
1388
- runOutputData = {};
1389
- config;
1390
- get id() {
1391
- return this.config.id;
1392
- }
1393
- runConfig = {};
1394
- status = TaskStatus.PENDING;
1395
- progress = 0;
1396
- createdAt = new Date;
1397
- startedAt;
1398
- completedAt;
1399
- error;
1400
- get events() {
1401
- if (!this._events) {
1402
- this._events = new EventEmitter3;
1403
- }
1404
- return this._events;
1405
- }
1406
- _events;
1407
- constructor(callerDefaultInputs = {}, config = {}, runConfig = {}) {
1408
- const inputDefaults = this.getDefaultInputsFromStaticInputDefinitions();
1409
- const mergedDefaults = Object.assign(inputDefaults, callerDefaultInputs);
1410
- this.defaults = this.stripSymbols(mergedDefaults);
1411
- this.resetInputData();
1412
- const title = this.constructor.title || undefined;
1413
- const baseConfig = Object.assign({
1414
- ...title ? { title } : {}
1415
- }, config);
1416
- if (baseConfig.id === undefined) {
1417
- baseConfig.id = uuid42();
1418
- }
1419
- this.config = this.validateAndApplyConfigDefaults(baseConfig);
1420
- this.runConfig = runConfig;
1421
- }
1422
- getDefaultInputsFromStaticInputDefinitions() {
1423
- const schema = this.inputSchema();
1424
- if (typeof schema === "boolean") {
1425
- return {};
1426
- }
1427
- try {
1428
- const compiledSchema = this.getInputSchemaNode();
1429
- const defaultData = compiledSchema.getData(undefined, {
1430
- addOptionalProps: true,
1431
- removeInvalidData: false,
1432
- useTypeDefaults: false
1433
- });
1434
- return defaultData || {};
1435
- } catch (error) {
1436
- console.warn(`Failed to compile input schema for ${this.type}, falling back to manual extraction:`, error);
1437
- return Object.entries(schema.properties || {}).reduce((acc, [id, prop]) => {
1438
- const defaultValue = prop.default;
1439
- if (defaultValue !== undefined) {
1440
- acc[id] = defaultValue;
1441
- }
1442
- return acc;
1443
- }, {});
1444
- }
1445
- }
1446
- resetInputData() {
1447
- this.runInputData = this.smartClone(this.defaults);
1448
- }
1449
- smartClone(obj, visited = new WeakSet) {
1450
- if (obj === null || obj === undefined) {
1451
- return obj;
1452
- }
1453
- if (typeof obj !== "object") {
1454
- return obj;
1455
- }
1456
- if (visited.has(obj)) {
1457
- throw new TaskConfigurationError("Circular reference detected in input data. " + "Cannot clone objects with circular references.");
1458
- }
1459
- if (ArrayBuffer.isView(obj)) {
1460
- if (typeof DataView !== "undefined" && obj instanceof DataView) {
1461
- return obj;
1462
- }
1463
- const typedArray = obj;
1464
- return new typedArray.constructor(typedArray);
1465
- }
1466
- if (!Array.isArray(obj)) {
1467
- const proto = Object.getPrototypeOf(obj);
1468
- if (proto !== Object.prototype && proto !== null) {
1469
- return obj;
1470
- }
1471
- }
1472
- visited.add(obj);
1473
- try {
1474
- if (Array.isArray(obj)) {
1475
- return obj.map((item) => this.smartClone(item, visited));
1476
- }
1477
- const result = {};
1478
- for (const key in obj) {
1479
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
1480
- result[key] = this.smartClone(obj[key], visited);
1481
- }
1482
- }
1483
- return result;
1484
- } finally {
1485
- visited.delete(obj);
1486
- }
1487
- }
1488
- setDefaults(defaults) {
1489
- this.defaults = this.stripSymbols(defaults);
1490
- }
1491
- setInput(input) {
1492
- const schema = this.inputSchema();
1493
- if (typeof schema === "boolean") {
1494
- if (schema === true) {
1495
- for (const [inputId, value] of Object.entries(input)) {
1496
- if (value !== undefined) {
1497
- this.runInputData[inputId] = value;
1498
- }
1499
- }
1500
- }
1501
- return;
1502
- }
1503
- const properties = schema.properties || {};
1504
- for (const [inputId, prop] of Object.entries(properties)) {
1505
- if (input[inputId] !== undefined) {
1506
- this.runInputData[inputId] = input[inputId];
1507
- } else if (this.runInputData[inputId] === undefined && prop.default !== undefined) {
1508
- this.runInputData[inputId] = prop.default;
1509
- }
1510
- }
1511
- if (schema.additionalProperties) {
1512
- for (const [inputId, value] of Object.entries(input)) {
1513
- if (!(inputId in properties)) {
1514
- this.runInputData[inputId] = value;
1515
- }
1516
- }
1517
- }
1518
- }
1519
- addInput(overrides) {
1520
- if (!overrides)
1521
- return false;
1522
- let changed = false;
1523
- const inputSchema = this.inputSchema();
1524
- if (typeof inputSchema === "boolean") {
1525
- if (inputSchema === false) {
1526
- return false;
1527
- }
1528
- for (const [key, value] of Object.entries(overrides)) {
1529
- if (!deepEqual(this.runInputData[key], value)) {
1530
- this.runInputData[key] = value;
1531
- changed = true;
1532
- }
1533
- }
1534
- return changed;
1535
- }
1536
- const properties = inputSchema.properties || {};
1537
- for (const [inputId, prop] of Object.entries(properties)) {
1538
- if (inputId === DATAFLOW_ALL_PORTS) {
1539
- this.runInputData = { ...this.runInputData, ...overrides };
1540
- changed = true;
1541
- } else {
1542
- if (overrides[inputId] === undefined)
1543
- continue;
1544
- const isArray = prop?.type === "array" || prop?.type === "any" && (Array.isArray(overrides[inputId]) || Array.isArray(this.runInputData[inputId]));
1545
- if (isArray) {
1546
- const existingItems = Array.isArray(this.runInputData[inputId]) ? this.runInputData[inputId] : this.runInputData[inputId] !== undefined ? [this.runInputData[inputId]] : [];
1547
- const newitems = [...existingItems];
1548
- const overrideItem = overrides[inputId];
1549
- if (Array.isArray(overrideItem)) {
1550
- newitems.push(...overrideItem);
1551
- } else {
1552
- newitems.push(overrideItem);
1553
- }
1554
- this.runInputData[inputId] = newitems;
1555
- changed = true;
1556
- } else {
1557
- if (!deepEqual(this.runInputData[inputId], overrides[inputId])) {
1558
- this.runInputData[inputId] = overrides[inputId];
1559
- changed = true;
1560
- }
1561
- }
1562
- }
1563
- }
1564
- if (inputSchema.additionalProperties) {
1565
- for (const [inputId, value] of Object.entries(overrides)) {
1566
- if (!(inputId in properties)) {
1567
- if (!deepEqual(this.runInputData[inputId], value)) {
1568
- this.runInputData[inputId] = value;
1569
- changed = true;
1570
- }
1571
- }
1572
- }
1573
- }
1574
- return changed;
1575
- }
1576
- async narrowInput(input, _registry) {
1577
- return input;
1578
- }
1579
- subscribe(name, fn) {
1580
- return this.events.subscribe(name, fn);
1581
- }
1582
- on(name, fn) {
1583
- this.events.on(name, fn);
1584
- }
1585
- off(name, fn) {
1586
- this.events.off(name, fn);
1587
- }
1588
- once(name, fn) {
1589
- this.events.once(name, fn);
1590
- }
1591
- waitOn(name) {
1592
- return this.events.waitOn(name);
1593
- }
1594
- emit(name, ...args) {
1595
- this._events?.emit(name, ...args);
1596
- }
1597
- emitSchemaChange(inputSchema, outputSchema) {
1598
- const finalInputSchema = inputSchema ?? this.inputSchema();
1599
- const finalOutputSchema = outputSchema ?? this.outputSchema();
1600
- this.emit("schemaChange", finalInputSchema, finalOutputSchema);
1601
- }
1602
- static getConfigSchemaNode() {
1603
- const schema = this.configSchema();
1604
- if (!schema)
1605
- return;
1606
- if (!Object.hasOwn(this, "__compiledConfigSchema")) {
1607
- try {
1608
- const schemaNode = typeof schema === "boolean" ? compileSchema(schema ? {} : { not: {} }) : compileSchema(schema);
1609
- Object.defineProperty(this, "__compiledConfigSchema", {
1610
- value: schemaNode,
1611
- writable: true,
1612
- configurable: true,
1613
- enumerable: false
1614
- });
1615
- } catch (error) {
1616
- console.warn(`Failed to compile config schema for ${this.type}:`, error);
1617
- return;
1618
- }
1619
- }
1620
- return this.__compiledConfigSchema;
1621
- }
1622
- validateAndApplyConfigDefaults(config) {
1623
- const ctor = this.constructor;
1624
- const schemaNode = ctor.getConfigSchemaNode();
1625
- if (!schemaNode)
1626
- return config;
1627
- const result = schemaNode.validate(config);
1628
- if (!result.valid) {
1629
- const errorMessages = result.errors.map((e) => {
1630
- const path = e.data?.pointer || "";
1631
- return `${e.message}${path ? ` (${path})` : ""}`;
1632
- });
1633
- throw new TaskConfigurationError(`[${ctor.name}] Configuration Error: ${errorMessages.join(", ")}`);
1634
- }
1635
- return config;
1636
- }
1637
- static generateInputSchemaNode(schema) {
1638
- if (typeof schema === "boolean") {
1639
- if (schema === false) {
1640
- return compileSchema({ not: {} });
1641
- }
1642
- return compileSchema({});
1643
- }
1644
- return compileSchema(schema);
1645
- }
1646
- static getInputSchemaNode() {
1647
- if (!Object.hasOwn(this, "__compiledInputSchema")) {
1648
- const dataPortSchema = this.inputSchema();
1649
- const schemaNode = this.generateInputSchemaNode(dataPortSchema);
1650
- try {
1651
- Object.defineProperty(this, "__compiledInputSchema", {
1652
- value: schemaNode,
1653
- writable: true,
1654
- configurable: true,
1655
- enumerable: false
1656
- });
1657
- } catch (error) {
1658
- console.warn(`Failed to compile input schema for ${this.type}, falling back to permissive validation:`, error);
1659
- Object.defineProperty(this, "__compiledInputSchema", {
1660
- value: compileSchema({}),
1661
- writable: true,
1662
- configurable: true,
1663
- enumerable: false
1664
- });
1665
- }
1666
- }
1667
- return this.__compiledInputSchema;
1668
- }
1669
- getInputSchemaNode() {
1670
- return this.constructor.getInputSchemaNode();
1671
- }
1672
- async validateInput(input) {
1673
- const ctor = this.constructor;
1674
- let schemaNode;
1675
- if (ctor.hasDynamicSchemas) {
1676
- const instanceSchema = this.inputSchema();
1677
- schemaNode = ctor.generateInputSchemaNode(instanceSchema);
1678
- } else {
1679
- schemaNode = this.getInputSchemaNode();
1680
- }
1681
- const result = schemaNode.validate(input);
1682
- if (!result.valid) {
1683
- const errorMessages = result.errors.map((e) => {
1684
- const path = e.data.pointer || "";
1685
- return `${e.message}${path ? ` (${path})` : ""}`;
1686
- });
1687
- throw new TaskInvalidInputError(`Input ${JSON.stringify(Object.keys(input))} does not match schema: ${errorMessages.join(", ")}`);
1688
- }
1689
- return true;
1690
- }
1691
- stripSymbols(obj) {
1692
- if (obj === null || obj === undefined) {
1693
- return obj;
1694
- }
1695
- if (ArrayBuffer.isView(obj)) {
1696
- return obj;
1697
- }
1698
- if (Array.isArray(obj)) {
1699
- return obj.map((item) => this.stripSymbols(item));
1700
- }
1701
- if (typeof obj === "object") {
1702
- const result = {};
1703
- for (const key in obj) {
1704
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
1705
- result[key] = this.stripSymbols(obj[key]);
1706
- }
1707
- }
1708
- return result;
1709
- }
1710
- return obj;
1711
- }
1712
- toJSON(_options) {
1713
- const extras = this.config.extras;
1714
- const json = this.stripSymbols({
1715
- id: this.id,
1716
- type: this.type,
1717
- defaults: this.defaults,
1718
- config: {
1719
- ...this.config.title ? { title: this.config.title } : {},
1720
- ...this.config.description ? { description: this.config.description } : {},
1721
- ...this.config.inputSchema ? { inputSchema: this.config.inputSchema } : {},
1722
- ...this.config.outputSchema ? { outputSchema: this.config.outputSchema } : {},
1723
- ...extras && Object.keys(extras).length ? { extras } : {}
1724
- }
1725
- });
1726
- return json;
1727
- }
1728
- toDependencyJSON(options) {
1729
- const json = this.toJSON(options);
1730
- return json;
1731
- }
1732
- hasChildren() {
1733
- return this._subGraph !== undefined && this._subGraph !== null && this._subGraph.getTasks().length > 0;
1734
- }
1735
- _taskAddedListener = () => {
1736
- this.emit("regenerate");
1737
- };
1738
- _subGraph = undefined;
1739
- set subGraph(subGraph) {
1740
- if (this._subGraph) {
1741
- this._subGraph.off("task_added", this._taskAddedListener);
1742
- }
1743
- this._subGraph = subGraph;
1744
- this._subGraph.on("task_added", this._taskAddedListener);
1745
- }
1746
- get subGraph() {
1747
- if (!this._subGraph) {
1748
- this._subGraph = new TaskGraph;
1749
- this._subGraph.on("task_added", this._taskAddedListener);
1750
- }
1751
- return this._subGraph;
1752
- }
1753
- regenerateGraph() {
1754
- if (this.hasChildren()) {
1755
- for (const dataflow of this.subGraph.getDataflows()) {
1756
- this.subGraph.removeDataflow(dataflow);
1757
- }
1758
- for (const child of this.subGraph.getTasks()) {
1759
- this.subGraph.removeTask(child.id);
1760
- }
1761
- }
1762
- this.events.emit("regenerate");
1763
- }
1764
- }
1765
-
1766
- // src/task/ConditionalTask.ts
1767
- var conditionalTaskConfigSchema = {
1768
- type: "object",
1769
- properties: {
1770
- ...TaskConfigSchema["properties"],
1771
- branches: { type: "array", items: {} },
1772
- defaultBranch: { type: "string" },
1773
- exclusive: { type: "boolean" },
1774
- conditionConfig: { type: "object", additionalProperties: true }
1775
- },
1776
- additionalProperties: false
1777
- };
1778
-
1779
- class ConditionalTask extends Task {
1780
- static type = "ConditionalTask";
1781
- static category = "Flow Control";
1782
- static title = "Condition";
1783
- static description = "Route data based on conditions";
1784
- static hasDynamicSchemas = true;
1785
- static configSchema() {
1786
- return conditionalTaskConfigSchema;
1787
- }
1788
- activeBranches = new Set;
1789
- buildBranchesFromConditionConfig(conditionConfig) {
1790
- if (!conditionConfig?.branches || conditionConfig.branches.length === 0) {
1791
- return [
1792
- {
1793
- id: "default",
1794
- condition: () => true,
1795
- outputPort: "1"
1796
- }
1797
- ];
1798
- }
1799
- return conditionConfig.branches.map((branch, index) => ({
1800
- id: branch.id,
1801
- outputPort: String(index + 1),
1802
- condition: (inputData) => {
1803
- const fieldValue = getNestedValue(inputData, branch.field);
1804
- return evaluateCondition(fieldValue, branch.operator, branch.value);
1805
- }
1806
- }));
1807
- }
1808
- resolveBranches(input) {
1809
- const configBranches = this.config.branches ?? [];
1810
- if (configBranches.length > 0 && typeof configBranches[0].condition === "function") {
1811
- return {
1812
- branches: configBranches,
1813
- isExclusive: this.config.exclusive ?? true,
1814
- defaultBranch: this.config.defaultBranch,
1815
- fromConditionConfig: false
1816
- };
1817
- }
1818
- const conditionConfig = input.conditionConfig ?? this.config.conditionConfig;
1819
- if (conditionConfig) {
1820
- return {
1821
- branches: this.buildBranchesFromConditionConfig(conditionConfig),
1822
- isExclusive: conditionConfig.exclusive ?? true,
1823
- defaultBranch: conditionConfig.defaultBranch,
1824
- fromConditionConfig: true
1825
- };
1826
- }
1827
- return {
1828
- branches: configBranches,
1829
- isExclusive: this.config.exclusive ?? true,
1830
- defaultBranch: this.config.defaultBranch,
1831
- fromConditionConfig: false
1832
- };
1833
- }
1834
- async execute(input, context) {
1835
- if (context.signal?.aborted) {
1836
- return;
1837
- }
1838
- this.activeBranches.clear();
1839
- const { branches, isExclusive, defaultBranch, fromConditionConfig } = this.resolveBranches(input);
1840
- for (const branch of branches) {
1841
- try {
1842
- const isActive = branch.condition(input);
1843
- if (isActive) {
1844
- this.activeBranches.add(branch.id);
1845
- if (isExclusive) {
1846
- break;
1847
- }
1848
- }
1849
- } catch (error) {
1850
- getLogger2().warn(`Condition evaluation failed for branch "${branch.id}":`, { error });
1851
- }
1852
- }
1853
- if (this.activeBranches.size === 0 && defaultBranch) {
1854
- const defaultBranchExists = branches.some((b) => b.id === defaultBranch);
1855
- if (defaultBranchExists) {
1856
- this.activeBranches.add(defaultBranch);
1857
- }
1858
- }
1859
- if (fromConditionConfig) {
1860
- return this.buildConditionConfigOutput(input, branches, isExclusive);
1861
- }
1862
- return this.buildOutput(input);
1863
- }
1864
- buildConditionConfigOutput(input, branches, isExclusive) {
1865
- const output = {};
1866
- const { conditionConfig, ...passThrough } = input;
1867
- const inputKeys = Object.keys(passThrough);
1868
- let matchedBranchNumber = null;
1869
- for (let i = 0;i < branches.length; i++) {
1870
- if (this.activeBranches.has(branches[i].id)) {
1871
- if (matchedBranchNumber === null) {
1872
- matchedBranchNumber = i + 1;
1873
- }
1874
- }
1875
- }
1876
- if (isExclusive) {
1877
- if (matchedBranchNumber !== null) {
1878
- for (const key of inputKeys) {
1879
- output[`${key}_${matchedBranchNumber}`] = passThrough[key];
1880
- }
1881
- } else {
1882
- for (const key of inputKeys) {
1883
- output[`${key}_else`] = passThrough[key];
1884
- }
1885
- }
1886
- } else {
1887
- for (let i = 0;i < branches.length; i++) {
1888
- if (this.activeBranches.has(branches[i].id)) {
1889
- for (const key of inputKeys) {
1890
- output[`${key}_${i + 1}`] = passThrough[key];
1891
- }
1892
- }
1893
- }
1894
- }
1895
- return output;
1896
- }
1897
- buildOutput(input) {
1898
- const output = {
1899
- _activeBranches: Array.from(this.activeBranches)
1900
- };
1901
- const branches = this.config.branches ?? [];
1902
- for (const branch of branches) {
1903
- if (this.activeBranches.has(branch.id)) {
1904
- output[branch.outputPort] = { ...input };
1905
- }
1906
- }
1907
- return output;
1908
- }
1909
- isBranchActive(branchId) {
1910
- return this.activeBranches.has(branchId);
1911
- }
1912
- getActiveBranches() {
1913
- return new Set(this.activeBranches);
1914
- }
1915
- getPortActiveStatus() {
1916
- const status = new Map;
1917
- const branches = this.config.branches ?? [];
1918
- for (const branch of branches) {
1919
- status.set(branch.outputPort, this.activeBranches.has(branch.id));
1920
- }
1921
- return status;
1922
- }
1923
- static outputSchema() {
1924
- return {
1925
- type: "object",
1926
- properties: {
1927
- _activeBranches: {
1928
- type: "array",
1929
- items: { type: "string" },
1930
- description: "List of active branch IDs after condition evaluation"
1931
- }
1932
- },
1933
- additionalProperties: true
1934
- };
1935
- }
1936
- outputSchema() {
1937
- const branches = this.config?.branches ?? [];
1938
- const properties = {
1939
- _activeBranches: {
1940
- type: "array",
1941
- items: { type: "string" },
1942
- description: "List of active branch IDs after condition evaluation"
1943
- }
1944
- };
1945
- for (const branch of branches) {
1946
- properties[branch.outputPort] = {
1947
- type: "object",
1948
- description: `Output for branch "${branch.id}" when active`,
1949
- additionalProperties: true
1950
- };
1951
- }
1952
- return {
1953
- type: "object",
1954
- properties,
1955
- additionalProperties: false
1956
- };
1957
- }
1958
- static inputSchema() {
1959
- return {
1960
- type: "object",
1961
- properties: {},
1962
- additionalProperties: true
1963
- };
1964
- }
1965
- inputSchema() {
1966
- return {
1967
- type: "object",
1968
- properties: {},
1969
- additionalProperties: true
1970
- };
1971
- }
1972
- }
1973
-
1974
- // src/task-graph/TaskGraphScheduler.ts
1975
- class TopologicalScheduler {
1976
- dag;
1977
- sortedNodes;
1978
- currentIndex;
1979
- constructor(dag) {
1980
- this.dag = dag;
1981
- this.sortedNodes = [];
1982
- this.currentIndex = 0;
1983
- this.reset();
1984
- }
1985
- async* tasks() {
1986
- while (this.currentIndex < this.sortedNodes.length) {
1987
- yield this.sortedNodes[this.currentIndex++];
1988
- }
1989
- }
1990
- onTaskCompleted(taskId) {}
1991
- onTaskStreaming(taskId) {}
1992
- reset() {
1993
- this.sortedNodes = this.dag.topologicallySortedNodes();
1994
- this.currentIndex = 0;
1995
- }
1996
- }
1997
-
1998
- class DependencyBasedScheduler {
1999
- dag;
2000
- completedTasks;
2001
- streamingTasks;
2002
- pendingTasks;
2003
- nextResolver = null;
2004
- constructor(dag) {
2005
- this.dag = dag;
2006
- this.completedTasks = new Set;
2007
- this.streamingTasks = new Set;
2008
- this.pendingTasks = new Set;
2009
- this.reset();
2010
- }
2011
- isTaskReady(task) {
2012
- if (task.status === TaskStatus.DISABLED) {
2013
- return false;
2014
- }
2015
- const sourceDataflows = this.dag.getSourceDataflows(task.id);
2016
- if (sourceDataflows.length > 0) {
2017
- const allIncomingDisabled = sourceDataflows.every((df) => df.status === TaskStatus.DISABLED);
2018
- if (allIncomingDisabled) {
2019
- return false;
2020
- }
2021
- }
2022
- const activeDataflows = sourceDataflows.filter((df) => df.status !== TaskStatus.DISABLED);
2023
- return activeDataflows.every((df) => {
2024
- const depId = df.sourceTaskId;
2025
- if (this.completedTasks.has(depId))
2026
- return true;
2027
- if (this.streamingTasks.has(depId)) {
2028
- const sourceTask = this.dag.getTask(depId);
2029
- if (sourceTask) {
2030
- const sourceMode = getPortStreamMode(sourceTask.outputSchema(), df.sourceTaskPortId);
2031
- const targetMode = getPortStreamMode(task.inputSchema(), df.targetTaskPortId);
2032
- if (sourceMode !== "none" && sourceMode === targetMode) {
2033
- return true;
2034
- }
2035
- }
2036
- }
2037
- return false;
2038
- });
2039
- }
2040
- async waitForNextTask() {
2041
- if (this.pendingTasks.size === 0)
2042
- return null;
2043
- for (const task of Array.from(this.pendingTasks)) {
2044
- if (task.status === TaskStatus.DISABLED) {
2045
- this.pendingTasks.delete(task);
2046
- }
2047
- }
2048
- if (this.pendingTasks.size === 0)
2049
- return null;
2050
- const readyTask = Array.from(this.pendingTasks).find((task) => this.isTaskReady(task));
2051
- if (readyTask) {
2052
- this.pendingTasks.delete(readyTask);
2053
- return readyTask;
2054
- }
2055
- if (this.pendingTasks.size > 0) {
2056
- return new Promise((resolve) => {
2057
- this.nextResolver = resolve;
2058
- });
2059
- }
2060
- return null;
2061
- }
2062
- async* tasks() {
2063
- while (this.pendingTasks.size > 0) {
2064
- const task = await this.waitForNextTask();
2065
- if (task) {
2066
- yield task;
2067
- } else {
2068
- break;
2069
- }
2070
- }
2071
- }
2072
- onTaskCompleted(taskId) {
2073
- this.completedTasks.add(taskId);
2074
- for (const task of Array.from(this.pendingTasks)) {
2075
- if (task.status === TaskStatus.DISABLED) {
2076
- this.pendingTasks.delete(task);
2077
- }
2078
- }
2079
- if (this.nextResolver) {
2080
- const readyTask = Array.from(this.pendingTasks).find((task) => this.isTaskReady(task));
2081
- if (readyTask) {
2082
- this.pendingTasks.delete(readyTask);
2083
- const resolver = this.nextResolver;
2084
- this.nextResolver = null;
2085
- resolver(readyTask);
2086
- } else if (this.pendingTasks.size === 0) {
2087
- const resolver = this.nextResolver;
2088
- this.nextResolver = null;
2089
- resolver(null);
2090
- }
2091
- }
2092
- }
2093
- onTaskStreaming(taskId) {
2094
- this.streamingTasks.add(taskId);
2095
- for (const task of Array.from(this.pendingTasks)) {
2096
- if (task.status === TaskStatus.DISABLED) {
2097
- this.pendingTasks.delete(task);
2098
- }
2099
- }
2100
- if (this.nextResolver) {
2101
- const readyTask = Array.from(this.pendingTasks).find((task) => this.isTaskReady(task));
2102
- if (readyTask) {
2103
- this.pendingTasks.delete(readyTask);
2104
- const resolver = this.nextResolver;
2105
- this.nextResolver = null;
2106
- resolver(readyTask);
2107
- }
2108
- }
2109
- }
2110
- reset() {
2111
- this.completedTasks.clear();
2112
- this.streamingTasks.clear();
2113
- this.pendingTasks = new Set(this.dag.topologicallySortedNodes());
2114
- this.nextResolver = null;
2115
- }
2116
- }
2117
-
2118
- // src/task-graph/TaskGraphRunner.ts
2119
- var PROPERTY_ARRAY = "PROPERTY_ARRAY";
2120
- var GRAPH_RESULT_ARRAY = "GRAPH_RESULT_ARRAY";
2121
-
2122
- class TaskGraphRunner {
2123
- processScheduler;
2124
- reactiveScheduler;
2125
- running = false;
2126
- reactiveRunning = false;
2127
- graph;
2128
- outputCache;
2129
- accumulateLeafOutputs = true;
2130
- registry = globalServiceRegistry2;
2131
- abortController;
2132
- inProgressTasks = new Map;
2133
- inProgressFunctions = new Map;
2134
- failedTaskErrors = new Map;
2135
- constructor(graph, outputCache, processScheduler = new DependencyBasedScheduler(graph), reactiveScheduler = new TopologicalScheduler(graph)) {
2136
- this.processScheduler = processScheduler;
2137
- this.reactiveScheduler = reactiveScheduler;
2138
- this.graph = graph;
2139
- graph.outputCache = outputCache;
2140
- this.handleProgress = this.handleProgress.bind(this);
2141
- }
2142
- runId = "";
2143
- get timerLabel() {
2144
- return `graph:${this.runId}`;
2145
- }
2146
- async runGraph(input = {}, config) {
2147
- await this.handleStart(config);
2148
- const results = [];
2149
- let error;
2150
- try {
2151
- for await (const task of this.processScheduler.tasks()) {
2152
- if (this.abortController?.signal.aborted) {
2153
- break;
2154
- }
2155
- if (this.failedTaskErrors.size > 0) {
2156
- break;
2157
- }
2158
- const isRootTask = this.graph.getSourceDataflows(task.id).length === 0;
2159
- const runAsync = async () => {
2160
- let errorRouted = false;
2161
- try {
2162
- const taskInput = isRootTask ? input : this.filterInputForTask(task, input);
2163
- const taskPromise = this.runTask(task, taskInput);
2164
- this.inProgressTasks.set(task.id, taskPromise);
2165
- const taskResult = await taskPromise;
2166
- if (this.graph.getTargetDataflows(task.id).length === 0) {
2167
- results.push(taskResult);
2168
- }
2169
- } catch (error2) {
2170
- if (this.hasErrorOutputEdges(task)) {
2171
- errorRouted = true;
2172
- this.pushErrorOutputToEdges(task);
2173
- } else {
2174
- this.failedTaskErrors.set(task.id, error2);
2175
- }
2176
- } finally {
2177
- if (!errorRouted) {
2178
- this.pushStatusFromNodeToEdges(this.graph, task);
2179
- this.pushErrorFromNodeToEdges(this.graph, task);
2180
- }
2181
- this.processScheduler.onTaskCompleted(task.id);
2182
- }
2183
- };
2184
- this.inProgressFunctions.set(Symbol(task.id), runAsync());
2185
- }
2186
- } catch (err) {
2187
- error = err;
2188
- }
2189
- await Promise.allSettled(Array.from(this.inProgressTasks.values()));
2190
- await Promise.allSettled(Array.from(this.inProgressFunctions.values()));
2191
- if (this.failedTaskErrors.size > 0) {
2192
- const latestError = this.failedTaskErrors.values().next().value;
2193
- this.handleError(latestError);
2194
- throw latestError;
2195
- }
2196
- if (this.abortController?.signal.aborted) {
2197
- await this.handleAbort();
2198
- throw new TaskAbortedError;
2199
- }
2200
- await this.handleComplete();
2201
- return results;
2202
- }
2203
- async runGraphReactive(input = {}, config) {
2204
- await this.handleStartReactive(config);
2205
- const results = [];
2206
- try {
2207
- for await (const task of this.reactiveScheduler.tasks()) {
2208
- const isRootTask = this.graph.getSourceDataflows(task.id).length === 0;
2209
- if (task.status === TaskStatus.PENDING) {
2210
- task.resetInputData();
2211
- this.copyInputFromEdgesToNode(task);
2212
- }
2213
- const taskInput = isRootTask ? input : {};
2214
- const taskResult = await task.runReactive(taskInput);
2215
- await this.pushOutputFromNodeToEdges(task, taskResult);
2216
- if (this.graph.getTargetDataflows(task.id).length === 0) {
2217
- results.push({
2218
- id: task.id,
2219
- type: task.constructor.runtype || task.constructor.type,
2220
- data: taskResult
2221
- });
2222
- }
2223
- }
2224
- await this.handleCompleteReactive();
2225
- return results;
2226
- } catch (error) {
2227
- await this.handleErrorReactive();
2228
- throw error;
2229
- }
2230
- }
2231
- abort() {
2232
- this.abortController?.abort();
2233
- }
2234
- async disable() {
2235
- await this.handleDisable();
2236
- }
2237
- filterInputForTask(task, input) {
2238
- const sourceDataflows = this.graph.getSourceDataflows(task.id);
2239
- const connectedInputs = new Set(sourceDataflows.map((df) => df.targetTaskPortId));
2240
- const allPortsConnected = connectedInputs.has(DATAFLOW_ALL_PORTS);
2241
- const filteredInput = {};
2242
- for (const [key, value] of Object.entries(input)) {
2243
- if (!connectedInputs.has(key) && !allPortsConnected) {
2244
- filteredInput[key] = value;
2245
- }
2246
- }
2247
- return filteredInput;
2248
- }
2249
- addInputData(task, overrides) {
2250
- if (!overrides)
2251
- return;
2252
- const changed = task.addInput(overrides);
2253
- if (changed && "regenerateGraph" in task && typeof task.regenerateGraph === "function") {
2254
- task.regenerateGraph();
2255
- }
2256
- }
2257
- mergeExecuteOutputsToRunOutput(results, compoundMerge) {
2258
- if (compoundMerge === GRAPH_RESULT_ARRAY) {
2259
- return results;
2260
- }
2261
- if (compoundMerge === PROPERTY_ARRAY) {
2262
- let fixedOutput = {};
2263
- const outputs = results.map((result) => result.data);
2264
- if (outputs.length === 1) {
2265
- fixedOutput = outputs[0];
2266
- } else if (outputs.length > 1) {
2267
- const collected = collectPropertyValues(outputs);
2268
- if (Object.keys(collected).length > 0) {
2269
- fixedOutput = collected;
2270
- }
2271
- }
2272
- return fixedOutput;
2273
- }
2274
- throw new TaskConfigurationError(`Unknown compound merge strategy: ${compoundMerge}`);
2275
- }
2276
- copyInputFromEdgesToNode(task) {
2277
- const dataflows = this.graph.getSourceDataflows(task.id);
2278
- for (const dataflow of dataflows) {
2279
- this.addInputData(task, dataflow.getPortData());
2280
- }
2281
- }
2282
- async pushOutputFromNodeToEdges(node, results) {
2283
- const dataflows = this.graph.getTargetDataflows(node.id);
2284
- for (const dataflow of dataflows) {
2285
- const compatibility = dataflow.semanticallyCompatible(this.graph, dataflow);
2286
- getLogger3().debug("pushOutputFromNodeToEdges", {
2287
- dataflowId: dataflow.id,
2288
- compatibility,
2289
- resultsKeys: Object.keys(results)
2290
- });
2291
- if (compatibility === "static") {
2292
- dataflow.setPortData(results);
2293
- } else if (compatibility === "runtime") {
2294
- const task = this.graph.getTask(dataflow.targetTaskId);
2295
- const narrowed = await task.narrowInput({ ...results }, this.registry);
2296
- dataflow.setPortData(narrowed);
2297
- } else {
2298
- getLogger3().warn("pushOutputFromNodeToEdges", {
2299
- dataflowId: dataflow.id,
2300
- compatibility,
2301
- resultsKeys: Object.keys(results)
2302
- });
2303
- }
2304
- }
2305
- }
2306
- pushStatusFromNodeToEdges(graph, node, status) {
2307
- if (!node?.config?.id)
2308
- return;
2309
- const dataflows = graph.getTargetDataflows(node.id);
2310
- const effectiveStatus = status ?? node.status;
2311
- if (node instanceof ConditionalTask && effectiveStatus === TaskStatus.COMPLETED) {
2312
- const branches = node.config.branches ?? [];
2313
- const portToBranch = new Map;
2314
- for (const branch of branches) {
2315
- portToBranch.set(branch.outputPort, branch.id);
2316
- }
2317
- const activeBranches = node.getActiveBranches();
2318
- for (const dataflow of dataflows) {
2319
- const branchId = portToBranch.get(dataflow.sourceTaskPortId);
2320
- if (branchId !== undefined) {
2321
- if (activeBranches.has(branchId)) {
2322
- dataflow.setStatus(TaskStatus.COMPLETED);
2323
- } else {
2324
- dataflow.setStatus(TaskStatus.DISABLED);
2325
- }
2326
- } else {
2327
- dataflow.setStatus(effectiveStatus);
2328
- }
2329
- }
2330
- this.propagateDisabledStatus(graph);
2331
- return;
2332
- }
2333
- dataflows.forEach((dataflow) => {
2334
- dataflow.setStatus(effectiveStatus);
2335
- });
2336
- }
2337
- pushErrorFromNodeToEdges(graph, node) {
2338
- if (!node?.config?.id)
2339
- return;
2340
- graph.getTargetDataflows(node.id).forEach((dataflow) => {
2341
- dataflow.error = node.error;
2342
- });
2343
- }
2344
- hasErrorOutputEdges(task) {
2345
- const dataflows = this.graph.getTargetDataflows(task.id);
2346
- return dataflows.some((df) => df.sourceTaskPortId === DATAFLOW_ERROR_PORT);
2347
- }
2348
- pushErrorOutputToEdges(task) {
2349
- const taskError = task.error;
2350
- const errorData = {
2351
- error: taskError?.message ?? "Unknown error",
2352
- errorType: taskError?.constructor?.type ?? "TaskError"
2353
- };
2354
- const dataflows = this.graph.getTargetDataflows(task.id);
2355
- for (const df of dataflows) {
2356
- if (df.sourceTaskPortId === DATAFLOW_ERROR_PORT) {
2357
- df.value = errorData;
2358
- df.setStatus(TaskStatus.COMPLETED);
2359
- } else {
2360
- df.setStatus(TaskStatus.DISABLED);
2361
- }
2362
- }
2363
- this.propagateDisabledStatus(this.graph);
2364
- }
2365
- propagateDisabledStatus(graph) {
2366
- let changed = true;
2367
- while (changed) {
2368
- changed = false;
2369
- for (const task of graph.getTasks()) {
2370
- if (task.status !== TaskStatus.PENDING) {
2371
- continue;
2372
- }
2373
- const incomingDataflows = graph.getSourceDataflows(task.id);
2374
- if (incomingDataflows.length === 0) {
2375
- continue;
2376
- }
2377
- const allDisabled = incomingDataflows.every((df) => df.status === TaskStatus.DISABLED);
2378
- if (allDisabled) {
2379
- task.status = TaskStatus.DISABLED;
2380
- task.progress = 100;
2381
- task.completedAt = new Date;
2382
- task.emit("disabled");
2383
- task.emit("status", task.status);
2384
- graph.getTargetDataflows(task.id).forEach((dataflow) => {
2385
- dataflow.setStatus(TaskStatus.DISABLED);
2386
- });
2387
- this.processScheduler.onTaskCompleted(task.id);
2388
- changed = true;
2389
- }
2390
- }
2391
- }
2392
- }
2393
- taskNeedsAccumulation(task) {
2394
- if (this.outputCache)
2395
- return true;
2396
- const outEdges = this.graph.getTargetDataflows(task.id);
2397
- if (outEdges.length === 0)
2398
- return this.accumulateLeafOutputs;
2399
- const outSchema = task.outputSchema();
2400
- for (const df of outEdges) {
2401
- if (df.sourceTaskPortId === DATAFLOW_ALL_PORTS) {
2402
- if (getStreamingPorts(outSchema).length > 0)
2403
- return true;
2404
- continue;
2405
- }
2406
- const targetTask = this.graph.getTask(df.targetTaskId);
2407
- if (!targetTask)
2408
- continue;
2409
- const inSchema = targetTask.inputSchema();
2410
- if (edgeNeedsAccumulation(outSchema, df.sourceTaskPortId, inSchema, df.targetTaskPortId)) {
2411
- return true;
2412
- }
2413
- }
2414
- return false;
2415
- }
2416
- async runTask(task, input) {
2417
- const isStreamable = isTaskStreamable(task);
2418
- if (isStreamable) {
2419
- const dataflows = this.graph.getSourceDataflows(task.id);
2420
- const streamingEdges = dataflows.filter((df) => df.stream !== undefined);
2421
- if (streamingEdges.length > 0) {
2422
- const inputStreams = new Map;
2423
- for (const df of streamingEdges) {
2424
- const stream = df.stream;
2425
- const [forwardCopy, materializeCopy] = stream.tee();
2426
- inputStreams.set(df.targetTaskPortId, forwardCopy);
2427
- df.setStream(materializeCopy);
2428
- }
2429
- task.runner.inputStreams = inputStreams;
2430
- }
2431
- }
2432
- await this.awaitStreamInputs(task);
2433
- this.copyInputFromEdgesToNode(task);
2434
- if (isStreamable) {
2435
- return this.runStreamingTask(task, input);
2436
- }
2437
- const results = await task.runner.run(input, {
2438
- outputCache: this.outputCache ?? false,
2439
- updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
2440
- registry: this.registry
2441
- });
2442
- await this.pushOutputFromNodeToEdges(task, results);
2443
- return {
2444
- id: task.id,
2445
- type: task.constructor.runtype || task.constructor.type,
2446
- data: results
2447
- };
2448
- }
2449
- async awaitStreamInputs(task) {
2450
- const dataflows = this.graph.getSourceDataflows(task.id);
2451
- const streamPromises = dataflows.filter((df) => df.stream !== undefined).map((df) => df.awaitStreamValue());
2452
- if (streamPromises.length > 0) {
2453
- await Promise.all(streamPromises);
2454
- }
2455
- }
2456
- async runStreamingTask(task, input) {
2457
- const streamMode = getOutputStreamMode(task.outputSchema());
2458
- const shouldAccumulate = this.taskNeedsAccumulation(task);
2459
- let streamingNotified = false;
2460
- const onStatus = (status) => {
2461
- if (status === TaskStatus.STREAMING && !streamingNotified) {
2462
- streamingNotified = true;
2463
- this.pushStatusFromNodeToEdges(this.graph, task, TaskStatus.STREAMING);
2464
- this.pushStreamToEdges(task, streamMode);
2465
- this.processScheduler.onTaskStreaming(task.id);
2466
- }
2467
- };
2468
- const onStreamStart = () => {
2469
- this.graph.emit("task_stream_start", task.id);
2470
- };
2471
- const onStreamChunk = (event) => {
2472
- this.graph.emit("task_stream_chunk", task.id, event);
2473
- };
2474
- const onStreamEnd = (output) => {
2475
- this.graph.emit("task_stream_end", task.id, output);
2476
- };
2477
- task.on("status", onStatus);
2478
- task.on("stream_start", onStreamStart);
2479
- task.on("stream_chunk", onStreamChunk);
2480
- task.on("stream_end", onStreamEnd);
2481
- try {
2482
- const results = await task.runner.run(input, {
2483
- outputCache: this.outputCache ?? false,
2484
- shouldAccumulate,
2485
- updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
2486
- registry: this.registry
2487
- });
2488
- await this.pushOutputFromNodeToEdges(task, results);
2489
- return {
2490
- id: task.id,
2491
- type: task.constructor.runtype || task.constructor.type,
2492
- data: results
2493
- };
2494
- } finally {
2495
- task.off("status", onStatus);
2496
- task.off("stream_start", onStreamStart);
2497
- task.off("stream_chunk", onStreamChunk);
2498
- task.off("stream_end", onStreamEnd);
2499
- }
2500
- }
2501
- static isPortDelta(event) {
2502
- return event.type === "text-delta" || event.type === "object-delta";
2503
- }
2504
- createStreamFromTaskEvents(task, portId) {
2505
- return new ReadableStream({
2506
- start: (controller) => {
2507
- const onChunk = (event) => {
2508
- try {
2509
- if (portId !== undefined && TaskGraphRunner.isPortDelta(event) && event.port !== portId) {
2510
- return;
2511
- }
2512
- controller.enqueue(event);
2513
- } catch {}
2514
- };
2515
- const onEnd = () => {
2516
- try {
2517
- controller.close();
2518
- } catch {}
2519
- task.off("stream_chunk", onChunk);
2520
- task.off("stream_end", onEnd);
2521
- };
2522
- task.on("stream_chunk", onChunk);
2523
- task.on("stream_end", onEnd);
2524
- }
2525
- });
2526
- }
2527
- pushStreamToEdges(task, streamMode) {
2528
- const targetDataflows = this.graph.getTargetDataflows(task.id);
2529
- if (targetDataflows.length === 0)
2530
- return;
2531
- const groups = new Map;
2532
- for (const df of targetDataflows) {
2533
- const key = df.sourceTaskPortId;
2534
- let group = groups.get(key);
2535
- if (!group) {
2536
- group = [];
2537
- groups.set(key, group);
2538
- }
2539
- group.push(df);
2540
- }
2541
- for (const [portKey, edges] of groups) {
2542
- const filterPort = portKey === DATAFLOW_ALL_PORTS ? undefined : portKey;
2543
- const stream = this.createStreamFromTaskEvents(task, filterPort);
2544
- if (edges.length === 1) {
2545
- edges[0].setStream(stream);
2546
- } else {
2547
- let currentStream = stream;
2548
- for (let i = 0;i < edges.length; i++) {
2549
- if (i === edges.length - 1) {
2550
- edges[i].setStream(currentStream);
2551
- } else {
2552
- const [s1, s2] = currentStream.tee();
2553
- edges[i].setStream(s1);
2554
- currentStream = s2;
2555
- }
2556
- }
2557
- }
2558
- }
2559
- }
2560
- resetTask(graph, task, runId) {
2561
- task.status = TaskStatus.PENDING;
2562
- task.resetInputData();
2563
- task.runOutputData = {};
2564
- task.error = undefined;
2565
- task.progress = 0;
2566
- task.runConfig = { ...task.runConfig, runnerId: runId };
2567
- this.pushStatusFromNodeToEdges(graph, task);
2568
- this.pushErrorFromNodeToEdges(graph, task);
2569
- task.emit("reset");
2570
- task.emit("status", task.status);
2571
- }
2572
- resetGraph(graph, runnerId) {
2573
- graph.getTasks().forEach((node) => {
2574
- this.resetTask(graph, node, runnerId);
2575
- node.regenerateGraph();
2576
- if (node.hasChildren()) {
2577
- this.resetGraph(node.subGraph, runnerId);
2578
- }
2579
- });
2580
- graph.getDataflows().forEach((dataflow) => {
2581
- dataflow.reset();
2582
- });
2583
- }
2584
- async handleStart(config) {
2585
- if (config?.registry !== undefined) {
2586
- this.registry = config.registry;
2587
- } else if (this.registry === undefined) {
2588
- this.registry = new ServiceRegistry2(globalServiceRegistry2.container.createChildContainer());
2589
- }
2590
- this.accumulateLeafOutputs = config?.accumulateLeafOutputs !== false;
2591
- if (config?.outputCache !== undefined) {
2592
- if (typeof config.outputCache === "boolean") {
2593
- if (config.outputCache === true) {
2594
- this.outputCache = this.registry.get(TASK_OUTPUT_REPOSITORY);
2595
- } else {
2596
- this.outputCache = undefined;
2597
- }
2598
- } else {
2599
- this.outputCache = config.outputCache;
2600
- }
2601
- this.graph.outputCache = this.outputCache;
2602
- }
2603
- if (this.running || this.reactiveRunning) {
2604
- throw new TaskConfigurationError("Graph is already running");
2605
- }
2606
- this.running = true;
2607
- this.abortController = new AbortController;
2608
- this.abortController.signal.addEventListener("abort", () => {
2609
- this.handleAbort();
2610
- });
2611
- if (config?.parentSignal?.aborted) {
2612
- this.abortController.abort();
2613
- return;
2614
- } else {
2615
- config?.parentSignal?.addEventListener("abort", () => {
2616
- this.abortController?.abort();
2617
- }, { once: true });
2618
- }
2619
- this.runId = uuid43();
2620
- this.resetGraph(this.graph, this.runId);
2621
- this.processScheduler.reset();
2622
- this.inProgressTasks.clear();
2623
- this.inProgressFunctions.clear();
2624
- this.failedTaskErrors.clear();
2625
- const logger = getLogger3();
2626
- logger.group(this.timerLabel, { graph: this.graph });
2627
- logger.time(this.timerLabel);
2628
- this.graph.emit("start");
2629
- }
2630
- async handleStartReactive(config) {
2631
- if (this.reactiveRunning) {
2632
- throw new TaskConfigurationError("Graph is already running reactively");
2633
- }
2634
- if (config?.registry !== undefined) {
2635
- this.registry = config.registry;
2636
- }
2637
- this.reactiveScheduler.reset();
2638
- this.reactiveRunning = true;
2639
- }
2640
- async handleComplete() {
2641
- this.running = false;
2642
- const logger = getLogger3();
2643
- logger.timeEnd(this.timerLabel);
2644
- logger.groupEnd();
2645
- this.graph.emit("complete");
2646
- }
2647
- async handleCompleteReactive() {
2648
- this.reactiveRunning = false;
2649
- }
2650
- async handleError(error) {
2651
- await Promise.allSettled(this.graph.getTasks().map(async (task) => {
2652
- if (task.status === TaskStatus.PROCESSING || task.status === TaskStatus.STREAMING) {
2653
- task.abort();
2654
- }
2655
- }));
2656
- this.running = false;
2657
- const logger = getLogger3();
2658
- logger.timeEnd(this.timerLabel);
2659
- logger.groupEnd();
2660
- this.graph.emit("error", error);
2661
- }
2662
- async handleErrorReactive() {
2663
- this.reactiveRunning = false;
2664
- }
2665
- async handleAbort() {
2666
- this.graph.getTasks().map(async (task) => {
2667
- if (task.status === TaskStatus.PROCESSING || task.status === TaskStatus.STREAMING) {
2668
- task.abort();
2669
- }
2670
- });
2671
- this.running = false;
2672
- const logger = getLogger3();
2673
- logger.timeEnd(this.timerLabel);
2674
- logger.groupEnd();
2675
- this.graph.emit("abort");
2676
- }
2677
- async handleAbortReactive() {
2678
- this.reactiveRunning = false;
2679
- }
2680
- async handleDisable() {
2681
- await Promise.allSettled(this.graph.getTasks().map(async (task) => {
2682
- if (task.status === TaskStatus.PENDING) {
2683
- return task.disable();
2684
- }
2685
- }));
2686
- this.running = false;
2687
- this.graph.emit("disabled");
2688
- }
2689
- async handleProgress(task, progress, message, ...args) {
2690
- const total = this.graph.getTasks().length;
2691
- if (total > 1) {
2692
- const completed = this.graph.getTasks().reduce((acc, t) => acc + t.progress, 0);
2693
- progress = Math.round(completed / total);
2694
- }
2695
- this.pushStatusFromNodeToEdges(this.graph, task);
2696
- await this.pushOutputFromNodeToEdges(task, task.runOutputData);
2697
- this.graph.emit("graph_progress", progress, message, args);
2698
- }
2699
- }
2700
-
2701
- // src/task/GraphAsTaskRunner.ts
2702
- class GraphAsTaskRunner extends TaskRunner {
2703
- async executeTaskChildren(input) {
2704
- const unsubscribe = this.task.subGraph.subscribe("graph_progress", (progress, message, ...args) => {
2705
- this.task.emit("progress", progress, message, ...args);
2706
- });
2707
- const results = await this.task.subGraph.run(input, {
2708
- parentSignal: this.abortController?.signal,
2709
- outputCache: this.outputCache
2710
- });
2711
- unsubscribe();
2712
- return results;
2713
- }
2714
- async executeTaskChildrenReactive() {
2715
- return this.task.subGraph.runReactive(this.task.runInputData);
2716
- }
2717
- async handleDisable() {
2718
- if (this.task.hasChildren()) {
2719
- await this.task.subGraph.disable();
2720
- }
2721
- super.handleDisable();
2722
- }
2723
- async executeTask(input) {
2724
- if (this.task.hasChildren()) {
2725
- const runExecuteOutputData = await this.executeTaskChildren(input);
2726
- this.task.runOutputData = this.task.subGraph.mergeExecuteOutputsToRunOutput(runExecuteOutputData, this.task.compoundMerge);
2727
- } else {
2728
- const result = await super.executeTask(input);
2729
- this.task.runOutputData = result ?? {};
2730
- }
2731
- return this.task.runOutputData;
2732
- }
2733
- async executeTaskReactive(input, output) {
2734
- if (this.task.hasChildren()) {
2735
- const reactiveResults = await this.executeTaskChildrenReactive();
2736
- this.task.runOutputData = this.task.subGraph.mergeExecuteOutputsToRunOutput(reactiveResults, this.task.compoundMerge);
2737
- } else {
2738
- const reactiveResults = await super.executeTaskReactive(input, output);
2739
- this.task.runOutputData = Object.assign({}, output, reactiveResults ?? {});
2740
- }
2741
- return this.task.runOutputData;
2742
- }
2743
- }
2744
-
2745
- // src/task/GraphAsTask.ts
2746
- var graphAsTaskConfigSchema = {
2747
- type: "object",
2748
- properties: {
2749
- ...TaskConfigSchema["properties"],
2750
- compoundMerge: { type: "string", "x-ui-hidden": true }
2751
- },
2752
- additionalProperties: false
2753
- };
2754
-
2755
- class GraphAsTask extends Task {
2756
- static type = "GraphAsTask";
2757
- static title = "Group";
2758
- static description = "A group of tasks that are executed together";
2759
- static category = "Flow Control";
2760
- static compoundMerge = PROPERTY_ARRAY;
2761
- static hasDynamicSchemas = true;
2762
- constructor(input = {}, config = {}) {
2763
- const { subGraph, ...rest } = config;
2764
- super(input, rest);
2765
- if (subGraph) {
2766
- this.subGraph = subGraph;
2767
- }
2768
- this.regenerateGraph();
2769
- }
2770
- get runner() {
2771
- if (!this._runner) {
2772
- this._runner = new GraphAsTaskRunner(this);
2773
- }
2774
- return this._runner;
2775
- }
2776
- static configSchema() {
2777
- return graphAsTaskConfigSchema;
2778
- }
2779
- get compoundMerge() {
2780
- return this.config?.compoundMerge || this.constructor.compoundMerge;
2781
- }
2782
- get cacheable() {
2783
- return this.runConfig?.cacheable ?? this.config?.cacheable ?? (this.constructor.cacheable && !this.hasChildren());
2784
- }
2785
- inputSchema() {
2786
- if (!this.hasChildren()) {
2787
- return this.constructor.inputSchema();
2788
- }
2789
- return computeGraphInputSchema(this.subGraph);
2790
- }
2791
- _inputSchemaNode;
2792
- getInputSchemaNode() {
2793
- if (!this._inputSchemaNode) {
2794
- try {
2795
- const dataPortSchema = this.inputSchema();
2796
- const schemaNode = Task.generateInputSchemaNode(dataPortSchema);
2797
- this._inputSchemaNode = schemaNode;
2798
- } catch (error) {
2799
- console.warn(`Failed to compile input schema for ${this.type}, falling back to permissive validation:`, error);
2800
- this._inputSchemaNode = compileSchema2({});
2801
- }
2802
- }
2803
- return this._inputSchemaNode;
2804
- }
2805
- outputSchema() {
2806
- if (!this.hasChildren()) {
2807
- return this.constructor.outputSchema();
2808
- }
2809
- return computeGraphOutputSchema(this.subGraph);
2810
- }
2811
- resetInputData() {
2812
- super.resetInputData();
2813
- if (this.hasChildren()) {
2814
- this.subGraph.getTasks().forEach((node) => {
2815
- node.resetInputData();
2816
- });
2817
- this.subGraph.getDataflows().forEach((dataflow) => {
2818
- dataflow.reset();
2819
- });
2820
- }
2821
- }
2822
- async* executeStream(input, context) {
2823
- if (context.inputStreams) {
2824
- for (const [, stream] of context.inputStreams) {
2825
- const reader = stream.getReader();
2826
- try {
2827
- while (true) {
2828
- const { done, value } = await reader.read();
2829
- if (done)
2830
- break;
2831
- if (value.type === "finish")
2832
- continue;
2833
- yield value;
2834
- }
2835
- } finally {
2836
- reader.releaseLock();
2837
- }
2838
- }
2839
- }
2840
- if (this.hasChildren()) {
2841
- const endingNodeIds = new Set;
2842
- const tasks = this.subGraph.getTasks();
2843
- for (const task of tasks) {
2844
- if (this.subGraph.getTargetDataflows(task.id).length === 0) {
2845
- endingNodeIds.add(task.id);
2846
- }
2847
- }
2848
- const eventQueue = [];
2849
- let resolveWaiting;
2850
- let subgraphDone = false;
2851
- const unsub = this.subGraph.subscribeToTaskStreaming({
2852
- onStreamChunk: (taskId, event) => {
2853
- if (endingNodeIds.has(taskId) && event.type !== "finish") {
2854
- eventQueue.push(event);
2855
- resolveWaiting?.();
2856
- }
2857
- }
2858
- });
2859
- const runPromise = this.subGraph.run(input, { parentSignal: context.signal, accumulateLeafOutputs: false }).then((results2) => {
2860
- subgraphDone = true;
2861
- resolveWaiting?.();
2862
- return results2;
2863
- });
2864
- while (!subgraphDone) {
2865
- if (eventQueue.length === 0) {
2866
- await new Promise((resolve) => {
2867
- resolveWaiting = resolve;
2868
- });
2869
- }
2870
- while (eventQueue.length > 0) {
2871
- yield eventQueue.shift();
2872
- }
2873
- }
2874
- while (eventQueue.length > 0) {
2875
- yield eventQueue.shift();
2876
- }
2877
- unsub();
2878
- const results = await runPromise;
2879
- const mergedOutput = this.subGraph.mergeExecuteOutputsToRunOutput(results, this.compoundMerge);
2880
- yield { type: "finish", data: mergedOutput };
2881
- } else {
2882
- yield { type: "finish", data: input };
2883
- }
2884
- }
2885
- regenerateGraph() {
2886
- this._inputSchemaNode = undefined;
2887
- this.events.emit("regenerate");
2888
- }
2889
- toJSON(options) {
2890
- let json = super.toJSON(options);
2891
- const hasChildren = this.hasChildren();
2892
- if (hasChildren) {
2893
- json = {
2894
- ...json,
2895
- merge: this.compoundMerge,
2896
- subgraph: this.subGraph.toJSON(options)
2897
- };
2898
- }
2899
- return json;
2900
- }
2901
- toDependencyJSON(options) {
2902
- const json = this.toJSON(options);
2903
- if (this.hasChildren()) {
2904
- if ("subgraph" in json) {
2905
- delete json.subgraph;
2906
- }
2907
- return { ...json, subtasks: this.subGraph.toDependencyJSON(options) };
2908
- }
2909
- return json;
2910
- }
2911
- }
2912
-
2913
- // src/task-graph/Workflow.ts
2914
2
  import {
2915
- EventEmitter as EventEmitter4,
2916
- getLogger as getLogger4,
2917
- uuid4 as uuid44
2918
- } from "@workglow/util";
2919
- function CreateWorkflow(taskClass) {
2920
- return Workflow.createWorkflow(taskClass);
2921
- }
2922
- function CreateLoopWorkflow(taskClass) {
2923
- return function(config = {}) {
2924
- return this.addLoopTask(taskClass, config);
2925
- };
2926
- }
2927
- function CreateEndLoopWorkflow(methodName) {
2928
- return function() {
2929
- if (!this.isLoopBuilder) {
2930
- throw new Error(`${methodName}() can only be called on loop workflows`);
2931
- }
2932
- return this.finalizeAndReturn();
2933
- };
2934
- }
2935
- var TYPED_ARRAY_FORMAT_PREFIX = "TypedArray";
2936
- function schemaHasTypedArrayFormat(schema) {
2937
- if (typeof schema === "boolean")
2938
- return false;
2939
- if (!schema || typeof schema !== "object" || Array.isArray(schema))
2940
- return false;
2941
- const s = schema;
2942
- if (typeof s.format === "string" && s.format.startsWith(TYPED_ARRAY_FORMAT_PREFIX)) {
2943
- return true;
2944
- }
2945
- const checkUnion = (schemas) => {
2946
- if (!Array.isArray(schemas))
2947
- return false;
2948
- return schemas.some((sub) => schemaHasTypedArrayFormat(sub));
2949
- };
2950
- if (checkUnion(s.oneOf) || checkUnion(s.anyOf))
2951
- return true;
2952
- const items = s.items;
2953
- if (items && typeof items === "object" && !Array.isArray(items)) {
2954
- if (schemaHasTypedArrayFormat(items))
2955
- return true;
2956
- }
2957
- return false;
2958
- }
2959
- function hasVectorOutput(task) {
2960
- const outputSchema = task.outputSchema();
2961
- if (typeof outputSchema === "boolean" || !outputSchema?.properties)
2962
- return false;
2963
- return Object.values(outputSchema.properties).some((prop) => schemaHasTypedArrayFormat(prop));
2964
- }
2965
- function hasVectorLikeInput(input) {
2966
- if (!input || typeof input !== "object")
2967
- return false;
2968
- const v = input.vectors;
2969
- return Array.isArray(v) && v.length > 0 && typeof v[0] === "object" && v[0] !== null && ArrayBuffer.isView(v[0]);
2970
- }
2971
- function CreateAdaptiveWorkflow(scalarClass, vectorClass) {
2972
- const scalarHelper = Workflow.createWorkflow(scalarClass);
2973
- const vectorHelper = Workflow.createWorkflow(vectorClass);
2974
- return function(input = {}, config = {}) {
2975
- const parent = getLastTask(this);
2976
- const useVector = parent !== undefined && hasVectorOutput(parent) || hasVectorLikeInput(input);
2977
- if (useVector) {
2978
- return vectorHelper.call(this, input, config);
2979
- }
2980
- return scalarHelper.call(this, input, config);
2981
- };
2982
- }
2983
-
2984
- class WorkflowTask extends GraphAsTask {
2985
- static type = "Workflow";
2986
- static compoundMerge = PROPERTY_ARRAY;
2987
- }
2988
-
2989
- class Workflow {
2990
- constructor(cache, parent, iteratorTask) {
2991
- this._outputCache = cache;
2992
- this._parentWorkflow = parent;
2993
- this._iteratorTask = iteratorTask;
2994
- this._graph = new TaskGraph({ outputCache: this._outputCache });
2995
- if (!parent) {
2996
- this._onChanged = this._onChanged.bind(this);
2997
- this.setupEvents();
2998
- }
2999
- }
3000
- _graph;
3001
- _dataFlows = [];
3002
- _error = "";
3003
- _outputCache;
3004
- _abortController;
3005
- _parentWorkflow;
3006
- _iteratorTask;
3007
- _pendingLoopConnect;
3008
- outputCache() {
3009
- return this._outputCache;
3010
- }
3011
- get isLoopBuilder() {
3012
- return this._parentWorkflow !== undefined;
3013
- }
3014
- events = new EventEmitter4;
3015
- static createWorkflow(taskClass) {
3016
- const helper = function(input = {}, config = {}) {
3017
- this._error = "";
3018
- const parent = getLastTask(this);
3019
- const task = this.addTaskToGraph(taskClass, input, { id: uuid44(), ...config });
3020
- if (this._dataFlows.length > 0) {
3021
- this._dataFlows.forEach((dataflow) => {
3022
- const taskSchema = task.inputSchema();
3023
- if (typeof taskSchema !== "boolean" && taskSchema.properties?.[dataflow.targetTaskPortId] === undefined && taskSchema.additionalProperties !== true || taskSchema === true && dataflow.targetTaskPortId !== DATAFLOW_ALL_PORTS) {
3024
- this._error = `Input ${dataflow.targetTaskPortId} not found on task ${task.id}`;
3025
- getLogger4().error(this._error);
3026
- return;
3027
- }
3028
- dataflow.targetTaskId = task.id;
3029
- this.graph.addDataflow(dataflow);
3030
- });
3031
- this._dataFlows = [];
3032
- }
3033
- if (parent) {
3034
- const nodes = this._graph.getTasks();
3035
- const parentIndex = nodes.findIndex((n) => n.id === parent.id);
3036
- const earlierTasks = [];
3037
- for (let i = parentIndex - 1;i >= 0; i--) {
3038
- earlierTasks.push(nodes[i]);
3039
- }
3040
- const providedInputKeys = new Set(Object.keys(input || {}));
3041
- const connectedInputKeys = new Set(this.graph.getSourceDataflows(task.id).map((df) => df.targetTaskPortId));
3042
- const result = Workflow.autoConnect(this.graph, parent, task, {
3043
- providedInputKeys,
3044
- connectedInputKeys,
3045
- earlierTasks
3046
- });
3047
- if (result.error) {
3048
- if (this.isLoopBuilder) {
3049
- this._error = result.error;
3050
- getLogger4().warn(this._error);
3051
- } else {
3052
- this._error = result.error + " Task not added.";
3053
- getLogger4().error(this._error);
3054
- this.graph.removeTask(task.id);
3055
- }
3056
- }
3057
- }
3058
- if (!this._error) {
3059
- Workflow.updateBoundaryTaskSchemas(this._graph);
3060
- }
3061
- return this;
3062
- };
3063
- helper.type = taskClass.runtype ?? taskClass.type;
3064
- helper.category = taskClass.category;
3065
- helper.inputSchema = taskClass.inputSchema;
3066
- helper.outputSchema = taskClass.outputSchema;
3067
- helper.cacheable = taskClass.cacheable;
3068
- helper.workflowCreate = true;
3069
- return helper;
3070
- }
3071
- get graph() {
3072
- return this._graph;
3073
- }
3074
- set graph(value) {
3075
- this._dataFlows = [];
3076
- this._error = "";
3077
- this.clearEvents();
3078
- this._graph = value;
3079
- this.setupEvents();
3080
- this.events.emit("reset");
3081
- }
3082
- get error() {
3083
- return this._error;
3084
- }
3085
- on(name, fn) {
3086
- this.events.on(name, fn);
3087
- }
3088
- off(name, fn) {
3089
- this.events.off(name, fn);
3090
- }
3091
- once(name, fn) {
3092
- this.events.once(name, fn);
3093
- }
3094
- waitOn(name) {
3095
- return this.events.waitOn(name);
3096
- }
3097
- async run(input = {}) {
3098
- if (this.isLoopBuilder) {
3099
- this.finalizeTemplate();
3100
- if (this._pendingLoopConnect) {
3101
- this._parentWorkflow.autoConnectLoopTask(this._pendingLoopConnect);
3102
- this._pendingLoopConnect = undefined;
3103
- }
3104
- return this._parentWorkflow.run(input);
3105
- }
3106
- this.events.emit("start");
3107
- this._abortController = new AbortController;
3108
- const unsubStreaming = this.graph.subscribeToTaskStreaming({
3109
- onStreamStart: (taskId) => this.events.emit("stream_start", taskId),
3110
- onStreamChunk: (taskId, event) => this.events.emit("stream_chunk", taskId, event),
3111
- onStreamEnd: (taskId, output) => this.events.emit("stream_end", taskId, output)
3112
- });
3
+ ConditionalTask,
4
+ CreateAdaptiveWorkflow,
5
+ CreateEndLoopWorkflow,
6
+ CreateLoopWorkflow,
7
+ CreateWorkflow,
8
+ DATAFLOW_ALL_PORTS,
9
+ DATAFLOW_ERROR_PORT,
10
+ Dataflow,
11
+ DataflowArrow,
12
+ EventDagToTaskGraphMapping,
13
+ EventTaskGraphToDagMapping,
14
+ GRAPH_RESULT_ARRAY,
15
+ GraphAsTask,
16
+ GraphAsTaskRunner,
17
+ JobTaskFailedError,
18
+ PROPERTY_ARRAY,
19
+ TASK_OUTPUT_REPOSITORY,
20
+ Task,
21
+ TaskAbortedError,
22
+ TaskConfigSchema,
23
+ TaskConfigurationError,
24
+ TaskError,
25
+ TaskFailedError,
26
+ TaskGraph,
27
+ TaskGraphRunner,
28
+ TaskInvalidInputError,
29
+ TaskJSONError,
30
+ TaskOutputRepository,
31
+ TaskStatus,
32
+ TaskTimeoutError,
33
+ Workflow,
34
+ WorkflowError,
35
+ __require,
36
+ addBoundaryNodesToDependencyJson,
37
+ addBoundaryNodesToGraphJson,
38
+ calculateNodeDepths,
39
+ computeGraphInputSchema,
40
+ computeGraphOutputSchema,
41
+ conditionalTaskConfigSchema,
42
+ connect,
43
+ edgeNeedsAccumulation,
44
+ ensureTask,
45
+ evaluateCondition,
46
+ getAppendPortId,
47
+ getLastTask,
48
+ getNestedValue,
49
+ getObjectPortId,
50
+ getOutputStreamMode,
51
+ getPortStreamMode,
52
+ getStreamingPorts,
53
+ getStructuredOutputSchemas,
54
+ graphAsTaskConfigSchema,
55
+ hasStructuredOutput,
56
+ hasVectorLikeInput,
57
+ hasVectorOutput,
58
+ init_ConditionUtils,
59
+ init_ConditionalTask,
60
+ init_Conversions,
61
+ init_Dataflow,
62
+ init_GraphAsTask,
63
+ init_GraphAsTaskRunner,
64
+ init_GraphSchemaUtils,
65
+ init_InputResolver,
66
+ init_StreamTypes,
67
+ init_Task,
68
+ init_TaskError,
69
+ init_TaskGraph,
70
+ init_TaskGraphEvents,
71
+ init_TaskGraphRunner,
72
+ init_TaskOutputRepository,
73
+ init_TaskTypes,
74
+ init_Workflow,
75
+ isTaskStreamable,
76
+ parallel,
77
+ pipe,
78
+ resolveSchemaInputs,
79
+ serialGraph
80
+ } from "./bun-tgs39x49.js";
81
+
82
+ // src/common.ts
83
+ init_Dataflow();
84
+ init_GraphSchemaUtils();
85
+ init_TaskGraph();
86
+ init_TaskGraphEvents();
87
+ init_TaskGraphRunner();
88
+ init_Conversions();
89
+
90
+ // src/task-graph/GraphToWorkflowCode.ts
91
+ init_Dataflow();
92
+ init_Workflow();
93
+ var methodNameCache;
94
+ function getMethodNameMap() {
95
+ if (methodNameCache)
96
+ return methodNameCache;
97
+ methodNameCache = new Map;
98
+ for (const key of Object.getOwnPropertyNames(Workflow.prototype)) {
3113
99
  try {
3114
- const output = await this.graph.run(input, {
3115
- parentSignal: this._abortController.signal,
3116
- outputCache: this._outputCache
3117
- });
3118
- const results = this.graph.mergeExecuteOutputsToRunOutput(output, PROPERTY_ARRAY);
3119
- this.events.emit("complete");
3120
- return results;
3121
- } catch (error) {
3122
- this.events.emit("error", String(error));
3123
- throw error;
3124
- } finally {
3125
- unsubStreaming();
3126
- this._abortController = undefined;
3127
- }
3128
- }
3129
- async abort() {
3130
- if (this._parentWorkflow) {
3131
- return this._parentWorkflow.abort();
3132
- }
3133
- this._abortController?.abort();
3134
- }
3135
- pop() {
3136
- this._error = "";
3137
- const nodes = this._graph.getTasks();
3138
- if (nodes.length === 0) {
3139
- this._error = "No tasks to remove";
3140
- getLogger4().error(this._error);
3141
- return this;
3142
- }
3143
- const lastNode = nodes[nodes.length - 1];
3144
- this._graph.removeTask(lastNode.id);
3145
- return this;
3146
- }
3147
- toJSON(options = { withBoundaryNodes: true }) {
3148
- return this._graph.toJSON(options);
3149
- }
3150
- toDependencyJSON(options = { withBoundaryNodes: true }) {
3151
- return this._graph.toDependencyJSON(options);
3152
- }
3153
- pipe(...args) {
3154
- return pipe(args, this);
3155
- }
3156
- static pipe(...args) {
3157
- return pipe(args, new Workflow);
3158
- }
3159
- parallel(args, mergeFn) {
3160
- return parallel(args, mergeFn ?? PROPERTY_ARRAY, this);
3161
- }
3162
- static parallel(args, mergeFn) {
3163
- return parallel(args, mergeFn ?? PROPERTY_ARRAY, new Workflow);
3164
- }
3165
- rename(source, target, index = -1) {
3166
- this._error = "";
3167
- const nodes = this._graph.getTasks();
3168
- if (-index > nodes.length) {
3169
- const errorMsg = `Back index greater than number of tasks`;
3170
- this._error = errorMsg;
3171
- getLogger4().error(this._error);
3172
- throw new WorkflowError(errorMsg);
3173
- }
3174
- const lastNode = nodes[nodes.length + index];
3175
- const outputSchema = lastNode.outputSchema();
3176
- if (typeof outputSchema === "boolean") {
3177
- if (outputSchema === false && source !== DATAFLOW_ALL_PORTS) {
3178
- const errorMsg = `Task ${lastNode.id} has schema 'false' and outputs nothing`;
3179
- this._error = errorMsg;
3180
- getLogger4().error(this._error);
3181
- throw new WorkflowError(errorMsg);
3182
- }
3183
- } else if (!outputSchema.properties?.[source] && source !== DATAFLOW_ALL_PORTS) {
3184
- const errorMsg = `Output ${source} not found on task ${lastNode.id}`;
3185
- this._error = errorMsg;
3186
- getLogger4().error(this._error);
3187
- throw new WorkflowError(errorMsg);
3188
- }
3189
- this._dataFlows.push(new Dataflow(lastNode.id, source, undefined, target));
3190
- return this;
3191
- }
3192
- onError(handler) {
3193
- this._error = "";
3194
- const parent = getLastTask(this);
3195
- if (!parent) {
3196
- this._error = "onError() requires a preceding task in the workflow";
3197
- getLogger4().error(this._error);
3198
- throw new WorkflowError(this._error);
3199
- }
3200
- const handlerTask = ensureTask(handler);
3201
- this.graph.addTask(handlerTask);
3202
- const dataflow = new Dataflow(parent.id, DATAFLOW_ERROR_PORT, handlerTask.id, DATAFLOW_ALL_PORTS);
3203
- this.graph.addDataflow(dataflow);
3204
- this.events.emit("changed", handlerTask.id);
3205
- return this;
3206
- }
3207
- toTaskGraph() {
3208
- return this._graph;
3209
- }
3210
- toTask() {
3211
- const task = new WorkflowTask;
3212
- task.subGraph = this.toTaskGraph();
3213
- return task;
3214
- }
3215
- reset() {
3216
- if (this._parentWorkflow) {
3217
- throw new WorkflowError("Cannot reset a loop workflow. Call reset() on the parent workflow.");
3218
- }
3219
- this.clearEvents();
3220
- this._graph = new TaskGraph({
3221
- outputCache: this._outputCache
3222
- });
3223
- this._dataFlows = [];
3224
- this._error = "";
3225
- this.setupEvents();
3226
- this.events.emit("changed", undefined);
3227
- this.events.emit("reset");
3228
- return this;
3229
- }
3230
- setupEvents() {
3231
- this._graph.on("task_added", this._onChanged);
3232
- this._graph.on("task_replaced", this._onChanged);
3233
- this._graph.on("task_removed", this._onChanged);
3234
- this._graph.on("dataflow_added", this._onChanged);
3235
- this._graph.on("dataflow_replaced", this._onChanged);
3236
- this._graph.on("dataflow_removed", this._onChanged);
3237
- }
3238
- clearEvents() {
3239
- this._graph.off("task_added", this._onChanged);
3240
- this._graph.off("task_replaced", this._onChanged);
3241
- this._graph.off("task_removed", this._onChanged);
3242
- this._graph.off("dataflow_added", this._onChanged);
3243
- this._graph.off("dataflow_replaced", this._onChanged);
3244
- this._graph.off("dataflow_removed", this._onChanged);
3245
- }
3246
- _onChanged(id) {
3247
- this.events.emit("changed", id);
3248
- }
3249
- connect(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId) {
3250
- const sourceTask = this.graph.getTask(sourceTaskId);
3251
- const targetTask = this.graph.getTask(targetTaskId);
3252
- if (!sourceTask || !targetTask) {
3253
- throw new WorkflowError("Source or target task not found");
3254
- }
3255
- const sourceSchema = sourceTask.outputSchema();
3256
- const targetSchema = targetTask.inputSchema();
3257
- if (typeof sourceSchema === "boolean") {
3258
- if (sourceSchema === false) {
3259
- throw new WorkflowError(`Source task has schema 'false' and outputs nothing`);
100
+ const val = Workflow.prototype[key];
101
+ if (val && val.workflowCreate && val.type) {
102
+ methodNameCache.set(val.type, key);
3260
103
  }
3261
- } else if (!sourceSchema.properties?.[sourceTaskPortId]) {
3262
- throw new WorkflowError(`Output ${sourceTaskPortId} not found on source task`);
3263
- }
3264
- if (typeof targetSchema === "boolean") {
3265
- if (targetSchema === false) {
3266
- throw new WorkflowError(`Target task has schema 'false' and accepts no inputs`);
3267
- }
3268
- if (targetSchema === true) {}
3269
- } else if (targetSchema.additionalProperties === true) {} else if (!targetSchema.properties?.[targetTaskPortId]) {
3270
- throw new WorkflowError(`Input ${targetTaskPortId} not found on target task`);
3271
- }
3272
- const dataflow = new Dataflow(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId);
3273
- this.graph.addDataflow(dataflow);
3274
- return this;
3275
- }
3276
- addTaskToGraph(taskClass, input, config) {
3277
- const task = new taskClass(input, config);
3278
- const id = this.graph.addTask(task);
3279
- this.events.emit("changed", id);
3280
- return task;
3281
- }
3282
- addTask(taskClass, input, config) {
3283
- const helper = Workflow.createWorkflow(taskClass);
3284
- return helper.call(this, input, config);
3285
- }
3286
- addLoopTask(taskClass, config = {}) {
3287
- this._error = "";
3288
- const parent = getLastTask(this);
3289
- const task = this.addTaskToGraph(taskClass, {}, { id: uuid44(), ...config });
3290
- if (this._dataFlows.length > 0) {
3291
- this._dataFlows.forEach((dataflow) => {
3292
- const taskSchema = task.inputSchema();
3293
- if (typeof taskSchema !== "boolean" && taskSchema.properties?.[dataflow.targetTaskPortId] === undefined && taskSchema.additionalProperties !== true || taskSchema === true && dataflow.targetTaskPortId !== DATAFLOW_ALL_PORTS) {
3294
- this._error = `Input ${dataflow.targetTaskPortId} not found on task ${task.id}`;
3295
- getLogger4().error(this._error);
3296
- return;
3297
- }
3298
- dataflow.targetTaskId = task.id;
3299
- this.graph.addDataflow(dataflow);
3300
- });
3301
- this._dataFlows = [];
3302
- }
3303
- const loopBuilder = new Workflow(this.outputCache(), this, task);
3304
- if (parent) {
3305
- loopBuilder._pendingLoopConnect = { parent, iteratorTask: task };
104
+ } catch {}
105
+ }
106
+ return methodNameCache;
107
+ }
108
+ var LOOP_TASK_TYPES = {
109
+ MapTask: { method: "map", endMethod: "endMap" },
110
+ ReduceTask: { method: "reduce", endMethod: "endReduce" },
111
+ WhileTask: { method: "while", endMethod: "endWhile" },
112
+ GraphAsTask: { method: "group", endMethod: "endGroup" }
113
+ };
114
+ function graphToWorkflowCode(graph, options = {}) {
115
+ const { variableName = "workflow", includeDeclaration = true, indent = " " } = options;
116
+ const lines = [];
117
+ if (includeDeclaration) {
118
+ lines.push(`const ${variableName} = new Workflow();`);
119
+ }
120
+ const tasks = graph.topologicallySortedNodes();
121
+ const dataflows = graph.getDataflows();
122
+ const incomingDataflows = new Map;
123
+ for (const df of dataflows) {
124
+ const list = incomingDataflows.get(df.targetTaskId) ?? [];
125
+ list.push(df);
126
+ incomingDataflows.set(df.targetTaskId, list);
127
+ }
128
+ const taskOrder = [];
129
+ generateTaskChain(tasks, incomingDataflows, taskOrder, variableName, indent, 0, lines);
130
+ return lines.join(`
131
+ `);
132
+ }
133
+ function generateTaskChain(tasks, incomingDataflows, taskOrder, variableName, indent, depth, lines) {
134
+ if (tasks.length === 0)
135
+ return;
136
+ const prefix = indent.repeat(depth);
137
+ const chainIndent = indent.repeat(depth + 1);
138
+ const chainLines = [];
139
+ for (let i = 0;i < tasks.length; i++) {
140
+ const task = tasks[i];
141
+ const loopInfo = LOOP_TASK_TYPES[task.type];
142
+ const renames = computeRenames(task, incomingDataflows, taskOrder);
143
+ for (const rename of renames) {
144
+ chainLines.push(`${chainIndent}.rename(${formatValue(rename.source)}, ${formatValue(rename.target)})`);
145
+ }
146
+ if (loopInfo) {
147
+ generateLoopTask(task, loopInfo, incomingDataflows, taskOrder, indent, depth, chainLines);
148
+ } else {
149
+ generateRegularTask(task, chainIndent, chainLines);
3306
150
  }
3307
- return loopBuilder;
151
+ taskOrder.push(task.id);
3308
152
  }
3309
- autoConnectLoopTask(pending) {
3310
- if (!pending)
153
+ if (chainLines.length === 1 && !chainLines[0].includes(`
154
+ `)) {
155
+ const call = chainLines[0].trimStart();
156
+ const oneLine = `${prefix}${variableName}${call}`;
157
+ if (oneLine.length < 80) {
158
+ lines.push(`${oneLine};`);
3311
159
  return;
3312
- const { parent, iteratorTask } = pending;
3313
- if (this.graph.getTargetDataflows(parent.id).length === 0) {
3314
- const nodes = this._graph.getTasks();
3315
- const parentIndex = nodes.findIndex((n) => n.id === parent.id);
3316
- const earlierTasks = [];
3317
- for (let i = parentIndex - 1;i >= 0; i--) {
3318
- earlierTasks.push(nodes[i]);
3319
- }
3320
- const result = Workflow.autoConnect(this.graph, parent, iteratorTask, {
3321
- earlierTasks
3322
- });
3323
- if (result.error) {
3324
- this._error = result.error + " Task not added.";
3325
- getLogger4().error(this._error);
3326
- this.graph.removeTask(iteratorTask.id);
3327
- }
3328
160
  }
3329
161
  }
3330
- static updateBoundaryTaskSchemas(graph) {
3331
- const tasks = graph.getTasks();
3332
- for (const task of tasks) {
3333
- if (task.type === "InputTask") {
3334
- const outgoing = graph.getTargetDataflows(task.id);
3335
- if (outgoing.length === 0)
3336
- continue;
3337
- const properties = {};
3338
- const required = [];
3339
- for (const df of outgoing) {
3340
- const targetTask = graph.getTask(df.targetTaskId);
3341
- if (!targetTask)
3342
- continue;
3343
- const targetSchema = targetTask.inputSchema();
3344
- if (typeof targetSchema === "boolean")
3345
- continue;
3346
- const prop = targetSchema.properties?.[df.targetTaskPortId];
3347
- if (prop && typeof prop !== "boolean") {
3348
- properties[df.sourceTaskPortId] = prop;
3349
- if (targetSchema.required?.includes(df.targetTaskPortId)) {
3350
- if (!required.includes(df.sourceTaskPortId)) {
3351
- required.push(df.sourceTaskPortId);
3352
- }
3353
- }
3354
- }
3355
- }
3356
- const schema = {
3357
- type: "object",
3358
- properties,
3359
- ...required.length > 0 ? { required } : {},
3360
- additionalProperties: false
3361
- };
3362
- task.config = {
3363
- ...task.config,
3364
- inputSchema: schema,
3365
- outputSchema: schema
3366
- };
3367
- }
3368
- if (task.type === "OutputTask") {
3369
- const incoming = graph.getSourceDataflows(task.id);
3370
- if (incoming.length === 0)
3371
- continue;
3372
- const properties = {};
3373
- const required = [];
3374
- for (const df of incoming) {
3375
- const sourceTask = graph.getTask(df.sourceTaskId);
3376
- if (!sourceTask)
3377
- continue;
3378
- const sourceSchema = sourceTask.outputSchema();
3379
- if (typeof sourceSchema === "boolean")
3380
- continue;
3381
- const prop = sourceSchema.properties?.[df.sourceTaskPortId];
3382
- if (prop && typeof prop !== "boolean") {
3383
- properties[df.targetTaskPortId] = prop;
3384
- if (sourceSchema.required?.includes(df.sourceTaskPortId) && !required.includes(df.targetTaskPortId)) {
3385
- required.push(df.targetTaskPortId);
3386
- }
3387
- }
3388
- }
3389
- const schema = {
3390
- type: "object",
3391
- properties,
3392
- ...required.length > 0 ? { required } : {},
3393
- additionalProperties: false
3394
- };
3395
- task.config = {
3396
- ...task.config,
3397
- inputSchema: schema,
3398
- outputSchema: schema
3399
- };
162
+ lines.push(`${prefix}${variableName}`);
163
+ for (const line of chainLines) {
164
+ lines.push(line);
165
+ }
166
+ lines[lines.length - 1] += ";";
167
+ }
168
+ function generateRegularTask(task, chainIndent, lines) {
169
+ const methodMap = getMethodNameMap();
170
+ const methodName = methodMap.get(task.type);
171
+ const defaults = task.defaults;
172
+ const config = extractTaskConfig(task);
173
+ if (methodName) {
174
+ const colOffset = chainIndent.length + `.${methodName}(`.length;
175
+ const args = buildMethodArgs(defaults, config, chainIndent, colOffset);
176
+ lines.push(`${chainIndent}.${methodName}(${args})`);
177
+ } else {
178
+ const colOffset = chainIndent.length + ".addTask(".length;
179
+ const args = buildAddTaskArgs(task.type, defaults, config, chainIndent, colOffset);
180
+ lines.push(`${chainIndent}.addTask(${args})`);
181
+ }
182
+ }
183
+ function generateLoopTask(task, loopInfo, incomingDataflows, taskOrder, indent, depth, lines) {
184
+ const chainIndent = indent.repeat(depth + 1);
185
+ const config = extractLoopConfig(task);
186
+ const loopColOffset = chainIndent.length + `.${loopInfo.method}(`.length;
187
+ const configStr = Object.keys(config).length > 0 ? formatValue(config, chainIndent, loopColOffset) : "";
188
+ lines.push(`${chainIndent}.${loopInfo.method}(${configStr})`);
189
+ if (task.hasChildren()) {
190
+ const subGraph = task.subGraph;
191
+ const innerTasks = subGraph.topologicallySortedNodes();
192
+ const innerDataflows = subGraph.getDataflows();
193
+ const innerIncoming = new Map;
194
+ for (const df of innerDataflows) {
195
+ const list = innerIncoming.get(df.targetTaskId) ?? [];
196
+ list.push(df);
197
+ innerIncoming.set(df.targetTaskId, list);
198
+ }
199
+ const innerOrder = [];
200
+ generateChainedInnerTasks(innerTasks, innerIncoming, innerOrder, indent, depth + 1, lines);
201
+ }
202
+ lines.push(`${chainIndent}.${loopInfo.endMethod}()`);
203
+ }
204
+ function generateChainedInnerTasks(tasks, incomingDataflows, taskOrder, indent, depth, lines) {
205
+ const innerPrefix = indent.repeat(depth + 1);
206
+ for (let i = 0;i < tasks.length; i++) {
207
+ const task = tasks[i];
208
+ const loopInfo = LOOP_TASK_TYPES[task.type];
209
+ const renames = computeRenames(task, incomingDataflows, taskOrder);
210
+ for (const rename of renames) {
211
+ lines.push(`${innerPrefix}.rename(${formatValue(rename.source)}, ${formatValue(rename.target)})`);
212
+ }
213
+ if (loopInfo) {
214
+ const config = extractLoopConfig(task);
215
+ const nestedColOffset = innerPrefix.length + `.${loopInfo.method}(`.length;
216
+ const configStr = Object.keys(config).length > 0 ? formatValue(config, innerPrefix, nestedColOffset) : "";
217
+ lines.push(`${innerPrefix}.${loopInfo.method}(${configStr})`);
218
+ if (task.hasChildren()) {
219
+ const subGraph = task.subGraph;
220
+ const innerTasks = subGraph.topologicallySortedNodes();
221
+ const innerDataflows = subGraph.getDataflows();
222
+ const innerIncoming = new Map;
223
+ for (const df of innerDataflows) {
224
+ const list = innerIncoming.get(df.targetTaskId) ?? [];
225
+ list.push(df);
226
+ innerIncoming.set(df.targetTaskId, list);
227
+ }
228
+ const innerOrder = [];
229
+ generateChainedInnerTasks(innerTasks, innerIncoming, innerOrder, indent, depth + 1, lines);
230
+ }
231
+ lines.push(`${innerPrefix}.${loopInfo.endMethod}()`);
232
+ } else {
233
+ const methodMap = getMethodNameMap();
234
+ const methodName = methodMap.get(task.type);
235
+ const defaults = task.defaults;
236
+ const config = extractTaskConfig(task);
237
+ if (methodName) {
238
+ const colOffset = innerPrefix.length + `.${methodName}(`.length;
239
+ const args = buildMethodArgs(defaults, config, innerPrefix, colOffset);
240
+ lines.push(`${innerPrefix}.${methodName}(${args})`);
241
+ } else {
242
+ const colOffset = innerPrefix.length + ".addTask(".length;
243
+ const args = buildAddTaskArgs(task.type, defaults, config, innerPrefix, colOffset);
244
+ lines.push(`${innerPrefix}.addTask(${args})`);
3400
245
  }
3401
246
  }
247
+ taskOrder.push(task.id);
3402
248
  }
3403
- static AutoConnectOptions = Symbol("AutoConnectOptions");
3404
- static autoConnect(graph, sourceTask, targetTask, options) {
3405
- const matches = new Map;
3406
- const sourceSchema = sourceTask.outputSchema();
3407
- const targetSchema = targetTask.inputSchema();
3408
- const providedInputKeys = options?.providedInputKeys ?? new Set;
3409
- const connectedInputKeys = options?.connectedInputKeys ?? new Set;
3410
- const earlierTasks = options?.earlierTasks ?? [];
3411
- const getSpecificTypeIdentifiers = (schema) => {
3412
- const formats = new Set;
3413
- const ids = new Set;
3414
- if (typeof schema === "boolean") {
3415
- return { formats, ids };
3416
- }
3417
- const extractFromSchema = (s) => {
3418
- if (!s || typeof s !== "object" || Array.isArray(s))
3419
- return;
3420
- if (s.format)
3421
- formats.add(s.format);
3422
- if (s.$id)
3423
- ids.add(s.$id);
3424
- };
3425
- extractFromSchema(schema);
3426
- const checkUnion = (schemas) => {
3427
- if (!schemas)
3428
- return;
3429
- for (const s of schemas) {
3430
- if (typeof s === "boolean")
3431
- continue;
3432
- extractFromSchema(s);
3433
- if (s.items && typeof s.items === "object" && !Array.isArray(s.items)) {
3434
- extractFromSchema(s.items);
3435
- }
3436
- }
3437
- };
3438
- checkUnion(schema.oneOf);
3439
- checkUnion(schema.anyOf);
3440
- if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
3441
- extractFromSchema(schema.items);
3442
- }
3443
- return { formats, ids };
3444
- };
3445
- const isTypeCompatible = (fromPortOutputSchema, toPortInputSchema, requireSpecificType = false) => {
3446
- if (typeof fromPortOutputSchema === "boolean" || typeof toPortInputSchema === "boolean") {
3447
- return fromPortOutputSchema === true && toPortInputSchema === true;
3448
- }
3449
- const outputIds = getSpecificTypeIdentifiers(fromPortOutputSchema);
3450
- const inputIds = getSpecificTypeIdentifiers(toPortInputSchema);
3451
- for (const format of outputIds.formats) {
3452
- if (inputIds.formats.has(format)) {
3453
- return true;
3454
- }
3455
- }
3456
- for (const id of outputIds.ids) {
3457
- if (inputIds.ids.has(id)) {
3458
- return true;
3459
- }
3460
- }
3461
- if (requireSpecificType) {
3462
- return false;
3463
- }
3464
- const idTypeBlank = fromPortOutputSchema.$id === undefined && toPortInputSchema.$id === undefined;
3465
- if (!idTypeBlank)
3466
- return false;
3467
- if (fromPortOutputSchema.type === toPortInputSchema.type)
3468
- return true;
3469
- const matchesOneOf = toPortInputSchema.oneOf?.some((schema) => {
3470
- if (typeof schema === "boolean")
3471
- return schema;
3472
- return schema.type === fromPortOutputSchema.type;
3473
- }) ?? false;
3474
- const matchesAnyOf = toPortInputSchema.anyOf?.some((schema) => {
3475
- if (typeof schema === "boolean")
3476
- return schema;
3477
- return schema.type === fromPortOutputSchema.type;
3478
- }) ?? false;
3479
- return matchesOneOf || matchesAnyOf;
3480
- };
3481
- const makeMatch = (fromSchema, toSchema, fromTaskId, toTaskId, comparator) => {
3482
- if (typeof fromSchema === "object") {
3483
- if (toSchema === true || typeof toSchema === "object" && toSchema.additionalProperties === true) {
3484
- for (const fromOutputPortId of Object.keys(fromSchema.properties || {})) {
3485
- if (matches.has(fromOutputPortId))
3486
- continue;
3487
- matches.set(fromOutputPortId, fromOutputPortId);
3488
- graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, fromOutputPortId));
3489
- }
3490
- return;
3491
- }
3492
- }
3493
- if (typeof fromSchema === "object" && fromSchema.additionalProperties === true && typeof toSchema === "object" && (sourceTask.type === "InputTask" || sourceTask.type === "OutputTask")) {
3494
- for (const toInputPortId of Object.keys(toSchema.properties || {})) {
3495
- if (matches.has(toInputPortId))
3496
- continue;
3497
- if (connectedInputKeys.has(toInputPortId))
3498
- continue;
3499
- matches.set(toInputPortId, toInputPortId);
3500
- graph.addDataflow(new Dataflow(fromTaskId, toInputPortId, toTaskId, toInputPortId));
3501
- }
3502
- return;
3503
- }
3504
- if (typeof fromSchema === "boolean" || typeof toSchema === "boolean") {
3505
- return;
3506
- }
3507
- for (const [toInputPortId, toPortInputSchema] of Object.entries(toSchema.properties || {})) {
3508
- if (matches.has(toInputPortId))
3509
- continue;
3510
- if (connectedInputKeys.has(toInputPortId))
3511
- continue;
3512
- const candidates = [];
3513
- for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(fromSchema.properties || {})) {
3514
- if (comparator([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema])) {
3515
- candidates.push(fromOutputPortId);
3516
- }
3517
- }
3518
- if (candidates.length === 0)
3519
- continue;
3520
- let winner = candidates[0];
3521
- if (candidates.length > 1) {
3522
- const targetStreamMode = getPortStreamMode(toSchema, toInputPortId);
3523
- const streamMatch = candidates.find((portId) => getPortStreamMode(fromSchema, portId) === targetStreamMode);
3524
- if (streamMatch)
3525
- winner = streamMatch;
3526
- }
3527
- matches.set(toInputPortId, winner);
3528
- graph.addDataflow(new Dataflow(fromTaskId, winner, toTaskId, toInputPortId));
3529
- }
3530
- };
3531
- makeMatch(sourceSchema, targetSchema, sourceTask.id, targetTask.id, ([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
3532
- const outputPortIdMatch = fromOutputPortId === toInputPortId;
3533
- const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
3534
- const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
3535
- return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
3536
- });
3537
- makeMatch(sourceSchema, targetSchema, sourceTask.id, targetTask.id, ([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
3538
- return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
3539
- });
3540
- const requiredInputs = new Set(typeof targetSchema === "object" ? targetSchema.required || [] : []);
3541
- const requiredInputsNeedingConnection = [...requiredInputs].filter((r) => !providedInputKeys.has(r) && !connectedInputKeys.has(r));
3542
- let unmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
3543
- if (unmatchedRequired.length > 0 && earlierTasks.length > 0) {
3544
- for (let i = 0;i < earlierTasks.length && unmatchedRequired.length > 0; i++) {
3545
- const earlierTask = earlierTasks[i];
3546
- const earlierOutputSchema = earlierTask.outputSchema();
3547
- if (earlierTask.type === "InputTask") {
3548
- for (const requiredInputId of [...unmatchedRequired]) {
3549
- if (matches.has(requiredInputId))
3550
- continue;
3551
- matches.set(requiredInputId, requiredInputId);
3552
- graph.addDataflow(new Dataflow(earlierTask.id, requiredInputId, targetTask.id, requiredInputId));
3553
- }
3554
- unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
3555
- continue;
3556
- }
3557
- const makeMatchFromEarlier = (comparator) => {
3558
- if (typeof earlierOutputSchema === "boolean" || typeof targetSchema === "boolean") {
3559
- return;
3560
- }
3561
- for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(earlierOutputSchema.properties || {})) {
3562
- for (const requiredInputId of unmatchedRequired) {
3563
- const toPortInputSchema = targetSchema.properties?.[requiredInputId];
3564
- if (!matches.has(requiredInputId) && toPortInputSchema && comparator([fromOutputPortId, fromPortOutputSchema], [requiredInputId, toPortInputSchema])) {
3565
- matches.set(requiredInputId, fromOutputPortId);
3566
- graph.addDataflow(new Dataflow(earlierTask.id, fromOutputPortId, targetTask.id, requiredInputId));
3567
- }
3568
- }
3569
- }
3570
- };
3571
- makeMatchFromEarlier(([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
3572
- const outputPortIdMatch = fromOutputPortId === toInputPortId;
3573
- const outputPortIdOutputInput = fromOutputPortId === "output" && toInputPortId === "input";
3574
- const portIdsCompatible = outputPortIdMatch || outputPortIdOutputInput;
3575
- return portIdsCompatible && isTypeCompatible(fromPortOutputSchema, toPortInputSchema, false);
3576
- });
3577
- makeMatchFromEarlier(([_fromOutputPortId, fromPortOutputSchema], [_toInputPortId, toPortInputSchema]) => {
3578
- return isTypeCompatible(fromPortOutputSchema, toPortInputSchema, true);
3579
- });
3580
- unmatchedRequired = unmatchedRequired.filter((r) => !matches.has(r));
3581
- }
3582
- }
3583
- const stillUnmatchedRequired = requiredInputsNeedingConnection.filter((r) => !matches.has(r));
3584
- if (stillUnmatchedRequired.length > 0) {
3585
- return {
3586
- matches,
3587
- error: `Could not find matches for required inputs [${stillUnmatchedRequired.join(", ")}] of ${targetTask.type}. ` + `Attempted to match from ${sourceTask.type} and earlier tasks.`,
3588
- unmatchedRequired: stillUnmatchedRequired
3589
- };
3590
- }
3591
- if (matches.size === 0 && requiredInputsNeedingConnection.length === 0) {
3592
- const existingTargetConnections = graph.getSourceDataflows(targetTask.id);
3593
- if (existingTargetConnections.length > 0) {
3594
- return { matches, unmatchedRequired: [] };
3595
- }
3596
- const hasRequiredInputs = requiredInputs.size > 0;
3597
- const allRequiredInputsProvided = hasRequiredInputs && [...requiredInputs].every((r) => providedInputKeys.has(r));
3598
- const hasInputsWithDefaults = typeof targetSchema === "object" && targetSchema.properties && Object.values(targetSchema.properties).some((prop) => prop && typeof prop === "object" && ("default" in prop));
3599
- if (!allRequiredInputsProvided && !hasInputsWithDefaults) {
3600
- return {
3601
- matches,
3602
- error: `Could not find a match between the outputs of ${sourceTask.type} and the inputs of ${targetTask.type}. ` + `You may need to connect the outputs to the inputs via connect() manually.`,
3603
- unmatchedRequired: []
3604
- };
3605
- }
249
+ }
250
+ function computeRenames(task, incomingDataflows, taskOrder) {
251
+ const incoming = incomingDataflows.get(task.id) ?? [];
252
+ const renames = [];
253
+ const prevTaskId = taskOrder.length > 0 ? taskOrder[taskOrder.length - 1] : undefined;
254
+ for (const df of incoming) {
255
+ if (df.sourceTaskPortId === DATAFLOW_ALL_PORTS && df.targetTaskPortId === DATAFLOW_ALL_PORTS) {
256
+ continue;
3606
257
  }
3607
- return {
3608
- matches,
3609
- unmatchedRequired: []
3610
- };
3611
- }
3612
- finalizeTemplate() {
3613
- if (!this._iteratorTask || this.graph.getTasks().length === 0) {
3614
- return;
258
+ if (df.sourceTaskPortId === DATAFLOW_ERROR_PORT || df.targetTaskPortId === DATAFLOW_ERROR_PORT) {
259
+ continue;
3615
260
  }
3616
- this._iteratorTask.subGraph = this.graph;
3617
- }
3618
- finalizeAndReturn() {
3619
- if (!this._parentWorkflow) {
3620
- throw new WorkflowError("finalizeAndReturn() can only be called on loop workflows");
261
+ if (df.sourceTaskId !== prevTaskId) {
262
+ continue;
3621
263
  }
3622
- this.finalizeTemplate();
3623
- if (this._pendingLoopConnect) {
3624
- this._parentWorkflow.autoConnectLoopTask(this._pendingLoopConnect);
3625
- this._pendingLoopConnect = undefined;
264
+ if (df.sourceTaskPortId === df.targetTaskPortId) {
265
+ continue;
3626
266
  }
3627
- return this._parentWorkflow;
3628
- }
3629
- }
3630
-
3631
- // src/task-graph/Conversions.ts
3632
- class ListeningGraphAsTask extends GraphAsTask {
3633
- constructor(input, config) {
3634
- super(input, config);
3635
- this.subGraph.on("start", () => {
3636
- this.emit("start");
3637
- });
3638
- this.subGraph.on("complete", () => {
3639
- this.emit("complete");
267
+ renames.push({ source: df.sourceTaskPortId, target: df.targetTaskPortId });
268
+ }
269
+ return renames;
270
+ }
271
+ function extractTaskConfig(task) {
272
+ const config = {};
273
+ const rawConfig = task.config;
274
+ const staticTitle = task.constructor.title || "";
275
+ const staticDescription = task.constructor.description || "";
276
+ if (rawConfig.title && rawConfig.title !== staticTitle)
277
+ config.title = rawConfig.title;
278
+ if (rawConfig.description && rawConfig.description !== staticDescription)
279
+ config.description = rawConfig.description;
280
+ return config;
281
+ }
282
+ function extractLoopConfig(task) {
283
+ const config = {};
284
+ const rawConfig = task.config;
285
+ switch (task.type) {
286
+ case "GraphAsTask": {
287
+ if (rawConfig.compoundMerge !== undefined) {
288
+ config.compoundMerge = rawConfig.compoundMerge;
289
+ }
290
+ break;
291
+ }
292
+ case "MapTask": {
293
+ if (rawConfig.preserveOrder !== undefined && rawConfig.preserveOrder !== true) {
294
+ config.preserveOrder = rawConfig.preserveOrder;
295
+ }
296
+ if (rawConfig.flatten !== undefined && rawConfig.flatten !== false) {
297
+ config.flatten = rawConfig.flatten;
298
+ }
299
+ if (rawConfig.concurrencyLimit !== undefined) {
300
+ config.concurrencyLimit = rawConfig.concurrencyLimit;
301
+ }
302
+ if (rawConfig.batchSize !== undefined) {
303
+ config.batchSize = rawConfig.batchSize;
304
+ }
305
+ break;
306
+ }
307
+ case "ReduceTask": {
308
+ if (rawConfig.initialValue !== undefined) {
309
+ config.initialValue = rawConfig.initialValue;
310
+ }
311
+ break;
312
+ }
313
+ case "WhileTask": {
314
+ if (rawConfig.maxIterations !== undefined && rawConfig.maxIterations !== 100) {
315
+ config.maxIterations = rawConfig.maxIterations;
316
+ }
317
+ if (rawConfig.chainIterations !== undefined && rawConfig.chainIterations !== true) {
318
+ config.chainIterations = rawConfig.chainIterations;
319
+ }
320
+ if (rawConfig.conditionField !== undefined) {
321
+ config.conditionField = rawConfig.conditionField;
322
+ }
323
+ if (rawConfig.conditionOperator !== undefined) {
324
+ config.conditionOperator = rawConfig.conditionOperator;
325
+ }
326
+ if (rawConfig.conditionValue !== undefined) {
327
+ config.conditionValue = rawConfig.conditionValue;
328
+ }
329
+ if (rawConfig.condition && !rawConfig.conditionOperator) {
330
+ config.condition = null;
331
+ }
332
+ break;
333
+ }
334
+ }
335
+ return config;
336
+ }
337
+ function buildMethodArgs(defaults, config, baseIndent = "", columnOffset = 0) {
338
+ const hasDefaults = defaults && Object.keys(defaults).length > 0;
339
+ const hasConfig = Object.keys(config).length > 0;
340
+ if (!hasDefaults && !hasConfig)
341
+ return "";
342
+ if (hasDefaults && !hasConfig)
343
+ return formatValue(defaults, baseIndent, columnOffset);
344
+ if (!hasDefaults && hasConfig)
345
+ return `{}, ${formatValue(config, baseIndent, columnOffset + 4)}`;
346
+ const defaultsStr = formatValue(defaults, baseIndent, columnOffset);
347
+ return `${defaultsStr}, ${formatValue(config, baseIndent, columnOffset + defaultsStr.length + 2)}`;
348
+ }
349
+ function buildAddTaskArgs(taskType, defaults, config, baseIndent = "", columnOffset = 0) {
350
+ const hasDefaults = defaults && Object.keys(defaults).length > 0;
351
+ const hasConfig = Object.keys(config).length > 0;
352
+ const typeOffset = columnOffset + taskType.length + 2;
353
+ if (!hasDefaults && !hasConfig)
354
+ return taskType;
355
+ if (hasDefaults && !hasConfig)
356
+ return `${taskType}, ${formatValue(defaults, baseIndent, typeOffset)}`;
357
+ if (!hasDefaults && hasConfig)
358
+ return `${taskType}, {}, ${formatValue(config, baseIndent, typeOffset + 4)}`;
359
+ const defaultsStr = formatValue(defaults, baseIndent, typeOffset);
360
+ return `${taskType}, ${defaultsStr}, ${formatValue(config, baseIndent, typeOffset + defaultsStr.length + 2)}`;
361
+ }
362
+ function formatValue(value, baseIndent = "", columnOffset = 0) {
363
+ if (value === undefined)
364
+ return "undefined";
365
+ if (value === null)
366
+ return "null";
367
+ if (typeof value === "string") {
368
+ return JSON.stringify(value);
369
+ }
370
+ if (typeof value === "number" || typeof value === "boolean")
371
+ return String(value);
372
+ if (value instanceof Float32Array) {
373
+ return `new Float32Array([${Array.from(value).join(", ")}])`;
374
+ }
375
+ if (value instanceof Float64Array) {
376
+ return `new Float64Array([${Array.from(value).join(", ")}])`;
377
+ }
378
+ const entryIndent = baseIndent + " ";
379
+ if (Array.isArray(value)) {
380
+ if (value.length === 0)
381
+ return "[]";
382
+ const items = value.map((v) => formatValue(v, entryIndent));
383
+ const oneLine = `[${items.join(", ")}]`;
384
+ if (columnOffset + oneLine.length < 80)
385
+ return oneLine;
386
+ return `[
387
+ ${items.map((item) => `${entryIndent}${item}`).join(`,
388
+ `)}
389
+ ${baseIndent}]`;
390
+ }
391
+ if (typeof value === "object") {
392
+ const obj = value;
393
+ const keys = Object.keys(obj);
394
+ if (keys.length === 0)
395
+ return "{}";
396
+ const entries = keys.map((k) => {
397
+ const formattedKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
398
+ return `${formattedKey}: ${formatValue(obj[k], entryIndent)}`;
3640
399
  });
3641
- this.subGraph.on("error", (e) => {
3642
- this.emit("error", e);
3643
- });
3644
- }
3645
- }
3646
-
3647
- class OwnGraphTask extends ListeningGraphAsTask {
3648
- static type = "Own[Graph]";
3649
- }
3650
-
3651
- class OwnWorkflowTask extends ListeningGraphAsTask {
3652
- static type = "Own[Workflow]";
3653
- }
3654
-
3655
- class GraphTask extends GraphAsTask {
3656
- static type = "Graph";
3657
- }
3658
-
3659
- class WorkflowTask2 extends GraphAsTask {
3660
- static type = "Workflow";
3661
- }
3662
- function convertPipeFunctionToTask(fn, config) {
3663
-
3664
- class QuickTask extends Task {
3665
- static type = fn.name ? `\uD835\uDC53 ${fn.name}` : "\uD835\uDC53";
3666
- static inputSchema = () => {
3667
- return {
3668
- type: "object",
3669
- properties: {
3670
- [DATAFLOW_ALL_PORTS]: {}
3671
- },
3672
- additionalProperties: false
3673
- };
3674
- };
3675
- static outputSchema = () => {
3676
- return {
3677
- type: "object",
3678
- properties: {
3679
- [DATAFLOW_ALL_PORTS]: {}
3680
- },
3681
- additionalProperties: false
3682
- };
3683
- };
3684
- static cacheable = false;
3685
- async execute(input, context) {
3686
- return fn(input, context);
3687
- }
3688
- }
3689
- return new QuickTask({}, config);
3690
- }
3691
- function ensureTask(arg, config = {}) {
3692
- if (arg instanceof Task) {
3693
- return arg;
3694
- }
3695
- if (arg instanceof TaskGraph) {
3696
- const { isOwned, ...cleanConfig } = config;
3697
- if (isOwned) {
3698
- return new OwnGraphTask({}, { ...cleanConfig, subGraph: arg });
3699
- } else {
3700
- return new GraphTask({}, { ...cleanConfig, subGraph: arg });
3701
- }
3702
- }
3703
- if (arg instanceof Workflow) {
3704
- const { isOwned, ...cleanConfig } = config;
3705
- if (isOwned) {
3706
- return new OwnWorkflowTask({}, { ...cleanConfig, subGraph: arg.graph });
3707
- } else {
3708
- return new WorkflowTask2({}, { ...cleanConfig, subGraph: arg.graph });
3709
- }
400
+ const oneLine = `{ ${entries.join(", ")} }`;
401
+ if (columnOffset + oneLine.length < 80)
402
+ return oneLine;
403
+ return `{
404
+ ${entries.map((e) => `${entryIndent}${e}`).join(`,
405
+ `)}
406
+ ${baseIndent}}`;
3710
407
  }
3711
- return convertPipeFunctionToTask(arg, config);
3712
- }
3713
- function getLastTask(workflow) {
3714
- const tasks = workflow.graph.getTasks();
3715
- return tasks.length > 0 ? tasks[tasks.length - 1] : undefined;
3716
- }
3717
- function connect(source, target, workflow) {
3718
- workflow.graph.addDataflow(new Dataflow(source.id, "*", target.id, "*"));
408
+ return String(value);
3719
409
  }
3720
- function pipe(args, workflow = new Workflow) {
3721
- let previousTask = getLastTask(workflow);
3722
- const tasks = args.map((arg) => ensureTask(arg));
3723
- tasks.forEach((task) => {
3724
- workflow.graph.addTask(task);
3725
- if (previousTask) {
3726
- connect(previousTask, task, workflow);
3727
- }
3728
- previousTask = task;
3729
- });
3730
- return workflow;
410
+ function resetMethodNameCache() {
411
+ methodNameCache = undefined;
3731
412
  }
3732
- function parallel(args, mergeFn = PROPERTY_ARRAY, workflow = new Workflow) {
3733
- let previousTask = getLastTask(workflow);
3734
- const tasks = args.map((arg) => ensureTask(arg));
3735
- const input = {};
3736
- const config = {
3737
- compoundMerge: mergeFn
3738
- };
3739
- const name = `\u2016${args.map((arg) => "\uD835\uDC53").join("\u2016")}\u2016`;
3740
413
 
3741
- class ParallelTask extends GraphAsTask {
3742
- static type = name;
3743
- }
3744
- const mergeTask = new ParallelTask(input, config);
3745
- mergeTask.subGraph.addTasks(tasks);
3746
- workflow.graph.addTask(mergeTask);
3747
- if (previousTask) {
3748
- connect(previousTask, mergeTask, workflow);
3749
- }
3750
- return workflow;
3751
- }
414
+ // src/common.ts
415
+ init_Workflow();
3752
416
 
3753
- // src/task-graph/TaskGraphEvents.ts
3754
- var EventDagToTaskGraphMapping = {
3755
- "node-added": "task_added",
3756
- "node-removed": "task_removed",
3757
- "node-replaced": "task_replaced",
3758
- "edge-added": "dataflow_added",
3759
- "edge-removed": "dataflow_removed",
3760
- "edge-replaced": "dataflow_replaced"
3761
- };
3762
- var EventTaskGraphToDagMapping = {
3763
- task_added: "node-added",
3764
- task_removed: "node-removed",
3765
- task_replaced: "node-replaced",
3766
- dataflow_added: "edge-added",
3767
- dataflow_removed: "edge-removed",
3768
- dataflow_replaced: "edge-replaced"
3769
- };
417
+ // src/task/index.ts
418
+ init_ConditionalTask();
419
+ init_ConditionUtils();
3770
420
 
3771
- // src/task-graph/TaskGraph.ts
3772
- class TaskGraphDAG extends DirectedAcyclicGraph {
3773
- constructor() {
3774
- super((task) => task.id, (dataflow) => dataflow.id);
3775
- }
3776
- }
421
+ // src/task/FallbackTask.ts
422
+ init_Workflow();
423
+ init_GraphAsTask();
3777
424
 
3778
- class TaskGraph {
3779
- outputCache;
3780
- constructor({ outputCache, dag } = {}) {
3781
- this.outputCache = outputCache;
3782
- this._dag = dag || new TaskGraphDAG;
3783
- }
3784
- _dag;
3785
- _runner;
3786
- get runner() {
3787
- if (!this._runner) {
3788
- this._runner = new TaskGraphRunner(this, this.outputCache);
3789
- }
3790
- return this._runner;
3791
- }
3792
- run(input = {}, config = {}) {
3793
- return this.runner.runGraph(input, {
3794
- outputCache: config?.outputCache || this.outputCache,
3795
- parentSignal: config?.parentSignal || undefined,
3796
- accumulateLeafOutputs: config?.accumulateLeafOutputs,
3797
- registry: config?.registry
3798
- });
3799
- }
3800
- runReactive(input = {}, config = {}) {
3801
- return this.runner.runGraphReactive(input, config);
3802
- }
3803
- mergeExecuteOutputsToRunOutput(results, compoundMerge) {
3804
- return this.runner.mergeExecuteOutputsToRunOutput(results, compoundMerge);
3805
- }
3806
- abort() {
3807
- this.runner.abort();
3808
- }
3809
- async disable() {
3810
- await this.runner.disable();
3811
- }
3812
- getTask(id) {
3813
- return this._dag.getNode(id);
3814
- }
3815
- getTasks() {
3816
- return this._dag.getNodes();
3817
- }
3818
- topologicallySortedNodes() {
3819
- return this._dag.topologicallySortedNodes();
3820
- }
3821
- addTask(task, config) {
3822
- return this._dag.addNode(ensureTask(task, config));
3823
- }
3824
- addTasks(tasks) {
3825
- return this._dag.addNodes(tasks.map(ensureTask));
3826
- }
3827
- addDataflow(dataflow) {
3828
- return this._dag.addEdge(dataflow.sourceTaskId, dataflow.targetTaskId, dataflow);
3829
- }
3830
- addDataflows(dataflows) {
3831
- const addedEdges = dataflows.map((edge) => {
3832
- return [edge.sourceTaskId, edge.targetTaskId, edge];
3833
- });
3834
- return this._dag.addEdges(addedEdges);
3835
- }
3836
- getDataflow(id) {
3837
- for (const i in this._dag.adjacency) {
3838
- for (const j in this._dag.adjacency[i]) {
3839
- const maybeEdges = this._dag.adjacency[i][j];
3840
- if (maybeEdges !== null) {
3841
- for (const edge of maybeEdges) {
3842
- if (this._dag.edgeIdentity(edge, "", "") == id) {
3843
- return edge;
3844
- }
3845
- }
3846
- }
3847
- }
3848
- }
3849
- }
3850
- getDataflows() {
3851
- return this._dag.getEdges().map((edge) => edge[2]);
3852
- }
3853
- removeDataflow(dataflow) {
3854
- return this._dag.removeEdge(dataflow.sourceTaskId, dataflow.targetTaskId, dataflow.id);
3855
- }
3856
- getSourceDataflows(taskId) {
3857
- return this._dag.inEdges(taskId).map(([, , dataflow]) => dataflow);
3858
- }
3859
- getTargetDataflows(taskId) {
3860
- return this._dag.outEdges(taskId).map(([, , dataflow]) => dataflow);
3861
- }
3862
- getSourceTasks(taskId) {
3863
- return this.getSourceDataflows(taskId).map((dataflow) => this.getTask(dataflow.sourceTaskId));
3864
- }
3865
- getTargetTasks(taskId) {
3866
- return this.getTargetDataflows(taskId).map((dataflow) => this.getTask(dataflow.targetTaskId));
3867
- }
3868
- removeTask(taskId) {
3869
- return this._dag.removeNode(taskId);
3870
- }
3871
- resetGraph() {
3872
- this.runner.resetGraph(this, uuid45());
3873
- }
3874
- toJSON(options) {
3875
- const tasks = this.getTasks().map((node) => node.toJSON(options));
3876
- const dataflows = this.getDataflows().map((df) => df.toJSON());
3877
- let json = {
3878
- tasks,
3879
- dataflows
3880
- };
3881
- if (options?.withBoundaryNodes) {
3882
- json = addBoundaryNodesToGraphJson(json, this);
3883
- }
3884
- return json;
3885
- }
3886
- toDependencyJSON(options) {
3887
- const tasks = this.getTasks().flatMap((node) => node.toDependencyJSON(options));
3888
- this.getDataflows().forEach((df) => {
3889
- const target = tasks.find((node) => node.id === df.targetTaskId);
3890
- if (!target.dependencies) {
3891
- target.dependencies = {};
3892
- }
3893
- const targetDeps = target.dependencies[df.targetTaskPortId];
3894
- if (!targetDeps) {
3895
- target.dependencies[df.targetTaskPortId] = {
3896
- id: df.sourceTaskId,
3897
- output: df.sourceTaskPortId
3898
- };
3899
- } else {
3900
- if (Array.isArray(targetDeps)) {
3901
- targetDeps.push({
3902
- id: df.sourceTaskId,
3903
- output: df.sourceTaskPortId
3904
- });
3905
- } else {
3906
- target.dependencies[df.targetTaskPortId] = [
3907
- targetDeps,
3908
- { id: df.sourceTaskId, output: df.sourceTaskPortId }
3909
- ];
3910
- }
3911
- }
3912
- });
3913
- if (options?.withBoundaryNodes) {
3914
- return addBoundaryNodesToDependencyJson(tasks, this);
3915
- }
3916
- return tasks;
3917
- }
3918
- get events() {
3919
- if (!this._events) {
3920
- this._events = new EventEmitter5;
3921
- }
3922
- return this._events;
3923
- }
3924
- _events;
3925
- subscribe(name, fn) {
3926
- this.on(name, fn);
3927
- return () => this.off(name, fn);
3928
- }
3929
- subscribeToTaskStatus(callback) {
3930
- const unsubscribes = [];
3931
- const tasks = this.getTasks();
3932
- tasks.forEach((task) => {
3933
- const unsub = task.subscribe("status", (status) => {
3934
- callback(task.id, status);
3935
- });
3936
- unsubscribes.push(unsub);
3937
- });
3938
- const handleTaskAdded = (taskId) => {
3939
- const task = this.getTask(taskId);
3940
- if (!task || typeof task.subscribe !== "function")
3941
- return;
3942
- const unsub = task.subscribe("status", (status) => {
3943
- callback(task.id, status);
3944
- });
3945
- unsubscribes.push(unsub);
3946
- };
3947
- const graphUnsub = this.subscribe("task_added", handleTaskAdded);
3948
- unsubscribes.push(graphUnsub);
3949
- return () => {
3950
- unsubscribes.forEach((unsub) => unsub());
3951
- };
3952
- }
3953
- subscribeToTaskProgress(callback) {
3954
- const unsubscribes = [];
3955
- const tasks = this.getTasks();
3956
- tasks.forEach((task) => {
3957
- const unsub = task.subscribe("progress", (progress, message, ...args) => {
3958
- callback(task.id, progress, message, ...args);
3959
- });
3960
- unsubscribes.push(unsub);
3961
- });
3962
- const handleTaskAdded = (taskId) => {
3963
- const task = this.getTask(taskId);
3964
- if (!task || typeof task.subscribe !== "function")
3965
- return;
3966
- const unsub = task.subscribe("progress", (progress, message, ...args) => {
3967
- callback(task.id, progress, message, ...args);
3968
- });
3969
- unsubscribes.push(unsub);
3970
- };
3971
- const graphUnsub = this.subscribe("task_added", handleTaskAdded);
3972
- unsubscribes.push(graphUnsub);
3973
- return () => {
3974
- unsubscribes.forEach((unsub) => unsub());
3975
- };
3976
- }
3977
- subscribeToDataflowStatus(callback) {
3978
- const unsubscribes = [];
3979
- const dataflows = this.getDataflows();
3980
- dataflows.forEach((dataflow) => {
3981
- const unsub = dataflow.subscribe("status", (status) => {
3982
- callback(dataflow.id, status);
3983
- });
3984
- unsubscribes.push(unsub);
3985
- });
3986
- const handleDataflowAdded = (dataflowId) => {
3987
- const dataflow = this.getDataflow(dataflowId);
3988
- if (!dataflow || typeof dataflow.subscribe !== "function")
3989
- return;
3990
- const unsub = dataflow.subscribe("status", (status) => {
3991
- callback(dataflow.id, status);
3992
- });
3993
- unsubscribes.push(unsub);
3994
- };
3995
- const graphUnsub = this.subscribe("dataflow_added", handleDataflowAdded);
3996
- unsubscribes.push(graphUnsub);
3997
- return () => {
3998
- unsubscribes.forEach((unsub) => unsub());
3999
- };
4000
- }
4001
- subscribeToTaskStreaming(callbacks) {
4002
- const unsubscribes = [];
4003
- if (callbacks.onStreamStart) {
4004
- const unsub = this.subscribe("task_stream_start", callbacks.onStreamStart);
4005
- unsubscribes.push(unsub);
4006
- }
4007
- if (callbacks.onStreamChunk) {
4008
- const unsub = this.subscribe("task_stream_chunk", callbacks.onStreamChunk);
4009
- unsubscribes.push(unsub);
4010
- }
4011
- if (callbacks.onStreamEnd) {
4012
- const unsub = this.subscribe("task_stream_end", callbacks.onStreamEnd);
4013
- unsubscribes.push(unsub);
4014
- }
4015
- return () => {
4016
- unsubscribes.forEach((unsub) => unsub());
4017
- };
4018
- }
4019
- on(name, fn) {
4020
- const dagEvent = EventTaskGraphToDagMapping[name];
4021
- if (dagEvent) {
4022
- return this._dag.on(dagEvent, fn);
4023
- }
4024
- return this.events.on(name, fn);
4025
- }
4026
- off(name, fn) {
4027
- const dagEvent = EventTaskGraphToDagMapping[name];
4028
- if (dagEvent) {
4029
- return this._dag.off(dagEvent, fn);
4030
- }
4031
- return this.events.off(name, fn);
4032
- }
4033
- emit(name, ...args) {
4034
- const dagEvent = EventTaskGraphToDagMapping[name];
4035
- if (dagEvent) {
4036
- return this.emit_dag(name, ...args);
4037
- } else {
4038
- return this.emit_local(name, ...args);
4039
- }
4040
- }
4041
- emit_local(name, ...args) {
4042
- return this.events?.emit(name, ...args);
4043
- }
4044
- emit_dag(name, ...args) {
4045
- const dagEvent = EventTaskGraphToDagMapping[name];
4046
- return this._dag.emit(dagEvent, ...args);
4047
- }
4048
- }
4049
- function serialGraphEdges(tasks, inputHandle, outputHandle) {
4050
- const edges = [];
4051
- for (let i = 0;i < tasks.length - 1; i++) {
4052
- edges.push(new Dataflow(tasks[i].id, inputHandle, tasks[i + 1].id, outputHandle));
4053
- }
4054
- return edges;
4055
- }
4056
- function serialGraph(tasks, inputHandle, outputHandle) {
4057
- const graph = new TaskGraph;
4058
- graph.addTasks(tasks);
4059
- graph.addDataflows(serialGraphEdges(tasks, inputHandle, outputHandle));
4060
- return graph;
4061
- }
4062
425
  // src/task/FallbackTaskRunner.ts
426
+ init_GraphAsTaskRunner();
427
+ init_TaskError();
428
+ init_TaskTypes();
429
+
4063
430
  class FallbackTaskRunner extends GraphAsTaskRunner {
4064
431
  async executeTask(input) {
4065
432
  if (this.task.fallbackMode === "data") {
@@ -4257,7 +624,21 @@ queueMicrotask(() => {
4257
624
  };
4258
625
  Workflow.prototype.endFallbackWith = CreateEndLoopWorkflow("endFallbackWith");
4259
626
  });
627
+
628
+ // src/task/index.ts
629
+ init_GraphAsTask();
630
+ init_GraphAsTaskRunner();
631
+ init_InputResolver();
632
+
633
+ // src/task/IteratorTask.ts
634
+ init_GraphAsTask();
635
+
4260
636
  // src/task/IteratorTaskRunner.ts
637
+ init_Dataflow();
638
+ init_TaskGraph();
639
+ init_GraphAsTaskRunner();
640
+ import { uuid4 } from "@workglow/util";
641
+
4261
642
  class IteratorTaskRunner extends GraphAsTaskRunner {
4262
643
  async executeTask(input) {
4263
644
  const analysis = this.task.analyzeIterationInput(input);
@@ -4348,16 +729,20 @@ class IteratorTaskRunner extends GraphAsTaskRunner {
4348
729
  }
4349
730
  cloneGraph(graph) {
4350
731
  const clone = new TaskGraph;
732
+ const idMap = new Map;
4351
733
  for (const task of graph.getTasks()) {
4352
734
  const ctor = task.constructor;
4353
- const newTask = new ctor(task.defaults, task.config, task.runConfig);
735
+ const newId = uuid4();
736
+ idMap.set(task.config.id, newId);
737
+ const clonedConfig = { ...task.config, id: newId };
738
+ const newTask = new ctor(task.defaults, clonedConfig, task.runConfig);
4354
739
  if (task.hasChildren()) {
4355
740
  newTask.subGraph = this.cloneGraph(task.subGraph);
4356
741
  }
4357
742
  clone.addTask(newTask);
4358
743
  }
4359
744
  for (const df of graph.getDataflows()) {
4360
- clone.addDataflow(new Dataflow(df.sourceTaskId, df.sourceTaskPortId, df.targetTaskId, df.targetTaskPortId));
745
+ clone.addDataflow(new Dataflow(idMap.get(df.sourceTaskId) ?? df.sourceTaskId, df.sourceTaskPortId, idMap.get(df.targetTaskId) ?? df.targetTaskId, df.targetTaskPortId));
4361
746
  }
4362
747
  return clone;
4363
748
  }
@@ -4378,6 +763,7 @@ class IteratorTaskRunner extends GraphAsTaskRunner {
4378
763
  }
4379
764
 
4380
765
  // src/task/IteratorTask.ts
766
+ init_TaskError();
4381
767
  var ITERATOR_CONTEXT_SCHEMA = {
4382
768
  type: "object",
4383
769
  properties: {
@@ -4869,7 +1255,15 @@ class IteratorTask extends GraphAsTask {
4869
1255
  }
4870
1256
  }
4871
1257
 
1258
+ // src/task/WhileTask.ts
1259
+ init_Workflow();
1260
+ init_ConditionUtils();
1261
+ init_GraphAsTask();
1262
+ init_TaskError();
1263
+
4872
1264
  // src/task/WhileTaskRunner.ts
1265
+ init_GraphAsTaskRunner();
1266
+
4873
1267
  class WhileTaskRunner extends GraphAsTaskRunner {
4874
1268
  async executeTask(input) {
4875
1269
  const result = await this.task.execute(input, {
@@ -5425,8 +1819,8 @@ import {
5425
1819
  JobQueueServer
5426
1820
  } from "@workglow/job-queue";
5427
1821
  import { InMemoryQueueStorage } from "@workglow/storage";
5428
- import { createServiceToken as createServiceToken2, globalServiceRegistry as globalServiceRegistry3 } from "@workglow/util";
5429
- var JOB_QUEUE_FACTORY = createServiceToken2("taskgraph.jobQueueFactory");
1822
+ import { createServiceToken, globalServiceRegistry } from "@workglow/util";
1823
+ var JOB_QUEUE_FACTORY = createServiceToken("taskgraph.jobQueueFactory");
5430
1824
  var defaultJobQueueFactory = async ({
5431
1825
  queueName,
5432
1826
  jobClass,
@@ -5453,7 +1847,7 @@ var defaultJobQueueFactory = async ({
5453
1847
  return { server, client, storage };
5454
1848
  };
5455
1849
  function registerJobQueueFactory(factory) {
5456
- globalServiceRegistry3.registerInstance(JOB_QUEUE_FACTORY, factory);
1850
+ globalServiceRegistry.registerInstance(JOB_QUEUE_FACTORY, factory);
5457
1851
  }
5458
1852
  function createJobQueueFactoryWithOptions(defaultOptions = {}) {
5459
1853
  return async ({
@@ -5487,16 +1881,18 @@ function createJobQueueFactoryWithOptions(defaultOptions = {}) {
5487
1881
  };
5488
1882
  }
5489
1883
  function getJobQueueFactory() {
5490
- if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
1884
+ if (!globalServiceRegistry.has(JOB_QUEUE_FACTORY)) {
5491
1885
  registerJobQueueFactory(defaultJobQueueFactory);
5492
1886
  }
5493
- return globalServiceRegistry3.get(JOB_QUEUE_FACTORY);
1887
+ return globalServiceRegistry.get(JOB_QUEUE_FACTORY);
5494
1888
  }
5495
- if (!globalServiceRegistry3.has(JOB_QUEUE_FACTORY)) {
1889
+ if (!globalServiceRegistry.has(JOB_QUEUE_FACTORY)) {
5496
1890
  registerJobQueueFactory(defaultJobQueueFactory);
5497
1891
  }
5498
1892
  // src/task/JobQueueTask.ts
1893
+ init_GraphAsTask();
5499
1894
  import { Job as Job2 } from "@workglow/job-queue";
1895
+ init_TaskError();
5500
1896
 
5501
1897
  // src/task/TaskQueueRegistry.ts
5502
1898
  var taskQueueRegistry = null;
@@ -5693,6 +2089,7 @@ class JobQueueTask extends GraphAsTask {
5693
2089
  }
5694
2090
  }
5695
2091
  // src/task/MapTask.ts
2092
+ init_TaskGraphRunner();
5696
2093
  var mapTaskConfigSchema = {
5697
2094
  type: "object",
5698
2095
  properties: {
@@ -5768,11 +2165,13 @@ class MapTask extends IteratorTask {
5768
2165
  return flattened;
5769
2166
  }
5770
2167
  }
5771
- queueMicrotask(() => {
5772
- Workflow.prototype.map = CreateLoopWorkflow(MapTask);
5773
- Workflow.prototype.endMap = CreateEndLoopWorkflow("endMap");
2168
+ queueMicrotask(async () => {
2169
+ const { CreateLoopWorkflow: CreateLoopWorkflow3, CreateEndLoopWorkflow: CreateEndLoopWorkflow2, Workflow: Workflow2 } = await import("./Workflow-wrs2y87g.js");
2170
+ Workflow2.prototype.map = CreateLoopWorkflow3(MapTask);
2171
+ Workflow2.prototype.endMap = CreateEndLoopWorkflow2("endMap");
5774
2172
  });
5775
2173
  // src/task/ReduceTask.ts
2174
+ init_Workflow();
5776
2175
  var reduceTaskConfigSchema = {
5777
2176
  type: "object",
5778
2177
  properties: {
@@ -5866,10 +2265,21 @@ queueMicrotask(() => {
5866
2265
  Workflow.prototype.reduce = CreateLoopWorkflow(ReduceTask);
5867
2266
  Workflow.prototype.endReduce = CreateEndLoopWorkflow("endReduce");
5868
2267
  });
2268
+
2269
+ // src/task/index.ts
2270
+ init_StreamTypes();
2271
+ init_Task();
2272
+ init_TaskError();
2273
+
2274
+ // src/task/TaskJSON.ts
2275
+ init_Dataflow();
2276
+ init_TaskGraph();
2277
+ init_TaskError();
2278
+
5869
2279
  // src/task/TaskRegistry.ts
5870
2280
  import {
5871
- createServiceToken as createServiceToken3,
5872
- globalServiceRegistry as globalServiceRegistry4,
2281
+ createServiceToken as createServiceToken2,
2282
+ globalServiceRegistry as globalServiceRegistry2,
5873
2283
  registerInputResolver
5874
2284
  } from "@workglow/util";
5875
2285
  var taskConstructors = new Map;
@@ -5881,15 +2291,15 @@ var TaskRegistry = {
5881
2291
  all: taskConstructors,
5882
2292
  registerTask
5883
2293
  };
5884
- var TASK_CONSTRUCTORS = createServiceToken3("task.constructors");
5885
- if (!globalServiceRegistry4.has(TASK_CONSTRUCTORS)) {
5886
- globalServiceRegistry4.register(TASK_CONSTRUCTORS, () => TaskRegistry.all, true);
2294
+ var TASK_CONSTRUCTORS = createServiceToken2("task.constructors");
2295
+ if (!globalServiceRegistry2.has(TASK_CONSTRUCTORS)) {
2296
+ globalServiceRegistry2.register(TASK_CONSTRUCTORS, () => TaskRegistry.all, true);
5887
2297
  }
5888
2298
  function getGlobalTaskConstructors() {
5889
- return globalServiceRegistry4.get(TASK_CONSTRUCTORS);
2299
+ return globalServiceRegistry2.get(TASK_CONSTRUCTORS);
5890
2300
  }
5891
2301
  function setGlobalTaskConstructors(map) {
5892
- globalServiceRegistry4.registerInstance(TASK_CONSTRUCTORS, map);
2302
+ globalServiceRegistry2.registerInstance(TASK_CONSTRUCTORS, map);
5893
2303
  }
5894
2304
  function getTaskConstructors(registry) {
5895
2305
  if (!registry)
@@ -5911,6 +2321,7 @@ function resolveTaskFromRegistry(id, _format, registry) {
5911
2321
  registerInputResolver("tasks", resolveTaskFromRegistry);
5912
2322
 
5913
2323
  // src/task/TaskJSON.ts
2324
+ init_GraphAsTask();
5914
2325
  var createSingleTaskFromJSON = (item, registry) => {
5915
2326
  if (!item.id)
5916
2327
  throw new TaskJSONError("Task id required");
@@ -5966,21 +2377,25 @@ var createGraphFromGraphJSON = (graphJsonObj, registry) => {
5966
2377
  }
5967
2378
  return subGraph;
5968
2379
  };
2380
+
5969
2381
  // src/task/index.ts
2382
+ init_ConditionalTask();
2383
+ init_TaskTypes();
2384
+ init_GraphAsTask();
5970
2385
  var registerBaseTasks = () => {
5971
2386
  const tasks = [GraphAsTask, ConditionalTask, FallbackTask, MapTask, WhileTask, ReduceTask];
5972
2387
  tasks.map(TaskRegistry.registerTask);
5973
2388
  return tasks;
5974
2389
  };
5975
2390
  // src/storage/TaskGraphRepository.ts
5976
- import { createServiceToken as createServiceToken4, EventEmitter as EventEmitter6 } from "@workglow/util";
5977
- var TASK_GRAPH_REPOSITORY = createServiceToken4("taskgraph.taskGraphRepository");
2391
+ import { createServiceToken as createServiceToken3, EventEmitter } from "@workglow/util";
2392
+ var TASK_GRAPH_REPOSITORY = createServiceToken3("taskgraph.taskGraphRepository");
5978
2393
 
5979
2394
  class TaskGraphRepository {
5980
2395
  type = "TaskGraphRepository";
5981
2396
  get events() {
5982
2397
  if (!this._events) {
5983
- this._events = new EventEmitter6;
2398
+ this._events = new EventEmitter;
5984
2399
  }
5985
2400
  return this._events;
5986
2401
  }
@@ -6048,7 +2463,12 @@ class TaskGraphTabularRepository extends TaskGraphRepository {
6048
2463
  return await this.tabularRepository.size();
6049
2464
  }
6050
2465
  }
2466
+
2467
+ // src/common.ts
2468
+ init_TaskOutputRepository();
2469
+
6051
2470
  // src/storage/TaskOutputTabularRepository.ts
2471
+ init_TaskOutputRepository();
6052
2472
  import { compress, decompress, makeFingerprint } from "@workglow/util";
6053
2473
  var TaskOutputSchema = {
6054
2474
  type: "object",
@@ -6138,6 +2558,7 @@ export {
6138
2558
  serialGraph,
6139
2559
  schemaAcceptsArray,
6140
2560
  resolveSchemaInputs,
2561
+ resetMethodNameCache,
6141
2562
  removeIterationProperties,
6142
2563
  registerJobQueueFactory,
6143
2564
  registerBaseTasks,
@@ -6155,6 +2576,7 @@ export {
6155
2576
  hasVectorOutput,
6156
2577
  hasVectorLikeInput,
6157
2578
  hasStructuredOutput,
2579
+ graphToWorkflowCode,
6158
2580
  graphAsTaskConfigSchema,
6159
2581
  getTaskQueueRegistry,
6160
2582
  getTaskConstructors,
@@ -6170,6 +2592,7 @@ export {
6170
2592
  getInputModeFromSchema,
6171
2593
  getGlobalTaskConstructors,
6172
2594
  getAppendPortId,
2595
+ formatValue,
6173
2596
  findArrayPorts,
6174
2597
  filterIterationProperties,
6175
2598
  fallbackTaskConfigSchema,
@@ -6251,4 +2674,4 @@ export {
6251
2674
  ConditionalTask
6252
2675
  };
6253
2676
 
6254
- //# debugId=4EA0BC88063B5C3D64756E2164756E21
2677
+ //# debugId=C456BB2ACC871E6964756E2164756E21