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