@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.
@@ -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,2 @@
1
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
2
+ export declare const validatorChain: (llm: BaseChatModel) => import("@langchain/core/runnables").Runnable<any, boolean, import("@langchain/core/runnables").RunnableConfig<Record<string, any>>>;
@@ -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"}
@@ -0,0 +1,4 @@
1
+ export * from './ai-workflow-builder.service';
2
+ export * from './types';
3
+ export * from './workflow-state';
4
+ export * from './interfaces';
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,4 @@
1
+ export interface ILicenseService {
2
+ loadCertStr(): Promise<string>;
3
+ getConsumerId(): string;
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":""}