@camstack/addon-pipeline 1.0.8 → 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 +685 -577
- package/dist/detection-pipeline/index.mjs +673 -565
- 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-C7AjBsX9-B0ekM6dF.mjs → model-download-service-C-IHWnXx-3Mmeob3l.mjs} +36 -6
- package/dist/{model-download-service-C7AjBsX9-rXY-VFDk.js → model-download-service-C-IHWnXx-BnQ_awK4.js} +36 -6
- 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-BFy9iszl.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-zRy9SzlX.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/inference_pool.py +65 -6
- 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
- package/python/__pycache__/inference_pool.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/__init__.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/_safety.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/arcface.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/arcface.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/ctc.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/ctc.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/saliency.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/saliency.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/scrfd.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/scrfd.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/softmax.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/softmax.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yamnet.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yamnet.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo_seg.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo_seg.cpython-313.pyc +0 -0
|
@@ -1,11 +1,255 @@
|
|
|
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";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
8
|
import sharp from "sharp";
|
|
9
|
+
//#region src/detection-pipeline/engine-store-keys.ts
|
|
10
|
+
/**
|
|
11
|
+
* Per-node scoping for the detection-pipeline engine cascade.
|
|
12
|
+
*
|
|
13
|
+
* The detection addon's settings store is a single CLUSTER-SHARED blob (the
|
|
14
|
+
* settings-store cap is hub-resident; every node's detection instance reads and
|
|
15
|
+
* writes the same keys). That is correct for node-agnostic settings (pipeline
|
|
16
|
+
* steps, tuning) but WRONG for the engine cascade — `engineBackend` /
|
|
17
|
+
* `engineDevice` / `probedBestEngine` are hardware-specific, so the hub (NPU)
|
|
18
|
+
* and a remote agent (iGPU, or no accelerator at all) must hold INDEPENDENT
|
|
19
|
+
* selections. Sharing them lets one node's pick (e.g. `openvino/npu`) override
|
|
20
|
+
* another node that has no NPU.
|
|
21
|
+
*
|
|
22
|
+
* These three keys are therefore persisted node-scoped as `<key>@<nodeId>`.
|
|
23
|
+
* Everything else in the store stays shared. A legacy un-scoped value (written
|
|
24
|
+
* before this change, or by an older build) is read as a migration fallback and
|
|
25
|
+
* re-persisted under the node-scoped key on the next write.
|
|
26
|
+
*/
|
|
27
|
+
var ENGINE_CASCADE_KEYS = [
|
|
28
|
+
"engineBackend",
|
|
29
|
+
"engineDevice",
|
|
30
|
+
"probedBestEngine"
|
|
31
|
+
];
|
|
32
|
+
function isEngineCascadeKey(key) {
|
|
33
|
+
return ENGINE_CASCADE_KEYS.includes(key);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Normalise a raw kernel node id to the bare node id used for scoping.
|
|
37
|
+
* `localNodeId` can carry a `<node>/<addon>` suffix; the engine selection is
|
|
38
|
+
* per-NODE, so strip the addon segment. Falls back to `hub`.
|
|
39
|
+
*/
|
|
40
|
+
function normalizeEngineNodeId(rawNodeId) {
|
|
41
|
+
const raw = rawNodeId ?? "hub";
|
|
42
|
+
return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
|
|
43
|
+
}
|
|
44
|
+
/** The node-scoped store key for an engine cascade field. */
|
|
45
|
+
function nodeEngineKey(base, nodeId) {
|
|
46
|
+
return `${base}@${normalizeEngineNodeId(nodeId)}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Read an engine cascade value for a node: the node-scoped key if present,
|
|
50
|
+
* otherwise the legacy un-scoped value (migration), otherwise undefined.
|
|
51
|
+
*/
|
|
52
|
+
function readNodeEngineValue(store, base, nodeId) {
|
|
53
|
+
const scoped = store[nodeEngineKey(base, nodeId)];
|
|
54
|
+
return scoped !== void 0 ? scoped : store[base];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Project a raw store onto the plain engine cascade keys for THIS node, so the
|
|
58
|
+
* UI schema (whose field keys are the bare `engineBackend` etc.) hydrates from
|
|
59
|
+
* the node's own selection. Non-engine keys are left untouched. Node-scoped
|
|
60
|
+
* keys for OTHER nodes are dropped from the projection (not relevant to this
|
|
61
|
+
* node's form).
|
|
62
|
+
*/
|
|
63
|
+
function projectNodeEngine(store, nodeId) {
|
|
64
|
+
const out = {};
|
|
65
|
+
const scopedForAnyNode = /* @__PURE__ */ new Set();
|
|
66
|
+
for (const key of Object.keys(store)) {
|
|
67
|
+
const atIdx = key.indexOf("@");
|
|
68
|
+
if (isEngineCascadeKey(atIdx >= 0 ? key.slice(0, atIdx) : key)) {
|
|
69
|
+
scopedForAnyNode.add(key);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
out[key] = store[key];
|
|
73
|
+
}
|
|
74
|
+
for (const base of ENGINE_CASCADE_KEYS) {
|
|
75
|
+
const value = readNodeEngineValue(store, base, nodeId);
|
|
76
|
+
if (value !== void 0) out[base] = value;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/detection-pipeline/runtimes.ts
|
|
82
|
+
var KNOWN_PLATFORMS = [
|
|
83
|
+
"darwin",
|
|
84
|
+
"linux",
|
|
85
|
+
"win32"
|
|
86
|
+
];
|
|
87
|
+
var KNOWN_ARCHES = ["arm64", "x64"];
|
|
88
|
+
var KNOWN_GPU_TYPES = [
|
|
89
|
+
"nvidia",
|
|
90
|
+
"amd",
|
|
91
|
+
"intel",
|
|
92
|
+
"apple"
|
|
93
|
+
];
|
|
94
|
+
var KNOWN_NPU_TYPES = ["apple-ane", "intel-npu"];
|
|
95
|
+
function toKnownPlatform(p) {
|
|
96
|
+
return KNOWN_PLATFORMS.find((v) => v === p) ?? "linux";
|
|
97
|
+
}
|
|
98
|
+
function toKnownArch(a) {
|
|
99
|
+
return KNOWN_ARCHES.find((v) => v === a) ?? "x64";
|
|
100
|
+
}
|
|
101
|
+
function gpuInfoFrom(hw) {
|
|
102
|
+
if (!hw.gpu) return null;
|
|
103
|
+
const type = KNOWN_GPU_TYPES.find((v) => v === hw.gpu?.type);
|
|
104
|
+
if (!type) return null;
|
|
105
|
+
return {
|
|
106
|
+
type,
|
|
107
|
+
name: ""
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function npuInfoFrom(hw) {
|
|
111
|
+
if (!hw.npu) return null;
|
|
112
|
+
const type = KNOWN_NPU_TYPES.find((v) => v === hw.npu?.type);
|
|
113
|
+
if (!type) return null;
|
|
114
|
+
return { type };
|
|
115
|
+
}
|
|
116
|
+
function envToHardwareInfo(env) {
|
|
117
|
+
if (!env.hardware) return null;
|
|
118
|
+
return {
|
|
119
|
+
platform: toKnownPlatform(env.platform),
|
|
120
|
+
arch: toKnownArch(env.arch),
|
|
121
|
+
cpuModel: "",
|
|
122
|
+
cpuCores: 0,
|
|
123
|
+
totalRAM_MB: 0,
|
|
124
|
+
availableRAM_MB: 0,
|
|
125
|
+
gpu: gpuInfoFrom(env.hardware),
|
|
126
|
+
npu: npuInfoFrom(env.hardware)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function probedToHardwareInfo(hw) {
|
|
130
|
+
if (!hw) return null;
|
|
131
|
+
return {
|
|
132
|
+
platform: toKnownPlatform(process.platform),
|
|
133
|
+
arch: toKnownArch(process.arch),
|
|
134
|
+
cpuModel: "",
|
|
135
|
+
cpuCores: 0,
|
|
136
|
+
totalRAM_MB: 0,
|
|
137
|
+
availableRAM_MB: 0,
|
|
138
|
+
gpu: gpuInfoFrom(hw),
|
|
139
|
+
npu: npuInfoFrom(hw)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
var RUNTIME_DETAIL = {
|
|
143
|
+
onnx: {
|
|
144
|
+
label: "ONNX Runtime",
|
|
145
|
+
pythonRequirements: ["requirements.txt", "requirements-onnxruntime.txt"],
|
|
146
|
+
tuning: {
|
|
147
|
+
concurrency: 4,
|
|
148
|
+
batchMode: "list",
|
|
149
|
+
maxBatchSize: 8,
|
|
150
|
+
intraOpThreads: 0
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
openvino: {
|
|
154
|
+
label: "OpenVINO",
|
|
155
|
+
pythonRequirements: ["requirements.txt", "requirements-openvino.txt"],
|
|
156
|
+
tuning: {
|
|
157
|
+
concurrency: 1,
|
|
158
|
+
batchMode: "none",
|
|
159
|
+
numStreams: 0
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
coreml: {
|
|
163
|
+
label: "CoreML",
|
|
164
|
+
pythonRequirements: ["requirements.txt", "requirements-coreml.txt"],
|
|
165
|
+
tuning: {
|
|
166
|
+
concurrency: 1,
|
|
167
|
+
batchMode: "none",
|
|
168
|
+
windowMs: 8,
|
|
169
|
+
maxBatchSize: 8,
|
|
170
|
+
numWorkers: 1
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Returns the list of supported runtime IDs for the given hardware env.
|
|
176
|
+
* Delegates to `@camstack/types` `supportedRuntimes`.
|
|
177
|
+
*/
|
|
178
|
+
function supportedRuntimes(env) {
|
|
179
|
+
return supportedRuntimes$1(envToHardwareInfo(env));
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Returns the device options for a given runtime and probed hardware.
|
|
183
|
+
* Delegates to `@camstack/types` `runtimeDevices`.
|
|
184
|
+
*/
|
|
185
|
+
function runtimeDevices(id, hardware) {
|
|
186
|
+
return runtimeDevices$1(id, probedToHardwareInfo(hardware));
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Returns the default device string for a given runtime.
|
|
190
|
+
* Delegates to `@camstack/types` `defaultDeviceFor`.
|
|
191
|
+
*/
|
|
192
|
+
function defaultDeviceFor(id) {
|
|
193
|
+
return defaultDeviceFor$1(id);
|
|
194
|
+
}
|
|
195
|
+
/** Model format for each inference runtime supported by the detection pipeline. */
|
|
196
|
+
var RUNTIME_FORMAT = {
|
|
197
|
+
onnx: "onnx",
|
|
198
|
+
openvino: "openvino",
|
|
199
|
+
coreml: "coreml"
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Returns the model format required for a given runtime.
|
|
203
|
+
* Returns the locally-typed format string ('onnx' | 'openvino' | 'coreml')
|
|
204
|
+
* matching the engine-provisioner's own ModelFormat type.
|
|
205
|
+
*/
|
|
206
|
+
function modelFormatFor(id) {
|
|
207
|
+
return RUNTIME_FORMAT[id];
|
|
208
|
+
}
|
|
209
|
+
function pythonRequirementsFor(id) {
|
|
210
|
+
return RUNTIME_DETAIL[id].pythonRequirements;
|
|
211
|
+
}
|
|
212
|
+
function tuningFor(id) {
|
|
213
|
+
return RUNTIME_DETAIL[id].tuning;
|
|
214
|
+
}
|
|
215
|
+
function runtimeLabel(id) {
|
|
216
|
+
return RUNTIME_DETAIL[id].label;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Proactive-install hint kept for back-compat (re-exported from index.ts).
|
|
220
|
+
* Intel on Linux warrants installing openvino; coremltools covers macOS;
|
|
221
|
+
* openvino has no AMD/NVIDIA backend.
|
|
222
|
+
*/
|
|
223
|
+
function shouldInstallOpenvino(env) {
|
|
224
|
+
if (env.platform === "darwin") return false;
|
|
225
|
+
return env.gpu?.type === "intel" || env.npu?.type === "intel-npu";
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/detection-pipeline/auto-pick.ts
|
|
229
|
+
var PREFERENCE = [
|
|
230
|
+
"coreml",
|
|
231
|
+
"openvino",
|
|
232
|
+
"onnx"
|
|
233
|
+
];
|
|
234
|
+
/**
|
|
235
|
+
* Pure function — picks the best supported runtime for the given hardware env.
|
|
236
|
+
*
|
|
237
|
+
* Logic:
|
|
238
|
+
* 1. If `bestBackendHint` is in the supported set, use it.
|
|
239
|
+
* 2. Otherwise, walk PREFERENCE order and pick the first supported runtime.
|
|
240
|
+
* 3. Floor to `'onnx'` (always supported).
|
|
241
|
+
*
|
|
242
|
+
* Device is always `defaultDeviceFor(chosen)`.
|
|
243
|
+
*/
|
|
244
|
+
function pickBestRuntime(env, bestBackendHint) {
|
|
245
|
+
const supported = supportedRuntimes(env);
|
|
246
|
+
const chosen = supported.find((id) => id === bestBackendHint) ?? PREFERENCE.find((id) => supported.includes(id)) ?? "onnx";
|
|
247
|
+
return {
|
|
248
|
+
runtimeId: chosen,
|
|
249
|
+
device: defaultDeviceFor(chosen)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
9
253
|
//#region src/detection-pipeline/engine/shared-inference-pool.ts
|
|
10
254
|
var MSG_COMMAND = 0;
|
|
11
255
|
var MSG_INFER_JPEG = 1;
|
|
@@ -772,6 +1016,46 @@ var HF_REPO = "camstack/camstack-models";
|
|
|
772
1016
|
var HF_SCRYPTED = "scrypted/plugin-models";
|
|
773
1017
|
var hf = (path) => hfModelUrl(HF_REPO, path);
|
|
774
1018
|
var hfScrypted = (path) => hfModelUrl(HF_SCRYPTED, path);
|
|
1019
|
+
/**
|
|
1020
|
+
* Build an OpenVINO format entry (always python runtime).
|
|
1021
|
+
*
|
|
1022
|
+
* OpenVINO IR is a two-file bundle: a `.xml` topology + a sibling `.bin`
|
|
1023
|
+
* weights file with the same basename. We declare the `.bin` in `files` so
|
|
1024
|
+
* the (format-agnostic) downloader fetches it alongside the `.xml` — without
|
|
1025
|
+
* the weights, OpenVINO compile fails with "Empty weights data in bin file".
|
|
1026
|
+
* A plain `.onnx` run through the OpenVINO runtime (e.g. yamnet) has no
|
|
1027
|
+
* sibling, so none is added.
|
|
1028
|
+
*/
|
|
1029
|
+
var ovFormat = (url, sizeMB) => {
|
|
1030
|
+
const base = url.split("/").pop() ?? "";
|
|
1031
|
+
const files = base.endsWith(".xml") ? [base.replace(/\.xml$/, ".bin")] : void 0;
|
|
1032
|
+
return {
|
|
1033
|
+
url,
|
|
1034
|
+
sizeMB,
|
|
1035
|
+
runtimes: ["python"],
|
|
1036
|
+
...files ? { files } : {}
|
|
1037
|
+
};
|
|
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
|
+
});
|
|
775
1059
|
var MLPACKAGE_FILES = [
|
|
776
1060
|
"Manifest.json",
|
|
777
1061
|
"Data/com.apple.CoreML/model.mlmodel",
|
|
@@ -800,11 +1084,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
800
1084
|
files: [...MLPACKAGE_FILES],
|
|
801
1085
|
runtimes: ["python"]
|
|
802
1086
|
},
|
|
803
|
-
openvino:
|
|
804
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9t.xml"),
|
|
805
|
-
sizeMB: 6,
|
|
806
|
-
runtimes: ["python"]
|
|
807
|
-
}
|
|
1087
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9t.xml"), 6)
|
|
808
1088
|
}
|
|
809
1089
|
},
|
|
810
1090
|
{
|
|
@@ -829,11 +1109,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
829
1109
|
files: [...MLPACKAGE_FILES],
|
|
830
1110
|
runtimes: ["python"]
|
|
831
1111
|
},
|
|
832
|
-
openvino:
|
|
833
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9s.xml"),
|
|
834
|
-
sizeMB: 16,
|
|
835
|
-
runtimes: ["python"]
|
|
836
|
-
}
|
|
1112
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9s.xml"), 16)
|
|
837
1113
|
}
|
|
838
1114
|
},
|
|
839
1115
|
{
|
|
@@ -858,11 +1134,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
858
1134
|
files: [...MLPACKAGE_FILES],
|
|
859
1135
|
runtimes: ["python"]
|
|
860
1136
|
},
|
|
861
|
-
openvino:
|
|
862
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9c.xml"),
|
|
863
|
-
sizeMB: 49,
|
|
864
|
-
runtimes: ["python"]
|
|
865
|
-
}
|
|
1137
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9c.xml"), 49)
|
|
866
1138
|
}
|
|
867
1139
|
},
|
|
868
1140
|
{
|
|
@@ -887,11 +1159,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
887
1159
|
files: [...MLPACKAGE_FILES],
|
|
888
1160
|
runtimes: ["python"]
|
|
889
1161
|
},
|
|
890
|
-
openvino:
|
|
891
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26n.xml"),
|
|
892
|
-
sizeMB: 9,
|
|
893
|
-
runtimes: ["python"]
|
|
894
|
-
}
|
|
1162
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26n.xml"), 9)
|
|
895
1163
|
}
|
|
896
1164
|
},
|
|
897
1165
|
{
|
|
@@ -916,11 +1184,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
916
1184
|
files: [...MLPACKAGE_FILES],
|
|
917
1185
|
runtimes: ["python"]
|
|
918
1186
|
},
|
|
919
|
-
openvino:
|
|
920
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26s.xml"),
|
|
921
|
-
sizeMB: 36,
|
|
922
|
-
runtimes: ["python"]
|
|
923
|
-
}
|
|
1187
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26s.xml"), 36)
|
|
924
1188
|
}
|
|
925
1189
|
},
|
|
926
1190
|
{
|
|
@@ -945,11 +1209,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
945
1209
|
files: [...MLPACKAGE_FILES],
|
|
946
1210
|
runtimes: ["python"]
|
|
947
1211
|
},
|
|
948
|
-
openvino:
|
|
949
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26m.xml"),
|
|
950
|
-
sizeMB: 78,
|
|
951
|
-
runtimes: ["python"]
|
|
952
|
-
}
|
|
1212
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26m.xml"), 78)
|
|
953
1213
|
}
|
|
954
1214
|
},
|
|
955
1215
|
{
|
|
@@ -974,11 +1234,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
974
1234
|
files: [...MLPACKAGE_FILES],
|
|
975
1235
|
runtimes: ["python"]
|
|
976
1236
|
},
|
|
977
|
-
openvino:
|
|
978
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26l.xml"),
|
|
979
|
-
sizeMB: 95,
|
|
980
|
-
runtimes: ["python"]
|
|
981
|
-
}
|
|
1237
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26l.xml"), 95)
|
|
982
1238
|
}
|
|
983
1239
|
},
|
|
984
1240
|
{
|
|
@@ -1003,11 +1259,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1003
1259
|
files: [...MLPACKAGE_FILES],
|
|
1004
1260
|
runtimes: ["python"]
|
|
1005
1261
|
},
|
|
1006
|
-
openvino:
|
|
1007
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26x.xml"),
|
|
1008
|
-
sizeMB: 213,
|
|
1009
|
-
runtimes: ["python"]
|
|
1010
|
-
}
|
|
1262
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26x.xml"), 213)
|
|
1011
1263
|
}
|
|
1012
1264
|
},
|
|
1013
1265
|
{
|
|
@@ -1032,11 +1284,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1032
1284
|
files: [...MLPACKAGE_FILES],
|
|
1033
1285
|
runtimes: ["python"]
|
|
1034
1286
|
},
|
|
1035
|
-
openvino:
|
|
1036
|
-
url: hfScrypted("openvino/scrypted_yolov9t_relu/best.xml"),
|
|
1037
|
-
sizeMB: 6,
|
|
1038
|
-
runtimes: ["python"]
|
|
1039
|
-
}
|
|
1287
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9t_relu.xml"), 6)
|
|
1040
1288
|
}
|
|
1041
1289
|
},
|
|
1042
1290
|
{
|
|
@@ -1061,11 +1309,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1061
1309
|
files: [...MLPACKAGE_FILES],
|
|
1062
1310
|
runtimes: ["python"]
|
|
1063
1311
|
},
|
|
1064
|
-
openvino:
|
|
1065
|
-
url: hfScrypted("openvino/scrypted_yolov9s_relu/best.xml"),
|
|
1066
|
-
sizeMB: 16,
|
|
1067
|
-
runtimes: ["python"]
|
|
1068
|
-
}
|
|
1312
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9s_relu.xml"), 16)
|
|
1069
1313
|
}
|
|
1070
1314
|
},
|
|
1071
1315
|
{
|
|
@@ -1090,11 +1334,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1090
1334
|
files: [...MLPACKAGE_FILES],
|
|
1091
1335
|
runtimes: ["python"]
|
|
1092
1336
|
},
|
|
1093
|
-
openvino:
|
|
1094
|
-
url: hfScrypted("openvino/scrypted_yolov9c_relu/best.xml"),
|
|
1095
|
-
sizeMB: 49,
|
|
1096
|
-
runtimes: ["python"]
|
|
1097
|
-
}
|
|
1337
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9c_relu.xml"), 49)
|
|
1098
1338
|
}
|
|
1099
1339
|
},
|
|
1100
1340
|
{
|
|
@@ -1119,13 +1359,23 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1119
1359
|
files: [...MLPACKAGE_FILES],
|
|
1120
1360
|
runtimes: ["python"]
|
|
1121
1361
|
},
|
|
1122
|
-
openvino:
|
|
1123
|
-
url: hfScrypted("openvino/scrypted_yolov9m_relu/best.xml"),
|
|
1124
|
-
sizeMB: 38,
|
|
1125
|
-
runtimes: ["python"]
|
|
1126
|
-
}
|
|
1362
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9m_relu.xml"), 38)
|
|
1127
1363
|
}
|
|
1128
|
-
}
|
|
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)
|
|
1129
1379
|
];
|
|
1130
1380
|
var FACE_DETECTION_MODELS = [{
|
|
1131
1381
|
id: "scrfd-2.5g",
|
|
@@ -1152,11 +1402,7 @@ var FACE_DETECTION_MODELS = [{
|
|
|
1152
1402
|
files: [...MLPACKAGE_FILES],
|
|
1153
1403
|
runtimes: ["python"]
|
|
1154
1404
|
},
|
|
1155
|
-
openvino:
|
|
1156
|
-
url: hf("faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"),
|
|
1157
|
-
sizeMB: 1.8,
|
|
1158
|
-
runtimes: ["python"]
|
|
1159
|
-
}
|
|
1405
|
+
openvino: ovFormat(hf("faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"), 1.8)
|
|
1160
1406
|
}
|
|
1161
1407
|
}, {
|
|
1162
1408
|
id: "scrypted-yolov9t-face",
|
|
@@ -1183,11 +1429,7 @@ var FACE_DETECTION_MODELS = [{
|
|
|
1183
1429
|
files: [...MLPACKAGE_FILES],
|
|
1184
1430
|
runtimes: ["python"]
|
|
1185
1431
|
},
|
|
1186
|
-
openvino:
|
|
1187
|
-
url: hfScrypted("openvino/scrypted_yolov9t_relu_face/best.xml"),
|
|
1188
|
-
sizeMB: 6,
|
|
1189
|
-
runtimes: ["python"]
|
|
1190
|
-
}
|
|
1432
|
+
openvino: ovFormat(hf("faceDetection/scrypted-yolov9-face/openvino/scrypted_yolov9t_relu_face.xml"), 6)
|
|
1191
1433
|
}
|
|
1192
1434
|
}];
|
|
1193
1435
|
var FACE_EMBEDDING_MODELS = [{
|
|
@@ -1217,11 +1459,7 @@ var FACE_EMBEDDING_MODELS = [{
|
|
|
1217
1459
|
files: [...MLPACKAGE_FILES],
|
|
1218
1460
|
runtimes: ["python"]
|
|
1219
1461
|
},
|
|
1220
|
-
openvino:
|
|
1221
|
-
url: hf("faceRecognition/arcface/openvino/camstack-arcface-r100.xml"),
|
|
1222
|
-
sizeMB: 65,
|
|
1223
|
-
runtimes: ["python"]
|
|
1224
|
-
}
|
|
1462
|
+
openvino: ovFormat(hf("faceRecognition/arcface/openvino/camstack-arcface-r100.xml"), 65)
|
|
1225
1463
|
}
|
|
1226
1464
|
}, {
|
|
1227
1465
|
id: "inception-resnet-v1",
|
|
@@ -1248,11 +1486,7 @@ var FACE_EMBEDDING_MODELS = [{
|
|
|
1248
1486
|
files: [...MLPACKAGE_FILES],
|
|
1249
1487
|
runtimes: ["python"]
|
|
1250
1488
|
},
|
|
1251
|
-
openvino:
|
|
1252
|
-
url: hfScrypted("openvino/inception_resnet_v1/best.xml"),
|
|
1253
|
-
sizeMB: 45,
|
|
1254
|
-
runtimes: ["python"]
|
|
1255
|
-
}
|
|
1489
|
+
openvino: ovFormat(hf("faceRecognition/inception-resnet-v1/openvino/camstack-inception-resnet-v1.xml"), 45)
|
|
1256
1490
|
}
|
|
1257
1491
|
}];
|
|
1258
1492
|
var PLATE_DETECTION_MODELS = [{
|
|
@@ -1280,11 +1514,7 @@ var PLATE_DETECTION_MODELS = [{
|
|
|
1280
1514
|
files: [...MLPACKAGE_FILES],
|
|
1281
1515
|
runtimes: ["python"]
|
|
1282
1516
|
},
|
|
1283
|
-
openvino:
|
|
1284
|
-
url: hf("plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"),
|
|
1285
|
-
sizeMB: 6.1,
|
|
1286
|
-
runtimes: ["python"]
|
|
1287
|
-
}
|
|
1517
|
+
openvino: ovFormat(hf("plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"), 6.1)
|
|
1288
1518
|
}
|
|
1289
1519
|
}];
|
|
1290
1520
|
var PLATE_OCR_MODELS = [{
|
|
@@ -1312,11 +1542,7 @@ var PLATE_OCR_MODELS = [{
|
|
|
1312
1542
|
files: [...MLPACKAGE_FILES],
|
|
1313
1543
|
runtimes: ["python"]
|
|
1314
1544
|
},
|
|
1315
|
-
openvino:
|
|
1316
|
-
url: hfScrypted("openvino/vgg_english_g2/best.xml"),
|
|
1317
|
-
sizeMB: 7.2,
|
|
1318
|
-
runtimes: ["python"]
|
|
1319
|
-
}
|
|
1545
|
+
openvino: ovFormat(hf("plateRecognition/vgg_english_g2/openvino/vgg_english_g2.xml"), 7.2)
|
|
1320
1546
|
}
|
|
1321
1547
|
}];
|
|
1322
1548
|
var ANIMAL_CLASSIFIER_MODELS = [{
|
|
@@ -1345,11 +1571,7 @@ var ANIMAL_CLASSIFIER_MODELS = [{
|
|
|
1345
1571
|
files: [...MLPACKAGE_FILES],
|
|
1346
1572
|
runtimes: ["python"]
|
|
1347
1573
|
},
|
|
1348
|
-
openvino:
|
|
1349
|
-
url: hf("animalClassification/animals-10/openvino/camstack-animals-10.xml"),
|
|
1350
|
-
sizeMB: 164,
|
|
1351
|
-
runtimes: ["python"]
|
|
1352
|
-
}
|
|
1574
|
+
openvino: ovFormat(hf("animalClassification/animals-10/openvino/camstack-animals-10.xml"), 164)
|
|
1353
1575
|
}
|
|
1354
1576
|
}];
|
|
1355
1577
|
var BIRD_CLASSIFIER_MODELS = [{
|
|
@@ -1378,11 +1600,7 @@ var BIRD_CLASSIFIER_MODELS = [{
|
|
|
1378
1600
|
files: [...MLPACKAGE_FILES],
|
|
1379
1601
|
runtimes: ["python"]
|
|
1380
1602
|
},
|
|
1381
|
-
openvino:
|
|
1382
|
-
url: hf("animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml"),
|
|
1383
|
-
sizeMB: 47,
|
|
1384
|
-
runtimes: ["python"]
|
|
1385
|
-
}
|
|
1603
|
+
openvino: ovFormat(hf("animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml"), 47)
|
|
1386
1604
|
},
|
|
1387
1605
|
extraFiles: [{
|
|
1388
1606
|
url: hf("animalClassification/bird-nabirds/onnx/camstack-bird-nabirds-404-labels.json"),
|
|
@@ -1416,11 +1634,7 @@ var VEHICLE_CLASSIFIER_MODELS = [{
|
|
|
1416
1634
|
files: [...MLPACKAGE_FILES],
|
|
1417
1635
|
runtimes: ["python"]
|
|
1418
1636
|
},
|
|
1419
|
-
openvino:
|
|
1420
|
-
url: hf("vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml"),
|
|
1421
|
-
sizeMB: 68,
|
|
1422
|
-
runtimes: ["python"]
|
|
1423
|
-
}
|
|
1637
|
+
openvino: ovFormat(hf("vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml"), 68)
|
|
1424
1638
|
},
|
|
1425
1639
|
extraFiles: [{
|
|
1426
1640
|
url: hf("vehicleClassification/efficientnet/camstack-vehicle-type-labels.json"),
|
|
@@ -1453,11 +1667,7 @@ var SEGMENTATION_REFINER_MODELS = [{
|
|
|
1453
1667
|
files: [...MLPACKAGE_FILES],
|
|
1454
1668
|
runtimes: ["python"]
|
|
1455
1669
|
},
|
|
1456
|
-
openvino:
|
|
1457
|
-
url: hf("segmentationRefiner/u2netp/openvino/camstack-u2netp.xml"),
|
|
1458
|
-
sizeMB: 2.5,
|
|
1459
|
-
runtimes: ["python"]
|
|
1460
|
-
}
|
|
1670
|
+
openvino: ovFormat(hf("segmentationRefiner/u2netp/openvino/camstack-u2netp.xml"), 2.5)
|
|
1461
1671
|
}
|
|
1462
1672
|
}];
|
|
1463
1673
|
var INSTANCE_SEGMENTATION_MODELS = [
|
|
@@ -1483,11 +1693,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1483
1693
|
files: [...MLPACKAGE_FILES],
|
|
1484
1694
|
runtimes: ["python"]
|
|
1485
1695
|
},
|
|
1486
|
-
openvino:
|
|
1487
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml"),
|
|
1488
|
-
sizeMB: 11,
|
|
1489
|
-
runtimes: ["python"]
|
|
1490
|
-
}
|
|
1696
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml"), 11)
|
|
1491
1697
|
}
|
|
1492
1698
|
},
|
|
1493
1699
|
{
|
|
@@ -1512,11 +1718,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1512
1718
|
files: [...MLPACKAGE_FILES],
|
|
1513
1719
|
runtimes: ["python"]
|
|
1514
1720
|
},
|
|
1515
|
-
openvino:
|
|
1516
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml"),
|
|
1517
|
-
sizeMB: 40,
|
|
1518
|
-
runtimes: ["python"]
|
|
1519
|
-
}
|
|
1721
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml"), 40)
|
|
1520
1722
|
}
|
|
1521
1723
|
},
|
|
1522
1724
|
{
|
|
@@ -1541,11 +1743,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1541
1743
|
files: [...MLPACKAGE_FILES],
|
|
1542
1744
|
runtimes: ["python"]
|
|
1543
1745
|
},
|
|
1544
|
-
openvino:
|
|
1545
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml"),
|
|
1546
|
-
sizeMB: 90,
|
|
1547
|
-
runtimes: ["python"]
|
|
1548
|
-
}
|
|
1746
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml"), 90)
|
|
1549
1747
|
}
|
|
1550
1748
|
}
|
|
1551
1749
|
];
|
|
@@ -1561,16 +1759,12 @@ var AUDIO_CLASSIFIER_MODELS = [{
|
|
|
1561
1759
|
preprocessMode: "resize",
|
|
1562
1760
|
formats: {
|
|
1563
1761
|
onnx: {
|
|
1564
|
-
url: hf("
|
|
1762
|
+
url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1565
1763
|
sizeMB: 3.2
|
|
1566
1764
|
},
|
|
1567
|
-
openvino:
|
|
1568
|
-
url: hf("audioClassifier/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1569
|
-
sizeMB: 3.2,
|
|
1570
|
-
runtimes: ["python"]
|
|
1571
|
-
},
|
|
1765
|
+
openvino: ovFormat(hf("audioClassification/yamnet/openvino/camstack-yamnet.xml"), 3.2),
|
|
1572
1766
|
coreml: {
|
|
1573
|
-
url: hf("
|
|
1767
|
+
url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1574
1768
|
sizeMB: 3.2,
|
|
1575
1769
|
runtimes: ["python"]
|
|
1576
1770
|
}
|
|
@@ -1628,7 +1822,7 @@ var ObjectDetectionStep = class {
|
|
|
1628
1822
|
"animal"
|
|
1629
1823
|
],
|
|
1630
1824
|
models: [...OBJECT_DETECTION_MODELS],
|
|
1631
|
-
defaultModelId: "
|
|
1825
|
+
defaultModelId: "yolo26n",
|
|
1632
1826
|
defaultConfidence: .5,
|
|
1633
1827
|
labels: COCO_80_LABELS.map((l) => l.id),
|
|
1634
1828
|
classMap: COCO_TO_MACRO
|
|
@@ -1877,9 +2071,9 @@ var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
|
|
|
1877
2071
|
enabledByDefault: false,
|
|
1878
2072
|
defaultConfidence: .3
|
|
1879
2073
|
});
|
|
1880
|
-
var
|
|
1881
|
-
id: "segmentation
|
|
1882
|
-
name: "
|
|
2074
|
+
var STEP_SEGMENTATION = new PipelineStepBase({
|
|
2075
|
+
id: "segmentation",
|
|
2076
|
+
name: "Segmentation",
|
|
1883
2077
|
slot: "refiner",
|
|
1884
2078
|
postprocessor: "saliency",
|
|
1885
2079
|
extractMode: "crop-roi",
|
|
@@ -1891,7 +2085,7 @@ var STEP_SEGMENTATION_REFINER = new PipelineStepBase({
|
|
|
1891
2085
|
defaultConfidence: 0,
|
|
1892
2086
|
group: "Segmentation"
|
|
1893
2087
|
});
|
|
1894
|
-
|
|
2088
|
+
new PipelineStepBase({
|
|
1895
2089
|
id: "instance-segmentation",
|
|
1896
2090
|
name: "Instance Segmentation",
|
|
1897
2091
|
slot: "refiner",
|
|
@@ -1918,8 +2112,7 @@ var ALL_PIPELINE_STEPS = [
|
|
|
1918
2112
|
new AnimalClassifierStep(),
|
|
1919
2113
|
STEP_BIRD_CLASSIFIER,
|
|
1920
2114
|
STEP_VEHICLE_CLASSIFIER,
|
|
1921
|
-
|
|
1922
|
-
STEP_INSTANCE_SEGMENTATION,
|
|
2115
|
+
STEP_SEGMENTATION,
|
|
1923
2116
|
STEP_AUDIO_CLASSIFIER_INSTANCE
|
|
1924
2117
|
];
|
|
1925
2118
|
/** Compat: flat array of StepDefinition for existing consumers */
|
|
@@ -2158,77 +2351,108 @@ var EngineFactory = class {
|
|
|
2158
2351
|
}
|
|
2159
2352
|
};
|
|
2160
2353
|
//#endregion
|
|
2161
|
-
//#region src/detection-pipeline/engine-
|
|
2162
|
-
/**
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
* and a remote agent (iGPU, or no accelerator at all) must hold INDEPENDENT
|
|
2171
|
-
* selections. Sharing them lets one node's pick (e.g. `openvino/npu`) override
|
|
2172
|
-
* another node that has no NPU.
|
|
2173
|
-
*
|
|
2174
|
-
* These three keys are therefore persisted node-scoped as `<key>@<nodeId>`.
|
|
2175
|
-
* Everything else in the store stays shared. A legacy un-scoped value (written
|
|
2176
|
-
* before this change, or by an older build) is read as a migration fallback and
|
|
2177
|
-
* re-persisted under the node-scoped key on the next write.
|
|
2178
|
-
*/
|
|
2179
|
-
var ENGINE_CASCADE_KEYS = [
|
|
2180
|
-
"engineBackend",
|
|
2181
|
-
"engineDevice",
|
|
2182
|
-
"probedBestEngine"
|
|
2354
|
+
//#region src/detection-pipeline/engine-provisioner.ts
|
|
2355
|
+
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
2356
|
+
var BACKOFF_SCHEDULE_MS = [
|
|
2357
|
+
5e3,
|
|
2358
|
+
15e3,
|
|
2359
|
+
3e4,
|
|
2360
|
+
6e4,
|
|
2361
|
+
12e4,
|
|
2362
|
+
3e5
|
|
2183
2363
|
];
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2364
|
+
var IDLE_STATE = {
|
|
2365
|
+
runtimeId: null,
|
|
2366
|
+
device: null,
|
|
2367
|
+
state: "idle"
|
|
2368
|
+
};
|
|
2369
|
+
var EngineProvisioner = class {
|
|
2370
|
+
fx;
|
|
2371
|
+
current = IDLE_STATE;
|
|
2372
|
+
/** Bumped on every select/dispose — stale async results (old generation) are ignored. */
|
|
2373
|
+
generation = 0;
|
|
2374
|
+
cancelTimer = null;
|
|
2375
|
+
retryIndex = 0;
|
|
2376
|
+
constructor(fx) {
|
|
2377
|
+
this.fx = fx;
|
|
2378
|
+
}
|
|
2379
|
+
get state() {
|
|
2380
|
+
return this.current;
|
|
2381
|
+
}
|
|
2382
|
+
isReady() {
|
|
2383
|
+
return this.current.state === "ready";
|
|
2384
|
+
}
|
|
2385
|
+
select(runtimeId, device) {
|
|
2386
|
+
this.generation++;
|
|
2387
|
+
this.retryIndex = 0;
|
|
2388
|
+
this.clearTimer();
|
|
2389
|
+
this.transition({
|
|
2390
|
+
runtimeId,
|
|
2391
|
+
device,
|
|
2392
|
+
state: "installing",
|
|
2393
|
+
progress: 0
|
|
2394
|
+
});
|
|
2395
|
+
this.provision(this.generation, runtimeId, device);
|
|
2396
|
+
}
|
|
2397
|
+
dispose() {
|
|
2398
|
+
this.generation++;
|
|
2399
|
+
this.clearTimer();
|
|
2400
|
+
}
|
|
2401
|
+
clearTimer() {
|
|
2402
|
+
if (this.cancelTimer !== null) {
|
|
2403
|
+
this.cancelTimer();
|
|
2404
|
+
this.cancelTimer = null;
|
|
2223
2405
|
}
|
|
2224
|
-
out[key] = store[key];
|
|
2225
2406
|
}
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2407
|
+
transition(next) {
|
|
2408
|
+
this.current = next;
|
|
2409
|
+
this.fx.onChange(next);
|
|
2229
2410
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2411
|
+
async provision(gen, runtimeId, device) {
|
|
2412
|
+
try {
|
|
2413
|
+
await Promise.all([this.fx.installRequirements(this.fx.requirementsFor(runtimeId)), this.fx.ensureModelForFormat(this.fx.modelFormatFor(runtimeId))]);
|
|
2414
|
+
if (gen !== this.generation) return;
|
|
2415
|
+
this.transition({
|
|
2416
|
+
runtimeId,
|
|
2417
|
+
device,
|
|
2418
|
+
state: "verifying",
|
|
2419
|
+
progress: 100
|
|
2420
|
+
});
|
|
2421
|
+
await this.fx.verify(runtimeId, device);
|
|
2422
|
+
if (gen !== this.generation) return;
|
|
2423
|
+
this.retryIndex = 0;
|
|
2424
|
+
this.transition({
|
|
2425
|
+
runtimeId,
|
|
2426
|
+
device,
|
|
2427
|
+
state: "ready"
|
|
2428
|
+
});
|
|
2429
|
+
} catch (err) {
|
|
2430
|
+
if (gen !== this.generation) return;
|
|
2431
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2432
|
+
const delay = BACKOFF_SCHEDULE_MS[Math.min(this.retryIndex, BACKOFF_SCHEDULE_MS.length - 1)];
|
|
2433
|
+
this.retryIndex++;
|
|
2434
|
+
const nextRetryAt = this.fx.now() + delay;
|
|
2435
|
+
this.transition({
|
|
2436
|
+
runtimeId,
|
|
2437
|
+
device,
|
|
2438
|
+
state: "failed",
|
|
2439
|
+
error: message,
|
|
2440
|
+
nextRetryAt
|
|
2441
|
+
});
|
|
2442
|
+
this.cancelTimer = this.fx.setTimer(delay, () => {
|
|
2443
|
+
if (gen !== this.generation) return;
|
|
2444
|
+
this.cancelTimer = null;
|
|
2445
|
+
this.transition({
|
|
2446
|
+
runtimeId,
|
|
2447
|
+
device,
|
|
2448
|
+
state: "installing",
|
|
2449
|
+
progress: 0
|
|
2450
|
+
});
|
|
2451
|
+
this.provision(gen, runtimeId, device);
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
};
|
|
2232
2456
|
//#endregion
|
|
2233
2457
|
//#region src/detection-pipeline/postprocess/dispatch.ts
|
|
2234
2458
|
var VALID_KINDS = new Set([
|
|
@@ -3059,6 +3283,18 @@ function applyChildOutput(parent, childStep, output, stepLatencyMs, ctx) {
|
|
|
3059
3283
|
parent.mask = output.mask;
|
|
3060
3284
|
parent.maskWidth = output.maskWidth;
|
|
3061
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
|
+
}
|
|
3062
3298
|
break;
|
|
3063
3299
|
}
|
|
3064
3300
|
}
|
|
@@ -3109,6 +3345,12 @@ function buildFrameResult(input) {
|
|
|
3109
3345
|
macroClass: m.macroClass,
|
|
3110
3346
|
score: m.score,
|
|
3111
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
|
+
} : {},
|
|
3112
3354
|
labels,
|
|
3113
3355
|
...m.parentId !== void 0 ? { parentId: m.parentId } : {},
|
|
3114
3356
|
...embedding !== void 0 ? {
|
|
@@ -3540,338 +3782,97 @@ var PipelineExecutor = class {
|
|
|
3540
3782
|
const perMacroThreshold = settings[`minConfidence${capitalize(macroClass)}`];
|
|
3541
3783
|
if (typeof perMacroThreshold === "number" && score < perMacroThreshold) return false;
|
|
3542
3784
|
return true;
|
|
3543
|
-
}
|
|
3544
|
-
};
|
|
3545
|
-
function capitalize(s) {
|
|
3546
|
-
if (s.length === 0) return s;
|
|
3547
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
3548
|
-
}
|
|
3549
|
-
//#endregion
|
|
3550
|
-
//#region src/detection-pipeline/pipeline/tree-builder.ts
|
|
3551
|
-
/**
|
|
3552
|
-
* Build an executable tree from user config.
|
|
3553
|
-
*
|
|
3554
|
-
* @param steps - User-configured pipeline steps (from PipelineDefaultStep[])
|
|
3555
|
-
* @param getEngine - Function that returns an IInferenceEngine for a step ID.
|
|
3556
|
-
* Throws if step not loaded.
|
|
3557
|
-
*/
|
|
3558
|
-
function buildExecutableTree(steps, getEngine) {
|
|
3559
|
-
return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine)) };
|
|
3560
|
-
}
|
|
3561
|
-
function buildNode(step, getEngine) {
|
|
3562
|
-
const definition = getStepDefinition(step.addonId);
|
|
3563
|
-
const engine = getEngine(step.addonId);
|
|
3564
|
-
const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine));
|
|
3565
|
-
const mergedSettings = {
|
|
3566
|
-
...collectSchemaDefaults(step.addonId),
|
|
3567
|
-
...step.settings
|
|
3568
|
-
};
|
|
3569
|
-
return {
|
|
3570
|
-
stepId: step.addonId,
|
|
3571
|
-
definition,
|
|
3572
|
-
engine,
|
|
3573
|
-
modelId: step.modelId,
|
|
3574
|
-
inputClasses: definition.inputClasses ?? [],
|
|
3575
|
-
enabled: step.enabled,
|
|
3576
|
-
children,
|
|
3577
|
-
...Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {}
|
|
3578
|
-
};
|
|
3579
|
-
}
|
|
3580
|
-
/**
|
|
3581
|
-
* Walk the step's declared `getConfigSchema()` and collect every field
|
|
3582
|
-
* with a primitive `default` value into a plain record. Group fields
|
|
3583
|
-
* are flattened — a nested `{ type: 'group', fields: [...] }` is
|
|
3584
|
-
* entered recursively. Fields without a declared default are skipped.
|
|
3585
|
-
*/
|
|
3586
|
-
function collectSchemaDefaults(stepId) {
|
|
3587
|
-
const schema = getStep(stepId).getConfigSchema();
|
|
3588
|
-
const out = {};
|
|
3589
|
-
walkFieldsForDefaults(schema, out);
|
|
3590
|
-
return out;
|
|
3591
|
-
}
|
|
3592
|
-
function walkFieldsForDefaults(fields, out) {
|
|
3593
|
-
for (const field of fields) {
|
|
3594
|
-
if ("type" in field && field.type === "group" && "fields" in field) {
|
|
3595
|
-
walkFieldsForDefaults(field.fields, out);
|
|
3596
|
-
continue;
|
|
3597
|
-
}
|
|
3598
|
-
if ("key" in field && "default" in field && field.default !== void 0) out[field.key] = field.default;
|
|
3599
|
-
}
|
|
3600
|
-
}
|
|
3601
|
-
//#endregion
|
|
3602
|
-
//#region src/detection-pipeline/runtimes.ts
|
|
3603
|
-
var KNOWN_PLATFORMS = [
|
|
3604
|
-
"darwin",
|
|
3605
|
-
"linux",
|
|
3606
|
-
"win32"
|
|
3607
|
-
];
|
|
3608
|
-
var KNOWN_ARCHES = ["arm64", "x64"];
|
|
3609
|
-
var KNOWN_GPU_TYPES = [
|
|
3610
|
-
"nvidia",
|
|
3611
|
-
"amd",
|
|
3612
|
-
"intel",
|
|
3613
|
-
"apple"
|
|
3614
|
-
];
|
|
3615
|
-
var KNOWN_NPU_TYPES = ["apple-ane", "intel-npu"];
|
|
3616
|
-
function toKnownPlatform(p) {
|
|
3617
|
-
return KNOWN_PLATFORMS.find((v) => v === p) ?? "linux";
|
|
3618
|
-
}
|
|
3619
|
-
function toKnownArch(a) {
|
|
3620
|
-
return KNOWN_ARCHES.find((v) => v === a) ?? "x64";
|
|
3621
|
-
}
|
|
3622
|
-
function gpuInfoFrom(hw) {
|
|
3623
|
-
if (!hw.gpu) return null;
|
|
3624
|
-
const type = KNOWN_GPU_TYPES.find((v) => v === hw.gpu?.type);
|
|
3625
|
-
if (!type) return null;
|
|
3626
|
-
return {
|
|
3627
|
-
type,
|
|
3628
|
-
name: ""
|
|
3629
|
-
};
|
|
3630
|
-
}
|
|
3631
|
-
function npuInfoFrom(hw) {
|
|
3632
|
-
if (!hw.npu) return null;
|
|
3633
|
-
const type = KNOWN_NPU_TYPES.find((v) => v === hw.npu?.type);
|
|
3634
|
-
if (!type) return null;
|
|
3635
|
-
return { type };
|
|
3636
|
-
}
|
|
3637
|
-
function envToHardwareInfo(env) {
|
|
3638
|
-
if (!env.hardware) return null;
|
|
3639
|
-
return {
|
|
3640
|
-
platform: toKnownPlatform(env.platform),
|
|
3641
|
-
arch: toKnownArch(env.arch),
|
|
3642
|
-
cpuModel: "",
|
|
3643
|
-
cpuCores: 0,
|
|
3644
|
-
totalRAM_MB: 0,
|
|
3645
|
-
availableRAM_MB: 0,
|
|
3646
|
-
gpu: gpuInfoFrom(env.hardware),
|
|
3647
|
-
npu: npuInfoFrom(env.hardware)
|
|
3648
|
-
};
|
|
3649
|
-
}
|
|
3650
|
-
function probedToHardwareInfo(hw) {
|
|
3651
|
-
if (!hw) return null;
|
|
3652
|
-
return {
|
|
3653
|
-
platform: toKnownPlatform(process.platform),
|
|
3654
|
-
arch: toKnownArch(process.arch),
|
|
3655
|
-
cpuModel: "",
|
|
3656
|
-
cpuCores: 0,
|
|
3657
|
-
totalRAM_MB: 0,
|
|
3658
|
-
availableRAM_MB: 0,
|
|
3659
|
-
gpu: gpuInfoFrom(hw),
|
|
3660
|
-
npu: npuInfoFrom(hw)
|
|
3661
|
-
};
|
|
3662
|
-
}
|
|
3663
|
-
var RUNTIME_DETAIL = {
|
|
3664
|
-
onnx: {
|
|
3665
|
-
label: "ONNX Runtime",
|
|
3666
|
-
pythonRequirements: ["requirements.txt", "requirements-onnxruntime.txt"],
|
|
3667
|
-
tuning: {
|
|
3668
|
-
concurrency: 4,
|
|
3669
|
-
batchMode: "list",
|
|
3670
|
-
maxBatchSize: 8,
|
|
3671
|
-
intraOpThreads: 0
|
|
3672
|
-
}
|
|
3673
|
-
},
|
|
3674
|
-
openvino: {
|
|
3675
|
-
label: "OpenVINO",
|
|
3676
|
-
pythonRequirements: ["requirements.txt", "requirements-openvino.txt"],
|
|
3677
|
-
tuning: {
|
|
3678
|
-
concurrency: 1,
|
|
3679
|
-
batchMode: "none",
|
|
3680
|
-
numStreams: 0
|
|
3681
|
-
}
|
|
3682
|
-
},
|
|
3683
|
-
coreml: {
|
|
3684
|
-
label: "CoreML",
|
|
3685
|
-
pythonRequirements: ["requirements.txt", "requirements-coreml.txt"],
|
|
3686
|
-
tuning: {
|
|
3687
|
-
concurrency: 1,
|
|
3688
|
-
batchMode: "none",
|
|
3689
|
-
windowMs: 8,
|
|
3690
|
-
maxBatchSize: 8,
|
|
3691
|
-
numWorkers: 1
|
|
3692
|
-
}
|
|
3693
|
-
}
|
|
3694
|
-
};
|
|
3695
|
-
/**
|
|
3696
|
-
* Returns the list of supported runtime IDs for the given hardware env.
|
|
3697
|
-
* Delegates to `@camstack/types` `supportedRuntimes`.
|
|
3698
|
-
*/
|
|
3699
|
-
function supportedRuntimes(env) {
|
|
3700
|
-
return supportedRuntimes$1(envToHardwareInfo(env));
|
|
3785
|
+
}
|
|
3786
|
+
};
|
|
3787
|
+
function capitalize(s) {
|
|
3788
|
+
if (s.length === 0) return s;
|
|
3789
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
3701
3790
|
}
|
|
3791
|
+
//#endregion
|
|
3792
|
+
//#region src/detection-pipeline/pipeline/tree-builder.ts
|
|
3702
3793
|
/**
|
|
3703
|
-
*
|
|
3704
|
-
*
|
|
3794
|
+
* Build an executable tree from user config.
|
|
3795
|
+
*
|
|
3796
|
+
* @param steps - User-configured pipeline steps (from PipelineDefaultStep[])
|
|
3797
|
+
* @param getEngine - Function that returns an IInferenceEngine for a step ID.
|
|
3798
|
+
* Throws if step not loaded.
|
|
3705
3799
|
*/
|
|
3706
|
-
function
|
|
3707
|
-
return
|
|
3800
|
+
function buildExecutableTree(steps, getEngine) {
|
|
3801
|
+
return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine)) };
|
|
3708
3802
|
}
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3803
|
+
function buildNode(step, getEngine) {
|
|
3804
|
+
const definition = getStepDefinition(step.addonId);
|
|
3805
|
+
const engine = getEngine(step.addonId);
|
|
3806
|
+
const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine));
|
|
3807
|
+
const mergedSettings = {
|
|
3808
|
+
...collectSchemaDefaults(step.addonId),
|
|
3809
|
+
...step.settings
|
|
3810
|
+
};
|
|
3811
|
+
return {
|
|
3812
|
+
stepId: step.addonId,
|
|
3813
|
+
definition,
|
|
3814
|
+
engine,
|
|
3815
|
+
modelId: step.modelId,
|
|
3816
|
+
inputClasses: definition.inputClasses ?? [],
|
|
3817
|
+
enabled: step.enabled,
|
|
3818
|
+
children,
|
|
3819
|
+
...Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {}
|
|
3820
|
+
};
|
|
3715
3821
|
}
|
|
3716
|
-
/** Model format for each inference runtime supported by the detection pipeline. */
|
|
3717
|
-
var RUNTIME_FORMAT = {
|
|
3718
|
-
onnx: "onnx",
|
|
3719
|
-
openvino: "openvino",
|
|
3720
|
-
coreml: "coreml"
|
|
3721
|
-
};
|
|
3722
3822
|
/**
|
|
3723
|
-
*
|
|
3724
|
-
*
|
|
3725
|
-
*
|
|
3823
|
+
* Walk the step's declared `getConfigSchema()` and collect every field
|
|
3824
|
+
* with a primitive `default` value into a plain record. Group fields
|
|
3825
|
+
* are flattened — a nested `{ type: 'group', fields: [...] }` is
|
|
3826
|
+
* entered recursively. Fields without a declared default are skipped.
|
|
3726
3827
|
*/
|
|
3727
|
-
function
|
|
3728
|
-
|
|
3729
|
-
}
|
|
3730
|
-
|
|
3731
|
-
return
|
|
3732
|
-
}
|
|
3733
|
-
function tuningFor(id) {
|
|
3734
|
-
return RUNTIME_DETAIL[id].tuning;
|
|
3828
|
+
function collectSchemaDefaults(stepId) {
|
|
3829
|
+
const schema = getStep(stepId).getConfigSchema();
|
|
3830
|
+
const out = {};
|
|
3831
|
+
walkFieldsForDefaults(schema, out);
|
|
3832
|
+
return out;
|
|
3735
3833
|
}
|
|
3736
|
-
function
|
|
3737
|
-
|
|
3834
|
+
function walkFieldsForDefaults(fields, out) {
|
|
3835
|
+
for (const field of fields) {
|
|
3836
|
+
if ("type" in field && field.type === "group" && "fields" in field) {
|
|
3837
|
+
walkFieldsForDefaults(field.fields, out);
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
if ("key" in field && "default" in field && field.default !== void 0) out[field.key] = field.default;
|
|
3841
|
+
}
|
|
3738
3842
|
}
|
|
3843
|
+
//#endregion
|
|
3844
|
+
//#region src/detection-pipeline/registry/custom-models.ts
|
|
3739
3845
|
/**
|
|
3740
|
-
*
|
|
3741
|
-
*
|
|
3742
|
-
*
|
|
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.
|
|
3743
3849
|
*/
|
|
3744
|
-
function
|
|
3745
|
-
|
|
3746
|
-
|
|
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;
|
|
3747
3858
|
}
|
|
3748
|
-
//#endregion
|
|
3749
|
-
//#region src/detection-pipeline/engine-provisioner.ts
|
|
3750
|
-
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
3751
|
-
var BACKOFF_SCHEDULE_MS = [
|
|
3752
|
-
5e3,
|
|
3753
|
-
15e3,
|
|
3754
|
-
3e4,
|
|
3755
|
-
6e4,
|
|
3756
|
-
12e4,
|
|
3757
|
-
3e5
|
|
3758
|
-
];
|
|
3759
|
-
var IDLE_STATE = {
|
|
3760
|
-
runtimeId: null,
|
|
3761
|
-
device: null,
|
|
3762
|
-
state: "idle"
|
|
3763
|
-
};
|
|
3764
|
-
var EngineProvisioner = class {
|
|
3765
|
-
fx;
|
|
3766
|
-
current = IDLE_STATE;
|
|
3767
|
-
/** Bumped on every select/dispose — stale async results (old generation) are ignored. */
|
|
3768
|
-
generation = 0;
|
|
3769
|
-
cancelTimer = null;
|
|
3770
|
-
retryIndex = 0;
|
|
3771
|
-
constructor(fx) {
|
|
3772
|
-
this.fx = fx;
|
|
3773
|
-
}
|
|
3774
|
-
get state() {
|
|
3775
|
-
return this.current;
|
|
3776
|
-
}
|
|
3777
|
-
isReady() {
|
|
3778
|
-
return this.current.state === "ready";
|
|
3779
|
-
}
|
|
3780
|
-
select(runtimeId, device) {
|
|
3781
|
-
this.generation++;
|
|
3782
|
-
this.retryIndex = 0;
|
|
3783
|
-
this.clearTimer();
|
|
3784
|
-
this.transition({
|
|
3785
|
-
runtimeId,
|
|
3786
|
-
device,
|
|
3787
|
-
state: "installing",
|
|
3788
|
-
progress: 0
|
|
3789
|
-
});
|
|
3790
|
-
this.provision(this.generation, runtimeId, device);
|
|
3791
|
-
}
|
|
3792
|
-
dispose() {
|
|
3793
|
-
this.generation++;
|
|
3794
|
-
this.clearTimer();
|
|
3795
|
-
}
|
|
3796
|
-
clearTimer() {
|
|
3797
|
-
if (this.cancelTimer !== null) {
|
|
3798
|
-
this.cancelTimer();
|
|
3799
|
-
this.cancelTimer = null;
|
|
3800
|
-
}
|
|
3801
|
-
}
|
|
3802
|
-
transition(next) {
|
|
3803
|
-
this.current = next;
|
|
3804
|
-
this.fx.onChange(next);
|
|
3805
|
-
}
|
|
3806
|
-
async provision(gen, runtimeId, device) {
|
|
3807
|
-
try {
|
|
3808
|
-
await Promise.all([this.fx.installRequirements(this.fx.requirementsFor(runtimeId)), this.fx.ensureModelForFormat(this.fx.modelFormatFor(runtimeId))]);
|
|
3809
|
-
if (gen !== this.generation) return;
|
|
3810
|
-
this.transition({
|
|
3811
|
-
runtimeId,
|
|
3812
|
-
device,
|
|
3813
|
-
state: "verifying",
|
|
3814
|
-
progress: 100
|
|
3815
|
-
});
|
|
3816
|
-
await this.fx.verify(runtimeId, device);
|
|
3817
|
-
if (gen !== this.generation) return;
|
|
3818
|
-
this.retryIndex = 0;
|
|
3819
|
-
this.transition({
|
|
3820
|
-
runtimeId,
|
|
3821
|
-
device,
|
|
3822
|
-
state: "ready"
|
|
3823
|
-
});
|
|
3824
|
-
} catch (err) {
|
|
3825
|
-
if (gen !== this.generation) return;
|
|
3826
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3827
|
-
const delay = BACKOFF_SCHEDULE_MS[Math.min(this.retryIndex, BACKOFF_SCHEDULE_MS.length - 1)];
|
|
3828
|
-
this.retryIndex++;
|
|
3829
|
-
const nextRetryAt = this.fx.now() + delay;
|
|
3830
|
-
this.transition({
|
|
3831
|
-
runtimeId,
|
|
3832
|
-
device,
|
|
3833
|
-
state: "failed",
|
|
3834
|
-
error: message,
|
|
3835
|
-
nextRetryAt
|
|
3836
|
-
});
|
|
3837
|
-
this.cancelTimer = this.fx.setTimer(delay, () => {
|
|
3838
|
-
if (gen !== this.generation) return;
|
|
3839
|
-
this.cancelTimer = null;
|
|
3840
|
-
this.transition({
|
|
3841
|
-
runtimeId,
|
|
3842
|
-
device,
|
|
3843
|
-
state: "installing",
|
|
3844
|
-
progress: 0
|
|
3845
|
-
});
|
|
3846
|
-
this.provision(gen, runtimeId, device);
|
|
3847
|
-
});
|
|
3848
|
-
}
|
|
3849
|
-
}
|
|
3850
|
-
};
|
|
3851
|
-
//#endregion
|
|
3852
|
-
//#region src/detection-pipeline/auto-pick.ts
|
|
3853
|
-
var PREFERENCE = [
|
|
3854
|
-
"coreml",
|
|
3855
|
-
"openvino",
|
|
3856
|
-
"onnx"
|
|
3857
|
-
];
|
|
3858
3859
|
/**
|
|
3859
|
-
*
|
|
3860
|
-
*
|
|
3861
|
-
*
|
|
3862
|
-
* 1. If `bestBackendHint` is in the supported set, use it.
|
|
3863
|
-
* 2. Otherwise, walk PREFERENCE order and pick the first supported runtime.
|
|
3864
|
-
* 3. Floor to `'onnx'` (always supported).
|
|
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.
|
|
3865
3863
|
*
|
|
3866
|
-
*
|
|
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.
|
|
3867
3866
|
*/
|
|
3868
|
-
function
|
|
3869
|
-
const
|
|
3870
|
-
const
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
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;
|
|
3875
3876
|
}
|
|
3876
3877
|
//#endregion
|
|
3877
3878
|
//#region src/detection-pipeline/provider.ts
|
|
@@ -4109,6 +4110,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4109
4110
|
/** Addon context — ctx.api resolves lazily (direct caller created after boot) */
|
|
4110
4111
|
addonCtx = null;
|
|
4111
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
|
+
/**
|
|
4112
4120
|
* Per-device {@link DeviceProxy} cache used for zone gating at the
|
|
4113
4121
|
* runtime path. Reads `state.zones.value` + `state.zoneRules.value`
|
|
4114
4122
|
* synchronously per frame so detections inside an `exclude` zone
|
|
@@ -4154,6 +4162,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4154
4162
|
*/
|
|
4155
4163
|
needsAutoPick = false;
|
|
4156
4164
|
/**
|
|
4165
|
+
* Unsubscribe handle for the deferred-auto-pick `platform-probe` ready
|
|
4166
|
+
* listener (armed in `setApi` when the probe isn't ready yet). Cleared once
|
|
4167
|
+
* the engine is resolved — either by the listener firing or by the boot
|
|
4168
|
+
* safety-net `ensureBootEngineProvisioned`.
|
|
4169
|
+
*/
|
|
4170
|
+
deferredAutoPickUnsub = null;
|
|
4171
|
+
/**
|
|
4157
4172
|
* Warm cache for benchmark engine-override runs.
|
|
4158
4173
|
*
|
|
4159
4174
|
* Each override rebuild costs a full Python pool spin-up (~300-500ms)
|
|
@@ -4186,7 +4201,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4186
4201
|
this.writeStore = (patch) => settings.writeAddonStore(patch);
|
|
4187
4202
|
this.readDeviceStore = settings.readDeviceStore ?? (async () => ({}));
|
|
4188
4203
|
this.currentEngine = ONNX_FLOOR;
|
|
4189
|
-
this.log.info("Engine
|
|
4204
|
+
this.log.info("Engine pick pending (placeholder until probe / persisted selection)", { meta: {
|
|
4190
4205
|
runtime: this.currentEngine.runtime,
|
|
4191
4206
|
backend: this.currentEngine.backend,
|
|
4192
4207
|
format: this.currentEngine.format
|
|
@@ -4259,26 +4274,70 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4259
4274
|
this.needsAutoPick = false;
|
|
4260
4275
|
this.startProvisioningForCurrentEngine();
|
|
4261
4276
|
} else {
|
|
4262
|
-
this.startProvisioningForCurrentEngine();
|
|
4263
4277
|
const unsubscribe = this.addonCtx.onCapabilityStateChange("platform-probe", { type: "global" }, (state) => {
|
|
4264
4278
|
if (state !== "ready") return;
|
|
4265
|
-
|
|
4279
|
+
this.cancelDeferredAutoPick();
|
|
4266
4280
|
if (!this.needsAutoPick) return;
|
|
4267
4281
|
this.autoPickAndPersist().then(() => {
|
|
4268
4282
|
this.needsAutoPick = false;
|
|
4269
4283
|
this.startProvisioningForCurrentEngine();
|
|
4270
4284
|
});
|
|
4271
4285
|
});
|
|
4272
|
-
this.
|
|
4286
|
+
this.deferredAutoPickUnsub = unsubscribe;
|
|
4287
|
+
this.addonCtx.addDisposer(() => this.cancelDeferredAutoPick());
|
|
4273
4288
|
}
|
|
4274
4289
|
else this.startProvisioningForCurrentEngine();
|
|
4275
4290
|
}
|
|
4291
|
+
/** Tear down the deferred-auto-pick probe listener, if still armed. */
|
|
4292
|
+
cancelDeferredAutoPick() {
|
|
4293
|
+
if (this.deferredAutoPickUnsub) {
|
|
4294
|
+
this.deferredAutoPickUnsub();
|
|
4295
|
+
this.deferredAutoPickUnsub = null;
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
/**
|
|
4299
|
+
* Boot safety-net: deterministically provision the hardware-probed engine.
|
|
4300
|
+
*
|
|
4301
|
+
* Called from the addon's `onInitialize` right after `reprobeEngine` has
|
|
4302
|
+
* written this node's probe-driven selection (e.g. `engineBackend=openvino`)
|
|
4303
|
+
* to the store. When first-boot auto-pick was DEFERRED (probe not ready at
|
|
4304
|
+
* `setApi`), the engine would otherwise stay `idle` — selected but never
|
|
4305
|
+
* provisioned — because the `onCapabilityStateChange('platform-probe')` ready
|
|
4306
|
+
* edge can be missed when the cap is already 'ready' by the time we subscribe
|
|
4307
|
+
* (the old eager-onnx-floor masked this; removing it surfaced a node that
|
|
4308
|
+
* boots with no engine at all). This loads the now-persisted selection and
|
|
4309
|
+
* starts provisioning it, so the node reliably comes up on its real best
|
|
4310
|
+
* engine (openvino on Intel) with no onnx floor and no missed boot. No-op
|
|
4311
|
+
* when provisioning already started (persisted-engine path / listener fired)
|
|
4312
|
+
* or when nothing has been selected yet.
|
|
4313
|
+
*/
|
|
4314
|
+
async ensureBootEngineProvisioned() {
|
|
4315
|
+
if (this.getEngineProvisioning().state !== "idle") return;
|
|
4316
|
+
const stored = await this.loadEngine();
|
|
4317
|
+
if (!stored) return;
|
|
4318
|
+
this.currentEngine = stored;
|
|
4319
|
+
this.needsAutoPick = false;
|
|
4320
|
+
this.cancelDeferredAutoPick();
|
|
4321
|
+
this.log.info("Boot engine provisioning from probed selection", { meta: {
|
|
4322
|
+
runtime: stored.runtime,
|
|
4323
|
+
backend: stored.backend,
|
|
4324
|
+
device: stored.device ?? null
|
|
4325
|
+
} });
|
|
4326
|
+
this.startProvisioningForCurrentEngine();
|
|
4327
|
+
}
|
|
4276
4328
|
/**
|
|
4277
4329
|
* Auto-pick the best supported runtime at first boot (no stored engine).
|
|
4278
4330
|
* Uses the platform-probe cap's hardware + bestScore hint when available;
|
|
4279
4331
|
* falls back to platform/arch when the probe cap is not yet reachable.
|
|
4332
|
+
*
|
|
4280
4333
|
* Persists the selection as `engineBackend` + `engineDevice` so subsequent
|
|
4281
|
-
* boots load it via `loadEngine()` and skip this path
|
|
4334
|
+
* boots load it via `loadEngine()` and skip this path — but ONLY when the
|
|
4335
|
+
* probe actually answered (real `hardware` or a `bestScore` hint). If the
|
|
4336
|
+
* probe query failed (cold-start race, cap momentarily unreachable), the
|
|
4337
|
+
* pick floors to onnx purely for lack of information; persisting that would
|
|
4338
|
+
* LOCK onnx and skip auto-pick on every future boot even after the
|
|
4339
|
+
* accelerator surfaces. In that case we set the in-memory floor for liveness
|
|
4340
|
+
* but leave the store untouched so the next boot re-attempts the pick.
|
|
4282
4341
|
*/
|
|
4283
4342
|
async autoPickAndPersist() {
|
|
4284
4343
|
let hardware = null;
|
|
@@ -4300,6 +4359,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4300
4359
|
device: pick.device
|
|
4301
4360
|
};
|
|
4302
4361
|
this.currentEngine = engine;
|
|
4362
|
+
if (!(hardware !== null || bestBackendHint !== null)) {
|
|
4363
|
+
this.log.warn("Auto-pick: probe returned no hardware/hint — using onnx floor WITHOUT persisting", { meta: {
|
|
4364
|
+
backend: pick.runtimeId,
|
|
4365
|
+
device: pick.device
|
|
4366
|
+
} });
|
|
4367
|
+
return;
|
|
4368
|
+
}
|
|
4303
4369
|
const apNode = this.localProbeNodeId();
|
|
4304
4370
|
await this.writeStore({
|
|
4305
4371
|
[nodeEngineKey("engineBackend", apNode)]: pick.runtimeId,
|
|
@@ -4379,8 +4445,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4379
4445
|
* runtime through installing → verifying → ready.
|
|
4380
4446
|
*/
|
|
4381
4447
|
async onEngineSelectionChanged() {
|
|
4448
|
+
const prev = this.currentEngine;
|
|
4382
4449
|
const stored = await this.loadEngine();
|
|
4383
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
|
+
}
|
|
4384
4462
|
this.startProvisioningForCurrentEngine();
|
|
4385
4463
|
}
|
|
4386
4464
|
/**
|
|
@@ -4418,7 +4496,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4418
4496
|
if (!steps || steps.length === 0) return;
|
|
4419
4497
|
for (const step of flattenSteps(steps)) {
|
|
4420
4498
|
if (!step.enabled) continue;
|
|
4421
|
-
const modelEntry =
|
|
4499
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
4422
4500
|
if (!modelEntry) continue;
|
|
4423
4501
|
if (isModelDownloaded(this.modelsDir, modelEntry, format)) continue;
|
|
4424
4502
|
await this.downloadWithRetry(modelEntry, format, 3);
|
|
@@ -4496,10 +4574,44 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4496
4574
|
return { hardware: null };
|
|
4497
4575
|
}
|
|
4498
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
|
+
}
|
|
4499
4610
|
async getSchema(engine) {
|
|
4500
4611
|
if (!engine || !engine.runtime) engine = await this.getSelectedEngine();
|
|
4501
4612
|
const format = engine.format;
|
|
4502
|
-
const
|
|
4613
|
+
const customByStep = await this.getCustomModels();
|
|
4614
|
+
const slots = buildSchemaSlots(format, this.modelsDir, customByStep);
|
|
4503
4615
|
const { hardware } = await this.fetchProbeGatingData();
|
|
4504
4616
|
const env = runtimeEnvFromProcess(toProbedHardware(hardware));
|
|
4505
4617
|
return {
|
|
@@ -4686,7 +4798,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4686
4798
|
}
|
|
4687
4799
|
async downloadModel(input) {
|
|
4688
4800
|
const { modelId, format, addonId } = input;
|
|
4689
|
-
const modelEntry =
|
|
4801
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4690
4802
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4691
4803
|
const formatEntry = modelEntry.formats[format];
|
|
4692
4804
|
if (!formatEntry) throw new Error(`Model "${modelId}" has no ${format} format. Available: ${Object.keys(modelEntry.formats).join(", ")}`);
|
|
@@ -4749,7 +4861,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4749
4861
|
}
|
|
4750
4862
|
async deleteModel(input) {
|
|
4751
4863
|
const { modelId, format, addonId } = input;
|
|
4752
|
-
const modelEntry =
|
|
4864
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4753
4865
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4754
4866
|
if (!deleteModelFromDisk(this.modelsDir, modelEntry, format)) throw new Error(`Model "${modelId}" (${format}) is not downloaded — nothing to delete`);
|
|
4755
4867
|
this.log.info("Model deleted from disk", { meta: {
|
|
@@ -5205,7 +5317,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5205
5317
|
const work = (async () => {
|
|
5206
5318
|
const format = this.currentEngine?.format ?? "onnx";
|
|
5207
5319
|
for (const step of needed) {
|
|
5208
|
-
const modelEntry =
|
|
5320
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5209
5321
|
if (modelEntry && !isModelDownloaded(this.modelsDir, modelEntry, format)) {
|
|
5210
5322
|
this.log.info("Downloading model for step", { meta: {
|
|
5211
5323
|
modelId: step.modelId,
|
|
@@ -5700,7 +5812,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5700
5812
|
const format = this.currentEngine.format;
|
|
5701
5813
|
const downloads = [];
|
|
5702
5814
|
for (const step of flattenSteps(steps)) {
|
|
5703
|
-
const modelEntry =
|
|
5815
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5704
5816
|
if (!modelEntry) {
|
|
5705
5817
|
this.log.warn("Model not found in step catalog — skipping download", { meta: {
|
|
5706
5818
|
modelId: step.modelId,
|
|
@@ -6103,11 +6215,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6103
6215
|
}
|
|
6104
6216
|
}
|
|
6105
6217
|
};
|
|
6106
|
-
function buildSchemaSlots(format, modelsDir) {
|
|
6218
|
+
function buildSchemaSlots(format, modelsDir, customByStep) {
|
|
6107
6219
|
const slotMap = /* @__PURE__ */ new Map();
|
|
6108
6220
|
for (const pipelineStep of ALL_PIPELINE_STEPS) {
|
|
6109
6221
|
const step = pipelineStep.definition;
|
|
6110
|
-
const availableModels = step.models.filter((m) => m.formats[format]);
|
|
6222
|
+
const availableModels = mergeCustomModels(step.models, customByStep?.get(step.id) ?? []).filter((m) => m.formats[format]);
|
|
6111
6223
|
if (availableModels.length === 0) continue;
|
|
6112
6224
|
const slot = step.slot;
|
|
6113
6225
|
if (!slotMap.has(slot)) slotMap.set(slot, []);
|
|
@@ -6212,8 +6324,7 @@ function buildDefaultStepTree(format) {
|
|
|
6212
6324
|
makeStep("animal-classifier", [], { enabled: false }),
|
|
6213
6325
|
makeStep("bird-classifier", [], { enabled: false }),
|
|
6214
6326
|
makeStep("vehicle-classifier", [], { enabled: false }),
|
|
6215
|
-
makeStep("segmentation
|
|
6216
|
-
makeStep("instance-segmentation", [], { enabled: false })
|
|
6327
|
+
makeStep("segmentation", [], { enabled: false })
|
|
6217
6328
|
].filter((s) => s !== null));
|
|
6218
6329
|
const audioEngine = getDefaultModelForFormat("audio-classifier", format) === "apple-soundanalysis" ? {
|
|
6219
6330
|
runtime: "python",
|
|
@@ -6503,8 +6614,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6503
6614
|
label: "Execution provider",
|
|
6504
6615
|
options: [...STATIC_BACKEND_OPTIONS],
|
|
6505
6616
|
default: DEFAULT_CONFIG.engineBackend,
|
|
6506
|
-
immediate: true
|
|
6507
|
-
requiresRestart: true
|
|
6617
|
+
immediate: true
|
|
6508
6618
|
}),
|
|
6509
6619
|
this.field({
|
|
6510
6620
|
type: "select",
|
|
@@ -6512,8 +6622,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6512
6622
|
label: "Hardware device",
|
|
6513
6623
|
options: [...STATIC_DEFAULT_DEVICE_OPTIONS],
|
|
6514
6624
|
default: DEFAULT_CONFIG.engineDevice,
|
|
6515
|
-
immediate: true
|
|
6516
|
-
requiresRestart: true
|
|
6625
|
+
immediate: true
|
|
6517
6626
|
})
|
|
6518
6627
|
]
|
|
6519
6628
|
}, {
|
|
@@ -6552,8 +6661,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6552
6661
|
"onnx",
|
|
6553
6662
|
"cpu"
|
|
6554
6663
|
]
|
|
6555
|
-
}
|
|
6556
|
-
requiresRestart: true
|
|
6664
|
+
}
|
|
6557
6665
|
}),
|
|
6558
6666
|
this.field({
|
|
6559
6667
|
type: "slider",
|
|
@@ -6570,8 +6678,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6570
6678
|
showWhen: {
|
|
6571
6679
|
field: "batchMode",
|
|
6572
6680
|
notEquals: "none"
|
|
6573
|
-
}
|
|
6574
|
-
requiresRestart: true
|
|
6681
|
+
}
|
|
6575
6682
|
}),
|
|
6576
6683
|
this.field({
|
|
6577
6684
|
type: "slider",
|
|
@@ -6588,8 +6695,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6588
6695
|
showWhen: {
|
|
6589
6696
|
field: "batchMode",
|
|
6590
6697
|
notEquals: "none"
|
|
6591
|
-
}
|
|
6592
|
-
requiresRestart: true
|
|
6698
|
+
}
|
|
6593
6699
|
}),
|
|
6594
6700
|
this.field({
|
|
6595
6701
|
type: "slider",
|
|
@@ -6602,8 +6708,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6602
6708
|
default: void 0,
|
|
6603
6709
|
showValue: true,
|
|
6604
6710
|
nullable: true,
|
|
6605
|
-
nullLabel: "Auto"
|
|
6606
|
-
requiresRestart: true
|
|
6711
|
+
nullLabel: "Auto"
|
|
6607
6712
|
}),
|
|
6608
6713
|
this.field({
|
|
6609
6714
|
type: "slider",
|
|
@@ -6616,8 +6721,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6616
6721
|
default: void 0,
|
|
6617
6722
|
showValue: true,
|
|
6618
6723
|
nullable: true,
|
|
6619
|
-
nullLabel: "Auto"
|
|
6620
|
-
requiresRestart: true
|
|
6724
|
+
nullLabel: "Auto"
|
|
6621
6725
|
})
|
|
6622
6726
|
]
|
|
6623
6727
|
}] });
|
|
@@ -6880,6 +6984,9 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6880
6984
|
if (!this.nodeProbedBestEngine) await this.provider.reprobeEngine().catch((err) => {
|
|
6881
6985
|
this.ctx.logger.warn("auto-reprobe engine failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
6882
6986
|
});
|
|
6987
|
+
await this.provider.ensureBootEngineProvisioned().catch((err) => {
|
|
6988
|
+
this.ctx.logger.warn("ensureBootEngineProvisioned failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
6989
|
+
});
|
|
6883
6990
|
await this.provider.warmPool();
|
|
6884
6991
|
this.engineMetricsTimer = setInterval(() => this.emitEngineMetricsSnapshot(), ENGINE_METRICS_SNAPSHOT_INTERVAL_MS);
|
|
6885
6992
|
this.lastAppliedPoolConfig = this.snapshotPoolConfig();
|
|
@@ -6987,16 +7094,17 @@ var DetectionPipelineAddon = class extends BaseAddon {
|
|
|
6987
7094
|
return false;
|
|
6988
7095
|
}
|
|
6989
7096
|
/**
|
|
6990
|
-
* BaseAddon calls `onConfigChanged` after every settings write.
|
|
6991
|
-
*
|
|
6992
|
-
*
|
|
6993
|
-
*
|
|
6994
|
-
*
|
|
6995
|
-
* respawn the
|
|
6996
|
-
*
|
|
6997
|
-
*
|
|
6998
|
-
*
|
|
6999
|
-
*
|
|
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.
|
|
7000
7108
|
*/
|
|
7001
7109
|
async onConfigChanged() {
|
|
7002
7110
|
await this.refreshNodeEngineFromStore();
|