n8n-nodes-fasttext-language-detector 0.1.4 → 0.1.6
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.
|
@@ -195,31 +195,50 @@ class FastTextLanguageDetector {
|
|
|
195
195
|
}
|
|
196
196
|
// Динамически загружаем fasttext.js только когда он нужен
|
|
197
197
|
// Это предотвращает загрузку WASM при старте n8n
|
|
198
|
-
//
|
|
198
|
+
// ВАЖНО: используем прямой путь к lib/index.js, чтобы избежать автоматической загрузки WASM
|
|
199
199
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-var-requires
|
|
200
200
|
const fastTextModule = await new Promise((resolve, reject) => {
|
|
201
201
|
try {
|
|
202
|
-
// Используем
|
|
202
|
+
// Используем прямой путь к index.js, минуя автоматическую загрузку WASM
|
|
203
203
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
204
|
-
const
|
|
205
|
-
resolve(
|
|
204
|
+
const FastText = require('fasttext.js/lib/index');
|
|
205
|
+
resolve(FastText);
|
|
206
206
|
}
|
|
207
207
|
catch (error) {
|
|
208
|
-
|
|
208
|
+
// Если прямой путь не работает, пробуем обычный require
|
|
209
|
+
try {
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
211
|
+
const FastText = require('fasttext.js');
|
|
212
|
+
resolve(FastText);
|
|
213
|
+
}
|
|
214
|
+
catch (fallbackError) {
|
|
215
|
+
reject(fallbackError);
|
|
216
|
+
}
|
|
209
217
|
}
|
|
210
218
|
});
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
(fastTextModule.default && fastTextModule.default.
|
|
215
|
-
|
|
216
|
-
if (!FastTextClassifier || typeof FastTextClassifier !== 'function') {
|
|
219
|
+
// fasttext.js экспортирует класс FastText напрямую
|
|
220
|
+
const FastTextClass = fastTextModule ||
|
|
221
|
+
fastTextModule.default ||
|
|
222
|
+
(fastTextModule.default && fastTextModule.default.default);
|
|
223
|
+
if (!FastTextClass || typeof FastTextClass !== 'function') {
|
|
217
224
|
const availableExports = Object.keys(fastTextModule).join(', ');
|
|
218
|
-
throw new n8n_workflow_1.ApplicationError(`Failed to load fastText module:
|
|
225
|
+
throw new n8n_workflow_1.ApplicationError(`Failed to load fastText module: FastText class not found. Available exports: ${availableExports}`, { level: 'error' });
|
|
219
226
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
227
|
+
// Создаем экземпляр FastText с путем к модели
|
|
228
|
+
// ВАЖНО: явно отключаем WASM, используем бинарный режим
|
|
229
|
+
const fastTextInstance = new FastTextClass({
|
|
230
|
+
loadModel: modelPath,
|
|
231
|
+
predict: {
|
|
232
|
+
wasm: false, // КРИТИЧНО: отключаем WASM, используем бинарный файл
|
|
233
|
+
mostlikely: topK,
|
|
234
|
+
verbosity: 0,
|
|
235
|
+
normalize: false
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
// Загружаем модель
|
|
239
|
+
await fastTextInstance.load();
|
|
240
|
+
model = fastTextInstance;
|
|
241
|
+
FastTextLanguageDetector.modelCache.set(modelPath, fastTextInstance);
|
|
223
242
|
}
|
|
224
243
|
}
|
|
225
244
|
catch (error) {
|
|
@@ -265,19 +284,48 @@ class FastTextLanguageDetector {
|
|
|
265
284
|
throw new n8n_workflow_1.ApplicationError('Model not initialized', { level: 'error' });
|
|
266
285
|
}
|
|
267
286
|
// Определяем язык
|
|
268
|
-
|
|
287
|
+
// fasttext.js возвращает массив массивов [label, probability] или массив объектов
|
|
288
|
+
const predictionsRaw = await model.predict(processedText);
|
|
289
|
+
if (!Array.isArray(predictionsRaw) || predictionsRaw.length === 0) {
|
|
290
|
+
throw new n8n_workflow_1.ApplicationError('No predictions returned from fastText model', { level: 'error' });
|
|
291
|
+
}
|
|
292
|
+
// Преобразуем формат ответа fasttext.js в наш формат
|
|
293
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
294
|
+
const predictions = predictionsRaw.map((pred) => {
|
|
295
|
+
// fasttext.js возвращает либо [label, value], либо объект
|
|
296
|
+
let label;
|
|
297
|
+
let probability;
|
|
298
|
+
if (Array.isArray(pred)) {
|
|
299
|
+
label = String(pred[0] || '');
|
|
300
|
+
probability = Number(pred[1] || 0);
|
|
301
|
+
}
|
|
302
|
+
else if (pred && typeof pred === 'object') {
|
|
303
|
+
label = String(pred.label || pred[0] || '');
|
|
304
|
+
probability = Number(pred.value || pred.probability || pred[1] || 0);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
label = String(pred || '');
|
|
308
|
+
probability = 0;
|
|
309
|
+
}
|
|
310
|
+
// Убираем префикс __label__ если есть
|
|
311
|
+
const cleanLabel = label.replace(/^__label__/, '');
|
|
312
|
+
return {
|
|
313
|
+
label: cleanLabel,
|
|
314
|
+
probability: probability
|
|
315
|
+
};
|
|
316
|
+
});
|
|
269
317
|
// Фильтруем по порогу уверенности
|
|
270
318
|
const filteredPredictions = predictions.filter((pred) => pred.probability >= confidenceThreshold);
|
|
271
319
|
if (filteredPredictions.length === 0) {
|
|
272
320
|
// Если нет предсказаний выше порога, возвращаем лучшее
|
|
273
321
|
const result = {
|
|
274
322
|
...json,
|
|
275
|
-
[outputField]: predictions[0]?.label
|
|
323
|
+
[outputField]: predictions[0]?.label || 'unknown',
|
|
276
324
|
};
|
|
277
325
|
if (includeConfidence) {
|
|
278
326
|
result[`${outputField}Confidence`] = predictions[0]?.probability || 0;
|
|
279
327
|
result[`${outputField}All`] = predictions.map((p) => ({
|
|
280
|
-
language: p.label
|
|
328
|
+
language: p.label || 'unknown',
|
|
281
329
|
confidence: p.probability || 0,
|
|
282
330
|
}));
|
|
283
331
|
}
|
|
@@ -288,7 +336,7 @@ class FastTextLanguageDetector {
|
|
|
288
336
|
const topPrediction = filteredPredictions[0];
|
|
289
337
|
const result = {
|
|
290
338
|
...json,
|
|
291
|
-
[outputField]: topPrediction.label
|
|
339
|
+
[outputField]: topPrediction.label || 'unknown',
|
|
292
340
|
};
|
|
293
341
|
if (includeConfidence) {
|
|
294
342
|
result[`${outputField}Confidence`] = topPrediction.probability || 0;
|