@alpic80/rivet-core 1.19.1-aidon.2 → 1.24.0-aidon.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/cjs/bundle.cjs +4187 -1020
- package/dist/cjs/bundle.cjs.map +4 -4
- package/dist/esm/api/createProcessor.js +8 -17
- package/dist/esm/api/looseDataValue.js +16 -0
- package/dist/esm/exports.js +2 -0
- package/dist/esm/integrations/CodeRunner.js +36 -0
- package/dist/esm/integrations/GptTokenizerTokenizer.js +7 -4
- package/dist/esm/integrations/openai/OpenAIEmbeddingGenerator.js +1 -1
- package/dist/esm/model/DataValue.js +14 -2
- package/dist/esm/model/GraphProcessor.js +275 -104
- package/dist/esm/model/NodeBase.js +11 -1
- package/dist/esm/model/NodeImpl.js +8 -0
- package/dist/esm/model/Nodes.js +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 +1275 -0
- package/dist/esm/model/nodes/ChunkNode.js +2 -2
- package/dist/esm/model/nodes/CodeNode.js +40 -4
- package/dist/esm/model/nodes/CronNode.js +248 -0
- package/dist/esm/model/nodes/DelegateFunctionCallNode.js +37 -12
- package/dist/esm/model/nodes/DestructureNode.js +1 -1
- package/dist/esm/model/nodes/DocumentNode.js +183 -0
- package/dist/esm/model/nodes/ExtractJsonNode.js +4 -4
- package/dist/esm/model/nodes/ExtractRegexNode.js +10 -11
- package/dist/esm/model/nodes/GetEmbeddingNode.js +1 -1
- package/dist/esm/model/nodes/HttpCallNode.js +3 -1
- package/dist/esm/model/nodes/IfNode.js +5 -0
- package/dist/esm/model/nodes/ImageToMDNode.js +116 -0
- package/dist/esm/model/nodes/LoopUntilNode.js +214 -0
- package/dist/esm/model/nodes/PromptNode.js +29 -6
- package/dist/esm/model/nodes/ReadAllFilesNode.js +210 -0
- package/dist/esm/model/nodes/ReadDirectoryNode.js +31 -25
- package/dist/esm/model/nodes/ReferencedGraphAliasNode.js +199 -0
- package/dist/esm/model/nodes/TextNode.js +9 -4
- package/dist/esm/model/nodes/ToMarkdownTableNode.js +119 -0
- package/dist/esm/model/nodes/ToTreeNode.js +133 -0
- package/dist/esm/model/nodes/{GptFunctionNode.js → ToolNode.js} +10 -10
- package/dist/esm/model/nodes/UserInputNode.js +10 -12
- package/dist/esm/plugins/aidon/nodes/ChatAidonNode.js +3 -3
- package/dist/esm/plugins/anthropic/anthropic.js +29 -10
- package/dist/esm/plugins/anthropic/fetchEventSource.js +3 -2
- package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +267 -147
- package/dist/esm/plugins/anthropic/plugin.js +9 -1
- package/dist/esm/plugins/gentrace/plugin.js +6 -6
- package/dist/esm/plugins/google/google.js +113 -5
- package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +211 -54
- package/dist/esm/plugins/google/plugin.js +13 -6
- package/dist/esm/plugins/openai/nodes/RunThreadNode.js +2 -2
- package/dist/esm/recording/ExecutionRecorder.js +5 -1
- package/dist/esm/utils/chatMessageToOpenAIChatCompletionMessage.js +15 -2
- package/dist/esm/utils/coerceType.js +1 -1
- package/dist/esm/utils/fetchEventSource.js +1 -1
- package/dist/esm/utils/interpolation.js +108 -3
- package/dist/esm/utils/openai.js +106 -50
- package/dist/esm/utils/paths.js +80 -0
- package/dist/esm/utils/serialization/serialization_v4.js +5 -0
- package/dist/types/api/createProcessor.d.ts +11 -5
- package/dist/types/api/looseDataValue.d.ts +4 -0
- package/dist/types/api/streaming.d.ts +1 -1
- package/dist/types/exports.d.ts +2 -0
- package/dist/types/integrations/CodeRunner.d.ts +18 -0
- package/dist/types/model/DataValue.d.ts +29 -6
- package/dist/types/model/EditorDefinition.d.ts +6 -1
- package/dist/types/model/GraphProcessor.d.ts +14 -7
- package/dist/types/model/NodeBase.d.ts +4 -0
- package/dist/types/model/NodeImpl.d.ts +5 -4
- package/dist/types/model/Nodes.d.ts +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/ImageToMDNode.d.ts +20 -0
- package/dist/types/model/nodes/LoopUntilNode.d.ts +32 -0
- package/dist/types/model/nodes/PromptNode.d.ts +2 -0
- package/dist/types/model/nodes/ReadAllFilesNode.d.ts +30 -0
- package/dist/types/model/nodes/ReadDirectoryNode.d.ts +1 -1
- package/dist/types/model/nodes/ReferencedGraphAliasNode.d.ts +31 -0
- package/dist/types/model/nodes/ToMarkdownTableNode.d.ts +19 -0
- package/dist/types/model/nodes/ToTreeNode.d.ts +21 -0
- package/dist/types/model/nodes/UserInputNode.d.ts +2 -3
- package/dist/types/plugins/anthropic/anthropic.d.ts +94 -13
- package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +7 -2
- package/dist/types/plugins/google/google.d.ts +93 -18
- package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +3 -2
- package/dist/types/recording/RecordedEvents.d.ts +2 -0
- package/dist/types/utils/base64.d.ts +1 -1
- package/dist/types/utils/chatMessageToOpenAIChatCompletionMessage.d.ts +3 -1
- package/dist/types/utils/interpolation.d.ts +3 -0
- package/dist/types/utils/openai.d.ts +127 -21
- package/dist/types/utils/paths.d.ts +8 -0
- package/package.json +15 -11
- /package/dist/types/model/nodes/{GptFunctionNode.d.ts → ToolNode.d.ts} +0 -0
|
@@ -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
|
}
|
|
@@ -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) {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { uint8ArrayToBase64 } from '../../index.js';
|
|
2
|
+
import { nanoid } from 'nanoid/non-secure';
|
|
3
|
+
import { NodeImpl } from '../NodeImpl.js';
|
|
4
|
+
import { nodeDefinition } from '../NodeDefinition.js';
|
|
5
|
+
import {} from '../GraphProcessor.js';
|
|
6
|
+
import { dedent } from 'ts-dedent';
|
|
7
|
+
import { expectType } from '../../utils/expectType.js';
|
|
8
|
+
export class ImageToMDNodeImpl extends NodeImpl {
|
|
9
|
+
static create() {
|
|
10
|
+
const chartNode = {
|
|
11
|
+
type: 'imagetoMD',
|
|
12
|
+
title: 'Image To Markdown',
|
|
13
|
+
id: nanoid(),
|
|
14
|
+
visualData: {
|
|
15
|
+
x: 0,
|
|
16
|
+
y: 0,
|
|
17
|
+
width: 175,
|
|
18
|
+
},
|
|
19
|
+
data: {
|
|
20
|
+
useDataInput: false,
|
|
21
|
+
mediaType: 'image/png',
|
|
22
|
+
useMediaTypeInput: false,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
return chartNode;
|
|
26
|
+
}
|
|
27
|
+
getInputDefinitions() {
|
|
28
|
+
const inputDefinitions = [];
|
|
29
|
+
if (this.chartNode.data.useDataInput) {
|
|
30
|
+
inputDefinitions.push({
|
|
31
|
+
id: 'data',
|
|
32
|
+
title: 'Data',
|
|
33
|
+
dataType: 'image',
|
|
34
|
+
coerced: false,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (this.chartNode.data.useMediaTypeInput) {
|
|
38
|
+
inputDefinitions.push({
|
|
39
|
+
id: 'mediaType',
|
|
40
|
+
title: 'Media Type',
|
|
41
|
+
dataType: 'string',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return inputDefinitions;
|
|
45
|
+
}
|
|
46
|
+
getOutputDefinitions() {
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
id: 'imageMarkdown',
|
|
50
|
+
title: 'Image',
|
|
51
|
+
dataType: 'string',
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
getEditors() {
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
58
|
+
type: 'dropdown',
|
|
59
|
+
label: 'Media Type',
|
|
60
|
+
dataKey: 'mediaType',
|
|
61
|
+
options: [
|
|
62
|
+
{ value: 'image/png', label: 'PNG' },
|
|
63
|
+
{ value: 'image/jpeg', label: 'JPEG' },
|
|
64
|
+
{ value: 'image/gif', label: 'GIF' },
|
|
65
|
+
],
|
|
66
|
+
useInputToggleDataKey: 'useMediaTypeInput',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'imageBrowser',
|
|
70
|
+
label: 'Image',
|
|
71
|
+
dataKey: 'data',
|
|
72
|
+
useInputToggleDataKey: 'useDataInput',
|
|
73
|
+
mediaTypeDataKey: 'mediaType',
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
getBody() {
|
|
78
|
+
return this.data.mediaType;
|
|
79
|
+
}
|
|
80
|
+
static getUIData() {
|
|
81
|
+
return {
|
|
82
|
+
infoBoxBody: dedent `
|
|
83
|
+
Turns the input value (image byte array) into its Markdown equivalent.
|
|
84
|
+
`,
|
|
85
|
+
infoBoxTitle: 'Image to Markdown Node',
|
|
86
|
+
contextMenuTitle: 'Image to Markdown',
|
|
87
|
+
group: ['Data'],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async process(inputData, context) {
|
|
91
|
+
let data;
|
|
92
|
+
if (this.chartNode.data.useDataInput) {
|
|
93
|
+
const imageData = expectType(inputData['data'], 'image');
|
|
94
|
+
data = (await uint8ArrayToBase64(imageData.data));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const dataRef = this.data.data?.refId;
|
|
98
|
+
if (!dataRef) {
|
|
99
|
+
throw new Error('No data ref');
|
|
100
|
+
}
|
|
101
|
+
const encodedData = context.project.data?.[dataRef];
|
|
102
|
+
if (!encodedData) {
|
|
103
|
+
throw new Error(`No data at ref ${dataRef}`);
|
|
104
|
+
}
|
|
105
|
+
data = encodedData;
|
|
106
|
+
}
|
|
107
|
+
data = ``;
|
|
108
|
+
return {
|
|
109
|
+
['imageMarkdown']: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
value: data,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export const imageToMDNode = nodeDefinition(ImageToMDNodeImpl, 'Image To Markdown');
|
|
@@ -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');
|
|
@@ -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}`);
|