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