@camstack/addon-pipeline 1.1.0 → 1.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.
- package/dist/audio-analyzer/index.js +104 -29
- package/dist/audio-analyzer/index.mjs +100 -25
- package/dist/audio-codec-nodeav/index.js +1 -1
- package/dist/audio-codec-nodeav/index.mjs +1 -1
- package/dist/decoder-nodeav/index.js +1 -1
- package/dist/decoder-nodeav/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +187 -56
- package/dist/detection-pipeline/index.mjs +175 -44
- package/dist/{dist-BA6DR_jV.mjs → dist-BWc-HYQz.mjs} +194 -1
- package/dist/{dist-BLcTVvol.js → dist-DnD2tm7T.js} +194 -1
- package/dist/{model-download-service-RxAOiYvX-CMAvhgO7.mjs → model-download-service-C-IHWnXx-3Mmeob3l.mjs} +1 -1
- package/dist/{model-download-service-RxAOiYvX-C8rTRJy_.js → model-download-service-C-IHWnXx-BnQ_awK4.js} +1 -1
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +14 -10
- package/dist/pipeline-runner/index.mjs +14 -10
- package/dist/recorder/index.js +4 -4
- package/dist/recorder/index.mjs +2 -2
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DrohyZ5L.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Do7lgO8N.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-FRD2eBuz.mjs +26 -0
- package/dist/stream-broker/{hostInit-zLZbYJcg.mjs → hostInit-D5y5VMK8.mjs} +3 -3
- package/dist/stream-broker/index.js +8 -8
- package/dist/stream-broker/index.mjs +2 -2
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-DJ7ztnFv.js → MaskShapeCanvas-DI4BY7W2-5UPreLSr.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CQzEnQoq.js → MotionZonesSettings-NcxxQN8r-Bxqs-CpZ.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-Cl0eOy_U.js → PrivacyMaskSettings-APgPLF7p-BDMPeMJd.js} +1 -1
- package/embed-dist/assets/{index-CSuLwWK-.js → index-BgGwqHYl.js} +9 -9
- package/embed-dist/index.html +1 -1
- package/package.json +1 -1
- package/python/postprocessors/saliency.py +47 -1
- package/python/postprocessors/test_saliency.py +23 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-COa17XL2.mjs +0 -26
|
@@ -2,17 +2,17 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const
|
|
6
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BnQ_awK4.js");
|
|
6
|
+
const require_dist = require("../dist-DnD2tm7T.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
|
-
node_fs =
|
|
8
|
+
node_fs = require_model_download_service_C_IHWnXx.__toESM(node_fs);
|
|
9
9
|
let node_path = require("node:path");
|
|
10
|
-
node_path =
|
|
10
|
+
node_path = require_model_download_service_C_IHWnXx.__toESM(node_path);
|
|
11
11
|
let node_os = require("node:os");
|
|
12
|
-
node_os =
|
|
12
|
+
node_os = require_model_download_service_C_IHWnXx.__toESM(node_os);
|
|
13
13
|
let node_child_process = require("node:child_process");
|
|
14
14
|
let sharp = require("sharp");
|
|
15
|
-
sharp =
|
|
15
|
+
sharp = require_model_download_service_C_IHWnXx.__toESM(sharp);
|
|
16
16
|
//#region src/detection-pipeline/engine-store-keys.ts
|
|
17
17
|
/**
|
|
18
18
|
* Per-node scoping for the detection-pipeline engine cascade.
|
|
@@ -1043,6 +1043,26 @@ var ovFormat = (url, sizeMB) => {
|
|
|
1043
1043
|
...files ? { files } : {}
|
|
1044
1044
|
};
|
|
1045
1045
|
};
|
|
1046
|
+
/**
|
|
1047
|
+
* Build a precision-variant catalog entry (OpenVINO-only) derived from a base
|
|
1048
|
+
* detection model. fp16 halves the weights (Intel iGPU/NPU sweet spot); int8 is
|
|
1049
|
+
* NNCF post-training-quantized (~4× smaller, fastest on CPU/iGPU at a small
|
|
1050
|
+
* accuracy cost). The IRs live next to the base `.xml` on HF as
|
|
1051
|
+
* `camstack-<id>-<precision>.xml`. Lets an operator scale the model to the node
|
|
1052
|
+
* (e.g. yolo26x-int8 on a 265K, yolo26n-int8 on an N100).
|
|
1053
|
+
*/
|
|
1054
|
+
var ovPrecisionVariant = (baseId, ovDir, baseName, precision, sizeMB) => ({
|
|
1055
|
+
id: `${baseId}-${precision}`,
|
|
1056
|
+
name: `${baseName} (${precision.toUpperCase()})`,
|
|
1057
|
+
description: `${baseName} — OpenVINO ${precision.toUpperCase()} variant for Intel iGPU/NPU; scale by hardware`,
|
|
1058
|
+
inputSize: {
|
|
1059
|
+
width: 640,
|
|
1060
|
+
height: 640
|
|
1061
|
+
},
|
|
1062
|
+
labels: [],
|
|
1063
|
+
preprocessMode: "letterbox",
|
|
1064
|
+
formats: { openvino: ovFormat(hf(`${ovDir}/camstack-${baseId}-${precision}.xml`), sizeMB) }
|
|
1065
|
+
});
|
|
1046
1066
|
var MLPACKAGE_FILES = [
|
|
1047
1067
|
"Manifest.json",
|
|
1048
1068
|
"Data/com.apple.CoreML/model.mlmodel",
|
|
@@ -1348,7 +1368,21 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1348
1368
|
},
|
|
1349
1369
|
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9m_relu.xml"), 38)
|
|
1350
1370
|
}
|
|
1351
|
-
}
|
|
1371
|
+
},
|
|
1372
|
+
ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "fp16", 5),
|
|
1373
|
+
ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "int8", 3),
|
|
1374
|
+
ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "fp16", 15),
|
|
1375
|
+
ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "int8", 8),
|
|
1376
|
+
ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "fp16", 5),
|
|
1377
|
+
ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "int8", 3),
|
|
1378
|
+
ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "fp16", 19),
|
|
1379
|
+
ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "int8", 10),
|
|
1380
|
+
ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "fp16", 41),
|
|
1381
|
+
ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "int8", 21),
|
|
1382
|
+
ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "fp16", 50),
|
|
1383
|
+
ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "int8", 25),
|
|
1384
|
+
ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "fp16", 112),
|
|
1385
|
+
ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "int8", 56)
|
|
1352
1386
|
];
|
|
1353
1387
|
var FACE_DETECTION_MODELS = [{
|
|
1354
1388
|
id: "scrfd-2.5g",
|
|
@@ -1795,7 +1829,7 @@ var ObjectDetectionStep = class {
|
|
|
1795
1829
|
"animal"
|
|
1796
1830
|
],
|
|
1797
1831
|
models: [...OBJECT_DETECTION_MODELS],
|
|
1798
|
-
defaultModelId: "
|
|
1832
|
+
defaultModelId: "yolo26n",
|
|
1799
1833
|
defaultConfidence: .5,
|
|
1800
1834
|
labels: require_dist.COCO_80_LABELS.map((l) => l.id),
|
|
1801
1835
|
classMap: require_dist.COCO_TO_MACRO
|
|
@@ -2044,9 +2078,9 @@ var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
|
|
|
2044
2078
|
enabledByDefault: false,
|
|
2045
2079
|
defaultConfidence: .3
|
|
2046
2080
|
});
|
|
2047
|
-
var
|
|
2048
|
-
id: "segmentation
|
|
2049
|
-
name: "
|
|
2081
|
+
var STEP_SEGMENTATION = new PipelineStepBase({
|
|
2082
|
+
id: "segmentation",
|
|
2083
|
+
name: "Segmentation",
|
|
2050
2084
|
slot: "refiner",
|
|
2051
2085
|
postprocessor: "saliency",
|
|
2052
2086
|
extractMode: "crop-roi",
|
|
@@ -2058,7 +2092,7 @@ var STEP_SEGMENTATION_REFINER = new PipelineStepBase({
|
|
|
2058
2092
|
defaultConfidence: 0,
|
|
2059
2093
|
group: "Segmentation"
|
|
2060
2094
|
});
|
|
2061
|
-
|
|
2095
|
+
new PipelineStepBase({
|
|
2062
2096
|
id: "instance-segmentation",
|
|
2063
2097
|
name: "Instance Segmentation",
|
|
2064
2098
|
slot: "refiner",
|
|
@@ -2085,8 +2119,7 @@ var ALL_PIPELINE_STEPS = [
|
|
|
2085
2119
|
new AnimalClassifierStep(),
|
|
2086
2120
|
STEP_BIRD_CLASSIFIER,
|
|
2087
2121
|
STEP_VEHICLE_CLASSIFIER,
|
|
2088
|
-
|
|
2089
|
-
STEP_INSTANCE_SEGMENTATION,
|
|
2122
|
+
STEP_SEGMENTATION,
|
|
2090
2123
|
STEP_AUDIO_CLASSIFIER_INSTANCE
|
|
2091
2124
|
];
|
|
2092
2125
|
/** Compat: flat array of StepDefinition for existing consumers */
|
|
@@ -3257,6 +3290,18 @@ function applyChildOutput(parent, childStep, output, stepLatencyMs, ctx) {
|
|
|
3257
3290
|
parent.mask = output.mask;
|
|
3258
3291
|
parent.maskWidth = output.maskWidth;
|
|
3259
3292
|
parent.maskHeight = output.maskHeight;
|
|
3293
|
+
if (output.maskBbox !== void 0 && output.maskWidth > 0 && output.maskHeight > 0) {
|
|
3294
|
+
const [px1, py1, px2, py2] = parent.bbox;
|
|
3295
|
+
const parentW = px2 - px1;
|
|
3296
|
+
const parentH = py2 - py1;
|
|
3297
|
+
const [mbx, mby, mbw, mbh] = output.maskBbox;
|
|
3298
|
+
parent.refinedBbox = [
|
|
3299
|
+
px1 + mbx / output.maskWidth * parentW,
|
|
3300
|
+
py1 + mby / output.maskHeight * parentH,
|
|
3301
|
+
px1 + (mbx + mbw) / output.maskWidth * parentW,
|
|
3302
|
+
py1 + (mby + mbh) / output.maskHeight * parentH
|
|
3303
|
+
];
|
|
3304
|
+
}
|
|
3260
3305
|
break;
|
|
3261
3306
|
}
|
|
3262
3307
|
}
|
|
@@ -3307,6 +3352,12 @@ function buildFrameResult(input) {
|
|
|
3307
3352
|
macroClass: m.macroClass,
|
|
3308
3353
|
score: m.score,
|
|
3309
3354
|
bbox,
|
|
3355
|
+
...m.refinedBbox !== void 0 ? { refinedBbox: bboxTupleToRect(m.refinedBbox) } : {},
|
|
3356
|
+
...m.mask !== void 0 && m.maskWidth !== void 0 && m.maskHeight !== void 0 ? {
|
|
3357
|
+
mask: m.mask,
|
|
3358
|
+
maskWidth: m.maskWidth,
|
|
3359
|
+
maskHeight: m.maskHeight
|
|
3360
|
+
} : {},
|
|
3310
3361
|
labels,
|
|
3311
3362
|
...m.parentId !== void 0 ? { parentId: m.parentId } : {},
|
|
3312
3363
|
...embedding !== void 0 ? {
|
|
@@ -3797,6 +3848,40 @@ function walkFieldsForDefaults(fields, out) {
|
|
|
3797
3848
|
}
|
|
3798
3849
|
}
|
|
3799
3850
|
//#endregion
|
|
3851
|
+
//#region src/detection-pipeline/registry/custom-models.ts
|
|
3852
|
+
/**
|
|
3853
|
+
* Group a flat list of custom-model descriptors (as returned by the
|
|
3854
|
+
* `custom-model-registry` collection cap) into a `stepId → entries` map for
|
|
3855
|
+
* the picker / resolution union. Pure; order within a step preserved.
|
|
3856
|
+
*/
|
|
3857
|
+
function groupCustomModelsByStep(descriptors) {
|
|
3858
|
+
const byStep = /* @__PURE__ */ new Map();
|
|
3859
|
+
for (const d of descriptors) {
|
|
3860
|
+
const arr = byStep.get(d.stepId) ?? [];
|
|
3861
|
+
arr.push(d.entry);
|
|
3862
|
+
byStep.set(d.stepId, arr);
|
|
3863
|
+
}
|
|
3864
|
+
return byStep;
|
|
3865
|
+
}
|
|
3866
|
+
/**
|
|
3867
|
+
* Union a step's static catalog models with operator-registered custom
|
|
3868
|
+
* models. On an `id` collision the static catalog entry wins — a custom
|
|
3869
|
+
* model can never shadow a built-in one.
|
|
3870
|
+
*
|
|
3871
|
+
* Pure + side-effect-free so it can be unit-tested in isolation and called
|
|
3872
|
+
* from the (free) `buildSchemaSlots` builder without any addon context.
|
|
3873
|
+
*/
|
|
3874
|
+
function mergeCustomModels(staticModels, customModels) {
|
|
3875
|
+
const seen = new Set(staticModels.map((m) => m.id));
|
|
3876
|
+
const merged = [...staticModels];
|
|
3877
|
+
for (const m of customModels) {
|
|
3878
|
+
if (seen.has(m.id)) continue;
|
|
3879
|
+
seen.add(m.id);
|
|
3880
|
+
merged.push(m);
|
|
3881
|
+
}
|
|
3882
|
+
return merged;
|
|
3883
|
+
}
|
|
3884
|
+
//#endregion
|
|
3800
3885
|
//#region src/detection-pipeline/provider.ts
|
|
3801
3886
|
/**
|
|
3802
3887
|
* DetectionPipelineProvider — implements IPipelineExecutorProvider.
|
|
@@ -4032,6 +4117,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4032
4117
|
/** Addon context — ctx.api resolves lazily (direct caller created after boot) */
|
|
4033
4118
|
addonCtx = null;
|
|
4034
4119
|
/**
|
|
4120
|
+
* Short-lived cache of custom models pulled from the `custom-model-registry`
|
|
4121
|
+
* collection cap, grouped by step id. TTL-bounded so the picker / resolution
|
|
4122
|
+
* paths don't issue a cap round-trip on every call. Empty map = no provider
|
|
4123
|
+
* (or a query failure) → behaviour identical to the static-catalog-only path.
|
|
4124
|
+
*/
|
|
4125
|
+
customModelsCache = null;
|
|
4126
|
+
/**
|
|
4035
4127
|
* Per-device {@link DeviceProxy} cache used for zone gating at the
|
|
4036
4128
|
* runtime path. Reads `state.zones.value` + `state.zoneRules.value`
|
|
4037
4129
|
* synchronously per frame so detections inside an `exclude` zone
|
|
@@ -4360,8 +4452,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4360
4452
|
* runtime through installing → verifying → ready.
|
|
4361
4453
|
*/
|
|
4362
4454
|
async onEngineSelectionChanged() {
|
|
4455
|
+
const prev = this.currentEngine;
|
|
4363
4456
|
const stored = await this.loadEngine();
|
|
4364
4457
|
if (stored) this.currentEngine = stored;
|
|
4458
|
+
if ((prev.runtime !== this.currentEngine.runtime || prev.backend !== this.currentEngine.backend || prev.format !== this.currentEngine.format || (prev.device ?? "") !== (this.currentEngine.device ?? "")) && this.engineFactory) {
|
|
4459
|
+
this.log.info("engine selection changed — rebuilding pool in place", { meta: {
|
|
4460
|
+
from: `${prev.backend}/${prev.device ?? "default"}`,
|
|
4461
|
+
to: `${this.currentEngine.backend}/${this.currentEngine.device ?? "default"}`
|
|
4462
|
+
} });
|
|
4463
|
+
try {
|
|
4464
|
+
await this.engineFactory.dispose();
|
|
4465
|
+
} catch {}
|
|
4466
|
+
this.engineFactory = null;
|
|
4467
|
+
this.executor = null;
|
|
4468
|
+
}
|
|
4365
4469
|
this.startProvisioningForCurrentEngine();
|
|
4366
4470
|
}
|
|
4367
4471
|
/**
|
|
@@ -4399,9 +4503,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4399
4503
|
if (!steps || steps.length === 0) return;
|
|
4400
4504
|
for (const step of flattenSteps(steps)) {
|
|
4401
4505
|
if (!step.enabled) continue;
|
|
4402
|
-
const modelEntry =
|
|
4506
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
4403
4507
|
if (!modelEntry) continue;
|
|
4404
|
-
if (
|
|
4508
|
+
if (require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, modelEntry, format)) continue;
|
|
4405
4509
|
await this.downloadWithRetry(modelEntry, format, 3);
|
|
4406
4510
|
}
|
|
4407
4511
|
}
|
|
@@ -4477,10 +4581,44 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4477
4581
|
return { hardware: null };
|
|
4478
4582
|
}
|
|
4479
4583
|
}
|
|
4584
|
+
/**
|
|
4585
|
+
* Pull custom models from the `custom-model-registry` collection cap,
|
|
4586
|
+
* grouped by step id. 5s TTL cache. Fully graceful: when no provider is
|
|
4587
|
+
* registered (or the query fails) it returns an empty map and logs at most
|
|
4588
|
+
* a warning — callers then behave exactly like the static-catalog path.
|
|
4589
|
+
*/
|
|
4590
|
+
async getCustomModels() {
|
|
4591
|
+
const now = Date.now();
|
|
4592
|
+
if (this.customModelsCache && now - this.customModelsCache.at < 5e3) return this.customModelsCache.byStep;
|
|
4593
|
+
let byStep = /* @__PURE__ */ new Map();
|
|
4594
|
+
try {
|
|
4595
|
+
const api = this.addonCtx?.api;
|
|
4596
|
+
if (api) {
|
|
4597
|
+
if ((await api.addons.listCapabilityProviders.query({ capName: "custom-model-registry" })).some((p) => p.isActive)) byStep = groupCustomModelsByStep(await api.customModelRegistry.listModels.query());
|
|
4598
|
+
}
|
|
4599
|
+
} catch (err) {
|
|
4600
|
+
this.log.warn("custom-model-registry query failed — using static catalog only", { meta: { error: require_dist.errMsg(err) } });
|
|
4601
|
+
}
|
|
4602
|
+
this.customModelsCache = {
|
|
4603
|
+
at: now,
|
|
4604
|
+
byStep
|
|
4605
|
+
};
|
|
4606
|
+
return byStep;
|
|
4607
|
+
}
|
|
4608
|
+
/**
|
|
4609
|
+
* Resolve a model id within a step to a catalog entry — static catalog
|
|
4610
|
+
* first, then the custom registry. Returns undefined if neither has it.
|
|
4611
|
+
*/
|
|
4612
|
+
async resolveModelEntry(addonId, modelId) {
|
|
4613
|
+
const fromCatalog = getStepDefinition(addonId).models.find((m) => m.id === modelId);
|
|
4614
|
+
if (fromCatalog) return fromCatalog;
|
|
4615
|
+
return (await this.getCustomModels()).get(addonId)?.find((m) => m.id === modelId);
|
|
4616
|
+
}
|
|
4480
4617
|
async getSchema(engine) {
|
|
4481
4618
|
if (!engine || !engine.runtime) engine = await this.getSelectedEngine();
|
|
4482
4619
|
const format = engine.format;
|
|
4483
|
-
const
|
|
4620
|
+
const customByStep = await this.getCustomModels();
|
|
4621
|
+
const slots = buildSchemaSlots(format, this.modelsDir, customByStep);
|
|
4484
4622
|
const { hardware } = await this.fetchProbeGatingData();
|
|
4485
4623
|
const env = runtimeEnvFromProcess(toProbedHardware(hardware));
|
|
4486
4624
|
return {
|
|
@@ -4609,7 +4747,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4609
4747
|
const formats = {};
|
|
4610
4748
|
for (const [formatKey, entry] of Object.entries(m.formats)) {
|
|
4611
4749
|
if (!entry) continue;
|
|
4612
|
-
const downloaded =
|
|
4750
|
+
const downloaded = require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, m, formatKey);
|
|
4613
4751
|
formats[formatKey] = {
|
|
4614
4752
|
url: entry.url,
|
|
4615
4753
|
sizeMB: entry.sizeMB,
|
|
@@ -4667,7 +4805,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4667
4805
|
}
|
|
4668
4806
|
async downloadModel(input) {
|
|
4669
4807
|
const { modelId, format, addonId } = input;
|
|
4670
|
-
const modelEntry =
|
|
4808
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4671
4809
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4672
4810
|
const formatEntry = modelEntry.formats[format];
|
|
4673
4811
|
if (!formatEntry) throw new Error(`Model "${modelId}" has no ${format} format. Available: ${Object.keys(modelEntry.formats).join(", ")}`);
|
|
@@ -4730,9 +4868,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4730
4868
|
}
|
|
4731
4869
|
async deleteModel(input) {
|
|
4732
4870
|
const { modelId, format, addonId } = input;
|
|
4733
|
-
const modelEntry =
|
|
4871
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4734
4872
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4735
|
-
if (!
|
|
4873
|
+
if (!require_model_download_service_C_IHWnXx.deleteModelFromDisk(this.modelsDir, modelEntry, format)) throw new Error(`Model "${modelId}" (${format}) is not downloaded — nothing to delete`);
|
|
4736
4874
|
this.log.info("Model deleted from disk", { meta: {
|
|
4737
4875
|
modelId,
|
|
4738
4876
|
format
|
|
@@ -5186,8 +5324,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5186
5324
|
const work = (async () => {
|
|
5187
5325
|
const format = this.currentEngine?.format ?? "onnx";
|
|
5188
5326
|
for (const step of needed) {
|
|
5189
|
-
const modelEntry =
|
|
5190
|
-
if (modelEntry && !
|
|
5327
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5328
|
+
if (modelEntry && !require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, modelEntry, format)) {
|
|
5191
5329
|
this.log.info("Downloading model for step", { meta: {
|
|
5192
5330
|
modelId: step.modelId,
|
|
5193
5331
|
format,
|
|
@@ -5212,7 +5350,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5212
5350
|
/** Download a model with retry + exponential backoff */
|
|
5213
5351
|
async downloadWithRetry(entry, format, maxRetries, onProgress) {
|
|
5214
5352
|
for (let attempt = 1; attempt <= maxRetries; attempt++) try {
|
|
5215
|
-
await
|
|
5353
|
+
await require_model_download_service_C_IHWnXx.ensureModel(this.modelsDir, entry, format, onProgress);
|
|
5216
5354
|
this.log.info("Model downloaded successfully", { meta: { modelId: entry.id } });
|
|
5217
5355
|
return;
|
|
5218
5356
|
} catch (err) {
|
|
@@ -5681,7 +5819,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5681
5819
|
const format = this.currentEngine.format;
|
|
5682
5820
|
const downloads = [];
|
|
5683
5821
|
for (const step of flattenSteps(steps)) {
|
|
5684
|
-
const modelEntry =
|
|
5822
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5685
5823
|
if (!modelEntry) {
|
|
5686
5824
|
this.log.warn("Model not found in step catalog — skipping download", { meta: {
|
|
5687
5825
|
modelId: step.modelId,
|
|
@@ -5689,11 +5827,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5689
5827
|
} });
|
|
5690
5828
|
continue;
|
|
5691
5829
|
}
|
|
5692
|
-
if (!
|
|
5830
|
+
if (!require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, modelEntry, format)) this.log.info("Downloading model", { meta: {
|
|
5693
5831
|
modelId: step.modelId,
|
|
5694
5832
|
format
|
|
5695
5833
|
} });
|
|
5696
|
-
downloads.push(
|
|
5834
|
+
downloads.push(require_model_download_service_C_IHWnXx.ensureModel(this.modelsDir, modelEntry, format).then(() => {}));
|
|
5697
5835
|
}
|
|
5698
5836
|
await Promise.all(downloads);
|
|
5699
5837
|
await this.ensureBackendDeps(this.currentEngine);
|
|
@@ -6084,11 +6222,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6084
6222
|
}
|
|
6085
6223
|
}
|
|
6086
6224
|
};
|
|
6087
|
-
function buildSchemaSlots(format, modelsDir) {
|
|
6225
|
+
function buildSchemaSlots(format, modelsDir, customByStep) {
|
|
6088
6226
|
const slotMap = /* @__PURE__ */ new Map();
|
|
6089
6227
|
for (const pipelineStep of ALL_PIPELINE_STEPS) {
|
|
6090
6228
|
const step = pipelineStep.definition;
|
|
6091
|
-
const availableModels = step.models.filter((m) => m.formats[format]);
|
|
6229
|
+
const availableModels = mergeCustomModels(step.models, customByStep?.get(step.id) ?? []).filter((m) => m.formats[format]);
|
|
6092
6230
|
if (availableModels.length === 0) continue;
|
|
6093
6231
|
const slot = step.slot;
|
|
6094
6232
|
if (!slotMap.has(slot)) slotMap.set(slot, []);
|
|
@@ -6104,7 +6242,7 @@ function buildSchemaSlots(format, modelsDir) {
|
|
|
6104
6242
|
id: m.id,
|
|
6105
6243
|
name: m.name,
|
|
6106
6244
|
formats: Object.fromEntries(Object.entries(m.formats).map(([f, entry]) => [f, {
|
|
6107
|
-
downloaded:
|
|
6245
|
+
downloaded: require_model_download_service_C_IHWnXx.isModelDownloaded(modelsDir, m, f),
|
|
6108
6246
|
sizeMB: entry.sizeMB
|
|
6109
6247
|
}]))
|
|
6110
6248
|
})),
|
|
@@ -6193,8 +6331,7 @@ function buildDefaultStepTree(format) {
|
|
|
6193
6331
|
makeStep("animal-classifier", [], { enabled: false }),
|
|
6194
6332
|
makeStep("bird-classifier", [], { enabled: false }),
|
|
6195
6333
|
makeStep("vehicle-classifier", [], { enabled: false }),
|
|
6196
|
-
makeStep("segmentation
|
|
6197
|
-
makeStep("instance-segmentation", [], { enabled: false })
|
|
6334
|
+
makeStep("segmentation", [], { enabled: false })
|
|
6198
6335
|
].filter((s) => s !== null));
|
|
6199
6336
|
const audioEngine = getDefaultModelForFormat("audio-classifier", format) === "apple-soundanalysis" ? {
|
|
6200
6337
|
runtime: "python",
|
|
@@ -6484,8 +6621,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6484
6621
|
label: "Execution provider",
|
|
6485
6622
|
options: [...STATIC_BACKEND_OPTIONS],
|
|
6486
6623
|
default: DEFAULT_CONFIG.engineBackend,
|
|
6487
|
-
immediate: true
|
|
6488
|
-
requiresRestart: true
|
|
6624
|
+
immediate: true
|
|
6489
6625
|
}),
|
|
6490
6626
|
this.field({
|
|
6491
6627
|
type: "select",
|
|
@@ -6493,8 +6629,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6493
6629
|
label: "Hardware device",
|
|
6494
6630
|
options: [...STATIC_DEFAULT_DEVICE_OPTIONS],
|
|
6495
6631
|
default: DEFAULT_CONFIG.engineDevice,
|
|
6496
|
-
immediate: true
|
|
6497
|
-
requiresRestart: true
|
|
6632
|
+
immediate: true
|
|
6498
6633
|
})
|
|
6499
6634
|
]
|
|
6500
6635
|
}, {
|
|
@@ -6533,8 +6668,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6533
6668
|
"onnx",
|
|
6534
6669
|
"cpu"
|
|
6535
6670
|
]
|
|
6536
|
-
}
|
|
6537
|
-
requiresRestart: true
|
|
6671
|
+
}
|
|
6538
6672
|
}),
|
|
6539
6673
|
this.field({
|
|
6540
6674
|
type: "slider",
|
|
@@ -6551,8 +6685,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6551
6685
|
showWhen: {
|
|
6552
6686
|
field: "batchMode",
|
|
6553
6687
|
notEquals: "none"
|
|
6554
|
-
}
|
|
6555
|
-
requiresRestart: true
|
|
6688
|
+
}
|
|
6556
6689
|
}),
|
|
6557
6690
|
this.field({
|
|
6558
6691
|
type: "slider",
|
|
@@ -6569,8 +6702,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6569
6702
|
showWhen: {
|
|
6570
6703
|
field: "batchMode",
|
|
6571
6704
|
notEquals: "none"
|
|
6572
|
-
}
|
|
6573
|
-
requiresRestart: true
|
|
6705
|
+
}
|
|
6574
6706
|
}),
|
|
6575
6707
|
this.field({
|
|
6576
6708
|
type: "slider",
|
|
@@ -6583,8 +6715,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6583
6715
|
default: void 0,
|
|
6584
6716
|
showValue: true,
|
|
6585
6717
|
nullable: true,
|
|
6586
|
-
nullLabel: "Auto"
|
|
6587
|
-
requiresRestart: true
|
|
6718
|
+
nullLabel: "Auto"
|
|
6588
6719
|
}),
|
|
6589
6720
|
this.field({
|
|
6590
6721
|
type: "slider",
|
|
@@ -6597,8 +6728,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6597
6728
|
default: void 0,
|
|
6598
6729
|
showValue: true,
|
|
6599
6730
|
nullable: true,
|
|
6600
|
-
nullLabel: "Auto"
|
|
6601
|
-
requiresRestart: true
|
|
6731
|
+
nullLabel: "Auto"
|
|
6602
6732
|
})
|
|
6603
6733
|
]
|
|
6604
6734
|
}] });
|
|
@@ -6971,16 +7101,17 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6971
7101
|
return false;
|
|
6972
7102
|
}
|
|
6973
7103
|
/**
|
|
6974
|
-
* BaseAddon calls `onConfigChanged` after every settings write.
|
|
6975
|
-
*
|
|
6976
|
-
*
|
|
6977
|
-
*
|
|
6978
|
-
*
|
|
6979
|
-
* respawn the
|
|
6980
|
-
*
|
|
6981
|
-
*
|
|
6982
|
-
*
|
|
6983
|
-
*
|
|
7104
|
+
* BaseAddon calls `onConfigChanged` after every settings write. This is the
|
|
7105
|
+
* SOLE apply path for engine + tuning changes — none of those fields declare
|
|
7106
|
+
* `requiresRestart` anymore (an addon restart re-probes hardware + reloads
|
|
7107
|
+
* every model on every set, which is exactly what we want to avoid). Instead:
|
|
7108
|
+
* - Pool-bound tuning (`concurrency`/`batchMode`/`windowMs`/…) → in-place
|
|
7109
|
+
* pool respawn when the snapshot flips (`poolConfigChanged` below).
|
|
7110
|
+
* - Engine cascade (`engineBackend`/`engineDevice`) → the provider's
|
|
7111
|
+
* `onEngineSelectionChanged` disposes the device-bound factory so the next
|
|
7112
|
+
* runPipeline rebuilds the pool on the new selection, in place.
|
|
7113
|
+
* Both apply optimistically — the inference gate stays closed for the short
|
|
7114
|
+
* re-spin, frames are dropped (never crashed), no addon bounce.
|
|
6984
7115
|
*/
|
|
6985
7116
|
async onConfigChanged() {
|
|
6986
7117
|
await this.refreshNodeEngineFromStore();
|