@workglow/tf-mediapipe 0.2.34 → 0.2.36
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/ai/TensorFlowMediaPipeProvider.d.ts +8 -10
- package/dist/ai/TensorFlowMediaPipeProvider.d.ts.map +1 -1
- package/dist/ai/TensorFlowMediaPipeQueuedProvider.d.ts +6 -3
- package/dist/ai/TensorFlowMediaPipeQueuedProvider.d.ts.map +1 -1
- package/dist/ai/common/TFMP_Capabilities.d.ts +24 -0
- package/dist/ai/common/TFMP_Capabilities.d.ts.map +1 -0
- package/dist/ai/common/TFMP_CapabilitySets.d.ts +23 -0
- package/dist/ai/common/TFMP_CapabilitySets.d.ts.map +1 -0
- package/dist/ai/common/TFMP_Constants.d.ts +1 -1
- package/dist/ai/common/TFMP_Constants.d.ts.map +1 -1
- package/dist/ai/common/TFMP_Download.d.ts +14 -2
- package/dist/ai/common/TFMP_Download.d.ts.map +1 -1
- package/dist/ai/common/TFMP_FaceDetector.d.ts.map +1 -1
- package/dist/ai/common/TFMP_FaceLandmarker.d.ts.map +1 -1
- package/dist/ai/common/TFMP_GestureRecognizer.d.ts.map +1 -1
- package/dist/ai/common/TFMP_HandLandmarker.d.ts.map +1 -1
- package/dist/ai/common/TFMP_ImageClassification.d.ts.map +1 -1
- package/dist/ai/common/TFMP_ImageEmbedding.d.ts.map +1 -1
- package/dist/ai/common/TFMP_ImageSegmentation.d.ts.map +1 -1
- package/dist/ai/common/TFMP_JobRunFns.d.ts +9 -2
- package/dist/ai/common/TFMP_JobRunFns.d.ts.map +1 -1
- package/dist/ai/common/TFMP_ModelInfo.d.ts.map +1 -1
- package/dist/ai/common/TFMP_ModelSchema.d.ts +3 -3
- package/dist/ai/common/TFMP_ObjectDetection.d.ts.map +1 -1
- package/dist/ai/common/TFMP_PoseLandmarker.d.ts.map +1 -1
- package/dist/ai/common/TFMP_Runtime.d.ts +2 -1
- package/dist/ai/common/TFMP_Runtime.d.ts.map +1 -1
- package/dist/ai/common/TFMP_TextClassification.d.ts.map +1 -1
- package/dist/ai/common/TFMP_TextEmbedding.d.ts.map +1 -1
- package/dist/ai/common/TFMP_TextLanguageDetection.d.ts.map +1 -1
- package/dist/ai/common/TFMP_Unload.d.ts +2 -2
- package/dist/ai/common/TFMP_Unload.d.ts.map +1 -1
- package/dist/ai/index.d.ts +25 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/runtime.d.ts.map +1 -1
- package/dist/ai-runtime.d.ts.map +1 -1
- package/dist/ai-runtime.js +482 -395
- package/dist/ai-runtime.js.map +28 -26
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +735 -22
- package/dist/ai.js.map +27 -6
- package/package.json +12 -13
package/dist/ai.js
CHANGED
|
@@ -9,8 +9,8 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
// src/ai/common/TFMP_Constants.ts
|
|
10
10
|
var TENSORFLOW_MEDIAPIPE = "TENSORFLOW_MEDIAPIPE";
|
|
11
11
|
var TFMP_DEFAULT_TASK_TYPES = [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
"ModelDownloadTask",
|
|
13
|
+
"ModelDownloadRemoveTask",
|
|
14
14
|
"ModelInfoTask",
|
|
15
15
|
"TextEmbeddingTask",
|
|
16
16
|
"TextLanguageDetectionTask",
|
|
@@ -46,7 +46,6 @@ var TextPipelineTask = {
|
|
|
46
46
|
"vision-object-detector": "vision-object-detector",
|
|
47
47
|
"vision-pose-landmarker": "vision-pose-landmarker"
|
|
48
48
|
};
|
|
49
|
-
|
|
50
49
|
// src/ai/common/TFMP_ModelSchema.ts
|
|
51
50
|
import { ModelConfigSchema, ModelRecordSchema } from "@workglow/ai/worker";
|
|
52
51
|
var TFMPModelSchema = {
|
|
@@ -100,7 +99,6 @@ var TFMPModelConfigSchema = {
|
|
|
100
99
|
required: [...ModelConfigSchema.required, ...TFMPModelSchema.required],
|
|
101
100
|
additionalProperties: false
|
|
102
101
|
};
|
|
103
|
-
|
|
104
102
|
// src/ai/common/TFMP_ModelSearch.ts
|
|
105
103
|
import { filterModelSearchResultsByQuery } from "@workglow/ai/provider-utils";
|
|
106
104
|
var TFMP_MODEL_RESULTS = [
|
|
@@ -112,7 +110,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
112
110
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
113
111
|
title: "Universal Sentence Encoder",
|
|
114
112
|
description: "Universal Sentence Encoder",
|
|
115
|
-
|
|
113
|
+
capabilities: ["text.embedding"],
|
|
116
114
|
provider_config: {
|
|
117
115
|
model_path: "https://storage.googleapis.com/mediapipe-tasks/text_embedder/universal_sentence_encoder.tflite",
|
|
118
116
|
task_engine: "text",
|
|
@@ -130,7 +128,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
130
128
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
131
129
|
title: "MediaPipe Language Detector",
|
|
132
130
|
description: "MediaPipe Language Detector",
|
|
133
|
-
|
|
131
|
+
capabilities: ["text.language-detection"],
|
|
134
132
|
provider_config: {
|
|
135
133
|
model_path: "https://storage.googleapis.com/mediapipe-models/language_detector/language_detector/float32/latest/language_detector.tflite",
|
|
136
134
|
task_engine: "text",
|
|
@@ -148,7 +146,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
148
146
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
149
147
|
title: "MediaPipe BERT Text Classifier",
|
|
150
148
|
description: "MediaPipe BERT Text Classifier",
|
|
151
|
-
|
|
149
|
+
capabilities: ["text.classification"],
|
|
152
150
|
provider_config: {
|
|
153
151
|
model_path: "https://storage.googleapis.com/mediapipe-tasks/text_classifier/bert_text_classifier.tflite",
|
|
154
152
|
task_engine: "text",
|
|
@@ -166,7 +164,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
166
164
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
167
165
|
title: "MediaPipe Image Embedder",
|
|
168
166
|
description: "MediaPipe Image Embedder",
|
|
169
|
-
|
|
167
|
+
capabilities: ["image.embedding"],
|
|
170
168
|
provider_config: {
|
|
171
169
|
model_path: "https://storage.googleapis.com/mediapipe-models/image_embedder/mobilenet_v3_small/float32/1/mobilenet_v3_small.tflite",
|
|
172
170
|
task_engine: "vision",
|
|
@@ -184,7 +182,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
184
182
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
185
183
|
title: "EfficientNet Lite0",
|
|
186
184
|
description: "Image classification model",
|
|
187
|
-
|
|
185
|
+
capabilities: ["image.classification"],
|
|
188
186
|
provider_config: {
|
|
189
187
|
model_path: "https://storage.googleapis.com/mediapipe-models/image_classifier/efficientnet_lite0/float32/1/efficientnet_lite0.tflite",
|
|
190
188
|
task_engine: "vision",
|
|
@@ -202,7 +200,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
202
200
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
203
201
|
title: "Efficient Object Detector Lite0",
|
|
204
202
|
description: "Object detection model",
|
|
205
|
-
|
|
203
|
+
capabilities: ["image.object-detection"],
|
|
206
204
|
provider_config: {
|
|
207
205
|
model_path: "https://storage.googleapis.com/mediapipe-models/object_detector/efficientdet_lite0/float32/1/efficientdet_lite0.tflite",
|
|
208
206
|
task_engine: "vision",
|
|
@@ -220,7 +218,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
220
218
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
221
219
|
title: "Efficient Image Segmenter Lite0",
|
|
222
220
|
description: "Image segmentation model",
|
|
223
|
-
|
|
221
|
+
capabilities: ["image.segmentation"],
|
|
224
222
|
provider_config: {
|
|
225
223
|
model_path: "https://storage.googleapis.com/mediapipe-assets/deeplabv3.tflite?generation=1661875711618421",
|
|
226
224
|
task_engine: "vision",
|
|
@@ -238,7 +236,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
238
236
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
239
237
|
title: "Face Landmarker",
|
|
240
238
|
description: "Detects 478 facial landmarks with blendshapes",
|
|
241
|
-
|
|
239
|
+
capabilities: ["vision.face-landmarks"],
|
|
242
240
|
provider_config: {
|
|
243
241
|
model_path: "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task",
|
|
244
242
|
task_engine: "vision",
|
|
@@ -256,7 +254,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
256
254
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
257
255
|
title: "Gesture Recognizer",
|
|
258
256
|
description: "Recognizes hand gestures such as thumbs up and victory",
|
|
259
|
-
|
|
257
|
+
capabilities: ["vision.gesture"],
|
|
260
258
|
provider_config: {
|
|
261
259
|
model_path: "https://storage.googleapis.com/mediapipe-models/gesture_recognizer/gesture_recognizer/float16/1/gesture_recognizer.task",
|
|
262
260
|
task_engine: "vision",
|
|
@@ -274,7 +272,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
274
272
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
275
273
|
title: "Hand Landmarker",
|
|
276
274
|
description: "Detects 21 hand landmarks",
|
|
277
|
-
|
|
275
|
+
capabilities: ["vision.hand-landmarks"],
|
|
278
276
|
provider_config: {
|
|
279
277
|
model_path: "https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",
|
|
280
278
|
task_engine: "vision",
|
|
@@ -292,7 +290,7 @@ var TFMP_MODEL_RESULTS = [
|
|
|
292
290
|
provider: TENSORFLOW_MEDIAPIPE,
|
|
293
291
|
title: "Pose Landmarker",
|
|
294
292
|
description: "Detects 33 body pose landmarks",
|
|
295
|
-
|
|
293
|
+
capabilities: ["vision.pose-landmarks"],
|
|
296
294
|
provider_config: {
|
|
297
295
|
model_path: "https://storage.googleapis.com/mediapipe-models/pose_landmarker/pose_landmarker_lite/float16/1/pose_landmarker_lite.task",
|
|
298
296
|
task_engine: "vision",
|
|
@@ -304,29 +302,131 @@ var TFMP_MODEL_RESULTS = [
|
|
|
304
302
|
}
|
|
305
303
|
];
|
|
306
304
|
function createTFMPModelSearch(providerId) {
|
|
307
|
-
return async (input) => {
|
|
305
|
+
return async (input, _model, _signal, emit) => {
|
|
308
306
|
const results = filterModelSearchResultsByQuery(TFMP_MODEL_RESULTS.map((result) => ({
|
|
309
307
|
...result,
|
|
310
308
|
record: { ...result.record, provider: providerId }
|
|
311
309
|
})), input.query);
|
|
312
|
-
|
|
310
|
+
emit({ type: "finish", data: { results } });
|
|
313
311
|
};
|
|
314
312
|
}
|
|
315
313
|
var TFMP_ModelSearch = createTFMPModelSearch(TENSORFLOW_MEDIAPIPE);
|
|
316
|
-
|
|
317
314
|
// src/ai/registerTensorFlowMediaPipe.ts
|
|
318
315
|
import { registerProviderWithWorker } from "@workglow/ai/provider-utils";
|
|
319
316
|
|
|
320
317
|
// src/ai/TensorFlowMediaPipeQueuedProvider.ts
|
|
321
318
|
import { AiProvider } from "@workglow/ai";
|
|
319
|
+
|
|
320
|
+
// src/ai/common/TFMP_CapabilitySets.ts
|
|
321
|
+
var TFMP_TEXT_EMBEDDING = ["text.embedding"];
|
|
322
|
+
var TFMP_TEXT_CLASSIFICATION = ["text.classification"];
|
|
323
|
+
var TFMP_TEXT_LANGUAGE_DETECTION = [
|
|
324
|
+
"text.language-detection"
|
|
325
|
+
];
|
|
326
|
+
var TFMP_IMAGE_CLASSIFICATION = ["image.classification"];
|
|
327
|
+
var TFMP_IMAGE_EMBEDDING = ["image.embedding"];
|
|
328
|
+
var TFMP_IMAGE_SEGMENTATION = ["image.segmentation"];
|
|
329
|
+
var TFMP_IMAGE_OBJECT_DETECTION = [
|
|
330
|
+
"image.object-detection"
|
|
331
|
+
];
|
|
332
|
+
var TFMP_VISION_FACE_DETECTION = ["vision.face-detection"];
|
|
333
|
+
var TFMP_VISION_FACE_LANDMARKS = ["vision.face-landmarks"];
|
|
334
|
+
var TFMP_VISION_HAND_LANDMARKS = ["vision.hand-landmarks"];
|
|
335
|
+
var TFMP_VISION_POSE_LANDMARKS = ["vision.pose-landmarks"];
|
|
336
|
+
var TFMP_VISION_GESTURE = ["vision.gesture"];
|
|
337
|
+
var TFMP_MODEL_DOWNLOAD = ["model.download"];
|
|
338
|
+
var TFMP_MODEL_UNLOAD = ["model.download-remove"];
|
|
339
|
+
var TFMP_MODEL_SEARCH = ["model.search"];
|
|
340
|
+
var TFMP_MODEL_INFO = ["model.info"];
|
|
341
|
+
var TFMP_CAPABILITY_SETS = [
|
|
342
|
+
TFMP_TEXT_EMBEDDING,
|
|
343
|
+
TFMP_TEXT_CLASSIFICATION,
|
|
344
|
+
TFMP_TEXT_LANGUAGE_DETECTION,
|
|
345
|
+
TFMP_IMAGE_CLASSIFICATION,
|
|
346
|
+
TFMP_IMAGE_EMBEDDING,
|
|
347
|
+
TFMP_IMAGE_SEGMENTATION,
|
|
348
|
+
TFMP_IMAGE_OBJECT_DETECTION,
|
|
349
|
+
TFMP_VISION_FACE_DETECTION,
|
|
350
|
+
TFMP_VISION_FACE_LANDMARKS,
|
|
351
|
+
TFMP_VISION_HAND_LANDMARKS,
|
|
352
|
+
TFMP_VISION_POSE_LANDMARKS,
|
|
353
|
+
TFMP_VISION_GESTURE,
|
|
354
|
+
TFMP_MODEL_DOWNLOAD,
|
|
355
|
+
TFMP_MODEL_UNLOAD,
|
|
356
|
+
TFMP_MODEL_SEARCH,
|
|
357
|
+
TFMP_MODEL_INFO
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
// src/ai/common/TFMP_Capabilities.ts
|
|
361
|
+
var TFMP_RUN_FN_SPECS = TFMP_CAPABILITY_SETS.map((serves) => ({ serves }));
|
|
362
|
+
function tfmpWorkerRunFnSpecs() {
|
|
363
|
+
return TFMP_RUN_FN_SPECS;
|
|
364
|
+
}
|
|
365
|
+
function inferTfmpCapabilities(model) {
|
|
366
|
+
const declared = model.capabilities ?? [];
|
|
367
|
+
if (declared.length > 0)
|
|
368
|
+
return declared;
|
|
369
|
+
const id = String(model.model_id ?? model.provider_config?.model_path ?? model.provider_config?.model_name ?? "");
|
|
370
|
+
const baseName = (id.split("/").pop() ?? id).toLowerCase();
|
|
371
|
+
if (/gesture_recognizer/.test(baseName)) {
|
|
372
|
+
return [
|
|
373
|
+
"vision.gesture",
|
|
374
|
+
"vision.hand-landmarks",
|
|
375
|
+
"model.download-remove",
|
|
376
|
+
"model.info",
|
|
377
|
+
"model.search"
|
|
378
|
+
];
|
|
379
|
+
}
|
|
380
|
+
if (/hand_landmarker/.test(baseName)) {
|
|
381
|
+
return ["vision.hand-landmarks", "model.download-remove", "model.info", "model.search"];
|
|
382
|
+
}
|
|
383
|
+
if (/face_landmarker/.test(baseName)) {
|
|
384
|
+
return ["vision.face-landmarks", "model.download-remove", "model.info", "model.search"];
|
|
385
|
+
}
|
|
386
|
+
if (/face_detector|blaze_face/.test(baseName)) {
|
|
387
|
+
return ["vision.face-detection", "model.download-remove", "model.info", "model.search"];
|
|
388
|
+
}
|
|
389
|
+
if (/pose_landmarker/.test(baseName)) {
|
|
390
|
+
return ["vision.pose-landmarks", "model.download-remove", "model.info", "model.search"];
|
|
391
|
+
}
|
|
392
|
+
if (/object_detector|efficientdet|ssd_mobilenet|yolo/.test(baseName)) {
|
|
393
|
+
return ["image.object-detection", "model.download-remove", "model.info", "model.search"];
|
|
394
|
+
}
|
|
395
|
+
if (/segmenter|deeplab|selfie/.test(baseName)) {
|
|
396
|
+
return ["image.segmentation", "model.download-remove", "model.info", "model.search"];
|
|
397
|
+
}
|
|
398
|
+
if (/image_classifier|efficientnet|mobilenet/.test(baseName)) {
|
|
399
|
+
return ["image.classification", "model.download-remove", "model.info", "model.search"];
|
|
400
|
+
}
|
|
401
|
+
if (/image_embed/.test(baseName)) {
|
|
402
|
+
return ["image.embedding", "model.download-remove", "model.info", "model.search"];
|
|
403
|
+
}
|
|
404
|
+
if (/text_embed|universal_sentence_encoder|use_/.test(baseName)) {
|
|
405
|
+
return ["text.embedding", "model.download-remove", "model.info", "model.search"];
|
|
406
|
+
}
|
|
407
|
+
if (/text_classifier|bert_classifier/.test(baseName)) {
|
|
408
|
+
return ["text.classification", "model.download-remove", "model.info", "model.search"];
|
|
409
|
+
}
|
|
410
|
+
if (/language_detector/.test(baseName)) {
|
|
411
|
+
return ["text.language-detection", "model.download-remove", "model.info", "model.search"];
|
|
412
|
+
}
|
|
413
|
+
return ["model.search", "model.info"];
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// src/ai/TensorFlowMediaPipeQueuedProvider.ts
|
|
322
417
|
class TensorFlowMediaPipeQueuedProvider extends AiProvider {
|
|
323
418
|
name = TENSORFLOW_MEDIAPIPE;
|
|
324
419
|
displayName = "TensorFlow MediaPipe";
|
|
325
420
|
isLocal = true;
|
|
326
421
|
supportsBrowser = true;
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
422
|
+
constructor(promiseRunFns, previewTasks) {
|
|
423
|
+
super(promiseRunFns, previewTasks);
|
|
424
|
+
}
|
|
425
|
+
inferCapabilities(model) {
|
|
426
|
+
return inferTfmpCapabilities(model);
|
|
427
|
+
}
|
|
428
|
+
workerRunFnSpecs() {
|
|
429
|
+
return tfmpWorkerRunFnSpecs();
|
|
330
430
|
}
|
|
331
431
|
}
|
|
332
432
|
|
|
@@ -334,9 +434,622 @@ class TensorFlowMediaPipeQueuedProvider extends AiProvider {
|
|
|
334
434
|
async function registerTensorFlowMediaPipe(options) {
|
|
335
435
|
await registerProviderWithWorker(new TensorFlowMediaPipeQueuedProvider, "TensorFlow MediaPipe", options);
|
|
336
436
|
}
|
|
437
|
+
// src/ai/common/TFMP_Download.ts
|
|
438
|
+
import { PermanentJobError as PermanentJobError2 } from "@workglow/job-queue";
|
|
439
|
+
|
|
440
|
+
// src/ai/common/TFMP_Client.ts
|
|
441
|
+
var _loadPromiseText;
|
|
442
|
+
var _loadPromiseVision;
|
|
443
|
+
async function loadTfmpTasksTextSDK() {
|
|
444
|
+
_loadPromiseText ??= import("@mediapipe/tasks-text").catch(() => {
|
|
445
|
+
_loadPromiseText = undefined;
|
|
446
|
+
throw new Error("@mediapipe/tasks-text is required for TensorFlow MediaPipe text (and related) tasks. Install with: bun add @mediapipe/tasks-text");
|
|
447
|
+
});
|
|
448
|
+
return _loadPromiseText;
|
|
449
|
+
}
|
|
450
|
+
async function loadTfmpTasksVisionSDK() {
|
|
451
|
+
_loadPromiseVision ??= import("@mediapipe/tasks-vision").catch(() => {
|
|
452
|
+
_loadPromiseVision = undefined;
|
|
453
|
+
throw new Error("@mediapipe/tasks-vision is required for TensorFlow MediaPipe vision tasks. Install with: bun add @mediapipe/tasks-vision");
|
|
454
|
+
});
|
|
455
|
+
return _loadPromiseVision;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/ai/common/TFMP_Runtime.ts
|
|
459
|
+
import { PermanentJobError } from "@workglow/job-queue";
|
|
460
|
+
var wasm_tasks = new Map;
|
|
461
|
+
var wasm_reference_counts = new Map;
|
|
462
|
+
var modelTaskCache = new Map;
|
|
463
|
+
var optionsMatch = (opts1, opts2) => {
|
|
464
|
+
const keys1 = Object.keys(opts1).sort();
|
|
465
|
+
const keys2 = Object.keys(opts2).sort();
|
|
466
|
+
if (keys1.length !== keys2.length)
|
|
467
|
+
return false;
|
|
468
|
+
return keys1.every((key) => {
|
|
469
|
+
const val1 = opts1[key];
|
|
470
|
+
const val2 = opts2[key];
|
|
471
|
+
if (Array.isArray(val1) && Array.isArray(val2)) {
|
|
472
|
+
return JSON.stringify(val1) === JSON.stringify(val2);
|
|
473
|
+
}
|
|
474
|
+
return val1 === val2;
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
var getWasmTask = async (model, emit, signal) => {
|
|
478
|
+
const task_engine = model.provider_config.task_engine;
|
|
479
|
+
if (wasm_tasks.has(task_engine)) {
|
|
480
|
+
return wasm_tasks.get(task_engine);
|
|
481
|
+
}
|
|
482
|
+
if (signal.aborted) {
|
|
483
|
+
throw new PermanentJobError("Aborted job");
|
|
484
|
+
}
|
|
485
|
+
emit({ type: "phase", message: "Loading WASM task", progress: 0.1 });
|
|
486
|
+
let wasmFileset;
|
|
487
|
+
switch (task_engine) {
|
|
488
|
+
case "vision": {
|
|
489
|
+
const { FilesetResolver } = await loadTfmpTasksVisionSDK();
|
|
490
|
+
wasmFileset = await FilesetResolver.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm");
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
case "text": {
|
|
494
|
+
const { FilesetResolver } = await loadTfmpTasksTextSDK();
|
|
495
|
+
wasmFileset = await FilesetResolver.forTextTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-text@latest/wasm");
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case "audio": {
|
|
499
|
+
const { FilesetResolver } = await loadTfmpTasksTextSDK();
|
|
500
|
+
wasmFileset = await FilesetResolver.forAudioTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-audio@latest/wasm");
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
case "genai": {
|
|
504
|
+
const { FilesetResolver } = await loadTfmpTasksTextSDK();
|
|
505
|
+
wasmFileset = await FilesetResolver.forGenAiTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-genai@latest/wasm");
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
default:
|
|
509
|
+
throw new PermanentJobError("Invalid task engine");
|
|
510
|
+
}
|
|
511
|
+
wasm_tasks.set(task_engine, wasmFileset);
|
|
512
|
+
return wasmFileset;
|
|
513
|
+
};
|
|
514
|
+
var getModelTask = async (model, options, emit, signal, TaskType) => {
|
|
515
|
+
const model_path = model.provider_config.model_path;
|
|
516
|
+
const task_engine = model.provider_config.task_engine;
|
|
517
|
+
const cachedTasks = modelTaskCache.get(model_path);
|
|
518
|
+
if (cachedTasks) {
|
|
519
|
+
const matchedTask = cachedTasks.find((cached) => optionsMatch(cached.options, options));
|
|
520
|
+
if (matchedTask) {
|
|
521
|
+
return matchedTask.task;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const wasmFileset = await getWasmTask(model, emit, signal);
|
|
525
|
+
emit({ type: "phase", message: "Creating model task", progress: 0.2 });
|
|
526
|
+
const task = await TaskType.createFromOptions(wasmFileset, {
|
|
527
|
+
baseOptions: {
|
|
528
|
+
modelAssetPath: model_path
|
|
529
|
+
},
|
|
530
|
+
...options
|
|
531
|
+
});
|
|
532
|
+
const cachedTask = { task, options, task_engine };
|
|
533
|
+
if (!modelTaskCache.has(model_path)) {
|
|
534
|
+
modelTaskCache.set(model_path, []);
|
|
535
|
+
}
|
|
536
|
+
modelTaskCache.get(model_path).push(cachedTask);
|
|
537
|
+
wasm_reference_counts.set(task_engine, (wasm_reference_counts.get(task_engine) || 0) + 1);
|
|
538
|
+
return task;
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// src/ai/common/TFMP_Download.ts
|
|
542
|
+
var TFMP_Download = async (input, model, signal, emit) => {
|
|
543
|
+
const pipeline = model?.provider_config.pipeline;
|
|
544
|
+
let task;
|
|
545
|
+
switch (pipeline) {
|
|
546
|
+
case "text-embedder": {
|
|
547
|
+
const { TextEmbedder } = await loadTfmpTasksTextSDK();
|
|
548
|
+
task = await getModelTask(model, {}, emit, signal, TextEmbedder);
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
case "text-classifier": {
|
|
552
|
+
const { TextClassifier } = await loadTfmpTasksTextSDK();
|
|
553
|
+
task = await getModelTask(model, {}, emit, signal, TextClassifier);
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
case "text-language-detector": {
|
|
557
|
+
const { LanguageDetector } = await loadTfmpTasksTextSDK();
|
|
558
|
+
task = await getModelTask(model, {}, emit, signal, LanguageDetector);
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
case "vision-image-classifier": {
|
|
562
|
+
const { ImageClassifier } = await loadTfmpTasksVisionSDK();
|
|
563
|
+
task = await getModelTask(model, {}, emit, signal, ImageClassifier);
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
case "vision-image-embedder": {
|
|
567
|
+
const { ImageEmbedder } = await loadTfmpTasksVisionSDK();
|
|
568
|
+
task = await getModelTask(model, {}, emit, signal, ImageEmbedder);
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
case "vision-image-segmenter": {
|
|
572
|
+
const { ImageSegmenter } = await loadTfmpTasksVisionSDK();
|
|
573
|
+
task = await getModelTask(model, {}, emit, signal, ImageSegmenter);
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
576
|
+
case "vision-object-detector": {
|
|
577
|
+
const { ObjectDetector } = await loadTfmpTasksVisionSDK();
|
|
578
|
+
task = await getModelTask(model, {}, emit, signal, ObjectDetector);
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
case "vision-face-detector": {
|
|
582
|
+
const { FaceDetector } = await loadTfmpTasksVisionSDK();
|
|
583
|
+
task = await getModelTask(model, {}, emit, signal, FaceDetector);
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
case "vision-face-landmarker": {
|
|
587
|
+
const { FaceLandmarker } = await loadTfmpTasksVisionSDK();
|
|
588
|
+
task = await getModelTask(model, {}, emit, signal, FaceLandmarker);
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
case "vision-gesture-recognizer": {
|
|
592
|
+
const { GestureRecognizer } = await loadTfmpTasksVisionSDK();
|
|
593
|
+
task = await getModelTask(model, {}, emit, signal, GestureRecognizer);
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
case "vision-hand-landmarker": {
|
|
597
|
+
const { HandLandmarker } = await loadTfmpTasksVisionSDK();
|
|
598
|
+
task = await getModelTask(model, {}, emit, signal, HandLandmarker);
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
case "vision-pose-landmarker": {
|
|
602
|
+
const { PoseLandmarker } = await loadTfmpTasksVisionSDK();
|
|
603
|
+
task = await getModelTask(model, {}, emit, signal, PoseLandmarker);
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
default:
|
|
607
|
+
throw new PermanentJobError2(`Invalid pipeline: ${pipeline}. Supported pipelines: text-embedder, text-classifier, text-language-detector, vision-image-classifier, vision-image-embedder, vision-image-segmenter, vision-object-detector, vision-face-detector, vision-face-landmarker, vision-gesture-recognizer, vision-hand-landmarker, vision-pose-landmarker`);
|
|
608
|
+
}
|
|
609
|
+
emit({ type: "phase", message: "Pipeline loaded", progress: 0.9 });
|
|
610
|
+
task.close();
|
|
611
|
+
const task_engine = model.provider_config.task_engine;
|
|
612
|
+
wasm_reference_counts.set(task_engine, wasm_reference_counts.get(task_engine) - 1);
|
|
613
|
+
emit({ type: "finish", data: { model: input.model } });
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// src/ai/common/TFMP_FaceDetector.ts
|
|
617
|
+
import { PermanentJobError as PermanentJobError3 } from "@workglow/job-queue";
|
|
618
|
+
var TFMP_FaceDetector = async (input, model, signal, emit) => {
|
|
619
|
+
const { FaceDetector } = await loadTfmpTasksVisionSDK();
|
|
620
|
+
const faceDetector = await getModelTask(model, {
|
|
621
|
+
minDetectionConfidence: input.minDetectionConfidence,
|
|
622
|
+
minSuppressionThreshold: input.minSuppressionThreshold
|
|
623
|
+
}, emit, signal, FaceDetector);
|
|
624
|
+
const result = faceDetector.detect(input.image);
|
|
625
|
+
if (!result.detections) {
|
|
626
|
+
throw new PermanentJobError3("Failed to detect faces: Empty result");
|
|
627
|
+
}
|
|
628
|
+
const faces = result.detections.map((detection) => ({
|
|
629
|
+
box: {
|
|
630
|
+
x: detection.boundingBox?.originX || 0,
|
|
631
|
+
y: detection.boundingBox?.originY || 0,
|
|
632
|
+
width: detection.boundingBox?.width || 0,
|
|
633
|
+
height: detection.boundingBox?.height || 0
|
|
634
|
+
},
|
|
635
|
+
keypoints: detection.keypoints?.map((kp) => ({
|
|
636
|
+
x: kp.x,
|
|
637
|
+
y: kp.y,
|
|
638
|
+
label: kp.label
|
|
639
|
+
})) || [],
|
|
640
|
+
score: detection.categories?.[0]?.score || 0
|
|
641
|
+
}));
|
|
642
|
+
emit({ type: "finish", data: { faces } });
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// src/ai/common/TFMP_FaceLandmarker.ts
|
|
646
|
+
import { PermanentJobError as PermanentJobError4 } from "@workglow/job-queue";
|
|
647
|
+
var TFMP_FaceLandmarker = async (input, model, signal, emit) => {
|
|
648
|
+
const { FaceLandmarker } = await loadTfmpTasksVisionSDK();
|
|
649
|
+
const faceLandmarker = await getModelTask(model, {
|
|
650
|
+
numFaces: input.numFaces,
|
|
651
|
+
minFaceDetectionConfidence: input.minFaceDetectionConfidence,
|
|
652
|
+
minFacePresenceConfidence: input.minFacePresenceConfidence,
|
|
653
|
+
minTrackingConfidence: input.minTrackingConfidence,
|
|
654
|
+
outputFaceBlendshapes: input.outputFaceBlendshapes,
|
|
655
|
+
outputFacialTransformationMatrixes: input.outputFacialTransformationMatrixes
|
|
656
|
+
}, emit, signal, FaceLandmarker);
|
|
657
|
+
const result = faceLandmarker.detect(input.image);
|
|
658
|
+
if (!result.faceLandmarks) {
|
|
659
|
+
throw new PermanentJobError4("Failed to detect face landmarks: Empty result");
|
|
660
|
+
}
|
|
661
|
+
const faces = result.faceLandmarks.map((landmarks, index) => {
|
|
662
|
+
const face = {
|
|
663
|
+
landmarks: landmarks.map((l) => ({
|
|
664
|
+
x: l.x,
|
|
665
|
+
y: l.y,
|
|
666
|
+
z: l.z
|
|
667
|
+
}))
|
|
668
|
+
};
|
|
669
|
+
if (result.faceBlendshapes && result.faceBlendshapes[index]) {
|
|
670
|
+
face.blendshapes = result.faceBlendshapes[index].categories.map((b) => ({
|
|
671
|
+
label: b.categoryName,
|
|
672
|
+
score: b.score
|
|
673
|
+
}));
|
|
674
|
+
}
|
|
675
|
+
if (result.facialTransformationMatrixes && result.facialTransformationMatrixes[index]) {
|
|
676
|
+
face.transformationMatrix = Array.from(result.facialTransformationMatrixes[index].data);
|
|
677
|
+
}
|
|
678
|
+
return face;
|
|
679
|
+
});
|
|
680
|
+
emit({ type: "finish", data: { faces } });
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// src/ai/common/TFMP_GestureRecognizer.ts
|
|
684
|
+
import { PermanentJobError as PermanentJobError5 } from "@workglow/job-queue";
|
|
685
|
+
var TFMP_GestureRecognizer = async (input, model, signal, emit) => {
|
|
686
|
+
const { GestureRecognizer } = await loadTfmpTasksVisionSDK();
|
|
687
|
+
const gestureRecognizer = await getModelTask(model, {
|
|
688
|
+
numHands: input.numHands,
|
|
689
|
+
minHandDetectionConfidence: input.minHandDetectionConfidence,
|
|
690
|
+
minHandPresenceConfidence: input.minHandPresenceConfidence,
|
|
691
|
+
minTrackingConfidence: input.minTrackingConfidence
|
|
692
|
+
}, emit, signal, GestureRecognizer);
|
|
693
|
+
const result = gestureRecognizer.recognize(input.image);
|
|
694
|
+
if (!result.gestures || !result.landmarks) {
|
|
695
|
+
throw new PermanentJobError5("Failed to recognize gestures: Empty result");
|
|
696
|
+
}
|
|
697
|
+
const hands = result.gestures.map((gestures, index) => ({
|
|
698
|
+
gestures: gestures.map((g) => ({
|
|
699
|
+
label: g.categoryName,
|
|
700
|
+
score: g.score
|
|
701
|
+
})),
|
|
702
|
+
handedness: result.handedness[index].map((h) => ({
|
|
703
|
+
label: h.categoryName,
|
|
704
|
+
score: h.score
|
|
705
|
+
})),
|
|
706
|
+
landmarks: result.landmarks[index].map((l) => ({
|
|
707
|
+
x: l.x,
|
|
708
|
+
y: l.y,
|
|
709
|
+
z: l.z
|
|
710
|
+
})),
|
|
711
|
+
worldLandmarks: result.worldLandmarks[index].map((l) => ({
|
|
712
|
+
x: l.x,
|
|
713
|
+
y: l.y,
|
|
714
|
+
z: l.z
|
|
715
|
+
}))
|
|
716
|
+
}));
|
|
717
|
+
emit({ type: "finish", data: { hands } });
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// src/ai/common/TFMP_HandLandmarker.ts
|
|
721
|
+
import { PermanentJobError as PermanentJobError6 } from "@workglow/job-queue";
|
|
722
|
+
var TFMP_HandLandmarker = async (input, model, signal, emit) => {
|
|
723
|
+
const { HandLandmarker } = await loadTfmpTasksVisionSDK();
|
|
724
|
+
const handLandmarker = await getModelTask(model, {
|
|
725
|
+
numHands: input.numHands,
|
|
726
|
+
minHandDetectionConfidence: input.minHandDetectionConfidence,
|
|
727
|
+
minHandPresenceConfidence: input.minHandPresenceConfidence,
|
|
728
|
+
minTrackingConfidence: input.minTrackingConfidence
|
|
729
|
+
}, emit, signal, HandLandmarker);
|
|
730
|
+
const result = handLandmarker.detect(input.image);
|
|
731
|
+
if (!result.landmarks) {
|
|
732
|
+
throw new PermanentJobError6("Failed to detect hand landmarks: Empty result");
|
|
733
|
+
}
|
|
734
|
+
const hands = result.landmarks.map((landmarks, index) => ({
|
|
735
|
+
handedness: result.handedness[index].map((h) => ({
|
|
736
|
+
label: h.categoryName,
|
|
737
|
+
score: h.score
|
|
738
|
+
})),
|
|
739
|
+
landmarks: landmarks.map((l) => ({
|
|
740
|
+
x: l.x,
|
|
741
|
+
y: l.y,
|
|
742
|
+
z: l.z
|
|
743
|
+
})),
|
|
744
|
+
worldLandmarks: result.worldLandmarks[index].map((l) => ({
|
|
745
|
+
x: l.x,
|
|
746
|
+
y: l.y,
|
|
747
|
+
z: l.z
|
|
748
|
+
}))
|
|
749
|
+
}));
|
|
750
|
+
emit({ type: "finish", data: { hands } });
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// src/ai/common/TFMP_ImageClassification.ts
|
|
754
|
+
import { PermanentJobError as PermanentJobError7 } from "@workglow/job-queue";
|
|
755
|
+
var TFMP_ImageClassification = async (input, model, signal, emit) => {
|
|
756
|
+
const { ImageClassifier } = await loadTfmpTasksVisionSDK();
|
|
757
|
+
const imageClassifier = await getModelTask(model, {
|
|
758
|
+
maxResults: input.maxCategories
|
|
759
|
+
}, emit, signal, ImageClassifier);
|
|
760
|
+
const result = imageClassifier.classify(input.image);
|
|
761
|
+
if (!result.classifications?.[0]?.categories) {
|
|
762
|
+
throw new PermanentJobError7("Failed to classify image: Empty result");
|
|
763
|
+
}
|
|
764
|
+
const categories = result.classifications[0].categories.map((category) => ({
|
|
765
|
+
label: category.categoryName,
|
|
766
|
+
score: category.score
|
|
767
|
+
}));
|
|
768
|
+
emit({ type: "finish", data: { categories } });
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
// src/ai/common/TFMP_ImageEmbedding.ts
|
|
772
|
+
import { PermanentJobError as PermanentJobError8 } from "@workglow/job-queue";
|
|
773
|
+
var TFMP_ImageEmbedding = async (input, model, signal, emit) => {
|
|
774
|
+
const { ImageEmbedder } = await loadTfmpTasksVisionSDK();
|
|
775
|
+
const imageEmbedder = await getModelTask(model, {}, emit, signal, ImageEmbedder);
|
|
776
|
+
if (Array.isArray(input.image)) {
|
|
777
|
+
const vectors = [];
|
|
778
|
+
for (const image of input.image) {
|
|
779
|
+
const result2 = imageEmbedder.embed(image);
|
|
780
|
+
if (!result2.embeddings?.[0]?.floatEmbedding) {
|
|
781
|
+
throw new PermanentJobError8("Failed to generate embedding: Empty result");
|
|
782
|
+
}
|
|
783
|
+
vectors.push(Float32Array.from(result2.embeddings[0].floatEmbedding));
|
|
784
|
+
}
|
|
785
|
+
emit({ type: "finish", data: { vector: vectors } });
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const result = imageEmbedder.embed(input.image);
|
|
789
|
+
if (!result.embeddings?.[0]?.floatEmbedding) {
|
|
790
|
+
throw new PermanentJobError8("Failed to generate embedding: Empty result");
|
|
791
|
+
}
|
|
792
|
+
const embedding = Float32Array.from(result.embeddings[0].floatEmbedding);
|
|
793
|
+
emit({ type: "finish", data: { vector: embedding } });
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
// src/ai/common/TFMP_ImageSegmentation.ts
|
|
797
|
+
import { PermanentJobError as PermanentJobError9 } from "@workglow/job-queue";
|
|
798
|
+
var TFMP_ImageSegmentation = async (input, model, signal, emit) => {
|
|
799
|
+
const { ImageSegmenter } = await loadTfmpTasksVisionSDK();
|
|
800
|
+
const imageSegmenter = await getModelTask(model, {}, emit, signal, ImageSegmenter);
|
|
801
|
+
const result = imageSegmenter.segment(input.image);
|
|
802
|
+
if (!result.categoryMask) {
|
|
803
|
+
throw new PermanentJobError9("Failed to segment image: Empty result");
|
|
804
|
+
}
|
|
805
|
+
const masks = [
|
|
806
|
+
{
|
|
807
|
+
label: "segment",
|
|
808
|
+
score: 1,
|
|
809
|
+
mask: {
|
|
810
|
+
data: result.categoryMask.canvas,
|
|
811
|
+
width: result.categoryMask.width,
|
|
812
|
+
height: result.categoryMask.height
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
];
|
|
816
|
+
emit({ type: "finish", data: { masks } });
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
// src/ai/common/TFMP_ModelInfo.ts
|
|
820
|
+
var TFMP_EMBEDDING_DIMENSIONS = {
|
|
821
|
+
"universal-sentence-encoder": { native_dimensions: 512, mrl: false }
|
|
822
|
+
};
|
|
823
|
+
var TFMP_ModelInfo = async (input, model, _signal, emit) => {
|
|
824
|
+
if (input.detail === "dimensions") {
|
|
825
|
+
const pc = model?.provider_config;
|
|
826
|
+
let native_dimensions = typeof pc?.native_dimensions === "number" ? pc.native_dimensions : undefined;
|
|
827
|
+
const mrl = typeof pc?.mrl === "boolean" ? pc.mrl : false;
|
|
828
|
+
if (native_dimensions === undefined) {
|
|
829
|
+
const modelPath = pc?.model_path ?? "";
|
|
830
|
+
const known = TFMP_EMBEDDING_DIMENSIONS[modelPath];
|
|
831
|
+
if (known) {
|
|
832
|
+
native_dimensions = known.native_dimensions;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
emit({
|
|
836
|
+
type: "finish",
|
|
837
|
+
data: {
|
|
838
|
+
model: input.model,
|
|
839
|
+
is_local: true,
|
|
840
|
+
is_remote: false,
|
|
841
|
+
supports_browser: true,
|
|
842
|
+
supports_node: false,
|
|
843
|
+
is_cached: false,
|
|
844
|
+
is_loaded: false,
|
|
845
|
+
file_sizes: null,
|
|
846
|
+
...native_dimensions !== undefined ? { native_dimensions } : {},
|
|
847
|
+
...mrl ? { mrl } : {}
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const model_path = model.provider_config.model_path;
|
|
853
|
+
const is_loaded = modelTaskCache.has(model_path);
|
|
854
|
+
emit({
|
|
855
|
+
type: "finish",
|
|
856
|
+
data: {
|
|
857
|
+
model: input.model,
|
|
858
|
+
is_local: true,
|
|
859
|
+
is_remote: false,
|
|
860
|
+
supports_browser: true,
|
|
861
|
+
supports_node: false,
|
|
862
|
+
is_cached: is_loaded,
|
|
863
|
+
is_loaded,
|
|
864
|
+
file_sizes: null
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
// src/ai/common/TFMP_ObjectDetection.ts
|
|
870
|
+
import { PermanentJobError as PermanentJobError10 } from "@workglow/job-queue";
|
|
871
|
+
var TFMP_ObjectDetection = async (input, model, signal, emit) => {
|
|
872
|
+
const { ObjectDetector } = await loadTfmpTasksVisionSDK();
|
|
873
|
+
const objectDetector = await getModelTask(model, {
|
|
874
|
+
scoreThreshold: input.threshold
|
|
875
|
+
}, emit, signal, ObjectDetector);
|
|
876
|
+
const result = objectDetector.detect(input.image);
|
|
877
|
+
if (!result.detections) {
|
|
878
|
+
throw new PermanentJobError10("Failed to detect objects: Empty result");
|
|
879
|
+
}
|
|
880
|
+
const detections = result.detections.map((detection) => ({
|
|
881
|
+
label: detection.categories?.[0]?.categoryName || "unknown",
|
|
882
|
+
score: detection.categories?.[0]?.score || 0,
|
|
883
|
+
box: {
|
|
884
|
+
x: detection.boundingBox?.originX || 0,
|
|
885
|
+
y: detection.boundingBox?.originY || 0,
|
|
886
|
+
width: detection.boundingBox?.width || 0,
|
|
887
|
+
height: detection.boundingBox?.height || 0
|
|
888
|
+
}
|
|
889
|
+
}));
|
|
890
|
+
emit({ type: "finish", data: { detections } });
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
// src/ai/common/TFMP_PoseLandmarker.ts
|
|
894
|
+
import { PermanentJobError as PermanentJobError11 } from "@workglow/job-queue";
|
|
895
|
+
var TFMP_PoseLandmarker = async (input, model, signal, emit) => {
|
|
896
|
+
const { PoseLandmarker } = await loadTfmpTasksVisionSDK();
|
|
897
|
+
const poseLandmarker = await getModelTask(model, {
|
|
898
|
+
numPoses: input.numPoses,
|
|
899
|
+
minPoseDetectionConfidence: input.minPoseDetectionConfidence,
|
|
900
|
+
minPosePresenceConfidence: input.minPosePresenceConfidence,
|
|
901
|
+
minTrackingConfidence: input.minTrackingConfidence,
|
|
902
|
+
outputSegmentationMasks: input.outputSegmentationMasks
|
|
903
|
+
}, emit, signal, PoseLandmarker);
|
|
904
|
+
const result = poseLandmarker.detect(input.image);
|
|
905
|
+
if (!result.landmarks) {
|
|
906
|
+
throw new PermanentJobError11("Failed to detect pose landmarks: Empty result");
|
|
907
|
+
}
|
|
908
|
+
const poses = result.landmarks.map((landmarks, index) => {
|
|
909
|
+
const pose = {
|
|
910
|
+
landmarks: landmarks.map((l) => ({
|
|
911
|
+
x: l.x,
|
|
912
|
+
y: l.y,
|
|
913
|
+
z: l.z,
|
|
914
|
+
visibility: l.visibility,
|
|
915
|
+
presence: l.presence
|
|
916
|
+
})),
|
|
917
|
+
worldLandmarks: result.worldLandmarks[index].map((l) => ({
|
|
918
|
+
x: l.x,
|
|
919
|
+
y: l.y,
|
|
920
|
+
z: l.z,
|
|
921
|
+
visibility: l.visibility,
|
|
922
|
+
presence: l.presence
|
|
923
|
+
}))
|
|
924
|
+
};
|
|
925
|
+
if (result.segmentationMasks && result.segmentationMasks[index]) {
|
|
926
|
+
const mask = result.segmentationMasks[index];
|
|
927
|
+
pose.segmentationMask = {
|
|
928
|
+
data: mask.canvas || mask,
|
|
929
|
+
width: mask.width,
|
|
930
|
+
height: mask.height
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
return pose;
|
|
934
|
+
});
|
|
935
|
+
emit({ type: "finish", data: { poses } });
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
// src/ai/common/TFMP_TextClassification.ts
|
|
939
|
+
import { PermanentJobError as PermanentJobError12 } from "@workglow/job-queue";
|
|
940
|
+
var TFMP_TextClassification = async (input, model, signal, emit) => {
|
|
941
|
+
const { TextClassifier } = await loadTfmpTasksTextSDK();
|
|
942
|
+
const TextClassification = await getModelTask(model, {
|
|
943
|
+
maxCategories: input.maxCategories
|
|
944
|
+
}, emit, signal, TextClassifier);
|
|
945
|
+
const result = TextClassification.classify(input.text);
|
|
946
|
+
if (!result.classifications?.[0]?.categories) {
|
|
947
|
+
throw new PermanentJobError12("Failed to classify text: Empty result");
|
|
948
|
+
}
|
|
949
|
+
const categories = result.classifications[0].categories.map((category) => ({
|
|
950
|
+
label: category.categoryName,
|
|
951
|
+
score: category.score
|
|
952
|
+
}));
|
|
953
|
+
emit({ type: "finish", data: { categories } });
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
// src/ai/common/TFMP_TextEmbedding.ts
|
|
957
|
+
import { PermanentJobError as PermanentJobError13 } from "@workglow/job-queue";
|
|
958
|
+
var TFMP_TextEmbedding = async (input, model, signal, emit) => {
|
|
959
|
+
const { TextEmbedder } = await loadTfmpTasksTextSDK();
|
|
960
|
+
const textEmbedder = await getModelTask(model, {}, emit, signal, TextEmbedder);
|
|
961
|
+
if (Array.isArray(input.text)) {
|
|
962
|
+
const embeddings = input.text.map((text) => {
|
|
963
|
+
const result2 = textEmbedder.embed(text);
|
|
964
|
+
if (!result2.embeddings?.[0]?.floatEmbedding) {
|
|
965
|
+
throw new PermanentJobError13("Failed to generate embedding: Empty result");
|
|
966
|
+
}
|
|
967
|
+
return Float32Array.from(result2.embeddings[0].floatEmbedding);
|
|
968
|
+
});
|
|
969
|
+
emit({ type: "finish", data: { vector: embeddings } });
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const result = textEmbedder.embed(input.text);
|
|
973
|
+
if (!result.embeddings?.[0]?.floatEmbedding) {
|
|
974
|
+
throw new PermanentJobError13("Failed to generate embedding: Empty result");
|
|
975
|
+
}
|
|
976
|
+
const embedding = Float32Array.from(result.embeddings[0].floatEmbedding);
|
|
977
|
+
emit({ type: "finish", data: { vector: embedding } });
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
// src/ai/common/TFMP_TextLanguageDetection.ts
|
|
981
|
+
import { PermanentJobError as PermanentJobError14 } from "@workglow/job-queue";
|
|
982
|
+
var TFMP_TextLanguageDetection = async (input, model, signal, emit) => {
|
|
983
|
+
const maxLanguages = input.maxLanguages === 0 ? -1 : input.maxLanguages;
|
|
984
|
+
const { LanguageDetector } = await loadTfmpTasksTextSDK();
|
|
985
|
+
const textLanguageDetector = await getModelTask(model, {
|
|
986
|
+
maxLanguages
|
|
987
|
+
}, emit, signal, LanguageDetector);
|
|
988
|
+
const result = textLanguageDetector.detect(input.text);
|
|
989
|
+
if (!result.languages?.[0]?.languageCode) {
|
|
990
|
+
throw new PermanentJobError14("Failed to detect language: Empty result");
|
|
991
|
+
}
|
|
992
|
+
const languages = result.languages.map((language) => ({
|
|
993
|
+
language: language.languageCode,
|
|
994
|
+
score: language.probability
|
|
995
|
+
}));
|
|
996
|
+
emit({ type: "finish", data: { languages } });
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
// src/ai/common/TFMP_Unload.ts
|
|
1000
|
+
var TFMP_Unload = async (input, model, _signal, emit) => {
|
|
1001
|
+
const model_path = model.provider_config.model_path;
|
|
1002
|
+
if (modelTaskCache.has(model_path)) {
|
|
1003
|
+
const cachedTasks = modelTaskCache.get(model_path);
|
|
1004
|
+
for (const cachedTask of cachedTasks) {
|
|
1005
|
+
const task = cachedTask.task;
|
|
1006
|
+
if ("close" in task && typeof task.close === "function")
|
|
1007
|
+
task.close();
|
|
1008
|
+
const task_engine = cachedTask.task_engine;
|
|
1009
|
+
const currentCount = wasm_reference_counts.get(task_engine) || 0;
|
|
1010
|
+
const newCount = currentCount - 1;
|
|
1011
|
+
if (newCount <= 0) {
|
|
1012
|
+
wasm_tasks.delete(task_engine);
|
|
1013
|
+
wasm_reference_counts.delete(task_engine);
|
|
1014
|
+
} else {
|
|
1015
|
+
wasm_reference_counts.set(task_engine, newCount);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
modelTaskCache.delete(model_path);
|
|
1019
|
+
}
|
|
1020
|
+
emit({ type: "finish", data: { model: input.model } });
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
// src/ai/common/TFMP_JobRunFns.ts
|
|
1024
|
+
var TFMP_RUN_FNS = [
|
|
1025
|
+
{ serves: TFMP_TEXT_EMBEDDING, runFn: TFMP_TextEmbedding },
|
|
1026
|
+
{ serves: TFMP_TEXT_CLASSIFICATION, runFn: TFMP_TextClassification },
|
|
1027
|
+
{ serves: TFMP_TEXT_LANGUAGE_DETECTION, runFn: TFMP_TextLanguageDetection },
|
|
1028
|
+
{ serves: TFMP_IMAGE_CLASSIFICATION, runFn: TFMP_ImageClassification },
|
|
1029
|
+
{ serves: TFMP_IMAGE_EMBEDDING, runFn: TFMP_ImageEmbedding },
|
|
1030
|
+
{ serves: TFMP_IMAGE_SEGMENTATION, runFn: TFMP_ImageSegmentation },
|
|
1031
|
+
{ serves: TFMP_IMAGE_OBJECT_DETECTION, runFn: TFMP_ObjectDetection },
|
|
1032
|
+
{ serves: TFMP_VISION_FACE_DETECTION, runFn: TFMP_FaceDetector },
|
|
1033
|
+
{ serves: TFMP_VISION_FACE_LANDMARKS, runFn: TFMP_FaceLandmarker },
|
|
1034
|
+
{ serves: TFMP_VISION_HAND_LANDMARKS, runFn: TFMP_HandLandmarker },
|
|
1035
|
+
{ serves: TFMP_VISION_POSE_LANDMARKS, runFn: TFMP_PoseLandmarker },
|
|
1036
|
+
{ serves: TFMP_VISION_GESTURE, runFn: TFMP_GestureRecognizer },
|
|
1037
|
+
{ serves: TFMP_MODEL_DOWNLOAD, runFn: TFMP_Download },
|
|
1038
|
+
{ serves: TFMP_MODEL_UNLOAD, runFn: TFMP_Unload },
|
|
1039
|
+
{ serves: TFMP_MODEL_SEARCH, runFn: TFMP_ModelSearch },
|
|
1040
|
+
{ serves: TFMP_MODEL_INFO, runFn: TFMP_ModelInfo }
|
|
1041
|
+
];
|
|
1042
|
+
|
|
1043
|
+
// src/ai/index.ts
|
|
1044
|
+
var _testOnly = {
|
|
1045
|
+
TensorFlowMediaPipeQueuedProvider,
|
|
1046
|
+
TFMP_RUN_FN_SPECS,
|
|
1047
|
+
TFMP_RUN_FNS
|
|
1048
|
+
};
|
|
337
1049
|
export {
|
|
338
1050
|
registerTensorFlowMediaPipe,
|
|
339
1051
|
createTFMPModelSearch,
|
|
1052
|
+
_testOnly,
|
|
340
1053
|
TextPipelineTask,
|
|
341
1054
|
TFMP_ModelSearch,
|
|
342
1055
|
TFMP_DEFAULT_TASK_TYPES,
|
|
@@ -346,4 +1059,4 @@ export {
|
|
|
346
1059
|
TENSORFLOW_MEDIAPIPE
|
|
347
1060
|
};
|
|
348
1061
|
|
|
349
|
-
//# debugId=
|
|
1062
|
+
//# debugId=23504D8C865655B764756E2164756E21
|