@atom8n/ai-workflow-builder 1.4.3 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.tsbuildinfo +1 -1
- package/dist/prompts/chains/parameter-updater/guides/predecessor-output.d.ts +2 -0
- package/dist/prompts/chains/parameter-updater/guides/predecessor-output.js +24 -0
- package/dist/prompts/shared/node-guidance/index.d.ts +1 -0
- package/dist/prompts/shared/node-guidance/index.js +5 -0
- package/dist/prompts/shared/node-guidance/structured-output-parser.d.ts +2 -0
- package/dist/prompts/shared/node-guidance/structured-output-parser.js +34 -0
- package/dist/prompts/shared/node-recommendations/audio-generation.d.ts +2 -0
- package/dist/prompts/shared/node-recommendations/audio-generation.js +17 -0
- package/dist/prompts/shared/node-recommendations/image-generation.d.ts +2 -0
- package/dist/prompts/shared/node-recommendations/image-generation.js +17 -0
- package/dist/prompts/shared/node-recommendations/index.d.ts +3 -0
- package/dist/prompts/shared/node-recommendations/index.js +16 -0
- package/dist/prompts/shared/node-recommendations/text-manipulation.d.ts +2 -0
- package/dist/prompts/shared/node-recommendations/text-manipulation.js +26 -0
- package/dist/prompts/shared/node-recommendations/utils/format-recommendation.d.ts +2 -0
- package/dist/prompts/shared/node-recommendations/utils/format-recommendation.js +23 -0
- package/dist/prompts/shared/node-recommendations/video-generation.d.ts +2 -0
- package/dist/prompts/shared/node-recommendations/video-generation.js +14 -0
- package/dist/tools/best-practices/data-persistence.d.ts +7 -0
- package/dist/tools/best-practices/data-persistence.js +196 -0
- package/dist/tools/get-documentation.tool.d.ts +79 -0
- package/dist/tools/get-documentation.tool.js +163 -0
- package/dist/tools/rename-node.tool.d.ts +15 -0
- package/dist/tools/rename-node.tool.js +140 -0
- package/dist/types/node-guidance.d.ts +7 -0
- package/dist/types/node-guidance.js +2 -0
- package/dist/types/node-recommendations.d.ts +26 -0
- package/dist/types/node-recommendations.js +15 -0
- package/dist/utils/resource-operation-extractor.d.ts +16 -0
- package/dist/utils/resource-operation-extractor.js +147 -0
- package/dist/validation/auto-fix/auto-fix-connections.d.ts +21 -0
- package/dist/validation/auto-fix/auto-fix-connections.js +206 -0
- package/dist/validation/auto-fix/index.d.ts +2 -0
- package/dist/validation/auto-fix/index.js +5 -0
- package/package.json +7 -7
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET_DOCUMENTATION_TOOL = exports.DocumentationType = void 0;
|
|
4
|
+
exports.createGetDocumentationTool = createGetDocumentationTool;
|
|
5
|
+
const tools_1 = require("@langchain/core/tools");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
const builder_1 = require("../prompts/builder");
|
|
9
|
+
const node_recommendations_1 = require("../prompts/shared/node-recommendations");
|
|
10
|
+
const best_practices_1 = require("../tools/best-practices");
|
|
11
|
+
const progress_1 = require("../tools/helpers/progress");
|
|
12
|
+
const response_1 = require("../tools/helpers/response");
|
|
13
|
+
const categorization_1 = require("../types/categorization");
|
|
14
|
+
const node_recommendations_2 = require("../types/node-recommendations");
|
|
15
|
+
exports.DocumentationType = {
|
|
16
|
+
BEST_PRACTICES: 'best_practices',
|
|
17
|
+
NODE_RECOMMENDATIONS: 'node_recommendations',
|
|
18
|
+
};
|
|
19
|
+
const bestPracticesRequestSchema = zod_1.z.object({
|
|
20
|
+
type: zod_1.z.literal(exports.DocumentationType.BEST_PRACTICES),
|
|
21
|
+
techniques: zod_1.z
|
|
22
|
+
.array(zod_1.z.nativeEnum(categorization_1.WorkflowTechnique))
|
|
23
|
+
.min(1)
|
|
24
|
+
.describe('Workflow techniques to get best practices for'),
|
|
25
|
+
});
|
|
26
|
+
const nodeRecommendationsRequestSchema = zod_1.z.object({
|
|
27
|
+
type: zod_1.z.literal(exports.DocumentationType.NODE_RECOMMENDATIONS),
|
|
28
|
+
categories: zod_1.z
|
|
29
|
+
.array(zod_1.z.nativeEnum(node_recommendations_2.RecommendationCategory))
|
|
30
|
+
.min(1)
|
|
31
|
+
.describe('Task categories to get node recommendations for'),
|
|
32
|
+
});
|
|
33
|
+
const getDocumentationSchema = zod_1.z.object({
|
|
34
|
+
requests: zod_1.z
|
|
35
|
+
.array(zod_1.z.union([bestPracticesRequestSchema, nodeRecommendationsRequestSchema]))
|
|
36
|
+
.min(1)
|
|
37
|
+
.describe('Array of documentation requests to retrieve'),
|
|
38
|
+
});
|
|
39
|
+
function formatBestPractices(techniques) {
|
|
40
|
+
const foundDocs = [];
|
|
41
|
+
for (const technique of techniques) {
|
|
42
|
+
const doc = best_practices_1.documentation[technique];
|
|
43
|
+
if (doc) {
|
|
44
|
+
foundDocs.push(doc.getDocumentation());
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (foundDocs.length === 0) {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
return (0, builder_1.prompt)().section('best_practices', foundDocs.join('\n---\n')).build();
|
|
51
|
+
}
|
|
52
|
+
function formatCategory(category, content) {
|
|
53
|
+
return (0, builder_1.prompt)().section(category, content).build();
|
|
54
|
+
}
|
|
55
|
+
function formatNodeRecommendations(categories) {
|
|
56
|
+
const foundRecs = [];
|
|
57
|
+
for (const category of categories) {
|
|
58
|
+
const rec = node_recommendations_1.recommendations[category];
|
|
59
|
+
if (rec) {
|
|
60
|
+
foundRecs.push(formatCategory(category, (0, node_recommendations_1.formatRecommendation)(rec.recommendation)));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (foundRecs.length === 0) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
return (0, builder_1.prompt)().section('node_recommendations', foundRecs.join('\n')).build();
|
|
67
|
+
}
|
|
68
|
+
exports.GET_DOCUMENTATION_TOOL = {
|
|
69
|
+
toolName: 'get_documentation',
|
|
70
|
+
displayTitle: 'Getting documentation',
|
|
71
|
+
};
|
|
72
|
+
function createGetDocumentationTool() {
|
|
73
|
+
const dynamicTool = (0, tools_1.tool)((input, config) => {
|
|
74
|
+
const reporter = (0, progress_1.createProgressReporter)(config, exports.GET_DOCUMENTATION_TOOL.toolName, exports.GET_DOCUMENTATION_TOOL.displayTitle);
|
|
75
|
+
try {
|
|
76
|
+
const validatedInput = getDocumentationSchema.parse(input);
|
|
77
|
+
const { requests } = validatedInput;
|
|
78
|
+
reporter.start(validatedInput);
|
|
79
|
+
reporter.progress(`Processing ${requests.length} documentation request(s)...`);
|
|
80
|
+
const results = [];
|
|
81
|
+
let bestPracticesContent;
|
|
82
|
+
const allTechniques = [];
|
|
83
|
+
for (const request of requests) {
|
|
84
|
+
if (request.type === exports.DocumentationType.BEST_PRACTICES) {
|
|
85
|
+
const { techniques } = request;
|
|
86
|
+
const availableDocs = techniques.filter((t) => best_practices_1.documentation[t]);
|
|
87
|
+
if (availableDocs.length > 0) {
|
|
88
|
+
const content = formatBestPractices(techniques);
|
|
89
|
+
results.push(content);
|
|
90
|
+
bestPracticesContent = content;
|
|
91
|
+
allTechniques.push(...techniques);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const { categories } = request;
|
|
96
|
+
const availableRecs = categories.filter((c) => node_recommendations_1.recommendations[c]);
|
|
97
|
+
if (availableRecs.length > 0) {
|
|
98
|
+
results.push(formatNodeRecommendations(categories));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (results.length === 0) {
|
|
103
|
+
const message = 'No documentation available for the requested items.';
|
|
104
|
+
reporter.complete({ requests: requests.length, found: 0 });
|
|
105
|
+
return (0, response_1.createSuccessResponse)(config, message);
|
|
106
|
+
}
|
|
107
|
+
const message = results.join('\n\n');
|
|
108
|
+
reporter.complete({ requests: requests.length, found: results.length });
|
|
109
|
+
const stateUpdates = {};
|
|
110
|
+
if (bestPracticesContent) {
|
|
111
|
+
stateUpdates.bestPractices = bestPracticesContent;
|
|
112
|
+
stateUpdates.techniqueCategories = allTechniques;
|
|
113
|
+
}
|
|
114
|
+
return (0, response_1.createSuccessResponse)(config, message, Object.keys(stateUpdates).length > 0 ? stateUpdates : undefined);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error instanceof zod_1.z.ZodError) {
|
|
118
|
+
const validationError = new errors_1.ValidationError('Invalid input parameters', {
|
|
119
|
+
extra: { errors: error.errors },
|
|
120
|
+
});
|
|
121
|
+
reporter.error(validationError);
|
|
122
|
+
return (0, response_1.createErrorResponse)(config, validationError);
|
|
123
|
+
}
|
|
124
|
+
const toolError = new errors_1.ToolExecutionError(error instanceof Error ? error.message : 'Unknown error occurred', {
|
|
125
|
+
toolName: exports.GET_DOCUMENTATION_TOOL.toolName,
|
|
126
|
+
cause: error instanceof Error ? error : undefined,
|
|
127
|
+
});
|
|
128
|
+
reporter.error(toolError);
|
|
129
|
+
return (0, response_1.createErrorResponse)(config, toolError);
|
|
130
|
+
}
|
|
131
|
+
}, {
|
|
132
|
+
name: exports.GET_DOCUMENTATION_TOOL.toolName,
|
|
133
|
+
description: `Retrieve documentation to guide workflow building.
|
|
134
|
+
|
|
135
|
+
TYPE: best_practices
|
|
136
|
+
Use when you need guidance on workflow patterns and implementation.
|
|
137
|
+
Requires: techniques array (workflow techniques to get practices for)
|
|
138
|
+
Returns: Best practices including recommended nodes, common pitfalls, performance tips.
|
|
139
|
+
|
|
140
|
+
TYPE: node_recommendations
|
|
141
|
+
Use when user wants AI capabilities but doesn't specify exact nodes/providers.
|
|
142
|
+
Requires: categories array (task categories to get recommendations for)
|
|
143
|
+
Returns: Default node recommendations with alternatives.
|
|
144
|
+
|
|
145
|
+
Do NOT use node_recommendations when:
|
|
146
|
+
- User explicitly names a provider (e.g., "use Claude", "with OpenAI", "using Gemini")
|
|
147
|
+
- User specifies exact node names
|
|
148
|
+
|
|
149
|
+
Available techniques for best_practices:
|
|
150
|
+
- trigger, loop, branch, subroutine, pagination, parallel_execution, error_handling, scheduling, rate_limiting, batch_processing, ai_agent, ai_chain, rag, data_transformation, http_request
|
|
151
|
+
|
|
152
|
+
Available categories for node_recommendations:
|
|
153
|
+
- text_manipulation: summarization, analysis, extraction, classification, chat
|
|
154
|
+
- image_generation: generate, analyze, edit images
|
|
155
|
+
- video_generation: create videos from text/images
|
|
156
|
+
- audio_generation: text-to-speech, transcription, translation`,
|
|
157
|
+
schema: getDocumentationSchema,
|
|
158
|
+
});
|
|
159
|
+
return {
|
|
160
|
+
tool: dynamicTool,
|
|
161
|
+
...exports.GET_DOCUMENTATION_TOOL,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Logger } from '@n8n/backend-common';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { BuilderTool, BuilderToolBase } from '../utils/stream-processor';
|
|
4
|
+
export declare const renameNodeSchema: z.ZodObject<{
|
|
5
|
+
nodeId: z.ZodString;
|
|
6
|
+
newName: z.ZodString;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
nodeId: string;
|
|
9
|
+
newName: string;
|
|
10
|
+
}, {
|
|
11
|
+
nodeId: string;
|
|
12
|
+
newName: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare const RENAME_NODE_TOOL: BuilderToolBase;
|
|
15
|
+
export declare function createRenameNodeTool(logger?: Logger): BuilderTool;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RENAME_NODE_TOOL = exports.renameNodeSchema = void 0;
|
|
4
|
+
exports.createRenameNodeTool = createRenameNodeTool;
|
|
5
|
+
const tools_1 = require("@langchain/core/tools");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
const progress_1 = require("./helpers/progress");
|
|
9
|
+
const response_1 = require("./helpers/response");
|
|
10
|
+
const state_1 = require("./helpers/state");
|
|
11
|
+
const validation_1 = require("./helpers/validation");
|
|
12
|
+
exports.renameNodeSchema = zod_1.z.object({
|
|
13
|
+
nodeId: zod_1.z.string().describe('The UUID of the node to rename'),
|
|
14
|
+
newName: zod_1.z.string().min(1).describe('The new name for the node'),
|
|
15
|
+
});
|
|
16
|
+
exports.RENAME_NODE_TOOL = {
|
|
17
|
+
toolName: 'rename_node',
|
|
18
|
+
displayTitle: 'Renaming node',
|
|
19
|
+
};
|
|
20
|
+
function buildResponseMessage(oldName, newName) {
|
|
21
|
+
return `Successfully renamed node from "${oldName}" to "${newName}"`;
|
|
22
|
+
}
|
|
23
|
+
function createRenameNodeTool(logger) {
|
|
24
|
+
const dynamicTool = (0, tools_1.tool)((input, config) => {
|
|
25
|
+
const reporter = (0, progress_1.createProgressReporter)(config, exports.RENAME_NODE_TOOL.toolName, exports.RENAME_NODE_TOOL.displayTitle);
|
|
26
|
+
try {
|
|
27
|
+
const validatedInput = exports.renameNodeSchema.parse(input);
|
|
28
|
+
reporter.start(validatedInput);
|
|
29
|
+
const state = (0, state_1.getWorkflowState)();
|
|
30
|
+
const workflow = (0, state_1.getCurrentWorkflow)(state);
|
|
31
|
+
(0, progress_1.reportProgress)(reporter, 'Finding node to rename...');
|
|
32
|
+
const node = (0, validation_1.validateNodeExists)(validatedInput.nodeId, workflow.nodes);
|
|
33
|
+
if (!node) {
|
|
34
|
+
const error = (0, validation_1.createNodeNotFoundError)(validatedInput.nodeId);
|
|
35
|
+
reporter.error(error);
|
|
36
|
+
return (0, response_1.createErrorResponse)(config, error);
|
|
37
|
+
}
|
|
38
|
+
const oldName = node.name;
|
|
39
|
+
const newName = validatedInput.newName;
|
|
40
|
+
if (oldName === newName) {
|
|
41
|
+
const validationError = new errors_1.ValidationError(`Node "${oldName}" already has this name`, {
|
|
42
|
+
field: 'newName',
|
|
43
|
+
value: newName,
|
|
44
|
+
});
|
|
45
|
+
const error = {
|
|
46
|
+
message: validationError.message,
|
|
47
|
+
code: 'SAME_NAME',
|
|
48
|
+
details: {
|
|
49
|
+
nodeId: validatedInput.nodeId,
|
|
50
|
+
currentName: oldName,
|
|
51
|
+
newName,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
reporter.error(error);
|
|
55
|
+
return (0, response_1.createErrorResponse)(config, error);
|
|
56
|
+
}
|
|
57
|
+
const existingNode = workflow.nodes.find((n) => n.name === newName && n.id !== validatedInput.nodeId);
|
|
58
|
+
if (existingNode) {
|
|
59
|
+
const validationError = new errors_1.ValidationError(`A node with the name "${newName}" already exists`, {
|
|
60
|
+
field: 'newName',
|
|
61
|
+
value: newName,
|
|
62
|
+
});
|
|
63
|
+
const error = {
|
|
64
|
+
message: validationError.message,
|
|
65
|
+
code: 'NAME_CONFLICT',
|
|
66
|
+
details: {
|
|
67
|
+
nodeId: validatedInput.nodeId,
|
|
68
|
+
newName,
|
|
69
|
+
conflictingNodeId: existingNode.id,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
reporter.error(error);
|
|
73
|
+
return (0, response_1.createErrorResponse)(config, error);
|
|
74
|
+
}
|
|
75
|
+
logger?.debug('\n=== Rename Node Tool ===');
|
|
76
|
+
logger?.debug(`Renaming node: "${oldName}" -> "${newName}"`);
|
|
77
|
+
(0, progress_1.reportProgress)(reporter, `Renaming "${oldName}" to "${newName}"...`);
|
|
78
|
+
const message = buildResponseMessage(oldName, newName);
|
|
79
|
+
logger?.debug('Node will be renamed');
|
|
80
|
+
const output = {
|
|
81
|
+
nodeId: validatedInput.nodeId,
|
|
82
|
+
oldName,
|
|
83
|
+
newName,
|
|
84
|
+
message,
|
|
85
|
+
};
|
|
86
|
+
const stateUpdates = (0, state_1.renameNodeInWorkflow)(validatedInput.nodeId, oldName, newName);
|
|
87
|
+
reporter.complete(output);
|
|
88
|
+
return (0, response_1.createSuccessResponse)(config, message, stateUpdates);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
let toolError;
|
|
92
|
+
if (error instanceof zod_1.z.ZodError) {
|
|
93
|
+
const validationError = new errors_1.ValidationError('Invalid rename node parameters', {
|
|
94
|
+
field: error.errors[0]?.path.join('.'),
|
|
95
|
+
value: error.errors[0]?.message,
|
|
96
|
+
});
|
|
97
|
+
toolError = {
|
|
98
|
+
message: validationError.message,
|
|
99
|
+
code: 'VALIDATION_ERROR',
|
|
100
|
+
details: error.errors,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
toolError = {
|
|
105
|
+
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
106
|
+
code: 'EXECUTION_ERROR',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
reporter.error(toolError);
|
|
110
|
+
return (0, response_1.createErrorResponse)(config, toolError);
|
|
111
|
+
}
|
|
112
|
+
}, {
|
|
113
|
+
name: exports.RENAME_NODE_TOOL.toolName,
|
|
114
|
+
description: `Rename a node in the workflow. This updates the node's display name and automatically updates all connection references.
|
|
115
|
+
|
|
116
|
+
USAGE:
|
|
117
|
+
Use this tool when you need to give a node a more descriptive or meaningful name.
|
|
118
|
+
|
|
119
|
+
PARAMETERS:
|
|
120
|
+
- nodeId: The UUID of the node to rename
|
|
121
|
+
- newName: The new name for the node (must be unique in the workflow)
|
|
122
|
+
|
|
123
|
+
EXAMPLES:
|
|
124
|
+
1. Rename a generic node:
|
|
125
|
+
nodeId: "abc-123", newName: "Process Customer Data"
|
|
126
|
+
|
|
127
|
+
2. Rename after duplicating:
|
|
128
|
+
nodeId: "def-456", newName: "Send Welcome Email"
|
|
129
|
+
|
|
130
|
+
NOTES:
|
|
131
|
+
- The node must exist in the workflow
|
|
132
|
+
- The new name must not conflict with any other node name in the workflow
|
|
133
|
+
- All connections to/from this node will be automatically updated`,
|
|
134
|
+
schema: exports.renameNodeSchema,
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
tool: dynamicTool,
|
|
138
|
+
...exports.RENAME_NODE_TOOL,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NodeConnectionType } from 'n8n-workflow';
|
|
2
|
+
export declare const RecommendationCategory: {
|
|
3
|
+
readonly TEXT_MANIPULATION: "text_manipulation";
|
|
4
|
+
readonly IMAGE_GENERATION: "image_generation";
|
|
5
|
+
readonly VIDEO_GENERATION: "video_generation";
|
|
6
|
+
readonly AUDIO_GENERATION: "audio_generation";
|
|
7
|
+
};
|
|
8
|
+
export type RecommendationCategoryType = (typeof RecommendationCategory)[keyof typeof RecommendationCategory];
|
|
9
|
+
export declare const RecommendationCategoryDescription: Record<RecommendationCategoryType, string>;
|
|
10
|
+
export interface ConnectedNodeRecommendation {
|
|
11
|
+
nodeType: string;
|
|
12
|
+
connectionType: NodeConnectionType;
|
|
13
|
+
description?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface NodeRecommendation {
|
|
16
|
+
defaultNode: string;
|
|
17
|
+
operations: string[];
|
|
18
|
+
reasoning: string;
|
|
19
|
+
connectedNodes?: ConnectedNodeRecommendation[];
|
|
20
|
+
note?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface NodeRecommendationDocument {
|
|
23
|
+
readonly category: RecommendationCategoryType;
|
|
24
|
+
readonly version: string;
|
|
25
|
+
readonly recommendation: NodeRecommendation;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RecommendationCategoryDescription = exports.RecommendationCategory = void 0;
|
|
4
|
+
exports.RecommendationCategory = {
|
|
5
|
+
TEXT_MANIPULATION: 'text_manipulation',
|
|
6
|
+
IMAGE_GENERATION: 'image_generation',
|
|
7
|
+
VIDEO_GENERATION: 'video_generation',
|
|
8
|
+
AUDIO_GENERATION: 'audio_generation',
|
|
9
|
+
};
|
|
10
|
+
exports.RecommendationCategoryDescription = {
|
|
11
|
+
[exports.RecommendationCategory.TEXT_MANIPULATION]: 'AI text tasks: summarization, analysis, extraction, classification, chat',
|
|
12
|
+
[exports.RecommendationCategory.IMAGE_GENERATION]: 'AI image tasks: generate, analyze, edit images',
|
|
13
|
+
[exports.RecommendationCategory.VIDEO_GENERATION]: 'AI video tasks: generate video from text/images',
|
|
14
|
+
[exports.RecommendationCategory.AUDIO_GENERATION]: 'AI audio tasks: generate speech, transcribe, translate audio',
|
|
15
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type INodeTypeDescription, type Logger } from 'n8n-workflow';
|
|
2
|
+
export interface OperationInfo {
|
|
3
|
+
value: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ResourceInfo {
|
|
7
|
+
value: string;
|
|
8
|
+
displayName: string;
|
|
9
|
+
operations: OperationInfo[];
|
|
10
|
+
}
|
|
11
|
+
export interface ResourceOperationInfo {
|
|
12
|
+
resources: ResourceInfo[];
|
|
13
|
+
}
|
|
14
|
+
export declare function extractResourceOperations(nodeType: INodeTypeDescription, nodeVersion: number, logger?: Logger): ResourceOperationInfo | null;
|
|
15
|
+
export declare function createResourceCacheKey(nodeName: string, version: number): string;
|
|
16
|
+
export declare function formatResourceOperationsForPrompt(info: ResourceOperationInfo): string;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractResourceOperations = extractResourceOperations;
|
|
4
|
+
exports.createResourceCacheKey = createResourceCacheKey;
|
|
5
|
+
exports.formatResourceOperationsForPrompt = formatResourceOperationsForPrompt;
|
|
6
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
7
|
+
function isPropertyVisibleForVersion(property, nodeVersion) {
|
|
8
|
+
const displayOptions = property.displayOptions;
|
|
9
|
+
if (!displayOptions) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
const showVersion = displayOptions.show?.['@version'];
|
|
13
|
+
if (showVersion && !(0, n8n_workflow_1.checkConditions)(showVersion, [nodeVersion])) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const hideVersion = displayOptions.hide?.['@version'];
|
|
17
|
+
if (hideVersion && (0, n8n_workflow_1.checkConditions)(hideVersion, [nodeVersion])) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
function findResourceProperty(properties, nodeVersion) {
|
|
23
|
+
return properties.find((prop) => prop.name === 'resource' &&
|
|
24
|
+
prop.type === 'options' &&
|
|
25
|
+
isPropertyVisibleForVersion(prop, nodeVersion));
|
|
26
|
+
}
|
|
27
|
+
function isOperationVisibleForResource(prop, resourceValue) {
|
|
28
|
+
const displayOptions = prop.displayOptions;
|
|
29
|
+
if (!displayOptions) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
const showResource = displayOptions.show?.resource;
|
|
33
|
+
if (showResource && !(0, n8n_workflow_1.checkConditions)(showResource, [resourceValue])) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const hideResource = displayOptions.hide?.resource;
|
|
37
|
+
if (hideResource && (0, n8n_workflow_1.checkConditions)(hideResource, [resourceValue])) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
function findOperationProperties(properties, resourceValue, nodeVersion) {
|
|
43
|
+
return properties.filter((prop) => {
|
|
44
|
+
if (prop.name !== 'operation' || prop.type !== 'options') {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (!isPropertyVisibleForVersion(prop, nodeVersion)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return isOperationVisibleForResource(prop, resourceValue);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function extractOptions(property, logger) {
|
|
54
|
+
if (!property.options || !Array.isArray(property.options)) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
return property.options
|
|
58
|
+
.filter((opt) => {
|
|
59
|
+
if (typeof opt !== 'object' || opt === null || !('name' in opt) || !('value' in opt)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const optName = opt.name;
|
|
63
|
+
const optValue = opt.value;
|
|
64
|
+
if (typeof optValue !== 'string') {
|
|
65
|
+
logger?.debug('Skipping non-string option value in resource/operation extraction', {
|
|
66
|
+
propertyName: property.name,
|
|
67
|
+
optionName: optName,
|
|
68
|
+
valueType: typeof optValue,
|
|
69
|
+
});
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
})
|
|
74
|
+
.map((opt) => ({
|
|
75
|
+
value: opt.value,
|
|
76
|
+
displayName: opt.name,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
function extractResourceOperations(nodeType, nodeVersion, logger) {
|
|
80
|
+
const properties = nodeType.properties;
|
|
81
|
+
if (!properties || properties.length === 0) {
|
|
82
|
+
logger?.debug('extractResourceOperations: No properties found', {
|
|
83
|
+
nodeType: nodeType.name,
|
|
84
|
+
nodeVersion,
|
|
85
|
+
});
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const resourceProperty = findResourceProperty(properties, nodeVersion);
|
|
89
|
+
if (!resourceProperty) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const resourceOptions = extractOptions(resourceProperty, logger);
|
|
93
|
+
if (resourceOptions.length === 0) {
|
|
94
|
+
logger?.warn('extractResourceOperations: Resource property found but no string options', {
|
|
95
|
+
nodeType: nodeType.name,
|
|
96
|
+
nodeVersion,
|
|
97
|
+
propertyDefault: resourceProperty.default,
|
|
98
|
+
});
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const resources = resourceOptions.map((resource) => {
|
|
102
|
+
const operationProperties = findOperationProperties(properties, resource.value, nodeVersion);
|
|
103
|
+
const allOperations = [];
|
|
104
|
+
const seenValues = new Set();
|
|
105
|
+
for (const opProp of operationProperties) {
|
|
106
|
+
const ops = extractOptions(opProp, logger);
|
|
107
|
+
for (const op of ops) {
|
|
108
|
+
if (!seenValues.has(op.value)) {
|
|
109
|
+
seenValues.add(op.value);
|
|
110
|
+
allOperations.push({
|
|
111
|
+
value: op.value,
|
|
112
|
+
displayName: op.displayName,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
value: resource.value,
|
|
119
|
+
displayName: resource.displayName,
|
|
120
|
+
operations: allOperations,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
return { resources };
|
|
124
|
+
}
|
|
125
|
+
function createResourceCacheKey(nodeName, version) {
|
|
126
|
+
return `${nodeName}:${version}`;
|
|
127
|
+
}
|
|
128
|
+
function formatResourceOperationsForPrompt(info) {
|
|
129
|
+
const parts = ['<available_resources_and_operations>'];
|
|
130
|
+
for (const resource of info.resources) {
|
|
131
|
+
if (resource.value === '__CUSTOM_API_CALL__')
|
|
132
|
+
continue;
|
|
133
|
+
parts.push(` Resource: ${resource.displayName} (value: "${resource.value}")`);
|
|
134
|
+
const filteredOps = resource.operations.filter((op) => op.value !== '__CUSTOM_API_CALL__');
|
|
135
|
+
if (filteredOps.length > 0) {
|
|
136
|
+
parts.push(' Operations:');
|
|
137
|
+
for (const op of filteredOps) {
|
|
138
|
+
parts.push(` - ${op.displayName} (value: "${op.value}")`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
parts.push(' Operations: none defined');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
parts.push('</available_resources_and_operations>');
|
|
146
|
+
return parts.join('\n');
|
|
147
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { INodeTypeDescription, NodeConnectionType, IConnections } from 'n8n-workflow';
|
|
2
|
+
import type { SimpleWorkflow } from '../../types';
|
|
3
|
+
import type { ProgrammaticViolation } from '../types';
|
|
4
|
+
export interface AutoFixResult {
|
|
5
|
+
fixed: AutoFixedConnection[];
|
|
6
|
+
unfixable: UnfixableConnection[];
|
|
7
|
+
updatedConnections: IConnections;
|
|
8
|
+
}
|
|
9
|
+
export interface AutoFixedConnection {
|
|
10
|
+
sourceNodeName: string;
|
|
11
|
+
targetNodeName: string;
|
|
12
|
+
connectionType: NodeConnectionType;
|
|
13
|
+
reason: string;
|
|
14
|
+
}
|
|
15
|
+
export interface UnfixableConnection {
|
|
16
|
+
nodeName: string;
|
|
17
|
+
missingInputType: NodeConnectionType;
|
|
18
|
+
reason: string;
|
|
19
|
+
candidateCount: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function autoFixConnections(workflow: SimpleWorkflow, nodeTypes: INodeTypeDescription[], violations: ProgrammaticViolation[]): AutoFixResult;
|