@alpic80/rivet-core 1.19.1-aidon.3 → 1.24.0-aidon.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/bundle.cjs +3993 -943
  3. package/dist/cjs/bundle.cjs.map +4 -4
  4. package/dist/esm/api/createProcessor.js +8 -17
  5. package/dist/esm/api/looseDataValue.js +16 -0
  6. package/dist/esm/exports.js +2 -0
  7. package/dist/esm/integrations/CodeRunner.js +36 -0
  8. package/dist/esm/integrations/GptTokenizerTokenizer.js +7 -4
  9. package/dist/esm/integrations/openai/OpenAIEmbeddingGenerator.js +1 -1
  10. package/dist/esm/model/DataValue.js +14 -2
  11. package/dist/esm/model/GraphProcessor.js +275 -104
  12. package/dist/esm/model/NodeBase.js +11 -1
  13. package/dist/esm/model/NodeImpl.js +8 -0
  14. package/dist/esm/model/Nodes.js +28 -4
  15. package/dist/esm/model/ProjectReferenceLoader.js +1 -0
  16. package/dist/esm/model/nodes/AssembleMessageNode.js +12 -2
  17. package/dist/esm/model/nodes/AssemblePromptNode.js +22 -0
  18. package/dist/esm/model/nodes/CallGraphNode.js +3 -4
  19. package/dist/esm/model/nodes/ChatLoopNode.js +150 -0
  20. package/dist/esm/model/nodes/ChatNode.js +7 -934
  21. package/dist/esm/model/nodes/ChatNodeBase.js +1275 -0
  22. package/dist/esm/model/nodes/ChunkNode.js +2 -2
  23. package/dist/esm/model/nodes/CodeNode.js +40 -4
  24. package/dist/esm/model/nodes/CronNode.js +248 -0
  25. package/dist/esm/model/nodes/DelegateFunctionCallNode.js +37 -12
  26. package/dist/esm/model/nodes/DestructureNode.js +1 -1
  27. package/dist/esm/model/nodes/DocumentNode.js +183 -0
  28. package/dist/esm/model/nodes/ExtractJsonNode.js +4 -4
  29. package/dist/esm/model/nodes/ExtractRegexNode.js +10 -11
  30. package/dist/esm/model/nodes/GetEmbeddingNode.js +1 -1
  31. package/dist/esm/model/nodes/HttpCallNode.js +3 -1
  32. package/dist/esm/model/nodes/IfNode.js +5 -0
  33. package/dist/esm/model/nodes/LoopUntilNode.js +214 -0
  34. package/dist/esm/model/nodes/PromptNode.js +29 -6
  35. package/dist/esm/model/nodes/ReadAllFilesNode.js +210 -0
  36. package/dist/esm/model/nodes/ReadDirectoryNode.js +31 -25
  37. package/dist/esm/model/nodes/ReferencedGraphAliasNode.js +199 -0
  38. package/dist/esm/model/nodes/TextNode.js +9 -4
  39. package/dist/esm/model/nodes/ToMarkdownTableNode.js +119 -0
  40. package/dist/esm/model/nodes/ToTreeNode.js +133 -0
  41. package/dist/esm/model/nodes/{GptFunctionNode.js → ToolNode.js} +10 -10
  42. package/dist/esm/model/nodes/UserInputNode.js +10 -12
  43. package/dist/esm/plugins/aidon/nodes/ChatAidonNode.js +2 -2
  44. package/dist/esm/plugins/anthropic/anthropic.js +29 -10
  45. package/dist/esm/plugins/anthropic/fetchEventSource.js +3 -2
  46. package/dist/esm/plugins/anthropic/nodes/ChatAnthropicNode.js +267 -147
  47. package/dist/esm/plugins/anthropic/plugin.js +9 -1
  48. package/dist/esm/plugins/gentrace/plugin.js +6 -6
  49. package/dist/esm/plugins/google/google.js +113 -5
  50. package/dist/esm/plugins/google/nodes/ChatGoogleNode.js +211 -54
  51. package/dist/esm/plugins/google/plugin.js +13 -6
  52. package/dist/esm/plugins/openai/nodes/RunThreadNode.js +2 -2
  53. package/dist/esm/recording/ExecutionRecorder.js +5 -1
  54. package/dist/esm/utils/chatMessageToOpenAIChatCompletionMessage.js +15 -2
  55. package/dist/esm/utils/coerceType.js +1 -1
  56. package/dist/esm/utils/fetchEventSource.js +1 -1
  57. package/dist/esm/utils/interpolation.js +108 -3
  58. package/dist/esm/utils/openai.js +106 -50
  59. package/dist/esm/utils/paths.js +80 -0
  60. package/dist/esm/utils/serialization/serialization_v4.js +5 -0
  61. package/dist/types/api/createProcessor.d.ts +11 -5
  62. package/dist/types/api/looseDataValue.d.ts +4 -0
  63. package/dist/types/api/streaming.d.ts +1 -1
  64. package/dist/types/exports.d.ts +2 -0
  65. package/dist/types/integrations/CodeRunner.d.ts +18 -0
  66. package/dist/types/model/DataValue.d.ts +29 -6
  67. package/dist/types/model/EditorDefinition.d.ts +6 -1
  68. package/dist/types/model/GraphProcessor.d.ts +14 -7
  69. package/dist/types/model/NodeBase.d.ts +4 -0
  70. package/dist/types/model/NodeImpl.d.ts +5 -4
  71. package/dist/types/model/Nodes.d.ts +12 -4
  72. package/dist/types/model/ProcessContext.d.ts +16 -1
  73. package/dist/types/model/Project.d.ts +19 -7
  74. package/dist/types/model/ProjectReferenceLoader.d.ts +5 -0
  75. package/dist/types/model/RivetPlugin.d.ts +6 -0
  76. package/dist/types/model/RivetUIContext.d.ts +5 -1
  77. package/dist/types/model/Settings.d.ts +1 -0
  78. package/dist/types/model/nodes/AssemblePromptNode.d.ts +4 -1
  79. package/dist/types/model/nodes/ChatLoopNode.d.ts +21 -0
  80. package/dist/types/model/nodes/ChatNode.d.ts +2 -62
  81. package/dist/types/model/nodes/ChatNodeBase.d.ts +85 -0
  82. package/dist/types/model/nodes/CodeNode.d.ts +8 -2
  83. package/dist/types/model/nodes/CronNode.d.ts +34 -0
  84. package/dist/types/model/nodes/DelegateFunctionCallNode.d.ts +1 -0
  85. package/dist/types/model/nodes/DocumentNode.d.ts +28 -0
  86. package/dist/types/model/nodes/LoopUntilNode.d.ts +32 -0
  87. package/dist/types/model/nodes/PromptNode.d.ts +2 -0
  88. package/dist/types/model/nodes/ReadAllFilesNode.d.ts +30 -0
  89. package/dist/types/model/nodes/ReadDirectoryNode.d.ts +1 -1
  90. package/dist/types/model/nodes/ReferencedGraphAliasNode.d.ts +31 -0
  91. package/dist/types/model/nodes/ToMarkdownTableNode.d.ts +19 -0
  92. package/dist/types/model/nodes/ToTreeNode.d.ts +21 -0
  93. package/dist/types/model/nodes/UserInputNode.d.ts +2 -3
  94. package/dist/types/plugins/anthropic/anthropic.d.ts +94 -13
  95. package/dist/types/plugins/anthropic/nodes/ChatAnthropicNode.d.ts +7 -2
  96. package/dist/types/plugins/google/google.d.ts +93 -18
  97. package/dist/types/plugins/google/nodes/ChatGoogleNode.d.ts +3 -2
  98. package/dist/types/recording/RecordedEvents.d.ts +2 -0
  99. package/dist/types/utils/base64.d.ts +1 -1
  100. package/dist/types/utils/chatMessageToOpenAIChatCompletionMessage.d.ts +3 -1
  101. package/dist/types/utils/interpolation.d.ts +3 -0
  102. package/dist/types/utils/openai.d.ts +127 -21
  103. package/dist/types/utils/paths.d.ts +8 -0
  104. package/package.json +15 -11
  105. /package/dist/types/model/nodes/{GptFunctionNode.d.ts → ToolNode.d.ts} +0 -0
@@ -3,7 +3,7 @@ import { NodeImpl } from '../../model/NodeImpl.js';
3
3
  import { nanoid } from 'nanoid/non-secure';
4
4
  import { coerceType } from '../../utils/coerceType.js';
5
5
  import { dedent } from 'ts-dedent';
6
- import { openAiModelOptions, openaiModels } from '../../utils/openai.js';
6
+ import { openAiModelOptions } from '../../utils/openai.js';
7
7
  import {} from '../../index.js';
8
8
  import { nodeDefinition } from '../NodeDefinition.js';
9
9
  export class ChunkNodeImpl extends NodeImpl {
@@ -18,7 +18,7 @@ export class ChunkNodeImpl extends NodeImpl {
18
18
  width: 200,
19
19
  },
20
20
  data: {
21
- model: 'gpt-3.5-turbo',
21
+ model: 'gpt-4o',
22
22
  useModelInput: false,
23
23
  numTokensPerChunk: 1024,
24
24
  overlap: 0,
@@ -33,6 +33,11 @@ export class CodeNodeImpl extends NodeImpl {
33
33
  `,
34
34
  inputNames: 'input1',
35
35
  outputNames: 'output1',
36
+ allowFetch: false,
37
+ allowRequire: false,
38
+ allowRivet: false,
39
+ allowProcess: false,
40
+ allowConsole: false,
36
41
  },
37
42
  };
38
43
  return chartNode;
@@ -90,6 +95,33 @@ export class CodeNodeImpl extends NodeImpl {
90
95
  label: 'Outputs',
91
96
  dataKey: 'outputNames',
92
97
  },
98
+ {
99
+ type: 'toggle',
100
+ label: 'Allow using `fetch`',
101
+ dataKey: 'allowFetch',
102
+ },
103
+ {
104
+ type: 'toggle',
105
+ label: 'Allow using `require`',
106
+ dataKey: 'allowRequire',
107
+ helperMessage: 'This is only available when using the Node executor.',
108
+ },
109
+ {
110
+ type: 'toggle',
111
+ label: 'Allow using `Rivet`',
112
+ dataKey: 'allowRivet',
113
+ },
114
+ {
115
+ type: 'toggle',
116
+ label: 'Allow using `process`',
117
+ dataKey: 'allowProcess',
118
+ helperMessage: 'This is only available when using the Node executor.',
119
+ },
120
+ {
121
+ type: 'toggle',
122
+ label: 'Allow using `console`',
123
+ dataKey: 'allowConsole',
124
+ },
93
125
  ];
94
126
  }
95
127
  getBody() {
@@ -117,10 +149,14 @@ export class CodeNodeImpl extends NodeImpl {
117
149
  group: ['Advanced'],
118
150
  };
119
151
  }
120
- async process(inputs) {
121
- // eslint-disable-next-line no-new-func
122
- const codeFunction = new Function('inputs', this.chartNode.data.code);
123
- const outputs = codeFunction(inputs);
152
+ async process(inputs, context) {
153
+ const outputs = await context.codeRunner.runCode(this.data.code, inputs, {
154
+ includeFetch: this.data.allowFetch ?? false,
155
+ includeRequire: this.data.allowRequire ?? false,
156
+ includeRivet: this.data.allowRivet ?? false,
157
+ includeProcess: this.data.allowProcess ?? false,
158
+ includeConsole: this.data.allowConsole ?? false,
159
+ });
124
160
  if (outputs == null || typeof outputs !== 'object' || ('then' in outputs && typeof outputs.then === 'function')) {
125
161
  throw new Error('Code node must return an object with output values.');
126
162
  }
@@ -0,0 +1,248 @@
1
+ import { nanoid } from 'nanoid';
2
+ import { NodeImpl } from '../NodeImpl.js';
3
+ import { dedent } from 'ts-dedent';
4
+ import { nodeDefinition } from '../NodeDefinition.js';
5
+ import { coerceType, coerceTypeOptional } from '../../utils/coerceType.js';
6
+ import * as cronParser from 'cron-parser';
7
+ export class CronNodeImpl extends NodeImpl {
8
+ static create() {
9
+ const chartNode = {
10
+ type: 'cron',
11
+ title: 'Cron',
12
+ id: nanoid(),
13
+ visualData: {
14
+ x: 0,
15
+ y: 0,
16
+ width: 200,
17
+ },
18
+ data: {
19
+ targetGraph: undefined,
20
+ scheduleType: 'interval',
21
+ schedule: '5 minutes',
22
+ executeImmediately: true,
23
+ },
24
+ };
25
+ return chartNode;
26
+ }
27
+ getInputDefinitions() {
28
+ const inputs = [];
29
+ inputs.push({
30
+ id: 'trigger',
31
+ title: 'Trigger',
32
+ dataType: 'boolean',
33
+ description: 'Starts the scheduled job when true.',
34
+ });
35
+ if (this.data.useTargetGraphInput) {
36
+ inputs.push({
37
+ id: 'targetGraph',
38
+ title: 'Target Graph',
39
+ dataType: 'string',
40
+ description: 'The subgraph to execute on schedule',
41
+ });
42
+ }
43
+ return inputs;
44
+ }
45
+ getOutputDefinitions() {
46
+ return [
47
+ {
48
+ id: 'output',
49
+ title: 'Last Output',
50
+ dataType: 'any',
51
+ description: 'The last output from the subgraph execution.',
52
+ },
53
+ {
54
+ id: 'iteration',
55
+ title: 'Iteration',
56
+ dataType: 'number',
57
+ description: 'The current iteration number.',
58
+ },
59
+ {
60
+ id: 'completed',
61
+ title: 'Completed',
62
+ dataType: 'boolean',
63
+ description: 'True when the job has completed.',
64
+ },
65
+ {
66
+ id: 'nextRun',
67
+ title: 'Next Run',
68
+ dataType: 'string',
69
+ description: 'The scheduled time for the next execution.',
70
+ },
71
+ ];
72
+ }
73
+ static getUIData() {
74
+ return {
75
+ infoBoxBody: dedent `
76
+ Executes a subgraph on a schedule. Supports:
77
+ - Natural language (e.g., "every 5 minutes", "daily at 3pm")
78
+ - Cron expressions (e.g., "0 * * * *")
79
+ - Simple intervals (e.g., "5 minutes", "1 hour")
80
+ `,
81
+ infoBoxTitle: 'Cron Node',
82
+ contextMenuTitle: 'Cron',
83
+ group: ['Advanced'],
84
+ };
85
+ }
86
+ getEditors() {
87
+ return [
88
+ {
89
+ type: 'toggle',
90
+ dataKey: 'executeImmediately',
91
+ label: 'Execute Immediately',
92
+ helperMessage: 'Starts the job immediately when the node is run, in addition to the schedule',
93
+ },
94
+ {
95
+ type: 'graphSelector',
96
+ dataKey: 'targetGraph',
97
+ useInputToggleDataKey: 'useTargetGraphInput',
98
+ label: 'Target Graph',
99
+ helperMessage: 'The subgraph to execute on schedule',
100
+ },
101
+ {
102
+ type: 'dropdown',
103
+ dataKey: 'scheduleType',
104
+ label: 'Schedule Type',
105
+ options: [
106
+ { label: 'Cron Expression', value: 'cron' },
107
+ { label: 'Simple Interval', value: 'interval' },
108
+ ],
109
+ helperMessage: 'How to specify the schedule',
110
+ },
111
+ {
112
+ type: 'string',
113
+ dataKey: 'schedule',
114
+ label: 'Schedule',
115
+ helperMessage: dedent `
116
+ Examples:
117
+ Cron: "*/5 * * * *", "0 15 * * *"
118
+ Interval: "5 minutes", "1 hour", "7 days"
119
+ `,
120
+ },
121
+ ];
122
+ }
123
+ parseSchedule() {
124
+ const { scheduleType, schedule } = this.data;
125
+ if (scheduleType === 'cron') {
126
+ return { type: 'cron', expression: schedule };
127
+ }
128
+ // Parse interval
129
+ const match = schedule.match(/^(\d+)\s*(second|seconds|minute|minutes|hour|hours|day|days|week|weeks)$/i);
130
+ if (!match) {
131
+ throw new Error('Invalid interval format. Expected: "number unit" (e.g., "5 minutes")');
132
+ }
133
+ const value = parseInt(match[1], 10);
134
+ let unit = match[2].toLowerCase();
135
+ // Normalize unit to plural
136
+ if (unit.endsWith('s')) {
137
+ unit = unit;
138
+ }
139
+ else {
140
+ unit = `${unit}s`;
141
+ }
142
+ return { type: 'interval', value, unit };
143
+ }
144
+ getNextRunTime(schedule) {
145
+ const now = new Date();
146
+ if (schedule.type === 'interval') {
147
+ const { value, unit } = schedule;
148
+ const next = new Date(now);
149
+ switch (unit) {
150
+ case 'seconds':
151
+ next.setSeconds(now.getSeconds() + value);
152
+ break;
153
+ case 'minutes':
154
+ next.setMinutes(now.getMinutes() + value);
155
+ break;
156
+ case 'hours':
157
+ next.setHours(now.getHours() + value);
158
+ break;
159
+ case 'days':
160
+ next.setDate(now.getDate() + value);
161
+ break;
162
+ case 'weeks':
163
+ next.setDate(now.getDate() + value * 7);
164
+ break;
165
+ }
166
+ return next;
167
+ }
168
+ if (schedule.type === 'cron') {
169
+ const cron = cronParser.parseExpression(schedule.expression, { currentDate: now });
170
+ const next = cron.next().toDate();
171
+ return next;
172
+ }
173
+ throw new Error('Invalid schedule type');
174
+ }
175
+ getBody(context) {
176
+ if (!this.data.targetGraph && !this.data.useTargetGraphInput) {
177
+ return 'No target graph selected';
178
+ }
179
+ const graphName = this.data.useTargetGraphInput
180
+ ? 'graph from input'
181
+ : context.project.graphs[this.data.targetGraph]?.metadata?.name ?? 'Unknown Graph';
182
+ return `Executes ${graphName}\n${this.data.schedule}`;
183
+ }
184
+ async process(inputs, context) {
185
+ if (inputs['trigger'] !== undefined) {
186
+ const trigger = coerceTypeOptional(inputs['trigger'], 'boolean');
187
+ if (!trigger) {
188
+ return {
189
+ ['completed']: { type: 'boolean', value: false },
190
+ ['iteration']: { type: 'number', value: 0 },
191
+ };
192
+ }
193
+ }
194
+ if (!this.data.targetGraph) {
195
+ throw new Error('No target graph selected');
196
+ }
197
+ let iteration = 0;
198
+ const schedule = this.parseSchedule();
199
+ let lastOutputs = {};
200
+ let didExecuteFirstTime = !this.data.executeImmediately;
201
+ while (!context.signal.aborted) {
202
+ const nextRun = didExecuteFirstTime ? this.getNextRunTime(schedule) : new Date();
203
+ const delay = nextRun.getTime() - Date.now();
204
+ didExecuteFirstTime = true;
205
+ // Emit the next run time
206
+ const outputs = {
207
+ ['output']: lastOutputs
208
+ ? { type: 'object', value: lastOutputs }
209
+ : {
210
+ type: 'any',
211
+ value: null,
212
+ },
213
+ ['iteration']: { type: 'number', value: iteration },
214
+ ['completed']: { type: 'boolean', value: false },
215
+ ['nextRun']: { type: 'string', value: nextRun.toISOString() },
216
+ };
217
+ if (delay > 0) {
218
+ await new Promise((resolve) => {
219
+ context.signal.addEventListener('abort', resolve, { once: true });
220
+ setTimeout(resolve, delay);
221
+ });
222
+ }
223
+ if (context.signal.aborted) {
224
+ throw new Error('Aborted');
225
+ }
226
+ iteration++;
227
+ const subprocessor = context.createSubProcessor(this.data.targetGraph, { signal: context.signal });
228
+ lastOutputs = await subprocessor.processGraph(context, lastOutputs, context.contextValues);
229
+ // Check for break signal
230
+ const breakSignal = coerceTypeOptional(lastOutputs['break'], 'boolean');
231
+ if (breakSignal === true) {
232
+ return {
233
+ ...outputs,
234
+ ['completed']: { type: 'boolean', value: true },
235
+ };
236
+ }
237
+ }
238
+ return {
239
+ ['output']: lastOutputs
240
+ ? { type: 'object', value: lastOutputs }
241
+ : { type: 'any', value: null },
242
+ ['iteration']: { type: 'number', value: iteration },
243
+ ['completed']: { type: 'boolean', value: true },
244
+ ['nextRun']: { type: 'string', value: new Date().toISOString() },
245
+ };
246
+ }
247
+ }
248
+ export const cronNode = nodeDefinition(CronNodeImpl, 'Cron');
@@ -7,7 +7,7 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
7
7
  static create() {
8
8
  const chartNode = {
9
9
  type: 'delegateFunctionCall',
10
- title: 'Delegate Function Call',
10
+ title: 'Delegate Tool Call',
11
11
  id: nanoid(),
12
12
  visualData: {
13
13
  x: 0,
@@ -17,6 +17,7 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
17
17
  data: {
18
18
  handlers: [],
19
19
  unknownHandler: undefined,
20
+ autoDelegate: true,
20
21
  },
21
22
  };
22
23
  return chartNode;
@@ -26,10 +27,10 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
26
27
  inputs.push({
27
28
  id: 'function-call',
28
29
  dataType: 'object',
29
- title: 'Function Call',
30
+ title: 'Tool Call',
30
31
  coerced: true,
31
32
  required: true,
32
- description: 'The function call to delegate to a subgraph.',
33
+ description: 'The tool call to delegate to a subgraph.',
33
34
  });
34
35
  return inputs;
35
36
  }
@@ -39,7 +40,7 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
39
40
  id: 'output',
40
41
  dataType: 'string',
41
42
  title: 'Output',
42
- description: 'The output of the function call.',
43
+ description: 'The output of the tool call.',
43
44
  });
44
45
  outputs.push({
45
46
  id: 'message',
@@ -52,30 +53,40 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
52
53
  static getUIData() {
53
54
  return {
54
55
  infoBoxBody: dedent `
55
- Handles a function call by delegating it to a different subgraph depending on the function call.
56
+ Handles a tool call by delegating it to a different subgraph depending on the tool call.
56
57
  `,
57
- infoBoxTitle: 'Delegate Function Call Node',
58
- contextMenuTitle: 'Delegate Function Call',
58
+ infoBoxTitle: 'Delegate Tool Call Node',
59
+ contextMenuTitle: 'Delegate Tool Call',
59
60
  group: ['Advanced'],
60
61
  };
61
62
  }
62
63
  getEditors() {
63
64
  return [
65
+ {
66
+ type: 'toggle',
67
+ label: 'Auto Delegate',
68
+ dataKey: 'autoDelegate',
69
+ helperMessage: 'Automatically delegates tool calls to the subgraph containing the same name as the tool.',
70
+ },
64
71
  {
65
72
  type: 'custom',
66
73
  customEditorId: 'ToolCallHandlers',
67
74
  label: 'Handlers',
68
75
  dataKey: 'handlers',
76
+ hideIf: (data) => data.autoDelegate,
69
77
  },
70
78
  {
71
79
  type: 'graphSelector',
72
80
  dataKey: 'unknownHandler',
73
81
  label: 'Unknown Handler',
74
- helperMessage: 'The subgraph to delegate to if the function call does not match any handlers.',
82
+ helperMessage: 'The subgraph to delegate to if the tool call does not match any handlers.',
75
83
  },
76
84
  ];
77
85
  }
78
86
  getBody(context) {
87
+ if (this.data.autoDelegate) {
88
+ return 'Auto Delegate To Subgraphs';
89
+ }
79
90
  if (this.data.handlers.length === 0) {
80
91
  return 'No handlers defined';
81
92
  }
@@ -88,13 +99,27 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
88
99
  }
89
100
  async process(inputs, context) {
90
101
  const functionCall = coerceType(inputs['function-call'], 'object');
91
- let handler = this.data.handlers.find((handler) => handler.key === functionCall.name);
102
+ let handler;
103
+ if (this.data.autoDelegate) {
104
+ const matchingGraph = Object.values(context.project.graphs).find((graph) => graph.metadata?.name?.includes(functionCall.name));
105
+ if (matchingGraph) {
106
+ handler = { key: undefined, value: matchingGraph.metadata.id };
107
+ }
108
+ }
109
+ else {
110
+ handler = this.data.handlers.find((handler) => handler.key === functionCall.name);
111
+ }
92
112
  if (!handler) {
93
113
  if (this.data.unknownHandler) {
94
114
  handler = { key: undefined, value: this.data.unknownHandler };
95
115
  }
96
116
  else {
97
- throw new Error(`No handler found for function call: ${functionCall.name}`);
117
+ if (this.data.autoDelegate) {
118
+ throw new Error(`No handler found for tool call: ${functionCall.name}, no graph containing the name "${functionCall.name}" was found.`);
119
+ }
120
+ else {
121
+ throw new Error(`No handler found for tool call: ${functionCall.name}`);
122
+ }
98
123
  }
99
124
  }
100
125
  const subgraphInputs = {
@@ -107,7 +132,7 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
107
132
  value: functionCall.arguments,
108
133
  },
109
134
  };
110
- for (const [argName, argument] of Object.entries(functionCall.arguments)) {
135
+ for (const [argName, argument] of Object.entries(functionCall.arguments ?? {})) {
111
136
  subgraphInputs[argName] = {
112
137
  type: 'any',
113
138
  value: argument,
@@ -133,4 +158,4 @@ export class DelegateFunctionCallNodeImpl extends NodeImpl {
133
158
  };
134
159
  }
135
160
  }
136
- export const delegateFunctionCallNode = nodeDefinition(DelegateFunctionCallNodeImpl, 'Delegate Function Call');
161
+ export const delegateFunctionCallNode = nodeDefinition(DelegateFunctionCallNodeImpl, 'Delegate Tool Call');
@@ -35,7 +35,7 @@ export class DestructureNodeImpl extends NodeImpl {
35
35
  ];
36
36
  }
37
37
  getOutputDefinitions() {
38
- return this.chartNode.data.paths.map((path, index) => ({
38
+ return this.data.paths.map((path, index) => ({
39
39
  id: `match_${index}`,
40
40
  title: path,
41
41
  dataType: 'any',
@@ -0,0 +1,183 @@
1
+ import {} from '../NodeBase.js';
2
+ import { NodeImpl } from '../NodeImpl.js';
3
+ import { nanoid } from 'nanoid/non-secure';
4
+ import {} from '../../index.js';
5
+ import { base64ToUint8Array, expectType } from '../../utils/index.js';
6
+ import { nodeDefinition } from '../NodeDefinition.js';
7
+ import { getInputOrData } from '../../utils/inputs.js';
8
+ import { dedent } from '../../utils/misc.js';
9
+ export class DocumentNodeImpl extends NodeImpl {
10
+ static create() {
11
+ return {
12
+ id: nanoid(),
13
+ type: 'document',
14
+ title: 'Document',
15
+ visualData: { x: 0, y: 0, width: 300 },
16
+ data: {
17
+ useDataInput: false,
18
+ useMediaTypeInput: false,
19
+ title: '',
20
+ useTitleInput: false,
21
+ context: '',
22
+ useContextInput: false,
23
+ enableCitations: false,
24
+ useEnableCitationsInput: false,
25
+ },
26
+ };
27
+ }
28
+ getInputDefinitions() {
29
+ const inputDefinitions = [];
30
+ if (this.chartNode.data.useDataInput) {
31
+ inputDefinitions.push({
32
+ id: 'data',
33
+ title: 'Data',
34
+ dataType: ['string', 'binary'],
35
+ coerced: false,
36
+ description: 'The document data. Either string or binary data loaded from a file.',
37
+ });
38
+ }
39
+ if (this.chartNode.data.useMediaTypeInput) {
40
+ inputDefinitions.push({
41
+ id: 'mediaType',
42
+ title: 'Media Type',
43
+ dataType: 'string',
44
+ coerced: false,
45
+ description: 'The media type of the document, such as text/plain or application/pdf.',
46
+ });
47
+ }
48
+ if (this.data.useTitleInput) {
49
+ inputDefinitions.push({
50
+ id: 'title',
51
+ title: 'Title',
52
+ dataType: 'string',
53
+ coerced: true,
54
+ description: 'The title of the document.',
55
+ });
56
+ }
57
+ if (this.data.useContextInput) {
58
+ inputDefinitions.push({
59
+ id: 'context',
60
+ title: 'Context',
61
+ dataType: 'string',
62
+ coerced: true,
63
+ description: 'The context of the document.',
64
+ });
65
+ }
66
+ if (this.data.useEnableCitationsInput) {
67
+ inputDefinitions.push({
68
+ id: 'enableCitations',
69
+ title: 'Enable Citations',
70
+ dataType: 'boolean',
71
+ coerced: true,
72
+ description: 'Whether to enable citations for the document.',
73
+ });
74
+ }
75
+ return inputDefinitions;
76
+ }
77
+ getOutputDefinitions() {
78
+ return [
79
+ {
80
+ id: 'data',
81
+ title: 'Document Data',
82
+ dataType: 'document',
83
+ },
84
+ ];
85
+ }
86
+ getEditors() {
87
+ return [
88
+ {
89
+ type: 'fileBrowser',
90
+ label: 'Document File',
91
+ dataKey: 'data',
92
+ mediaTypeDataKey: 'mediaType',
93
+ useInputToggleDataKey: 'useDataInput',
94
+ accept: '*/*',
95
+ },
96
+ {
97
+ type: 'string',
98
+ label: 'Media Type',
99
+ dataKey: 'mediaType',
100
+ useInputToggleDataKey: 'useMediaTypeInput',
101
+ },
102
+ {
103
+ type: 'string',
104
+ label: 'Title',
105
+ dataKey: 'title',
106
+ useInputToggleDataKey: 'useTitleInput',
107
+ },
108
+ {
109
+ type: 'string',
110
+ label: 'Context',
111
+ dataKey: 'context',
112
+ useInputToggleDataKey: 'useContextInput',
113
+ },
114
+ {
115
+ type: 'toggle',
116
+ label: 'Enable Citations',
117
+ dataKey: 'enableCitations',
118
+ useInputToggleDataKey: 'useEnableCitationsInput',
119
+ },
120
+ ];
121
+ }
122
+ static getUIData() {
123
+ return {
124
+ contextMenuTitle: 'Document',
125
+ group: 'Data',
126
+ infoBoxTitle: 'Document Node',
127
+ infoBoxBody: 'Defines a document for use with other nodes such as Assemble Message. Can accept text and PDF files.',
128
+ };
129
+ }
130
+ getBody(_context) {
131
+ const parts = [
132
+ this.data.useDataInput ? '(Data from input)' : '(Data stored in node)',
133
+ this.data.useMediaTypeInput
134
+ ? '(Media type from input)'
135
+ : this.data.mediaType
136
+ ? `(${this.data.mediaType.trim()})`
137
+ : undefined,
138
+ this.data.useTitleInput ? '(Title from input)' : this.data.title ? `Title: ${this.data.title}` : undefined,
139
+ this.data.useContextInput
140
+ ? '(Context from input)'
141
+ : this.data.context
142
+ ? `Context: ${this.data.context}`
143
+ : undefined,
144
+ ].filter((x) => x != null);
145
+ return dedent `
146
+ ${parts.join('\n')}
147
+ `;
148
+ }
149
+ async process(inputData, context) {
150
+ let data;
151
+ const mediaType = getInputOrData(this.data, inputData, 'mediaType', 'string') || 'text/plain';
152
+ const title = getInputOrData(this.data, inputData, 'title');
153
+ const contextInput = getInputOrData(this.data, inputData, 'context');
154
+ const enableCitations = getInputOrData(this.data, inputData, 'enableCitations', 'boolean');
155
+ if (this.chartNode.data.useDataInput) {
156
+ data = expectType(inputData['data'], 'binary');
157
+ }
158
+ else {
159
+ const dataRef = this.data.data?.refId;
160
+ if (!dataRef) {
161
+ throw new Error('No data ref');
162
+ }
163
+ const encodedData = context.project.data?.[dataRef];
164
+ if (!encodedData) {
165
+ throw new Error(`No data at ref ${dataRef}`);
166
+ }
167
+ data = base64ToUint8Array(encodedData);
168
+ }
169
+ return {
170
+ ['data']: {
171
+ type: 'document',
172
+ value: {
173
+ data,
174
+ mediaType: mediaType,
175
+ title,
176
+ context: contextInput,
177
+ enableCitations,
178
+ },
179
+ },
180
+ };
181
+ }
182
+ }
183
+ export const documentNode = nodeDefinition(DocumentNodeImpl, 'Document');
@@ -76,10 +76,10 @@ export class ExtractJsonNodeImpl extends NodeImpl {
76
76
  // Fall back to more manual parsing
77
77
  }
78
78
  // Find the first { or [ and the last } or ], and try parsing everything in between including them.
79
- let firstBracket = inputString.indexOf('{');
80
- let lastBracket = inputString.lastIndexOf('}');
81
- let firstSquareBracket = inputString.indexOf('[');
82
- let lastSquareBracket = inputString.lastIndexOf(']');
79
+ const firstBracket = inputString.indexOf('{');
80
+ const lastBracket = inputString.lastIndexOf('}');
81
+ const firstSquareBracket = inputString.indexOf('[');
82
+ const lastSquareBracket = inputString.lastIndexOf(']');
83
83
  const firstIndex = firstBracket >= 0 && firstSquareBracket >= 0
84
84
  ? Math.min(firstBracket, firstSquareBracket)
85
85
  : firstBracket >= 0