@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.
Files changed (129) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/bundle.cjs +4512 -1240
  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/DatasetProvider.js +1 -1
  9. package/dist/esm/integrations/GptTokenizerTokenizer.js +7 -4
  10. package/dist/esm/integrations/openai/OpenAIEmbeddingGenerator.js +1 -1
  11. package/dist/esm/model/DataValue.js +14 -2
  12. package/dist/esm/model/GraphProcessor.js +276 -107
  13. package/dist/esm/model/NodeBase.js +11 -1
  14. package/dist/esm/model/NodeImpl.js +8 -0
  15. package/dist/esm/model/Nodes.js +31 -4
  16. package/dist/esm/model/ProjectReferenceLoader.js +1 -0
  17. package/dist/esm/model/nodes/AssembleMessageNode.js +12 -2
  18. package/dist/esm/model/nodes/AssemblePromptNode.js +22 -0
  19. package/dist/esm/model/nodes/CallGraphNode.js +3 -4
  20. package/dist/esm/model/nodes/ChatLoopNode.js +150 -0
  21. package/dist/esm/model/nodes/ChatNode.js +7 -934
  22. package/dist/esm/model/nodes/ChatNodeBase.js +1277 -0
  23. package/dist/esm/model/nodes/ChunkNode.js +2 -2
  24. package/dist/esm/model/nodes/CodeNode.js +40 -5
  25. package/dist/esm/model/nodes/CronNode.js +248 -0
  26. package/dist/esm/model/nodes/DelegateFunctionCallNode.js +37 -12
  27. package/dist/esm/model/nodes/DestructureNode.js +1 -1
  28. package/dist/esm/model/nodes/DocumentNode.js +183 -0
  29. package/dist/esm/model/nodes/ExtractJsonNode.js +4 -4
  30. package/dist/esm/model/nodes/ExtractRegexNode.js +10 -11
  31. package/dist/esm/model/nodes/GetAllDatasetsNode.js +1 -1
  32. package/dist/esm/model/nodes/GetEmbeddingNode.js +1 -1
  33. package/dist/esm/model/nodes/HttpCallNode.js +3 -1
  34. package/dist/esm/model/nodes/IfNode.js +5 -0
  35. package/dist/esm/model/nodes/LoopControllerNode.js +1 -1
  36. package/dist/esm/model/nodes/LoopUntilNode.js +214 -0
  37. package/dist/esm/model/nodes/ObjectNode.js +1 -1
  38. package/dist/esm/model/nodes/PromptNode.js +29 -6
  39. package/dist/esm/model/nodes/RaceInputsNode.js +1 -2
  40. package/dist/esm/model/nodes/ReadAllFilesNode.js +210 -0
  41. package/dist/esm/model/nodes/ReadDirectoryNode.js +31 -25
  42. package/dist/esm/model/nodes/ReferencedGraphAliasNode.js +199 -0
  43. package/dist/esm/model/nodes/ReplaceDatasetNode.js +1 -1
  44. package/dist/esm/model/nodes/SliceNode.js +0 -1
  45. package/dist/esm/model/nodes/SplitNode.js +1 -1
  46. package/dist/esm/model/nodes/SubGraphNode.js +0 -1
  47. package/dist/esm/model/nodes/TextNode.js +9 -4
  48. package/dist/esm/model/nodes/ToMarkdownTableNode.js +119 -0
  49. package/dist/esm/model/nodes/ToTreeNode.js +133 -0
  50. package/dist/esm/model/nodes/{GptFunctionNode.js → ToolNode.js} +10 -10
  51. package/dist/esm/model/nodes/UserInputNode.js +10 -12
  52. package/dist/esm/model/nodes/WriteFileNode.js +147 -0
  53. package/dist/esm/native/BrowserNativeApi.js +16 -1
  54. package/dist/esm/plugins/aidon/nodes/ChatAidonNode.js +5 -5
  55. package/dist/esm/plugins/anthropic/anthropic.js +29 -14
  56. package/dist/esm/plugins/anthropic/fetchEventSource.js +3 -2
  57. package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +264 -147
  58. package/dist/esm/plugins/anthropic/plugin.js +9 -1
  59. package/dist/esm/plugins/assemblyAi/LemurQaNode.js +1 -1
  60. package/dist/esm/plugins/assemblyAi/LemurSummaryNode.js +1 -1
  61. package/dist/esm/plugins/gentrace/plugin.js +6 -6
  62. package/dist/esm/plugins/google/google.js +120 -6
  63. package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +219 -56
  64. package/dist/esm/plugins/google/plugin.js +13 -6
  65. package/dist/esm/plugins/openai/nodes/RunThreadNode.js +2 -2
  66. package/dist/esm/plugins/openai/nodes/ThreadMessageNode.js +1 -1
  67. package/dist/esm/recording/ExecutionRecorder.js +59 -4
  68. package/dist/esm/utils/base64.js +13 -0
  69. package/dist/esm/utils/chatMessageToOpenAIChatCompletionMessage.js +15 -2
  70. package/dist/esm/utils/coerceType.js +4 -1
  71. package/dist/esm/utils/fetchEventSource.js +1 -1
  72. package/dist/esm/utils/interpolation.js +108 -3
  73. package/dist/esm/utils/openai.js +106 -50
  74. package/dist/esm/utils/paths.js +80 -0
  75. package/dist/esm/utils/serialization/serialization_v4.js +5 -0
  76. package/dist/types/api/createProcessor.d.ts +11 -5
  77. package/dist/types/api/looseDataValue.d.ts +4 -0
  78. package/dist/types/api/streaming.d.ts +1 -1
  79. package/dist/types/exports.d.ts +2 -0
  80. package/dist/types/integrations/CodeRunner.d.ts +18 -0
  81. package/dist/types/integrations/DatasetProvider.d.ts +1 -1
  82. package/dist/types/model/DataValue.d.ts +29 -6
  83. package/dist/types/model/EditorDefinition.d.ts +6 -1
  84. package/dist/types/model/GraphProcessor.d.ts +14 -7
  85. package/dist/types/model/NodeBase.d.ts +4 -0
  86. package/dist/types/model/NodeImpl.d.ts +5 -4
  87. package/dist/types/model/Nodes.d.ts +13 -4
  88. package/dist/types/model/ProcessContext.d.ts +16 -1
  89. package/dist/types/model/Project.d.ts +19 -7
  90. package/dist/types/model/ProjectReferenceLoader.d.ts +5 -0
  91. package/dist/types/model/RivetPlugin.d.ts +6 -0
  92. package/dist/types/model/RivetUIContext.d.ts +5 -1
  93. package/dist/types/model/Settings.d.ts +1 -0
  94. package/dist/types/model/nodes/AssemblePromptNode.d.ts +4 -1
  95. package/dist/types/model/nodes/ChatLoopNode.d.ts +21 -0
  96. package/dist/types/model/nodes/ChatNode.d.ts +2 -62
  97. package/dist/types/model/nodes/ChatNodeBase.d.ts +85 -0
  98. package/dist/types/model/nodes/CodeNode.d.ts +8 -2
  99. package/dist/types/model/nodes/CronNode.d.ts +34 -0
  100. package/dist/types/model/nodes/DelegateFunctionCallNode.d.ts +1 -0
  101. package/dist/types/model/nodes/DocumentNode.d.ts +28 -0
  102. package/dist/types/model/nodes/GetAllDatasetsNode.d.ts +2 -2
  103. package/dist/types/model/nodes/LoopUntilNode.d.ts +32 -0
  104. package/dist/types/model/nodes/ObjectNode.d.ts +2 -2
  105. package/dist/types/model/nodes/PromptNode.d.ts +2 -0
  106. package/dist/types/model/nodes/RaceInputsNode.d.ts +1 -2
  107. package/dist/types/model/nodes/ReadAllFilesNode.d.ts +30 -0
  108. package/dist/types/model/nodes/ReadDirectoryNode.d.ts +1 -1
  109. package/dist/types/model/nodes/ReferencedGraphAliasNode.d.ts +31 -0
  110. package/dist/types/model/nodes/SplitNode.d.ts +2 -2
  111. package/dist/types/model/nodes/ToMarkdownTableNode.d.ts +19 -0
  112. package/dist/types/model/nodes/ToTreeNode.d.ts +21 -0
  113. package/dist/types/model/nodes/UserInputNode.d.ts +2 -3
  114. package/dist/types/model/nodes/WriteFileNode.d.ts +23 -0
  115. package/dist/types/native/BrowserNativeApi.d.ts +8 -5
  116. package/dist/types/native/NativeApi.d.ts +12 -1
  117. package/dist/types/plugins/anthropic/anthropic.d.ts +94 -13
  118. package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +7 -2
  119. package/dist/types/plugins/google/google.d.ts +101 -18
  120. package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +3 -2
  121. package/dist/types/recording/RecordedEvents.d.ts +3 -0
  122. package/dist/types/utils/base64.d.ts +2 -1
  123. package/dist/types/utils/chatMessageToOpenAIChatCompletionMessage.d.ts +3 -1
  124. package/dist/types/utils/interpolation.d.ts +3 -0
  125. package/dist/types/utils/openai.d.ts +127 -21
  126. package/dist/types/utils/paths.d.ts +8 -0
  127. package/dist/types/utils/serialization/serialization_v3.d.ts +1 -0
  128. package/package.json +15 -11
  129. /package/dist/types/model/nodes/{GptFunctionNode.d.ts → ToolNode.d.ts} +0 -0
@@ -157,23 +157,22 @@ export class ExtractRegexNodeImpl extends NodeImpl {
157
157
  },
158
158
  };
159
159
  }
160
- const output = {
161
- ['succeeded']: {
162
- type: 'boolean',
163
- value: true,
164
- },
165
- ['failed']: {
166
- type: 'boolean',
167
- value: false,
168
- },
169
- };
170
- output['matches'] = outputArray;
160
+ const output = {};
171
161
  for (let i = 1; i < firstMatch.length; i++) {
172
162
  output[`output${i}`] = {
173
163
  type: 'string',
174
164
  value: firstMatch[i],
175
165
  };
176
166
  }
167
+ output['matches'] = outputArray;
168
+ output['succeeded'] = {
169
+ type: 'boolean',
170
+ value: true,
171
+ };
172
+ output['failed'] = {
173
+ type: 'boolean',
174
+ value: false,
175
+ };
177
176
  return output;
178
177
  }
179
178
  }
@@ -36,7 +36,7 @@ export class GetAllDatasetsNodeImpl extends NodeImpl {
36
36
  getEditors() {
37
37
  return [];
38
38
  }
39
- async process(inputs, context) {
39
+ async process(context) {
40
40
  const { datasetProvider } = context;
41
41
  if (datasetProvider == null) {
42
42
  throw new Error('datasetProvider is required');
@@ -19,7 +19,7 @@ export class GetEmbeddingNodeImpl extends NodeImpl {
19
19
  integration: 'openai',
20
20
  useIntegrationInput: false,
21
21
  model: undefined,
22
- dimensions: undefined
22
+ dimensions: undefined,
23
23
  },
24
24
  };
25
25
  }
@@ -126,8 +126,9 @@ export class HttpCallNodeImpl extends NodeImpl {
126
126
  },
127
127
  {
128
128
  type: 'toggle',
129
- label: 'Whether response body is expected to be a binary',
129
+ label: 'Binary Output',
130
130
  dataKey: 'isBinaryOutput',
131
+ helperMessage: 'Toggle on if the response is expected to be binary data',
131
132
  },
132
133
  {
133
134
  type: 'toggle',
@@ -158,6 +159,7 @@ export class HttpCallNodeImpl extends NodeImpl {
158
159
  async process(inputs, context) {
159
160
  const method = getInputOrData(this.data, inputs, 'method', 'string');
160
161
  const url = getInputOrData(this.data, inputs, 'url', 'string');
162
+ // TODO: Use URL.canParse when we drop support for Node 18
161
163
  try {
162
164
  new URL(url);
163
165
  }
@@ -106,6 +106,11 @@ export class IfNodeImpl extends NodeImpl {
106
106
  return isFalse;
107
107
  }
108
108
  }
109
+ if (ifValue.type === 'object' || ifValue.type === 'any') {
110
+ if (!ifValue.value) {
111
+ return isFalse;
112
+ }
113
+ }
109
114
  if (ifValue.type.endsWith('[]')) {
110
115
  const value = ifValue;
111
116
  if (!value.value || value.value.length === 0) {
@@ -7,7 +7,7 @@ import { coerceType } from '../../utils/coerceType.js';
7
7
  import {} from '../ProcessContext.js';
8
8
  import { dedent } from 'ts-dedent';
9
9
  import {} from '../../index.js';
10
- import { entries, values } from '../../utils/typeSafety.js';
10
+ import { entries } from '../../utils/typeSafety.js';
11
11
  export class LoopControllerNodeImpl extends NodeImpl {
12
12
  static create() {
13
13
  const chartNode = {
@@ -0,0 +1,214 @@
1
+ import {} from '../NodeBase.js';
2
+ import { NodeImpl } from '../NodeImpl.js';
3
+ import { nodeDefinition } from '../NodeDefinition.js';
4
+ import {} from '../GraphProcessor.js';
5
+ import {} from '../NodeGraph.js';
6
+ import { nanoid } from 'nanoid/non-secure';
7
+ import {} from '../Project.js';
8
+ import {} from './GraphInputNode.js';
9
+ import {} from './GraphOutputNode.js';
10
+ import {} from '../DataValue.js';
11
+ import {} from '../ProcessContext.js';
12
+ import {} from '../EditorDefinition.js';
13
+ import { dedent } from 'ts-dedent';
14
+ export class LoopUntilNodeImpl extends NodeImpl {
15
+ static create() {
16
+ const chartNode = {
17
+ type: 'loopUntil',
18
+ title: 'Loop Until',
19
+ id: nanoid(),
20
+ visualData: {
21
+ x: 0,
22
+ y: 0,
23
+ width: 200,
24
+ },
25
+ data: {
26
+ targetGraph: undefined,
27
+ conditionType: 'allOutputsSet',
28
+ },
29
+ };
30
+ return chartNode;
31
+ }
32
+ getInputDefinitions(_connections, _nodes, project) {
33
+ const inputs = [];
34
+ // Get inputs from the target graph
35
+ const graph = project.graphs[this.data.targetGraph ?? ''];
36
+ if (graph) {
37
+ const inputNodes = graph.nodes.filter((node) => node.type === 'graphInput');
38
+ const inputIds = [...new Set(inputNodes.map((node) => node.data.id))].sort();
39
+ inputIds.forEach((id) => {
40
+ const inputNode = inputNodes.find((node) => node.data.id === id);
41
+ inputs.push({
42
+ id: id,
43
+ title: id,
44
+ dataType: inputNode.data.dataType,
45
+ });
46
+ });
47
+ }
48
+ return inputs;
49
+ }
50
+ getOutputDefinitions(_connections, _nodes, project) {
51
+ const outputs = [];
52
+ // Get outputs from the target graph
53
+ const graph = project.graphs[this.data.targetGraph ?? ''];
54
+ if (graph) {
55
+ const outputNodes = graph.nodes.filter((node) => node.type === 'graphOutput');
56
+ const outputIds = [...new Set(outputNodes.map((node) => node.data.id))].sort();
57
+ outputIds.forEach((id) => {
58
+ const outputNode = outputNodes.find((node) => node.data.id === id);
59
+ outputs.push({
60
+ id: id,
61
+ title: id,
62
+ dataType: outputNode.data.dataType,
63
+ });
64
+ });
65
+ }
66
+ // Add standard loop outputs
67
+ outputs.push({
68
+ id: 'iteration',
69
+ title: 'Iterations',
70
+ dataType: 'number',
71
+ description: 'The number of iterations completed.',
72
+ }, {
73
+ id: 'completed',
74
+ title: 'Completed',
75
+ dataType: 'boolean',
76
+ description: 'True when the loop has completed.',
77
+ });
78
+ return outputs;
79
+ }
80
+ getEditors(context) {
81
+ const definitions = [
82
+ {
83
+ type: 'graphSelector',
84
+ label: 'Target Graph',
85
+ dataKey: 'targetGraph',
86
+ },
87
+ {
88
+ type: 'dropdown',
89
+ dataKey: 'conditionType',
90
+ label: 'Stop Condition',
91
+ options: [
92
+ { label: 'All Outputs Set', value: 'allOutputsSet' },
93
+ { label: 'Input Equals Value', value: 'inputEqual' },
94
+ ],
95
+ helperMessage: 'The condition that will stop the loop',
96
+ },
97
+ {
98
+ type: 'number',
99
+ dataKey: 'maxIterations',
100
+ label: 'Max Iterations',
101
+ helperMessage: 'Maximum number of iterations (optional, leave empty for unlimited)',
102
+ allowEmpty: true,
103
+ },
104
+ ];
105
+ if (this.data.conditionType === 'inputEqual') {
106
+ definitions.push({
107
+ type: 'string',
108
+ dataKey: 'inputToCheck',
109
+ label: 'Input to Check',
110
+ helperMessage: 'The name of the input to compare',
111
+ }, {
112
+ type: 'string',
113
+ dataKey: 'targetValue',
114
+ label: 'Target Value',
115
+ helperMessage: 'The value to compare against',
116
+ });
117
+ }
118
+ // Add dynamic editors for graph inputs
119
+ if (this.data.targetGraph) {
120
+ const graph = context.project.graphs[this.data.targetGraph];
121
+ if (graph) {
122
+ const inputNodes = graph.nodes.filter((node) => node.type === 'graphInput');
123
+ const inputIds = [...new Set(inputNodes.map((node) => node.data.id))].sort();
124
+ for (const inputId of inputIds) {
125
+ const inputNode = inputNodes.find((node) => node.data.id === inputId);
126
+ definitions.push({
127
+ type: 'dynamic',
128
+ dataKey: 'inputData',
129
+ dynamicDataKey: inputNode.data.id,
130
+ dataType: inputNode.data.dataType,
131
+ label: inputNode.data.id,
132
+ editor: inputNode.data.editor ?? 'auto',
133
+ });
134
+ }
135
+ }
136
+ }
137
+ return definitions;
138
+ }
139
+ static getUIData() {
140
+ return {
141
+ infoBoxBody: dedent `
142
+ Executes a subgraph in a loop until a condition is met. Each iteration's outputs become
143
+ the inputs for the next iteration. Supports different stopping conditions and optional
144
+ maximum iterations.
145
+ `,
146
+ infoBoxTitle: 'Loop Until Node',
147
+ contextMenuTitle: 'Loop Until',
148
+ group: ['Logic'],
149
+ };
150
+ }
151
+ getBody(context) {
152
+ if (!this.data.targetGraph) {
153
+ return 'No target graph selected';
154
+ }
155
+ const graphName = context.project.graphs[this.data.targetGraph]?.metadata?.name ?? 'Unknown Graph';
156
+ const condition = this.data.conditionType === 'allOutputsSet'
157
+ ? 'all outputs are set'
158
+ : `${this.data.inputToCheck} equals ${this.data.targetValue}`;
159
+ const maxIterations = this.data.maxIterations ? `\nMax iterations: ${this.data.maxIterations}` : '';
160
+ return `Executes ${graphName}\nuntil ${condition}${maxIterations}`;
161
+ }
162
+ shouldBreak(outputs) {
163
+ if (this.data.conditionType === 'allOutputsSet') {
164
+ // Check if any output is control-flow-excluded
165
+ const anyInputIsExcluded = Object.values(outputs)
166
+ .filter((o) => o != null)
167
+ .some((output) => output.type === 'control-flow-excluded');
168
+ return !anyInputIsExcluded;
169
+ }
170
+ else if (this.data.conditionType === 'inputEqual' && this.data.inputToCheck && this.data.targetValue) {
171
+ const inputValue = outputs[this.data.inputToCheck];
172
+ return inputValue?.value?.toString() === this.data.targetValue;
173
+ }
174
+ return false;
175
+ }
176
+ async process(inputs, context) {
177
+ if (!this.data.targetGraph) {
178
+ throw new Error('No target graph selected');
179
+ }
180
+ let iteration = 0;
181
+ let currentInputs = { ...inputs };
182
+ // Add any default values from inputData
183
+ if (this.data.inputData) {
184
+ Object.entries(this.data.inputData).forEach(([key, value]) => {
185
+ if (currentInputs[key] === undefined) {
186
+ currentInputs[key] = value;
187
+ }
188
+ });
189
+ }
190
+ let lastOutputs = {};
191
+ while (!context.signal.aborted) {
192
+ // Check max iterations if set
193
+ if (this.data.maxIterations && iteration >= this.data.maxIterations) {
194
+ break;
195
+ }
196
+ const subprocessor = context.createSubProcessor(this.data.targetGraph, { signal: context.signal });
197
+ lastOutputs = await subprocessor.processGraph(context, currentInputs, context.contextValues);
198
+ iteration++;
199
+ // Check if the condition is met
200
+ if (this.shouldBreak(lastOutputs)) {
201
+ break;
202
+ }
203
+ context.onPartialOutputs?.(lastOutputs);
204
+ // Use outputs as inputs for next iteration
205
+ currentInputs = lastOutputs;
206
+ }
207
+ return {
208
+ ...lastOutputs,
209
+ ['iteration']: { type: 'number', value: iteration },
210
+ ['completed']: { type: 'boolean', value: true },
211
+ };
212
+ }
213
+ }
214
+ export const loopUntilNode = nodeDefinition(LoopUntilNodeImpl, 'Loop Until');
@@ -25,7 +25,7 @@ export class ObjectNodeImpl extends NodeImpl {
25
25
  };
26
26
  return chartNode;
27
27
  }
28
- getInputDefinitions(connections) {
28
+ getInputDefinitions() {
29
29
  // Extract inputs from text, everything like {{input}}
30
30
  const inputNames = [...new Set(this.chartNode.data.jsonTemplate.match(/\{\{([^}]+)\}\}/g))];
31
31
  return (inputNames?.map((inputName) => {
@@ -7,7 +7,7 @@ import { mapValues } from 'lodash-es';
7
7
  import { dedent } from 'ts-dedent';
8
8
  import { coerceType, coerceTypeOptional } from '../../utils/coerceType.js';
9
9
  import { getInputOrData } from '../../utils/index.js';
10
- import { interpolate } from '../../utils/interpolation.js';
10
+ import { interpolate, extractInterpolationVariables } from '../../utils/interpolation.js';
11
11
  import { match } from 'ts-pattern';
12
12
  export class PromptNodeImpl extends NodeImpl {
13
13
  static create() {
@@ -52,15 +52,21 @@ export class PromptNodeImpl extends NodeImpl {
52
52
  dataType: 'string',
53
53
  });
54
54
  }
55
+ if (this.data.useIsCacheBreakpointInput) {
56
+ inputs.push({
57
+ id: 'isCacheBreakpoint',
58
+ title: 'Is Cache Breakpoint',
59
+ dataType: 'boolean',
60
+ });
61
+ }
55
62
  // Extract inputs from promptText, everything like {{input}}
56
- const inputNames = [...new Set(this.chartNode.data.promptText.match(/\{\{([^}]+)\}\}/g))];
63
+ const inputNames = extractInterpolationVariables(this.data.promptText);
57
64
  inputs = [
58
65
  ...inputs,
59
66
  ...(inputNames?.map((inputName) => {
60
67
  return {
61
- // id and title should not have the {{ and }}
62
- id: inputName.slice(2, -2),
63
- title: inputName.slice(2, -2),
68
+ id: inputName,
69
+ title: inputName,
64
70
  dataType: 'string',
65
71
  required: false,
66
72
  };
@@ -87,6 +93,11 @@ export class PromptNodeImpl extends NodeImpl {
87
93
  }
88
94
  getEditors() {
89
95
  return [
96
+ {
97
+ type: 'custom',
98
+ customEditorId: 'PromptNodeAiAssist',
99
+ label: 'Generate Using AI',
100
+ },
90
101
  {
91
102
  type: 'dropdown',
92
103
  label: 'Type',
@@ -118,6 +129,13 @@ export class PromptNodeImpl extends NodeImpl {
118
129
  label: 'Compute Token Count',
119
130
  dataKey: 'computeTokenCount',
120
131
  },
132
+ {
133
+ type: 'toggle',
134
+ label: 'Is Cache Breakpoint',
135
+ dataKey: 'isCacheBreakpoint',
136
+ helperMessage: 'For Anthropic, marks this message as a cache breakpoint - this message and every message before it will be cached using Prompt Caching.',
137
+ useInputToggleDataKey: 'useIsCacheBreakpointInput',
138
+ },
121
139
  {
122
140
  type: 'code',
123
141
  label: 'Prompt Text',
@@ -132,7 +150,7 @@ export class PromptNodeImpl extends NodeImpl {
132
150
  {
133
151
  type: 'markdown',
134
152
  text: dedent `
135
- _${typeDisplay[this.data.type]}${this.data.name ? ` (${this.data.name})` : ''}_
153
+ _${typeDisplay[this.data.type]}${this.data.name ? ` (${this.data.name})` : ''}_ ${this.data.isCacheBreakpoint ? ' (Cache Breakpoint)' : ''}
136
154
  `,
137
155
  },
138
156
  {
@@ -161,6 +179,7 @@ export class PromptNodeImpl extends NodeImpl {
161
179
  const inputMap = mapValues(inputs, (input) => coerceType(input, 'string'));
162
180
  const outputValue = interpolate(this.chartNode.data.promptText, inputMap);
163
181
  const type = getInputOrData(this.data, inputs, 'type', 'string');
182
+ const isCacheBreakpoint = getInputOrData(this.data, inputs, 'isCacheBreakpoint', 'boolean');
164
183
  if (['assistant', 'system', 'user', 'function'].includes(type) === false) {
165
184
  throw new Error(`Invalid type: ${type}`);
166
185
  }
@@ -168,10 +187,12 @@ export class PromptNodeImpl extends NodeImpl {
168
187
  .with('system', (type) => ({
169
188
  type,
170
189
  message: outputValue,
190
+ isCacheBreakpoint,
171
191
  }))
172
192
  .with('user', (type) => ({
173
193
  type,
174
194
  message: outputValue,
195
+ isCacheBreakpoint,
175
196
  }))
176
197
  .with('assistant', (type) => {
177
198
  let functionCall = this.data.enableFunctionCall
@@ -190,12 +211,14 @@ export class PromptNodeImpl extends NodeImpl {
190
211
  message: outputValue,
191
212
  function_call: functionCall,
192
213
  function_calls: functionCall ? [functionCall] : undefined,
214
+ isCacheBreakpoint,
193
215
  };
194
216
  })
195
217
  .with('function', (type) => ({
196
218
  type,
197
219
  message: outputValue,
198
220
  name: getInputOrData(this.data, inputs, 'name', 'string'),
221
+ isCacheBreakpoint,
199
222
  }))
200
223
  .otherwise(() => {
201
224
  throw new Error(`Invalid chat-message type: ${type}`);
@@ -3,7 +3,6 @@ import { nanoid } from 'nanoid/non-secure';
3
3
  import { NodeImpl } from '../NodeImpl.js';
4
4
  import { nodeDefinition } from '../NodeDefinition.js';
5
5
  import {} from '../GraphProcessor.js';
6
- import {} from '../ProcessContext.js';
7
6
  import { dedent } from 'ts-dedent';
8
7
  import {} from '../../index.js';
9
8
  export class RaceInputsNodeImpl extends NodeImpl {
@@ -67,7 +66,7 @@ export class RaceInputsNodeImpl extends NodeImpl {
67
66
  group: ['Logic'],
68
67
  };
69
68
  }
70
- async process(inputs, context) {
69
+ async process(inputs) {
71
70
  // GraphProcessor handles most of the racing/aborting logic for us.
72
71
  const value = Object.entries(inputs).find(([key, value]) => key.startsWith('input') && value !== undefined && value.type !== 'control-flow-excluded');
73
72
  if (!value) {
@@ -0,0 +1,210 @@
1
+ import {} from '../NodeBase.js';
2
+ import {} from '../DataValue.js';
3
+ import { NodeImpl } from '../NodeImpl.js';
4
+ import { nodeDefinition } from '../NodeDefinition.js';
5
+ import { nanoid } from 'nanoid/non-secure';
6
+ import { getInputOrData } from '../../utils/index.js';
7
+ import {} from '../ProcessContext.js';
8
+ import { dedent } from 'ts-dedent';
9
+ import { uint8ArrayToBase64 } from '../../utils/base64.js';
10
+ export class ReadAllFilesNodeImpl extends NodeImpl {
11
+ static create() {
12
+ return {
13
+ id: nanoid(),
14
+ type: 'readAllFiles',
15
+ title: 'Read All Files',
16
+ visualData: { x: 0, y: 0, width: 250 },
17
+ data: {
18
+ path: '',
19
+ usePathInput: false,
20
+ recursive: false,
21
+ useRecursiveInput: false,
22
+ filterGlobs: [],
23
+ useFilterGlobsInput: false,
24
+ ignores: [],
25
+ useIgnoresInput: false,
26
+ asBinary: false,
27
+ errorOnMissingFile: false,
28
+ },
29
+ };
30
+ }
31
+ getInputDefinitions() {
32
+ const inputDefinitions = [];
33
+ if (this.chartNode.data.usePathInput) {
34
+ inputDefinitions.push({
35
+ id: 'path',
36
+ title: 'Path',
37
+ dataType: 'string',
38
+ required: true,
39
+ coerced: false,
40
+ });
41
+ }
42
+ if (this.chartNode.data.useRecursiveInput) {
43
+ inputDefinitions.push({
44
+ id: 'recursive',
45
+ title: 'Recursive',
46
+ dataType: 'boolean',
47
+ required: true,
48
+ coerced: false,
49
+ });
50
+ }
51
+ if (this.chartNode.data.useFilterGlobsInput) {
52
+ inputDefinitions.push({
53
+ id: 'filterGlobs',
54
+ title: 'Filter Globs',
55
+ dataType: 'string[]',
56
+ required: true,
57
+ coerced: false,
58
+ });
59
+ }
60
+ if (this.chartNode.data.useIgnoresInput) {
61
+ inputDefinitions.push({
62
+ id: 'ignores',
63
+ title: 'Ignores',
64
+ dataType: 'string[]',
65
+ required: true,
66
+ coerced: false,
67
+ });
68
+ }
69
+ return inputDefinitions;
70
+ }
71
+ getOutputDefinitions() {
72
+ return [
73
+ {
74
+ id: 'files',
75
+ title: 'Files',
76
+ dataType: 'object[]',
77
+ },
78
+ {
79
+ id: 'rootPath',
80
+ title: 'Root Path',
81
+ dataType: 'string',
82
+ },
83
+ ];
84
+ }
85
+ getEditors() {
86
+ return [
87
+ {
88
+ type: 'directoryBrowser',
89
+ label: 'Path',
90
+ dataKey: 'path',
91
+ useInputToggleDataKey: 'usePathInput',
92
+ },
93
+ {
94
+ type: 'toggle',
95
+ label: 'Recursive',
96
+ dataKey: 'recursive',
97
+ useInputToggleDataKey: 'useRecursiveInput',
98
+ },
99
+ {
100
+ type: 'stringList',
101
+ label: 'Filter Globs',
102
+ dataKey: 'filterGlobs',
103
+ useInputToggleDataKey: 'useFilterGlobsInput',
104
+ },
105
+ {
106
+ type: 'stringList',
107
+ label: 'Ignores',
108
+ dataKey: 'ignores',
109
+ useInputToggleDataKey: 'useIgnoresInput',
110
+ },
111
+ {
112
+ type: 'toggle',
113
+ label: 'Read as Binary',
114
+ dataKey: 'asBinary',
115
+ },
116
+ {
117
+ type: 'toggle',
118
+ label: 'Error on Missing File',
119
+ dataKey: 'errorOnMissingFile',
120
+ },
121
+ ];
122
+ }
123
+ getBody() {
124
+ return dedent `
125
+ ${this.data.asBinary ? 'Read as Binary' : 'Read as Text'}
126
+ Path: ${this.data.usePathInput ? '(Input)' : this.data.path}
127
+ Recursive: ${this.data.useRecursiveInput ? '(Input)' : this.data.recursive}
128
+ Filters: ${this.data.useFilterGlobsInput
129
+ ? '(Input)'
130
+ : this.data.filterGlobs.length > 0
131
+ ? this.data.filterGlobs.join(', ')
132
+ : 'None'}
133
+ `;
134
+ }
135
+ static getUIData() {
136
+ return {
137
+ infoBoxBody: dedent `
138
+ Reads all files in the specified directory and outputs an array of objects containing each file's path and contents.
139
+ Each object has a 'path' (string) and 'content' (string or binary) property.
140
+ `,
141
+ infoBoxTitle: 'Read All Files Node',
142
+ contextMenuTitle: 'Read All Files',
143
+ group: ['Input/Output'],
144
+ };
145
+ }
146
+ async process(inputs, context) {
147
+ const { nativeApi } = context;
148
+ if (nativeApi == null) {
149
+ throw new Error('This node requires a native API to run.');
150
+ }
151
+ const path = getInputOrData(this.chartNode.data, inputs, 'path');
152
+ const recursive = getInputOrData(this.data, inputs, 'recursive', 'boolean');
153
+ const filterGlobs = getInputOrData(this.data, inputs, 'filterGlobs', 'string[]');
154
+ const ignores = getInputOrData(this.data, inputs, 'ignores', 'string[]');
155
+ try {
156
+ // First read the directory
157
+ const filePaths = await nativeApi.readdir(path, undefined, {
158
+ recursive,
159
+ includeDirectories: false, // We only want files since we're reading contents
160
+ filterGlobs,
161
+ relative: true, // Always use relative paths in output
162
+ ignores,
163
+ });
164
+ // Then read each file
165
+ const filePromises = filePaths.map(async (filePath) => {
166
+ try {
167
+ if (this.data.asBinary) {
168
+ const content = await nativeApi.readBinaryFile(`${path}/${filePath}`);
169
+ const buffer = await content.arrayBuffer();
170
+ return {
171
+ path: filePath,
172
+ content: (await uint8ArrayToBase64(new Uint8Array(buffer))) ?? '',
173
+ };
174
+ }
175
+ else {
176
+ const content = await nativeApi.readTextFile(`${path}/${filePath}`, undefined);
177
+ return {
178
+ path: filePath,
179
+ content,
180
+ };
181
+ }
182
+ }
183
+ catch (err) {
184
+ if (this.chartNode.data.errorOnMissingFile) {
185
+ throw err;
186
+ }
187
+ return {
188
+ path: filePath,
189
+ content: this.data.asBinary ? new Uint8Array() : '',
190
+ };
191
+ }
192
+ });
193
+ const files = await Promise.all(filePromises);
194
+ return {
195
+ ['files']: { type: 'object[]', value: files },
196
+ ['rootPath']: { type: 'string', value: path },
197
+ };
198
+ }
199
+ catch (err) {
200
+ if (this.chartNode.data.errorOnMissingFile) {
201
+ throw err;
202
+ }
203
+ return {
204
+ ['files']: { type: 'object[]', value: [] },
205
+ ['rootPath']: { type: 'string', value: path },
206
+ };
207
+ }
208
+ }
209
+ }
210
+ export const readAllFilesNode = nodeDefinition(ReadAllFilesNodeImpl, 'Read All Files');