n8n-nodes-comfyui-all 2.2.24 → 2.3.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/nodes/ComfyUi/ComfyUi.node.d.ts +63 -30
- package/dist/nodes/ComfyUi/ComfyUi.node.d.ts.map +1 -1
- package/dist/nodes/ComfyUi/ComfyUi.node.js +142 -55
- package/dist/nodes/ComfyUi/ComfyUi.node.js.map +1 -1
- package/dist/nodes/ComfyUiTool/ComfyUiTool.node.d.ts +62 -32
- package/dist/nodes/ComfyUiTool/ComfyUiTool.node.d.ts.map +1 -1
- package/dist/nodes/ComfyUiTool/ComfyUiTool.node.js +255 -119
- package/dist/nodes/ComfyUiTool/ComfyUiTool.node.js.map +1 -1
- package/dist/nodes/agentToolHelpers.d.ts +17 -0
- package/dist/nodes/agentToolHelpers.d.ts.map +1 -1
- package/dist/nodes/agentToolHelpers.js +95 -0
- package/dist/nodes/agentToolHelpers.js.map +1 -1
- package/dist/nodes/parameterProcessor.d.ts.map +1 -1
- package/dist/nodes/parameterProcessor.js +19 -9
- package/dist/nodes/parameterProcessor.js.map +1 -1
- package/dist/nodes/validation.d.ts +10 -0
- package/dist/nodes/validation.d.ts.map +1 -1
- package/dist/nodes/validation.js +81 -0
- package/dist/nodes/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -29,6 +29,12 @@ export declare class ComfyUiTool {
|
|
|
29
29
|
notes: string[];
|
|
30
30
|
inputSample: {
|
|
31
31
|
query: string;
|
|
32
|
+
parameters: {
|
|
33
|
+
seed: number;
|
|
34
|
+
steps: number;
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
};
|
|
32
38
|
};
|
|
33
39
|
outputSample: {
|
|
34
40
|
json: {
|
|
@@ -45,13 +51,6 @@ export declare class ComfyUiTool {
|
|
|
45
51
|
seed: number;
|
|
46
52
|
};
|
|
47
53
|
};
|
|
48
|
-
binary: {
|
|
49
|
-
data: {
|
|
50
|
-
data: string;
|
|
51
|
-
mimeType: string;
|
|
52
|
-
fileName: string;
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
54
|
};
|
|
56
55
|
properties: ({
|
|
57
56
|
displayName: string;
|
|
@@ -62,8 +61,10 @@ export declare class ComfyUiTool {
|
|
|
62
61
|
description: string;
|
|
63
62
|
minValue?: undefined;
|
|
64
63
|
maxValue?: undefined;
|
|
64
|
+
typeOptions?: undefined;
|
|
65
|
+
validate?: undefined;
|
|
65
66
|
placeholder?: undefined;
|
|
66
|
-
|
|
67
|
+
displayOptions?: undefined;
|
|
67
68
|
} | {
|
|
68
69
|
displayName: string;
|
|
69
70
|
name: string;
|
|
@@ -73,55 +74,84 @@ export declare class ComfyUiTool {
|
|
|
73
74
|
minValue: number;
|
|
74
75
|
maxValue: number;
|
|
75
76
|
required?: undefined;
|
|
77
|
+
typeOptions?: undefined;
|
|
78
|
+
validate?: undefined;
|
|
76
79
|
placeholder?: undefined;
|
|
77
|
-
|
|
80
|
+
displayOptions?: undefined;
|
|
78
81
|
} | {
|
|
79
82
|
displayName: string;
|
|
80
83
|
name: string;
|
|
81
84
|
type: string;
|
|
85
|
+
required: boolean;
|
|
86
|
+
typeOptions: {
|
|
87
|
+
rows: number;
|
|
88
|
+
loadOptionsMethod?: undefined;
|
|
89
|
+
};
|
|
82
90
|
default: string;
|
|
83
91
|
description: string;
|
|
92
|
+
validate: (parameterValue: string) => Promise<string | undefined>;
|
|
93
|
+
minValue?: undefined;
|
|
94
|
+
maxValue?: undefined;
|
|
95
|
+
placeholder?: undefined;
|
|
96
|
+
displayOptions?: undefined;
|
|
97
|
+
} | {
|
|
98
|
+
displayName: string;
|
|
99
|
+
name: string;
|
|
100
|
+
type: string;
|
|
101
|
+
description: string;
|
|
102
|
+
typeOptions: {
|
|
103
|
+
loadOptionsMethod: string;
|
|
104
|
+
rows?: undefined;
|
|
105
|
+
};
|
|
84
106
|
placeholder: string;
|
|
107
|
+
default: string;
|
|
85
108
|
required?: undefined;
|
|
86
109
|
minValue?: undefined;
|
|
87
110
|
maxValue?: undefined;
|
|
88
|
-
|
|
111
|
+
validate?: undefined;
|
|
112
|
+
displayOptions?: undefined;
|
|
89
113
|
} | {
|
|
90
114
|
displayName: string;
|
|
91
115
|
name: string;
|
|
92
116
|
type: string;
|
|
117
|
+
description: string;
|
|
118
|
+
typeOptions: {
|
|
119
|
+
loadOptionsMethod: string;
|
|
120
|
+
rows?: undefined;
|
|
121
|
+
};
|
|
93
122
|
placeholder: string;
|
|
94
|
-
default:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
typeOptions: {
|
|
100
|
-
rows: number;
|
|
123
|
+
default: never[];
|
|
124
|
+
displayOptions: {
|
|
125
|
+
show: {
|
|
126
|
+
nodeId: (string | null | undefined)[];
|
|
127
|
+
mode: string;
|
|
101
128
|
};
|
|
102
|
-
|
|
103
|
-
description: string;
|
|
104
|
-
minValue?: undefined;
|
|
105
|
-
maxValue?: undefined;
|
|
106
|
-
} | {
|
|
107
|
-
displayName: string;
|
|
108
|
-
name: string;
|
|
109
|
-
type: string;
|
|
110
|
-
default: number;
|
|
111
|
-
description: string;
|
|
112
|
-
minValue: number;
|
|
113
|
-
maxValue: number;
|
|
114
|
-
typeOptions?: undefined;
|
|
115
|
-
})[];
|
|
129
|
+
};
|
|
116
130
|
required?: undefined;
|
|
117
|
-
description?: undefined;
|
|
118
131
|
minValue?: undefined;
|
|
119
132
|
maxValue?: undefined;
|
|
133
|
+
validate?: undefined;
|
|
120
134
|
})[];
|
|
121
135
|
};
|
|
122
136
|
/**
|
|
123
137
|
* Execute function - called when the node runs
|
|
124
138
|
*/
|
|
125
139
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
140
|
+
/**
|
|
141
|
+
* Load all nodes from workflow JSON for Node ID dropdown
|
|
142
|
+
*/
|
|
143
|
+
getWorkflowNodes(this: IExecuteFunctions): Promise<{
|
|
144
|
+
name: string;
|
|
145
|
+
value: string;
|
|
146
|
+
description?: string;
|
|
147
|
+
}[]>;
|
|
148
|
+
/**
|
|
149
|
+
* Load parameters for a specific node for Allowed Parameters multi-select
|
|
150
|
+
*/
|
|
151
|
+
getNodeParameters(this: IExecuteFunctions): Promise<{
|
|
152
|
+
name: string;
|
|
153
|
+
value: string;
|
|
154
|
+
description?: string;
|
|
155
|
+
}[]>;
|
|
126
156
|
}
|
|
127
157
|
//# sourceMappingURL=ComfyUiTool.node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComfyUiTool.node.d.ts","sourceRoot":"","sources":["../../../nodes/ComfyUiTool/ComfyUiTool.node.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAEnB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ComfyUiTool.node.d.ts","sourceRoot":"","sources":["../../../nodes/ComfyUiTool/ComfyUiTool.node.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAEnB,MAAM,cAAc,CAAC;AAiBtB,qBAAa,WAAW;IACtB;;OAEG;IACH,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uCA+E4B,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAuD3C;IAEF;;OAEG;IACG,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;IA+OvE;;OAEG;IACG,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAmCjH;;OAEG;IACG,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAsDnH"}
|
|
@@ -13,7 +13,6 @@ const ComfyUiClient_1 = require("../ComfyUiClient");
|
|
|
13
13
|
const validation_1 = require("../validation");
|
|
14
14
|
const logger_1 = require("../logger");
|
|
15
15
|
const agentToolHelpers_1 = require("../agentToolHelpers");
|
|
16
|
-
const workflowConfig_1 = require("../workflowConfig");
|
|
17
16
|
class ComfyUiTool {
|
|
18
17
|
constructor() {
|
|
19
18
|
/**
|
|
@@ -36,14 +35,23 @@ class ComfyUiTool {
|
|
|
36
35
|
outputs: ['main'],
|
|
37
36
|
subtitle: 'AI Image Generator',
|
|
38
37
|
notes: [
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'Supports
|
|
43
|
-
'
|
|
38
|
+
'AI Agent-friendly ComfyUI tool',
|
|
39
|
+
'Requires ComfyUI workflow JSON in API format',
|
|
40
|
+
'Export workflow: ComfyUI Menu > Export > API Format',
|
|
41
|
+
'Supports both text-to-image and image-to-image workflows',
|
|
42
|
+
'Configure which node/parameters AI can modify using "Node ID" and "Allowed Parameters"',
|
|
43
|
+
'AI Agent provides: { query: "...", parameters: { seed: 123456, steps: 30 } }',
|
|
44
|
+
'Only parameters in "Allowed Parameters" list will be applied',
|
|
45
|
+
'Legacy format supported: "A sunset, size:1024x768, steps:30"',
|
|
44
46
|
],
|
|
45
47
|
inputSample: {
|
|
46
48
|
query: 'A beautiful landscape painting',
|
|
49
|
+
parameters: {
|
|
50
|
+
seed: 123456,
|
|
51
|
+
steps: 30,
|
|
52
|
+
width: 1024,
|
|
53
|
+
height: 768,
|
|
54
|
+
},
|
|
47
55
|
},
|
|
48
56
|
outputSample: {
|
|
49
57
|
json: {
|
|
@@ -53,18 +61,11 @@ class ComfyUiTool {
|
|
|
53
61
|
prompt: 'A beautiful landscape painting',
|
|
54
62
|
mode: 'text-to-image',
|
|
55
63
|
parameters: {
|
|
56
|
-
width:
|
|
57
|
-
height:
|
|
58
|
-
steps:
|
|
64
|
+
width: 1024,
|
|
65
|
+
height: 768,
|
|
66
|
+
steps: 30,
|
|
59
67
|
cfg: 8,
|
|
60
|
-
seed:
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
binary: {
|
|
64
|
-
data: {
|
|
65
|
-
data: 'base64_encoded_image_data',
|
|
66
|
-
mimeType: 'image/png',
|
|
67
|
-
fileName: 'ComfyUI_00001.png',
|
|
68
|
+
seed: 123456,
|
|
68
69
|
},
|
|
69
70
|
},
|
|
70
71
|
},
|
|
@@ -87,75 +88,63 @@ class ComfyUiTool {
|
|
|
87
88
|
maxValue: 600,
|
|
88
89
|
},
|
|
89
90
|
{
|
|
90
|
-
displayName: '
|
|
91
|
-
name: '
|
|
91
|
+
displayName: 'Workflow JSON',
|
|
92
|
+
name: 'workflowJson',
|
|
92
93
|
type: 'string',
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
required: true,
|
|
95
|
+
typeOptions: {
|
|
96
|
+
rows: 20,
|
|
97
|
+
},
|
|
98
|
+
default: '',
|
|
99
|
+
description: 'ComfyUI workflow JSON in API format. Export your workflow from ComfyUI (Menu > Export > API Format) and paste here.',
|
|
100
|
+
validate: async (parameterValue) => {
|
|
101
|
+
if (!parameterValue || parameterValue.trim().length === 0) {
|
|
102
|
+
// eslint-disable-next-line n8n-nodes-base/node-execute-block-wrong-error-thrown
|
|
103
|
+
throw new Error('Workflow JSON is required');
|
|
104
|
+
}
|
|
105
|
+
const validation = (0, validation_1.validateComfyUIWorkflowEnhanced)(parameterValue);
|
|
106
|
+
if (!validation.valid) {
|
|
107
|
+
// eslint-disable-next-line n8n-nodes-base/node-execute-block-wrong-error-thrown
|
|
108
|
+
throw new Error(validation.error || 'Invalid workflow format');
|
|
109
|
+
}
|
|
110
|
+
if (validation.warnings && validation.warnings.length > 0) {
|
|
111
|
+
const warningMessage = 'Workflow validation warnings:\n' +
|
|
112
|
+
validation.warnings.map((w, i) => `${i + 1}. ${w}`).join('\n') +
|
|
113
|
+
'\n\nSuggestions:\n' +
|
|
114
|
+
(validation.suggestions?.map((s, i) => `${i + 1}. ${s}`).join('\n') || '');
|
|
115
|
+
// Return warning message (doesn't block save, but shows warning)
|
|
116
|
+
return warningMessage;
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
},
|
|
96
120
|
},
|
|
97
121
|
{
|
|
98
|
-
displayName: '
|
|
99
|
-
name: '
|
|
100
|
-
type: '
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
displayName: 'Node Name or ID',
|
|
123
|
+
name: 'nodeId',
|
|
124
|
+
type: 'options',
|
|
125
|
+
description: 'Select a node from your workflow. AI will be able to modify parameters of this node. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
126
|
+
typeOptions: {
|
|
127
|
+
loadOptionsMethod: 'getWorkflowNodes',
|
|
128
|
+
},
|
|
129
|
+
placeholder: 'Select a node to configure AI-accessible parameters...',
|
|
130
|
+
default: '',
|
|
104
131
|
},
|
|
105
132
|
{
|
|
106
|
-
displayName: '
|
|
107
|
-
name: '
|
|
108
|
-
type: '
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
default: '',
|
|
120
|
-
description: 'Optional: Custom ComfyUI workflow JSON (API Format). If empty, uses default workflow. Only use this if you want to override the default template.',
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
displayName: 'Default CFG',
|
|
124
|
-
name: 'defaultCfg',
|
|
125
|
-
type: 'number',
|
|
126
|
-
default: 8,
|
|
127
|
-
description: 'Default CFG scale when not specified in query',
|
|
128
|
-
minValue: 1,
|
|
129
|
-
maxValue: 30,
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
displayName: 'Default Height',
|
|
133
|
-
name: 'defaultHeight',
|
|
134
|
-
type: 'number',
|
|
135
|
-
default: 512,
|
|
136
|
-
description: 'Default image height when not specified in query',
|
|
137
|
-
minValue: 64,
|
|
138
|
-
maxValue: 4096,
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
displayName: 'Default Steps',
|
|
142
|
-
name: 'defaultSteps',
|
|
143
|
-
type: 'number',
|
|
144
|
-
default: 20,
|
|
145
|
-
description: 'Default sampling steps when not specified in query',
|
|
146
|
-
minValue: 1,
|
|
147
|
-
maxValue: 150,
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
displayName: 'Default Width',
|
|
151
|
-
name: 'defaultWidth',
|
|
152
|
-
type: 'number',
|
|
153
|
-
default: 512,
|
|
154
|
-
description: 'Default image width when not specified in query',
|
|
155
|
-
minValue: 64,
|
|
156
|
-
maxValue: 4096,
|
|
133
|
+
displayName: 'Allowed Parameter Names or IDs',
|
|
134
|
+
name: 'allowedParameters',
|
|
135
|
+
type: 'multiOptions',
|
|
136
|
+
description: 'Select which parameters of this node AI can modify. Parameters will be loaded from the selected node. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
137
|
+
typeOptions: {
|
|
138
|
+
loadOptionsMethod: 'getNodeParameters',
|
|
139
|
+
},
|
|
140
|
+
placeholder: 'Select parameters...',
|
|
141
|
+
default: [],
|
|
142
|
+
displayOptions: {
|
|
143
|
+
show: {
|
|
144
|
+
nodeId: ['', null, undefined],
|
|
145
|
+
mode: 'invert',
|
|
157
146
|
},
|
|
158
|
-
|
|
147
|
+
},
|
|
159
148
|
},
|
|
160
149
|
],
|
|
161
150
|
};
|
|
@@ -168,16 +157,15 @@ class ComfyUiTool {
|
|
|
168
157
|
// Get node parameters
|
|
169
158
|
const comfyUiUrl = this.getNodeParameter('comfyUiUrl', 0);
|
|
170
159
|
const timeout = this.getNodeParameter('timeout', 0);
|
|
171
|
-
const
|
|
172
|
-
const defaultNegativePrompt = this.getNodeParameter('defaultNegativePrompt', 0);
|
|
173
|
-
const advancedOptions = this.getNodeParameter('advancedOptions', 0);
|
|
174
|
-
const { customWorkflow = '', defaultWidth = 512, defaultHeight = 512, defaultSteps = 20, defaultCfg = 8, } = advancedOptions || {};
|
|
160
|
+
const workflowJson = this.getNodeParameter('workflowJson', 0);
|
|
175
161
|
// Validate URL
|
|
176
162
|
if (!(0, validation_1.validateUrl)(comfyUiUrl)) {
|
|
177
163
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid ComfyUI URL. Must be a valid HTTP/HTTPS URL.');
|
|
178
164
|
}
|
|
179
|
-
// Validate
|
|
180
|
-
(
|
|
165
|
+
// Validate workflow JSON
|
|
166
|
+
if (!workflowJson || workflowJson.trim().length === 0) {
|
|
167
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Workflow JSON is required. Please export your ComfyUI workflow (Menu > Export > API Format) and paste it in the "Workflow JSON" field.');
|
|
168
|
+
}
|
|
181
169
|
// Get input data
|
|
182
170
|
const inputData = this.getInputData();
|
|
183
171
|
if (!inputData || inputData.length === 0) {
|
|
@@ -195,32 +183,83 @@ class ComfyUiTool {
|
|
|
195
183
|
if (!query || query.trim().length === 0) {
|
|
196
184
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No query found in input. Please provide a "query", "text", or "prompt" field with a description of the image you want to generate.');
|
|
197
185
|
}
|
|
198
|
-
//
|
|
199
|
-
const mode = hasInputImage ? 'image-to-image' : 'text-to-image';
|
|
200
|
-
logger.info('Processing image generation request', { query, comfyUiUrl, timeout, mode });
|
|
201
|
-
// Parse the query to extract parameters
|
|
202
|
-
const params = (0, agentToolHelpers_1.parseInput)(query, {
|
|
203
|
-
negativePrompt: defaultNegativePrompt,
|
|
204
|
-
width: defaultWidth,
|
|
205
|
-
height: defaultHeight,
|
|
206
|
-
steps: defaultSteps,
|
|
207
|
-
cfg: defaultCfg,
|
|
208
|
-
});
|
|
209
|
-
logger.debug('Parsed parameters', {
|
|
210
|
-
prompt: params.prompt,
|
|
211
|
-
width: params.width,
|
|
212
|
-
height: params.height,
|
|
213
|
-
steps: params.steps,
|
|
214
|
-
cfg: params.cfg,
|
|
215
|
-
seed: params.seed,
|
|
216
|
-
});
|
|
217
|
-
// Get workflow template
|
|
186
|
+
// Parse workflow JSON (already validated in parameter validation)
|
|
218
187
|
let workflowTemplate;
|
|
219
|
-
|
|
220
|
-
workflowTemplate = (0, validation_1.safeJsonParse)(
|
|
188
|
+
try {
|
|
189
|
+
workflowTemplate = (0, validation_1.safeJsonParse)(workflowJson, 'Workflow JSON');
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid Workflow JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
193
|
+
}
|
|
194
|
+
// Check if structured parameters are provided
|
|
195
|
+
const structuredParams = firstItem.json.parameters;
|
|
196
|
+
// Get node configuration (which node/parameters AI can modify)
|
|
197
|
+
const configuredNodeId = this.getNodeParameter('nodeId', 0);
|
|
198
|
+
const allowedParams = this.getNodeParameter('allowedParameters', 0) || [];
|
|
199
|
+
let params;
|
|
200
|
+
let workflow = workflowTemplate;
|
|
201
|
+
let usedStructuredParams = false;
|
|
202
|
+
if (structuredParams && typeof structuredParams === 'object' && Object.keys(structuredParams).length > 0) {
|
|
203
|
+
// Use structured parameters from AI Agent
|
|
204
|
+
logger.info('Using structured parameters from input', { parameters: structuredParams });
|
|
205
|
+
// Filter parameters based on allowed list
|
|
206
|
+
let filteredParams = structuredParams;
|
|
207
|
+
if (configuredNodeId && allowedParams.length > 0) {
|
|
208
|
+
filteredParams = {};
|
|
209
|
+
for (const paramName of allowedParams) {
|
|
210
|
+
if (paramName in structuredParams) {
|
|
211
|
+
filteredParams[paramName] = structuredParams[paramName];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
logger.info('Filtered parameters based on allowed list', {
|
|
215
|
+
configuredNodeId,
|
|
216
|
+
allowedParams,
|
|
217
|
+
filteredParams,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// Convert structured parameters to ParsedParameters format
|
|
221
|
+
// Use filteredParams when available to ensure response only includes allowed parameters
|
|
222
|
+
const sourceParams = (configuredNodeId && allowedParams.length > 0) ? filteredParams : structuredParams;
|
|
223
|
+
params = {
|
|
224
|
+
prompt: query,
|
|
225
|
+
negative_prompt: sourceParams.negative_prompt ||
|
|
226
|
+
sourceParams.negative ||
|
|
227
|
+
structuredParams.negative_prompt ||
|
|
228
|
+
structuredParams.negative ||
|
|
229
|
+
'ugly, blurry, low quality, distorted',
|
|
230
|
+
width: sourceParams.width || 512,
|
|
231
|
+
height: sourceParams.height || 512,
|
|
232
|
+
steps: sourceParams.steps || 20,
|
|
233
|
+
cfg: sourceParams.cfg || 8,
|
|
234
|
+
seed: sourceParams.seed || Math.floor(Math.random() * 2147483647),
|
|
235
|
+
};
|
|
236
|
+
// Apply filtered parameters to the configured node
|
|
237
|
+
if (configuredNodeId && Object.keys(filteredParams).length > 0) {
|
|
238
|
+
workflow = (0, agentToolHelpers_1.applyParametersToNode)(workflowTemplate, configuredNodeId, filteredParams);
|
|
239
|
+
usedStructuredParams = true;
|
|
240
|
+
logger.debug('Applied filtered parameters to configured node', {
|
|
241
|
+
nodeId: configuredNodeId,
|
|
242
|
+
params: filteredParams,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
else if (Object.keys(structuredParams).length > 0) {
|
|
246
|
+
// Fallback to old behavior if no node configured
|
|
247
|
+
workflow = (0, agentToolHelpers_1.applyStructuredParameters)(workflowTemplate, structuredParams);
|
|
248
|
+
usedStructuredParams = true;
|
|
249
|
+
logger.debug('Applied structured parameters (auto-mapped)', structuredParams);
|
|
250
|
+
}
|
|
221
251
|
}
|
|
222
252
|
else {
|
|
223
|
-
|
|
253
|
+
// Parse the query to extract parameters (legacy behavior)
|
|
254
|
+
params = (0, agentToolHelpers_1.parseInput)(query);
|
|
255
|
+
logger.debug('Parsed parameters from query', {
|
|
256
|
+
prompt: params.prompt,
|
|
257
|
+
width: params.width,
|
|
258
|
+
height: params.height,
|
|
259
|
+
steps: params.steps,
|
|
260
|
+
cfg: params.cfg,
|
|
261
|
+
seed: params.seed,
|
|
262
|
+
});
|
|
224
263
|
}
|
|
225
264
|
// Create ComfyUI client
|
|
226
265
|
const client = new ComfyUiClient_1.ComfyUIClient({
|
|
@@ -251,8 +290,11 @@ class ComfyUiTool {
|
|
|
251
290
|
uploadedImageFilename = await client.uploadImage(buffer, binaryData.fileName || 'input_image.png');
|
|
252
291
|
logger.info('Successfully uploaded input image', { filename: uploadedImageFilename });
|
|
253
292
|
}
|
|
254
|
-
// Update workflow with parsed parameters
|
|
255
|
-
|
|
293
|
+
// Update workflow with parsed parameters (only in legacy mode)
|
|
294
|
+
// Skip if structured parameters were already applied
|
|
295
|
+
if (!usedStructuredParams) {
|
|
296
|
+
workflow = (0, agentToolHelpers_1.updateWorkflow)(workflow, params);
|
|
297
|
+
}
|
|
256
298
|
// If we uploaded an image, update workflow with the image filename
|
|
257
299
|
if (uploadedImageFilename) {
|
|
258
300
|
workflow = (0, agentToolHelpers_1.updateWorkflowWithImage)(workflow, uploadedImageFilename);
|
|
@@ -263,13 +305,24 @@ class ComfyUiTool {
|
|
|
263
305
|
logger.error('Workflow execution failed', result.error);
|
|
264
306
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to generate image: ${result.error}`);
|
|
265
307
|
}
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
308
|
+
// Only return URLs without downloading (for AI Agent)
|
|
309
|
+
// Fix URL concatenation to handle trailing/leading slashes
|
|
310
|
+
const baseUrl = comfyUiUrl.endsWith('/') ? comfyUiUrl.slice(0, -1) : comfyUiUrl;
|
|
311
|
+
const imageUrls = result.images ? result.images.map(img => {
|
|
312
|
+
const imagePath = img.startsWith('/') ? img : `/${img}`;
|
|
313
|
+
return `${baseUrl}${imagePath}`;
|
|
314
|
+
}) : [];
|
|
315
|
+
const videoUrls = result.videos ? result.videos.map(vid => {
|
|
316
|
+
const videoPath = vid.startsWith('/') ? vid : `/${vid}`;
|
|
317
|
+
return `${baseUrl}${videoPath}`;
|
|
318
|
+
}) : [];
|
|
269
319
|
const enhancedJson = {
|
|
270
|
-
|
|
320
|
+
success: true,
|
|
321
|
+
imageUrls,
|
|
322
|
+
videoUrls,
|
|
323
|
+
imageCount: result.images?.length || 0,
|
|
324
|
+
videoCount: result.videos?.length || 0,
|
|
271
325
|
prompt: params.prompt,
|
|
272
|
-
mode: mode,
|
|
273
326
|
parameters: {
|
|
274
327
|
width: params.width,
|
|
275
328
|
height: params.height,
|
|
@@ -280,10 +333,10 @@ class ComfyUiTool {
|
|
|
280
333
|
},
|
|
281
334
|
};
|
|
282
335
|
logger.info('Image generation completed successfully', {
|
|
283
|
-
imageCount:
|
|
284
|
-
mode,
|
|
336
|
+
imageCount: enhancedJson.imageCount,
|
|
285
337
|
});
|
|
286
|
-
|
|
338
|
+
// Return result (no binary data for AI Agent)
|
|
339
|
+
return [this.helpers.constructExecutionMetaData([{ json: enhancedJson }], { itemData: { item: 0 } })];
|
|
287
340
|
}
|
|
288
341
|
catch (error) {
|
|
289
342
|
logger.error('Error during workflow execution', error);
|
|
@@ -294,6 +347,89 @@ class ComfyUiTool {
|
|
|
294
347
|
logger.debug('Client destroyed');
|
|
295
348
|
}
|
|
296
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Load all nodes from workflow JSON for Node ID dropdown
|
|
352
|
+
*/
|
|
353
|
+
async getWorkflowNodes() {
|
|
354
|
+
try {
|
|
355
|
+
const workflowJson = this.getNodeParameter('workflowJson', 0);
|
|
356
|
+
if (!workflowJson || workflowJson.trim().length === 0) {
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
359
|
+
const workflow = (0, validation_1.safeJsonParse)(workflowJson, 'Workflow JSON');
|
|
360
|
+
const nodes = [];
|
|
361
|
+
for (const [nodeId, nodeData] of Object.entries(workflow)) {
|
|
362
|
+
if (nodeData && nodeData.class_type) {
|
|
363
|
+
nodes.push({
|
|
364
|
+
name: `${nodeId} - ${nodeData.class_type}`,
|
|
365
|
+
value: nodeId,
|
|
366
|
+
description: nodeData.class_type,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Sort by node ID (numeric)
|
|
371
|
+
nodes.sort((a, b) => {
|
|
372
|
+
const aNum = parseInt(a.value);
|
|
373
|
+
const bNum = parseInt(b.value);
|
|
374
|
+
return aNum - bNum;
|
|
375
|
+
});
|
|
376
|
+
return nodes;
|
|
377
|
+
}
|
|
378
|
+
catch (error) {
|
|
379
|
+
// Silently return empty array on error
|
|
380
|
+
return [];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Load parameters for a specific node for Allowed Parameters multi-select
|
|
385
|
+
*/
|
|
386
|
+
async getNodeParameters() {
|
|
387
|
+
try {
|
|
388
|
+
const workflowJson = this.getNodeParameter('workflowJson', 0);
|
|
389
|
+
const nodeId = this.getNodeParameter('nodeId', 0);
|
|
390
|
+
if (!workflowJson || workflowJson.trim().length === 0 || !nodeId) {
|
|
391
|
+
return [];
|
|
392
|
+
}
|
|
393
|
+
const workflow = (0, validation_1.safeJsonParse)(workflowJson, 'Workflow JSON');
|
|
394
|
+
const nodeData = workflow[nodeId];
|
|
395
|
+
if (!nodeData || !nodeData.inputs) {
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
398
|
+
const parameters = [];
|
|
399
|
+
for (const [paramName, paramValue] of Object.entries(nodeData.inputs)) {
|
|
400
|
+
// Skip array values (these are connections to other nodes)
|
|
401
|
+
if (Array.isArray(paramValue)) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
// Auto-detect type for display
|
|
405
|
+
let detectedType = 'text';
|
|
406
|
+
if (paramName.toLowerCase().includes('image') || paramName.toLowerCase().includes('upload')) {
|
|
407
|
+
detectedType = 'image';
|
|
408
|
+
}
|
|
409
|
+
else if (typeof paramValue === 'boolean') {
|
|
410
|
+
detectedType = 'boolean';
|
|
411
|
+
}
|
|
412
|
+
else if (typeof paramValue === 'number') {
|
|
413
|
+
detectedType = 'number';
|
|
414
|
+
}
|
|
415
|
+
// Add parameter with value and type as description
|
|
416
|
+
const valueStr = String(paramValue);
|
|
417
|
+
const description = `Type: ${detectedType} | Current value: ${valueStr.length > 50 ? valueStr.substring(0, 50) + '...' : valueStr}`;
|
|
418
|
+
parameters.push({
|
|
419
|
+
name: paramName,
|
|
420
|
+
value: paramName,
|
|
421
|
+
description: description,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
// Sort alphabetically
|
|
425
|
+
parameters.sort((a, b) => a.name.localeCompare(b.name));
|
|
426
|
+
return parameters;
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
// Silently return empty array on error
|
|
430
|
+
return [];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
297
433
|
}
|
|
298
434
|
exports.ComfyUiTool = ComfyUiTool;
|
|
299
435
|
//# sourceMappingURL=ComfyUiTool.node.js.map
|