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