@n8n/ai-workflow-builder 0.2.0
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/LICENSE.md +88 -0
- package/LICENSE_EE.md +27 -0
- package/dist/ai-workflow-builder.service.d.ts +25 -0
- package/dist/ai-workflow-builder.service.js +308 -0
- package/dist/ai-workflow-builder.service.js.map +1 -0
- package/dist/build.tsbuildinfo +1 -0
- package/dist/chains/connection-composer.d.ts +10 -0
- package/dist/chains/connection-composer.js +135 -0
- package/dist/chains/connection-composer.js.map +1 -0
- package/dist/chains/node-selector.d.ts +8 -0
- package/dist/chains/node-selector.js +90 -0
- package/dist/chains/node-selector.js.map +1 -0
- package/dist/chains/nodes-composer.d.ts +8 -0
- package/dist/chains/nodes-composer.js +451 -0
- package/dist/chains/nodes-composer.js.map +1 -0
- package/dist/chains/planner.d.ts +4 -0
- package/dist/chains/planner.js +87 -0
- package/dist/chains/planner.js.map +1 -0
- package/dist/chains/validator.d.ts +2 -0
- package/dist/chains/validator.js +67 -0
- package/dist/chains/validator.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +4 -0
- package/dist/interfaces.js +3 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/llm-config.d.ts +11 -0
- package/dist/llm-config.js +36 -0
- package/dist/llm-config.js.map +1 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/workflow-state.d.ts +11 -0
- package/dist/workflow-state.js +18 -0
- package/dist/workflow-state.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nodesComposerChain = exports.nodesComposerPrompt = void 0;
|
|
4
|
+
const messages_1 = require("@langchain/core/messages");
|
|
5
|
+
const prompts_1 = require("@langchain/core/prompts");
|
|
6
|
+
const tools_1 = require("@langchain/core/tools");
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const systemPrompt = new messages_1.SystemMessage(`You are an expert n8n workflow architect who creates complete node configurations for complex workflows.
|
|
10
|
+
|
|
11
|
+
## Your Task
|
|
12
|
+
Generate fully-formed n8n node configurations with properly structured parameters for each selected node.
|
|
13
|
+
|
|
14
|
+
## Reference Information
|
|
15
|
+
You will receive:
|
|
16
|
+
1. The original user workflow request
|
|
17
|
+
2. A list of selected n8n nodes with their descriptions and parameters
|
|
18
|
+
|
|
19
|
+
## Node Configuration Guidelines
|
|
20
|
+
1. CREATE PROPER STRUCTURE: Include all required fields (parameters, name, type)
|
|
21
|
+
2. USE DESCRIPTIVE NAMES: Each node name should clearly describe its function
|
|
22
|
+
3. POPULATE KEY PARAMETERS: Set values for essential parameters based on node type
|
|
23
|
+
4. MAINTAIN LOGICAL FLOW: Node parameters should enable proper data flow
|
|
24
|
+
5. FOLLOW NODE PATTERNS: Use the correct structure for each node type
|
|
25
|
+
6. ADD DOCUMENTATION: Include at least one sticky note, explaining the workflow. Include additional sticky notes for complex parts of the workflow.
|
|
26
|
+
|
|
27
|
+
## CRITICAL: Correctly Formatting n8n Expressions
|
|
28
|
+
When using expressions to reference data from other nodes:
|
|
29
|
+
- ALWAYS use the format: \`={{ $('Node Name').item.json.field }}\`
|
|
30
|
+
- NEVER omit the equals sign before the double curly braces
|
|
31
|
+
- ALWAYS use DOUBLE curly braces, never single
|
|
32
|
+
- NEVER use emojis or special characters inside expressions as they will break the expression
|
|
33
|
+
- INCORRECT: \`{ $('Node Name').item.json.field }\` (missing =, single braces)
|
|
34
|
+
- INCORRECT: \`{{ $('Node Name').item.json.field }}\` (missing =)
|
|
35
|
+
- INCORRECT: \`={{ $('👍 Node').item.json.field }}\` (contains emoji)
|
|
36
|
+
- CORRECT: \`={{ $('Previous Node').item.json.field }}\`
|
|
37
|
+
|
|
38
|
+
This format is essential for n8n to properly process the expression.
|
|
39
|
+
|
|
40
|
+
## IF Node Configuration (CRITICAL)
|
|
41
|
+
The IF node allows conditional branching based on comparing values. It has two outputs:
|
|
42
|
+
- Output 0: TRUE branch (when conditions are met)
|
|
43
|
+
- Output 1: FALSE branch (when conditions are NOT met)
|
|
44
|
+
|
|
45
|
+
### Key Points for IF Node:
|
|
46
|
+
1. MATCH OPERATOR TYPE TO DATA TYPE - Use the correct operator type that matches your data:
|
|
47
|
+
- For string values: use "type": "string" with operations like "equals", "contains", "exists"
|
|
48
|
+
- For number values: use "type": "number" with operations like "equals", "gt", "lt"
|
|
49
|
+
- For boolean values: use "type": "boolean" with operations like "equals", "true", "false"
|
|
50
|
+
- For arrays: use "type": "array" with operations like "empty", "contains"
|
|
51
|
+
- For objects: use "type": "object" with operations like "exists", "empty"
|
|
52
|
+
- For dates: use "type": "dateTime" with operations like "before", "after"
|
|
53
|
+
|
|
54
|
+
2. USE SINGLE VALUE OPERATORS CORRECTLY:
|
|
55
|
+
- Some operators like "exists", "notExists", "empty" don't need a right value
|
|
56
|
+
- For these operators, include "singleValue": true in the operator object
|
|
57
|
+
- Example: Checking if a string exists: "operator": { "type": "string", "operation": "exists", "singleValue": true }
|
|
58
|
+
|
|
59
|
+
3. USE CORRECT DATA TYPES FOR RIGHT VALUES:
|
|
60
|
+
- Number comparisons: use actual numbers (without quotes) like 5, not "5"
|
|
61
|
+
- Boolean comparisons: use true or false (without quotes), not "true" or "false"
|
|
62
|
+
- String comparisons: use quoted strings like "text"
|
|
63
|
+
- When using expressions for the right value, include the proper format: "={{ expression }}"
|
|
64
|
+
|
|
65
|
+
### IF Node Examples
|
|
66
|
+
#### Example 1: Check if a number is greater than 5
|
|
67
|
+
\`\`\`json
|
|
68
|
+
{
|
|
69
|
+
"parameters": {
|
|
70
|
+
"conditions": {
|
|
71
|
+
"options": {
|
|
72
|
+
"caseSensitive": false,
|
|
73
|
+
"leftValue": "",
|
|
74
|
+
"typeValidation": "loose"
|
|
75
|
+
},
|
|
76
|
+
"conditions": [
|
|
77
|
+
{
|
|
78
|
+
"leftValue": "={{ $('Previous Node').item.json.amount }}",
|
|
79
|
+
"rightValue": 5,
|
|
80
|
+
"operator": {
|
|
81
|
+
"type": "number",
|
|
82
|
+
"operation": "gt"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"combinator": "and"
|
|
87
|
+
},
|
|
88
|
+
"options": {
|
|
89
|
+
"ignoreCase": true,
|
|
90
|
+
"looseTypeValidation": true
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
\`\`\`
|
|
95
|
+
|
|
96
|
+
#### Example 2: Check if a string exists
|
|
97
|
+
\`\`\`json
|
|
98
|
+
{
|
|
99
|
+
"parameters": {
|
|
100
|
+
"conditions": {
|
|
101
|
+
"options": {
|
|
102
|
+
"caseSensitive": false,
|
|
103
|
+
"leftValue": "",
|
|
104
|
+
"typeValidation": "loose"
|
|
105
|
+
},
|
|
106
|
+
"conditions": [
|
|
107
|
+
{
|
|
108
|
+
"leftValue": "={{ $('Previous Node').item.json.email }}",
|
|
109
|
+
"rightValue": "",
|
|
110
|
+
"operator": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"operation": "exists",
|
|
113
|
+
"singleValue": true
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"combinator": "and"
|
|
118
|
+
},
|
|
119
|
+
"options": {
|
|
120
|
+
"ignoreCase": true,
|
|
121
|
+
"looseTypeValidation": true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
#### Example 3: Check if a boolean is true
|
|
128
|
+
\`\`\`json
|
|
129
|
+
{
|
|
130
|
+
"parameters": {
|
|
131
|
+
"conditions": {
|
|
132
|
+
"options": {
|
|
133
|
+
"caseSensitive": false,
|
|
134
|
+
"leftValue": "",
|
|
135
|
+
"typeValidation": "loose"
|
|
136
|
+
},
|
|
137
|
+
"conditions": [
|
|
138
|
+
{
|
|
139
|
+
"leftValue": "={{ $('Previous Node').item.json.isActive }}",
|
|
140
|
+
"rightValue": "",
|
|
141
|
+
"operator": {
|
|
142
|
+
"type": "boolean",
|
|
143
|
+
"operation": "true",
|
|
144
|
+
"singleValue": true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"combinator": "and"
|
|
149
|
+
},
|
|
150
|
+
"options": {
|
|
151
|
+
"ignoreCase": true,
|
|
152
|
+
"looseTypeValidation": true
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
\`\`\`
|
|
157
|
+
|
|
158
|
+
#### Example 4: Compare string value
|
|
159
|
+
\`\`\`json
|
|
160
|
+
{
|
|
161
|
+
"parameters": {
|
|
162
|
+
"conditions": {
|
|
163
|
+
"options": {
|
|
164
|
+
"caseSensitive": false,
|
|
165
|
+
"leftValue": "",
|
|
166
|
+
"typeValidation": "loose"
|
|
167
|
+
},
|
|
168
|
+
"conditions": [
|
|
169
|
+
{
|
|
170
|
+
"leftValue": "={{ $('Previous Node').item.json.status }}",
|
|
171
|
+
"rightValue": "active",
|
|
172
|
+
"operator": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"operation": "equals"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
],
|
|
178
|
+
"combinator": "and"
|
|
179
|
+
},
|
|
180
|
+
"options": {
|
|
181
|
+
"ignoreCase": true,
|
|
182
|
+
"looseTypeValidation": true
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
\`\`\`
|
|
187
|
+
|
|
188
|
+
#### Example 5: Compare boolean value
|
|
189
|
+
\`\`\`json
|
|
190
|
+
{
|
|
191
|
+
"parameters": {
|
|
192
|
+
"conditions": {
|
|
193
|
+
"options": {
|
|
194
|
+
"caseSensitive": false,
|
|
195
|
+
"leftValue": "",
|
|
196
|
+
"typeValidation": "loose"
|
|
197
|
+
},
|
|
198
|
+
"conditions": [
|
|
199
|
+
{
|
|
200
|
+
"leftValue": "={{ $('Previous Node').item.json.isVerified }}",
|
|
201
|
+
"rightValue": true,
|
|
202
|
+
"operator": {
|
|
203
|
+
"type": "boolean",
|
|
204
|
+
"operation": "equals"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
"combinator": "and"
|
|
209
|
+
},
|
|
210
|
+
"options": {
|
|
211
|
+
"ignoreCase": true,
|
|
212
|
+
"looseTypeValidation": true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
\`\`\`
|
|
217
|
+
|
|
218
|
+
### Common Operator Types and Operations
|
|
219
|
+
|
|
220
|
+
#### String Operators:
|
|
221
|
+
- "exists", "notExists", "empty", "notEmpty" (use with "singleValue": true)
|
|
222
|
+
- "equals", "notEquals", "contains", "notContains", "startsWith", "endsWith", "regex"
|
|
223
|
+
|
|
224
|
+
#### Number Operators:
|
|
225
|
+
- "exists", "notExists" (use with "singleValue": true)
|
|
226
|
+
- "equals", "notEquals", "gt" (greater than), "lt" (less than), "gte" (greater than or equal), "lte" (less than or equal)
|
|
227
|
+
|
|
228
|
+
#### Boolean Operators:
|
|
229
|
+
- "exists", "notExists" (use with "singleValue": true)
|
|
230
|
+
- "true", "false" (use with "singleValue": true)
|
|
231
|
+
- "equals", "notEquals"
|
|
232
|
+
|
|
233
|
+
#### Array Operators:
|
|
234
|
+
- "exists", "notExists", "empty", "notEmpty" (use with "singleValue": true)
|
|
235
|
+
- "contains", "notContains", "lengthEquals", "lengthNotEquals"
|
|
236
|
+
|
|
237
|
+
## Other Important Node Structures
|
|
238
|
+
|
|
239
|
+
### Set Node Structure
|
|
240
|
+
\`\`\`json
|
|
241
|
+
{
|
|
242
|
+
"parameters": {
|
|
243
|
+
"assignments": {
|
|
244
|
+
"assignments": [
|
|
245
|
+
{
|
|
246
|
+
"id": "unique-id-1",
|
|
247
|
+
"name": "property_name_1",
|
|
248
|
+
"value": "property_value_1",
|
|
249
|
+
"type": "string"
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
},
|
|
253
|
+
"options": {}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
\`\`\`
|
|
257
|
+
|
|
258
|
+
### HTTP Request Node Structures
|
|
259
|
+
|
|
260
|
+
#### GET Request
|
|
261
|
+
\`\`\`json
|
|
262
|
+
{
|
|
263
|
+
"parameters": {
|
|
264
|
+
"url": "https://example.com",
|
|
265
|
+
"sendHeaders": true,
|
|
266
|
+
"headerParameters": {
|
|
267
|
+
"parameters": [
|
|
268
|
+
{
|
|
269
|
+
"name": "header-name",
|
|
270
|
+
"value": "header-value"
|
|
271
|
+
}
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
"options": {}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
\`\`\`
|
|
278
|
+
|
|
279
|
+
#### POST Request
|
|
280
|
+
\`\`\`json
|
|
281
|
+
{
|
|
282
|
+
"parameters": {
|
|
283
|
+
"method": "POST",
|
|
284
|
+
"url": "https://example.com",
|
|
285
|
+
"sendHeaders": true,
|
|
286
|
+
"headerParameters": {
|
|
287
|
+
"parameters": [
|
|
288
|
+
{
|
|
289
|
+
"name": "header-name",
|
|
290
|
+
"value": "header-value"
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
},
|
|
294
|
+
"sendBody": true,
|
|
295
|
+
"bodyParameters": {
|
|
296
|
+
"parameters": [
|
|
297
|
+
{
|
|
298
|
+
"name": "field-name",
|
|
299
|
+
"value": "field-value"
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
},
|
|
303
|
+
"options": {}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
\`\`\`
|
|
307
|
+
|
|
308
|
+
### Sticky Note Structure
|
|
309
|
+
\`\`\`json
|
|
310
|
+
{
|
|
311
|
+
"parameters": {
|
|
312
|
+
"content": "Note content here"
|
|
313
|
+
},
|
|
314
|
+
"name": "Descriptive Name",
|
|
315
|
+
"type": "n8n-nodes-base.stickyNote",
|
|
316
|
+
"notes": true
|
|
317
|
+
}
|
|
318
|
+
\`\`\`
|
|
319
|
+
|
|
320
|
+
## Expression Examples
|
|
321
|
+
1. Reference a field from another node:
|
|
322
|
+
\`\`\`
|
|
323
|
+
"value": "={{ $('Previous Node').item.json.fieldName }}"
|
|
324
|
+
\`\`\`
|
|
325
|
+
|
|
326
|
+
2. Use an expression with string concatenation:
|
|
327
|
+
\`\`\`
|
|
328
|
+
"value": "={{ 'Hello ' + $('User Input').item.json.name }}"
|
|
329
|
+
\`\`\`
|
|
330
|
+
|
|
331
|
+
3. Access an array item:
|
|
332
|
+
\`\`\`
|
|
333
|
+
"value": "={{ $('Data Node').item.json.items[0].id }}"
|
|
334
|
+
\`\`\`
|
|
335
|
+
|
|
336
|
+
4. IMPORTANT: How to properly format text fields with expressions
|
|
337
|
+
|
|
338
|
+
### PREFERRED METHOD: Embedding expressions directly within text
|
|
339
|
+
\`\`\`
|
|
340
|
+
"text": "=ALERT: It is currently raining in {{ $('Weather Node').item.json.city }}! Temperature: {{ $('Weather Node').item.json.main.temp }}°C"
|
|
341
|
+
\`\`\`
|
|
342
|
+
|
|
343
|
+
### Alternative method: Using string concatenation (use only when needed for complex operations)
|
|
344
|
+
\`\`\`
|
|
345
|
+
"text": "={{ 'ALERT: It is currently raining in ' + $('Weather Node').item.json.city + '! Temperature: ' + $('Weather Node').item.json.temp + '°C' }}"
|
|
346
|
+
\`\`\`
|
|
347
|
+
|
|
348
|
+
## CRITICAL: Formatting Text Fields with Expressions
|
|
349
|
+
|
|
350
|
+
### KEY RULES FOR THE PREFERRED METHOD (Embedding expressions in text):
|
|
351
|
+
- Start the string with just "=" (not "={{")
|
|
352
|
+
- Place each expression inside {{ }} without the = prefix
|
|
353
|
+
- MOST READABLE and RECOMMENDED approach
|
|
354
|
+
- Example: "text": "=Status: {{ $('Node').item.json.status }} at {{ $('Node').item.json.time }}"
|
|
355
|
+
|
|
356
|
+
### KEY RULES FOR THE ALTERNATIVE METHOD (String concatenation):
|
|
357
|
+
- Only use when you need complex operations not possible with embedded expressions
|
|
358
|
+
- Enclose the entire text in a single expression with "={{ }}"
|
|
359
|
+
- Put all static text in quotes and connect with + operators
|
|
360
|
+
- Example: "text": "={{ 'Status: ' + $('Node').item.json.status + ' at ' + $('Node').item.json.time }}"
|
|
361
|
+
|
|
362
|
+
### EXAMPLES OF PREFERRED USAGE:
|
|
363
|
+
|
|
364
|
+
1. Slack message (PREFERRED):
|
|
365
|
+
\`\`\`json
|
|
366
|
+
"text": "=ALERT: It is currently raining in {{ $('Weather Node').item.json.city }}! Temperature: {{ $('Weather Node').item.json.main.temp }}°C"
|
|
367
|
+
\`\`\`
|
|
368
|
+
|
|
369
|
+
2. Email subject (PREFERRED):
|
|
370
|
+
\`\`\`json
|
|
371
|
+
"subject": "=Order #{{ $('Order Node').item.json.orderId }} Status Update"
|
|
372
|
+
\`\`\`
|
|
373
|
+
|
|
374
|
+
3. Image prompt (PREFERRED):
|
|
375
|
+
\`\`\`json
|
|
376
|
+
"prompt": "=Create an image of {{ $('Location Node').item.json.city }} during {{ $('Weather Node').item.json.weather[0].description }}"
|
|
377
|
+
\`\`\`
|
|
378
|
+
|
|
379
|
+
4. Slack message with multiple data points (PREFERRED):
|
|
380
|
+
\`\`\`json
|
|
381
|
+
"text": "=Customer {{ $('Customer Data').item.json.name }} has placed order #{{ $('Order Data').item.json.id }} for {{ $('Order Data').item.json.amount }}€"
|
|
382
|
+
\`\`\`
|
|
383
|
+
|
|
384
|
+
5. HTTP request URL (PREFERRED):
|
|
385
|
+
\`\`\`json
|
|
386
|
+
"url": "=https://api.example.com/users/{{ $('User Data').item.json.id }}/orders?status={{ $('Filter').item.json.status }}"
|
|
387
|
+
\`\`\`
|
|
388
|
+
|
|
389
|
+
### COMMON MISTAKES TO AVOID:
|
|
390
|
+
- INCORRECT: "text": "ALERT: Temperature is {{ $('Weather Node').item.json.temp }}°C" (missing = prefix)
|
|
391
|
+
- INCORRECT: "text": "={{ $('Weather Node').item.json.temp }}" (using expression for dynamic part only)
|
|
392
|
+
- INCORRECT: "text": "={{ $('⚠️ Weather').item.json.temp }}" (emoji in node name)
|
|
393
|
+
- INCORRECT: "text": "={{ 'ALERT' }} {{ $('Weather').item.json.city }}" (mixing methods)
|
|
394
|
+
|
|
395
|
+
## Output Format
|
|
396
|
+
Return valid JSON that can be consumed by the n8n platform. Your response must match the tool's required schema.`);
|
|
397
|
+
const humanTemplate = `
|
|
398
|
+
<user_workflow_prompt>
|
|
399
|
+
{user_workflow_prompt}
|
|
400
|
+
</user_workflow_prompt>
|
|
401
|
+
<selected_n8n_nodes>
|
|
402
|
+
{nodes}
|
|
403
|
+
</selected_n8n_nodes>
|
|
404
|
+
`;
|
|
405
|
+
exports.nodesComposerPrompt = prompts_1.ChatPromptTemplate.fromMessages([
|
|
406
|
+
systemPrompt,
|
|
407
|
+
prompts_1.HumanMessagePromptTemplate.fromTemplate(humanTemplate),
|
|
408
|
+
]);
|
|
409
|
+
const nodeConfigSchema = zod_1.z.object({
|
|
410
|
+
nodes: zod_1.z
|
|
411
|
+
.array(zod_1.z
|
|
412
|
+
.object({
|
|
413
|
+
parameters: zod_1.z
|
|
414
|
+
.record(zod_1.z.string(), zod_1.z.any())
|
|
415
|
+
.describe("The node's configuration parameters. Must include all required parameters for the node type to function properly. For expressions referencing other nodes, use the format: \"={{ $('Node Name').item.json.field }}\"")
|
|
416
|
+
.refine((data) => Object.keys(data).length > 0, {
|
|
417
|
+
message: 'Parameters cannot be empty',
|
|
418
|
+
}),
|
|
419
|
+
type: zod_1.z
|
|
420
|
+
.string()
|
|
421
|
+
.describe('The full node type identifier (e.g., "n8n-nodes-base.httpRequest")'),
|
|
422
|
+
name: zod_1.z
|
|
423
|
+
.string()
|
|
424
|
+
.describe('A descriptive name for the node that clearly indicates its purpose in the workflow'),
|
|
425
|
+
})
|
|
426
|
+
.describe('A complete n8n node configuration'))
|
|
427
|
+
.describe('Array of all nodes for the workflow with their complete configurations'),
|
|
428
|
+
});
|
|
429
|
+
const generateNodeConfigTool = new tools_1.DynamicStructuredTool({
|
|
430
|
+
name: 'generate_n8n_nodes',
|
|
431
|
+
description: 'Generate fully configured n8n nodes with appropriate parameters based on the workflow requirements and selected node types.',
|
|
432
|
+
schema: nodeConfigSchema,
|
|
433
|
+
func: async (input) => {
|
|
434
|
+
return { nodes: input.nodes };
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
const nodesComposerChain = (llm) => {
|
|
438
|
+
if (!llm.bindTools) {
|
|
439
|
+
throw new n8n_workflow_1.OperationalError("LLM doesn't support binding tools");
|
|
440
|
+
}
|
|
441
|
+
return exports.nodesComposerPrompt
|
|
442
|
+
.pipe(llm.bindTools([generateNodeConfigTool], {
|
|
443
|
+
tool_choice: generateNodeConfigTool.name,
|
|
444
|
+
}))
|
|
445
|
+
.pipe((x) => {
|
|
446
|
+
const toolCall = x.tool_calls?.[0];
|
|
447
|
+
return (toolCall?.args).nodes;
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
exports.nodesComposerChain = nodesComposerChain;
|
|
451
|
+
//# sourceMappingURL=nodes-composer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodes-composer.js","sourceRoot":"","sources":["../../src/chains/nodes-composer.ts"],"names":[],"mappings":";;;AAEA,uDAAyD;AACzD,qDAAyF;AACzF,iDAA8D;AAC9D,+CAAgD;AAChD,6BAAwB;AAIxB,MAAM,YAAY,GAAG,IAAI,wBAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iHAmY0E,CAAC,CAAC;AAEnH,MAAM,aAAa,GAAG;;;;;;;CAOrB,CAAC;AAEW,QAAA,mBAAmB,GAAG,4BAAkB,CAAC,YAAY,CAAC;IAClE,YAAY;IACZ,oCAA0B,CAAC,YAAY,CAAC,aAAa,CAAC;CACtD,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IACjC,KAAK,EAAE,OAAC;SACN,KAAK,CACL,OAAC;SACC,MAAM,CAAC;QACP,UAAU,EAAE,OAAC;aACX,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,GAAG,EAAE,CAAC;aAC3B,QAAQ,CACR,sNAAsN,CACtN;aACA,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;YAC/C,OAAO,EAAE,4BAA4B;SACrC,CAAC;QACH,IAAI,EAAE,OAAC;aACL,MAAM,EAAE;aACR,QAAQ,CAAC,oEAAoE,CAAC;QAChF,IAAI,EAAE,OAAC;aACL,MAAM,EAAE;aACR,QAAQ,CACR,oFAAoF,CACpF;KACF,CAAC;SACD,QAAQ,CAAC,mCAAmC,CAAC,CAC/C;SACA,QAAQ,CAAC,wEAAwE,CAAC;CACpF,CAAC,CAAC;AAEH,MAAM,sBAAsB,GAAG,IAAI,6BAAqB,CAAC;IACxD,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EACV,6HAA6H;IAC9H,MAAM,EAAE,gBAAgB;IACxB,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACrB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;CACD,CAAC,CAAC;AAEI,MAAM,kBAAkB,GAAG,CAAC,GAAkB,EAAE,EAAE;IACxD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,IAAI,+BAAgB,CAAC,mCAAmC,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,2BAAmB;SACxB,IAAI,CACJ,GAAG,CAAC,SAAS,CAAC,CAAC,sBAAsB,CAAC,EAAE;QACvC,WAAW,EAAE,sBAAsB,CAAC,IAAI;KACxC,CAAC,CACF;SACA,IAAI,CAAC,CAAC,CAAiB,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO,CAAC,QAAQ,EAAE,IAAyC,CAAA,CAAC,KAAK,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAfW,QAAA,kBAAkB,sBAe7B"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import { SystemMessage } from '@langchain/core/messages';
|
|
3
|
+
export declare const plannerPrompt: SystemMessage;
|
|
4
|
+
export declare const plannerChain: (llm: BaseChatModel) => import("@langchain/core/runnables").Runnable<any, string[], import("@langchain/core/runnables").RunnableConfig<Record<string, any>>>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.plannerChain = exports.plannerPrompt = void 0;
|
|
4
|
+
const messages_1 = require("@langchain/core/messages");
|
|
5
|
+
const prompts_1 = require("@langchain/core/prompts");
|
|
6
|
+
const tools_1 = require("@langchain/core/tools");
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
exports.plannerPrompt = new messages_1.SystemMessage(`You are a Workflow Planner for n8n, a platform that helps users automate processes across different services and APIs.
|
|
10
|
+
|
|
11
|
+
## Your Task
|
|
12
|
+
Convert user requests into clear, sequential workflow steps that can be implemented with n8n nodes. ONLY include steps that are explicitly stated or directly implied in the user request.
|
|
13
|
+
|
|
14
|
+
## Guidelines
|
|
15
|
+
1. Analyze the user request to understand their end goal and required process
|
|
16
|
+
2. Break down the automation into logical steps based on complexity - simpler workflows need fewer steps, complex ones may need more
|
|
17
|
+
3. Focus ONLY on actions mentioned directly in the user prompt
|
|
18
|
+
4. Create steps that can be mapped to n8n nodes later
|
|
19
|
+
5. Order steps sequentially from trigger to final action
|
|
20
|
+
6. Be specific about data transformations needed ONLY if mentioned in the request
|
|
21
|
+
7. NEVER add extra steps like storing data or sending notifications unless explicitly requested
|
|
22
|
+
8. Only recommend raw HTTP requests if you think there isn't a suitable n8n node
|
|
23
|
+
|
|
24
|
+
## CRITICAL REQUIREMENTS
|
|
25
|
+
- DO NOT add any steps not directly mentioned or implied in the user request
|
|
26
|
+
- DO NOT assume the user wants to store data in a database unless explicitly stated
|
|
27
|
+
- DO NOT assume the user wants to send notifications or emails unless explicitly stated
|
|
28
|
+
- DO NOT add any "nice to have" steps that aren't clearly part of the user's request
|
|
29
|
+
- Keep the workflow EXACTLY focused on what was requested, nothing more
|
|
30
|
+
|
|
31
|
+
## Output Format
|
|
32
|
+
Return ONLY a JSON object with this structure:
|
|
33
|
+
\`\`\`json
|
|
34
|
+
{
|
|
35
|
+
"steps": [
|
|
36
|
+
"[Brief action-oriented description]",
|
|
37
|
+
"[Brief action-oriented description]",
|
|
38
|
+
...
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
\`\`\`
|
|
42
|
+
|
|
43
|
+
## Examples of Good Step Descriptions
|
|
44
|
+
- "Trigger when a new email arrives in Gmail inbox"
|
|
45
|
+
- "Filter emails to only include those with attachments"
|
|
46
|
+
- "Extract data from CSV attachments"
|
|
47
|
+
- "Transform data to required format for the API"
|
|
48
|
+
- "Send HTTP request to external API with extracted data"
|
|
49
|
+
- "Post success message to Slack channel"
|
|
50
|
+
|
|
51
|
+
IMPORTANT: Do not include HTML tags, markdown formatting, or explanations outside the JSON.`);
|
|
52
|
+
const planSchema = zod_1.z.object({
|
|
53
|
+
steps: zod_1.z
|
|
54
|
+
.array(zod_1.z
|
|
55
|
+
.string()
|
|
56
|
+
.describe('A clear, action-oriented description of a single workflow step. Do not include "Step N" or similar, just the action'))
|
|
57
|
+
.min(1)
|
|
58
|
+
.describe('An ordered list of workflow steps that, when implemented, will fulfill the user request. Each step should be concise, action-oriented, and implementable with n8n nodes.'),
|
|
59
|
+
});
|
|
60
|
+
const generatePlanTool = new tools_1.DynamicStructuredTool({
|
|
61
|
+
name: 'generate_plan',
|
|
62
|
+
description: 'Convert a user workflow request into a logical sequence of clear, achievable steps that can be implemented with n8n nodes.',
|
|
63
|
+
schema: planSchema,
|
|
64
|
+
func: async (input) => {
|
|
65
|
+
return { steps: input.steps };
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const humanTemplate = '{prompt}';
|
|
69
|
+
const chatPrompt = prompts_1.ChatPromptTemplate.fromMessages([
|
|
70
|
+
exports.plannerPrompt,
|
|
71
|
+
prompts_1.HumanMessagePromptTemplate.fromTemplate(humanTemplate),
|
|
72
|
+
]);
|
|
73
|
+
const plannerChain = (llm) => {
|
|
74
|
+
if (!llm.bindTools) {
|
|
75
|
+
throw new n8n_workflow_1.OperationalError("LLM doesn't support binding tools");
|
|
76
|
+
}
|
|
77
|
+
return chatPrompt
|
|
78
|
+
.pipe(llm.bindTools([generatePlanTool], {
|
|
79
|
+
tool_choice: generatePlanTool.name,
|
|
80
|
+
}))
|
|
81
|
+
.pipe((x) => {
|
|
82
|
+
const toolCall = x.tool_calls?.[0];
|
|
83
|
+
return (toolCall?.args).steps;
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
exports.plannerChain = plannerChain;
|
|
87
|
+
//# sourceMappingURL=planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../../src/chains/planner.ts"],"names":[],"mappings":";;;AAEA,uDAAyD;AACzD,qDAAyF;AACzF,iDAA8D;AAC9D,+CAAgD;AAChD,6BAAwB;AAEX,QAAA,aAAa,GAAG,IAAI,wBAAa,CAC7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4FA0C2F,CAC3F,CAAC;AAEF,MAAM,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3B,KAAK,EAAE,OAAC;SACN,KAAK,CACL,OAAC;SACC,MAAM,EAAE;SACR,QAAQ,CACR,qHAAqH,CACrH,CACF;SACA,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACR,0KAA0K,CAC1K;CACF,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,IAAI,6BAAqB,CAAC;IAClD,IAAI,EAAE,eAAe;IACrB,WAAW,EACV,4HAA4H;IAC7H,MAAM,EAAE,UAAU;IAClB,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACrB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;CACD,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,UAAU,CAAC;AACjC,MAAM,UAAU,GAAG,4BAAkB,CAAC,YAAY,CAAC;IAClD,qBAAa;IACb,oCAA0B,CAAC,YAAY,CAAC,aAAa,CAAC;CACtD,CAAC,CAAC;AAEI,MAAM,YAAY,GAAG,CAAC,GAAkB,EAAE,EAAE;IAClD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,IAAI,+BAAgB,CAAC,mCAAmC,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,UAAU;SACf,IAAI,CACJ,GAAG,CAAC,SAAS,CAAC,CAAC,gBAAgB,CAAC,EAAE;QACjC,WAAW,EAAE,gBAAgB,CAAC,IAAI;KAClC,CAAC,CACF;SACA,IAAI,CAAC,CAAC,CAAiB,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO,CAAC,QAAQ,EAAE,IAAmC,CAAA,CAAC,KAAK,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAfW,QAAA,YAAY,gBAevB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validatorChain = void 0;
|
|
4
|
+
const messages_1 = require("@langchain/core/messages");
|
|
5
|
+
const prompts_1 = require("@langchain/core/prompts");
|
|
6
|
+
const tools_1 = require("@langchain/core/tools");
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const validatorPrompt = new messages_1.SystemMessage(`You are a workflow prompt validator for n8n. You need to analyze the user's prompt and determine
|
|
10
|
+
if they're actually trying to build a workflow that connects different online services or automates a task.
|
|
11
|
+
|
|
12
|
+
A workflow prompt should:
|
|
13
|
+
- Describe an automation or integration task
|
|
14
|
+
- Potentially mention connecting services (like Google Sheets, Slack, etc.)
|
|
15
|
+
- Describe a process that could be broken down into steps
|
|
16
|
+
- Mention something that could be automated
|
|
17
|
+
|
|
18
|
+
Examples of VALID workflow prompts:
|
|
19
|
+
- "Create a workflow that sends a Slack message when a new row is added to Google Sheets"
|
|
20
|
+
- "I want to automatically save Gmail attachments to Dropbox"
|
|
21
|
+
- "Build a workflow that posts new Twitter mentions to a Discord channel"
|
|
22
|
+
- "When I get a new lead in my CRM, add them to my email marketing list"
|
|
23
|
+
|
|
24
|
+
Examples of INVALID workflow prompts:
|
|
25
|
+
- "What's the weather like today?"
|
|
26
|
+
- "Tell me a joke"
|
|
27
|
+
- "What is n8n?"
|
|
28
|
+
- "Help me fix my computer"
|
|
29
|
+
- "What time is it?"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Analyze the prompt and determine if it's a valid workflow prompt. Respond with just true or false.`);
|
|
33
|
+
const validatorSchema = zod_1.z.object({
|
|
34
|
+
isWorkflowPrompt: zod_1.z.boolean(),
|
|
35
|
+
});
|
|
36
|
+
const validatorTool = new tools_1.DynamicStructuredTool({
|
|
37
|
+
name: 'validate_prompt',
|
|
38
|
+
description: 'Validate if the user prompt is a workflow prompt',
|
|
39
|
+
schema: validatorSchema,
|
|
40
|
+
func: async ({ isWorkflowPrompt }) => {
|
|
41
|
+
return { isWorkflowPrompt };
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
const humanTemplate = `
|
|
45
|
+
<user_prompt>
|
|
46
|
+
{prompt}
|
|
47
|
+
</user_prompt>
|
|
48
|
+
`;
|
|
49
|
+
const chatPrompt = prompts_1.ChatPromptTemplate.fromMessages([
|
|
50
|
+
validatorPrompt,
|
|
51
|
+
prompts_1.HumanMessagePromptTemplate.fromTemplate(humanTemplate),
|
|
52
|
+
]);
|
|
53
|
+
const validatorChain = (llm) => {
|
|
54
|
+
if (!llm.bindTools) {
|
|
55
|
+
throw new n8n_workflow_1.OperationalError("LLM doesn't support binding tools");
|
|
56
|
+
}
|
|
57
|
+
return chatPrompt
|
|
58
|
+
.pipe(llm.bindTools([validatorTool], {
|
|
59
|
+
tool_choice: validatorTool.name,
|
|
60
|
+
}))
|
|
61
|
+
.pipe((x) => {
|
|
62
|
+
const toolCall = x.tool_calls?.[0];
|
|
63
|
+
return (toolCall?.args).isWorkflowPrompt;
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
exports.validatorChain = validatorChain;
|
|
67
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/chains/validator.ts"],"names":[],"mappings":";;;AAEA,uDAAyD;AACzD,qDAAyF;AACzF,iDAA8D;AAC9D,+CAAgD;AAChD,6BAAwB;AAExB,MAAM,eAAe,GAAG,IAAI,wBAAa,CACxC;;;;;;;;;;;;;;;;;;;;;;;mGAuBkG,CAClG,CAAC;AAEF,MAAM,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IAChC,gBAAgB,EAAE,OAAC,CAAC,OAAO,EAAE;CAC7B,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,IAAI,6BAAqB,CAAC;IAC/C,IAAI,EAAE,iBAAiB;IACvB,WAAW,EAAE,kDAAkD;IAC/D,MAAM,EAAE,eAAe;IACvB,IAAI,EAAE,KAAK,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE;QACpC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC7B,CAAC;CACD,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG;;;;CAIrB,CAAC;AAEF,MAAM,UAAU,GAAG,4BAAkB,CAAC,YAAY,CAAC;IAClD,eAAe;IACf,oCAA0B,CAAC,YAAY,CAAC,aAAa,CAAC;CACtD,CAAC,CAAC;AAEI,MAAM,cAAc,GAAG,CAAC,GAAkB,EAAE,EAAE;IACpD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,IAAI,+BAAgB,CAAC,mCAAmC,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,UAAU;SACf,IAAI,CACJ,GAAG,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,EAAE;QAC9B,WAAW,EAAE,aAAa,CAAC,IAAI;KAC/B,CAAC,CACF;SACA,IAAI,CAAC,CAAC,CAAiB,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO,CAAC,QAAQ,EAAE,IAA6C,CAAA,CAAC,gBAAgB,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAfW,QAAA,cAAc,kBAezB"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./ai-workflow-builder.service"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
__exportStar(require("./workflow-state"), exports);
|
|
20
|
+
__exportStar(require("./interfaces"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gEAA8C;AAC9C,0CAAwB;AACxB,mDAAiC;AACjC,+CAA6B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":""}
|