@workglow/task-graph 0.0.90 → 0.0.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.js +402 -12
- package/dist/browser.js.map +13 -12
- package/dist/bun.js +402 -12
- package/dist/bun.js.map +13 -12
- package/dist/node.js +402 -12
- package/dist/node.js.map +13 -12
- package/dist/task/ITask.d.ts +7 -0
- package/dist/task/ITask.d.ts.map +1 -1
- package/dist/task/StreamTypes.d.ts +96 -0
- package/dist/task/StreamTypes.d.ts.map +1 -0
- package/dist/task/TaskEvents.d.ts +7 -0
- package/dist/task/TaskEvents.d.ts.map +1 -1
- package/dist/task/TaskJSON.d.ts +3 -2
- package/dist/task/TaskJSON.d.ts.map +1 -1
- package/dist/task/TaskRunner.d.ts +7 -0
- package/dist/task/TaskRunner.d.ts.map +1 -1
- package/dist/task/TaskTypes.d.ts +4 -1
- package/dist/task/TaskTypes.d.ts.map +1 -1
- package/dist/task/index.d.ts +1 -0
- package/dist/task/index.d.ts.map +1 -1
- package/dist/task-graph/Dataflow.d.ts +34 -0
- package/dist/task-graph/Dataflow.d.ts.map +1 -1
- package/dist/task-graph/DataflowEvents.d.ts +2 -0
- package/dist/task-graph/DataflowEvents.d.ts.map +1 -1
- package/dist/task-graph/ITaskGraph.d.ts +6 -0
- package/dist/task-graph/ITaskGraph.d.ts.map +1 -1
- package/dist/task-graph/TaskGraph.d.ts +14 -0
- package/dist/task-graph/TaskGraph.d.ts.map +1 -1
- package/dist/task-graph/TaskGraphEvents.d.ts +7 -0
- package/dist/task-graph/TaskGraphEvents.d.ts.map +1 -1
- package/dist/task-graph/TaskGraphRunner.d.ts +25 -0
- package/dist/task-graph/TaskGraphRunner.d.ts.map +1 -1
- package/dist/task-graph/TaskGraphScheduler.d.ts +9 -0
- package/dist/task-graph/TaskGraphScheduler.d.ts.map +1 -1
- package/dist/task-graph/Workflow.d.ts +36 -1
- package/dist/task-graph/Workflow.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/bun.js
CHANGED
|
@@ -7,6 +7,7 @@ var TaskStatus = {
|
|
|
7
7
|
PENDING: "PENDING",
|
|
8
8
|
DISABLED: "DISABLED",
|
|
9
9
|
PROCESSING: "PROCESSING",
|
|
10
|
+
STREAMING: "STREAMING",
|
|
10
11
|
COMPLETED: "COMPLETED",
|
|
11
12
|
ABORTING: "ABORTING",
|
|
12
13
|
FAILED: "FAILED"
|
|
@@ -36,10 +37,62 @@ class Dataflow {
|
|
|
36
37
|
value = undefined;
|
|
37
38
|
status = TaskStatus.PENDING;
|
|
38
39
|
error;
|
|
40
|
+
stream = undefined;
|
|
41
|
+
setStream(stream) {
|
|
42
|
+
this.stream = stream;
|
|
43
|
+
}
|
|
44
|
+
getStream() {
|
|
45
|
+
return this.stream;
|
|
46
|
+
}
|
|
47
|
+
async awaitStreamValue() {
|
|
48
|
+
if (!this.stream)
|
|
49
|
+
return;
|
|
50
|
+
const reader = this.stream.getReader();
|
|
51
|
+
let accumulatedText = "";
|
|
52
|
+
let lastSnapshotData = undefined;
|
|
53
|
+
let finishData = undefined;
|
|
54
|
+
let hasTextDelta = false;
|
|
55
|
+
try {
|
|
56
|
+
while (true) {
|
|
57
|
+
const { done, value: event } = await reader.read();
|
|
58
|
+
if (done)
|
|
59
|
+
break;
|
|
60
|
+
switch (event.type) {
|
|
61
|
+
case "text-delta":
|
|
62
|
+
hasTextDelta = true;
|
|
63
|
+
accumulatedText += event.textDelta;
|
|
64
|
+
break;
|
|
65
|
+
case "snapshot":
|
|
66
|
+
lastSnapshotData = event.data;
|
|
67
|
+
break;
|
|
68
|
+
case "finish":
|
|
69
|
+
finishData = event.data;
|
|
70
|
+
break;
|
|
71
|
+
case "error":
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
reader.releaseLock();
|
|
77
|
+
this.stream = undefined;
|
|
78
|
+
}
|
|
79
|
+
if (lastSnapshotData !== undefined) {
|
|
80
|
+
this.setPortData(lastSnapshotData);
|
|
81
|
+
} else if (finishData && Object.keys(finishData).length > 0) {
|
|
82
|
+
this.setPortData(finishData);
|
|
83
|
+
} else if (hasTextDelta) {
|
|
84
|
+
if (this.sourceTaskPortId === DATAFLOW_ALL_PORTS) {
|
|
85
|
+
this.value = { text: accumulatedText };
|
|
86
|
+
} else {
|
|
87
|
+
this.value = accumulatedText;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
39
91
|
reset() {
|
|
40
92
|
this.status = TaskStatus.PENDING;
|
|
41
93
|
this.error = undefined;
|
|
42
94
|
this.value = undefined;
|
|
95
|
+
this.stream = undefined;
|
|
43
96
|
this.emit("reset");
|
|
44
97
|
this.emit("status", this.status);
|
|
45
98
|
}
|
|
@@ -51,6 +104,9 @@ class Dataflow {
|
|
|
51
104
|
case TaskStatus.PROCESSING:
|
|
52
105
|
this.emit("start");
|
|
53
106
|
break;
|
|
107
|
+
case TaskStatus.STREAMING:
|
|
108
|
+
this.emit("streaming");
|
|
109
|
+
break;
|
|
54
110
|
case TaskStatus.COMPLETED:
|
|
55
111
|
this.emit("complete");
|
|
56
112
|
break;
|
|
@@ -398,6 +454,49 @@ async function resolveSchemaInputs(input, schema, config) {
|
|
|
398
454
|
return resolved;
|
|
399
455
|
}
|
|
400
456
|
|
|
457
|
+
// src/task/StreamTypes.ts
|
|
458
|
+
function getPortStreamMode(schema, portId) {
|
|
459
|
+
if (typeof schema === "boolean")
|
|
460
|
+
return "none";
|
|
461
|
+
const prop = schema.properties?.[portId];
|
|
462
|
+
if (!prop || typeof prop === "boolean")
|
|
463
|
+
return "none";
|
|
464
|
+
const xStream = prop["x-stream"];
|
|
465
|
+
if (xStream === "append" || xStream === "replace")
|
|
466
|
+
return xStream;
|
|
467
|
+
return "none";
|
|
468
|
+
}
|
|
469
|
+
function getOutputStreamMode(outputSchema) {
|
|
470
|
+
if (typeof outputSchema === "boolean")
|
|
471
|
+
return "none";
|
|
472
|
+
const props = outputSchema.properties;
|
|
473
|
+
if (!props)
|
|
474
|
+
return "none";
|
|
475
|
+
let found = "none";
|
|
476
|
+
for (const prop of Object.values(props)) {
|
|
477
|
+
if (!prop || typeof prop === "boolean")
|
|
478
|
+
continue;
|
|
479
|
+
const xStream = prop["x-stream"];
|
|
480
|
+
if (xStream === "append")
|
|
481
|
+
return "append";
|
|
482
|
+
if (xStream === "replace")
|
|
483
|
+
found = "replace";
|
|
484
|
+
}
|
|
485
|
+
return found;
|
|
486
|
+
}
|
|
487
|
+
function isTaskStreamable(task) {
|
|
488
|
+
if (typeof task.executeStream !== "function")
|
|
489
|
+
return false;
|
|
490
|
+
return getOutputStreamMode(task.outputSchema()) !== "none";
|
|
491
|
+
}
|
|
492
|
+
function edgeNeedsAccumulation(sourceSchema, sourcePort, targetSchema, targetPort) {
|
|
493
|
+
const sourceMode = getPortStreamMode(sourceSchema, sourcePort);
|
|
494
|
+
if (sourceMode === "none")
|
|
495
|
+
return false;
|
|
496
|
+
const targetMode = getPortStreamMode(targetSchema, targetPort);
|
|
497
|
+
return sourceMode !== targetMode;
|
|
498
|
+
}
|
|
499
|
+
|
|
401
500
|
// src/task/TaskRunner.ts
|
|
402
501
|
class TaskRunner {
|
|
403
502
|
running = false;
|
|
@@ -427,14 +526,27 @@ class TaskRunner {
|
|
|
427
526
|
}
|
|
428
527
|
const inputs = this.task.runInputData;
|
|
429
528
|
let outputs;
|
|
529
|
+
const isStreamable = isTaskStreamable(this.task);
|
|
430
530
|
if (this.task.cacheable) {
|
|
431
531
|
outputs = await this.outputCache?.getOutput(this.task.type, inputs);
|
|
432
532
|
if (outputs) {
|
|
433
|
-
|
|
533
|
+
if (isStreamable) {
|
|
534
|
+
this.task.runOutputData = outputs;
|
|
535
|
+
this.task.emit("stream_start");
|
|
536
|
+
this.task.emit("stream_chunk", { type: "finish", data: outputs });
|
|
537
|
+
this.task.emit("stream_end", outputs);
|
|
538
|
+
this.task.runOutputData = await this.executeTaskReactive(inputs, outputs);
|
|
539
|
+
} else {
|
|
540
|
+
this.task.runOutputData = await this.executeTaskReactive(inputs, outputs);
|
|
541
|
+
}
|
|
434
542
|
}
|
|
435
543
|
}
|
|
436
544
|
if (!outputs) {
|
|
437
|
-
|
|
545
|
+
if (isStreamable) {
|
|
546
|
+
outputs = await this.executeStreamingTask(inputs);
|
|
547
|
+
} else {
|
|
548
|
+
outputs = await this.executeTask(inputs);
|
|
549
|
+
}
|
|
438
550
|
if (this.task.cacheable && outputs !== undefined) {
|
|
439
551
|
await this.outputCache?.saveOutput(this.task.type, inputs, outputs);
|
|
440
552
|
}
|
|
@@ -493,6 +605,64 @@ class TaskRunner {
|
|
|
493
605
|
const reactiveResult = await this.task.executeReactive(input, output, { own: this.own });
|
|
494
606
|
return Object.assign({}, output, reactiveResult ?? {});
|
|
495
607
|
}
|
|
608
|
+
async executeStreamingTask(input) {
|
|
609
|
+
const streamMode = getOutputStreamMode(this.task.outputSchema());
|
|
610
|
+
let accumulated = "";
|
|
611
|
+
let chunkCount = 0;
|
|
612
|
+
let finalOutput;
|
|
613
|
+
this.task.emit("stream_start");
|
|
614
|
+
const stream = this.task.executeStream(input, {
|
|
615
|
+
signal: this.abortController.signal,
|
|
616
|
+
updateProgress: this.handleProgress.bind(this),
|
|
617
|
+
own: this.own,
|
|
618
|
+
registry: this.registry
|
|
619
|
+
});
|
|
620
|
+
for await (const event of stream) {
|
|
621
|
+
chunkCount++;
|
|
622
|
+
if (chunkCount === 1) {
|
|
623
|
+
this.task.status = TaskStatus.STREAMING;
|
|
624
|
+
this.task.emit("status", this.task.status);
|
|
625
|
+
}
|
|
626
|
+
if (event.type === "snapshot") {
|
|
627
|
+
this.task.runOutputData = event.data;
|
|
628
|
+
}
|
|
629
|
+
this.task.emit("stream_chunk", event);
|
|
630
|
+
switch (event.type) {
|
|
631
|
+
case "text-delta": {
|
|
632
|
+
accumulated += event.textDelta;
|
|
633
|
+
const progress = Math.min(99, Math.round(100 * (1 - Math.exp(-0.02 * chunkCount))));
|
|
634
|
+
await this.handleProgress(progress);
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
case "snapshot": {
|
|
638
|
+
const progress = Math.min(99, Math.round(100 * (1 - Math.exp(-0.02 * chunkCount))));
|
|
639
|
+
await this.handleProgress(progress);
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
case "finish": {
|
|
643
|
+
if (streamMode === "append") {
|
|
644
|
+
const text = accumulated.length > 0 ? accumulated : event.data?.text ?? "";
|
|
645
|
+
finalOutput = { ...event.data || {}, text };
|
|
646
|
+
} else if (streamMode === "replace") {
|
|
647
|
+
finalOutput = event.data;
|
|
648
|
+
}
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
case "error": {
|
|
652
|
+
throw event.error;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (this.abortController?.signal.aborted) {
|
|
657
|
+
throw new TaskAbortedError("Task aborted during streaming");
|
|
658
|
+
}
|
|
659
|
+
if (finalOutput !== undefined) {
|
|
660
|
+
this.task.runOutputData = finalOutput;
|
|
661
|
+
}
|
|
662
|
+
this.task.emit("stream_end", this.task.runOutputData);
|
|
663
|
+
const reactiveResult = await this.executeTaskReactive(input, this.task.runOutputData || {});
|
|
664
|
+
return reactiveResult;
|
|
665
|
+
}
|
|
496
666
|
async handleStart(config = {}) {
|
|
497
667
|
if (this.task.status === TaskStatus.PROCESSING)
|
|
498
668
|
return;
|
|
@@ -1186,6 +1356,7 @@ class TopologicalScheduler {
|
|
|
1186
1356
|
}
|
|
1187
1357
|
}
|
|
1188
1358
|
onTaskCompleted(taskId) {}
|
|
1359
|
+
onTaskStreaming(taskId) {}
|
|
1189
1360
|
reset() {
|
|
1190
1361
|
this.sortedNodes = this.dag.topologicallySortedNodes();
|
|
1191
1362
|
this.currentIndex = 0;
|
|
@@ -1195,11 +1366,13 @@ class TopologicalScheduler {
|
|
|
1195
1366
|
class DependencyBasedScheduler {
|
|
1196
1367
|
dag;
|
|
1197
1368
|
completedTasks;
|
|
1369
|
+
streamingTasks;
|
|
1198
1370
|
pendingTasks;
|
|
1199
1371
|
nextResolver = null;
|
|
1200
1372
|
constructor(dag) {
|
|
1201
1373
|
this.dag = dag;
|
|
1202
1374
|
this.completedTasks = new Set;
|
|
1375
|
+
this.streamingTasks = new Set;
|
|
1203
1376
|
this.pendingTasks = new Set;
|
|
1204
1377
|
this.reset();
|
|
1205
1378
|
}
|
|
@@ -1214,8 +1387,23 @@ class DependencyBasedScheduler {
|
|
|
1214
1387
|
return false;
|
|
1215
1388
|
}
|
|
1216
1389
|
}
|
|
1217
|
-
const
|
|
1218
|
-
return
|
|
1390
|
+
const activeDataflows = sourceDataflows.filter((df) => df.status !== TaskStatus.DISABLED);
|
|
1391
|
+
return activeDataflows.every((df) => {
|
|
1392
|
+
const depId = df.sourceTaskId;
|
|
1393
|
+
if (this.completedTasks.has(depId))
|
|
1394
|
+
return true;
|
|
1395
|
+
if (this.streamingTasks.has(depId)) {
|
|
1396
|
+
const sourceTask = this.dag.getTask(depId);
|
|
1397
|
+
if (sourceTask) {
|
|
1398
|
+
const sourceMode = getPortStreamMode(sourceTask.outputSchema(), df.sourceTaskPortId);
|
|
1399
|
+
const targetMode = getPortStreamMode(task.inputSchema(), df.targetTaskPortId);
|
|
1400
|
+
if (sourceMode !== "none" && sourceMode === targetMode) {
|
|
1401
|
+
return true;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return false;
|
|
1406
|
+
});
|
|
1219
1407
|
}
|
|
1220
1408
|
async waitForNextTask() {
|
|
1221
1409
|
if (this.pendingTasks.size === 0)
|
|
@@ -1270,8 +1458,26 @@ class DependencyBasedScheduler {
|
|
|
1270
1458
|
}
|
|
1271
1459
|
}
|
|
1272
1460
|
}
|
|
1461
|
+
onTaskStreaming(taskId) {
|
|
1462
|
+
this.streamingTasks.add(taskId);
|
|
1463
|
+
for (const task of Array.from(this.pendingTasks)) {
|
|
1464
|
+
if (task.status === TaskStatus.DISABLED) {
|
|
1465
|
+
this.pendingTasks.delete(task);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (this.nextResolver) {
|
|
1469
|
+
const readyTask = Array.from(this.pendingTasks).find((task) => this.isTaskReady(task));
|
|
1470
|
+
if (readyTask) {
|
|
1471
|
+
this.pendingTasks.delete(readyTask);
|
|
1472
|
+
const resolver = this.nextResolver;
|
|
1473
|
+
this.nextResolver = null;
|
|
1474
|
+
resolver(readyTask);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1273
1478
|
reset() {
|
|
1274
1479
|
this.completedTasks.clear();
|
|
1480
|
+
this.streamingTasks.clear();
|
|
1275
1481
|
this.pendingTasks = new Set(this.dag.topologicallySortedNodes());
|
|
1276
1482
|
this.nextResolver = null;
|
|
1277
1483
|
}
|
|
@@ -1508,7 +1714,12 @@ class TaskGraphRunner {
|
|
|
1508
1714
|
}
|
|
1509
1715
|
}
|
|
1510
1716
|
async runTask(task, input) {
|
|
1717
|
+
const isStreamable = isTaskStreamable(task);
|
|
1718
|
+
await this.awaitStreamInputs(task);
|
|
1511
1719
|
this.copyInputFromEdgesToNode(task);
|
|
1720
|
+
if (isStreamable) {
|
|
1721
|
+
return this.runStreamingTask(task, input);
|
|
1722
|
+
}
|
|
1512
1723
|
const results = await task.runner.run(input, {
|
|
1513
1724
|
outputCache: this.outputCache,
|
|
1514
1725
|
updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
|
|
@@ -1521,6 +1732,93 @@ class TaskGraphRunner {
|
|
|
1521
1732
|
data: results
|
|
1522
1733
|
};
|
|
1523
1734
|
}
|
|
1735
|
+
async awaitStreamInputs(task) {
|
|
1736
|
+
const dataflows = this.graph.getSourceDataflows(task.config.id);
|
|
1737
|
+
const streamPromises = dataflows.filter((df) => df.stream !== undefined).map((df) => df.awaitStreamValue());
|
|
1738
|
+
if (streamPromises.length > 0) {
|
|
1739
|
+
await Promise.all(streamPromises);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
async runStreamingTask(task, input) {
|
|
1743
|
+
const streamMode = getOutputStreamMode(task.outputSchema());
|
|
1744
|
+
let streamingNotified = false;
|
|
1745
|
+
const onStatus = (status) => {
|
|
1746
|
+
if (status === TaskStatus.STREAMING && !streamingNotified) {
|
|
1747
|
+
streamingNotified = true;
|
|
1748
|
+
this.pushStatusFromNodeToEdges(this.graph, task, TaskStatus.STREAMING);
|
|
1749
|
+
this.pushStreamToEdges(task, streamMode);
|
|
1750
|
+
this.processScheduler.onTaskStreaming(task.config.id);
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1753
|
+
const onStreamStart = () => {
|
|
1754
|
+
this.graph.emit("task_stream_start", task.config.id);
|
|
1755
|
+
};
|
|
1756
|
+
const onStreamChunk = (event) => {
|
|
1757
|
+
this.graph.emit("task_stream_chunk", task.config.id, event);
|
|
1758
|
+
};
|
|
1759
|
+
const onStreamEnd = (output) => {
|
|
1760
|
+
this.graph.emit("task_stream_end", task.config.id, output);
|
|
1761
|
+
};
|
|
1762
|
+
task.on("status", onStatus);
|
|
1763
|
+
task.on("stream_start", onStreamStart);
|
|
1764
|
+
task.on("stream_chunk", onStreamChunk);
|
|
1765
|
+
task.on("stream_end", onStreamEnd);
|
|
1766
|
+
try {
|
|
1767
|
+
const results = await task.runner.run(input, {
|
|
1768
|
+
outputCache: this.outputCache,
|
|
1769
|
+
updateProgress: async (task2, progress, message, ...args) => await this.handleProgress(task2, progress, message, ...args),
|
|
1770
|
+
registry: this.registry
|
|
1771
|
+
});
|
|
1772
|
+
await this.pushOutputFromNodeToEdges(task, results);
|
|
1773
|
+
return {
|
|
1774
|
+
id: task.config.id,
|
|
1775
|
+
type: task.constructor.runtype || task.constructor.type,
|
|
1776
|
+
data: results
|
|
1777
|
+
};
|
|
1778
|
+
} finally {
|
|
1779
|
+
task.off("status", onStatus);
|
|
1780
|
+
task.off("stream_start", onStreamStart);
|
|
1781
|
+
task.off("stream_chunk", onStreamChunk);
|
|
1782
|
+
task.off("stream_end", onStreamEnd);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
pushStreamToEdges(task, streamMode) {
|
|
1786
|
+
const targetDataflows = this.graph.getTargetDataflows(task.config.id);
|
|
1787
|
+
if (targetDataflows.length === 0)
|
|
1788
|
+
return;
|
|
1789
|
+
const stream = new ReadableStream({
|
|
1790
|
+
start: (controller) => {
|
|
1791
|
+
const onChunk = (event) => {
|
|
1792
|
+
try {
|
|
1793
|
+
controller.enqueue(event);
|
|
1794
|
+
} catch {}
|
|
1795
|
+
};
|
|
1796
|
+
const onEnd = () => {
|
|
1797
|
+
try {
|
|
1798
|
+
controller.close();
|
|
1799
|
+
} catch {}
|
|
1800
|
+
task.off("stream_chunk", onChunk);
|
|
1801
|
+
task.off("stream_end", onEnd);
|
|
1802
|
+
};
|
|
1803
|
+
task.on("stream_chunk", onChunk);
|
|
1804
|
+
task.on("stream_end", onEnd);
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
if (targetDataflows.length === 1) {
|
|
1808
|
+
targetDataflows[0].setStream(stream);
|
|
1809
|
+
} else {
|
|
1810
|
+
let currentStream = stream;
|
|
1811
|
+
for (let i = 0;i < targetDataflows.length; i++) {
|
|
1812
|
+
if (i === targetDataflows.length - 1) {
|
|
1813
|
+
targetDataflows[i].setStream(currentStream);
|
|
1814
|
+
} else {
|
|
1815
|
+
const [s1, s2] = currentStream.tee();
|
|
1816
|
+
targetDataflows[i].setStream(s1);
|
|
1817
|
+
currentStream = s2;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1524
1822
|
resetTask(graph, task, runId) {
|
|
1525
1823
|
task.status = TaskStatus.PENDING;
|
|
1526
1824
|
task.resetInputData();
|
|
@@ -1604,7 +1902,7 @@ class TaskGraphRunner {
|
|
|
1604
1902
|
}
|
|
1605
1903
|
async handleError(error) {
|
|
1606
1904
|
await Promise.allSettled(this.graph.getTasks().map(async (task) => {
|
|
1607
|
-
if (task.status === TaskStatus.PROCESSING) {
|
|
1905
|
+
if (task.status === TaskStatus.PROCESSING || task.status === TaskStatus.STREAMING) {
|
|
1608
1906
|
task.abort();
|
|
1609
1907
|
}
|
|
1610
1908
|
}));
|
|
@@ -1616,7 +1914,7 @@ class TaskGraphRunner {
|
|
|
1616
1914
|
}
|
|
1617
1915
|
async handleAbort() {
|
|
1618
1916
|
this.graph.getTasks().map(async (task) => {
|
|
1619
|
-
if (task.status === TaskStatus.PROCESSING) {
|
|
1917
|
+
if (task.status === TaskStatus.PROCESSING || task.status === TaskStatus.STREAMING) {
|
|
1620
1918
|
task.abort();
|
|
1621
1919
|
}
|
|
1622
1920
|
});
|
|
@@ -1893,6 +2191,54 @@ function CreateEndLoopWorkflow(methodName) {
|
|
|
1893
2191
|
return this.finalizeAndReturn();
|
|
1894
2192
|
};
|
|
1895
2193
|
}
|
|
2194
|
+
var TYPED_ARRAY_FORMAT_PREFIX = "TypedArray";
|
|
2195
|
+
function schemaHasTypedArrayFormat(schema) {
|
|
2196
|
+
if (typeof schema === "boolean")
|
|
2197
|
+
return false;
|
|
2198
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
2199
|
+
return false;
|
|
2200
|
+
const s = schema;
|
|
2201
|
+
if (typeof s.format === "string" && s.format.startsWith(TYPED_ARRAY_FORMAT_PREFIX)) {
|
|
2202
|
+
return true;
|
|
2203
|
+
}
|
|
2204
|
+
const checkUnion = (schemas) => {
|
|
2205
|
+
if (!Array.isArray(schemas))
|
|
2206
|
+
return false;
|
|
2207
|
+
return schemas.some((sub) => schemaHasTypedArrayFormat(sub));
|
|
2208
|
+
};
|
|
2209
|
+
if (checkUnion(s.oneOf) || checkUnion(s.anyOf))
|
|
2210
|
+
return true;
|
|
2211
|
+
const items = s.items;
|
|
2212
|
+
if (items && typeof items === "object" && !Array.isArray(items)) {
|
|
2213
|
+
if (schemaHasTypedArrayFormat(items))
|
|
2214
|
+
return true;
|
|
2215
|
+
}
|
|
2216
|
+
return false;
|
|
2217
|
+
}
|
|
2218
|
+
function hasVectorOutput(task) {
|
|
2219
|
+
const outputSchema = task.outputSchema();
|
|
2220
|
+
if (typeof outputSchema === "boolean" || !outputSchema?.properties)
|
|
2221
|
+
return false;
|
|
2222
|
+
return Object.values(outputSchema.properties).some((prop) => schemaHasTypedArrayFormat(prop));
|
|
2223
|
+
}
|
|
2224
|
+
function hasVectorLikeInput(input) {
|
|
2225
|
+
if (!input || typeof input !== "object")
|
|
2226
|
+
return false;
|
|
2227
|
+
const v = input.vectors;
|
|
2228
|
+
return Array.isArray(v) && v.length > 0 && typeof v[0] === "object" && v[0] !== null && ArrayBuffer.isView(v[0]);
|
|
2229
|
+
}
|
|
2230
|
+
function CreateAdaptiveWorkflow(scalarClass, vectorClass) {
|
|
2231
|
+
const scalarHelper = Workflow.createWorkflow(scalarClass);
|
|
2232
|
+
const vectorHelper = Workflow.createWorkflow(vectorClass);
|
|
2233
|
+
return function(input = {}, config = {}) {
|
|
2234
|
+
const parent = getLastTask(this);
|
|
2235
|
+
const useVector = parent !== undefined && hasVectorOutput(parent) || hasVectorLikeInput(input);
|
|
2236
|
+
if (useVector) {
|
|
2237
|
+
return vectorHelper.call(this, input, config);
|
|
2238
|
+
}
|
|
2239
|
+
return scalarHelper.call(this, input, config);
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
1896
2242
|
|
|
1897
2243
|
class WorkflowTask extends GraphAsTask {
|
|
1898
2244
|
static type = "Workflow";
|
|
@@ -2013,6 +2359,11 @@ class Workflow {
|
|
|
2013
2359
|
}
|
|
2014
2360
|
this.events.emit("start");
|
|
2015
2361
|
this._abortController = new AbortController;
|
|
2362
|
+
const unsubStreaming = this.graph.subscribeToTaskStreaming({
|
|
2363
|
+
onStreamStart: (taskId) => this.events.emit("stream_start", taskId),
|
|
2364
|
+
onStreamChunk: (taskId, event) => this.events.emit("stream_chunk", taskId, event),
|
|
2365
|
+
onStreamEnd: (taskId, output) => this.events.emit("stream_end", taskId, output)
|
|
2366
|
+
});
|
|
2016
2367
|
try {
|
|
2017
2368
|
const output = await this.graph.run(input, {
|
|
2018
2369
|
parentSignal: this._abortController.signal,
|
|
@@ -2025,6 +2376,7 @@ class Workflow {
|
|
|
2025
2376
|
this.events.emit("error", String(error));
|
|
2026
2377
|
throw error;
|
|
2027
2378
|
} finally {
|
|
2379
|
+
unsubStreaming();
|
|
2028
2380
|
this._abortController = undefined;
|
|
2029
2381
|
}
|
|
2030
2382
|
}
|
|
@@ -2304,13 +2656,26 @@ class Workflow {
|
|
|
2304
2656
|
if (typeof fromSchema === "boolean" || typeof toSchema === "boolean") {
|
|
2305
2657
|
return;
|
|
2306
2658
|
}
|
|
2307
|
-
for (const [
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2659
|
+
for (const [toInputPortId, toPortInputSchema] of Object.entries(toSchema.properties || {})) {
|
|
2660
|
+
if (matches.has(toInputPortId))
|
|
2661
|
+
continue;
|
|
2662
|
+
const candidates = [];
|
|
2663
|
+
for (const [fromOutputPortId, fromPortOutputSchema] of Object.entries(fromSchema.properties || {})) {
|
|
2664
|
+
if (comparator([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema])) {
|
|
2665
|
+
candidates.push(fromOutputPortId);
|
|
2312
2666
|
}
|
|
2313
2667
|
}
|
|
2668
|
+
if (candidates.length === 0)
|
|
2669
|
+
continue;
|
|
2670
|
+
let winner = candidates[0];
|
|
2671
|
+
if (candidates.length > 1) {
|
|
2672
|
+
const targetStreamMode = getPortStreamMode(toSchema, toInputPortId);
|
|
2673
|
+
const streamMatch = candidates.find((portId) => getPortStreamMode(fromSchema, portId) === targetStreamMode);
|
|
2674
|
+
if (streamMatch)
|
|
2675
|
+
winner = streamMatch;
|
|
2676
|
+
}
|
|
2677
|
+
matches.set(toInputPortId, winner);
|
|
2678
|
+
graph.addDataflow(new Dataflow(fromTaskId, winner, toTaskId, toInputPortId));
|
|
2314
2679
|
}
|
|
2315
2680
|
};
|
|
2316
2681
|
makeMatch(sourceSchema, targetSchema, sourceTask.config.id, targetTask.config.id, ([fromOutputPortId, fromPortOutputSchema], [toInputPortId, toPortInputSchema]) => {
|
|
@@ -2758,6 +3123,24 @@ class TaskGraph {
|
|
|
2758
3123
|
unsubscribes.forEach((unsub) => unsub());
|
|
2759
3124
|
};
|
|
2760
3125
|
}
|
|
3126
|
+
subscribeToTaskStreaming(callbacks) {
|
|
3127
|
+
const unsubscribes = [];
|
|
3128
|
+
if (callbacks.onStreamStart) {
|
|
3129
|
+
const unsub = this.subscribe("task_stream_start", callbacks.onStreamStart);
|
|
3130
|
+
unsubscribes.push(unsub);
|
|
3131
|
+
}
|
|
3132
|
+
if (callbacks.onStreamChunk) {
|
|
3133
|
+
const unsub = this.subscribe("task_stream_chunk", callbacks.onStreamChunk);
|
|
3134
|
+
unsubscribes.push(unsub);
|
|
3135
|
+
}
|
|
3136
|
+
if (callbacks.onStreamEnd) {
|
|
3137
|
+
const unsub = this.subscribe("task_stream_end", callbacks.onStreamEnd);
|
|
3138
|
+
unsubscribes.push(unsub);
|
|
3139
|
+
}
|
|
3140
|
+
return () => {
|
|
3141
|
+
unsubscribes.forEach((unsub) => unsub());
|
|
3142
|
+
};
|
|
3143
|
+
}
|
|
2761
3144
|
on(name, fn) {
|
|
2762
3145
|
const dagEvent = EventTaskGraphToDagMapping[name];
|
|
2763
3146
|
if (dagEvent) {
|
|
@@ -4515,10 +4898,15 @@ export {
|
|
|
4515
4898
|
pipe,
|
|
4516
4899
|
parallel,
|
|
4517
4900
|
mergeChainedOutputToInput,
|
|
4901
|
+
isTaskStreamable,
|
|
4518
4902
|
isStrictArraySchema,
|
|
4519
4903
|
isIterationProperty,
|
|
4520
4904
|
isFlexibleSchema,
|
|
4905
|
+
hasVectorOutput,
|
|
4906
|
+
hasVectorLikeInput,
|
|
4521
4907
|
getTaskQueueRegistry,
|
|
4908
|
+
getPortStreamMode,
|
|
4909
|
+
getOutputStreamMode,
|
|
4522
4910
|
getNestedValue,
|
|
4523
4911
|
getLastTask,
|
|
4524
4912
|
getJobQueueFactory,
|
|
@@ -4530,6 +4918,7 @@ export {
|
|
|
4530
4918
|
extractBaseSchema,
|
|
4531
4919
|
evaluateCondition,
|
|
4532
4920
|
ensureTask,
|
|
4921
|
+
edgeNeedsAccumulation,
|
|
4533
4922
|
createTaskFromGraphJSON,
|
|
4534
4923
|
createTaskFromDependencyJSON,
|
|
4535
4924
|
createJobQueueFactoryWithOptions,
|
|
@@ -4588,7 +4977,8 @@ export {
|
|
|
4588
4977
|
CreateWorkflow,
|
|
4589
4978
|
CreateLoopWorkflow,
|
|
4590
4979
|
CreateEndLoopWorkflow,
|
|
4980
|
+
CreateAdaptiveWorkflow,
|
|
4591
4981
|
ConditionalTask
|
|
4592
4982
|
};
|
|
4593
4983
|
|
|
4594
|
-
//# debugId=
|
|
4984
|
+
//# debugId=65C6DA6762951D4864756E2164756E21
|