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