n8n-nodes-vlm 3.2.1 → 3.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.
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
2
|
/**
|
|
3
|
-
* VLM Complexity Classifier Workflow Node v3
|
|
3
|
+
* VLM Complexity Classifier Workflow Node v3.3
|
|
4
4
|
*
|
|
5
5
|
* Routes documents to LOW or HIGH outputs based on VLM classification.
|
|
6
6
|
* Always preserves both binary data and classification results.
|
|
7
|
+
*
|
|
8
|
+
* UI Structure:
|
|
9
|
+
* - Model selection
|
|
10
|
+
* - Input Settings (notice + fields)
|
|
11
|
+
* - Output Settings (notice + fields)
|
|
12
|
+
* - Advanced Options (collection)
|
|
7
13
|
*/
|
|
8
14
|
export declare class VLMComplexityWorkflow implements INodeType {
|
|
9
15
|
description: INodeTypeDescription;
|
|
10
16
|
/**
|
|
11
|
-
* Execute Method - v3 Dual Output
|
|
17
|
+
* Execute Method - v3.3 Dual Output with improved UI
|
|
12
18
|
*
|
|
13
19
|
* Routes items to LOW or HIGH output based on classification.
|
|
14
20
|
* Always preserves binary data alongside classification results.
|
|
@@ -4,10 +4,16 @@ exports.VLMComplexityWorkflow = void 0;
|
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const vlm_logic_1 = require("../shared/vlm-logic");
|
|
6
6
|
/**
|
|
7
|
-
* VLM Complexity Classifier Workflow Node v3
|
|
7
|
+
* VLM Complexity Classifier Workflow Node v3.3
|
|
8
8
|
*
|
|
9
9
|
* Routes documents to LOW or HIGH outputs based on VLM classification.
|
|
10
10
|
* Always preserves both binary data and classification results.
|
|
11
|
+
*
|
|
12
|
+
* UI Structure:
|
|
13
|
+
* - Model selection
|
|
14
|
+
* - Input Settings (notice + fields)
|
|
15
|
+
* - Output Settings (notice + fields)
|
|
16
|
+
* - Advanced Options (collection)
|
|
11
17
|
*/
|
|
12
18
|
class VLMComplexityWorkflow {
|
|
13
19
|
constructor() {
|
|
@@ -16,7 +22,7 @@ class VLMComplexityWorkflow {
|
|
|
16
22
|
name: 'vlmComplexityWorkflow',
|
|
17
23
|
icon: 'file:vlm.svg',
|
|
18
24
|
group: ['transform'],
|
|
19
|
-
version:
|
|
25
|
+
version: 3,
|
|
20
26
|
subtitle: '={{$parameter["model"]}}',
|
|
21
27
|
description: 'Classify document complexity and route to LOW/HIGH outputs',
|
|
22
28
|
defaults: {
|
|
@@ -43,6 +49,9 @@ class VLMComplexityWorkflow {
|
|
|
43
49
|
baseURL: '={{ $credentials.baseUrl.replace(new RegExp("/$"), "") }}',
|
|
44
50
|
},
|
|
45
51
|
properties: [
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════
|
|
53
|
+
// MODEL SELECTION
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════
|
|
46
55
|
{
|
|
47
56
|
displayName: 'Model',
|
|
48
57
|
name: 'model',
|
|
@@ -84,75 +93,106 @@ class VLMComplexityWorkflow {
|
|
|
84
93
|
},
|
|
85
94
|
required: true,
|
|
86
95
|
},
|
|
96
|
+
// ═══════════════════════════════════════════════════════════════
|
|
97
|
+
// INPUT SETTINGS
|
|
98
|
+
// ═══════════════════════════════════════════════════════════════
|
|
87
99
|
{
|
|
88
|
-
displayName: '
|
|
89
|
-
name: '
|
|
90
|
-
type: '
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
displayName: 'Input Settings',
|
|
101
|
+
name: 'inputSettingsNotice',
|
|
102
|
+
type: 'notice',
|
|
103
|
+
default: '',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
displayName: 'Image Source',
|
|
107
|
+
name: 'imageSource',
|
|
108
|
+
type: 'options',
|
|
93
109
|
options: [
|
|
94
110
|
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
options: [
|
|
99
|
-
{
|
|
100
|
-
name: 'From Binary',
|
|
101
|
-
value: 'binary',
|
|
102
|
-
description: 'Extract image from binary field (n8n standard)',
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
name: 'From Base64 Field',
|
|
106
|
-
value: 'base64field',
|
|
107
|
-
description: 'Use base64 string from JSON field (with or without Data URI prefix)',
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
default: 'binary',
|
|
111
|
-
description: 'Where to get the image data from',
|
|
111
|
+
name: 'From Binary',
|
|
112
|
+
value: 'binary',
|
|
113
|
+
description: 'Extract image from binary field (n8n standard)',
|
|
112
114
|
},
|
|
113
115
|
{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
default: '',
|
|
118
|
-
placeholder: 'e.g. data (auto-detect if empty)',
|
|
119
|
-
description: 'Name of the binary field to use for classification. Leave empty to auto-detect first image.',
|
|
116
|
+
name: 'From Base64 Field',
|
|
117
|
+
value: 'base64field',
|
|
118
|
+
description: 'Use base64 string from JSON field (with or without Data URI prefix)',
|
|
120
119
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
],
|
|
121
|
+
default: 'binary',
|
|
122
|
+
description: 'Where to get the image data from',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
displayName: 'Binary Field',
|
|
126
|
+
name: 'binaryField',
|
|
127
|
+
type: 'string',
|
|
128
|
+
default: '',
|
|
129
|
+
placeholder: 'e.g. data (leave empty to auto-detect)',
|
|
130
|
+
description: 'Name of the binary field containing the image. Leave empty to auto-detect first image.',
|
|
131
|
+
displayOptions: {
|
|
132
|
+
show: {
|
|
133
|
+
imageSource: ['binary'],
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
displayName: 'Base64 Field',
|
|
139
|
+
name: 'base64Field',
|
|
140
|
+
type: 'string',
|
|
141
|
+
default: 'data',
|
|
142
|
+
placeholder: 'e.g. data, base64Image, imageData',
|
|
143
|
+
description: 'JSON field containing the base64 string. Auto-detects Data URI prefix.',
|
|
144
|
+
displayOptions: {
|
|
145
|
+
show: {
|
|
146
|
+
imageSource: ['base64field'],
|
|
128
147
|
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════
|
|
151
|
+
// OUTPUT SETTINGS
|
|
152
|
+
// ═══════════════════════════════════════════════════════════════
|
|
153
|
+
{
|
|
154
|
+
displayName: 'Output Settings',
|
|
155
|
+
name: 'outputSettingsNotice',
|
|
156
|
+
type: 'notice',
|
|
157
|
+
default: '',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
displayName: 'Output Base64 Field',
|
|
161
|
+
name: 'outputBase64Field',
|
|
162
|
+
type: 'string',
|
|
163
|
+
default: '',
|
|
164
|
+
placeholder: 'e.g. base64Image (leave empty to skip)',
|
|
165
|
+
description: 'If set, the base64 image will be added to output JSON under this field name',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
displayName: 'Output Base64 Format',
|
|
169
|
+
name: 'outputBase64Format',
|
|
170
|
+
type: 'options',
|
|
171
|
+
options: [
|
|
129
172
|
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
default: '',
|
|
134
|
-
placeholder: 'e.g. base64Image',
|
|
135
|
-
description: 'If set, the base64 image will be added to output JSON under this field name.',
|
|
173
|
+
name: 'Raw Base64',
|
|
174
|
+
value: 'raw',
|
|
175
|
+
description: 'iVBORw0KGgo... (like n8n native binary.data.data)',
|
|
136
176
|
},
|
|
137
177
|
{
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
options: [
|
|
142
|
-
{
|
|
143
|
-
name: 'Raw Base64',
|
|
144
|
-
value: 'raw',
|
|
145
|
-
description: 'iVBORw0KGgo... (like n8n native)',
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
name: 'Data URI',
|
|
149
|
-
value: 'datauri',
|
|
150
|
-
description: 'data:image/png;base64,iVBORw0KGgo...',
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
default: 'raw',
|
|
154
|
-
description: 'Format of the base64 output (only applies if Output Base64 Field is set)',
|
|
178
|
+
name: 'Data URI',
|
|
179
|
+
value: 'datauri',
|
|
180
|
+
description: 'data:image/png;base64,iVBORw0KGgo...',
|
|
155
181
|
},
|
|
182
|
+
],
|
|
183
|
+
default: 'raw',
|
|
184
|
+
description: 'Format of the base64 in the output field. Only used when Output Base64 Field is set.',
|
|
185
|
+
},
|
|
186
|
+
// ═══════════════════════════════════════════════════════════════
|
|
187
|
+
// ADVANCED OPTIONS
|
|
188
|
+
// ═══════════════════════════════════════════════════════════════
|
|
189
|
+
{
|
|
190
|
+
displayName: 'Advanced Options',
|
|
191
|
+
name: 'advancedOptions',
|
|
192
|
+
type: 'collection',
|
|
193
|
+
placeholder: 'Add Option',
|
|
194
|
+
default: {},
|
|
195
|
+
options: [
|
|
156
196
|
{
|
|
157
197
|
displayName: 'Request Timeout (ms)',
|
|
158
198
|
name: 'timeout',
|
|
@@ -170,7 +210,7 @@ class VLMComplexityWorkflow {
|
|
|
170
210
|
};
|
|
171
211
|
}
|
|
172
212
|
/**
|
|
173
|
-
* Execute Method - v3 Dual Output
|
|
213
|
+
* Execute Method - v3.3 Dual Output with improved UI
|
|
174
214
|
*
|
|
175
215
|
* Routes items to LOW or HIGH output based on classification.
|
|
176
216
|
* Always preserves binary data alongside classification results.
|
|
@@ -178,22 +218,25 @@ class VLMComplexityWorkflow {
|
|
|
178
218
|
async execute() {
|
|
179
219
|
var _a;
|
|
180
220
|
const items = this.getInputData();
|
|
181
|
-
this.logger.debug(`VLM Complexity Classifier: Processing ${items.length} input items`);
|
|
221
|
+
this.logger.debug(`VLM Complexity Classifier v3.3: Processing ${items.length} input items`);
|
|
182
222
|
// Get credentials
|
|
183
223
|
const credentials = await this.getCredentials('ollamaApi');
|
|
184
224
|
const serverUrl = credentials.baseUrl.replace(/\/$/, '');
|
|
185
|
-
// Get node parameters
|
|
225
|
+
// Get node parameters - now directly from properties (not nested in options)
|
|
186
226
|
const model = this.getNodeParameter('model', 0);
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
this.logger.debug(`VLM DEBUG:
|
|
227
|
+
const imageSource = this.getNodeParameter('imageSource', 0, 'binary');
|
|
228
|
+
const binaryField = this.getNodeParameter('binaryField', 0, '');
|
|
229
|
+
const base64Field = this.getNodeParameter('base64Field', 0, 'data');
|
|
230
|
+
const outputBase64Field = this.getNodeParameter('outputBase64Field', 0, '');
|
|
231
|
+
const outputBase64Format = this.getNodeParameter('outputBase64Format', 0, 'raw');
|
|
232
|
+
// Advanced options
|
|
233
|
+
const advancedOptions = this.getNodeParameter('advancedOptions', 0, {});
|
|
234
|
+
const timeout = (_a = advancedOptions.timeout) !== null && _a !== void 0 ? _a : 30000;
|
|
235
|
+
// DEBUG: Log all parameters
|
|
236
|
+
this.logger.debug(`VLM DEBUG PARAMS: model='${model}'`);
|
|
237
|
+
this.logger.debug(`VLM DEBUG PARAMS: imageSource='${imageSource}', binaryField='${binaryField}', base64Field='${base64Field}'`);
|
|
238
|
+
this.logger.debug(`VLM DEBUG PARAMS: outputBase64Field='${outputBase64Field}', outputBase64Format='${outputBase64Format}'`);
|
|
239
|
+
this.logger.debug(`VLM DEBUG PARAMS: outputBase64Format === 'raw' ? ${outputBase64Format === 'raw'}`);
|
|
197
240
|
// Verify server has classifier capability (optional check)
|
|
198
241
|
const status = await (0, vlm_logic_1.checkServerStatus)(this, serverUrl, timeout);
|
|
199
242
|
if (status.status === 'error') {
|
|
@@ -215,6 +258,7 @@ class VLMComplexityWorkflow {
|
|
|
215
258
|
if (imageSource === 'base64field') {
|
|
216
259
|
// Extract from JSON field
|
|
217
260
|
const fieldValue = item.json[base64Field];
|
|
261
|
+
this.logger.debug(`VLM DEBUG INPUT [${i}]: Reading from JSON field '${base64Field}', exists=${!!fieldValue}, length=${(fieldValue === null || fieldValue === void 0 ? void 0 : fieldValue.length) || 0}`);
|
|
218
262
|
if (fieldValue) {
|
|
219
263
|
// Auto-detect if it's already a Data URI
|
|
220
264
|
const dataUriMatch = fieldValue.match(/^data:([^;]+);base64,(.+)$/);
|
|
@@ -222,17 +266,19 @@ class VLMComplexityWorkflow {
|
|
|
222
266
|
mimeType = dataUriMatch[1];
|
|
223
267
|
rawBase64 = dataUriMatch[2];
|
|
224
268
|
base64Image = fieldValue; // Already in Data URI format
|
|
269
|
+
this.logger.debug(`VLM DEBUG INPUT [${i}]: Detected Data URI, mimeType='${mimeType}'`);
|
|
225
270
|
}
|
|
226
271
|
else {
|
|
227
272
|
// Raw base64, wrap in Data URI
|
|
228
273
|
rawBase64 = fieldValue;
|
|
229
274
|
base64Image = `data:${mimeType};base64,${rawBase64}`;
|
|
275
|
+
this.logger.debug(`VLM DEBUG INPUT [${i}]: Raw base64, wrapped in Data URI`);
|
|
230
276
|
}
|
|
231
|
-
this.logger.debug(`Item ${i}: Using base64 from JSON field '${base64Field}' (${mimeType})`);
|
|
232
277
|
}
|
|
233
278
|
}
|
|
234
279
|
else {
|
|
235
280
|
// Extract from binary (default)
|
|
281
|
+
this.logger.debug(`VLM DEBUG INPUT [${i}]: Reading from binary, binaryField='${binaryField}', hasBinary=${!!item.binary}`);
|
|
236
282
|
if (item.binary) {
|
|
237
283
|
if (binaryField && item.binary[binaryField]) {
|
|
238
284
|
// Use specified binary field
|
|
@@ -241,7 +287,7 @@ class VLMComplexityWorkflow {
|
|
|
241
287
|
mimeType = data.mimeType || 'image/png';
|
|
242
288
|
rawBase64 = data.data;
|
|
243
289
|
base64Image = `data:${mimeType};base64,${rawBase64}`;
|
|
244
|
-
this.logger.debug(`
|
|
290
|
+
this.logger.debug(`VLM DEBUG INPUT [${i}]: Using specified binary field '${binaryField}' (${mimeType})`);
|
|
245
291
|
}
|
|
246
292
|
}
|
|
247
293
|
else {
|
|
@@ -252,7 +298,7 @@ class VLMComplexityWorkflow {
|
|
|
252
298
|
mimeType = data.mimeType;
|
|
253
299
|
rawBase64 = data.data;
|
|
254
300
|
base64Image = `data:${mimeType};base64,${rawBase64}`;
|
|
255
|
-
this.logger.debug(`
|
|
301
|
+
this.logger.debug(`VLM DEBUG INPUT [${i}]: Auto-detected image from binary '${binaryKey}' (${mimeType})`);
|
|
256
302
|
break;
|
|
257
303
|
}
|
|
258
304
|
}
|
|
@@ -260,7 +306,7 @@ class VLMComplexityWorkflow {
|
|
|
260
306
|
}
|
|
261
307
|
}
|
|
262
308
|
if (!base64Image) {
|
|
263
|
-
this.logger.warn(`
|
|
309
|
+
this.logger.warn(`VLM WARNING [${i}]: No image found (imageSource=${imageSource})`);
|
|
264
310
|
}
|
|
265
311
|
// Classify the document
|
|
266
312
|
const classification = await (0, vlm_logic_1.classifySingleDocument)(this, {
|
|
@@ -268,29 +314,35 @@ class VLMComplexityWorkflow {
|
|
|
268
314
|
base64Image,
|
|
269
315
|
timeout,
|
|
270
316
|
});
|
|
271
|
-
this.logger.debug(`
|
|
317
|
+
this.logger.debug(`VLM CLASSIFICATION [${i}]: ${classification.complexity} (confidence: ${classification.confidence}, model: ${classification.modelUsed})`);
|
|
272
318
|
// Determine base64 output value based on format preference
|
|
273
|
-
|
|
274
|
-
this.logger.debug(`VLM DEBUG OUTPUT
|
|
275
|
-
this.logger.debug(`VLM DEBUG OUTPUT: base64Image prefix='${base64Image === null || base64Image === void 0 ? void 0 : base64Image.substring(0,
|
|
276
|
-
this.logger.debug(`VLM DEBUG OUTPUT: choosing ${outputBase64Format === 'raw' ? 'rawBase64' : 'base64Image'}`);
|
|
319
|
+
this.logger.debug(`VLM DEBUG OUTPUT [${i}]: rawBase64 defined=${!!rawBase64}, length=${(rawBase64 === null || rawBase64 === void 0 ? void 0 : rawBase64.length) || 0}`);
|
|
320
|
+
this.logger.debug(`VLM DEBUG OUTPUT [${i}]: rawBase64 prefix='${rawBase64 === null || rawBase64 === void 0 ? void 0 : rawBase64.substring(0, 30)}...'`);
|
|
321
|
+
this.logger.debug(`VLM DEBUG OUTPUT [${i}]: base64Image prefix='${base64Image === null || base64Image === void 0 ? void 0 : base64Image.substring(0, 40)}...'`);
|
|
322
|
+
this.logger.debug(`VLM DEBUG OUTPUT [${i}]: format='${outputBase64Format}', choosing ${outputBase64Format === 'raw' ? 'rawBase64' : 'base64Image'}`);
|
|
277
323
|
const outputBase64Value = outputBase64Format === 'raw' ? rawBase64 : base64Image;
|
|
278
|
-
this.logger.debug(`VLM DEBUG OUTPUT: outputBase64Value prefix='${outputBase64Value === null || outputBase64Value === void 0 ? void 0 : outputBase64Value.substring(0,
|
|
324
|
+
this.logger.debug(`VLM DEBUG OUTPUT [${i}]: outputBase64Value prefix='${outputBase64Value === null || outputBase64Value === void 0 ? void 0 : outputBase64Value.substring(0, 40)}...'`);
|
|
325
|
+
this.logger.debug(`VLM DEBUG OUTPUT [${i}]: Will add to JSON? outputBase64Field='${outputBase64Field}' (truthy=${!!outputBase64Field}), hasValue=${!!outputBase64Value}`);
|
|
326
|
+
// Build base JSON output
|
|
327
|
+
const outputJson = {
|
|
328
|
+
// Classification results (top level for visibility)
|
|
329
|
+
complexityClass: classification.complexity,
|
|
330
|
+
complexityConfidence: classification.confidence,
|
|
331
|
+
processingTime: classification.processingTime,
|
|
332
|
+
modelUsed: classification.modelUsed,
|
|
333
|
+
// Original JSON data merged in
|
|
334
|
+
...item.json,
|
|
335
|
+
// Item tracking
|
|
336
|
+
_itemIndex: i,
|
|
337
|
+
};
|
|
338
|
+
// Add base64 to output if field name is specified
|
|
339
|
+
if (outputBase64Field && outputBase64Field.trim() !== '' && outputBase64Value) {
|
|
340
|
+
outputJson[outputBase64Field] = outputBase64Value;
|
|
341
|
+
this.logger.debug(`VLM DEBUG OUTPUT [${i}]: Added '${outputBase64Field}' = '${outputBase64Value.substring(0, 30)}...'`);
|
|
342
|
+
}
|
|
279
343
|
// Build output item with BOTH json AND binary
|
|
280
344
|
const outputItem = {
|
|
281
|
-
json:
|
|
282
|
-
// Classification results (top level for visibility)
|
|
283
|
-
complexityClass: classification.complexity,
|
|
284
|
-
complexityConfidence: classification.confidence,
|
|
285
|
-
processingTime: classification.processingTime,
|
|
286
|
-
modelUsed: classification.modelUsed,
|
|
287
|
-
// Original JSON data merged in
|
|
288
|
-
...item.json,
|
|
289
|
-
// Item tracking
|
|
290
|
-
_itemIndex: i,
|
|
291
|
-
// Optional: include base64 in output JSON (format depends on outputBase64Format)
|
|
292
|
-
...(outputBase64Field && outputBase64Value ? { [outputBase64Field]: outputBase64Value } : {}),
|
|
293
|
-
},
|
|
345
|
+
json: outputJson,
|
|
294
346
|
// ALWAYS preserve binary data
|
|
295
347
|
binary: item.binary,
|
|
296
348
|
};
|
|
@@ -302,7 +354,7 @@ class VLMComplexityWorkflow {
|
|
|
302
354
|
lowItems.push(outputItem);
|
|
303
355
|
}
|
|
304
356
|
}
|
|
305
|
-
this.logger.debug(`
|
|
357
|
+
this.logger.debug(`VLM COMPLETE: ${lowItems.length} LOW, ${highItems.length} HIGH`);
|
|
306
358
|
// Return dual outputs: [LOW, HIGH]
|
|
307
359
|
return [lowItems, highItems];
|
|
308
360
|
}
|
package/package.json
CHANGED