n8n-nodes-vlm 2.0.7 → 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
- * A workflow node that classifies document complexity using Vision-Language Models.
6
- * Can be used as a standalone tool or chained in workflows.
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
- * Workflow nodes use execute() to process items through the workflow
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
- * A workflow node that classifies document complexity using Vision-Language Models.
10
- * Can be used as a standalone tool or chained in workflows.
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 Workflow',
15
+ displayName: 'VLM Complexity Classifier',
16
16
  name: 'vlmComplexityWorkflow',
17
17
  icon: 'file:vlm.svg',
18
18
  group: ['transform'],
19
- version: 1,
19
+ version: 2,
20
20
  subtitle: '={{$parameter["model"]}}',
21
- description: 'Classify document complexity using Vision-Language Models',
21
+ description: 'Classify document complexity and route to LOW/HIGH outputs',
22
22
  defaults: {
23
- name: 'VLM Complexity Workflow',
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: 'Document Source',
88
- name: 'documentSource',
89
- type: 'options',
90
- options: [
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
- name: 'Low Complexity Only',
165
- value: 'LOW',
166
- description: 'Keep only simple documents (good for OCR)',
167
- },
168
- {
169
- name: 'High Complexity Only',
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
- name: 'Both',
175
- value: 'both',
176
- description: 'Keep all documents regardless of complexity',
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
- * Workflow nodes use execute() to process items through the workflow
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, _b;
133
+ var _a;
241
134
  const items = this.getInputData();
242
- this.logger.debug(`VLM Complexity Workflow: Processing ${items.length} input items`);
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 documentSource = this.getNodeParameter('documentSource', 0);
249
- const classificationStrategy = this.getNodeParameter('classificationStrategy', 0);
250
- const filterComplexity = this.getNodeParameter('filterComplexity', 0, 'both');
251
- const additionalOptions = this.getNodeParameter('additionalOptions', 0, {});
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
- // Extract documents based on source
263
- let documents = [];
264
- if (documentSource === 'items') {
265
- // Use all items as documents
266
- documents = items.map((item, index) => {
267
- const doc = {
268
- pageContent: JSON.stringify(item.json),
269
- metadata: {
270
- _itemIndex: index,
271
- _originalBinary: item.binary, // Preserve original binary data
272
- },
273
- };
274
- // Extract binary image data from n8n item.binary
275
- if (item.binary) {
276
- // Find first image in binary data
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
- // n8n stores binary data as base64 in data.data
281
- const base64Data = data.data;
282
- // Add base64 image with data URI format
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
- else if (documentSource === 'expression') {
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
- if (documents.length === 0) {
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
- model,
387
- documents,
188
+ base64Image,
388
189
  timeout,
389
- classificationStrategy,
390
- filterComplexity,
391
190
  });
392
- this.logger.debug(`Classification complete: ${classifiedDocs.length} documents returned`);
393
- // Format output based on outputFormat option
394
- if (outputFormat === 'items') {
395
- // Return each document as a separate item
396
- return [
397
- classifiedDocs.map((doc) => {
398
- // Flatten structure for better n8n usability
399
- const flattenedJson = {
400
- // Classification results (top level for visibility)
401
- complexityClass: doc._complexityClass,
402
- complexityConfidence: doc._complexityConfidence,
403
- processingTime: doc._processingTime,
404
- modelUsed: doc._modelUsed,
405
- // Document content
406
- content: doc.pageContent,
407
- // Original metadata (if any)
408
- ...(doc.metadata && doc.metadata._itemIndex !== undefined ? { itemIndex: doc.metadata._itemIndex } : {}),
409
- ...(doc._originalIndex !== undefined ? { originalIndex: doc._originalIndex } : {}),
410
- };
411
- const outputItem = { json: flattenedJson };
412
- // Restore original binary data if it was preserved
413
- if (doc.metadata && doc.metadata._originalBinary) {
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
- // Default: return documents array with flattened structure
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
- catch (error) {
460
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `VLM classification failed: ${error.message}`);
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");
@@ -75,9 +76,9 @@ async function classifyDocumentComplexity(context, serverUrl, document, timeout)
75
76
  });
76
77
  (_d = context.logger) === null || _d === void 0 ? void 0 : _d.debug(`VLM Classification: Received response: ${JSON.stringify(response)}`);
77
78
  return {
78
- complexity: response.complexity || response.classification || 'LOW',
79
+ complexity: response.class_name || response.complexity || response.classification || 'LOW',
79
80
  confidence: response.confidence,
80
- processingTime: response.processing_time || response.processingTime,
81
+ processingTime: response.processing_time || response.processingTime || response.latency_ms,
81
82
  modelUsed: response.model || 'VLM-Classifier',
82
83
  };
83
84
  }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-vlm",
3
- "version": "2.0.7",
3
+ "version": "3.1.1",
4
4
  "description": "Vision-Language Models for n8n - Lightweight specialized VLMs for document analysis and classification",
5
5
  "main": "index.js",
6
6
  "author": "Nicolas Geysse",