@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { A as nodePin, B as BaseAddon, C as detectionPipelineCapability, J as hydrateSchema, L as supportedRuntimes$1, P as runtimeDevices$1, T as hfModelUrl, W as EventCategory, Z as parseJsonUnknown, a as COCO_TO_MACRO, et as sleep, i as COCO_80_LABELS, j as pipelineExecutorCapability, p as YAMNET_TO_MACRO, q as createEvent, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, w as evaluateZoneRules, x as defaultDeviceFor$1, z as errMsg } from "../dist-
|
|
3
|
-
import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-
|
|
2
|
+
import { A as nodePin, B as BaseAddon, C as detectionPipelineCapability, J as hydrateSchema, L as supportedRuntimes$1, P as runtimeDevices$1, T as hfModelUrl, W as EventCategory, Z as parseJsonUnknown, a as COCO_TO_MACRO, et as sleep, i as COCO_80_LABELS, j as pipelineExecutorCapability, p as YAMNET_TO_MACRO, q as createEvent, r as AUDIO_MACRO_LABELS, t as APPLE_SA_TO_MACRO, w as evaluateZoneRules, x as defaultDeviceFor$1, z as errMsg } from "../dist-BWc-HYQz.mjs";
|
|
3
|
+
import { a as isModelDownloaded, i as ensureModel, n as deleteModelFromDisk } from "../model-download-service-C-IHWnXx-3Mmeob3l.mjs";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path$1 from "node:path";
|
|
6
6
|
import * as os from "node:os";
|
|
@@ -1036,6 +1036,26 @@ var ovFormat = (url, sizeMB) => {
|
|
|
1036
1036
|
...files ? { files } : {}
|
|
1037
1037
|
};
|
|
1038
1038
|
};
|
|
1039
|
+
/**
|
|
1040
|
+
* Build a precision-variant catalog entry (OpenVINO-only) derived from a base
|
|
1041
|
+
* detection model. fp16 halves the weights (Intel iGPU/NPU sweet spot); int8 is
|
|
1042
|
+
* NNCF post-training-quantized (~4× smaller, fastest on CPU/iGPU at a small
|
|
1043
|
+
* accuracy cost). The IRs live next to the base `.xml` on HF as
|
|
1044
|
+
* `camstack-<id>-<precision>.xml`. Lets an operator scale the model to the node
|
|
1045
|
+
* (e.g. yolo26x-int8 on a 265K, yolo26n-int8 on an N100).
|
|
1046
|
+
*/
|
|
1047
|
+
var ovPrecisionVariant = (baseId, ovDir, baseName, precision, sizeMB) => ({
|
|
1048
|
+
id: `${baseId}-${precision}`,
|
|
1049
|
+
name: `${baseName} (${precision.toUpperCase()})`,
|
|
1050
|
+
description: `${baseName} — OpenVINO ${precision.toUpperCase()} variant for Intel iGPU/NPU; scale by hardware`,
|
|
1051
|
+
inputSize: {
|
|
1052
|
+
width: 640,
|
|
1053
|
+
height: 640
|
|
1054
|
+
},
|
|
1055
|
+
labels: [],
|
|
1056
|
+
preprocessMode: "letterbox",
|
|
1057
|
+
formats: { openvino: ovFormat(hf(`${ovDir}/camstack-${baseId}-${precision}.xml`), sizeMB) }
|
|
1058
|
+
});
|
|
1039
1059
|
var MLPACKAGE_FILES = [
|
|
1040
1060
|
"Manifest.json",
|
|
1041
1061
|
"Data/com.apple.CoreML/model.mlmodel",
|
|
@@ -1341,7 +1361,21 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1341
1361
|
},
|
|
1342
1362
|
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9m_relu.xml"), 38)
|
|
1343
1363
|
}
|
|
1344
|
-
}
|
|
1364
|
+
},
|
|
1365
|
+
ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "fp16", 5),
|
|
1366
|
+
ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "int8", 3),
|
|
1367
|
+
ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "fp16", 15),
|
|
1368
|
+
ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "int8", 8),
|
|
1369
|
+
ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "fp16", 5),
|
|
1370
|
+
ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "int8", 3),
|
|
1371
|
+
ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "fp16", 19),
|
|
1372
|
+
ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "int8", 10),
|
|
1373
|
+
ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "fp16", 41),
|
|
1374
|
+
ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "int8", 21),
|
|
1375
|
+
ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "fp16", 50),
|
|
1376
|
+
ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "int8", 25),
|
|
1377
|
+
ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "fp16", 112),
|
|
1378
|
+
ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "int8", 56)
|
|
1345
1379
|
];
|
|
1346
1380
|
var FACE_DETECTION_MODELS = [{
|
|
1347
1381
|
id: "scrfd-2.5g",
|
|
@@ -1788,7 +1822,7 @@ var ObjectDetectionStep = class {
|
|
|
1788
1822
|
"animal"
|
|
1789
1823
|
],
|
|
1790
1824
|
models: [...OBJECT_DETECTION_MODELS],
|
|
1791
|
-
defaultModelId: "
|
|
1825
|
+
defaultModelId: "yolo26n",
|
|
1792
1826
|
defaultConfidence: .5,
|
|
1793
1827
|
labels: COCO_80_LABELS.map((l) => l.id),
|
|
1794
1828
|
classMap: COCO_TO_MACRO
|
|
@@ -2037,9 +2071,9 @@ var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
|
|
|
2037
2071
|
enabledByDefault: false,
|
|
2038
2072
|
defaultConfidence: .3
|
|
2039
2073
|
});
|
|
2040
|
-
var
|
|
2041
|
-
id: "segmentation
|
|
2042
|
-
name: "
|
|
2074
|
+
var STEP_SEGMENTATION = new PipelineStepBase({
|
|
2075
|
+
id: "segmentation",
|
|
2076
|
+
name: "Segmentation",
|
|
2043
2077
|
slot: "refiner",
|
|
2044
2078
|
postprocessor: "saliency",
|
|
2045
2079
|
extractMode: "crop-roi",
|
|
@@ -2051,7 +2085,7 @@ var STEP_SEGMENTATION_REFINER = new PipelineStepBase({
|
|
|
2051
2085
|
defaultConfidence: 0,
|
|
2052
2086
|
group: "Segmentation"
|
|
2053
2087
|
});
|
|
2054
|
-
|
|
2088
|
+
new PipelineStepBase({
|
|
2055
2089
|
id: "instance-segmentation",
|
|
2056
2090
|
name: "Instance Segmentation",
|
|
2057
2091
|
slot: "refiner",
|
|
@@ -2078,8 +2112,7 @@ var ALL_PIPELINE_STEPS = [
|
|
|
2078
2112
|
new AnimalClassifierStep(),
|
|
2079
2113
|
STEP_BIRD_CLASSIFIER,
|
|
2080
2114
|
STEP_VEHICLE_CLASSIFIER,
|
|
2081
|
-
|
|
2082
|
-
STEP_INSTANCE_SEGMENTATION,
|
|
2115
|
+
STEP_SEGMENTATION,
|
|
2083
2116
|
STEP_AUDIO_CLASSIFIER_INSTANCE
|
|
2084
2117
|
];
|
|
2085
2118
|
/** Compat: flat array of StepDefinition for existing consumers */
|
|
@@ -3250,6 +3283,18 @@ function applyChildOutput(parent, childStep, output, stepLatencyMs, ctx) {
|
|
|
3250
3283
|
parent.mask = output.mask;
|
|
3251
3284
|
parent.maskWidth = output.maskWidth;
|
|
3252
3285
|
parent.maskHeight = output.maskHeight;
|
|
3286
|
+
if (output.maskBbox !== void 0 && output.maskWidth > 0 && output.maskHeight > 0) {
|
|
3287
|
+
const [px1, py1, px2, py2] = parent.bbox;
|
|
3288
|
+
const parentW = px2 - px1;
|
|
3289
|
+
const parentH = py2 - py1;
|
|
3290
|
+
const [mbx, mby, mbw, mbh] = output.maskBbox;
|
|
3291
|
+
parent.refinedBbox = [
|
|
3292
|
+
px1 + mbx / output.maskWidth * parentW,
|
|
3293
|
+
py1 + mby / output.maskHeight * parentH,
|
|
3294
|
+
px1 + (mbx + mbw) / output.maskWidth * parentW,
|
|
3295
|
+
py1 + (mby + mbh) / output.maskHeight * parentH
|
|
3296
|
+
];
|
|
3297
|
+
}
|
|
3253
3298
|
break;
|
|
3254
3299
|
}
|
|
3255
3300
|
}
|
|
@@ -3300,6 +3345,12 @@ function buildFrameResult(input) {
|
|
|
3300
3345
|
macroClass: m.macroClass,
|
|
3301
3346
|
score: m.score,
|
|
3302
3347
|
bbox,
|
|
3348
|
+
...m.refinedBbox !== void 0 ? { refinedBbox: bboxTupleToRect(m.refinedBbox) } : {},
|
|
3349
|
+
...m.mask !== void 0 && m.maskWidth !== void 0 && m.maskHeight !== void 0 ? {
|
|
3350
|
+
mask: m.mask,
|
|
3351
|
+
maskWidth: m.maskWidth,
|
|
3352
|
+
maskHeight: m.maskHeight
|
|
3353
|
+
} : {},
|
|
3303
3354
|
labels,
|
|
3304
3355
|
...m.parentId !== void 0 ? { parentId: m.parentId } : {},
|
|
3305
3356
|
...embedding !== void 0 ? {
|
|
@@ -3790,6 +3841,40 @@ function walkFieldsForDefaults(fields, out) {
|
|
|
3790
3841
|
}
|
|
3791
3842
|
}
|
|
3792
3843
|
//#endregion
|
|
3844
|
+
//#region src/detection-pipeline/registry/custom-models.ts
|
|
3845
|
+
/**
|
|
3846
|
+
* Group a flat list of custom-model descriptors (as returned by the
|
|
3847
|
+
* `custom-model-registry` collection cap) into a `stepId → entries` map for
|
|
3848
|
+
* the picker / resolution union. Pure; order within a step preserved.
|
|
3849
|
+
*/
|
|
3850
|
+
function groupCustomModelsByStep(descriptors) {
|
|
3851
|
+
const byStep = /* @__PURE__ */ new Map();
|
|
3852
|
+
for (const d of descriptors) {
|
|
3853
|
+
const arr = byStep.get(d.stepId) ?? [];
|
|
3854
|
+
arr.push(d.entry);
|
|
3855
|
+
byStep.set(d.stepId, arr);
|
|
3856
|
+
}
|
|
3857
|
+
return byStep;
|
|
3858
|
+
}
|
|
3859
|
+
/**
|
|
3860
|
+
* Union a step's static catalog models with operator-registered custom
|
|
3861
|
+
* models. On an `id` collision the static catalog entry wins — a custom
|
|
3862
|
+
* model can never shadow a built-in one.
|
|
3863
|
+
*
|
|
3864
|
+
* Pure + side-effect-free so it can be unit-tested in isolation and called
|
|
3865
|
+
* from the (free) `buildSchemaSlots` builder without any addon context.
|
|
3866
|
+
*/
|
|
3867
|
+
function mergeCustomModels(staticModels, customModels) {
|
|
3868
|
+
const seen = new Set(staticModels.map((m) => m.id));
|
|
3869
|
+
const merged = [...staticModels];
|
|
3870
|
+
for (const m of customModels) {
|
|
3871
|
+
if (seen.has(m.id)) continue;
|
|
3872
|
+
seen.add(m.id);
|
|
3873
|
+
merged.push(m);
|
|
3874
|
+
}
|
|
3875
|
+
return merged;
|
|
3876
|
+
}
|
|
3877
|
+
//#endregion
|
|
3793
3878
|
//#region src/detection-pipeline/provider.ts
|
|
3794
3879
|
/**
|
|
3795
3880
|
* DetectionPipelineProvider — implements IPipelineExecutorProvider.
|
|
@@ -4025,6 +4110,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4025
4110
|
/** Addon context — ctx.api resolves lazily (direct caller created after boot) */
|
|
4026
4111
|
addonCtx = null;
|
|
4027
4112
|
/**
|
|
4113
|
+
* Short-lived cache of custom models pulled from the `custom-model-registry`
|
|
4114
|
+
* collection cap, grouped by step id. TTL-bounded so the picker / resolution
|
|
4115
|
+
* paths don't issue a cap round-trip on every call. Empty map = no provider
|
|
4116
|
+
* (or a query failure) → behaviour identical to the static-catalog-only path.
|
|
4117
|
+
*/
|
|
4118
|
+
customModelsCache = null;
|
|
4119
|
+
/**
|
|
4028
4120
|
* Per-device {@link DeviceProxy} cache used for zone gating at the
|
|
4029
4121
|
* runtime path. Reads `state.zones.value` + `state.zoneRules.value`
|
|
4030
4122
|
* synchronously per frame so detections inside an `exclude` zone
|
|
@@ -4353,8 +4445,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4353
4445
|
* runtime through installing → verifying → ready.
|
|
4354
4446
|
*/
|
|
4355
4447
|
async onEngineSelectionChanged() {
|
|
4448
|
+
const prev = this.currentEngine;
|
|
4356
4449
|
const stored = await this.loadEngine();
|
|
4357
4450
|
if (stored) this.currentEngine = stored;
|
|
4451
|
+
if ((prev.runtime !== this.currentEngine.runtime || prev.backend !== this.currentEngine.backend || prev.format !== this.currentEngine.format || (prev.device ?? "") !== (this.currentEngine.device ?? "")) && this.engineFactory) {
|
|
4452
|
+
this.log.info("engine selection changed — rebuilding pool in place", { meta: {
|
|
4453
|
+
from: `${prev.backend}/${prev.device ?? "default"}`,
|
|
4454
|
+
to: `${this.currentEngine.backend}/${this.currentEngine.device ?? "default"}`
|
|
4455
|
+
} });
|
|
4456
|
+
try {
|
|
4457
|
+
await this.engineFactory.dispose();
|
|
4458
|
+
} catch {}
|
|
4459
|
+
this.engineFactory = null;
|
|
4460
|
+
this.executor = null;
|
|
4461
|
+
}
|
|
4358
4462
|
this.startProvisioningForCurrentEngine();
|
|
4359
4463
|
}
|
|
4360
4464
|
/**
|
|
@@ -4392,7 +4496,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4392
4496
|
if (!steps || steps.length === 0) return;
|
|
4393
4497
|
for (const step of flattenSteps(steps)) {
|
|
4394
4498
|
if (!step.enabled) continue;
|
|
4395
|
-
const modelEntry =
|
|
4499
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
4396
4500
|
if (!modelEntry) continue;
|
|
4397
4501
|
if (isModelDownloaded(this.modelsDir, modelEntry, format)) continue;
|
|
4398
4502
|
await this.downloadWithRetry(modelEntry, format, 3);
|
|
@@ -4470,10 +4574,44 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4470
4574
|
return { hardware: null };
|
|
4471
4575
|
}
|
|
4472
4576
|
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Pull custom models from the `custom-model-registry` collection cap,
|
|
4579
|
+
* grouped by step id. 5s TTL cache. Fully graceful: when no provider is
|
|
4580
|
+
* registered (or the query fails) it returns an empty map and logs at most
|
|
4581
|
+
* a warning — callers then behave exactly like the static-catalog path.
|
|
4582
|
+
*/
|
|
4583
|
+
async getCustomModels() {
|
|
4584
|
+
const now = Date.now();
|
|
4585
|
+
if (this.customModelsCache && now - this.customModelsCache.at < 5e3) return this.customModelsCache.byStep;
|
|
4586
|
+
let byStep = /* @__PURE__ */ new Map();
|
|
4587
|
+
try {
|
|
4588
|
+
const api = this.addonCtx?.api;
|
|
4589
|
+
if (api) {
|
|
4590
|
+
if ((await api.addons.listCapabilityProviders.query({ capName: "custom-model-registry" })).some((p) => p.isActive)) byStep = groupCustomModelsByStep(await api.customModelRegistry.listModels.query());
|
|
4591
|
+
}
|
|
4592
|
+
} catch (err) {
|
|
4593
|
+
this.log.warn("custom-model-registry query failed — using static catalog only", { meta: { error: errMsg(err) } });
|
|
4594
|
+
}
|
|
4595
|
+
this.customModelsCache = {
|
|
4596
|
+
at: now,
|
|
4597
|
+
byStep
|
|
4598
|
+
};
|
|
4599
|
+
return byStep;
|
|
4600
|
+
}
|
|
4601
|
+
/**
|
|
4602
|
+
* Resolve a model id within a step to a catalog entry — static catalog
|
|
4603
|
+
* first, then the custom registry. Returns undefined if neither has it.
|
|
4604
|
+
*/
|
|
4605
|
+
async resolveModelEntry(addonId, modelId) {
|
|
4606
|
+
const fromCatalog = getStepDefinition(addonId).models.find((m) => m.id === modelId);
|
|
4607
|
+
if (fromCatalog) return fromCatalog;
|
|
4608
|
+
return (await this.getCustomModels()).get(addonId)?.find((m) => m.id === modelId);
|
|
4609
|
+
}
|
|
4473
4610
|
async getSchema(engine) {
|
|
4474
4611
|
if (!engine || !engine.runtime) engine = await this.getSelectedEngine();
|
|
4475
4612
|
const format = engine.format;
|
|
4476
|
-
const
|
|
4613
|
+
const customByStep = await this.getCustomModels();
|
|
4614
|
+
const slots = buildSchemaSlots(format, this.modelsDir, customByStep);
|
|
4477
4615
|
const { hardware } = await this.fetchProbeGatingData();
|
|
4478
4616
|
const env = runtimeEnvFromProcess(toProbedHardware(hardware));
|
|
4479
4617
|
return {
|
|
@@ -4660,7 +4798,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4660
4798
|
}
|
|
4661
4799
|
async downloadModel(input) {
|
|
4662
4800
|
const { modelId, format, addonId } = input;
|
|
4663
|
-
const modelEntry =
|
|
4801
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4664
4802
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4665
4803
|
const formatEntry = modelEntry.formats[format];
|
|
4666
4804
|
if (!formatEntry) throw new Error(`Model "${modelId}" has no ${format} format. Available: ${Object.keys(modelEntry.formats).join(", ")}`);
|
|
@@ -4723,7 +4861,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4723
4861
|
}
|
|
4724
4862
|
async deleteModel(input) {
|
|
4725
4863
|
const { modelId, format, addonId } = input;
|
|
4726
|
-
const modelEntry =
|
|
4864
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4727
4865
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4728
4866
|
if (!deleteModelFromDisk(this.modelsDir, modelEntry, format)) throw new Error(`Model "${modelId}" (${format}) is not downloaded — nothing to delete`);
|
|
4729
4867
|
this.log.info("Model deleted from disk", { meta: {
|
|
@@ -5179,7 +5317,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5179
5317
|
const work = (async () => {
|
|
5180
5318
|
const format = this.currentEngine?.format ?? "onnx";
|
|
5181
5319
|
for (const step of needed) {
|
|
5182
|
-
const modelEntry =
|
|
5320
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5183
5321
|
if (modelEntry && !isModelDownloaded(this.modelsDir, modelEntry, format)) {
|
|
5184
5322
|
this.log.info("Downloading model for step", { meta: {
|
|
5185
5323
|
modelId: step.modelId,
|
|
@@ -5674,7 +5812,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5674
5812
|
const format = this.currentEngine.format;
|
|
5675
5813
|
const downloads = [];
|
|
5676
5814
|
for (const step of flattenSteps(steps)) {
|
|
5677
|
-
const modelEntry =
|
|
5815
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5678
5816
|
if (!modelEntry) {
|
|
5679
5817
|
this.log.warn("Model not found in step catalog — skipping download", { meta: {
|
|
5680
5818
|
modelId: step.modelId,
|
|
@@ -6077,11 +6215,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6077
6215
|
}
|
|
6078
6216
|
}
|
|
6079
6217
|
};
|
|
6080
|
-
function buildSchemaSlots(format, modelsDir) {
|
|
6218
|
+
function buildSchemaSlots(format, modelsDir, customByStep) {
|
|
6081
6219
|
const slotMap = /* @__PURE__ */ new Map();
|
|
6082
6220
|
for (const pipelineStep of ALL_PIPELINE_STEPS) {
|
|
6083
6221
|
const step = pipelineStep.definition;
|
|
6084
|
-
const availableModels = step.models.filter((m) => m.formats[format]);
|
|
6222
|
+
const availableModels = mergeCustomModels(step.models, customByStep?.get(step.id) ?? []).filter((m) => m.formats[format]);
|
|
6085
6223
|
if (availableModels.length === 0) continue;
|
|
6086
6224
|
const slot = step.slot;
|
|
6087
6225
|
if (!slotMap.has(slot)) slotMap.set(slot, []);
|
|
@@ -6186,8 +6324,7 @@ function buildDefaultStepTree(format) {
|
|
|
6186
6324
|
makeStep("animal-classifier", [], { enabled: false }),
|
|
6187
6325
|
makeStep("bird-classifier", [], { enabled: false }),
|
|
6188
6326
|
makeStep("vehicle-classifier", [], { enabled: false }),
|
|
6189
|
-
makeStep("segmentation
|
|
6190
|
-
makeStep("instance-segmentation", [], { enabled: false })
|
|
6327
|
+
makeStep("segmentation", [], { enabled: false })
|
|
6191
6328
|
].filter((s) => s !== null));
|
|
6192
6329
|
const audioEngine = getDefaultModelForFormat("audio-classifier", format) === "apple-soundanalysis" ? {
|
|
6193
6330
|
runtime: "python",
|
|
@@ -6477,8 +6614,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6477
6614
|
label: "Execution provider",
|
|
6478
6615
|
options: [...STATIC_BACKEND_OPTIONS],
|
|
6479
6616
|
default: DEFAULT_CONFIG.engineBackend,
|
|
6480
|
-
immediate: true
|
|
6481
|
-
requiresRestart: true
|
|
6617
|
+
immediate: true
|
|
6482
6618
|
}),
|
|
6483
6619
|
this.field({
|
|
6484
6620
|
type: "select",
|
|
@@ -6486,8 +6622,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6486
6622
|
label: "Hardware device",
|
|
6487
6623
|
options: [...STATIC_DEFAULT_DEVICE_OPTIONS],
|
|
6488
6624
|
default: DEFAULT_CONFIG.engineDevice,
|
|
6489
|
-
immediate: true
|
|
6490
|
-
requiresRestart: true
|
|
6625
|
+
immediate: true
|
|
6491
6626
|
})
|
|
6492
6627
|
]
|
|
6493
6628
|
}, {
|
|
@@ -6526,8 +6661,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6526
6661
|
"onnx",
|
|
6527
6662
|
"cpu"
|
|
6528
6663
|
]
|
|
6529
|
-
}
|
|
6530
|
-
requiresRestart: true
|
|
6664
|
+
}
|
|
6531
6665
|
}),
|
|
6532
6666
|
this.field({
|
|
6533
6667
|
type: "slider",
|
|
@@ -6544,8 +6678,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6544
6678
|
showWhen: {
|
|
6545
6679
|
field: "batchMode",
|
|
6546
6680
|
notEquals: "none"
|
|
6547
|
-
}
|
|
6548
|
-
requiresRestart: true
|
|
6681
|
+
}
|
|
6549
6682
|
}),
|
|
6550
6683
|
this.field({
|
|
6551
6684
|
type: "slider",
|
|
@@ -6562,8 +6695,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6562
6695
|
showWhen: {
|
|
6563
6696
|
field: "batchMode",
|
|
6564
6697
|
notEquals: "none"
|
|
6565
|
-
}
|
|
6566
|
-
requiresRestart: true
|
|
6698
|
+
}
|
|
6567
6699
|
}),
|
|
6568
6700
|
this.field({
|
|
6569
6701
|
type: "slider",
|
|
@@ -6576,8 +6708,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6576
6708
|
default: void 0,
|
|
6577
6709
|
showValue: true,
|
|
6578
6710
|
nullable: true,
|
|
6579
|
-
nullLabel: "Auto"
|
|
6580
|
-
requiresRestart: true
|
|
6711
|
+
nullLabel: "Auto"
|
|
6581
6712
|
}),
|
|
6582
6713
|
this.field({
|
|
6583
6714
|
type: "slider",
|
|
@@ -6590,8 +6721,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6590
6721
|
default: void 0,
|
|
6591
6722
|
showValue: true,
|
|
6592
6723
|
nullable: true,
|
|
6593
|
-
nullLabel: "Auto"
|
|
6594
|
-
requiresRestart: true
|
|
6724
|
+
nullLabel: "Auto"
|
|
6595
6725
|
})
|
|
6596
6726
|
]
|
|
6597
6727
|
}] });
|
|
@@ -6964,16 +7094,17 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6964
7094
|
return false;
|
|
6965
7095
|
}
|
|
6966
7096
|
/**
|
|
6967
|
-
* BaseAddon calls `onConfigChanged` after every settings write.
|
|
6968
|
-
*
|
|
6969
|
-
*
|
|
6970
|
-
*
|
|
6971
|
-
*
|
|
6972
|
-
* respawn the
|
|
6973
|
-
*
|
|
6974
|
-
*
|
|
6975
|
-
*
|
|
6976
|
-
*
|
|
7097
|
+
* BaseAddon calls `onConfigChanged` after every settings write. This is the
|
|
7098
|
+
* SOLE apply path for engine + tuning changes — none of those fields declare
|
|
7099
|
+
* `requiresRestart` anymore (an addon restart re-probes hardware + reloads
|
|
7100
|
+
* every model on every set, which is exactly what we want to avoid). Instead:
|
|
7101
|
+
* - Pool-bound tuning (`concurrency`/`batchMode`/`windowMs`/…) → in-place
|
|
7102
|
+
* pool respawn when the snapshot flips (`poolConfigChanged` below).
|
|
7103
|
+
* - Engine cascade (`engineBackend`/`engineDevice`) → the provider's
|
|
7104
|
+
* `onEngineSelectionChanged` disposes the device-bound factory so the next
|
|
7105
|
+
* runPipeline rebuilds the pool on the new selection, in place.
|
|
7106
|
+
* Both apply optimistically — the inference gate stays closed for the short
|
|
7107
|
+
* re-spin, frames are dropped (never crashed), no addon bounce.
|
|
6977
7108
|
*/
|
|
6978
7109
|
async onConfigChanged() {
|
|
6979
7110
|
await this.refreshNodeEngineFromStore();
|
|
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4627
4627
|
return inst;
|
|
4628
4628
|
}
|
|
4629
4629
|
//#endregion
|
|
4630
|
-
//#region ../types/dist/sleep-
|
|
4630
|
+
//#region ../types/dist/sleep-BV7rLc6Y.mjs
|
|
4631
4631
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4632
4632
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4633
4633
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5106,6 +5106,12 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5106
5106
|
* Payload: `{ deviceId, childDeviceIds, hiddenChildIds }`.
|
|
5107
5107
|
*/
|
|
5108
5108
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
5109
|
+
/**
|
|
5110
|
+
* Progress update from a running model conversion job.
|
|
5111
|
+
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
5112
|
+
* Emitted by `addon-model-studio` on the converting node.
|
|
5113
|
+
*/
|
|
5114
|
+
EventCategory["ModelConvertProgress"] = "model-convert.progress";
|
|
5109
5115
|
return EventCategory;
|
|
5110
5116
|
}({});
|
|
5111
5117
|
Object.fromEntries([
|
|
@@ -6756,6 +6762,13 @@ object({
|
|
|
6756
6762
|
unreachable: number()
|
|
6757
6763
|
})
|
|
6758
6764
|
});
|
|
6765
|
+
var LabelDefinitionSchema = object({
|
|
6766
|
+
id: string(),
|
|
6767
|
+
name: string(),
|
|
6768
|
+
category: string().optional(),
|
|
6769
|
+
description: string().optional(),
|
|
6770
|
+
icon: string().optional()
|
|
6771
|
+
});
|
|
6759
6772
|
var MODEL_FORMATS = [
|
|
6760
6773
|
"onnx",
|
|
6761
6774
|
"coreml",
|
|
@@ -6764,6 +6777,120 @@ var MODEL_FORMATS = [
|
|
|
6764
6777
|
"pt"
|
|
6765
6778
|
];
|
|
6766
6779
|
/**
|
|
6780
|
+
* Multi-file format payload.
|
|
6781
|
+
*
|
|
6782
|
+
* - Directory formats (`isDirectory: true`, e.g. `.mlpackage`): files
|
|
6783
|
+
* relative to the directory root — the downloader fetches each from
|
|
6784
|
+
* `{url}/{file}` into `{modelDir}/{file}`. If omitted, it probes the
|
|
6785
|
+
* HuggingFace API (slower).
|
|
6786
|
+
* - Single-file formats (no `isDirectory`, e.g. OpenVINO IR): sibling
|
|
6787
|
+
* files fetched from the SAME remote directory as `url` and stored flat
|
|
6788
|
+
* alongside the main file — e.g. `['camstack-yolov9t.bin']` for the IR
|
|
6789
|
+
* weights next to `camstack-yolov9t.xml`.
|
|
6790
|
+
*/
|
|
6791
|
+
var ModelFormatEntrySchema = object({
|
|
6792
|
+
url: string(),
|
|
6793
|
+
sizeMB: number(),
|
|
6794
|
+
/** Whether this format is a directory bundle (e.g., .mlpackage) rather than a single file */
|
|
6795
|
+
isDirectory: boolean().optional(),
|
|
6796
|
+
/** Multi-file payload (directory members or sibling files). */
|
|
6797
|
+
files: array(string()).readonly().optional(),
|
|
6798
|
+
/** Runtime(s) that can use this format. If omitted, inferred from ModelFormat key */
|
|
6799
|
+
runtimes: array(_enum(["node", "python"])).readonly().optional()
|
|
6800
|
+
});
|
|
6801
|
+
/**
|
|
6802
|
+
* Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
|
|
6803
|
+
* The downloader fetches from `url` and saves to `{modelsDir}/{filename}`.
|
|
6804
|
+
*/
|
|
6805
|
+
var ModelExtraFileSchema = object({
|
|
6806
|
+
url: string(),
|
|
6807
|
+
filename: string(),
|
|
6808
|
+
sizeMB: number()
|
|
6809
|
+
});
|
|
6810
|
+
/**
|
|
6811
|
+
* Per-format payload map. Modelled as an explicit object (one optional key
|
|
6812
|
+
* per `ModelFormat`) rather than `z.record(enum, …)` — zod v4's enum-keyed
|
|
6813
|
+
* record requires every key, but a catalog entry only ships a subset of
|
|
6814
|
+
* formats.
|
|
6815
|
+
*/
|
|
6816
|
+
var ModelFormatsSchema = object({
|
|
6817
|
+
onnx: ModelFormatEntrySchema.optional(),
|
|
6818
|
+
coreml: ModelFormatEntrySchema.optional(),
|
|
6819
|
+
openvino: ModelFormatEntrySchema.optional(),
|
|
6820
|
+
tflite: ModelFormatEntrySchema.optional(),
|
|
6821
|
+
pt: ModelFormatEntrySchema.optional()
|
|
6822
|
+
});
|
|
6823
|
+
var ModelCatalogEntrySchema = object({
|
|
6824
|
+
id: string(),
|
|
6825
|
+
name: string(),
|
|
6826
|
+
description: string(),
|
|
6827
|
+
formats: ModelFormatsSchema,
|
|
6828
|
+
inputSize: object({
|
|
6829
|
+
width: number(),
|
|
6830
|
+
height: number()
|
|
6831
|
+
}),
|
|
6832
|
+
labels: array(LabelDefinitionSchema).readonly(),
|
|
6833
|
+
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6834
|
+
inputNormalization: _enum([
|
|
6835
|
+
"zero-one",
|
|
6836
|
+
"imagenet",
|
|
6837
|
+
"none"
|
|
6838
|
+
]).optional(),
|
|
6839
|
+
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6840
|
+
/**
|
|
6841
|
+
* When true, the executor produces a landmark-aligned crop (similarity warp
|
|
6842
|
+
* onto the canonical template) before this step runs, instead of a plain
|
|
6843
|
+
* axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
|
|
6844
|
+
* their embeddings are only discriminative on an aligned input. The face
|
|
6845
|
+
* detector that produced the parent detail must emit 5 landmarks.
|
|
6846
|
+
*/
|
|
6847
|
+
faceAlignment: boolean().optional(),
|
|
6848
|
+
/**
|
|
6849
|
+
* Auxiliary files required at runtime (labels JSON, charset dict, etc.).
|
|
6850
|
+
* Downloaded into the same modelsDir alongside the model file.
|
|
6851
|
+
*/
|
|
6852
|
+
extraFiles: array(ModelExtraFileSchema).readonly().optional()
|
|
6853
|
+
});
|
|
6854
|
+
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
6855
|
+
format: literal("openvino"),
|
|
6856
|
+
precisions: array(_enum(["fp16", "int8"])).min(1).readonly()
|
|
6857
|
+
}), object({ format: literal("coreml") })]);
|
|
6858
|
+
var ModelConvertMetadataSchema = object({
|
|
6859
|
+
id: string().regex(/^[a-zA-Z0-9._-]+$/),
|
|
6860
|
+
name: string(),
|
|
6861
|
+
labels: array(LabelDefinitionSchema).readonly(),
|
|
6862
|
+
inputSize: object({
|
|
6863
|
+
width: number(),
|
|
6864
|
+
height: number()
|
|
6865
|
+
}),
|
|
6866
|
+
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6867
|
+
inputNormalization: _enum([
|
|
6868
|
+
"zero-one",
|
|
6869
|
+
"imagenet",
|
|
6870
|
+
"none"
|
|
6871
|
+
]).optional(),
|
|
6872
|
+
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6873
|
+
outputFormat: _enum([
|
|
6874
|
+
"yolo",
|
|
6875
|
+
"ssd",
|
|
6876
|
+
"embedding",
|
|
6877
|
+
"classification",
|
|
6878
|
+
"ocr",
|
|
6879
|
+
"segmentation"
|
|
6880
|
+
]),
|
|
6881
|
+
faceAlignment: boolean().optional()
|
|
6882
|
+
});
|
|
6883
|
+
var ConvertResultSchema = object({
|
|
6884
|
+
entry: ModelCatalogEntrySchema,
|
|
6885
|
+
artifacts: array(object({
|
|
6886
|
+
format: _enum(MODEL_FORMATS),
|
|
6887
|
+
precision: _enum(["fp16", "int8"]).optional(),
|
|
6888
|
+
sizeMB: number(),
|
|
6889
|
+
validated: boolean(),
|
|
6890
|
+
files: array(string()).readonly()
|
|
6891
|
+
})).readonly()
|
|
6892
|
+
});
|
|
6893
|
+
/**
|
|
6767
6894
|
* Numeric day-of-week: 0 = Sunday … 6 = Saturday (matches `Date.getDay`).
|
|
6768
6895
|
* Named `RecordingWeekday` to avoid collision with the string-union
|
|
6769
6896
|
* `Weekday` exported from `interfaces/timezones.ts`.
|
|
@@ -13667,6 +13794,54 @@ var EnrichedWidgetMetadataSchema = WidgetMetadataSchema.extend({
|
|
|
13667
13794
|
bundleUrl: string()
|
|
13668
13795
|
});
|
|
13669
13796
|
method(_void(), array(EnrichedWidgetMetadataSchema).readonly());
|
|
13797
|
+
/**
|
|
13798
|
+
* `custom-model-registry` — collection cap exposing operator-registered
|
|
13799
|
+
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
13800
|
+
* contributes a list of `CustomModelDescriptor`s; the hub auto-concatenates
|
|
13801
|
+
* them across providers (`concatCollection`).
|
|
13802
|
+
*
|
|
13803
|
+
* The detection-pipeline is *aware* of this cap: when at least one provider
|
|
13804
|
+
* exists it unions these descriptors into the per-step model picker and the
|
|
13805
|
+
* runtime model-resolution path, alongside the static catalog. When no
|
|
13806
|
+
* provider exists the consumer no-ops entirely (identical to the catalog-only
|
|
13807
|
+
* behaviour).
|
|
13808
|
+
*
|
|
13809
|
+
* A descriptor carries a full `ModelCatalogEntry` directly — the same shape
|
|
13810
|
+
* the static catalog uses — so the existing download/resolution code consumes
|
|
13811
|
+
* it unchanged. `stepId` is the detection step the model targets
|
|
13812
|
+
* (e.g. `'object-detection'`).
|
|
13813
|
+
*/
|
|
13814
|
+
var CustomModelDescriptorSchema = object({
|
|
13815
|
+
stepId: string(),
|
|
13816
|
+
entry: ModelCatalogEntrySchema
|
|
13817
|
+
});
|
|
13818
|
+
method(_void(), array(CustomModelDescriptorSchema).readonly());
|
|
13819
|
+
method(object({
|
|
13820
|
+
nodeId: string(),
|
|
13821
|
+
modelId: string(),
|
|
13822
|
+
format: _enum(MODEL_FORMATS),
|
|
13823
|
+
entry: ModelCatalogEntrySchema
|
|
13824
|
+
}), object({
|
|
13825
|
+
ok: boolean(),
|
|
13826
|
+
/** sha256 of the staged tarball (empty for a hub-local no-op). */
|
|
13827
|
+
sha256: string(),
|
|
13828
|
+
bytes: number(),
|
|
13829
|
+
/** The target node's modelsDir the artifact landed in. */
|
|
13830
|
+
path: string()
|
|
13831
|
+
}), {
|
|
13832
|
+
kind: "mutation",
|
|
13833
|
+
auth: "admin"
|
|
13834
|
+
});
|
|
13835
|
+
method(object({
|
|
13836
|
+
sourceUrl: string(),
|
|
13837
|
+
metadata: ModelConvertMetadataSchema,
|
|
13838
|
+
targets: array(ConvertTargetSchema).min(1).readonly(),
|
|
13839
|
+
calibrationRef: string().optional(),
|
|
13840
|
+
sessionId: string().optional()
|
|
13841
|
+
}), ConvertResultSchema, {
|
|
13842
|
+
kind: "mutation",
|
|
13843
|
+
auth: "admin"
|
|
13844
|
+
});
|
|
13670
13845
|
var AddonHttpRouteSchema = object({
|
|
13671
13846
|
method: _enum([
|
|
13672
13847
|
"GET",
|
|
@@ -18916,6 +19091,12 @@ Object.freeze({
|
|
|
18916
19091
|
addonId: null,
|
|
18917
19092
|
access: "create"
|
|
18918
19093
|
},
|
|
19094
|
+
"customModelRegistry.listModels": {
|
|
19095
|
+
capName: "custom-model-registry",
|
|
19096
|
+
capScope: "system",
|
|
19097
|
+
addonId: null,
|
|
19098
|
+
access: "view"
|
|
19099
|
+
},
|
|
18919
19100
|
"decoder.createSession": {
|
|
18920
19101
|
capName: "decoder",
|
|
18921
19102
|
capScope: "system",
|
|
@@ -20140,6 +20321,18 @@ Object.freeze({
|
|
|
20140
20321
|
addonId: null,
|
|
20141
20322
|
access: "view"
|
|
20142
20323
|
},
|
|
20324
|
+
"modelConvert.convert": {
|
|
20325
|
+
capName: "model-convert",
|
|
20326
|
+
capScope: "system",
|
|
20327
|
+
addonId: null,
|
|
20328
|
+
access: "create"
|
|
20329
|
+
},
|
|
20330
|
+
"modelDistributor.distributeModel": {
|
|
20331
|
+
capName: "model-distributor",
|
|
20332
|
+
capScope: "system",
|
|
20333
|
+
addonId: null,
|
|
20334
|
+
access: "create"
|
|
20335
|
+
},
|
|
20143
20336
|
"motion.isDetected": {
|
|
20144
20337
|
capName: "motion",
|
|
20145
20338
|
capScope: "device",
|