n8n-nodes-vlm 2.0.8 → 3.1.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,16 +1,17 @@
|
|
|
1
1
|
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
2
|
/**
|
|
3
|
-
* VLM Complexity Classifier Workflow Node
|
|
3
|
+
* VLM Complexity Classifier Workflow Node v3
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Routes documents to LOW or HIGH outputs based on VLM classification.
|
|
6
|
+
* Always preserves both binary data and classification results.
|
|
7
7
|
*/
|
|
8
8
|
export declare class VLMComplexityWorkflow implements INodeType {
|
|
9
9
|
description: INodeTypeDescription;
|
|
10
10
|
/**
|
|
11
|
-
* Execute Method
|
|
11
|
+
* Execute Method - v3 Dual Output
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* Routes items to LOW or HIGH output based on classification.
|
|
14
|
+
* Always preserves binary data alongside classification results.
|
|
14
15
|
*/
|
|
15
16
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
16
17
|
}
|
|
@@ -4,23 +4,23 @@ 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
|
|
7
|
+
* VLM Complexity Classifier Workflow Node v3
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Routes documents to LOW or HIGH outputs based on VLM classification.
|
|
10
|
+
* Always preserves both binary data and classification results.
|
|
11
11
|
*/
|
|
12
12
|
class VLMComplexityWorkflow {
|
|
13
13
|
constructor() {
|
|
14
14
|
this.description = {
|
|
15
|
-
displayName: 'VLM Complexity
|
|
15
|
+
displayName: 'VLM Complexity Classifier',
|
|
16
16
|
name: 'vlmComplexityWorkflow',
|
|
17
17
|
icon: 'file:vlm.svg',
|
|
18
18
|
group: ['transform'],
|
|
19
|
-
version:
|
|
19
|
+
version: 2,
|
|
20
20
|
subtitle: '={{$parameter["model"]}}',
|
|
21
|
-
description: 'Classify document complexity
|
|
21
|
+
description: 'Classify document complexity and route to LOW/HIGH outputs',
|
|
22
22
|
defaults: {
|
|
23
|
-
name: 'VLM Complexity
|
|
23
|
+
name: 'VLM Complexity Classifier',
|
|
24
24
|
},
|
|
25
25
|
usableAsTool: true,
|
|
26
26
|
codex: {
|
|
@@ -30,7 +30,8 @@ class VLMComplexityWorkflow {
|
|
|
30
30
|
},
|
|
31
31
|
},
|
|
32
32
|
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
33
|
-
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
33
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main, n8n_workflow_1.NodeConnectionTypes.Main],
|
|
34
|
+
outputNames: ['LOW', 'HIGH'],
|
|
34
35
|
credentials: [
|
|
35
36
|
{
|
|
36
37
|
name: 'ollamaApi',
|
|
@@ -84,113 +85,28 @@ class VLMComplexityWorkflow {
|
|
|
84
85
|
required: true,
|
|
85
86
|
},
|
|
86
87
|
{
|
|
87
|
-
displayName: '
|
|
88
|
-
name: '
|
|
89
|
-
type: '
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
name: 'All Items',
|
|
93
|
-
value: 'items',
|
|
94
|
-
description: 'Process all items from input',
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: 'From Field',
|
|
98
|
-
value: 'field',
|
|
99
|
-
description: 'Extract documents from a specific field',
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: 'From Expression',
|
|
103
|
-
value: 'expression',
|
|
104
|
-
description: 'Define documents using an expression',
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
default: 'items',
|
|
108
|
-
description: 'Where to get documents for classification',
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
displayName: 'Field Name',
|
|
112
|
-
name: 'fieldName',
|
|
113
|
-
type: 'string',
|
|
114
|
-
default: 'documents',
|
|
115
|
-
description: 'Name of the field containing documents',
|
|
116
|
-
displayOptions: {
|
|
117
|
-
show: {
|
|
118
|
-
documentSource: ['field'],
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
displayName: 'Documents Expression',
|
|
124
|
-
name: 'documentsExpression',
|
|
125
|
-
type: 'string',
|
|
126
|
-
default: '={{$json.documents}}',
|
|
127
|
-
description: 'Expression to extract documents',
|
|
128
|
-
displayOptions: {
|
|
129
|
-
show: {
|
|
130
|
-
documentSource: ['expression'],
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
displayName: 'Classification Strategy',
|
|
136
|
-
name: 'classificationStrategy',
|
|
137
|
-
type: 'options',
|
|
138
|
-
options: [
|
|
139
|
-
{
|
|
140
|
-
name: 'Add Metadata Only',
|
|
141
|
-
value: 'metadata',
|
|
142
|
-
description: 'Add complexity classification as document metadata',
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
name: 'Filter Documents',
|
|
146
|
-
value: 'filter',
|
|
147
|
-
description: 'Filter documents based on complexity',
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: 'Filter + Metadata',
|
|
151
|
-
value: 'both',
|
|
152
|
-
description: 'Both filter and add metadata',
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
default: 'metadata',
|
|
156
|
-
description: 'How to use the classification results',
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
displayName: 'Filter Complexity',
|
|
160
|
-
name: 'filterComplexity',
|
|
161
|
-
type: 'options',
|
|
88
|
+
displayName: 'Options',
|
|
89
|
+
name: 'options',
|
|
90
|
+
type: 'collection',
|
|
91
|
+
placeholder: 'Add Option',
|
|
92
|
+
default: {},
|
|
162
93
|
options: [
|
|
163
94
|
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
value: 'HIGH',
|
|
171
|
-
description: 'Keep only complex documents (good for VLM)',
|
|
95
|
+
displayName: 'Binary Field',
|
|
96
|
+
name: 'binaryField',
|
|
97
|
+
type: 'string',
|
|
98
|
+
default: '',
|
|
99
|
+
placeholder: 'e.g. data (auto-detect if empty)',
|
|
100
|
+
description: 'Name of the binary field to use for classification. Leave empty to auto-detect first image.',
|
|
172
101
|
},
|
|
173
102
|
{
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
103
|
+
displayName: 'Output Base64 Field',
|
|
104
|
+
name: 'outputBase64Field',
|
|
105
|
+
type: 'string',
|
|
106
|
+
default: '',
|
|
107
|
+
placeholder: 'e.g. base64Image',
|
|
108
|
+
description: 'If set, the base64 image string will be added to output JSON under this field name.',
|
|
177
109
|
},
|
|
178
|
-
],
|
|
179
|
-
default: 'both',
|
|
180
|
-
description: 'Which complexity level to keep when filtering',
|
|
181
|
-
displayOptions: {
|
|
182
|
-
show: {
|
|
183
|
-
classificationStrategy: ['filter', 'both'],
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
displayName: 'Additional Options',
|
|
189
|
-
name: 'additionalOptions',
|
|
190
|
-
type: 'collection',
|
|
191
|
-
placeholder: 'Add Option',
|
|
192
|
-
default: {},
|
|
193
|
-
options: [
|
|
194
110
|
{
|
|
195
111
|
displayName: 'Request Timeout (ms)',
|
|
196
112
|
name: 'timeout',
|
|
@@ -202,55 +118,30 @@ class VLMComplexityWorkflow {
|
|
|
202
118
|
},
|
|
203
119
|
description: 'Maximum time per API request',
|
|
204
120
|
},
|
|
205
|
-
{
|
|
206
|
-
displayName: 'Output Format',
|
|
207
|
-
name: 'outputFormat',
|
|
208
|
-
type: 'options',
|
|
209
|
-
options: [
|
|
210
|
-
{
|
|
211
|
-
name: 'Separate Items',
|
|
212
|
-
value: 'items',
|
|
213
|
-
description: 'Return each document as a separate item (recommended for binary data)',
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
name: 'Original Format',
|
|
217
|
-
value: 'original',
|
|
218
|
-
description: 'Maintain original input structure with classifications added',
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
name: 'Documents Array',
|
|
222
|
-
value: 'documents',
|
|
223
|
-
description: 'Return classified documents in an array',
|
|
224
|
-
},
|
|
225
|
-
],
|
|
226
|
-
default: 'items',
|
|
227
|
-
description: 'How to format the output',
|
|
228
|
-
},
|
|
229
121
|
],
|
|
230
122
|
},
|
|
231
123
|
],
|
|
232
124
|
};
|
|
233
125
|
}
|
|
234
126
|
/**
|
|
235
|
-
* Execute Method
|
|
127
|
+
* Execute Method - v3 Dual Output
|
|
236
128
|
*
|
|
237
|
-
*
|
|
129
|
+
* Routes items to LOW or HIGH output based on classification.
|
|
130
|
+
* Always preserves binary data alongside classification results.
|
|
238
131
|
*/
|
|
239
132
|
async execute() {
|
|
240
|
-
var _a
|
|
133
|
+
var _a;
|
|
241
134
|
const items = this.getInputData();
|
|
242
|
-
this.logger.debug(`VLM Complexity
|
|
135
|
+
this.logger.debug(`VLM Complexity Classifier: Processing ${items.length} input items`);
|
|
243
136
|
// Get credentials
|
|
244
137
|
const credentials = await this.getCredentials('ollamaApi');
|
|
245
138
|
const serverUrl = credentials.baseUrl.replace(/\/$/, '');
|
|
246
139
|
// Get node parameters
|
|
247
140
|
const model = this.getNodeParameter('model', 0);
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
const timeout = (_a = additionalOptions.timeout) !== null && _a !== void 0 ? _a : 30000;
|
|
253
|
-
const outputFormat = (_b = additionalOptions.outputFormat) !== null && _b !== void 0 ? _b : 'documents';
|
|
141
|
+
const options = this.getNodeParameter('options', 0, {});
|
|
142
|
+
const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : 30000;
|
|
143
|
+
const binaryField = options.binaryField || '';
|
|
144
|
+
const outputBase64Field = options.outputBase64Field || '';
|
|
254
145
|
// Verify server has classifier capability (optional check)
|
|
255
146
|
const status = await (0, vlm_logic_1.checkServerStatus)(this, serverUrl, timeout);
|
|
256
147
|
if (status.status === 'error') {
|
|
@@ -259,206 +150,74 @@ class VLMComplexityWorkflow {
|
|
|
259
150
|
else if (!status.hasClassifier) {
|
|
260
151
|
this.logger.warn(`Server at ${serverUrl} does not explicitly report VLM classifier support. Proceeding anyway...`);
|
|
261
152
|
}
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
153
|
+
// Dual output arrays
|
|
154
|
+
const lowItems = [];
|
|
155
|
+
const highItems = [];
|
|
156
|
+
// Process each input item
|
|
157
|
+
for (let i = 0; i < items.length; i++) {
|
|
158
|
+
const item = items[i];
|
|
159
|
+
// Extract base64 image from binary
|
|
160
|
+
let base64Image;
|
|
161
|
+
if (item.binary) {
|
|
162
|
+
if (binaryField && item.binary[binaryField]) {
|
|
163
|
+
// Use specified binary field
|
|
164
|
+
const data = item.binary[binaryField];
|
|
165
|
+
if (data.data) {
|
|
166
|
+
base64Image = `data:${data.mimeType || 'image/png'};base64,${data.data}`;
|
|
167
|
+
this.logger.debug(`Item ${i}: Using specified binary field '${binaryField}' (${data.mimeType || 'unknown'})`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// Auto-detect first image (default behavior)
|
|
277
172
|
for (const [binaryKey, binaryData] of Object.entries(item.binary)) {
|
|
278
173
|
const data = binaryData;
|
|
279
|
-
if (data.mimeType && data.mimeType.startsWith('image/')) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (base64Data) {
|
|
284
|
-
doc.base64Image = `data:${data.mimeType};base64,${base64Data}`;
|
|
285
|
-
doc.metadata._binaryKey = binaryKey; // Remember which binary key was used
|
|
286
|
-
this.logger.debug(`Extracted image from binary '${binaryKey}' (${data.mimeType}) for item ${index}`);
|
|
287
|
-
break; // Use first image found
|
|
288
|
-
}
|
|
174
|
+
if (data.mimeType && data.mimeType.startsWith('image/') && data.data) {
|
|
175
|
+
base64Image = `data:${data.mimeType};base64,${data.data}`;
|
|
176
|
+
this.logger.debug(`Item ${i}: Auto-detected image from binary '${binaryKey}' (${data.mimeType})`);
|
|
177
|
+
break;
|
|
289
178
|
}
|
|
290
179
|
}
|
|
291
180
|
}
|
|
292
|
-
return doc;
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
else if (documentSource === 'field') {
|
|
296
|
-
// Extract from field
|
|
297
|
-
const fieldName = this.getNodeParameter('fieldName', 0);
|
|
298
|
-
for (let i = 0; i < items.length; i++) {
|
|
299
|
-
const fieldData = items[i].json[fieldName];
|
|
300
|
-
const extractedDocs = [];
|
|
301
|
-
if (Array.isArray(fieldData)) {
|
|
302
|
-
extractedDocs.push(...fieldData);
|
|
303
|
-
}
|
|
304
|
-
else if (fieldData) {
|
|
305
|
-
extractedDocs.push(fieldData);
|
|
306
|
-
}
|
|
307
|
-
// For each extracted document, also attach binary data if available
|
|
308
|
-
extractedDocs.forEach((doc) => {
|
|
309
|
-
const enhancedDoc = typeof doc === 'object' ? { ...doc } : { content: doc };
|
|
310
|
-
// Extract binary image data from n8n item.binary
|
|
311
|
-
const itemBinary = items[i].binary;
|
|
312
|
-
if (itemBinary) {
|
|
313
|
-
enhancedDoc.metadata = enhancedDoc.metadata || {};
|
|
314
|
-
enhancedDoc.metadata._originalBinary = itemBinary;
|
|
315
|
-
enhancedDoc.metadata._itemIndex = i;
|
|
316
|
-
// Find first image in binary data
|
|
317
|
-
for (const [binaryKey, binaryData] of Object.entries(itemBinary)) {
|
|
318
|
-
const data = binaryData;
|
|
319
|
-
if (data.mimeType && data.mimeType.startsWith('image/')) {
|
|
320
|
-
const base64Data = data.data;
|
|
321
|
-
if (base64Data) {
|
|
322
|
-
enhancedDoc.base64Image = `data:${data.mimeType};base64,${base64Data}`;
|
|
323
|
-
enhancedDoc.metadata._binaryKey = binaryKey;
|
|
324
|
-
this.logger.debug(`Extracted image from binary '${binaryKey}' (${data.mimeType}) for item ${i} (field source)`);
|
|
325
|
-
break;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
documents.push(enhancedDoc);
|
|
331
|
-
});
|
|
332
181
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
// Extract from expression
|
|
336
|
-
for (let i = 0; i < items.length; i++) {
|
|
337
|
-
const expressionValue = this.getNodeParameter('documentsExpression', i);
|
|
338
|
-
const extractedDocs = [];
|
|
339
|
-
if (Array.isArray(expressionValue)) {
|
|
340
|
-
extractedDocs.push(...expressionValue);
|
|
341
|
-
}
|
|
342
|
-
else if (expressionValue) {
|
|
343
|
-
extractedDocs.push(expressionValue);
|
|
344
|
-
}
|
|
345
|
-
// For each extracted document, also attach binary data if available
|
|
346
|
-
extractedDocs.forEach((doc) => {
|
|
347
|
-
const enhancedDoc = typeof doc === 'object' ? { ...doc } : { content: doc };
|
|
348
|
-
// Extract binary image data from n8n item.binary
|
|
349
|
-
const itemBinary = items[i].binary;
|
|
350
|
-
if (itemBinary) {
|
|
351
|
-
enhancedDoc.metadata = enhancedDoc.metadata || {};
|
|
352
|
-
enhancedDoc.metadata._originalBinary = itemBinary;
|
|
353
|
-
enhancedDoc.metadata._itemIndex = i;
|
|
354
|
-
// Find first image in binary data
|
|
355
|
-
for (const [binaryKey, binaryData] of Object.entries(itemBinary)) {
|
|
356
|
-
const data = binaryData;
|
|
357
|
-
if (data.mimeType && data.mimeType.startsWith('image/')) {
|
|
358
|
-
const base64Data = data.data;
|
|
359
|
-
if (base64Data) {
|
|
360
|
-
enhancedDoc.base64Image = `data:${data.mimeType};base64,${base64Data}`;
|
|
361
|
-
enhancedDoc.metadata._binaryKey = binaryKey;
|
|
362
|
-
this.logger.debug(`Extracted image from binary '${binaryKey}' (${data.mimeType}) for item ${i} (expression source)`);
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
documents.push(enhancedDoc);
|
|
369
|
-
});
|
|
182
|
+
if (!base64Image) {
|
|
183
|
+
this.logger.warn(`Item ${i}: No image found in binary data`);
|
|
370
184
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
this.logger.debug('No documents found to classify');
|
|
374
|
-
return [items];
|
|
375
|
-
}
|
|
376
|
-
// Debug: Log how many documents have images
|
|
377
|
-
const docsWithImages = documents.filter((doc) => doc.base64Image).length;
|
|
378
|
-
this.logger.debug(`Classifying ${documents.length} documents with model: ${model} (${docsWithImages} have images)`);
|
|
379
|
-
if (docsWithImages === 0) {
|
|
380
|
-
this.logger.warn('⚠️ No images found in documents! Binary extraction may have failed. Check that input items have binary data.');
|
|
381
|
-
}
|
|
382
|
-
try {
|
|
383
|
-
// Classify documents
|
|
384
|
-
const classifiedDocs = await (0, vlm_logic_1.classifyDocuments)(this, {
|
|
185
|
+
// Classify the document
|
|
186
|
+
const classification = await (0, vlm_logic_1.classifySingleDocument)(this, {
|
|
385
187
|
serverUrl,
|
|
386
|
-
|
|
387
|
-
documents,
|
|
188
|
+
base64Image,
|
|
388
189
|
timeout,
|
|
389
|
-
classificationStrategy,
|
|
390
|
-
filterComplexity,
|
|
391
190
|
});
|
|
392
|
-
this.logger.debug(`
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
outputItem.binary = doc.metadata._originalBinary;
|
|
415
|
-
}
|
|
416
|
-
return outputItem;
|
|
417
|
-
}),
|
|
418
|
-
];
|
|
419
|
-
}
|
|
420
|
-
else if (outputFormat === 'original') {
|
|
421
|
-
// Maintain original structure but add flattened classification metadata
|
|
422
|
-
const outputItems = items.map((item, index) => {
|
|
423
|
-
// Find classification for this item
|
|
424
|
-
const itemClassification = classifiedDocs.find((doc) => { var _a; return ((_a = doc.metadata) === null || _a === void 0 ? void 0 : _a._itemIndex) === index || doc._originalIndex === index; });
|
|
425
|
-
const enhancedJson = {
|
|
426
|
-
...item.json,
|
|
427
|
-
};
|
|
428
|
-
// Add classification metadata at top level
|
|
429
|
-
if (itemClassification) {
|
|
430
|
-
enhancedJson.complexityClass = itemClassification._complexityClass;
|
|
431
|
-
enhancedJson.complexityConfidence = itemClassification._complexityConfidence;
|
|
432
|
-
enhancedJson.processingTime = itemClassification._processingTime;
|
|
433
|
-
enhancedJson.modelUsed = itemClassification._modelUsed;
|
|
434
|
-
}
|
|
435
|
-
return {
|
|
436
|
-
json: enhancedJson,
|
|
437
|
-
binary: item.binary, // Preserve original binaries
|
|
438
|
-
};
|
|
439
|
-
});
|
|
440
|
-
return [outputItems];
|
|
191
|
+
this.logger.debug(`Item ${i}: Classified as ${classification.complexity} (confidence: ${classification.confidence})`);
|
|
192
|
+
// Build output item with BOTH json AND binary
|
|
193
|
+
const outputItem = {
|
|
194
|
+
json: {
|
|
195
|
+
// Classification results (top level for visibility)
|
|
196
|
+
complexityClass: classification.complexity,
|
|
197
|
+
complexityConfidence: classification.confidence,
|
|
198
|
+
processingTime: classification.processingTime,
|
|
199
|
+
modelUsed: classification.modelUsed,
|
|
200
|
+
// Original JSON data merged in
|
|
201
|
+
...item.json,
|
|
202
|
+
// Item tracking
|
|
203
|
+
_itemIndex: i,
|
|
204
|
+
// Optional: include base64 in output JSON
|
|
205
|
+
...(outputBase64Field && base64Image ? { [outputBase64Field]: base64Image } : {}),
|
|
206
|
+
},
|
|
207
|
+
// ALWAYS preserve binary data
|
|
208
|
+
binary: item.binary,
|
|
209
|
+
};
|
|
210
|
+
// Route to appropriate output
|
|
211
|
+
if (classification.complexity === 'HIGH') {
|
|
212
|
+
highItems.push(outputItem);
|
|
441
213
|
}
|
|
442
214
|
else {
|
|
443
|
-
|
|
444
|
-
const flattenedDocs = classifiedDocs.map((doc) => {
|
|
445
|
-
var _a;
|
|
446
|
-
return ({
|
|
447
|
-
complexityClass: doc._complexityClass,
|
|
448
|
-
complexityConfidence: doc._complexityConfidence,
|
|
449
|
-
processingTime: doc._processingTime,
|
|
450
|
-
modelUsed: doc._modelUsed,
|
|
451
|
-
content: doc.pageContent,
|
|
452
|
-
itemIndex: (_a = doc.metadata) === null || _a === void 0 ? void 0 : _a._itemIndex,
|
|
453
|
-
originalIndex: doc._originalIndex,
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
return [[{ json: { documents: flattenedDocs } }]];
|
|
215
|
+
lowItems.push(outputItem);
|
|
457
216
|
}
|
|
458
217
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
218
|
+
this.logger.debug(`Classification complete: ${lowItems.length} LOW, ${highItems.length} HIGH`);
|
|
219
|
+
// Return dual outputs: [LOW, HIGH]
|
|
220
|
+
return [lowItems, highItems];
|
|
462
221
|
}
|
|
463
222
|
}
|
|
464
223
|
exports.VLMComplexityWorkflow = VLMComplexityWorkflow;
|
|
@@ -8,6 +8,11 @@ export interface VLMConfig {
|
|
|
8
8
|
classificationStrategy?: 'metadata' | 'filter' | 'both';
|
|
9
9
|
filterComplexity?: 'LOW' | 'HIGH' | 'both';
|
|
10
10
|
}
|
|
11
|
+
export interface SingleDocConfig {
|
|
12
|
+
serverUrl: string;
|
|
13
|
+
base64Image?: string;
|
|
14
|
+
timeout: number;
|
|
15
|
+
}
|
|
11
16
|
export interface VLClassificationResult {
|
|
12
17
|
complexity: 'LOW' | 'HIGH';
|
|
13
18
|
confidence?: number;
|
|
@@ -26,6 +31,11 @@ export interface ServerStatus {
|
|
|
26
31
|
* Check server status to detect VLM classifier capabilities
|
|
27
32
|
*/
|
|
28
33
|
export declare function checkServerStatus(context: VLMContext, serverUrl: string, timeout?: number): Promise<ServerStatus>;
|
|
34
|
+
/**
|
|
35
|
+
* Classify a single document - simplified API for workflow node v3
|
|
36
|
+
* Always returns a result (defaults to LOW on error)
|
|
37
|
+
*/
|
|
38
|
+
export declare function classifySingleDocument(context: VLMContext, config: SingleDocConfig): Promise<VLClassificationResult>;
|
|
29
39
|
/**
|
|
30
40
|
* Classify documents using VLM Classifier
|
|
31
41
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkServerStatus = checkServerStatus;
|
|
4
|
+
exports.classifySingleDocument = classifySingleDocument;
|
|
4
5
|
exports.classifyDocuments = classifyDocuments;
|
|
5
6
|
exports.listVLMModels = listVLMModels;
|
|
6
7
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
@@ -96,6 +97,47 @@ async function classifyDocumentComplexity(context, serverUrl, document, timeout)
|
|
|
96
97
|
};
|
|
97
98
|
}
|
|
98
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Classify a single document - simplified API for workflow node v3
|
|
102
|
+
* Always returns a result (defaults to LOW on error)
|
|
103
|
+
*/
|
|
104
|
+
async function classifySingleDocument(context, config) {
|
|
105
|
+
var _a, _b, _c;
|
|
106
|
+
const { serverUrl, base64Image, timeout } = config;
|
|
107
|
+
try {
|
|
108
|
+
const requestBody = base64Image
|
|
109
|
+
? { image: base64Image }
|
|
110
|
+
: { text: '' };
|
|
111
|
+
(_a = context.logger) === null || _a === void 0 ? void 0 : _a.debug(`VLM Single Classification: Calling ${serverUrl}/api/classify/base64 (hasImage: ${!!base64Image})`);
|
|
112
|
+
const response = await context.helpers.httpRequest({
|
|
113
|
+
method: 'POST',
|
|
114
|
+
url: `${serverUrl}/api/classify/base64`,
|
|
115
|
+
headers: {
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
Accept: 'application/json',
|
|
118
|
+
},
|
|
119
|
+
body: requestBody,
|
|
120
|
+
json: true,
|
|
121
|
+
timeout,
|
|
122
|
+
});
|
|
123
|
+
(_b = context.logger) === null || _b === void 0 ? void 0 : _b.debug(`VLM Single Classification: Response: ${JSON.stringify(response)}`);
|
|
124
|
+
return {
|
|
125
|
+
complexity: response.class_name || response.complexity || response.classification || 'LOW',
|
|
126
|
+
confidence: response.confidence,
|
|
127
|
+
processingTime: response.processing_time || response.processingTime || response.latency_ms,
|
|
128
|
+
modelUsed: response.model || 'VLM-Classifier',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
(_c = context.logger) === null || _c === void 0 ? void 0 : _c.error(`VLM Single Classification FAILED: ${error.message}`);
|
|
133
|
+
return {
|
|
134
|
+
complexity: 'LOW',
|
|
135
|
+
confidence: 0,
|
|
136
|
+
processingTime: undefined,
|
|
137
|
+
modelUsed: 'ERROR - Classification failed',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
99
141
|
/**
|
|
100
142
|
* Classify documents using VLM Classifier
|
|
101
143
|
*/
|
package/package.json
CHANGED