@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
|
@@ -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(
|
|
39
|
+
async process(context) {
|
|
40
40
|
const { datasetProvider } = context;
|
|
41
41
|
if (datasetProvider == null) {
|
|
42
42
|
throw new Error('datasetProvider is required');
|
|
@@ -126,8 +126,9 @@ export class HttpCallNodeImpl extends NodeImpl {
|
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
type: 'toggle',
|
|
129
|
-
label: '
|
|
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
|
|
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(
|
|
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 =
|
|
63
|
+
const inputNames = extractInterpolationVariables(this.data.promptText);
|
|
57
64
|
inputs = [
|
|
58
65
|
...inputs,
|
|
59
66
|
...(inputNames?.map((inputName) => {
|
|
60
67
|
return {
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
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');
|