@alpic80/rivet-core 1.19.1-aidon.3 → 1.24.0-aidon.1
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/README.md +4 -0
- package/dist/cjs/bundle.cjs +3993 -943
- package/dist/cjs/bundle.cjs.map +4 -4
- package/dist/esm/api/createProcessor.js +8 -17
- package/dist/esm/api/looseDataValue.js +16 -0
- package/dist/esm/exports.js +2 -0
- package/dist/esm/integrations/CodeRunner.js +36 -0
- package/dist/esm/integrations/GptTokenizerTokenizer.js +7 -4
- package/dist/esm/integrations/openai/OpenAIEmbeddingGenerator.js +1 -1
- package/dist/esm/model/DataValue.js +14 -2
- package/dist/esm/model/GraphProcessor.js +275 -104
- package/dist/esm/model/NodeBase.js +11 -1
- package/dist/esm/model/NodeImpl.js +8 -0
- package/dist/esm/model/Nodes.js +28 -4
- package/dist/esm/model/ProjectReferenceLoader.js +1 -0
- package/dist/esm/model/nodes/AssembleMessageNode.js +12 -2
- package/dist/esm/model/nodes/AssemblePromptNode.js +22 -0
- package/dist/esm/model/nodes/CallGraphNode.js +3 -4
- package/dist/esm/model/nodes/ChatLoopNode.js +150 -0
- package/dist/esm/model/nodes/ChatNode.js +7 -934
- package/dist/esm/model/nodes/ChatNodeBase.js +1275 -0
- package/dist/esm/model/nodes/ChunkNode.js +2 -2
- package/dist/esm/model/nodes/CodeNode.js +40 -4
- package/dist/esm/model/nodes/CronNode.js +248 -0
- package/dist/esm/model/nodes/DelegateFunctionCallNode.js +37 -12
- package/dist/esm/model/nodes/DestructureNode.js +1 -1
- package/dist/esm/model/nodes/DocumentNode.js +183 -0
- package/dist/esm/model/nodes/ExtractJsonNode.js +4 -4
- package/dist/esm/model/nodes/ExtractRegexNode.js +10 -11
- package/dist/esm/model/nodes/GetEmbeddingNode.js +1 -1
- package/dist/esm/model/nodes/HttpCallNode.js +3 -1
- package/dist/esm/model/nodes/IfNode.js +5 -0
- package/dist/esm/model/nodes/LoopUntilNode.js +214 -0
- package/dist/esm/model/nodes/PromptNode.js +29 -6
- package/dist/esm/model/nodes/ReadAllFilesNode.js +210 -0
- package/dist/esm/model/nodes/ReadDirectoryNode.js +31 -25
- package/dist/esm/model/nodes/ReferencedGraphAliasNode.js +199 -0
- package/dist/esm/model/nodes/TextNode.js +9 -4
- package/dist/esm/model/nodes/ToMarkdownTableNode.js +119 -0
- package/dist/esm/model/nodes/ToTreeNode.js +133 -0
- package/dist/esm/model/nodes/{GptFunctionNode.js → ToolNode.js} +10 -10
- package/dist/esm/model/nodes/UserInputNode.js +10 -12
- package/dist/esm/plugins/aidon/nodes/ChatAidonNode.js +2 -2
- package/dist/esm/plugins/anthropic/anthropic.js +29 -10
- package/dist/esm/plugins/anthropic/fetchEventSource.js +3 -2
- package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +267 -147
- package/dist/esm/plugins/anthropic/plugin.js +9 -1
- package/dist/esm/plugins/gentrace/plugin.js +6 -6
- package/dist/esm/plugins/google/google.js +113 -5
- package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +211 -54
- package/dist/esm/plugins/google/plugin.js +13 -6
- package/dist/esm/plugins/openai/nodes/RunThreadNode.js +2 -2
- package/dist/esm/recording/ExecutionRecorder.js +5 -1
- package/dist/esm/utils/chatMessageToOpenAIChatCompletionMessage.js +15 -2
- package/dist/esm/utils/coerceType.js +1 -1
- package/dist/esm/utils/fetchEventSource.js +1 -1
- package/dist/esm/utils/interpolation.js +108 -3
- package/dist/esm/utils/openai.js +106 -50
- package/dist/esm/utils/paths.js +80 -0
- package/dist/esm/utils/serialization/serialization_v4.js +5 -0
- package/dist/types/api/createProcessor.d.ts +11 -5
- package/dist/types/api/looseDataValue.d.ts +4 -0
- package/dist/types/api/streaming.d.ts +1 -1
- package/dist/types/exports.d.ts +2 -0
- package/dist/types/integrations/CodeRunner.d.ts +18 -0
- package/dist/types/model/DataValue.d.ts +29 -6
- package/dist/types/model/EditorDefinition.d.ts +6 -1
- package/dist/types/model/GraphProcessor.d.ts +14 -7
- package/dist/types/model/NodeBase.d.ts +4 -0
- package/dist/types/model/NodeImpl.d.ts +5 -4
- package/dist/types/model/Nodes.d.ts +12 -4
- package/dist/types/model/ProcessContext.d.ts +16 -1
- package/dist/types/model/Project.d.ts +19 -7
- package/dist/types/model/ProjectReferenceLoader.d.ts +5 -0
- package/dist/types/model/RivetPlugin.d.ts +6 -0
- package/dist/types/model/RivetUIContext.d.ts +5 -1
- package/dist/types/model/Settings.d.ts +1 -0
- package/dist/types/model/nodes/AssemblePromptNode.d.ts +4 -1
- package/dist/types/model/nodes/ChatLoopNode.d.ts +21 -0
- package/dist/types/model/nodes/ChatNode.d.ts +2 -62
- package/dist/types/model/nodes/ChatNodeBase.d.ts +85 -0
- package/dist/types/model/nodes/CodeNode.d.ts +8 -2
- package/dist/types/model/nodes/CronNode.d.ts +34 -0
- package/dist/types/model/nodes/DelegateFunctionCallNode.d.ts +1 -0
- package/dist/types/model/nodes/DocumentNode.d.ts +28 -0
- package/dist/types/model/nodes/LoopUntilNode.d.ts +32 -0
- package/dist/types/model/nodes/PromptNode.d.ts +2 -0
- package/dist/types/model/nodes/ReadAllFilesNode.d.ts +30 -0
- package/dist/types/model/nodes/ReadDirectoryNode.d.ts +1 -1
- package/dist/types/model/nodes/ReferencedGraphAliasNode.d.ts +31 -0
- package/dist/types/model/nodes/ToMarkdownTableNode.d.ts +19 -0
- package/dist/types/model/nodes/ToTreeNode.d.ts +21 -0
- package/dist/types/model/nodes/UserInputNode.d.ts +2 -3
- package/dist/types/plugins/anthropic/anthropic.d.ts +94 -13
- package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +7 -2
- package/dist/types/plugins/google/google.d.ts +93 -18
- package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +3 -2
- package/dist/types/recording/RecordedEvents.d.ts +2 -0
- package/dist/types/utils/base64.d.ts +1 -1
- package/dist/types/utils/chatMessageToOpenAIChatCompletionMessage.d.ts +3 -1
- package/dist/types/utils/interpolation.d.ts +3 -0
- package/dist/types/utils/openai.d.ts +127 -21
- package/dist/types/utils/paths.d.ts +8 -0
- package/package.json +15 -11
- /package/dist/types/model/nodes/{GptFunctionNode.d.ts → ToolNode.d.ts} +0 -0
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { max, range, sum, uniqBy } from 'lodash-es';
|
|
2
2
|
import { isArrayDataValue, arrayizeDataValue, getScalarTypeOf, } from './DataValue.js';
|
|
3
|
+
import { IF_PORT, } from './NodeBase.js';
|
|
3
4
|
import PQueueImport from 'p-queue';
|
|
4
5
|
import { getError } from '../utils/errors.js';
|
|
5
6
|
import Emittery from 'emittery';
|
|
6
7
|
import { entries, fromEntries, values } from '../utils/typeSafety.js';
|
|
7
8
|
import { isNotNull } from '../utils/genericUtilFunctions.js';
|
|
9
|
+
import {} from './Project.js';
|
|
8
10
|
import { nanoid } from 'nanoid/non-secure';
|
|
9
11
|
import { P, match } from 'ts-pattern';
|
|
10
12
|
import { coerceTypeOptional } from '../utils/coerceType.js';
|
|
11
13
|
import { globalRivetNodeRegistry } from './Nodes.js';
|
|
12
14
|
import { getPluginConfig } from '../utils/index.js';
|
|
13
15
|
import { GptTokenizerTokenizer } from '../integrations/GptTokenizerTokenizer.js';
|
|
16
|
+
// eslint-disable-next-line import/no-cycle -- There has to be a cycle because CodeRunner needs to import the entirety of Rivet
|
|
17
|
+
import { IsomorphicCodeRunner } from '../integrations/CodeRunner.js';
|
|
14
18
|
// CJS compatibility, gets default.default for whatever reason
|
|
15
19
|
let PQueue = PQueueImport;
|
|
16
20
|
if (typeof PQueue !== 'function') {
|
|
@@ -23,28 +27,28 @@ export class GraphProcessor {
|
|
|
23
27
|
#nodesById;
|
|
24
28
|
#nodeInstances;
|
|
25
29
|
#connections;
|
|
26
|
-
#definitions;
|
|
27
30
|
#emitter = new Emittery();
|
|
28
31
|
#running = false;
|
|
29
32
|
#isSubProcessor = false;
|
|
30
|
-
#scc;
|
|
31
|
-
#nodesNotInCycle;
|
|
32
33
|
#externalFunctions = {};
|
|
33
34
|
slowMode = false;
|
|
34
35
|
#isPaused = false;
|
|
35
36
|
#parent;
|
|
36
37
|
#registry;
|
|
37
38
|
id = nanoid();
|
|
39
|
+
#includeTrace = true;
|
|
38
40
|
executor;
|
|
39
41
|
/** If set, specifies the node(s) that the graph will run TO, instead of the nodes without any dependents. */
|
|
40
42
|
runToNodeIds;
|
|
43
|
+
/** If set, specifies the node that the graph will run FROM, instead of the start nodes. Requires preloading data. */
|
|
44
|
+
runFromNodeId;
|
|
41
45
|
/** The node that is executing this graph, almost always a subgraph node. Undefined for root. */
|
|
42
46
|
#executor;
|
|
43
47
|
/** The interval between nodeFinish events when playing back a recording. I.e. how fast the playback is. */
|
|
44
48
|
recordingPlaybackChatLatency = 1000;
|
|
45
49
|
warnOnInvalidGraph = false;
|
|
46
50
|
// Per-process state
|
|
47
|
-
#erroredNodes = undefined;
|
|
51
|
+
#erroredNodes = undefined; // Values are strings in recordings
|
|
48
52
|
#remainingNodes = undefined;
|
|
49
53
|
#visitedNodes = undefined;
|
|
50
54
|
#currentlyProcessing = undefined;
|
|
@@ -68,13 +72,18 @@ export class GraphProcessor {
|
|
|
68
72
|
#totalResponseTokens = 0;
|
|
69
73
|
#totalCost = 0;
|
|
70
74
|
#ignoreNodes = undefined;
|
|
75
|
+
#hasPreloadedData = false;
|
|
76
|
+
#loadedProjects = undefined;
|
|
77
|
+
#definitions = undefined;
|
|
78
|
+
#scc = undefined;
|
|
79
|
+
#nodesNotInCycle = undefined;
|
|
71
80
|
#nodeAbortControllers = new Map();
|
|
72
81
|
/** User input nodes that are pending user input. */
|
|
73
82
|
#pendingUserInputs = undefined;
|
|
74
83
|
get isRunning() {
|
|
75
84
|
return this.#running;
|
|
76
85
|
}
|
|
77
|
-
constructor(project, graphId, registry) {
|
|
86
|
+
constructor(project, graphId, registry, includeTrace) {
|
|
78
87
|
this.#project = project;
|
|
79
88
|
const graph = graphId
|
|
80
89
|
? project.graphs[graphId]
|
|
@@ -85,11 +94,19 @@ export class GraphProcessor {
|
|
|
85
94
|
throw new Error(`Graph ${graphId} not found in project`);
|
|
86
95
|
}
|
|
87
96
|
this.#graph = graph;
|
|
97
|
+
this.#includeTrace = includeTrace;
|
|
88
98
|
this.#nodeInstances = {};
|
|
89
99
|
this.#connections = {};
|
|
90
100
|
this.#nodesById = {};
|
|
91
101
|
this.#registry = registry ?? globalRivetNodeRegistry;
|
|
92
102
|
this.#emitter.bindMethods(this, ['on', 'off', 'once', 'onAny', 'offAny']);
|
|
103
|
+
this.setExternalFunction('echo', async (value) => ({ type: 'any', value }));
|
|
104
|
+
this.#emitter.on('globalSet', ({ id, value }) => {
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
106
|
+
this.#emitter.emit(`globalSet:${id}`, value);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
#preprocessGraph() {
|
|
93
110
|
// Create node instances and store them in a lookup table
|
|
94
111
|
for (const node of this.#graph.nodes) {
|
|
95
112
|
this.#nodeInstances[node.id] = this.#registry.createDynamicImpl(node);
|
|
@@ -100,10 +117,10 @@ export class GraphProcessor {
|
|
|
100
117
|
if (!this.#nodesById[conn.inputNodeId] || !this.#nodesById[conn.outputNodeId]) {
|
|
101
118
|
if (this.warnOnInvalidGraph) {
|
|
102
119
|
if (!this.#nodesById[conn.inputNodeId]) {
|
|
103
|
-
console.warn(`Missing node ${conn.inputNodeId} in graph ${
|
|
120
|
+
console.warn(`Missing node ${conn.inputNodeId} in graph ${this.#graph} (connection from ${this.#nodesById[conn.outputNodeId]?.title})`);
|
|
104
121
|
}
|
|
105
122
|
else {
|
|
106
|
-
console.warn(`Missing node ${conn.outputNodeId} in graph ${
|
|
123
|
+
console.warn(`Missing node ${conn.outputNodeId} in graph ${this.#graph} (connection to ${this.#nodesById[conn.inputNodeId]?.title}) `);
|
|
107
124
|
}
|
|
108
125
|
}
|
|
109
126
|
continue;
|
|
@@ -113,12 +130,17 @@ export class GraphProcessor {
|
|
|
113
130
|
this.#connections[conn.inputNodeId].push(conn);
|
|
114
131
|
this.#connections[conn.outputNodeId].push(conn);
|
|
115
132
|
}
|
|
133
|
+
this.#loadInputOutputDefinitions();
|
|
134
|
+
this.#scc = this.#tarjanSCC();
|
|
135
|
+
this.#nodesNotInCycle = this.#scc.filter((cycle) => cycle.length === 1).flat();
|
|
136
|
+
}
|
|
137
|
+
#loadInputOutputDefinitions() {
|
|
116
138
|
// Store input and output definitions in a lookup table
|
|
117
139
|
this.#definitions = {};
|
|
118
140
|
for (const node of this.#graph.nodes) {
|
|
119
141
|
const connectionsForNode = this.#connections[node.id] ?? [];
|
|
120
|
-
const inputDefs = this.#nodeInstances[node.id].
|
|
121
|
-
const outputDefs = this.#nodeInstances[node.id].getOutputDefinitions(connectionsForNode, this.#nodesById, this.#project);
|
|
142
|
+
const inputDefs = this.#nodeInstances[node.id].getInputDefinitionsIncludingBuiltIn(connectionsForNode, this.#nodesById, this.#project, this.#loadedProjects);
|
|
143
|
+
const outputDefs = this.#nodeInstances[node.id].getOutputDefinitions(connectionsForNode, this.#nodesById, this.#project, this.#loadedProjects);
|
|
122
144
|
this.#definitions[node.id] = { inputs: inputDefs, outputs: outputDefs };
|
|
123
145
|
// Find all invalid connections to or from the node, then remove them from consideration
|
|
124
146
|
const invalidConnections = connectionsForNode.filter((connection) => {
|
|
@@ -153,12 +175,12 @@ export class GraphProcessor {
|
|
|
153
175
|
}
|
|
154
176
|
}
|
|
155
177
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.#emitter.emit(
|
|
161
|
-
}
|
|
178
|
+
}
|
|
179
|
+
#emitTraceEvent(eventData) {
|
|
180
|
+
if (this.#includeTrace) {
|
|
181
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
182
|
+
this.#emitter.emit('trace', eventData);
|
|
183
|
+
}
|
|
162
184
|
}
|
|
163
185
|
on = undefined;
|
|
164
186
|
off = undefined;
|
|
@@ -199,8 +221,10 @@ export class GraphProcessor {
|
|
|
199
221
|
this.#abortController.abort();
|
|
200
222
|
this.#abortSuccessfully = successful;
|
|
201
223
|
this.#abortError = error;
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
202
225
|
this.#emitter.emit('graphAbort', { successful, error, graph: this.#graph });
|
|
203
226
|
if (!this.#isSubProcessor) {
|
|
227
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
204
228
|
this.#emitter.emit('abort', { successful, error });
|
|
205
229
|
}
|
|
206
230
|
await this.#processingQueue.onIdle();
|
|
@@ -208,12 +232,14 @@ export class GraphProcessor {
|
|
|
208
232
|
pause() {
|
|
209
233
|
if (this.#isPaused === false) {
|
|
210
234
|
this.#isPaused = true;
|
|
235
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
211
236
|
this.#emitter.emit('pause', void 0);
|
|
212
237
|
}
|
|
213
238
|
}
|
|
214
239
|
resume() {
|
|
215
240
|
if (this.#isPaused) {
|
|
216
241
|
this.#isPaused = false;
|
|
242
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
217
243
|
this.#emitter.emit('resume', void 0);
|
|
218
244
|
}
|
|
219
245
|
}
|
|
@@ -234,6 +260,38 @@ export class GraphProcessor {
|
|
|
234
260
|
}
|
|
235
261
|
}
|
|
236
262
|
}
|
|
263
|
+
preloadNodeData(nodeId, data) {
|
|
264
|
+
this.#nodeResults ??= new Map();
|
|
265
|
+
this.#visitedNodes ??= new Set();
|
|
266
|
+
for (const value of Object.values(data)) {
|
|
267
|
+
if (!value || !('type' in value) || !value.type) {
|
|
268
|
+
throw new Error(`Invalid data value for node ${nodeId}, must be a DataValue`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
this.#nodeResults.set(nodeId, data);
|
|
272
|
+
this.#visitedNodes.add(nodeId);
|
|
273
|
+
this.#hasPreloadedData = true;
|
|
274
|
+
}
|
|
275
|
+
/** Gets all node IDs that a given node ID depends on being complete before the given node ID can start. */
|
|
276
|
+
getDependencyNodesDeep(nodeId) {
|
|
277
|
+
const node = this.#nodesById[nodeId];
|
|
278
|
+
if (!node) {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
const connections = this.#connections[nodeId];
|
|
282
|
+
if (!connections) {
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
const dependencyNodes = connections
|
|
286
|
+
.map((conn) => {
|
|
287
|
+
if (conn.inputNodeId === nodeId) {
|
|
288
|
+
return this.getDependencyNodesDeep(conn.outputNodeId);
|
|
289
|
+
}
|
|
290
|
+
return [];
|
|
291
|
+
})
|
|
292
|
+
.flat();
|
|
293
|
+
return [...new Set([nodeId, ...dependencyNodes])];
|
|
294
|
+
}
|
|
237
295
|
async replayRecording(recorder) {
|
|
238
296
|
const { events } = recorder;
|
|
239
297
|
this.#initProcessState();
|
|
@@ -265,6 +323,7 @@ export class GraphProcessor {
|
|
|
265
323
|
await this.#waitUntilUnpaused();
|
|
266
324
|
await match(event)
|
|
267
325
|
.with({ type: 'start' }, ({ data }) => {
|
|
326
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
268
327
|
this.#emitter.emit('start', {
|
|
269
328
|
project: this.#project,
|
|
270
329
|
contextValues: data.contextValues,
|
|
@@ -275,43 +334,52 @@ export class GraphProcessor {
|
|
|
275
334
|
this.#graphInputs = data.inputs;
|
|
276
335
|
})
|
|
277
336
|
.with({ type: 'abort' }, ({ data }) => {
|
|
337
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
278
338
|
this.#emitter.emit('abort', data);
|
|
279
339
|
})
|
|
280
340
|
.with({ type: 'pause' }, () => { })
|
|
281
341
|
.with({ type: 'resume' }, () => { })
|
|
282
342
|
.with({ type: 'done' }, ({ data }) => {
|
|
343
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
283
344
|
this.#emitter.emit('done', data);
|
|
284
345
|
this.#graphOutputs = data.results;
|
|
285
346
|
this.#running = false;
|
|
286
347
|
})
|
|
287
348
|
.with({ type: 'error' }, ({ data }) => {
|
|
349
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
288
350
|
this.#emitter.emit('error', data);
|
|
289
351
|
})
|
|
290
352
|
.with({ type: 'globalSet' }, ({ data }) => {
|
|
353
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
291
354
|
this.#emitter.emit('globalSet', data);
|
|
292
355
|
})
|
|
293
356
|
.with({ type: 'trace' }, ({ data }) => {
|
|
357
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
294
358
|
this.#emitter.emit('trace', data);
|
|
295
359
|
})
|
|
296
360
|
.with({ type: 'graphStart' }, ({ data }) => {
|
|
361
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
297
362
|
this.#emitter.emit('graphStart', {
|
|
298
363
|
graph: getGraph(data.graphId),
|
|
299
364
|
inputs: data.inputs,
|
|
300
365
|
});
|
|
301
366
|
})
|
|
302
367
|
.with({ type: 'graphFinish' }, ({ data }) => {
|
|
368
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
303
369
|
this.#emitter.emit('graphFinish', {
|
|
304
370
|
graph: getGraph(data.graphId),
|
|
305
371
|
outputs: data.outputs,
|
|
306
372
|
});
|
|
307
373
|
})
|
|
308
374
|
.with({ type: 'graphError' }, ({ data }) => {
|
|
375
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
309
376
|
this.#emitter.emit('graphError', {
|
|
310
377
|
graph: getGraph(data.graphId),
|
|
311
378
|
error: data.error,
|
|
312
379
|
});
|
|
313
380
|
})
|
|
314
381
|
.with({ type: 'graphAbort' }, ({ data }) => {
|
|
382
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
315
383
|
this.#emitter.emit('graphAbort', {
|
|
316
384
|
graph: getGraph(data.graphId),
|
|
317
385
|
error: data.error,
|
|
@@ -320,6 +388,7 @@ export class GraphProcessor {
|
|
|
320
388
|
})
|
|
321
389
|
.with({ type: 'nodeStart' }, async ({ data }) => {
|
|
322
390
|
const node = getNode(data.nodeId);
|
|
391
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
323
392
|
this.#emitter.emit('nodeStart', {
|
|
324
393
|
node: getNode(data.nodeId),
|
|
325
394
|
inputs: data.inputs,
|
|
@@ -332,6 +401,7 @@ export class GraphProcessor {
|
|
|
332
401
|
})
|
|
333
402
|
.with({ type: 'nodeFinish' }, ({ data }) => {
|
|
334
403
|
const node = getNode(data.nodeId);
|
|
404
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
335
405
|
this.#emitter.emit('nodeFinish', {
|
|
336
406
|
node,
|
|
337
407
|
outputs: data.outputs,
|
|
@@ -341,6 +411,7 @@ export class GraphProcessor {
|
|
|
341
411
|
this.#visitedNodes.add(data.nodeId);
|
|
342
412
|
})
|
|
343
413
|
.with({ type: 'nodeError' }, ({ data }) => {
|
|
414
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
344
415
|
this.#emitter.emit('nodeError', {
|
|
345
416
|
node: getNode(data.nodeId),
|
|
346
417
|
error: data.error,
|
|
@@ -350,6 +421,7 @@ export class GraphProcessor {
|
|
|
350
421
|
this.#visitedNodes.add(data.nodeId);
|
|
351
422
|
})
|
|
352
423
|
.with({ type: 'nodeExcluded' }, ({ data }) => {
|
|
424
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
353
425
|
this.#emitter.emit('nodeExcluded', {
|
|
354
426
|
node: getNode(data.nodeId),
|
|
355
427
|
processId: data.processId,
|
|
@@ -362,28 +434,35 @@ export class GraphProcessor {
|
|
|
362
434
|
.with({ type: 'nodeOutputsCleared' }, () => { })
|
|
363
435
|
.with({ type: 'partialOutput' }, () => { })
|
|
364
436
|
.with({ type: 'userInput' }, ({ data }) => {
|
|
437
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
365
438
|
this.#emitter.emit('userInput', {
|
|
366
439
|
callback: undefined,
|
|
440
|
+
inputStrings: data.inputStrings,
|
|
367
441
|
inputs: data.inputs,
|
|
368
442
|
node: getNode(data.nodeId),
|
|
369
443
|
processId: data.processId,
|
|
444
|
+
renderingType: data.renderingType,
|
|
370
445
|
});
|
|
371
446
|
})
|
|
372
447
|
.with({ type: P.string.startsWith('globalSet:') }, ({ type, data }) => {
|
|
448
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
373
449
|
this.#emitter.emit(type, data);
|
|
374
450
|
})
|
|
375
451
|
.with({ type: P.string.startsWith('userEvent:') }, ({ type, data }) => {
|
|
452
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
376
453
|
this.#emitter.emit(type, data);
|
|
377
454
|
})
|
|
378
455
|
.with({ type: 'newAbortController' }, () => { })
|
|
379
456
|
.with({ type: 'finish' }, () => {
|
|
457
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
380
458
|
this.#emitter.emit('finish', undefined);
|
|
381
459
|
})
|
|
382
|
-
|
|
460
|
+
.with(P.nullish, () => { })
|
|
383
461
|
.exhaustive();
|
|
384
462
|
}
|
|
385
463
|
}
|
|
386
464
|
catch (err) {
|
|
465
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
387
466
|
this.#emitter.emit('error', { error: getError(err) });
|
|
388
467
|
}
|
|
389
468
|
finally {
|
|
@@ -393,9 +472,11 @@ export class GraphProcessor {
|
|
|
393
472
|
}
|
|
394
473
|
#initProcessState() {
|
|
395
474
|
this.#running = true;
|
|
396
|
-
this.#
|
|
475
|
+
if (!this.#hasPreloadedData) {
|
|
476
|
+
this.#nodeResults = new Map();
|
|
477
|
+
this.#visitedNodes = new Set();
|
|
478
|
+
}
|
|
397
479
|
this.#erroredNodes = new Map();
|
|
398
|
-
this.#visitedNodes = new Set();
|
|
399
480
|
this.#currentlyProcessing = new Set();
|
|
400
481
|
this.#remainingNodes = new Set(this.#graph.nodes.map((n) => n.id));
|
|
401
482
|
this.#pendingUserInputs = {};
|
|
@@ -416,6 +497,7 @@ export class GraphProcessor {
|
|
|
416
497
|
this.#abortError = undefined;
|
|
417
498
|
this.#abortSuccessfully = false;
|
|
418
499
|
this.#nodeAbortControllers = new Map();
|
|
500
|
+
this.#loadedProjects = {};
|
|
419
501
|
}
|
|
420
502
|
/** Main function for running a graph. Runs a graph and returns the outputs from the output nodes of the graph. */
|
|
421
503
|
async processGraph(
|
|
@@ -435,27 +517,49 @@ export class GraphProcessor {
|
|
|
435
517
|
this.#contextValues ??= contextValues;
|
|
436
518
|
if (this.#context.tokenizer) {
|
|
437
519
|
this.#context.tokenizer.on('error', (error) => {
|
|
520
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
438
521
|
this.#emitter.emit('error', { error });
|
|
439
522
|
});
|
|
440
523
|
}
|
|
524
|
+
await this.#loadProjectReferences();
|
|
525
|
+
this.#preprocessGraph();
|
|
441
526
|
if (!this.#isSubProcessor) {
|
|
442
|
-
this.#emitter.emit('start', {
|
|
527
|
+
await this.#emitter.emit('start', {
|
|
443
528
|
contextValues: this.#contextValues,
|
|
444
529
|
inputs: this.#graphInputs,
|
|
445
530
|
project: this.#project,
|
|
446
531
|
startGraph: this.#graph,
|
|
447
532
|
});
|
|
448
533
|
}
|
|
449
|
-
this.#emitter.emit('graphStart', { graph: this.#graph, inputs: this.#graphInputs });
|
|
534
|
+
await this.#emitter.emit('graphStart', { graph: this.#graph, inputs: this.#graphInputs });
|
|
535
|
+
if (this.#hasPreloadedData) {
|
|
536
|
+
for (const node of this.#graph.nodes) {
|
|
537
|
+
if (this.#nodeResults.has(node.id)) {
|
|
538
|
+
this.#emitTraceEvent(`Node ${node.title} has preloaded data`);
|
|
539
|
+
await this.#emitter.emit('nodeStart', {
|
|
540
|
+
node,
|
|
541
|
+
inputs: {},
|
|
542
|
+
processId: 'preload',
|
|
543
|
+
});
|
|
544
|
+
await this.#emitter.emit('nodeFinish', {
|
|
545
|
+
node,
|
|
546
|
+
outputs: this.#nodeResults.get(node.id),
|
|
547
|
+
processId: 'preload',
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
450
552
|
const startNodes = this.runToNodeIds
|
|
451
553
|
? this.#graph.nodes.filter((node) => this.runToNodeIds?.includes(node.id))
|
|
452
554
|
: this.#graph.nodes.filter((node) => this.#outputNodesFrom(node).nodes.length === 0);
|
|
453
555
|
await this.#waitUntilUnpaused();
|
|
454
556
|
for (const startNode of startNodes) {
|
|
557
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
455
558
|
this.#processingQueue.add(async () => {
|
|
456
559
|
await this.#fetchNodeDataAndProcessNode(startNode);
|
|
457
560
|
});
|
|
458
561
|
}
|
|
562
|
+
await this.#processingQueue.onIdle();
|
|
459
563
|
// Anything not queued at this phase here should be ignored
|
|
460
564
|
if (this.runToNodeIds) {
|
|
461
565
|
// For safety, we'll only activate this if runToNodeIds is set, in case there are bugs in the first pass
|
|
@@ -465,20 +569,28 @@ export class GraphProcessor {
|
|
|
465
569
|
}
|
|
466
570
|
}
|
|
467
571
|
}
|
|
468
|
-
await this.#processingQueue.onIdle();
|
|
469
572
|
// If we've aborted successfully, we can treat the graph like it succeeded
|
|
470
573
|
const erroredNodes = [...this.#erroredNodes.entries()].filter(([nodeId]) => {
|
|
471
574
|
const erroredNodeAttachedData = this.#getAttachedDataTo(nodeId);
|
|
472
575
|
return erroredNodeAttachedData.races == null || erroredNodeAttachedData.races.completed === false;
|
|
473
576
|
});
|
|
474
577
|
if (erroredNodes.length && !this.#abortSuccessfully) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
.
|
|
479
|
-
|
|
578
|
+
let error = this.#abortError;
|
|
579
|
+
if (!error) {
|
|
580
|
+
const message = `Graph ${this.#graph.metadata.name} (${this.#graph.metadata.id}) failed to process due to errors in nodes:\n${erroredNodes
|
|
581
|
+
.map(([nodeId]) => `- ${this.#nodesById[nodeId].title} (${nodeId})`)
|
|
582
|
+
.join('\n')}`;
|
|
583
|
+
if (erroredNodes.length === 1) {
|
|
584
|
+
const [, nodeError] = erroredNodes[0];
|
|
585
|
+
error = new Error(message, { cause: nodeError });
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
error = new AggregateError(erroredNodes.map(([, nodeError]) => nodeError), message);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
await this.#emitter.emit('graphError', { graph: this.#graph, error });
|
|
480
592
|
if (!this.#isSubProcessor) {
|
|
481
|
-
this.#emitter.emit('error', { error });
|
|
593
|
+
await this.#emitter.emit('error', { error });
|
|
482
594
|
}
|
|
483
595
|
throw error;
|
|
484
596
|
}
|
|
@@ -502,17 +614,39 @@ export class GraphProcessor {
|
|
|
502
614
|
}
|
|
503
615
|
const outputValues = this.#graphOutputs;
|
|
504
616
|
this.#running = false;
|
|
505
|
-
this.#emitter.emit('graphFinish', { graph: this.#graph, outputs: outputValues });
|
|
617
|
+
await this.#emitter.emit('graphFinish', { graph: this.#graph, outputs: outputValues });
|
|
506
618
|
if (!this.#isSubProcessor) {
|
|
507
|
-
this.#emitter.emit('done', { results: outputValues });
|
|
508
|
-
this.#emitter.emit('finish', undefined);
|
|
619
|
+
await this.#emitter.emit('done', { results: outputValues });
|
|
620
|
+
await this.#emitter.emit('finish', undefined);
|
|
509
621
|
}
|
|
510
622
|
return outputValues;
|
|
511
623
|
}
|
|
512
624
|
finally {
|
|
513
625
|
this.#running = false;
|
|
514
626
|
if (!this.#isSubProcessor) {
|
|
515
|
-
this.#emitter.emit('finish', undefined);
|
|
627
|
+
await this.#emitter.emit('finish', undefined);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
async #loadProjectReferences() {
|
|
632
|
+
if ((this.#project.references?.length ?? 0) > 0) {
|
|
633
|
+
if (!this.#context.projectReferenceLoader) {
|
|
634
|
+
throw new Error('Project references are set, but no projectReferenceLoader is set in the context. Since this project uses project references, you must provide a projectReferenceLoader in the context.');
|
|
635
|
+
}
|
|
636
|
+
const seenProjectIds = new Set();
|
|
637
|
+
const loadProject = async (ref) => {
|
|
638
|
+
if (seenProjectIds.has(ref.id)) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
seenProjectIds.add(ref.id);
|
|
642
|
+
const project = await this.#context.projectReferenceLoader.loadProject(this.#context.projectPath, ref);
|
|
643
|
+
this.#loadedProjects[project.metadata.id] = project;
|
|
644
|
+
for (const reference of project.references ?? []) {
|
|
645
|
+
await loadProject(reference);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
for (const reference of this.#project.references) {
|
|
649
|
+
await loadProject(reference);
|
|
516
650
|
}
|
|
517
651
|
}
|
|
518
652
|
}
|
|
@@ -539,7 +673,7 @@ export class GraphProcessor {
|
|
|
539
673
|
if (!inputsReady) {
|
|
540
674
|
return;
|
|
541
675
|
}
|
|
542
|
-
this.#
|
|
676
|
+
this.#emitTraceEvent(`Node ${node.title} has required inputs nodes: ${inputNodes.map((n) => n.title).join(', ')}`);
|
|
543
677
|
const attachedData = this.#getAttachedDataTo(node);
|
|
544
678
|
if (node.type === 'raceInputs' || attachedData.races) {
|
|
545
679
|
for (const inputNode of inputNodes) {
|
|
@@ -556,9 +690,10 @@ export class GraphProcessor {
|
|
|
556
690
|
}
|
|
557
691
|
}
|
|
558
692
|
this.#queuedNodes.add(node.id);
|
|
693
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
559
694
|
this.#processingQueue.addAll(inputNodes.map((inputNode) => {
|
|
560
695
|
return async () => {
|
|
561
|
-
this.#
|
|
696
|
+
this.#emitTraceEvent(`Fetching required data for node ${inputNode.title} (${inputNode.id})`);
|
|
562
697
|
await this.#fetchNodeDataAndProcessNode(inputNode);
|
|
563
698
|
};
|
|
564
699
|
}));
|
|
@@ -568,27 +703,34 @@ export class GraphProcessor {
|
|
|
568
703
|
async #processNodeIfAllInputsAvailable(node) {
|
|
569
704
|
const builtInNode = node;
|
|
570
705
|
if (this.#ignoreNodes.has(node.id)) {
|
|
571
|
-
this.#
|
|
706
|
+
this.#emitTraceEvent(`Node ${node.title} is ignored`);
|
|
572
707
|
return;
|
|
573
708
|
}
|
|
709
|
+
if (this.runToNodeIds) {
|
|
710
|
+
const dependencyNodes = this.getDependencyNodesDeep(node.id);
|
|
711
|
+
if (this.runToNodeIds.some((runTo) => runTo !== node.id && dependencyNodes.includes(runTo))) {
|
|
712
|
+
this.#emitTraceEvent(`Node ${node.title} is excluded due to runToNodeIds`);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
574
716
|
if (this.#currentlyProcessing.has(node.id)) {
|
|
575
|
-
this.#
|
|
717
|
+
this.#emitTraceEvent(`Node ${node.title} is already being processed`);
|
|
576
718
|
return;
|
|
577
719
|
}
|
|
578
720
|
// For a loop controller, it can run multiple times, otherwise we already processed this node so bail out
|
|
579
721
|
if (this.#visitedNodes.has(node.id) && node.type !== 'loopController') {
|
|
580
|
-
this.#
|
|
722
|
+
this.#emitTraceEvent(`Node ${node.title} has already been processed`);
|
|
581
723
|
return;
|
|
582
724
|
}
|
|
583
725
|
if (this.#erroredNodes.has(node.id)) {
|
|
584
|
-
this.#
|
|
726
|
+
this.#emitTraceEvent(`Node ${node.title} has already errored`);
|
|
585
727
|
return;
|
|
586
728
|
}
|
|
587
729
|
const inputNodes = this.#inputNodesTo(node);
|
|
588
730
|
// Check if all input nodes are free of errors
|
|
589
731
|
for (const inputNode of inputNodes) {
|
|
590
732
|
if (this.#erroredNodes.has(inputNode.id)) {
|
|
591
|
-
this.#
|
|
733
|
+
this.#emitTraceEvent(`Node ${node.title} has errored input node ${inputNode.title}`);
|
|
592
734
|
return;
|
|
593
735
|
}
|
|
594
736
|
}
|
|
@@ -599,13 +741,13 @@ export class GraphProcessor {
|
|
|
599
741
|
return connectionToInput || !input.required;
|
|
600
742
|
});
|
|
601
743
|
if (!inputsReady) {
|
|
602
|
-
this.#
|
|
744
|
+
this.#emitTraceEvent(`Node ${node.title} has required inputs nodes: ${inputNodes.map((n) => n.title).join(', ')}`);
|
|
603
745
|
return;
|
|
604
746
|
}
|
|
605
747
|
// Excluded because control flow is still in a loop - difference between "will not execute" and "has not executed yet"
|
|
606
748
|
const inputValues = this.#getInputValuesForNode(node);
|
|
607
749
|
if (this.#excludedDueToControlFlow(node, inputValues, nanoid(), 'loop-not-broken')) {
|
|
608
|
-
this.#
|
|
750
|
+
this.#emitTraceEvent(`Node ${node.title} is excluded due to control flow`);
|
|
609
751
|
return;
|
|
610
752
|
}
|
|
611
753
|
let waitingForInputNode = false;
|
|
@@ -628,7 +770,7 @@ export class GraphProcessor {
|
|
|
628
770
|
}
|
|
629
771
|
}
|
|
630
772
|
if (waitingForInputNode) {
|
|
631
|
-
this.#
|
|
773
|
+
this.#emitTraceEvent(`Node ${node.title} is waiting for input node ${waitingForInputNode}`);
|
|
632
774
|
return;
|
|
633
775
|
}
|
|
634
776
|
this.#currentlyProcessing.add(node.id);
|
|
@@ -640,14 +782,14 @@ export class GraphProcessor {
|
|
|
640
782
|
attachedData.loopInfo.nodes.add(node.id);
|
|
641
783
|
}
|
|
642
784
|
if (attachedData.races?.completed) {
|
|
643
|
-
this.#
|
|
785
|
+
this.#emitTraceEvent(`Node ${node.title} is part of a race that was completed`);
|
|
644
786
|
return;
|
|
645
787
|
}
|
|
646
788
|
const processId = await this.#processNode(node);
|
|
647
789
|
if (this.slowMode) {
|
|
648
790
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
649
791
|
}
|
|
650
|
-
this.#
|
|
792
|
+
this.#emitTraceEvent(`Finished processing node ${node.title} (${node.id})`);
|
|
651
793
|
this.#visitedNodes.add(node.id);
|
|
652
794
|
this.#currentlyProcessing.delete(node.id);
|
|
653
795
|
this.#remainingNodes.delete(node.id);
|
|
@@ -657,13 +799,15 @@ export class GraphProcessor {
|
|
|
657
799
|
const loopControllerResults = this.#nodeResults.get(node.id);
|
|
658
800
|
// If the loop controller is excluded, we have to "break" it or else it'll loop forever...
|
|
659
801
|
const breakValue = loopControllerResults['break'];
|
|
660
|
-
const didBreak =
|
|
802
|
+
const didBreak =
|
|
803
|
+
// @ts-ignore
|
|
804
|
+
!(breakValue?.type === 'control-flow-excluded' && breakValue?.value === 'loop-not-broken') ??
|
|
661
805
|
this.#excludedDueToControlFlow(node, this.#getInputValuesForNode(node), nanoid());
|
|
662
806
|
if (!didBreak) {
|
|
663
|
-
this.#
|
|
807
|
+
this.#emitTraceEvent(`Loop controller ${node.title} did not break, so we're looping again`);
|
|
664
808
|
for (const loopNodeId of attachedData.loopInfo?.nodes ?? []) {
|
|
665
809
|
const cycleNode = this.#nodesById[loopNodeId];
|
|
666
|
-
this.#
|
|
810
|
+
this.#emitTraceEvent(`Clearing cycle node ${cycleNode.title} (${cycleNode.id})`);
|
|
667
811
|
this.#visitedNodes.delete(cycleNode.id);
|
|
668
812
|
this.#currentlyProcessing.delete(cycleNode.id);
|
|
669
813
|
this.#remainingNodes.add(cycleNode.id);
|
|
@@ -678,7 +822,7 @@ export class GraphProcessor {
|
|
|
678
822
|
for (const [nodeId] of allNodesForRace) {
|
|
679
823
|
for (const [key, abortController] of this.#nodeAbortControllers.entries()) {
|
|
680
824
|
if (key.startsWith(nodeId)) {
|
|
681
|
-
this.#
|
|
825
|
+
this.#emitTraceEvent(`Aborting node ${nodeId} because other race branch won`);
|
|
682
826
|
abortController.abort();
|
|
683
827
|
}
|
|
684
828
|
}
|
|
@@ -727,8 +871,9 @@ export class GraphProcessor {
|
|
|
727
871
|
}
|
|
728
872
|
}
|
|
729
873
|
// Node is finished, check if we can run any more nodes that depend on this one
|
|
874
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
730
875
|
this.#processingQueue.addAll(outputNodes.nodes.map((outputNode) => async () => {
|
|
731
|
-
this.#
|
|
876
|
+
this.#emitTraceEvent(`Trying to run output node from ${node.title}: ${outputNode.title} (${outputNode.id})`);
|
|
732
877
|
await this.#processNodeIfAllInputsAvailable(outputNode);
|
|
733
878
|
}));
|
|
734
879
|
}
|
|
@@ -756,10 +901,7 @@ export class GraphProcessor {
|
|
|
756
901
|
this.#nodeErrored(node, error, processId);
|
|
757
902
|
return processId;
|
|
758
903
|
}
|
|
759
|
-
if (
|
|
760
|
-
await this.#processUserInputNode(node, processId);
|
|
761
|
-
}
|
|
762
|
-
else if (node.isSplitRun) {
|
|
904
|
+
if (node.isSplitRun) {
|
|
763
905
|
await this.#processSplitRunNode(node, processId);
|
|
764
906
|
}
|
|
765
907
|
else {
|
|
@@ -767,50 +909,13 @@ export class GraphProcessor {
|
|
|
767
909
|
}
|
|
768
910
|
return processId;
|
|
769
911
|
}
|
|
770
|
-
#isNodeOfType(type, node) {
|
|
771
|
-
return node.type === type;
|
|
772
|
-
}
|
|
773
|
-
async #processUserInputNode(node, processId) {
|
|
774
|
-
try {
|
|
775
|
-
const inputValues = this.#getInputValuesForNode(node);
|
|
776
|
-
if (this.#excludedDueToControlFlow(node, inputValues, processId)) {
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
this.#emitter.emit('nodeStart', { node, inputs: inputValues, processId });
|
|
780
|
-
const results = await new Promise((resolve, reject) => {
|
|
781
|
-
this.#pendingUserInputs[node.id] = {
|
|
782
|
-
resolve,
|
|
783
|
-
reject,
|
|
784
|
-
};
|
|
785
|
-
this.#abortController.signal.addEventListener('abort', () => {
|
|
786
|
-
delete this.#pendingUserInputs[node.id];
|
|
787
|
-
reject(new Error('Processing aborted'));
|
|
788
|
-
});
|
|
789
|
-
this.#emitter.emit('userInput', {
|
|
790
|
-
node,
|
|
791
|
-
inputs: inputValues,
|
|
792
|
-
callback: (results) => {
|
|
793
|
-
resolve(results);
|
|
794
|
-
delete this.#pendingUserInputs[node.id];
|
|
795
|
-
},
|
|
796
|
-
processId,
|
|
797
|
-
});
|
|
798
|
-
});
|
|
799
|
-
const outputValues = this.#nodeInstances[node.id].getOutputValuesFromUserInput(inputValues, results);
|
|
800
|
-
this.#nodeResults.set(node.id, outputValues);
|
|
801
|
-
this.#visitedNodes.add(node.id);
|
|
802
|
-
this.#emitter.emit('nodeFinish', { node, outputs: outputValues, processId });
|
|
803
|
-
}
|
|
804
|
-
catch (e) {
|
|
805
|
-
this.#nodeErrored(node, e, processId);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
912
|
async #processSplitRunNode(node, processId) {
|
|
809
913
|
const inputValues = this.#getInputValuesForNode(node);
|
|
810
914
|
if (this.#excludedDueToControlFlow(node, inputValues, processId)) {
|
|
811
915
|
return;
|
|
812
916
|
}
|
|
813
917
|
const splittingAmount = Math.min(max(values(inputValues).map((value) => (Array.isArray(value?.value) ? value?.value.length : 1))) ?? 1, node.splitRunMax ?? 10);
|
|
918
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
814
919
|
this.#emitter.emit('nodeStart', { node, inputs: inputValues, processId });
|
|
815
920
|
try {
|
|
816
921
|
let results = [];
|
|
@@ -824,7 +929,10 @@ export class GraphProcessor {
|
|
|
824
929
|
isArrayDataValue(value) ? arrayizeDataValue(value)[i] ?? undefined : value,
|
|
825
930
|
]));
|
|
826
931
|
try {
|
|
827
|
-
const output = await this.#processNodeWithInputData(node, inputs, i, processId, (node, partialOutputs, index) =>
|
|
932
|
+
const output = await this.#processNodeWithInputData(node, inputs, i, processId, (node, partialOutputs, index) => {
|
|
933
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
934
|
+
this.#emitter.emit('partialOutput', { node, outputs: partialOutputs, index, processId });
|
|
935
|
+
});
|
|
828
936
|
if (output['requestTokens']?.type === 'number') {
|
|
829
937
|
this.#totalRequestTokens += coerceTypeOptional(output['requestTokens'], 'number') ?? 0;
|
|
830
938
|
}
|
|
@@ -848,7 +956,10 @@ export class GraphProcessor {
|
|
|
848
956
|
isArrayDataValue(value) ? arrayizeDataValue(value)[i] ?? undefined : value,
|
|
849
957
|
]));
|
|
850
958
|
try {
|
|
851
|
-
const output = await this.#processNodeWithInputData(node, inputs, i, processId, (node, partialOutputs, index) =>
|
|
959
|
+
const output = await this.#processNodeWithInputData(node, inputs, i, processId, (node, partialOutputs, index) => {
|
|
960
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
961
|
+
this.#emitter.emit('partialOutput', { node, outputs: partialOutputs, index, processId });
|
|
962
|
+
});
|
|
852
963
|
if (output['requestTokens']?.type === 'number') {
|
|
853
964
|
this.#totalRequestTokens += coerceTypeOptional(output['requestTokens'], 'number') ?? 0;
|
|
854
965
|
}
|
|
@@ -871,7 +982,7 @@ export class GraphProcessor {
|
|
|
871
982
|
throw e;
|
|
872
983
|
}
|
|
873
984
|
else if (errors.length > 0) {
|
|
874
|
-
throw new
|
|
985
|
+
throw new AggregateError(errors);
|
|
875
986
|
}
|
|
876
987
|
// Combine the parallel results into the final output
|
|
877
988
|
// Turn a Record<PortId, DataValue[]> into a Record<PortId, AnyArrayDataValue>
|
|
@@ -887,6 +998,7 @@ export class GraphProcessor {
|
|
|
887
998
|
this.#totalRequestTokens += sum(results.map((r) => coerceTypeOptional(r.output?.['requestTokens'], 'number') ?? 0));
|
|
888
999
|
this.#totalResponseTokens += sum(results.map((r) => coerceTypeOptional(r.output?.['responseTokens'], 'number') ?? 0));
|
|
889
1000
|
this.#totalCost += sum(results.map((r) => coerceTypeOptional(r.output?.['cost'], 'number') ?? 0));
|
|
1001
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
890
1002
|
this.#emitter.emit('nodeFinish', { node, outputs: aggregateResults, processId });
|
|
891
1003
|
}
|
|
892
1004
|
catch (error) {
|
|
@@ -898,9 +1010,13 @@ export class GraphProcessor {
|
|
|
898
1010
|
if (this.#excludedDueToControlFlow(node, inputValues, processId)) {
|
|
899
1011
|
return;
|
|
900
1012
|
}
|
|
1013
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
901
1014
|
this.#emitter.emit('nodeStart', { node, inputs: inputValues, processId });
|
|
902
1015
|
try {
|
|
903
|
-
const outputValues = await this.#processNodeWithInputData(node, inputValues, 0, processId, (node, partialOutputs, index) =>
|
|
1016
|
+
const outputValues = await this.#processNodeWithInputData(node, inputValues, 0, processId, (node, partialOutputs, index) => {
|
|
1017
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1018
|
+
this.#emitter.emit('partialOutput', { node, outputs: partialOutputs, index, processId });
|
|
1019
|
+
});
|
|
904
1020
|
this.#nodeResults.set(node.id, outputValues);
|
|
905
1021
|
this.#visitedNodes.add(node.id);
|
|
906
1022
|
if (outputValues['requestTokens']?.type === 'number') {
|
|
@@ -912,6 +1028,7 @@ export class GraphProcessor {
|
|
|
912
1028
|
if (outputValues['cost']?.type === 'number') {
|
|
913
1029
|
this.#totalCost += coerceTypeOptional(outputValues['cost'], 'number') ?? 0;
|
|
914
1030
|
}
|
|
1031
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
915
1032
|
this.#emitter.emit('nodeFinish', { node, outputs: outputValues, processId });
|
|
916
1033
|
}
|
|
917
1034
|
catch (error) {
|
|
@@ -920,9 +1037,10 @@ export class GraphProcessor {
|
|
|
920
1037
|
}
|
|
921
1038
|
#nodeErrored(node, e, processId) {
|
|
922
1039
|
const error = getError(e);
|
|
1040
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
923
1041
|
this.#emitter.emit('nodeError', { node, error, processId });
|
|
924
|
-
this.#
|
|
925
|
-
this.#erroredNodes.set(node.id, error
|
|
1042
|
+
this.#emitTraceEvent(`Node ${node.title} (${node.id}-${processId}) errored: ${error.stack}`);
|
|
1043
|
+
this.#erroredNodes.set(node.id, error);
|
|
926
1044
|
}
|
|
927
1045
|
getRootProcessor() {
|
|
928
1046
|
let processor = this;
|
|
@@ -933,6 +1051,7 @@ export class GraphProcessor {
|
|
|
933
1051
|
}
|
|
934
1052
|
/** Raise a user event on the processor, all subprocessors, and their children. */
|
|
935
1053
|
raiseEvent(event, data) {
|
|
1054
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
936
1055
|
this.#emitter.emit(`userEvent:${event}`, data);
|
|
937
1056
|
for (const subprocessor of this.#subprocessors) {
|
|
938
1057
|
subprocessor.raiseEvent(event, data);
|
|
@@ -940,6 +1059,7 @@ export class GraphProcessor {
|
|
|
940
1059
|
}
|
|
941
1060
|
#newAbortController() {
|
|
942
1061
|
const controller = new AbortController();
|
|
1062
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
943
1063
|
this.#emitter.emit('newAbortController', controller);
|
|
944
1064
|
return controller;
|
|
945
1065
|
}
|
|
@@ -955,7 +1075,10 @@ export class GraphProcessor {
|
|
|
955
1075
|
let tokenizer = this.#context.tokenizer;
|
|
956
1076
|
if (!tokenizer) {
|
|
957
1077
|
tokenizer = new GptTokenizerTokenizer();
|
|
958
|
-
tokenizer.on('error', (e) =>
|
|
1078
|
+
tokenizer.on('error', (e) => {
|
|
1079
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1080
|
+
this.#emitter.emit('error', { error: e });
|
|
1081
|
+
});
|
|
959
1082
|
}
|
|
960
1083
|
const context = {
|
|
961
1084
|
...this.#context,
|
|
@@ -967,6 +1090,8 @@ export class GraphProcessor {
|
|
|
967
1090
|
graphInputs: this.#graphInputs,
|
|
968
1091
|
graphOutputs: this.#graphOutputs,
|
|
969
1092
|
attachedData: this.#getAttachedDataTo(node),
|
|
1093
|
+
codeRunner: this.#context.codeRunner ?? new IsomorphicCodeRunner(),
|
|
1094
|
+
referencedProjects: this.#loadedProjects,
|
|
970
1095
|
waitEvent: async (event) => {
|
|
971
1096
|
return new Promise((resolve, reject) => {
|
|
972
1097
|
this.#emitter.once(`userEvent:${event}`).then(resolve).catch(reject);
|
|
@@ -986,6 +1111,7 @@ export class GraphProcessor {
|
|
|
986
1111
|
if (useAsGraphPartialOutput && this.#executor && this.#parent) {
|
|
987
1112
|
const executorNode = this.#parent.#nodesById[this.#executor.nodeId];
|
|
988
1113
|
if (executorNode) {
|
|
1114
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
989
1115
|
this.#emitter.emit('partialOutput', {
|
|
990
1116
|
index: this.#executor.index,
|
|
991
1117
|
node: executorNode,
|
|
@@ -1000,6 +1126,7 @@ export class GraphProcessor {
|
|
|
1000
1126
|
getGlobal: (id) => this.#globals.get(id),
|
|
1001
1127
|
setGlobal: (id, value) => {
|
|
1002
1128
|
this.#globals.set(id, value);
|
|
1129
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1003
1130
|
this.#emitter.emit('globalSet', { id, value, processId });
|
|
1004
1131
|
},
|
|
1005
1132
|
waitForGlobal: async (id) => {
|
|
@@ -1009,8 +1136,8 @@ export class GraphProcessor {
|
|
|
1009
1136
|
await this.getRootProcessor().#emitter.once(`globalSet:${id}`);
|
|
1010
1137
|
return this.#globals.get(id);
|
|
1011
1138
|
},
|
|
1012
|
-
createSubProcessor: (subGraphId, { signal } = {}) => {
|
|
1013
|
-
const processor = new GraphProcessor(this.#project, subGraphId, this.#registry);
|
|
1139
|
+
createSubProcessor: (subGraphId, { signal, project } = {}) => {
|
|
1140
|
+
const processor = new GraphProcessor(project ?? this.#project, subGraphId, this.#registry);
|
|
1014
1141
|
processor.executor = this.executor;
|
|
1015
1142
|
processor.#isSubProcessor = true;
|
|
1016
1143
|
processor.#executionCache = this.#executionCache;
|
|
@@ -1033,6 +1160,7 @@ export class GraphProcessor {
|
|
|
1033
1160
|
processor.on('graphStart', (e) => this.#emitter.emit('graphStart', e));
|
|
1034
1161
|
processor.on('graphFinish', (e) => this.#emitter.emit('graphFinish', e));
|
|
1035
1162
|
processor.on('globalSet', (e) => this.#emitter.emit('globalSet', e));
|
|
1163
|
+
processor.on('newAbortController', (e) => this.#emitter.emit('newAbortController', e));
|
|
1036
1164
|
processor.on('pause', () => {
|
|
1037
1165
|
if (!this.#isPaused) {
|
|
1038
1166
|
this.pause();
|
|
@@ -1045,26 +1173,59 @@ export class GraphProcessor {
|
|
|
1045
1173
|
});
|
|
1046
1174
|
processor.onAny((event, data) => {
|
|
1047
1175
|
if (event.startsWith('globalSet:')) {
|
|
1176
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1048
1177
|
this.#emitter.emit(event, data);
|
|
1049
1178
|
}
|
|
1050
1179
|
});
|
|
1051
1180
|
this.#subprocessors.add(processor);
|
|
1052
1181
|
if (signal) {
|
|
1053
|
-
signal.addEventListener('abort', () =>
|
|
1182
|
+
signal.addEventListener('abort', () => {
|
|
1183
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1184
|
+
processor.abort();
|
|
1185
|
+
});
|
|
1054
1186
|
}
|
|
1055
1187
|
// If parent is aborted, abort subgraph with error (it's fine, success state is on the parent)
|
|
1056
|
-
this.#abortController.signal.addEventListener('abort', () =>
|
|
1188
|
+
this.#abortController.signal.addEventListener('abort', () => {
|
|
1189
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1190
|
+
processor.abort();
|
|
1191
|
+
});
|
|
1057
1192
|
this.on('pause', () => processor.pause());
|
|
1058
1193
|
this.on('resume', () => processor.resume());
|
|
1059
1194
|
return processor;
|
|
1060
1195
|
},
|
|
1061
1196
|
trace: (message) => {
|
|
1062
|
-
this.#
|
|
1197
|
+
this.#emitTraceEvent(message);
|
|
1063
1198
|
},
|
|
1064
1199
|
abortGraph: (error) => {
|
|
1200
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1065
1201
|
this.abort(error === undefined, error);
|
|
1066
1202
|
},
|
|
1067
1203
|
getPluginConfig: (name) => getPluginConfig(plugin, this.#context.settings, name),
|
|
1204
|
+
requestUserInput: async (inputStrings, renderingType) => {
|
|
1205
|
+
const results = await new Promise((resolve, reject) => {
|
|
1206
|
+
this.#pendingUserInputs[node.id] = {
|
|
1207
|
+
resolve,
|
|
1208
|
+
reject,
|
|
1209
|
+
};
|
|
1210
|
+
this.#abortController.signal.addEventListener('abort', () => {
|
|
1211
|
+
delete this.#pendingUserInputs[node.id];
|
|
1212
|
+
reject(new Error('Processing aborted'));
|
|
1213
|
+
});
|
|
1214
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1215
|
+
this.#emitter.emit('userInput', {
|
|
1216
|
+
node,
|
|
1217
|
+
inputStrings,
|
|
1218
|
+
inputs: inputValues,
|
|
1219
|
+
renderingType,
|
|
1220
|
+
callback: (results) => {
|
|
1221
|
+
resolve(results);
|
|
1222
|
+
delete this.#pendingUserInputs[node.id];
|
|
1223
|
+
},
|
|
1224
|
+
processId,
|
|
1225
|
+
});
|
|
1226
|
+
});
|
|
1227
|
+
return results;
|
|
1228
|
+
},
|
|
1068
1229
|
};
|
|
1069
1230
|
await this.#waitUntilUnpaused();
|
|
1070
1231
|
const results = await instance.process(inputValues, context);
|
|
@@ -1077,11 +1238,20 @@ export class GraphProcessor {
|
|
|
1077
1238
|
}
|
|
1078
1239
|
#excludedDueToControlFlow(node, inputValues, processId, typeOfExclusion = undefined) {
|
|
1079
1240
|
if (node.disabled) {
|
|
1080
|
-
this.#
|
|
1241
|
+
this.#emitTraceEvent(`Excluding node ${node.title} because it's disabled`);
|
|
1081
1242
|
this.#visitedNodes.add(node.id);
|
|
1082
1243
|
this.#markAsExcluded(node, processId, inputValues, 'disabled');
|
|
1083
1244
|
return true;
|
|
1084
1245
|
}
|
|
1246
|
+
if (node.isConditional && typeOfExclusion === undefined) {
|
|
1247
|
+
const ifValue = coerceTypeOptional(inputValues[IF_PORT.id], 'boolean');
|
|
1248
|
+
if (ifValue === false) {
|
|
1249
|
+
this.#emitTraceEvent(`Excluding node ${node.title} because if port is false`);
|
|
1250
|
+
this.#visitedNodes.add(node.id);
|
|
1251
|
+
this.#markAsExcluded(node, processId, inputValues, 'if port is false');
|
|
1252
|
+
return true;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1085
1255
|
const inputsWithValues = entries(inputValues);
|
|
1086
1256
|
const controlFlowExcludedValues = inputsWithValues.filter(([, value]) => value &&
|
|
1087
1257
|
getScalarTypeOf(value.type) === 'control-flow-excluded' &&
|
|
@@ -1100,7 +1270,7 @@ export class GraphProcessor {
|
|
|
1100
1270
|
if (inputIsExcludedValue && !allowedToConsumedExcludedValue) {
|
|
1101
1271
|
if (!isWaitingForLoop) {
|
|
1102
1272
|
if (inputIsExcludedValue) {
|
|
1103
|
-
this.#
|
|
1273
|
+
this.#emitTraceEvent(`Excluding node ${node.title} because of control flow. Input is has excluded value: ${controlFlowExcludedValues[0]?.[0]}`);
|
|
1104
1274
|
}
|
|
1105
1275
|
this.#visitedNodes.add(node.id);
|
|
1106
1276
|
this.#markAsExcluded(node, processId, inputValues, 'input is excluded value');
|
|
@@ -1119,6 +1289,7 @@ export class GraphProcessor {
|
|
|
1119
1289
|
outputs['break'] = { type: 'control-flow-excluded', value: 'loop-not-broken' };
|
|
1120
1290
|
}
|
|
1121
1291
|
this.#nodeResults.set(node.id, outputs);
|
|
1292
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1122
1293
|
this.#emitter.emit('nodeExcluded', {
|
|
1123
1294
|
node,
|
|
1124
1295
|
processId,
|