@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.
Files changed (107) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/bundle.cjs +4187 -1020
  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 +31 -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/ImageToMDNode.js +116 -0
  34. package/dist/esm/model/nodes/LoopUntilNode.js +214 -0
  35. package/dist/esm/model/nodes/PromptNode.js +29 -6
  36. package/dist/esm/model/nodes/ReadAllFilesNode.js +210 -0
  37. package/dist/esm/model/nodes/ReadDirectoryNode.js +31 -25
  38. package/dist/esm/model/nodes/ReferencedGraphAliasNode.js +199 -0
  39. package/dist/esm/model/nodes/TextNode.js +9 -4
  40. package/dist/esm/model/nodes/ToMarkdownTableNode.js +119 -0
  41. package/dist/esm/model/nodes/ToTreeNode.js +133 -0
  42. package/dist/esm/model/nodes/{GptFunctionNode.js → ToolNode.js} +10 -10
  43. package/dist/esm/model/nodes/UserInputNode.js +10 -12
  44. package/dist/esm/plugins/aidon/nodes/ChatAidonNode.js +3 -3
  45. package/dist/esm/plugins/anthropic/anthropic.js +29 -10
  46. package/dist/esm/plugins/anthropic/fetchEventSource.js +3 -2
  47. package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +267 -147
  48. package/dist/esm/plugins/anthropic/plugin.js +9 -1
  49. package/dist/esm/plugins/gentrace/plugin.js +6 -6
  50. package/dist/esm/plugins/google/google.js +113 -5
  51. package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +211 -54
  52. package/dist/esm/plugins/google/plugin.js +13 -6
  53. package/dist/esm/plugins/openai/nodes/RunThreadNode.js +2 -2
  54. package/dist/esm/recording/ExecutionRecorder.js +5 -1
  55. package/dist/esm/utils/chatMessageToOpenAIChatCompletionMessage.js +15 -2
  56. package/dist/esm/utils/coerceType.js +1 -1
  57. package/dist/esm/utils/fetchEventSource.js +1 -1
  58. package/dist/esm/utils/interpolation.js +108 -3
  59. package/dist/esm/utils/openai.js +106 -50
  60. package/dist/esm/utils/paths.js +80 -0
  61. package/dist/esm/utils/serialization/serialization_v4.js +5 -0
  62. package/dist/types/api/createProcessor.d.ts +11 -5
  63. package/dist/types/api/looseDataValue.d.ts +4 -0
  64. package/dist/types/api/streaming.d.ts +1 -1
  65. package/dist/types/exports.d.ts +2 -0
  66. package/dist/types/integrations/CodeRunner.d.ts +18 -0
  67. package/dist/types/model/DataValue.d.ts +29 -6
  68. package/dist/types/model/EditorDefinition.d.ts +6 -1
  69. package/dist/types/model/GraphProcessor.d.ts +14 -7
  70. package/dist/types/model/NodeBase.d.ts +4 -0
  71. package/dist/types/model/NodeImpl.d.ts +5 -4
  72. package/dist/types/model/Nodes.d.ts +13 -4
  73. package/dist/types/model/ProcessContext.d.ts +16 -1
  74. package/dist/types/model/Project.d.ts +19 -7
  75. package/dist/types/model/ProjectReferenceLoader.d.ts +5 -0
  76. package/dist/types/model/RivetPlugin.d.ts +6 -0
  77. package/dist/types/model/RivetUIContext.d.ts +5 -1
  78. package/dist/types/model/Settings.d.ts +1 -0
  79. package/dist/types/model/nodes/AssemblePromptNode.d.ts +4 -1
  80. package/dist/types/model/nodes/ChatLoopNode.d.ts +21 -0
  81. package/dist/types/model/nodes/ChatNode.d.ts +2 -62
  82. package/dist/types/model/nodes/ChatNodeBase.d.ts +85 -0
  83. package/dist/types/model/nodes/CodeNode.d.ts +8 -2
  84. package/dist/types/model/nodes/CronNode.d.ts +34 -0
  85. package/dist/types/model/nodes/DelegateFunctionCallNode.d.ts +1 -0
  86. package/dist/types/model/nodes/DocumentNode.d.ts +28 -0
  87. package/dist/types/model/nodes/ImageToMDNode.d.ts +20 -0
  88. package/dist/types/model/nodes/LoopUntilNode.d.ts +32 -0
  89. package/dist/types/model/nodes/PromptNode.d.ts +2 -0
  90. package/dist/types/model/nodes/ReadAllFilesNode.d.ts +30 -0
  91. package/dist/types/model/nodes/ReadDirectoryNode.d.ts +1 -1
  92. package/dist/types/model/nodes/ReferencedGraphAliasNode.d.ts +31 -0
  93. package/dist/types/model/nodes/ToMarkdownTableNode.d.ts +19 -0
  94. package/dist/types/model/nodes/ToTreeNode.d.ts +21 -0
  95. package/dist/types/model/nodes/UserInputNode.d.ts +2 -3
  96. package/dist/types/plugins/anthropic/anthropic.d.ts +94 -13
  97. package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +7 -2
  98. package/dist/types/plugins/google/google.d.ts +93 -18
  99. package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +3 -2
  100. package/dist/types/recording/RecordedEvents.d.ts +2 -0
  101. package/dist/types/utils/base64.d.ts +1 -1
  102. package/dist/types/utils/chatMessageToOpenAIChatCompletionMessage.d.ts +3 -1
  103. package/dist/types/utils/interpolation.d.ts +3 -0
  104. package/dist/types/utils/openai.d.ts +127 -21
  105. package/dist/types/utils/paths.d.ts +8 -0
  106. package/package.json +15 -11
  107. /package/dist/types/model/nodes/{GptFunctionNode.d.ts → ToolNode.d.ts} +0 -0
@@ -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');
@@ -6,7 +6,8 @@ import {} from '../GraphProcessor.js';
6
6
  import {} from '../../index.js';
7
7
  import {} from '../ProcessContext.js';
8
8
  import { dedent } from 'ts-dedent';
9
- import { expectType } from '../../utils/expectType.js';
9
+ import { createTreeFromPaths } from '../../utils/paths.js';
10
+ import { getInputOrData } from '../../utils/inputs.js';
10
11
  export class ReadDirectoryNodeImpl extends NodeImpl {
11
12
  static create() {
12
13
  return {
@@ -91,6 +92,11 @@ export class ReadDirectoryNodeImpl extends NodeImpl {
91
92
  title: 'Paths',
92
93
  dataType: 'string[]',
93
94
  },
95
+ {
96
+ id: 'tree',
97
+ title: 'Tree',
98
+ dataType: 'object',
99
+ },
94
100
  ];
95
101
  }
96
102
  getBody() {
@@ -109,36 +115,27 @@ export class ReadDirectoryNodeImpl extends NodeImpl {
109
115
  static getUIData() {
110
116
  return {
111
117
  infoBoxBody: dedent `
112
- Reads the contents of the specified directory and outputs an array of filenames.
118
+ Reads the contents of the specified directory and outputs:
119
+ 1. An array of filenames
120
+ 2. The root path of the search
121
+ 3. A tree structure representing the directory hierarchy
113
122
  `,
114
123
  infoBoxTitle: 'Read Directory Node',
115
124
  contextMenuTitle: 'Read Directory',
116
125
  group: ['Input/Output'],
117
126
  };
118
127
  }
119
- async process(inputData, context) {
128
+ async process(inputs, context) {
120
129
  const { nativeApi } = context;
121
130
  if (nativeApi == null) {
122
131
  throw new Error('This node requires a native API to run.');
123
132
  }
124
- const path = this.chartNode.data.usePathInput
125
- ? expectType(inputData['path'], 'string')
126
- : this.chartNode.data.path;
127
- const recursive = this.chartNode.data.useRecursiveInput
128
- ? expectType(inputData['recursive'], 'boolean')
129
- : this.chartNode.data.recursive;
130
- const includeDirectories = this.chartNode.data.useIncludeDirectoriesInput
131
- ? expectType(inputData['includeDirectories'], 'boolean')
132
- : this.chartNode.data.includeDirectories;
133
- const filterGlobs = this.chartNode.data.useFilterGlobsInput
134
- ? expectType(inputData['filterGlobs'], 'string[]')
135
- : this.chartNode.data.filterGlobs;
136
- const relative = this.chartNode.data.useRelativeInput
137
- ? expectType(inputData['relative'], 'boolean')
138
- : this.chartNode.data.relative;
139
- const ignores = this.chartNode.data.useIgnoresInput
140
- ? expectType(inputData['ignores'], 'string[]')
141
- : this.chartNode.data.ignores;
133
+ const path = getInputOrData(this.data, inputs, 'path');
134
+ const recursive = getInputOrData(this.data, inputs, 'recursive', 'boolean');
135
+ const includeDirectories = getInputOrData(this.data, inputs, 'includeDirectories', 'boolean');
136
+ const filterGlobs = getInputOrData(this.data, inputs, 'filterGlobs', 'string[]');
137
+ const relative = getInputOrData(this.data, inputs, 'relative', 'boolean');
138
+ const ignores = getInputOrData(this.data, inputs, 'ignores', 'string[]');
142
139
  try {
143
140
  const files = await nativeApi.readdir(path, undefined, {
144
141
  recursive,
@@ -147,18 +144,27 @@ export class ReadDirectoryNodeImpl extends NodeImpl {
147
144
  relative,
148
145
  ignores,
149
146
  });
150
- const outputs = {
147
+ const tree = createTreeFromPaths(files, path);
148
+ return {
151
149
  ['paths']: { type: 'string[]', value: files },
152
150
  ['rootPath']: { type: 'string', value: path },
151
+ ['tree']: { type: 'object', value: tree },
153
152
  };
154
- return outputs;
155
153
  }
156
154
  catch (err) {
157
- const outputs = {
155
+ return {
158
156
  ['paths']: { type: 'string[]', value: ['(no such path)'] },
159
157
  ['rootPath']: { type: 'string', value: path },
158
+ ['tree']: {
159
+ type: 'object',
160
+ value: {
161
+ path,
162
+ name: path.split('/').pop() || path,
163
+ isDirectory: true,
164
+ children: [],
165
+ },
166
+ },
160
167
  };
161
- return outputs;
162
168
  }
163
169
  }
164
170
  }
@@ -0,0 +1,199 @@
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 { dedent } from 'ts-dedent';
13
+ import { getError } from '../../utils/errors.js';
14
+ export class ReferencedGraphAliasNodeImpl extends NodeImpl {
15
+ static create() {
16
+ const chartNode = {
17
+ type: 'referencedGraphAlias',
18
+ title: '', // Always set initially by the editor
19
+ id: nanoid(),
20
+ visualData: {
21
+ x: 0,
22
+ y: 0,
23
+ width: 300,
24
+ },
25
+ data: {
26
+ projectId: undefined, // Always set initially by the editor
27
+ graphId: undefined, // Always set initially by the editor
28
+ useErrorOutput: false,
29
+ },
30
+ };
31
+ return chartNode;
32
+ }
33
+ getInputDefinitions(_connections, _nodes, _project, referencedProjects) {
34
+ const referencedProject = referencedProjects[this.data.projectId];
35
+ if (!referencedProject) {
36
+ return [];
37
+ }
38
+ const graph = referencedProject.graphs[this.data.graphId];
39
+ if (!graph) {
40
+ return [];
41
+ }
42
+ const inputNodes = graph.nodes.filter((node) => node.type === 'graphInput');
43
+ const inputIds = [...new Set(inputNodes.map((node) => node.data.id))].sort();
44
+ return inputIds.map((id) => ({
45
+ id: id,
46
+ title: id,
47
+ dataType: inputNodes.find((node) => node.data.id === id).data.dataType,
48
+ }));
49
+ }
50
+ getGraphOutputs(referencedProject) {
51
+ const graph = referencedProject.graphs[this.data.graphId];
52
+ if (!graph) {
53
+ return [];
54
+ }
55
+ const outputNodes = graph.nodes.filter((node) => node.type === 'graphOutput');
56
+ const outputIds = [...new Set(outputNodes.map((node) => node.data.id))].sort();
57
+ const outputs = outputIds.map((id) => ({
58
+ id: id,
59
+ title: id,
60
+ dataType: outputNodes.find((node) => node.data.id === id).data.dataType,
61
+ }));
62
+ return outputs;
63
+ }
64
+ getOutputDefinitions(_connections, _nodes, _project, referencedProjects) {
65
+ const outputs = [];
66
+ const referencedProject = referencedProjects[this.data.projectId];
67
+ if (!referencedProject) {
68
+ return outputs;
69
+ }
70
+ outputs.push(...this.getGraphOutputs(referencedProject));
71
+ if (this.data.useErrorOutput) {
72
+ outputs.push({
73
+ id: 'error',
74
+ title: 'Error',
75
+ dataType: 'string',
76
+ });
77
+ }
78
+ return outputs;
79
+ }
80
+ getEditors(context) {
81
+ const definitions = [
82
+ {
83
+ type: 'toggle',
84
+ label: 'Use Error Output',
85
+ dataKey: 'useErrorOutput',
86
+ },
87
+ {
88
+ type: 'toggle',
89
+ label: 'Output Cost & Duration',
90
+ dataKey: 'outputCostDuration',
91
+ },
92
+ ];
93
+ const referencedProject = context.referencedProjects[this.data.projectId];
94
+ if (referencedProject) {
95
+ const graph = referencedProject.graphs[this.data.graphId];
96
+ if (graph) {
97
+ const inputNodes = graph.nodes.filter((node) => node.type === 'graphInput');
98
+ const inputIds = [...new Set(inputNodes.map((node) => node.data.id))].sort();
99
+ for (const inputId of inputIds) {
100
+ const inputNode = inputNodes.find((node) => node.data.id === inputId);
101
+ definitions.push({
102
+ type: 'dynamic',
103
+ dataKey: 'inputData',
104
+ dynamicDataKey: inputNode.data.id,
105
+ dataType: inputNode.data.dataType,
106
+ label: inputNode.data.id,
107
+ editor: inputNode.data.editor ?? 'auto',
108
+ });
109
+ }
110
+ }
111
+ }
112
+ return definitions;
113
+ }
114
+ getBody(context) {
115
+ return context.referencedProjects[this.data.projectId]?.graphs[this.data.graphId]?.metadata?.description ?? '';
116
+ }
117
+ static getUIData() {
118
+ return {
119
+ infoBoxBody: dedent `
120
+ References a graph from another project. Inputs and outputs are defined by Graph Input and Graph Output nodes within the referenced graph.
121
+ `,
122
+ infoBoxTitle: 'Referenced Graph Alias Node',
123
+ contextMenuTitle: 'Referenced Graph Alias',
124
+ group: ['Advanced'],
125
+ };
126
+ }
127
+ async process(inputs, context) {
128
+ const referencedProject = context.referencedProjects[this.data.projectId];
129
+ if (!referencedProject) {
130
+ throw new Error(`ReferencedGraphAliasNode requires a project with id ${this.data.projectId} to be available in the context.referencedProjects.`);
131
+ }
132
+ const graph = referencedProject.graphs[this.data.graphId];
133
+ if (!graph) {
134
+ throw new Error(`ReferencedGraphAliasNode requires a graph with id ${this.data.graphId} to be present in the referenced project.`);
135
+ }
136
+ const inputNodes = graph.nodes.filter((node) => node.type === 'graphInput');
137
+ const inputIds = [...new Set(inputNodes.map((node) => node.data.id))].sort();
138
+ const inputData = inputIds.reduce((obj, id) => {
139
+ if (inputs[id] != null) {
140
+ return {
141
+ ...obj,
142
+ [id]: inputs[id],
143
+ };
144
+ }
145
+ if (this.data.inputData?.[id] != null) {
146
+ return {
147
+ ...obj,
148
+ [id]: this.data.inputData[id],
149
+ };
150
+ }
151
+ return obj;
152
+ }, {});
153
+ // Create a subprocessor using the referenced project's graph
154
+ const subGraphProcessor = context.createSubProcessor(this.data.graphId, {
155
+ signal: context.signal,
156
+ project: referencedProject,
157
+ });
158
+ try {
159
+ const startTime = Date.now();
160
+ const outputs = await subGraphProcessor.processGraph(context, inputData, context.contextValues);
161
+ const duration = Date.now() - startTime;
162
+ if (this.data.useErrorOutput) {
163
+ outputs['error'] = {
164
+ type: 'control-flow-excluded',
165
+ value: undefined,
166
+ };
167
+ }
168
+ if (outputs['duration'] == null) {
169
+ outputs['duration'] = {
170
+ type: 'number',
171
+ value: duration,
172
+ };
173
+ }
174
+ if (!this.data.outputCostDuration) {
175
+ delete outputs['cost'];
176
+ delete outputs['duration'];
177
+ }
178
+ return outputs;
179
+ }
180
+ catch (err) {
181
+ if (!this.data.useErrorOutput) {
182
+ throw err;
183
+ }
184
+ const outputs = this.getGraphOutputs(referencedProject).reduce((obj, output) => ({
185
+ ...obj,
186
+ [output.id]: {
187
+ type: 'control-flow-excluded',
188
+ value: undefined,
189
+ },
190
+ }), {});
191
+ outputs['error'] = {
192
+ type: 'string',
193
+ value: getError(err).message,
194
+ };
195
+ return outputs;
196
+ }
197
+ }
198
+ }
199
+ export const referencedGraphAliasNode = nodeDefinition(ReferencedGraphAliasNodeImpl, 'Referenced Graph Alias');
@@ -6,7 +6,7 @@ import {} from '../DataValue.js';
6
6
  import {} from '../../index.js';
7
7
  import { dedent } from 'ts-dedent';
8
8
  import { coerceTypeOptional } from '../../utils/coerceType.js';
9
- import { interpolate } from '../../utils/interpolation.js';
9
+ import { extractInterpolationVariables, interpolate } from '../../utils/interpolation.js';
10
10
  export class TextNodeImpl extends NodeImpl {
11
11
  static create() {
12
12
  const chartNode = {
@@ -26,13 +26,13 @@ export class TextNodeImpl extends NodeImpl {
26
26
  }
27
27
  getInputDefinitions() {
28
28
  // Extract inputs from text, everything like {{input}}
29
- const inputNames = [...new Set(this.chartNode.data.text.match(/\{\{([^}]+)\}\}/g))];
29
+ const inputNames = extractInterpolationVariables(this.data.text);
30
30
  return (inputNames?.map((inputName) => {
31
31
  return {
32
32
  type: 'string',
33
33
  // id and title should not have the {{ and }}
34
- id: inputName.slice(2, -2),
35
- title: inputName.slice(2, -2),
34
+ id: inputName,
35
+ title: inputName,
36
36
  dataType: 'string',
37
37
  required: false,
38
38
  };
@@ -49,6 +49,11 @@ export class TextNodeImpl extends NodeImpl {
49
49
  }
50
50
  getEditors() {
51
51
  return [
52
+ {
53
+ type: 'custom',
54
+ label: 'AI Assist',
55
+ customEditorId: 'TextNodeAiAssist',
56
+ },
52
57
  {
53
58
  type: 'code',
54
59
  label: 'Text',
@@ -0,0 +1,119 @@
1
+ import {} from '../NodeBase.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 {} from '../../index.js';
7
+ import { dedent } from 'ts-dedent';
8
+ import { coerceType } from '../../utils/coerceType.js';
9
+ export class ToMarkdownTableNodeImpl extends NodeImpl {
10
+ static create() {
11
+ const chartNode = {
12
+ type: 'toMarkdownTable',
13
+ title: 'To Markdown Table',
14
+ id: nanoid(),
15
+ visualData: {
16
+ x: 0,
17
+ y: 0,
18
+ width: 200,
19
+ },
20
+ data: {
21
+ includeHeaders: true,
22
+ alignPipes: false,
23
+ },
24
+ };
25
+ return chartNode;
26
+ }
27
+ getInputDefinitions() {
28
+ return [
29
+ {
30
+ id: 'data',
31
+ title: 'Data Array',
32
+ dataType: 'any',
33
+ required: true,
34
+ },
35
+ ];
36
+ }
37
+ getOutputDefinitions() {
38
+ return [
39
+ {
40
+ id: 'markdown',
41
+ title: 'Markdown Table',
42
+ dataType: 'string',
43
+ },
44
+ ];
45
+ }
46
+ getEditors() {
47
+ return [
48
+ {
49
+ type: 'toggle',
50
+ label: 'Include Headers',
51
+ dataKey: 'includeHeaders',
52
+ },
53
+ {
54
+ type: 'toggle',
55
+ label: 'Align Pipes',
56
+ dataKey: 'alignPipes',
57
+ },
58
+ ];
59
+ }
60
+ getBody() {
61
+ const parts = [];
62
+ if (this.data.includeHeaders)
63
+ parts.push('With Header Row');
64
+ if (this.data.alignPipes)
65
+ parts.push('Pipes Aligned');
66
+ return parts.length > 0 ? parts.join(', ') : undefined;
67
+ }
68
+ static getUIData() {
69
+ return {
70
+ infoBoxBody: dedent `
71
+ Converts an array of objects into a markdown table format.
72
+ Input should be an array of objects with consistent keys.
73
+ `,
74
+ infoBoxTitle: 'To Markdown Table Node',
75
+ contextMenuTitle: 'To Markdown Table',
76
+ group: ['Text'],
77
+ };
78
+ }
79
+ async process(inputs) {
80
+ const data = coerceType(inputs['data'], 'object[]');
81
+ const keys = data.length === 0 ? [] : Object.keys(data[0]);
82
+ // Dynamic import because these are ESM-only, and top level imports are converted to CommonJS for rivet-node.
83
+ // Dynamic import is able to load ESM-only modules.
84
+ const { toMarkdown } = await import('mdast-util-to-markdown');
85
+ const { gfmTableToMarkdown } = await import('mdast-util-gfm-table');
86
+ const markdownTable = toMarkdown({
87
+ type: 'table',
88
+ children: [
89
+ ...(this.data.includeHeaders
90
+ ? [
91
+ {
92
+ type: 'tableRow',
93
+ children: keys.map((key) => ({
94
+ type: 'tableCell',
95
+ children: [{ type: 'text', value: key }],
96
+ })),
97
+ },
98
+ ]
99
+ : []),
100
+ ...data.map((row) => ({
101
+ type: 'tableRow',
102
+ children: keys.map((key) => ({
103
+ type: 'tableCell',
104
+ children: [{ type: 'text', value: `${row[key]}` }],
105
+ })),
106
+ })),
107
+ ],
108
+ }, {
109
+ extensions: [gfmTableToMarkdown({ tablePipeAlign: this.data.alignPipes })],
110
+ });
111
+ return {
112
+ ['markdown']: {
113
+ type: 'string',
114
+ value: markdownTable,
115
+ },
116
+ };
117
+ }
118
+ }
119
+ export const toMarkdownTableNode = nodeDefinition(ToMarkdownTableNodeImpl, 'To Markdown Table');