@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.
Files changed (105) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/bundle.cjs +3993 -943
  3. package/dist/cjs/bundle.cjs.map +4 -4
  4. package/dist/esm/api/createProcessor.js +8 -17
  5. package/dist/esm/api/looseDataValue.js +16 -0
  6. package/dist/esm/exports.js +2 -0
  7. package/dist/esm/integrations/CodeRunner.js +36 -0
  8. package/dist/esm/integrations/GptTokenizerTokenizer.js +7 -4
  9. package/dist/esm/integrations/openai/OpenAIEmbeddingGenerator.js +1 -1
  10. package/dist/esm/model/DataValue.js +14 -2
  11. package/dist/esm/model/GraphProcessor.js +275 -104
  12. package/dist/esm/model/NodeBase.js +11 -1
  13. package/dist/esm/model/NodeImpl.js +8 -0
  14. package/dist/esm/model/Nodes.js +28 -4
  15. package/dist/esm/model/ProjectReferenceLoader.js +1 -0
  16. package/dist/esm/model/nodes/AssembleMessageNode.js +12 -2
  17. package/dist/esm/model/nodes/AssemblePromptNode.js +22 -0
  18. package/dist/esm/model/nodes/CallGraphNode.js +3 -4
  19. package/dist/esm/model/nodes/ChatLoopNode.js +150 -0
  20. package/dist/esm/model/nodes/ChatNode.js +7 -934
  21. package/dist/esm/model/nodes/ChatNodeBase.js +1275 -0
  22. package/dist/esm/model/nodes/ChunkNode.js +2 -2
  23. package/dist/esm/model/nodes/CodeNode.js +40 -4
  24. package/dist/esm/model/nodes/CronNode.js +248 -0
  25. package/dist/esm/model/nodes/DelegateFunctionCallNode.js +37 -12
  26. package/dist/esm/model/nodes/DestructureNode.js +1 -1
  27. package/dist/esm/model/nodes/DocumentNode.js +183 -0
  28. package/dist/esm/model/nodes/ExtractJsonNode.js +4 -4
  29. package/dist/esm/model/nodes/ExtractRegexNode.js +10 -11
  30. package/dist/esm/model/nodes/GetEmbeddingNode.js +1 -1
  31. package/dist/esm/model/nodes/HttpCallNode.js +3 -1
  32. package/dist/esm/model/nodes/IfNode.js +5 -0
  33. package/dist/esm/model/nodes/LoopUntilNode.js +214 -0
  34. package/dist/esm/model/nodes/PromptNode.js +29 -6
  35. package/dist/esm/model/nodes/ReadAllFilesNode.js +210 -0
  36. package/dist/esm/model/nodes/ReadDirectoryNode.js +31 -25
  37. package/dist/esm/model/nodes/ReferencedGraphAliasNode.js +199 -0
  38. package/dist/esm/model/nodes/TextNode.js +9 -4
  39. package/dist/esm/model/nodes/ToMarkdownTableNode.js +119 -0
  40. package/dist/esm/model/nodes/ToTreeNode.js +133 -0
  41. package/dist/esm/model/nodes/{GptFunctionNode.js → ToolNode.js} +10 -10
  42. package/dist/esm/model/nodes/UserInputNode.js +10 -12
  43. package/dist/esm/plugins/aidon/nodes/ChatAidonNode.js +2 -2
  44. package/dist/esm/plugins/anthropic/anthropic.js +29 -10
  45. package/dist/esm/plugins/anthropic/fetchEventSource.js +3 -2
  46. package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +267 -147
  47. package/dist/esm/plugins/anthropic/plugin.js +9 -1
  48. package/dist/esm/plugins/gentrace/plugin.js +6 -6
  49. package/dist/esm/plugins/google/google.js +113 -5
  50. package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +211 -54
  51. package/dist/esm/plugins/google/plugin.js +13 -6
  52. package/dist/esm/plugins/openai/nodes/RunThreadNode.js +2 -2
  53. package/dist/esm/recording/ExecutionRecorder.js +5 -1
  54. package/dist/esm/utils/chatMessageToOpenAIChatCompletionMessage.js +15 -2
  55. package/dist/esm/utils/coerceType.js +1 -1
  56. package/dist/esm/utils/fetchEventSource.js +1 -1
  57. package/dist/esm/utils/interpolation.js +108 -3
  58. package/dist/esm/utils/openai.js +106 -50
  59. package/dist/esm/utils/paths.js +80 -0
  60. package/dist/esm/utils/serialization/serialization_v4.js +5 -0
  61. package/dist/types/api/createProcessor.d.ts +11 -5
  62. package/dist/types/api/looseDataValue.d.ts +4 -0
  63. package/dist/types/api/streaming.d.ts +1 -1
  64. package/dist/types/exports.d.ts +2 -0
  65. package/dist/types/integrations/CodeRunner.d.ts +18 -0
  66. package/dist/types/model/DataValue.d.ts +29 -6
  67. package/dist/types/model/EditorDefinition.d.ts +6 -1
  68. package/dist/types/model/GraphProcessor.d.ts +14 -7
  69. package/dist/types/model/NodeBase.d.ts +4 -0
  70. package/dist/types/model/NodeImpl.d.ts +5 -4
  71. package/dist/types/model/Nodes.d.ts +12 -4
  72. package/dist/types/model/ProcessContext.d.ts +16 -1
  73. package/dist/types/model/Project.d.ts +19 -7
  74. package/dist/types/model/ProjectReferenceLoader.d.ts +5 -0
  75. package/dist/types/model/RivetPlugin.d.ts +6 -0
  76. package/dist/types/model/RivetUIContext.d.ts +5 -1
  77. package/dist/types/model/Settings.d.ts +1 -0
  78. package/dist/types/model/nodes/AssemblePromptNode.d.ts +4 -1
  79. package/dist/types/model/nodes/ChatLoopNode.d.ts +21 -0
  80. package/dist/types/model/nodes/ChatNode.d.ts +2 -62
  81. package/dist/types/model/nodes/ChatNodeBase.d.ts +85 -0
  82. package/dist/types/model/nodes/CodeNode.d.ts +8 -2
  83. package/dist/types/model/nodes/CronNode.d.ts +34 -0
  84. package/dist/types/model/nodes/DelegateFunctionCallNode.d.ts +1 -0
  85. package/dist/types/model/nodes/DocumentNode.d.ts +28 -0
  86. package/dist/types/model/nodes/LoopUntilNode.d.ts +32 -0
  87. package/dist/types/model/nodes/PromptNode.d.ts +2 -0
  88. package/dist/types/model/nodes/ReadAllFilesNode.d.ts +30 -0
  89. package/dist/types/model/nodes/ReadDirectoryNode.d.ts +1 -1
  90. package/dist/types/model/nodes/ReferencedGraphAliasNode.d.ts +31 -0
  91. package/dist/types/model/nodes/ToMarkdownTableNode.d.ts +19 -0
  92. package/dist/types/model/nodes/ToTreeNode.d.ts +21 -0
  93. package/dist/types/model/nodes/UserInputNode.d.ts +2 -3
  94. package/dist/types/plugins/anthropic/anthropic.d.ts +94 -13
  95. package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +7 -2
  96. package/dist/types/plugins/google/google.d.ts +93 -18
  97. package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +3 -2
  98. package/dist/types/recording/RecordedEvents.d.ts +2 -0
  99. package/dist/types/utils/base64.d.ts +1 -1
  100. package/dist/types/utils/chatMessageToOpenAIChatCompletionMessage.d.ts +3 -1
  101. package/dist/types/utils/interpolation.d.ts +3 -0
  102. package/dist/types/utils/openai.d.ts +127 -21
  103. package/dist/types/utils/paths.d.ts +8 -0
  104. package/package.json +15 -11
  105. /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 ${graphId} (connection from ${this.#nodesById[conn.outputNodeId]?.title})`);
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 ${graphId} (connection to ${this.#nodesById[conn.inputNodeId]?.title}) `);
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].getInputDefinitions(connectionsForNode, this.#nodesById, this.#project);
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
- this.#scc = this.#tarjanSCC();
157
- this.#nodesNotInCycle = this.#scc.filter((cycle) => cycle.length === 1).flat();
158
- this.setExternalFunction('echo', async (value) => ({ type: 'any', value }));
159
- this.#emitter.on('globalSet', ({ id, value }) => {
160
- this.#emitter.emit(`globalSet:${id}`, value);
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
- // .with(undefined, () => {})
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.#nodeResults = new Map();
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
- const error = this.#abortError ??
476
- Error(`Graph ${this.#graph.metadata.name} (${this.#graph.metadata.id}) failed to process due to errors in nodes: ${erroredNodes
477
- .map(([nodeId, error]) => `${this.#nodesById[nodeId].title} (${nodeId}): ${error}`)
478
- .join(', ')}`);
479
- this.#emitter.emit('graphError', { graph: this.#graph, error });
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.#emitter.emit('trace', `Node ${node.title} has required inputs nodes: ${inputNodes.map((n) => n.title).join(', ')}`);
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.#emitter.emit('trace', `Fetching required data for node ${inputNode.title} (${inputNode.id})`);
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.#emitter.emit('trace', `Node ${node.title} is ignored`);
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.#emitter.emit('trace', `Node ${node.title} is already being processed`);
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.#emitter.emit('trace', `Node ${node.title} has already been processed`);
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.#emitter.emit('trace', `Node ${node.title} has already errored`);
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.#emitter.emit('trace', `Node ${node.title} has errored input node ${inputNode.title}`);
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.#emitter.emit('trace', `Node ${node.title} has required inputs nodes: ${inputNodes.map((n) => n.title).join(', ')}`);
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.#emitter.emit('trace', `Node ${node.title} is excluded due to control flow`);
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.#emitter.emit('trace', `Node ${node.title} is waiting for input node ${waitingForInputNode}`);
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.#emitter.emit('trace', `Node ${node.title} is part of a race that was completed`);
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.#emitter.emit('trace', `Finished processing node ${node.title} (${node.id})`);
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 = !(breakValue?.type === 'control-flow-excluded' && breakValue?.value === 'loop-not-broken') ??
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.#emitter.emit('trace', `Loop controller ${node.title} did not break, so we're looping again`);
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.#emitter.emit('trace', `Clearing cycle node ${cycleNode.title} (${cycleNode.id})`);
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.#emitter.emit('trace', `Aborting node ${nodeId} because other race branch won`);
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.#emitter.emit('trace', `Trying to run output node from ${node.title}: ${outputNode.title} (${outputNode.id})`);
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 (this.#isNodeOfType('userInput', node)) {
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) => this.#emitter.emit('partialOutput', { node, outputs: partialOutputs, index, processId }));
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) => this.#emitter.emit('partialOutput', { node, outputs: partialOutputs, index, processId }));
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 Error(errors.join('\n'));
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) => this.#emitter.emit('partialOutput', { node, outputs: partialOutputs, index, processId }));
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.#emitter.emit('trace', `Node ${node.title} (${node.id}-${processId}) errored: ${error.stack}`);
925
- this.#erroredNodes.set(node.id, error.toString());
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) => this.#emitter.emit('error', { 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', () => processor.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', () => processor.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.#emitter.emit('trace', message);
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.#emitter.emit('trace', `Excluding node ${node.title} because it's disabled`);
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.#emitter.emit('trace', `Excluding node ${node.title} because of control flow. Input is has excluded value: ${controlFlowExcludedValues[0]?.[0]}`);
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,