@n8n/ai-workflow-builder 1.8.1 → 1.10.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/dist/agents/planner.agent.d.ts +1 -0
- package/dist/agents/planner.agent.js +1 -0
- package/dist/agents/planner.agent.js.map +1 -1
- package/dist/agents/responder.agent.d.ts +16 -6
- package/dist/agents/responder.agent.js +19 -8
- package/dist/agents/responder.agent.js.map +1 -1
- package/dist/agents/supervisor.agent.d.ts +8 -5
- package/dist/agents/supervisor.agent.js +37 -21
- package/dist/agents/supervisor.agent.js.map +1 -1
- package/dist/ai-workflow-builder-agent.service.d.ts +12 -4
- package/dist/ai-workflow-builder-agent.service.js +102 -51
- package/dist/ai-workflow-builder-agent.service.js.map +1 -1
- package/dist/assistant/assistant-handler.d.ts +23 -0
- package/dist/assistant/assistant-handler.js +265 -0
- package/dist/assistant/assistant-handler.js.map +1 -0
- package/dist/assistant/index.d.ts +2 -0
- package/dist/assistant/index.js +6 -0
- package/dist/assistant/index.js.map +1 -0
- package/dist/assistant/types.d.ts +83 -0
- package/dist/assistant/types.js +3 -0
- package/dist/assistant/types.js.map +1 -0
- package/dist/build.tsbuildinfo +1 -1
- package/dist/code-builder/code-builder-agent.d.ts +31 -0
- package/dist/code-builder/code-builder-agent.js +463 -0
- package/dist/code-builder/code-builder-agent.js.map +1 -0
- package/dist/code-builder/code-workflow-builder.d.ts +34 -0
- package/dist/code-builder/code-workflow-builder.js +80 -0
- package/dist/code-builder/code-workflow-builder.js.map +1 -0
- package/dist/code-builder/constants.d.ts +62 -0
- package/dist/code-builder/constants.js +83 -0
- package/dist/code-builder/constants.js.map +1 -0
- package/dist/code-builder/engines/code-builder-node-search-engine.d.ts +19 -0
- package/dist/code-builder/engines/code-builder-node-search-engine.js +236 -0
- package/dist/code-builder/engines/code-builder-node-search-engine.js.map +1 -0
- package/dist/code-builder/handlers/agent-iteration-handler.d.ts +36 -0
- package/dist/code-builder/handlers/agent-iteration-handler.js +78 -0
- package/dist/code-builder/handlers/agent-iteration-handler.js.map +1 -0
- package/dist/code-builder/handlers/auto-finalize-handler.d.ts +29 -0
- package/dist/code-builder/handlers/auto-finalize-handler.js +50 -0
- package/dist/code-builder/handlers/auto-finalize-handler.js.map +1 -0
- package/dist/code-builder/handlers/chat-setup-handler.d.ts +57 -0
- package/dist/code-builder/handlers/chat-setup-handler.js +147 -0
- package/dist/code-builder/handlers/chat-setup-handler.js.map +1 -0
- package/dist/code-builder/handlers/final-response-handler.d.ts +29 -0
- package/dist/code-builder/handlers/final-response-handler.js +75 -0
- package/dist/code-builder/handlers/final-response-handler.js.map +1 -0
- package/dist/code-builder/handlers/parse-validate-handler.d.ts +17 -0
- package/dist/code-builder/handlers/parse-validate-handler.js +116 -0
- package/dist/code-builder/handlers/parse-validate-handler.js.map +1 -0
- package/dist/code-builder/handlers/session-chat-handler.d.ts +30 -0
- package/dist/code-builder/handlers/session-chat-handler.js +96 -0
- package/dist/code-builder/handlers/session-chat-handler.js.map +1 -0
- package/dist/code-builder/handlers/text-editor-handler.d.ts +19 -0
- package/dist/code-builder/handlers/text-editor-handler.js +230 -0
- package/dist/code-builder/handlers/text-editor-handler.js.map +1 -0
- package/dist/code-builder/handlers/text-editor-tool-handler.d.ts +44 -0
- package/dist/code-builder/handlers/text-editor-tool-handler.js +145 -0
- package/dist/code-builder/handlers/text-editor-tool-handler.js.map +1 -0
- package/dist/code-builder/handlers/text-editor.types.d.ts +68 -0
- package/dist/code-builder/handlers/text-editor.types.js +68 -0
- package/dist/code-builder/handlers/text-editor.types.js.map +1 -0
- package/dist/code-builder/handlers/tool-dispatch-handler.d.ts +47 -0
- package/dist/code-builder/handlers/tool-dispatch-handler.js +287 -0
- package/dist/code-builder/handlers/tool-dispatch-handler.js.map +1 -0
- package/dist/code-builder/handlers/validate-tool-handler.d.ts +33 -0
- package/dist/code-builder/handlers/validate-tool-handler.js +101 -0
- package/dist/code-builder/handlers/validate-tool-handler.js.map +1 -0
- package/dist/code-builder/index.d.ts +5 -0
- package/dist/code-builder/index.js +10 -0
- package/dist/code-builder/index.js.map +1 -0
- package/dist/code-builder/prompts/index.d.ts +22 -0
- package/dist/code-builder/prompts/index.js +830 -0
- package/dist/code-builder/prompts/index.js.map +1 -0
- package/dist/code-builder/state/warning-tracker.d.ts +9 -0
- package/dist/code-builder/state/warning-tracker.js +28 -0
- package/dist/code-builder/state/warning-tracker.js.map +1 -0
- package/dist/code-builder/tools/ask-assistant.tool.d.ts +12 -0
- package/dist/code-builder/tools/ask-assistant.tool.js +16 -0
- package/dist/code-builder/tools/ask-assistant.tool.js.map +1 -0
- package/dist/code-builder/tools/build-workflow.tool.d.ts +12 -0
- package/dist/code-builder/tools/build-workflow.tool.js +16 -0
- package/dist/code-builder/tools/build-workflow.tool.js.map +1 -0
- package/dist/code-builder/tools/code-builder-get.tool.d.ts +61 -0
- package/dist/code-builder/tools/code-builder-get.tool.js +399 -0
- package/dist/code-builder/tools/code-builder-get.tool.js.map +1 -0
- package/dist/code-builder/tools/code-builder-search.tool.d.ts +15 -0
- package/dist/code-builder/tools/code-builder-search.tool.js +338 -0
- package/dist/code-builder/tools/code-builder-search.tool.js.map +1 -0
- package/dist/code-builder/tools/get-suggested-nodes.tool.d.ts +14 -0
- package/dist/code-builder/tools/get-suggested-nodes.tool.js +89 -0
- package/dist/code-builder/tools/get-suggested-nodes.tool.js.map +1 -0
- package/dist/code-builder/tools/suggested-nodes-data.d.ts +11 -0
- package/dist/code-builder/tools/suggested-nodes-data.js +236 -0
- package/dist/code-builder/tools/suggested-nodes-data.js.map +1 -0
- package/dist/code-builder/triage.agent.d.ts +48 -0
- package/dist/code-builder/triage.agent.js +253 -0
- package/dist/code-builder/triage.agent.js.map +1 -0
- package/dist/code-builder/types.d.ts +51 -0
- package/dist/code-builder/types.js +3 -0
- package/dist/code-builder/types.js.map +1 -0
- package/dist/code-builder/utils/code-builder-session.d.ts +25 -0
- package/dist/code-builder/utils/code-builder-session.js +186 -0
- package/dist/code-builder/utils/code-builder-session.js.map +1 -0
- package/dist/code-builder/utils/content-extractors.d.ts +5 -0
- package/dist/code-builder/utils/content-extractors.js +88 -0
- package/dist/code-builder/utils/content-extractors.js.map +1 -0
- package/dist/code-builder/utils/discriminator-utils.d.ts +22 -0
- package/dist/code-builder/utils/discriminator-utils.js +85 -0
- package/dist/code-builder/utils/discriminator-utils.js.map +1 -0
- package/dist/code-builder/utils/extract-code.d.ts +3 -0
- package/dist/code-builder/utils/extract-code.js +23 -0
- package/dist/code-builder/utils/extract-code.js.map +1 -0
- package/dist/code-builder/utils/format-warnings.d.ts +3 -0
- package/dist/code-builder/utils/format-warnings.js +12 -0
- package/dist/code-builder/utils/format-warnings.js.map +1 -0
- package/dist/code-builder/utils/llm-response-processor.d.ts +15 -0
- package/dist/code-builder/utils/llm-response-processor.js +38 -0
- package/dist/code-builder/utils/llm-response-processor.js.map +1 -0
- package/dist/code-builder/utils/node-diff.d.ts +13 -0
- package/dist/code-builder/utils/node-diff.js +22 -0
- package/dist/code-builder/utils/node-diff.js.map +1 -0
- package/dist/code-builder/utils/node-type-parser.d.ts +18 -0
- package/dist/code-builder/utils/node-type-parser.js +67 -0
- package/dist/code-builder/utils/node-type-parser.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +4 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/llm-config.js +1 -1
- package/dist/llm-config.js.map +1 -1
- package/dist/multi-agent-workflow-subgraphs.d.ts +103 -12
- package/dist/multi-agent-workflow-subgraphs.js +158 -51
- package/dist/multi-agent-workflow-subgraphs.js.map +1 -1
- package/dist/parent-graph-state.d.ts +9 -0
- package/dist/parent-graph-state.js +8 -0
- package/dist/parent-graph-state.js.map +1 -1
- package/dist/prompts/agents/builder.prompt.d.ts +1 -0
- package/dist/prompts/agents/builder.prompt.js +86 -4
- package/dist/prompts/agents/builder.prompt.js.map +1 -1
- package/dist/prompts/agents/planner.prompt.d.ts +1 -0
- package/dist/prompts/agents/planner.prompt.js +2 -1
- package/dist/prompts/agents/planner.prompt.js.map +1 -1
- package/dist/prompts/agents/responder.prompt.d.ts +4 -1
- package/dist/prompts/agents/responder.prompt.js +72 -1
- package/dist/prompts/agents/responder.prompt.js.map +1 -1
- package/dist/prompts/agents/supervisor.prompt.d.ts +3 -1
- package/dist/prompts/agents/supervisor.prompt.js +133 -13
- package/dist/prompts/agents/supervisor.prompt.js.map +1 -1
- package/dist/prompts/builder/prompt-builder.js +13 -5
- package/dist/prompts/builder/prompt-builder.js.map +1 -1
- package/dist/prompts/builder/types.d.ts +2 -1
- package/dist/prompts/shared/deictic-resolution.d.ts +14 -0
- package/dist/prompts/shared/deictic-resolution.js +142 -0
- package/dist/prompts/shared/deictic-resolution.js.map +1 -0
- package/dist/session-manager.service.d.ts +15 -5
- package/dist/session-manager.service.js +265 -74
- package/dist/session-manager.service.js.map +1 -1
- package/dist/subgraphs/builder.subgraph.d.ts +29 -0
- package/dist/subgraphs/builder.subgraph.js +32 -6
- package/dist/subgraphs/builder.subgraph.js.map +1 -1
- package/dist/subgraphs/discovery.subgraph.d.ts +8 -0
- package/dist/subgraphs/discovery.subgraph.js +23 -0
- package/dist/subgraphs/discovery.subgraph.js.map +1 -1
- package/dist/tools/add-node.tool.d.ts +4 -4
- package/dist/tools/builder-tools.js +6 -1
- package/dist/tools/builder-tools.js.map +1 -1
- package/dist/tools/introspect.tool.d.ts +34 -0
- package/dist/tools/introspect.tool.js +125 -0
- package/dist/tools/introspect.tool.js.map +1 -0
- package/dist/tools/submit-questions.tool.d.ts +8 -8
- package/dist/types/coordination.d.ts +8 -2
- package/dist/types/coordination.js +5 -0
- package/dist/types/coordination.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/session-storage.d.ts +11 -0
- package/dist/types/session-storage.js +3 -0
- package/dist/types/session-storage.js.map +1 -0
- package/dist/types/streaming.d.ts +20 -1
- package/dist/utils/cache-control/helpers.d.ts +6 -0
- package/dist/utils/cache-control/helpers.js +42 -22
- package/dist/utils/cache-control/helpers.js.map +1 -1
- package/dist/utils/cache-control/index.d.ts +1 -1
- package/dist/utils/cache-control/index.js +3 -1
- package/dist/utils/cache-control/index.js.map +1 -1
- package/dist/utils/context-builders.d.ts +2 -0
- package/dist/utils/context-builders.js +57 -0
- package/dist/utils/context-builders.js.map +1 -1
- package/dist/utils/coordination-log.d.ts +5 -2
- package/dist/utils/coordination-log.js +14 -0
- package/dist/utils/coordination-log.js.map +1 -1
- package/dist/utils/error-sanitizer.d.ts +1 -0
- package/dist/utils/error-sanitizer.js +39 -0
- package/dist/utils/error-sanitizer.js.map +1 -0
- package/dist/utils/resource-operation-extractor.d.ts +12 -2
- package/dist/utils/resource-operation-extractor.js +12 -4
- package/dist/utils/resource-operation-extractor.js.map +1 -1
- package/dist/utils/state-modifier.d.ts +1 -1
- package/dist/utils/state-modifier.js +15 -19
- package/dist/utils/state-modifier.js.map +1 -1
- package/dist/utils/stream-processor.js +29 -8
- package/dist/utils/stream-processor.js.map +1 -1
- package/dist/utils/subgraph-helpers.d.ts +2 -0
- package/dist/utils/subgraph-helpers.js +34 -1
- package/dist/utils/subgraph-helpers.js.map +1 -1
- package/dist/utils/thread-id.d.ts +1 -0
- package/dist/utils/thread-id.js +10 -0
- package/dist/utils/thread-id.js.map +1 -0
- package/dist/utils/token-usage-tracking-handler.d.ts +14 -0
- package/dist/utils/token-usage-tracking-handler.js +55 -0
- package/dist/utils/token-usage-tracking-handler.js.map +1 -0
- package/dist/validation/types.d.ts +3 -1
- package/dist/validation/types.js +11 -0
- package/dist/validation/types.js.map +1 -1
- package/dist/validation/utils/expressions.js +1 -1
- package/dist/validation/utils/expressions.js.map +1 -1
- package/dist/workflow-builder-agent.d.ts +29 -4
- package/dist/workflow-builder-agent.js +162 -5
- package/dist/workflow-builder-agent.js.map +1 -1
- package/dist/workflow-state.d.ts +6 -0
- package/package.json +10 -6
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCodeBuilderPrompt = buildCodeBuilderPrompt;
|
|
4
|
+
const prompts_1 = require("@langchain/core/prompts");
|
|
5
|
+
const workflow_sdk_1 = require("@n8n/workflow-sdk");
|
|
6
|
+
const plan_helpers_1 = require("../../utils/plan-helpers");
|
|
7
|
+
const text_editor_handler_1 = require("../handlers/text-editor-handler");
|
|
8
|
+
const code_builder_session_1 = require("../utils/code-builder-session");
|
|
9
|
+
const extract_code_1 = require("../utils/extract-code");
|
|
10
|
+
function escapeCurlyBrackets(text) {
|
|
11
|
+
return text.replace(/\{/g, '{{').replace(/\}/g, '}}');
|
|
12
|
+
}
|
|
13
|
+
const EXPRESSION_REFERENCE = `Available variables inside \`expr('{{{{ ... }}}}')\`:
|
|
14
|
+
|
|
15
|
+
- \`$json\` — current item's JSON data from the immediate predecessor node
|
|
16
|
+
- \`$('NodeName').item.json\` — access any node's output by name
|
|
17
|
+
- \`$input.first()\` — first item from immediate predecessor
|
|
18
|
+
- \`$input.all()\` — all items from immediate predecessor
|
|
19
|
+
- \`$input.item\` — current item being processed
|
|
20
|
+
- \`$binary\` — binary data of current item from immediate predecessor
|
|
21
|
+
- \`$now\` — current date/time (Luxon DateTime). Example: \`$now.toISO()\`
|
|
22
|
+
- \`$today\` — start of today (Luxon DateTime). Example: \`$today.plus(1, 'days')\`
|
|
23
|
+
- \`$itemIndex\` — index of current item being processed
|
|
24
|
+
- \`$runIndex\` — current run index
|
|
25
|
+
- \`$execution.id\` — unique execution ID
|
|
26
|
+
- \`$execution.mode\` — 'test' or 'production'
|
|
27
|
+
- \`$workflow.id\` — workflow ID
|
|
28
|
+
- \`$workflow.name\` — workflow name
|
|
29
|
+
|
|
30
|
+
String composition — variables MUST always be inside \`{{{{ }}}}\`, never outside as JS variables:
|
|
31
|
+
|
|
32
|
+
- \`expr('Hello {{{{ $json.name }}}}, welcome!')\` — variable embedded in text
|
|
33
|
+
- \`expr('Report for {{{{ $now.toFormat("MMMM d, yyyy") }}}} - {{{{ $json.title }}}}')\` — multiple variables with method call
|
|
34
|
+
- \`expr('{{{{ $json.firstName }}}} {{{{ $json.lastName }}}}')\` — combining multiple fields
|
|
35
|
+
- \`expr('Total: {{{{ $json.items.length }}}} items, updated {{{{ $now.toISO() }}}}')\` — expressions with method calls
|
|
36
|
+
- \`expr('Status: {{{{ $json.count > 0 ? "active" : "empty" }}}}')\` — inline ternary
|
|
37
|
+
|
|
38
|
+
Dynamic data from other nodes — \`$()\` MUST always be inside \`{{{{ }}}}\`, never used as plain JavaScript:
|
|
39
|
+
|
|
40
|
+
- WRONG: \`expr('{{{{ ' + JSON.stringify($('Source').all().map(i => i.json.name)) + ' }}}}')\` — $() outside {{{{ }}}}
|
|
41
|
+
- CORRECT: \`expr('{{{{ $("Source").all().map(i => ({{ option: i.json.name }})) }}}}')\` — $() inside {{{{ }}}}
|
|
42
|
+
- CORRECT: \`expr('{{{{ {{ "fields": [{{ "values": $("Fetch Projects").all().map(i => ({{ option: i.json.name }})) }}] }} }}}}')\` — complex JSON inside {{{{ }}}}`;
|
|
43
|
+
const ADDITIONAL_FUNCTIONS = `Additional SDK functions:
|
|
44
|
+
|
|
45
|
+
- \`placeholder('hint')\` — marks a parameter value for user input. Use directly as the parameter value — never wrap in \`expr()\`, objects, or arrays.
|
|
46
|
+
Example: \`parameters: {{ url: placeholder('Your API URL (e.g. https://api.example.com/v1)') }}\`
|
|
47
|
+
|
|
48
|
+
- \`sticky('content', nodes?, config?)\` — creates a sticky note on the canvas.
|
|
49
|
+
Example: \`sticky('## Data Processing', [httpNode, setNode], {{ color: 2 }})\`
|
|
50
|
+
|
|
51
|
+
- \`.output(n)\` — selects a specific output index for multi-output nodes. IF and Switch have dedicated methods (\`onTrue/onFalse\`, \`onCase\`), but \`.output(n)\` works as a generic alternative.
|
|
52
|
+
Example: \`classifier.output(1).to(categoryB)\`
|
|
53
|
+
|
|
54
|
+
- \`.onError(handler)\` — connects a node's error output to a handler node. Requires \`onError: 'continueErrorOutput'\` in the node config.
|
|
55
|
+
Example: \`httpNode.onError(errorHandler)\` (with \`config: {{ onError: 'continueErrorOutput' }}\`)
|
|
56
|
+
|
|
57
|
+
- Additional subnode factories (all follow the same pattern as \`languageModel()\` and \`tool()\`):
|
|
58
|
+
\`memory()\`, \`outputParser()\`, \`embeddings()\`, \`vectorStore()\`, \`retriever()\`, \`documentLoader()\`, \`textSplitter()\``;
|
|
59
|
+
const ROLE = 'You are an expert n8n workflow builder. Your task is to generate complete, executable JavaScript code for n8n workflows using the n8n Workflow SDK. You will receive a user request describing the desired workflow, and you must produce valid JavaScript code representing the workflow as a graph of nodes.';
|
|
60
|
+
const RESPONSE_STYLE = `**Be extremely concise in your visible responses.** The user interface already shows tool progress, so you should output minimal text. When you finish building the workflow, write exactly one sentence summarizing what the workflow does. Nothing more.
|
|
61
|
+
|
|
62
|
+
All your reasoning and analysis should happen in your internal thinking process before generating output. Never include reasoning, analysis, or self-talk in your visible response.`;
|
|
63
|
+
const WORKFLOW_RULES = `Follow these rules strictly when generating workflows:
|
|
64
|
+
|
|
65
|
+
1. **Always use newCredential() for authentication**
|
|
66
|
+
- When a node needs credentials, always use \`newCredential('Name')\` in the credentials config
|
|
67
|
+
- NEVER use placeholder strings, fake API keys, or hardcoded auth values
|
|
68
|
+
- Example: \`credentials: {{ slackApi: newCredential('Slack Bot') }}\`
|
|
69
|
+
- The credential type must match what the node expects`;
|
|
70
|
+
const SDK_IMPORT_ESCAPED = escapeCurlyBrackets(extract_code_1.SDK_IMPORT_STATEMENT);
|
|
71
|
+
const WORKFLOW_PATTERNS = `<linear_chain>
|
|
72
|
+
\`\`\`javascript
|
|
73
|
+
${SDK_IMPORT_ESCAPED}
|
|
74
|
+
|
|
75
|
+
// 1. Define all nodes first
|
|
76
|
+
const startTrigger = trigger({{
|
|
77
|
+
type: 'n8n-nodes-base.manualTrigger',
|
|
78
|
+
version: 1,
|
|
79
|
+
config: {{ name: 'Start', position: [240, 300] }},
|
|
80
|
+
output: [{{}}]
|
|
81
|
+
}});
|
|
82
|
+
|
|
83
|
+
const fetchData = node({{
|
|
84
|
+
type: 'n8n-nodes-base.httpRequest',
|
|
85
|
+
version: 4.3,
|
|
86
|
+
config: {{ name: 'Fetch Data', parameters: {{ method: 'GET', url: '...' }}, position: [540, 300] }},
|
|
87
|
+
output: [{{ id: 1, title: 'Item 1' }}]
|
|
88
|
+
}});
|
|
89
|
+
|
|
90
|
+
const processData = node({{
|
|
91
|
+
type: 'n8n-nodes-base.set',
|
|
92
|
+
version: 3.4,
|
|
93
|
+
config: {{ name: 'Process Data', parameters: {{}}, position: [840, 300] }},
|
|
94
|
+
output: [{{ id: 1, title: 'Item 1', processed: true }}]
|
|
95
|
+
}});
|
|
96
|
+
|
|
97
|
+
// 2. Compose workflow
|
|
98
|
+
export default workflow('id', 'name')
|
|
99
|
+
.add(startTrigger)
|
|
100
|
+
.to(fetchData)
|
|
101
|
+
.to(processData);
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
</linear_chain>
|
|
105
|
+
|
|
106
|
+
<independent_sources>
|
|
107
|
+
When nodes return more than 1 item, chaining causes item multiplication: if Source A returns N items, a chained Source B runs N times instead of once.
|
|
108
|
+
|
|
109
|
+
Fix with \`executeOnce: true\` (simplest) or parallel branches + Merge (when combining results):
|
|
110
|
+
|
|
111
|
+
\`\`\`javascript
|
|
112
|
+
// sourceA outputs 10 items. sourceB outputs 10 items.
|
|
113
|
+
// sourceB runs once per item from sourceA.
|
|
114
|
+
// WRONG - processResults runs 100 times
|
|
115
|
+
// startTrigger.to(sourceA.to(sourceB.to(processResults)))
|
|
116
|
+
|
|
117
|
+
// FIX 1 - executeOnce: sourceB runs once regardless of input items
|
|
118
|
+
const sourceB = node({{ ..., config: {{ ..., executeOnce: true }} }});
|
|
119
|
+
startTrigger.to(sourceA.to(sourceB.to(processResults)));
|
|
120
|
+
|
|
121
|
+
// FIX 2 - parallel branches + Merge (combine by position)
|
|
122
|
+
// Pairs items by index, merging fields from both inputs into one item.
|
|
123
|
+
// @example input0: [{{ a: 1 }}, {{ a: 2 }}] input1: [{{ b: 10, c: 'x' }}, {{ b: 20 }}]
|
|
124
|
+
// output: [{{ a: 1, b: 10, c: 'x' }}, {{ a: 2, b: 20, c: undefined }}]
|
|
125
|
+
const combineResults = merge({{
|
|
126
|
+
version: 3.2,
|
|
127
|
+
config: {{ name: 'Combine Results', parameters: {{ mode: 'combine', combineBy: 'combineByPosition' }} }}
|
|
128
|
+
}});
|
|
129
|
+
export default workflow('id', 'name')
|
|
130
|
+
.add(startTrigger)
|
|
131
|
+
.to(sourceA.to(combineResults.input(0)))
|
|
132
|
+
.add(startTrigger)
|
|
133
|
+
.to(sourceB.to(combineResults.input(1)))
|
|
134
|
+
.add(combineResults)
|
|
135
|
+
.to(processResults);
|
|
136
|
+
|
|
137
|
+
// FIX 3 - parallel branches + Merge (append)
|
|
138
|
+
// Concatenates all items from all inputs into one list sequentially.
|
|
139
|
+
// @example input0: [{{ a: 1 }}, {{ a: 2 }}] input1: [{{ b: 10 }}]
|
|
140
|
+
// output: [{{ a: 1 }}, {{ a: 2 }}, {{ b: 10 }}]
|
|
141
|
+
const allResults = merge({{
|
|
142
|
+
version: 3.2,
|
|
143
|
+
config: {{ name: 'All Results', parameters: {{ mode: 'append' }} }}
|
|
144
|
+
}});
|
|
145
|
+
export default workflow('id', 'name')
|
|
146
|
+
.add(startTrigger)
|
|
147
|
+
.to(sourceA.to(allResults.input(0)))
|
|
148
|
+
.add(startTrigger)
|
|
149
|
+
.to(sourceB.to(allResults.input(1)))
|
|
150
|
+
.add(allResults)
|
|
151
|
+
.to(processResults);
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
</independent_sources>
|
|
155
|
+
|
|
156
|
+
<conditional_branching>
|
|
157
|
+
|
|
158
|
+
**CRITICAL:** Each branch defines a COMPLETE processing path. Chain multiple steps INSIDE the branch using .to().
|
|
159
|
+
|
|
160
|
+
\`\`\`javascript
|
|
161
|
+
// Assume other nodes are declared
|
|
162
|
+
const checkValid = ifElse({{ version: 2.2, config: {{ name: 'Check Valid', parameters: {{...}} }} }});
|
|
163
|
+
|
|
164
|
+
export default workflow('id', 'name')
|
|
165
|
+
.add(startTrigger)
|
|
166
|
+
.to(checkValid
|
|
167
|
+
.onTrue(formatData.to(enrichData.to(saveToDb))) // Chain 3 nodes on true branch
|
|
168
|
+
.onFalse(logError));
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
</conditional_branching>
|
|
172
|
+
|
|
173
|
+
<multi_way_routing>
|
|
174
|
+
|
|
175
|
+
\`\`\`javascript
|
|
176
|
+
// Assume other nodes are declared
|
|
177
|
+
const routeByPriority = switchCase({{ version: 3.2, config: {{ name: 'Route by Priority', parameters: {{...}} }} }});
|
|
178
|
+
|
|
179
|
+
export default workflow('id', 'name')
|
|
180
|
+
.add(startTrigger)
|
|
181
|
+
.to(routeByPriority
|
|
182
|
+
.onCase(0, processUrgent.to(notifyTeam.to(escalate))) // Chain of 3 nodes
|
|
183
|
+
.onCase(1, processNormal)
|
|
184
|
+
.onCase(2, archive));
|
|
185
|
+
\`\`\`
|
|
186
|
+
|
|
187
|
+
</multi_way_routing>
|
|
188
|
+
|
|
189
|
+
<parallel_execution>
|
|
190
|
+
\`\`\`javascript
|
|
191
|
+
// First declare the Merge node using merge()
|
|
192
|
+
const combineResults = merge({{
|
|
193
|
+
version: 3.2,
|
|
194
|
+
config: {{
|
|
195
|
+
name: 'Combine Results',
|
|
196
|
+
parameters: {{ mode: 'combine' }},
|
|
197
|
+
position: [840, 300]
|
|
198
|
+
}}
|
|
199
|
+
}});
|
|
200
|
+
|
|
201
|
+
// Declare branch nodes
|
|
202
|
+
const branch1 = node({{ type: 'n8n-nodes-base.httpRequest', ... }});
|
|
203
|
+
const branch2 = node({{ type: 'n8n-nodes-base.httpRequest', ... }});
|
|
204
|
+
const processResults = node({{ type: 'n8n-nodes-base.set', ... }});
|
|
205
|
+
|
|
206
|
+
// Connect branches to specific merge inputs using .input(n)
|
|
207
|
+
export default workflow('id', 'name')
|
|
208
|
+
.add(trigger({{ ... }}))
|
|
209
|
+
.to(branch1.to(combineResults.input(0))) // Connect to input 0
|
|
210
|
+
.add(trigger({{ ... }}))
|
|
211
|
+
.to(branch2.to(combineResults.input(1))) // Connect to input 1
|
|
212
|
+
.add(combineResults)
|
|
213
|
+
.to(processResults); // Process merged results
|
|
214
|
+
\`\`\`
|
|
215
|
+
|
|
216
|
+
</parallel_execution>
|
|
217
|
+
|
|
218
|
+
<batch_processing>
|
|
219
|
+
\`\`\`javascript
|
|
220
|
+
const startTrigger = trigger({{
|
|
221
|
+
type: 'n8n-nodes-base.manualTrigger',
|
|
222
|
+
version: 1,
|
|
223
|
+
config: {{ name: 'Start', position: [240, 300] }},
|
|
224
|
+
output: [{{}}]
|
|
225
|
+
}});
|
|
226
|
+
|
|
227
|
+
const fetchRecords = node({{
|
|
228
|
+
type: 'n8n-nodes-base.httpRequest',
|
|
229
|
+
version: 4.3,
|
|
230
|
+
config: {{ name: 'Fetch Records', parameters: {{ method: 'GET', url: '...' }}, position: [540, 300] }},
|
|
231
|
+
output: [{{ id: 1 }}, {{ id: 2 }}, {{ id: 3 }}]
|
|
232
|
+
}});
|
|
233
|
+
|
|
234
|
+
const finalizeResults = node({{
|
|
235
|
+
type: 'n8n-nodes-base.set',
|
|
236
|
+
version: 3.4,
|
|
237
|
+
config: {{ name: 'Finalize', parameters: {{}}, position: [1140, 200] }},
|
|
238
|
+
output: [{{ summary: 'Processed 3 records' }}]
|
|
239
|
+
}});
|
|
240
|
+
|
|
241
|
+
const processRecord = node({{
|
|
242
|
+
type: 'n8n-nodes-base.httpRequest',
|
|
243
|
+
version: 4.3,
|
|
244
|
+
config: {{ name: 'Process Record', parameters: {{ method: 'POST', url: '...' }}, position: [1140, 400] }},
|
|
245
|
+
output: [{{ id: 1, status: 'processed' }}]
|
|
246
|
+
}});
|
|
247
|
+
|
|
248
|
+
const sibNode = splitInBatches({{ version: 3, config: {{ name: 'Batch Process', parameters: {{ batchSize: 10 }}, position: [840, 300] }} }});
|
|
249
|
+
|
|
250
|
+
export default workflow('id', 'name')
|
|
251
|
+
.add(startTrigger)
|
|
252
|
+
.to(fetchRecords)
|
|
253
|
+
.to(sibNode
|
|
254
|
+
.onDone(finalizeResults)
|
|
255
|
+
.onEachBatch(processRecord.to(nextBatch(sibNode)))
|
|
256
|
+
);
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
</batch_processing>
|
|
260
|
+
|
|
261
|
+
<multiple_triggers>
|
|
262
|
+
\`\`\`javascript
|
|
263
|
+
const webhookTrigger = trigger({{
|
|
264
|
+
type: 'n8n-nodes-base.webhook',
|
|
265
|
+
version: 2.1,
|
|
266
|
+
config: {{ name: 'Webhook', position: [240, 200] }},
|
|
267
|
+
output: [{{ body: {{ data: 'webhook payload' }} }}]
|
|
268
|
+
}});
|
|
269
|
+
|
|
270
|
+
const processWebhook = node({{
|
|
271
|
+
type: 'n8n-nodes-base.set',
|
|
272
|
+
version: 3.4,
|
|
273
|
+
config: {{ name: 'Process Webhook', parameters: {{}}, position: [540, 200] }},
|
|
274
|
+
output: [{{ data: 'webhook payload', processed: true }}]
|
|
275
|
+
}});
|
|
276
|
+
|
|
277
|
+
const scheduleTrigger = trigger({{
|
|
278
|
+
type: 'n8n-nodes-base.scheduleTrigger',
|
|
279
|
+
version: 1.3,
|
|
280
|
+
config: {{ name: 'Daily Schedule', parameters: {{}}, position: [240, 500] }},
|
|
281
|
+
output: [{{}}]
|
|
282
|
+
}});
|
|
283
|
+
|
|
284
|
+
const processSchedule = node({{
|
|
285
|
+
type: 'n8n-nodes-base.set',
|
|
286
|
+
version: 3.4,
|
|
287
|
+
config: {{ name: 'Process Schedule', parameters: {{}}, position: [540, 500] }},
|
|
288
|
+
output: [{{ scheduled: true }}]
|
|
289
|
+
}});
|
|
290
|
+
|
|
291
|
+
export default workflow('id', 'name')
|
|
292
|
+
.add(webhookTrigger)
|
|
293
|
+
.to(processWebhook)
|
|
294
|
+
.add(scheduleTrigger)
|
|
295
|
+
.to(processSchedule);
|
|
296
|
+
\`\`\`
|
|
297
|
+
|
|
298
|
+
</multiple_triggers>
|
|
299
|
+
|
|
300
|
+
<fan_in>
|
|
301
|
+
\`\`\`javascript
|
|
302
|
+
// Each trigger's execution runs in COMPLETE ISOLATION.
|
|
303
|
+
// Different branches have no effect on each other.
|
|
304
|
+
// Never duplicate chains for "isolation" - it's already guaranteed.
|
|
305
|
+
|
|
306
|
+
const webhookTrigger = trigger({{
|
|
307
|
+
type: 'n8n-nodes-base.webhook',
|
|
308
|
+
version: 2.1,
|
|
309
|
+
config: {{ name: 'Webhook Trigger', position: [240, 200] }},
|
|
310
|
+
output: [{{ source: 'webhook' }}]
|
|
311
|
+
}});
|
|
312
|
+
|
|
313
|
+
const scheduleTrigger = trigger({{
|
|
314
|
+
type: 'n8n-nodes-base.scheduleTrigger',
|
|
315
|
+
version: 1.3,
|
|
316
|
+
config: {{ name: 'Daily Schedule', position: [240, 500] }},
|
|
317
|
+
output: [{{ source: 'schedule' }}]
|
|
318
|
+
}});
|
|
319
|
+
|
|
320
|
+
const processData = node({{
|
|
321
|
+
type: 'n8n-nodes-base.set',
|
|
322
|
+
version: 3.4,
|
|
323
|
+
config: {{ name: 'Process Data', parameters: {{}}, position: [540, 350] }},
|
|
324
|
+
output: [{{ processed: true }}]
|
|
325
|
+
}});
|
|
326
|
+
|
|
327
|
+
const sendNotification = node({{
|
|
328
|
+
type: 'n8n-nodes-base.slack',
|
|
329
|
+
version: 2.3,
|
|
330
|
+
config: {{ name: 'Notify Slack', parameters: {{}}, position: [840, 350] }},
|
|
331
|
+
output: [{{ ok: true }}]
|
|
332
|
+
}});
|
|
333
|
+
|
|
334
|
+
export default workflow('id', 'name')
|
|
335
|
+
.add(webhookTrigger)
|
|
336
|
+
.to(processData)
|
|
337
|
+
.to(sendNotification)
|
|
338
|
+
.add(scheduleTrigger)
|
|
339
|
+
.to(processData);
|
|
340
|
+
\`\`\`
|
|
341
|
+
|
|
342
|
+
</fan_in>
|
|
343
|
+
|
|
344
|
+
<ai_agent_basic>
|
|
345
|
+
\`\`\`javascript
|
|
346
|
+
const openAiModel = languageModel({{
|
|
347
|
+
type: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
|
|
348
|
+
version: 1.3,
|
|
349
|
+
config: {{ name: 'OpenAI Model', parameters: {{}}, position: [540, 500] }}
|
|
350
|
+
}});
|
|
351
|
+
|
|
352
|
+
const startTrigger = trigger({{
|
|
353
|
+
type: 'n8n-nodes-base.manualTrigger',
|
|
354
|
+
version: 1,
|
|
355
|
+
config: {{ name: 'Start', position: [240, 300] }},
|
|
356
|
+
output: [{{}}]
|
|
357
|
+
}});
|
|
358
|
+
|
|
359
|
+
const aiAgent = node({{
|
|
360
|
+
type: '@n8n/n8n-nodes-langchain.agent',
|
|
361
|
+
version: 3.1,
|
|
362
|
+
config: {{
|
|
363
|
+
name: 'AI Assistant',
|
|
364
|
+
parameters: {{ promptType: 'define', text: 'You are a helpful assistant' }},
|
|
365
|
+
subnodes: {{ model: openAiModel }},
|
|
366
|
+
position: [540, 300]
|
|
367
|
+
}},
|
|
368
|
+
output: [{{ output: 'AI response text' }}]
|
|
369
|
+
}});
|
|
370
|
+
|
|
371
|
+
export default workflow('ai-assistant', 'AI Assistant')
|
|
372
|
+
.add(startTrigger)
|
|
373
|
+
.to(aiAgent);
|
|
374
|
+
\`\`\`
|
|
375
|
+
|
|
376
|
+
</ai_agent_basic>
|
|
377
|
+
|
|
378
|
+
<ai_agent_with_tools>
|
|
379
|
+
\`\`\`javascript
|
|
380
|
+
const openAiModel = languageModel({{
|
|
381
|
+
type: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
|
|
382
|
+
version: 1.3,
|
|
383
|
+
config: {{
|
|
384
|
+
name: 'OpenAI Model',
|
|
385
|
+
parameters: {{}},
|
|
386
|
+
credentials: {{ openAiApi: newCredential('OpenAI') }},
|
|
387
|
+
position: [540, 500]
|
|
388
|
+
}}
|
|
389
|
+
}});
|
|
390
|
+
|
|
391
|
+
const calculatorTool = tool({{
|
|
392
|
+
type: '@n8n/n8n-nodes-langchain.toolCalculator',
|
|
393
|
+
version: 1,
|
|
394
|
+
config: {{ name: 'Calculator', parameters: {{}}, position: [700, 500] }}
|
|
395
|
+
}});
|
|
396
|
+
|
|
397
|
+
const startTrigger = trigger({{
|
|
398
|
+
type: 'n8n-nodes-base.manualTrigger',
|
|
399
|
+
version: 1,
|
|
400
|
+
config: {{ name: 'Start', position: [240, 300] }},
|
|
401
|
+
output: [{{}}]
|
|
402
|
+
}});
|
|
403
|
+
|
|
404
|
+
const aiAgent = node({{
|
|
405
|
+
type: '@n8n/n8n-nodes-langchain.agent',
|
|
406
|
+
version: 3.1,
|
|
407
|
+
config: {{
|
|
408
|
+
name: 'Math Agent',
|
|
409
|
+
parameters: {{ promptType: 'define', text: 'You can use tools to help users' }},
|
|
410
|
+
subnodes: {{ model: openAiModel, tools: [calculatorTool] }},
|
|
411
|
+
position: [540, 300]
|
|
412
|
+
}},
|
|
413
|
+
output: [{{ output: '42' }}]
|
|
414
|
+
}});
|
|
415
|
+
|
|
416
|
+
export default workflow('ai-calculator', 'AI Calculator')
|
|
417
|
+
.add(startTrigger)
|
|
418
|
+
.to(aiAgent);
|
|
419
|
+
\`\`\`
|
|
420
|
+
|
|
421
|
+
</ai_agent_with_tools>
|
|
422
|
+
|
|
423
|
+
<ai_agent_with_from_ai>
|
|
424
|
+
\`\`\`javascript
|
|
425
|
+
const openAiModel = languageModel({{
|
|
426
|
+
type: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
|
|
427
|
+
version: 1.3,
|
|
428
|
+
config: {{
|
|
429
|
+
name: 'OpenAI Model',
|
|
430
|
+
parameters: {{}},
|
|
431
|
+
credentials: {{ openAiApi: newCredential('OpenAI') }},
|
|
432
|
+
position: [540, 500]
|
|
433
|
+
}}
|
|
434
|
+
}});
|
|
435
|
+
|
|
436
|
+
const gmailTool = tool({{
|
|
437
|
+
type: 'n8n-nodes-base.gmailTool',
|
|
438
|
+
version: 1,
|
|
439
|
+
config: {{
|
|
440
|
+
name: 'Gmail Tool',
|
|
441
|
+
parameters: {{
|
|
442
|
+
sendTo: fromAi('recipient', 'Email address'),
|
|
443
|
+
subject: fromAi('subject', 'Email subject'),
|
|
444
|
+
message: fromAi('body', 'Email content')
|
|
445
|
+
}},
|
|
446
|
+
credentials: {{ gmailOAuth2: newCredential('Gmail') }},
|
|
447
|
+
position: [700, 500]
|
|
448
|
+
}}
|
|
449
|
+
}});
|
|
450
|
+
|
|
451
|
+
const startTrigger = trigger({{
|
|
452
|
+
type: 'n8n-nodes-base.manualTrigger',
|
|
453
|
+
version: 1,
|
|
454
|
+
config: {{ name: 'Start', position: [240, 300] }},
|
|
455
|
+
output: [{{}}]
|
|
456
|
+
}});
|
|
457
|
+
|
|
458
|
+
const aiAgent = node({{
|
|
459
|
+
type: '@n8n/n8n-nodes-langchain.agent',
|
|
460
|
+
version: 3.1,
|
|
461
|
+
config: {{
|
|
462
|
+
name: 'Email Agent',
|
|
463
|
+
parameters: {{ promptType: 'define', text: 'You can send emails' }},
|
|
464
|
+
subnodes: {{ model: openAiModel, tools: [gmailTool] }},
|
|
465
|
+
position: [540, 300]
|
|
466
|
+
}},
|
|
467
|
+
output: [{{ output: 'Email sent successfully' }}]
|
|
468
|
+
}});
|
|
469
|
+
|
|
470
|
+
export default workflow('ai-email', 'AI Email Sender')
|
|
471
|
+
.add(startTrigger)
|
|
472
|
+
.to(aiAgent);
|
|
473
|
+
\`\`\`
|
|
474
|
+
</ai_agent_with_from_ai>
|
|
475
|
+
|
|
476
|
+
<ai_agent_with_structured_output>
|
|
477
|
+
\`\`\`javascript
|
|
478
|
+
const structuredParser = outputParser({{
|
|
479
|
+
type: '@n8n/n8n-nodes-langchain.outputParserStructured',
|
|
480
|
+
version: 1.3,
|
|
481
|
+
config: {{
|
|
482
|
+
name: 'Structured Output Parser',
|
|
483
|
+
parameters: {{
|
|
484
|
+
schemaType: 'fromJson',
|
|
485
|
+
jsonSchemaExample: '{{{{ "sentiment": "positive", "confidence": 0.95, "summary": "brief summary" }}}}'
|
|
486
|
+
}},
|
|
487
|
+
position: [700, 500]
|
|
488
|
+
}}
|
|
489
|
+
}});
|
|
490
|
+
|
|
491
|
+
const aiAgent = node({{
|
|
492
|
+
type: '@n8n/n8n-nodes-langchain.agent',
|
|
493
|
+
version: 3.1,
|
|
494
|
+
config: {{
|
|
495
|
+
name: 'Sentiment Analyzer',
|
|
496
|
+
parameters: {{ promptType: 'define', text: 'Analyze the sentiment of the input text', hasOutputParser: true }},
|
|
497
|
+
subnodes: {{ model: openAiModel, outputParser: structuredParser }},
|
|
498
|
+
position: [540, 300]
|
|
499
|
+
}},
|
|
500
|
+
output: [{{ sentiment: 'positive', confidence: 0.95, summary: 'The text expresses satisfaction' }}]
|
|
501
|
+
}});
|
|
502
|
+
|
|
503
|
+
export default workflow('ai-sentiment', 'AI Sentiment Analyzer')
|
|
504
|
+
.add(startTrigger)
|
|
505
|
+
.to(aiAgent);
|
|
506
|
+
\`\`\`
|
|
507
|
+
</ai_agent_with_structured_output>`;
|
|
508
|
+
const MANDATORY_WORKFLOW_INTRO = '**You MUST follow these steps in order. Do NOT produce visible output until the final step — only tool calls.**';
|
|
509
|
+
const ANALYZE_USER_REQUEST = `
|
|
510
|
+
Analyze the user request internally. Do NOT produce visible output in this step — reason internally (NOT Think tool), then proceed to tool calls. Be concise.
|
|
511
|
+
|
|
512
|
+
1. **Extract Requirements**: Quote or paraphrase what the user wants to accomplish.
|
|
513
|
+
|
|
514
|
+
2. **Identify All Relevant Workflow Technique Categories that might be relevant**:
|
|
515
|
+
- chatbot: Receiving chat messages and replying (built-in chat, Telegram, Slack, etc.)
|
|
516
|
+
- notification: Sending alerts or updates via email, chat, SMS when events occur
|
|
517
|
+
- scheduling: Running actions at specific times or intervals
|
|
518
|
+
- data_transformation: Cleaning, formatting, or restructuring data
|
|
519
|
+
- data_persistence: Storing, updating, or retrieving records from persistent storage
|
|
520
|
+
- data_extraction: Pulling specific information from structured or unstructured inputs
|
|
521
|
+
- document_processing: Taking action on content within files (PDFs, Word docs, images)
|
|
522
|
+
- form_input: Gathering data from users via forms
|
|
523
|
+
- content_generation: Creating text, images, audio, or video
|
|
524
|
+
- triage: Classifying data for routing or prioritization
|
|
525
|
+
- scraping_and_research: Collecting current/live information from external sources. ALSO applies when the workflow analyzes, monitors, tracks, or reports on real-world entities (stocks, weather, news, prices, people, companies) — the AI model alone cannot provide up-to-date data
|
|
526
|
+
|
|
527
|
+
3. **Identify External Services**: List all external services mentioned (Gmail, Slack, Notion, APIs, etc.)
|
|
528
|
+
- Do NOT assume you know the node names yet
|
|
529
|
+
- Just identify what services need to be connected
|
|
530
|
+
|
|
531
|
+
4. **Identify Workflow Concepts**: What patterns are needed?
|
|
532
|
+
- Trigger type — every workflow must start with one (\`manualTrigger\` for testing, \`scheduleTrigger\` for recurring, \`webhook\` for external)
|
|
533
|
+
- Branching/routing (if/else, switch, merge)
|
|
534
|
+
- Loops (batch processing)
|
|
535
|
+
- Data transformation needs
|
|
536
|
+
`;
|
|
537
|
+
const READ_APPROVED_PLAN = `
|
|
538
|
+
Read the approved plan provided in <approved_plan>. Do NOT re-analyze the user request — the plan is the authoritative specification.
|
|
539
|
+
Be EXTREMELY concise.
|
|
540
|
+
|
|
541
|
+
1. **Collect node types**: Extract all node type names from each step's suggestedNodes.
|
|
542
|
+
2. **Note the trigger**: Identify the trigger type described in the plan.
|
|
543
|
+
3. **Note additional specs**: Review any additionalSpecs for constraints.
|
|
544
|
+
|
|
545
|
+
If you have everything you need to build a workflow, continue directly with step 2b, reviewing search results, and step 3, planning workflow design.
|
|
546
|
+
`;
|
|
547
|
+
const GET_SUGGESTED_NODES = `
|
|
548
|
+
Do NOT produce visible output — only the tool call. Call \`get_suggested_nodes\` with the workflow technique categories identified in Step 1:
|
|
549
|
+
|
|
550
|
+
\`\`\`
|
|
551
|
+
get_suggested_nodes({{ categories: ["chatbot", "notification"] }})
|
|
552
|
+
\`\`\`
|
|
553
|
+
|
|
554
|
+
This returns curated node recommendations with pattern hints and configuration guidance.
|
|
555
|
+
`;
|
|
556
|
+
const SEARCH_NODES_BUILD = `
|
|
557
|
+
Do NOT produce visible output — only the tool call. Be EXTREMELY concise. Call \`search_nodes\` to find specific nodes for services identified in Step 1 and ALL node types you plan to use:
|
|
558
|
+
|
|
559
|
+
\`\`\`
|
|
560
|
+
search_nodes({{ queries: ["gmail", "slack", "schedule trigger", "set", ...] }})
|
|
561
|
+
\`\`\`
|
|
562
|
+
|
|
563
|
+
Search for:
|
|
564
|
+
- External services (Gmail, Slack, etc.)
|
|
565
|
+
- Workflow concepts (schedule, webhook, etc.)
|
|
566
|
+
- **Utility nodes you'll need** (set/edit fields, filter, if, code, merge, switch, etc.)
|
|
567
|
+
- AI-related terms if needed
|
|
568
|
+
- **Nodes from suggested results you plan to use**
|
|
569
|
+
`;
|
|
570
|
+
const SEARCH_NODES_PLAN = `
|
|
571
|
+
Do NOT produce visible output — only the tool call. Be EXTREMELY concise. Call \`search_nodes\` with node types from the plan's suggestedNodes to get discriminators, versions, and related nodes:
|
|
572
|
+
|
|
573
|
+
\`\`\`
|
|
574
|
+
search_nodes({{ queries: ["httpRequest", "slack", "schedule trigger", "set", ...] }})
|
|
575
|
+
\`\`\`
|
|
576
|
+
|
|
577
|
+
Search for:
|
|
578
|
+
- All node types listed in the plan's suggestedNodes
|
|
579
|
+
- The trigger type mentioned in the plan
|
|
580
|
+
- **Utility nodes you'll need** (set/edit fields, filter, if, code, merge, switch, etc.)
|
|
581
|
+
`;
|
|
582
|
+
const SEARCH_NODES_PREFETCHED = `
|
|
583
|
+
The search results for the plan's suggestedNodes are pre-fetched in <node_search_results>. Read those results carefully.
|
|
584
|
+
If you need additional nodes not covered (trigger, utility nodes (set/edit fields, filter, if, code, merge, switch, etc.)), call \`search_nodes\`. Otherwise proceed to the next step.
|
|
585
|
+
|
|
586
|
+
\`\`\`
|
|
587
|
+
search_nodes({{ queries: ["httpRequest", "slack", "schedule trigger", "set", ...] }})
|
|
588
|
+
\`\`\`
|
|
589
|
+
`;
|
|
590
|
+
const REVIEW_RESULTS_BUILD = `
|
|
591
|
+
Review all results internally. Do NOT produce visible output in this step. Be EXTREMELY concise.
|
|
592
|
+
|
|
593
|
+
For each service/concept searched, list the matching node(s) found:
|
|
594
|
+
- Note which nodes have [TRIGGER] tags for trigger nodes
|
|
595
|
+
- Note discriminator requirements (resource/operation or mode) for each node
|
|
596
|
+
- Note [RELATED] nodes that might be useful
|
|
597
|
+
- Note @relatedNodes with relationHints for complementary nodes
|
|
598
|
+
- **Pay special attention to @builderHint and @example annotations** — write these out as they are guides specifically meant to help you choose the right node configurations
|
|
599
|
+
- Review patternHints and notes from get_suggested_nodes. If multiple categories were returned, focus on the most relevant patternHint for the user's core request — don't try to follow all of them
|
|
600
|
+
- It's OK for this section to be quite long if many nodes were found
|
|
601
|
+
|
|
602
|
+
If you have everything you need to build a workflow, continue to step 3, planning the workflow design.
|
|
603
|
+
`;
|
|
604
|
+
const REVIEW_RESULTS_PLAN = `
|
|
605
|
+
Do NOT produce visible output in this step. Only internal thinking. Be EXTREMELY concise.
|
|
606
|
+
|
|
607
|
+
For each node searched, list the matching node(s) found:
|
|
608
|
+
- Note which nodes have [TRIGGER] tags for trigger nodes
|
|
609
|
+
- Note discriminator requirements (resource/operation or mode) for each node
|
|
610
|
+
- Note [RELATED] nodes that might be useful
|
|
611
|
+
- Note @relatedNodes with relationHints for complementary nodes
|
|
612
|
+
- **Pay special attention to @builderHint and @example annotations** — write these out as they are guides specifically meant to help you choose the right node configurations
|
|
613
|
+
- It's OK for this section to be quite long if many nodes were found
|
|
614
|
+
|
|
615
|
+
If you have everything you need to build a workflow, continue to step 3, planning the workflow design.
|
|
616
|
+
`;
|
|
617
|
+
const DESIGN_WORKFLOW_BUILD = `
|
|
618
|
+
Make design decisions internally based on the reviewed results. Do NOT produce visible output in this step.
|
|
619
|
+
|
|
620
|
+
1. **Select Nodes**: Based on search results AND suggested nodes, choose specific nodes:
|
|
621
|
+
- Use dedicated integration nodes when available (from search)
|
|
622
|
+
- Only use HTTP Request if no dedicated node was found
|
|
623
|
+
- Note discriminators needed for each node
|
|
624
|
+
- Use the most relevant patternHint as a starting template for your workflow structure. When multiple patternHints were returned, pick the one that best matches the user's core goal — don't merge all hints into one structure
|
|
625
|
+
- Review node notes from all categories for recommended additions the user may not have explicitly requested (e.g., a storage step, memory for agents)
|
|
626
|
+
- **If you identified \`scraping_and_research\` in Step 1, you MUST include a data-fetching node or tool** (e.g., SerpApi tool, Perplexity, HTTP Request). Do not rely on the AI model's training data for real-world information — commit to the data source you identified earlier
|
|
627
|
+
|
|
628
|
+
2. **Map Node Connections**:
|
|
629
|
+
- Is this linear, branching, parallel, or looped? Or merge to combine parallel branches?
|
|
630
|
+
- **Trace item counts**: For each connection A → B, if A returns N items, should B run N times or just once? If B doesn't need A's items (e.g., it fetches from an independent source), either set \`executeOnce: true\` on B or use parallel branches + Merge to combine results.
|
|
631
|
+
- Which nodes connect to which?
|
|
632
|
+
- Draw out the flow in text form (e.g., "Trigger → Node A → Node B → Node C" or "Trigger → Node A → [Node B (true), Node C (false)]")
|
|
633
|
+
- **Handling convergence after branches**: When a node receives data from multiple paths (after Switch, IF, Merge): use optional chaining \`expr('{{{{ $json.data?.approved ?? $json.status }}}}')\`, reference a node that ALWAYS runs \`expr("{{{{ $('Webhook').item.json.field }}}}")\`, or normalize data before convergence with Set nodes
|
|
634
|
+
|
|
635
|
+
3. **Prepare get_node_types Call**: Write the exact call including discriminators
|
|
636
|
+
|
|
637
|
+
It's OK for this section to be quite long as you work through the design.
|
|
638
|
+
**Pay attention to @builderHint annotations in the type definitions** - these provide critical guidance on how to correctly configure node parameters.
|
|
639
|
+
`;
|
|
640
|
+
const DESIGN_WORKFLOW_PLAN = `
|
|
641
|
+
Use thinking to make design decisions based on the search results and the approved plan's steps. Do NOT produce visible output in this step.
|
|
642
|
+
|
|
643
|
+
1. **Select Nodes**: Based on search results and the plan's steps, choose specific nodes:
|
|
644
|
+
- Use dedicated integration nodes when available (from search)
|
|
645
|
+
- Only use HTTP Request if no dedicated node was found
|
|
646
|
+
- Note discriminators needed for each node
|
|
647
|
+
- Follow the plan's step sequence as the workflow structure
|
|
648
|
+
|
|
649
|
+
2. **Map Node Connections**:
|
|
650
|
+
- Is this linear, branching, parallel, or looped? Or merge to combine parallel branches?
|
|
651
|
+
- **Trace item counts**: For each connection A → B, if A returns N items, should B run N times or just once? If B doesn't need A's items (e.g., it fetches from an independent source), either set \`executeOnce: true\` on B or use parallel branches + Merge to combine results.
|
|
652
|
+
- Which nodes connect to which?
|
|
653
|
+
- Draw out the flow in text form (e.g., "Trigger → Node A → Node B → Node C" or "Trigger → Node A → [Node B (true), Node C (false)]")
|
|
654
|
+
- **Handling convergence after branches**: When a node receives data from multiple paths (after Switch, IF, Merge): use optional chaining \`expr('{{{{ $json.data?.approved ?? $json.status }}}}')\`, reference a node that ALWAYS runs \`expr("{{{{ $('Webhook').item.json.field }}}}")\`, or normalize data before convergence with Set nodes
|
|
655
|
+
|
|
656
|
+
3. **Prepare get_node_types Call**: Write the exact call including discriminators
|
|
657
|
+
|
|
658
|
+
It's OK for this section to be quite long as you work through the design.
|
|
659
|
+
**Pay attention to @builderHint annotations in the type definitions** - these provide critical guidance on how to correctly configure node parameters.
|
|
660
|
+
`;
|
|
661
|
+
const STEPS_4_THROUGH_7 = `<step_4_get_node_type_definitions>
|
|
662
|
+
|
|
663
|
+
Do NOT produce visible output — only the tool call.
|
|
664
|
+
|
|
665
|
+
**MANDATORY:** Call \`get_node_types\` with ALL nodes you selected.
|
|
666
|
+
|
|
667
|
+
\`\`\`
|
|
668
|
+
get_node_types({{ nodeIds: ["n8n-nodes-base.manualTrigger", {{ nodeId: "n8n-nodes-base.gmail", resource: "message", operation: "send" }}, ...] }})
|
|
669
|
+
\`\`\`
|
|
670
|
+
|
|
671
|
+
Include discriminators for nodes that require them (shown in search results).
|
|
672
|
+
|
|
673
|
+
**DO NOT skip this step!** Guessing parameter names or versions creates invalid workflows.
|
|
674
|
+
|
|
675
|
+
**Pay attention to @builderHint annotations in the type definitions** - these provide critical guidance on how to correctly configure node parameters.
|
|
676
|
+
|
|
677
|
+
</step_4_get_node_type_definitions>
|
|
678
|
+
|
|
679
|
+
<step_5_create_or_edit_workflow>
|
|
680
|
+
|
|
681
|
+
Do NOT produce visible output — only the tool call to edit code.
|
|
682
|
+
|
|
683
|
+
Edit \`/workflow.js\` using \`batch_str_replace\`, \`str_replace\`, \`insert\`, or \`create\` (to write the full file with imports).
|
|
684
|
+
|
|
685
|
+
Rules:
|
|
686
|
+
- When making multiple edits, prefer \`batch_str_replace\` to apply all changes atomically in one call.
|
|
687
|
+
- Use exact parameter names and structures from the type definitions.
|
|
688
|
+
- Use unique variable names — never reuse builder function names (e.g. \`node\`, \`trigger\`) as variable names
|
|
689
|
+
- Use descriptive node names (Good: "Fetch Weather Data", "Check Temperature"; Bad: "HTTP Request", "Set", "If")
|
|
690
|
+
- Credentials: \`credentials: {{ slackApi: newCredential('Slack Bot') }}\` — type must match what the node expects
|
|
691
|
+
- Expressions: use \`expr()\` for any \`{{{{ }}}}\` syntax — always use single or double quotes, NOT backtick template literals
|
|
692
|
+
- e.g. \`expr('Hello {{{{ $json.name }}}}')\` or \`expr("{{{{ $('Node').item.json.field }}}}")\`
|
|
693
|
+
- For multiline expressions, use string concatenation: \`expr('Line 1\\n' + 'Line 2 {{{{ $json.value }}}}')\`
|
|
694
|
+
- WRONG: \`expr('Daily Digest - ' + $now.toFormat('MMMM d') + '\\n' + $json.output)\` — $now and $json are outside {{{{ }}}}
|
|
695
|
+
- CORRECT: \`expr('Daily Digest - {{{{ $now.toFormat("MMMM d") }}}}\\n{{{{ $json.output }}}}')\` — variables inside {{{{ }}}}
|
|
696
|
+
- WRONG: \`expr('{{{{ ' + JSON.stringify($('Node').all().map(i => i.json)) + ' }}}}')\` — $() used as JavaScript
|
|
697
|
+
- CORRECT: \`expr('{{{{ $("Node").all().map(i => i.json) }}}}')\` — $() inside {{{{ }}}} evaluated at runtime
|
|
698
|
+
- Placeholders: use \`placeholder('hint')\` directly as the parameter value, not inside \`expr()\`, objects, or arrays, etc.
|
|
699
|
+
- Every node MUST have an \`output\` property with sample data — following nodes depend on it for expressions
|
|
700
|
+
- String quoting: When a string value contains an apostrophe, use double quotes for that string.
|
|
701
|
+
Example: \`output: [{{ text: "I've arrived" }}]\`
|
|
702
|
+
- Do NOT add or edit comments. Comments are ignored and not shared with user. Use sticky(...) to provide guidance.
|
|
703
|
+
|
|
704
|
+
</step_5_create_or_edit_workflow>
|
|
705
|
+
|
|
706
|
+
<step_6_validate_workflow>
|
|
707
|
+
|
|
708
|
+
Do NOT produce visible output — only the tool call.
|
|
709
|
+
|
|
710
|
+
After writing or editing code in the previous step, call \`validate_workflow\` to check for errors:
|
|
711
|
+
|
|
712
|
+
\`\`\`
|
|
713
|
+
validate_workflow({{ path: "/workflow.js" }})
|
|
714
|
+
\`\`\`
|
|
715
|
+
|
|
716
|
+
**Only call validate_workflow after you have written or edited code.** Do not call it if no code exists yet.
|
|
717
|
+
|
|
718
|
+
If errors are reported, fix ALL relevant issues using \`batch_str_replace\` (preferred for multiple fixes) or individual str_replace/insert calls, then call \`validate_workflow\` again. Do not call validate_workflow after each individual fix — batch all fixes first, then validate once. Focus on warnings relevant to your changes and last user request.
|
|
719
|
+
|
|
720
|
+
</step_6_validate_workflow>
|
|
721
|
+
|
|
722
|
+
<step_7_finalize>
|
|
723
|
+
|
|
724
|
+
When validation passes, stop calling tools.
|
|
725
|
+
</step_7_finalize>`;
|
|
726
|
+
function wrapStep(tag, content) {
|
|
727
|
+
return `<${tag}>${content}</${tag}>`;
|
|
728
|
+
}
|
|
729
|
+
function buildBuildModeSteps() {
|
|
730
|
+
const step2Parts = [
|
|
731
|
+
wrapStep('step_2a_get_suggested_nodes', GET_SUGGESTED_NODES),
|
|
732
|
+
wrapStep('step_2b_search_for_nodes', SEARCH_NODES_BUILD),
|
|
733
|
+
wrapStep('step_2c_review_search_results', REVIEW_RESULTS_BUILD),
|
|
734
|
+
].join('\n');
|
|
735
|
+
return [
|
|
736
|
+
MANDATORY_WORKFLOW_INTRO,
|
|
737
|
+
wrapStep('step_1_analyze_user_request', ANALYZE_USER_REQUEST),
|
|
738
|
+
wrapStep('step_2_search_for_nodes', `\n${step2Parts}\n`),
|
|
739
|
+
wrapStep('step_3_plan_workflow_design', DESIGN_WORKFLOW_BUILD),
|
|
740
|
+
STEPS_4_THROUGH_7,
|
|
741
|
+
].join('\n\n');
|
|
742
|
+
}
|
|
743
|
+
function buildPlanModeSteps(hasPreSearchResults) {
|
|
744
|
+
const searchContent = hasPreSearchResults ? SEARCH_NODES_PREFETCHED : SEARCH_NODES_PLAN;
|
|
745
|
+
const step2Parts = [
|
|
746
|
+
wrapStep('step_2a_search_for_nodes', searchContent),
|
|
747
|
+
wrapStep('step_2b_review_search_results', REVIEW_RESULTS_PLAN),
|
|
748
|
+
].join('\n');
|
|
749
|
+
return [
|
|
750
|
+
MANDATORY_WORKFLOW_INTRO,
|
|
751
|
+
wrapStep('step_1_read_approved_plan', READ_APPROVED_PLAN),
|
|
752
|
+
wrapStep('step_2_search_for_nodes', `\n${step2Parts}\n`),
|
|
753
|
+
wrapStep('step_3_plan_workflow_design', DESIGN_WORKFLOW_PLAN),
|
|
754
|
+
STEPS_4_THROUGH_7,
|
|
755
|
+
].join('\n\n');
|
|
756
|
+
}
|
|
757
|
+
function buildMandatoryWorkflow(hasPlanOutput, hasPreSearchResults = false) {
|
|
758
|
+
if (hasPlanOutput) {
|
|
759
|
+
return buildPlanModeSteps(hasPreSearchResults);
|
|
760
|
+
}
|
|
761
|
+
return buildBuildModeSteps();
|
|
762
|
+
}
|
|
763
|
+
function buildUserMessageParts(currentWorkflow, historyContext, options) {
|
|
764
|
+
const parts = [];
|
|
765
|
+
if (historyContext?.previousSummary) {
|
|
766
|
+
parts.push(`<conversation_summary>\n${escapeCurlyBrackets(historyContext.previousSummary)}\n</conversation_summary>`);
|
|
767
|
+
}
|
|
768
|
+
if (historyContext?.conversationEntries && historyContext.conversationEntries.length > 0) {
|
|
769
|
+
parts.push('<previous_requests>');
|
|
770
|
+
historyContext.conversationEntries.forEach((msg, i) => {
|
|
771
|
+
parts.push(`${i + 1}. ${escapeCurlyBrackets((0, code_builder_session_1.entryToString)(msg))}`);
|
|
772
|
+
});
|
|
773
|
+
parts.push('</previous_requests>');
|
|
774
|
+
}
|
|
775
|
+
if (currentWorkflow) {
|
|
776
|
+
const workflowCode = options?.preGeneratedCode ??
|
|
777
|
+
(0, workflow_sdk_1.generateWorkflowCode)({
|
|
778
|
+
workflow: currentWorkflow,
|
|
779
|
+
executionSchema: options?.executionSchema,
|
|
780
|
+
executionData: options?.executionData,
|
|
781
|
+
expressionValues: options?.expressionValues,
|
|
782
|
+
valuesExcluded: options?.valuesExcluded,
|
|
783
|
+
pinnedNodes: options?.pinnedNodes,
|
|
784
|
+
});
|
|
785
|
+
const codeWithImport = `${extract_code_1.SDK_IMPORT_STATEMENT}\n\n${workflowCode}`;
|
|
786
|
+
const formattedCode = (0, text_editor_handler_1.formatCodeWithLineNumbers)(codeWithImport);
|
|
787
|
+
const escapedCode = escapeCurlyBrackets(formattedCode);
|
|
788
|
+
parts.push(`<workflow_file path="/workflow.js">\n${escapedCode}\n</workflow_file>`);
|
|
789
|
+
}
|
|
790
|
+
else {
|
|
791
|
+
parts.push('<workflow_file path="/workflow.js">\nNo file exists yet. Use the `create` command to write the initial workflow code.\n</workflow_file>');
|
|
792
|
+
}
|
|
793
|
+
if (options?.planOutput) {
|
|
794
|
+
parts.push(`<approved_plan>\n${escapeCurlyBrackets((0, plan_helpers_1.formatPlanAsText)(options.planOutput))}\n</approved_plan>`);
|
|
795
|
+
}
|
|
796
|
+
if (options?.preSearchResults) {
|
|
797
|
+
parts.push(`<node_search_results>\n${escapeCurlyBrackets(options.preSearchResults)}\n</node_search_results>`);
|
|
798
|
+
}
|
|
799
|
+
return parts;
|
|
800
|
+
}
|
|
801
|
+
function buildCodeBuilderPrompt(currentWorkflow, historyContext, options) {
|
|
802
|
+
const hasPlanOutput = !!options?.planOutput;
|
|
803
|
+
const hasPreSearchResults = !!options?.preSearchResults;
|
|
804
|
+
const mandatoryWorkflow = buildMandatoryWorkflow(hasPlanOutput, hasPreSearchResults);
|
|
805
|
+
const promptSections = [
|
|
806
|
+
`<role>\n${ROLE}\n</role>`,
|
|
807
|
+
`<response_style>\n${RESPONSE_STYLE}\n</response_style>`,
|
|
808
|
+
`<workflow_rules>\n${WORKFLOW_RULES}\n</workflow_rules>`,
|
|
809
|
+
`<workflow_patterns>\n${WORKFLOW_PATTERNS}\n</workflow_patterns>`,
|
|
810
|
+
`<expression_reference>\n${EXPRESSION_REFERENCE}\n</expression_reference>`,
|
|
811
|
+
`<additional_functions>\n${ADDITIONAL_FUNCTIONS}\n</additional_functions>`,
|
|
812
|
+
`<mandatory_workflow_process>\n${mandatoryWorkflow}\n</mandatory_workflow_process>`,
|
|
813
|
+
];
|
|
814
|
+
const systemMessage = promptSections.join('\n\n');
|
|
815
|
+
const userMessageParts = buildUserMessageParts(currentWorkflow, historyContext, options);
|
|
816
|
+
if (userMessageParts.length > 0) {
|
|
817
|
+
userMessageParts.push('<user_request>');
|
|
818
|
+
userMessageParts.push('{userMessage}');
|
|
819
|
+
userMessageParts.push('</user_request>');
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
userMessageParts.push('{userMessage}');
|
|
823
|
+
}
|
|
824
|
+
const userMessageTemplate = userMessageParts.join('\n');
|
|
825
|
+
return prompts_1.ChatPromptTemplate.fromMessages([
|
|
826
|
+
['system', [{ type: 'text', text: systemMessage, cache_control: { type: 'ephemeral' } }]],
|
|
827
|
+
['human', userMessageTemplate],
|
|
828
|
+
]);
|
|
829
|
+
}
|
|
830
|
+
//# sourceMappingURL=index.js.map
|