@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
|
@@ -2,17 +2,261 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const
|
|
6
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_model_download_service_C_IHWnXx = require("../model-download-service-C-IHWnXx-BnQ_awK4.js");
|
|
6
|
+
const require_dist = require("../dist-DnD2tm7T.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
|
-
node_fs =
|
|
8
|
+
node_fs = require_model_download_service_C_IHWnXx.__toESM(node_fs);
|
|
9
9
|
let node_path = require("node:path");
|
|
10
|
-
node_path =
|
|
10
|
+
node_path = require_model_download_service_C_IHWnXx.__toESM(node_path);
|
|
11
11
|
let node_os = require("node:os");
|
|
12
|
-
node_os =
|
|
12
|
+
node_os = require_model_download_service_C_IHWnXx.__toESM(node_os);
|
|
13
13
|
let node_child_process = require("node:child_process");
|
|
14
14
|
let sharp = require("sharp");
|
|
15
|
-
sharp =
|
|
15
|
+
sharp = require_model_download_service_C_IHWnXx.__toESM(sharp);
|
|
16
|
+
//#region src/detection-pipeline/engine-store-keys.ts
|
|
17
|
+
/**
|
|
18
|
+
* Per-node scoping for the detection-pipeline engine cascade.
|
|
19
|
+
*
|
|
20
|
+
* The detection addon's settings store is a single CLUSTER-SHARED blob (the
|
|
21
|
+
* settings-store cap is hub-resident; every node's detection instance reads and
|
|
22
|
+
* writes the same keys). That is correct for node-agnostic settings (pipeline
|
|
23
|
+
* steps, tuning) but WRONG for the engine cascade — `engineBackend` /
|
|
24
|
+
* `engineDevice` / `probedBestEngine` are hardware-specific, so the hub (NPU)
|
|
25
|
+
* and a remote agent (iGPU, or no accelerator at all) must hold INDEPENDENT
|
|
26
|
+
* selections. Sharing them lets one node's pick (e.g. `openvino/npu`) override
|
|
27
|
+
* another node that has no NPU.
|
|
28
|
+
*
|
|
29
|
+
* These three keys are therefore persisted node-scoped as `<key>@<nodeId>`.
|
|
30
|
+
* Everything else in the store stays shared. A legacy un-scoped value (written
|
|
31
|
+
* before this change, or by an older build) is read as a migration fallback and
|
|
32
|
+
* re-persisted under the node-scoped key on the next write.
|
|
33
|
+
*/
|
|
34
|
+
var ENGINE_CASCADE_KEYS = [
|
|
35
|
+
"engineBackend",
|
|
36
|
+
"engineDevice",
|
|
37
|
+
"probedBestEngine"
|
|
38
|
+
];
|
|
39
|
+
function isEngineCascadeKey(key) {
|
|
40
|
+
return ENGINE_CASCADE_KEYS.includes(key);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Normalise a raw kernel node id to the bare node id used for scoping.
|
|
44
|
+
* `localNodeId` can carry a `<node>/<addon>` suffix; the engine selection is
|
|
45
|
+
* per-NODE, so strip the addon segment. Falls back to `hub`.
|
|
46
|
+
*/
|
|
47
|
+
function normalizeEngineNodeId(rawNodeId) {
|
|
48
|
+
const raw = rawNodeId ?? "hub";
|
|
49
|
+
return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
|
|
50
|
+
}
|
|
51
|
+
/** The node-scoped store key for an engine cascade field. */
|
|
52
|
+
function nodeEngineKey(base, nodeId) {
|
|
53
|
+
return `${base}@${normalizeEngineNodeId(nodeId)}`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Read an engine cascade value for a node: the node-scoped key if present,
|
|
57
|
+
* otherwise the legacy un-scoped value (migration), otherwise undefined.
|
|
58
|
+
*/
|
|
59
|
+
function readNodeEngineValue(store, base, nodeId) {
|
|
60
|
+
const scoped = store[nodeEngineKey(base, nodeId)];
|
|
61
|
+
return scoped !== void 0 ? scoped : store[base];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Project a raw store onto the plain engine cascade keys for THIS node, so the
|
|
65
|
+
* UI schema (whose field keys are the bare `engineBackend` etc.) hydrates from
|
|
66
|
+
* the node's own selection. Non-engine keys are left untouched. Node-scoped
|
|
67
|
+
* keys for OTHER nodes are dropped from the projection (not relevant to this
|
|
68
|
+
* node's form).
|
|
69
|
+
*/
|
|
70
|
+
function projectNodeEngine(store, nodeId) {
|
|
71
|
+
const out = {};
|
|
72
|
+
const scopedForAnyNode = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const key of Object.keys(store)) {
|
|
74
|
+
const atIdx = key.indexOf("@");
|
|
75
|
+
if (isEngineCascadeKey(atIdx >= 0 ? key.slice(0, atIdx) : key)) {
|
|
76
|
+
scopedForAnyNode.add(key);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
out[key] = store[key];
|
|
80
|
+
}
|
|
81
|
+
for (const base of ENGINE_CASCADE_KEYS) {
|
|
82
|
+
const value = readNodeEngineValue(store, base, nodeId);
|
|
83
|
+
if (value !== void 0) out[base] = value;
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/detection-pipeline/runtimes.ts
|
|
89
|
+
var KNOWN_PLATFORMS = [
|
|
90
|
+
"darwin",
|
|
91
|
+
"linux",
|
|
92
|
+
"win32"
|
|
93
|
+
];
|
|
94
|
+
var KNOWN_ARCHES = ["arm64", "x64"];
|
|
95
|
+
var KNOWN_GPU_TYPES = [
|
|
96
|
+
"nvidia",
|
|
97
|
+
"amd",
|
|
98
|
+
"intel",
|
|
99
|
+
"apple"
|
|
100
|
+
];
|
|
101
|
+
var KNOWN_NPU_TYPES = ["apple-ane", "intel-npu"];
|
|
102
|
+
function toKnownPlatform(p) {
|
|
103
|
+
return KNOWN_PLATFORMS.find((v) => v === p) ?? "linux";
|
|
104
|
+
}
|
|
105
|
+
function toKnownArch(a) {
|
|
106
|
+
return KNOWN_ARCHES.find((v) => v === a) ?? "x64";
|
|
107
|
+
}
|
|
108
|
+
function gpuInfoFrom(hw) {
|
|
109
|
+
if (!hw.gpu) return null;
|
|
110
|
+
const type = KNOWN_GPU_TYPES.find((v) => v === hw.gpu?.type);
|
|
111
|
+
if (!type) return null;
|
|
112
|
+
return {
|
|
113
|
+
type,
|
|
114
|
+
name: ""
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function npuInfoFrom(hw) {
|
|
118
|
+
if (!hw.npu) return null;
|
|
119
|
+
const type = KNOWN_NPU_TYPES.find((v) => v === hw.npu?.type);
|
|
120
|
+
if (!type) return null;
|
|
121
|
+
return { type };
|
|
122
|
+
}
|
|
123
|
+
function envToHardwareInfo(env) {
|
|
124
|
+
if (!env.hardware) return null;
|
|
125
|
+
return {
|
|
126
|
+
platform: toKnownPlatform(env.platform),
|
|
127
|
+
arch: toKnownArch(env.arch),
|
|
128
|
+
cpuModel: "",
|
|
129
|
+
cpuCores: 0,
|
|
130
|
+
totalRAM_MB: 0,
|
|
131
|
+
availableRAM_MB: 0,
|
|
132
|
+
gpu: gpuInfoFrom(env.hardware),
|
|
133
|
+
npu: npuInfoFrom(env.hardware)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function probedToHardwareInfo(hw) {
|
|
137
|
+
if (!hw) return null;
|
|
138
|
+
return {
|
|
139
|
+
platform: toKnownPlatform(process.platform),
|
|
140
|
+
arch: toKnownArch(process.arch),
|
|
141
|
+
cpuModel: "",
|
|
142
|
+
cpuCores: 0,
|
|
143
|
+
totalRAM_MB: 0,
|
|
144
|
+
availableRAM_MB: 0,
|
|
145
|
+
gpu: gpuInfoFrom(hw),
|
|
146
|
+
npu: npuInfoFrom(hw)
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
var RUNTIME_DETAIL = {
|
|
150
|
+
onnx: {
|
|
151
|
+
label: "ONNX Runtime",
|
|
152
|
+
pythonRequirements: ["requirements.txt", "requirements-onnxruntime.txt"],
|
|
153
|
+
tuning: {
|
|
154
|
+
concurrency: 4,
|
|
155
|
+
batchMode: "list",
|
|
156
|
+
maxBatchSize: 8,
|
|
157
|
+
intraOpThreads: 0
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
openvino: {
|
|
161
|
+
label: "OpenVINO",
|
|
162
|
+
pythonRequirements: ["requirements.txt", "requirements-openvino.txt"],
|
|
163
|
+
tuning: {
|
|
164
|
+
concurrency: 1,
|
|
165
|
+
batchMode: "none",
|
|
166
|
+
numStreams: 0
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
coreml: {
|
|
170
|
+
label: "CoreML",
|
|
171
|
+
pythonRequirements: ["requirements.txt", "requirements-coreml.txt"],
|
|
172
|
+
tuning: {
|
|
173
|
+
concurrency: 1,
|
|
174
|
+
batchMode: "none",
|
|
175
|
+
windowMs: 8,
|
|
176
|
+
maxBatchSize: 8,
|
|
177
|
+
numWorkers: 1
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Returns the list of supported runtime IDs for the given hardware env.
|
|
183
|
+
* Delegates to `@camstack/types` `supportedRuntimes`.
|
|
184
|
+
*/
|
|
185
|
+
function supportedRuntimes(env) {
|
|
186
|
+
return require_dist.supportedRuntimes(envToHardwareInfo(env));
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Returns the device options for a given runtime and probed hardware.
|
|
190
|
+
* Delegates to `@camstack/types` `runtimeDevices`.
|
|
191
|
+
*/
|
|
192
|
+
function runtimeDevices(id, hardware) {
|
|
193
|
+
return require_dist.runtimeDevices(id, probedToHardwareInfo(hardware));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Returns the default device string for a given runtime.
|
|
197
|
+
* Delegates to `@camstack/types` `defaultDeviceFor`.
|
|
198
|
+
*/
|
|
199
|
+
function defaultDeviceFor(id) {
|
|
200
|
+
return require_dist.defaultDeviceFor(id);
|
|
201
|
+
}
|
|
202
|
+
/** Model format for each inference runtime supported by the detection pipeline. */
|
|
203
|
+
var RUNTIME_FORMAT = {
|
|
204
|
+
onnx: "onnx",
|
|
205
|
+
openvino: "openvino",
|
|
206
|
+
coreml: "coreml"
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Returns the model format required for a given runtime.
|
|
210
|
+
* Returns the locally-typed format string ('onnx' | 'openvino' | 'coreml')
|
|
211
|
+
* matching the engine-provisioner's own ModelFormat type.
|
|
212
|
+
*/
|
|
213
|
+
function modelFormatFor(id) {
|
|
214
|
+
return RUNTIME_FORMAT[id];
|
|
215
|
+
}
|
|
216
|
+
function pythonRequirementsFor(id) {
|
|
217
|
+
return RUNTIME_DETAIL[id].pythonRequirements;
|
|
218
|
+
}
|
|
219
|
+
function tuningFor(id) {
|
|
220
|
+
return RUNTIME_DETAIL[id].tuning;
|
|
221
|
+
}
|
|
222
|
+
function runtimeLabel(id) {
|
|
223
|
+
return RUNTIME_DETAIL[id].label;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Proactive-install hint kept for back-compat (re-exported from index.ts).
|
|
227
|
+
* Intel on Linux warrants installing openvino; coremltools covers macOS;
|
|
228
|
+
* openvino has no AMD/NVIDIA backend.
|
|
229
|
+
*/
|
|
230
|
+
function shouldInstallOpenvino(env) {
|
|
231
|
+
if (env.platform === "darwin") return false;
|
|
232
|
+
return env.gpu?.type === "intel" || env.npu?.type === "intel-npu";
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/detection-pipeline/auto-pick.ts
|
|
236
|
+
var PREFERENCE = [
|
|
237
|
+
"coreml",
|
|
238
|
+
"openvino",
|
|
239
|
+
"onnx"
|
|
240
|
+
];
|
|
241
|
+
/**
|
|
242
|
+
* Pure function — picks the best supported runtime for the given hardware env.
|
|
243
|
+
*
|
|
244
|
+
* Logic:
|
|
245
|
+
* 1. If `bestBackendHint` is in the supported set, use it.
|
|
246
|
+
* 2. Otherwise, walk PREFERENCE order and pick the first supported runtime.
|
|
247
|
+
* 3. Floor to `'onnx'` (always supported).
|
|
248
|
+
*
|
|
249
|
+
* Device is always `defaultDeviceFor(chosen)`.
|
|
250
|
+
*/
|
|
251
|
+
function pickBestRuntime(env, bestBackendHint) {
|
|
252
|
+
const supported = supportedRuntimes(env);
|
|
253
|
+
const chosen = supported.find((id) => id === bestBackendHint) ?? PREFERENCE.find((id) => supported.includes(id)) ?? "onnx";
|
|
254
|
+
return {
|
|
255
|
+
runtimeId: chosen,
|
|
256
|
+
device: defaultDeviceFor(chosen)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
16
260
|
//#region src/detection-pipeline/engine/shared-inference-pool.ts
|
|
17
261
|
var MSG_COMMAND = 0;
|
|
18
262
|
var MSG_INFER_JPEG = 1;
|
|
@@ -779,6 +1023,46 @@ var HF_REPO = "camstack/camstack-models";
|
|
|
779
1023
|
var HF_SCRYPTED = "scrypted/plugin-models";
|
|
780
1024
|
var hf = (path) => require_dist.hfModelUrl(HF_REPO, path);
|
|
781
1025
|
var hfScrypted = (path) => require_dist.hfModelUrl(HF_SCRYPTED, path);
|
|
1026
|
+
/**
|
|
1027
|
+
* Build an OpenVINO format entry (always python runtime).
|
|
1028
|
+
*
|
|
1029
|
+
* OpenVINO IR is a two-file bundle: a `.xml` topology + a sibling `.bin`
|
|
1030
|
+
* weights file with the same basename. We declare the `.bin` in `files` so
|
|
1031
|
+
* the (format-agnostic) downloader fetches it alongside the `.xml` — without
|
|
1032
|
+
* the weights, OpenVINO compile fails with "Empty weights data in bin file".
|
|
1033
|
+
* A plain `.onnx` run through the OpenVINO runtime (e.g. yamnet) has no
|
|
1034
|
+
* sibling, so none is added.
|
|
1035
|
+
*/
|
|
1036
|
+
var ovFormat = (url, sizeMB) => {
|
|
1037
|
+
const base = url.split("/").pop() ?? "";
|
|
1038
|
+
const files = base.endsWith(".xml") ? [base.replace(/\.xml$/, ".bin")] : void 0;
|
|
1039
|
+
return {
|
|
1040
|
+
url,
|
|
1041
|
+
sizeMB,
|
|
1042
|
+
runtimes: ["python"],
|
|
1043
|
+
...files ? { files } : {}
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
/**
|
|
1047
|
+
* Build a precision-variant catalog entry (OpenVINO-only) derived from a base
|
|
1048
|
+
* detection model. fp16 halves the weights (Intel iGPU/NPU sweet spot); int8 is
|
|
1049
|
+
* NNCF post-training-quantized (~4× smaller, fastest on CPU/iGPU at a small
|
|
1050
|
+
* accuracy cost). The IRs live next to the base `.xml` on HF as
|
|
1051
|
+
* `camstack-<id>-<precision>.xml`. Lets an operator scale the model to the node
|
|
1052
|
+
* (e.g. yolo26x-int8 on a 265K, yolo26n-int8 on an N100).
|
|
1053
|
+
*/
|
|
1054
|
+
var ovPrecisionVariant = (baseId, ovDir, baseName, precision, sizeMB) => ({
|
|
1055
|
+
id: `${baseId}-${precision}`,
|
|
1056
|
+
name: `${baseName} (${precision.toUpperCase()})`,
|
|
1057
|
+
description: `${baseName} — OpenVINO ${precision.toUpperCase()} variant for Intel iGPU/NPU; scale by hardware`,
|
|
1058
|
+
inputSize: {
|
|
1059
|
+
width: 640,
|
|
1060
|
+
height: 640
|
|
1061
|
+
},
|
|
1062
|
+
labels: [],
|
|
1063
|
+
preprocessMode: "letterbox",
|
|
1064
|
+
formats: { openvino: ovFormat(hf(`${ovDir}/camstack-${baseId}-${precision}.xml`), sizeMB) }
|
|
1065
|
+
});
|
|
782
1066
|
var MLPACKAGE_FILES = [
|
|
783
1067
|
"Manifest.json",
|
|
784
1068
|
"Data/com.apple.CoreML/model.mlmodel",
|
|
@@ -807,11 +1091,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
807
1091
|
files: [...MLPACKAGE_FILES],
|
|
808
1092
|
runtimes: ["python"]
|
|
809
1093
|
},
|
|
810
|
-
openvino:
|
|
811
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9t.xml"),
|
|
812
|
-
sizeMB: 6,
|
|
813
|
-
runtimes: ["python"]
|
|
814
|
-
}
|
|
1094
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9t.xml"), 6)
|
|
815
1095
|
}
|
|
816
1096
|
},
|
|
817
1097
|
{
|
|
@@ -836,11 +1116,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
836
1116
|
files: [...MLPACKAGE_FILES],
|
|
837
1117
|
runtimes: ["python"]
|
|
838
1118
|
},
|
|
839
|
-
openvino:
|
|
840
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9s.xml"),
|
|
841
|
-
sizeMB: 16,
|
|
842
|
-
runtimes: ["python"]
|
|
843
|
-
}
|
|
1119
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9s.xml"), 16)
|
|
844
1120
|
}
|
|
845
1121
|
},
|
|
846
1122
|
{
|
|
@@ -865,11 +1141,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
865
1141
|
files: [...MLPACKAGE_FILES],
|
|
866
1142
|
runtimes: ["python"]
|
|
867
1143
|
},
|
|
868
|
-
openvino:
|
|
869
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9c.xml"),
|
|
870
|
-
sizeMB: 49,
|
|
871
|
-
runtimes: ["python"]
|
|
872
|
-
}
|
|
1144
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9c.xml"), 49)
|
|
873
1145
|
}
|
|
874
1146
|
},
|
|
875
1147
|
{
|
|
@@ -894,11 +1166,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
894
1166
|
files: [...MLPACKAGE_FILES],
|
|
895
1167
|
runtimes: ["python"]
|
|
896
1168
|
},
|
|
897
|
-
openvino:
|
|
898
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26n.xml"),
|
|
899
|
-
sizeMB: 9,
|
|
900
|
-
runtimes: ["python"]
|
|
901
|
-
}
|
|
1169
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26n.xml"), 9)
|
|
902
1170
|
}
|
|
903
1171
|
},
|
|
904
1172
|
{
|
|
@@ -923,11 +1191,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
923
1191
|
files: [...MLPACKAGE_FILES],
|
|
924
1192
|
runtimes: ["python"]
|
|
925
1193
|
},
|
|
926
|
-
openvino:
|
|
927
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26s.xml"),
|
|
928
|
-
sizeMB: 36,
|
|
929
|
-
runtimes: ["python"]
|
|
930
|
-
}
|
|
1194
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26s.xml"), 36)
|
|
931
1195
|
}
|
|
932
1196
|
},
|
|
933
1197
|
{
|
|
@@ -952,11 +1216,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
952
1216
|
files: [...MLPACKAGE_FILES],
|
|
953
1217
|
runtimes: ["python"]
|
|
954
1218
|
},
|
|
955
|
-
openvino:
|
|
956
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26m.xml"),
|
|
957
|
-
sizeMB: 78,
|
|
958
|
-
runtimes: ["python"]
|
|
959
|
-
}
|
|
1219
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26m.xml"), 78)
|
|
960
1220
|
}
|
|
961
1221
|
},
|
|
962
1222
|
{
|
|
@@ -981,11 +1241,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
981
1241
|
files: [...MLPACKAGE_FILES],
|
|
982
1242
|
runtimes: ["python"]
|
|
983
1243
|
},
|
|
984
|
-
openvino:
|
|
985
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26l.xml"),
|
|
986
|
-
sizeMB: 95,
|
|
987
|
-
runtimes: ["python"]
|
|
988
|
-
}
|
|
1244
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26l.xml"), 95)
|
|
989
1245
|
}
|
|
990
1246
|
},
|
|
991
1247
|
{
|
|
@@ -1010,11 +1266,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1010
1266
|
files: [...MLPACKAGE_FILES],
|
|
1011
1267
|
runtimes: ["python"]
|
|
1012
1268
|
},
|
|
1013
|
-
openvino:
|
|
1014
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26x.xml"),
|
|
1015
|
-
sizeMB: 213,
|
|
1016
|
-
runtimes: ["python"]
|
|
1017
|
-
}
|
|
1269
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26x.xml"), 213)
|
|
1018
1270
|
}
|
|
1019
1271
|
},
|
|
1020
1272
|
{
|
|
@@ -1039,11 +1291,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1039
1291
|
files: [...MLPACKAGE_FILES],
|
|
1040
1292
|
runtimes: ["python"]
|
|
1041
1293
|
},
|
|
1042
|
-
openvino:
|
|
1043
|
-
url: hfScrypted("openvino/scrypted_yolov9t_relu/best.xml"),
|
|
1044
|
-
sizeMB: 6,
|
|
1045
|
-
runtimes: ["python"]
|
|
1046
|
-
}
|
|
1294
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9t_relu.xml"), 6)
|
|
1047
1295
|
}
|
|
1048
1296
|
},
|
|
1049
1297
|
{
|
|
@@ -1068,11 +1316,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1068
1316
|
files: [...MLPACKAGE_FILES],
|
|
1069
1317
|
runtimes: ["python"]
|
|
1070
1318
|
},
|
|
1071
|
-
openvino:
|
|
1072
|
-
url: hfScrypted("openvino/scrypted_yolov9s_relu/best.xml"),
|
|
1073
|
-
sizeMB: 16,
|
|
1074
|
-
runtimes: ["python"]
|
|
1075
|
-
}
|
|
1319
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9s_relu.xml"), 16)
|
|
1076
1320
|
}
|
|
1077
1321
|
},
|
|
1078
1322
|
{
|
|
@@ -1097,11 +1341,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1097
1341
|
files: [...MLPACKAGE_FILES],
|
|
1098
1342
|
runtimes: ["python"]
|
|
1099
1343
|
},
|
|
1100
|
-
openvino:
|
|
1101
|
-
url: hfScrypted("openvino/scrypted_yolov9c_relu/best.xml"),
|
|
1102
|
-
sizeMB: 49,
|
|
1103
|
-
runtimes: ["python"]
|
|
1104
|
-
}
|
|
1344
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9c_relu.xml"), 49)
|
|
1105
1345
|
}
|
|
1106
1346
|
},
|
|
1107
1347
|
{
|
|
@@ -1126,13 +1366,23 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1126
1366
|
files: [...MLPACKAGE_FILES],
|
|
1127
1367
|
runtimes: ["python"]
|
|
1128
1368
|
},
|
|
1129
|
-
openvino:
|
|
1130
|
-
url: hfScrypted("openvino/scrypted_yolov9m_relu/best.xml"),
|
|
1131
|
-
sizeMB: 38,
|
|
1132
|
-
runtimes: ["python"]
|
|
1133
|
-
}
|
|
1369
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9m_relu.xml"), 38)
|
|
1134
1370
|
}
|
|
1135
|
-
}
|
|
1371
|
+
},
|
|
1372
|
+
ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "fp16", 5),
|
|
1373
|
+
ovPrecisionVariant("yolov9t", "objectDetection/yolov9/openvino", "YOLOv9 Tiny", "int8", 3),
|
|
1374
|
+
ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "fp16", 15),
|
|
1375
|
+
ovPrecisionVariant("yolov9s", "objectDetection/yolov9/openvino", "YOLOv9 Small", "int8", 8),
|
|
1376
|
+
ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "fp16", 5),
|
|
1377
|
+
ovPrecisionVariant("yolo26n", "objectDetection/yolo26/openvino", "YOLO26 Nano", "int8", 3),
|
|
1378
|
+
ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "fp16", 19),
|
|
1379
|
+
ovPrecisionVariant("yolo26s", "objectDetection/yolo26/openvino", "YOLO26 Small", "int8", 10),
|
|
1380
|
+
ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "fp16", 41),
|
|
1381
|
+
ovPrecisionVariant("yolo26m", "objectDetection/yolo26/openvino", "YOLO26 Medium", "int8", 21),
|
|
1382
|
+
ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "fp16", 50),
|
|
1383
|
+
ovPrecisionVariant("yolo26l", "objectDetection/yolo26/openvino", "YOLO26 Large", "int8", 25),
|
|
1384
|
+
ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "fp16", 112),
|
|
1385
|
+
ovPrecisionVariant("yolo26x", "objectDetection/yolo26/openvino", "YOLO26 XLarge", "int8", 56)
|
|
1136
1386
|
];
|
|
1137
1387
|
var FACE_DETECTION_MODELS = [{
|
|
1138
1388
|
id: "scrfd-2.5g",
|
|
@@ -1159,11 +1409,7 @@ var FACE_DETECTION_MODELS = [{
|
|
|
1159
1409
|
files: [...MLPACKAGE_FILES],
|
|
1160
1410
|
runtimes: ["python"]
|
|
1161
1411
|
},
|
|
1162
|
-
openvino:
|
|
1163
|
-
url: hf("faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"),
|
|
1164
|
-
sizeMB: 1.8,
|
|
1165
|
-
runtimes: ["python"]
|
|
1166
|
-
}
|
|
1412
|
+
openvino: ovFormat(hf("faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"), 1.8)
|
|
1167
1413
|
}
|
|
1168
1414
|
}, {
|
|
1169
1415
|
id: "scrypted-yolov9t-face",
|
|
@@ -1190,11 +1436,7 @@ var FACE_DETECTION_MODELS = [{
|
|
|
1190
1436
|
files: [...MLPACKAGE_FILES],
|
|
1191
1437
|
runtimes: ["python"]
|
|
1192
1438
|
},
|
|
1193
|
-
openvino:
|
|
1194
|
-
url: hfScrypted("openvino/scrypted_yolov9t_relu_face/best.xml"),
|
|
1195
|
-
sizeMB: 6,
|
|
1196
|
-
runtimes: ["python"]
|
|
1197
|
-
}
|
|
1439
|
+
openvino: ovFormat(hf("faceDetection/scrypted-yolov9-face/openvino/scrypted_yolov9t_relu_face.xml"), 6)
|
|
1198
1440
|
}
|
|
1199
1441
|
}];
|
|
1200
1442
|
var FACE_EMBEDDING_MODELS = [{
|
|
@@ -1224,11 +1466,7 @@ var FACE_EMBEDDING_MODELS = [{
|
|
|
1224
1466
|
files: [...MLPACKAGE_FILES],
|
|
1225
1467
|
runtimes: ["python"]
|
|
1226
1468
|
},
|
|
1227
|
-
openvino:
|
|
1228
|
-
url: hf("faceRecognition/arcface/openvino/camstack-arcface-r100.xml"),
|
|
1229
|
-
sizeMB: 65,
|
|
1230
|
-
runtimes: ["python"]
|
|
1231
|
-
}
|
|
1469
|
+
openvino: ovFormat(hf("faceRecognition/arcface/openvino/camstack-arcface-r100.xml"), 65)
|
|
1232
1470
|
}
|
|
1233
1471
|
}, {
|
|
1234
1472
|
id: "inception-resnet-v1",
|
|
@@ -1255,11 +1493,7 @@ var FACE_EMBEDDING_MODELS = [{
|
|
|
1255
1493
|
files: [...MLPACKAGE_FILES],
|
|
1256
1494
|
runtimes: ["python"]
|
|
1257
1495
|
},
|
|
1258
|
-
openvino:
|
|
1259
|
-
url: hfScrypted("openvino/inception_resnet_v1/best.xml"),
|
|
1260
|
-
sizeMB: 45,
|
|
1261
|
-
runtimes: ["python"]
|
|
1262
|
-
}
|
|
1496
|
+
openvino: ovFormat(hf("faceRecognition/inception-resnet-v1/openvino/camstack-inception-resnet-v1.xml"), 45)
|
|
1263
1497
|
}
|
|
1264
1498
|
}];
|
|
1265
1499
|
var PLATE_DETECTION_MODELS = [{
|
|
@@ -1287,11 +1521,7 @@ var PLATE_DETECTION_MODELS = [{
|
|
|
1287
1521
|
files: [...MLPACKAGE_FILES],
|
|
1288
1522
|
runtimes: ["python"]
|
|
1289
1523
|
},
|
|
1290
|
-
openvino:
|
|
1291
|
-
url: hf("plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"),
|
|
1292
|
-
sizeMB: 6.1,
|
|
1293
|
-
runtimes: ["python"]
|
|
1294
|
-
}
|
|
1524
|
+
openvino: ovFormat(hf("plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"), 6.1)
|
|
1295
1525
|
}
|
|
1296
1526
|
}];
|
|
1297
1527
|
var PLATE_OCR_MODELS = [{
|
|
@@ -1319,11 +1549,7 @@ var PLATE_OCR_MODELS = [{
|
|
|
1319
1549
|
files: [...MLPACKAGE_FILES],
|
|
1320
1550
|
runtimes: ["python"]
|
|
1321
1551
|
},
|
|
1322
|
-
openvino:
|
|
1323
|
-
url: hfScrypted("openvino/vgg_english_g2/best.xml"),
|
|
1324
|
-
sizeMB: 7.2,
|
|
1325
|
-
runtimes: ["python"]
|
|
1326
|
-
}
|
|
1552
|
+
openvino: ovFormat(hf("plateRecognition/vgg_english_g2/openvino/vgg_english_g2.xml"), 7.2)
|
|
1327
1553
|
}
|
|
1328
1554
|
}];
|
|
1329
1555
|
var ANIMAL_CLASSIFIER_MODELS = [{
|
|
@@ -1352,11 +1578,7 @@ var ANIMAL_CLASSIFIER_MODELS = [{
|
|
|
1352
1578
|
files: [...MLPACKAGE_FILES],
|
|
1353
1579
|
runtimes: ["python"]
|
|
1354
1580
|
},
|
|
1355
|
-
openvino:
|
|
1356
|
-
url: hf("animalClassification/animals-10/openvino/camstack-animals-10.xml"),
|
|
1357
|
-
sizeMB: 164,
|
|
1358
|
-
runtimes: ["python"]
|
|
1359
|
-
}
|
|
1581
|
+
openvino: ovFormat(hf("animalClassification/animals-10/openvino/camstack-animals-10.xml"), 164)
|
|
1360
1582
|
}
|
|
1361
1583
|
}];
|
|
1362
1584
|
var BIRD_CLASSIFIER_MODELS = [{
|
|
@@ -1385,11 +1607,7 @@ var BIRD_CLASSIFIER_MODELS = [{
|
|
|
1385
1607
|
files: [...MLPACKAGE_FILES],
|
|
1386
1608
|
runtimes: ["python"]
|
|
1387
1609
|
},
|
|
1388
|
-
openvino:
|
|
1389
|
-
url: hf("animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml"),
|
|
1390
|
-
sizeMB: 47,
|
|
1391
|
-
runtimes: ["python"]
|
|
1392
|
-
}
|
|
1610
|
+
openvino: ovFormat(hf("animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml"), 47)
|
|
1393
1611
|
},
|
|
1394
1612
|
extraFiles: [{
|
|
1395
1613
|
url: hf("animalClassification/bird-nabirds/onnx/camstack-bird-nabirds-404-labels.json"),
|
|
@@ -1423,11 +1641,7 @@ var VEHICLE_CLASSIFIER_MODELS = [{
|
|
|
1423
1641
|
files: [...MLPACKAGE_FILES],
|
|
1424
1642
|
runtimes: ["python"]
|
|
1425
1643
|
},
|
|
1426
|
-
openvino:
|
|
1427
|
-
url: hf("vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml"),
|
|
1428
|
-
sizeMB: 68,
|
|
1429
|
-
runtimes: ["python"]
|
|
1430
|
-
}
|
|
1644
|
+
openvino: ovFormat(hf("vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml"), 68)
|
|
1431
1645
|
},
|
|
1432
1646
|
extraFiles: [{
|
|
1433
1647
|
url: hf("vehicleClassification/efficientnet/camstack-vehicle-type-labels.json"),
|
|
@@ -1460,11 +1674,7 @@ var SEGMENTATION_REFINER_MODELS = [{
|
|
|
1460
1674
|
files: [...MLPACKAGE_FILES],
|
|
1461
1675
|
runtimes: ["python"]
|
|
1462
1676
|
},
|
|
1463
|
-
openvino:
|
|
1464
|
-
url: hf("segmentationRefiner/u2netp/openvino/camstack-u2netp.xml"),
|
|
1465
|
-
sizeMB: 2.5,
|
|
1466
|
-
runtimes: ["python"]
|
|
1467
|
-
}
|
|
1677
|
+
openvino: ovFormat(hf("segmentationRefiner/u2netp/openvino/camstack-u2netp.xml"), 2.5)
|
|
1468
1678
|
}
|
|
1469
1679
|
}];
|
|
1470
1680
|
var INSTANCE_SEGMENTATION_MODELS = [
|
|
@@ -1490,11 +1700,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1490
1700
|
files: [...MLPACKAGE_FILES],
|
|
1491
1701
|
runtimes: ["python"]
|
|
1492
1702
|
},
|
|
1493
|
-
openvino:
|
|
1494
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml"),
|
|
1495
|
-
sizeMB: 11,
|
|
1496
|
-
runtimes: ["python"]
|
|
1497
|
-
}
|
|
1703
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml"), 11)
|
|
1498
1704
|
}
|
|
1499
1705
|
},
|
|
1500
1706
|
{
|
|
@@ -1519,11 +1725,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1519
1725
|
files: [...MLPACKAGE_FILES],
|
|
1520
1726
|
runtimes: ["python"]
|
|
1521
1727
|
},
|
|
1522
|
-
openvino:
|
|
1523
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml"),
|
|
1524
|
-
sizeMB: 40,
|
|
1525
|
-
runtimes: ["python"]
|
|
1526
|
-
}
|
|
1728
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml"), 40)
|
|
1527
1729
|
}
|
|
1528
1730
|
},
|
|
1529
1731
|
{
|
|
@@ -1548,11 +1750,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1548
1750
|
files: [...MLPACKAGE_FILES],
|
|
1549
1751
|
runtimes: ["python"]
|
|
1550
1752
|
},
|
|
1551
|
-
openvino:
|
|
1552
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml"),
|
|
1553
|
-
sizeMB: 90,
|
|
1554
|
-
runtimes: ["python"]
|
|
1555
|
-
}
|
|
1753
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml"), 90)
|
|
1556
1754
|
}
|
|
1557
1755
|
}
|
|
1558
1756
|
];
|
|
@@ -1568,16 +1766,12 @@ var AUDIO_CLASSIFIER_MODELS = [{
|
|
|
1568
1766
|
preprocessMode: "resize",
|
|
1569
1767
|
formats: {
|
|
1570
1768
|
onnx: {
|
|
1571
|
-
url: hf("
|
|
1769
|
+
url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1572
1770
|
sizeMB: 3.2
|
|
1573
1771
|
},
|
|
1574
|
-
openvino:
|
|
1575
|
-
url: hf("audioClassifier/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1576
|
-
sizeMB: 3.2,
|
|
1577
|
-
runtimes: ["python"]
|
|
1578
|
-
},
|
|
1772
|
+
openvino: ovFormat(hf("audioClassification/yamnet/openvino/camstack-yamnet.xml"), 3.2),
|
|
1579
1773
|
coreml: {
|
|
1580
|
-
url: hf("
|
|
1774
|
+
url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1581
1775
|
sizeMB: 3.2,
|
|
1582
1776
|
runtimes: ["python"]
|
|
1583
1777
|
}
|
|
@@ -1635,7 +1829,7 @@ var ObjectDetectionStep = class {
|
|
|
1635
1829
|
"animal"
|
|
1636
1830
|
],
|
|
1637
1831
|
models: [...OBJECT_DETECTION_MODELS],
|
|
1638
|
-
defaultModelId: "
|
|
1832
|
+
defaultModelId: "yolo26n",
|
|
1639
1833
|
defaultConfidence: .5,
|
|
1640
1834
|
labels: require_dist.COCO_80_LABELS.map((l) => l.id),
|
|
1641
1835
|
classMap: require_dist.COCO_TO_MACRO
|
|
@@ -1884,9 +2078,9 @@ var STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({
|
|
|
1884
2078
|
enabledByDefault: false,
|
|
1885
2079
|
defaultConfidence: .3
|
|
1886
2080
|
});
|
|
1887
|
-
var
|
|
1888
|
-
id: "segmentation
|
|
1889
|
-
name: "
|
|
2081
|
+
var STEP_SEGMENTATION = new PipelineStepBase({
|
|
2082
|
+
id: "segmentation",
|
|
2083
|
+
name: "Segmentation",
|
|
1890
2084
|
slot: "refiner",
|
|
1891
2085
|
postprocessor: "saliency",
|
|
1892
2086
|
extractMode: "crop-roi",
|
|
@@ -1898,7 +2092,7 @@ var STEP_SEGMENTATION_REFINER = new PipelineStepBase({
|
|
|
1898
2092
|
defaultConfidence: 0,
|
|
1899
2093
|
group: "Segmentation"
|
|
1900
2094
|
});
|
|
1901
|
-
|
|
2095
|
+
new PipelineStepBase({
|
|
1902
2096
|
id: "instance-segmentation",
|
|
1903
2097
|
name: "Instance Segmentation",
|
|
1904
2098
|
slot: "refiner",
|
|
@@ -1925,8 +2119,7 @@ var ALL_PIPELINE_STEPS = [
|
|
|
1925
2119
|
new AnimalClassifierStep(),
|
|
1926
2120
|
STEP_BIRD_CLASSIFIER,
|
|
1927
2121
|
STEP_VEHICLE_CLASSIFIER,
|
|
1928
|
-
|
|
1929
|
-
STEP_INSTANCE_SEGMENTATION,
|
|
2122
|
+
STEP_SEGMENTATION,
|
|
1930
2123
|
STEP_AUDIO_CLASSIFIER_INSTANCE
|
|
1931
2124
|
];
|
|
1932
2125
|
/** Compat: flat array of StepDefinition for existing consumers */
|
|
@@ -2165,77 +2358,108 @@ var EngineFactory = class {
|
|
|
2165
2358
|
}
|
|
2166
2359
|
};
|
|
2167
2360
|
//#endregion
|
|
2168
|
-
//#region src/detection-pipeline/engine-
|
|
2169
|
-
/**
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
* and a remote agent (iGPU, or no accelerator at all) must hold INDEPENDENT
|
|
2178
|
-
* selections. Sharing them lets one node's pick (e.g. `openvino/npu`) override
|
|
2179
|
-
* another node that has no NPU.
|
|
2180
|
-
*
|
|
2181
|
-
* These three keys are therefore persisted node-scoped as `<key>@<nodeId>`.
|
|
2182
|
-
* Everything else in the store stays shared. A legacy un-scoped value (written
|
|
2183
|
-
* before this change, or by an older build) is read as a migration fallback and
|
|
2184
|
-
* re-persisted under the node-scoped key on the next write.
|
|
2185
|
-
*/
|
|
2186
|
-
var ENGINE_CASCADE_KEYS = [
|
|
2187
|
-
"engineBackend",
|
|
2188
|
-
"engineDevice",
|
|
2189
|
-
"probedBestEngine"
|
|
2361
|
+
//#region src/detection-pipeline/engine-provisioner.ts
|
|
2362
|
+
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
2363
|
+
var BACKOFF_SCHEDULE_MS = [
|
|
2364
|
+
5e3,
|
|
2365
|
+
15e3,
|
|
2366
|
+
3e4,
|
|
2367
|
+
6e4,
|
|
2368
|
+
12e4,
|
|
2369
|
+
3e5
|
|
2190
2370
|
];
|
|
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
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2371
|
+
var IDLE_STATE = {
|
|
2372
|
+
runtimeId: null,
|
|
2373
|
+
device: null,
|
|
2374
|
+
state: "idle"
|
|
2375
|
+
};
|
|
2376
|
+
var EngineProvisioner = class {
|
|
2377
|
+
fx;
|
|
2378
|
+
current = IDLE_STATE;
|
|
2379
|
+
/** Bumped on every select/dispose — stale async results (old generation) are ignored. */
|
|
2380
|
+
generation = 0;
|
|
2381
|
+
cancelTimer = null;
|
|
2382
|
+
retryIndex = 0;
|
|
2383
|
+
constructor(fx) {
|
|
2384
|
+
this.fx = fx;
|
|
2385
|
+
}
|
|
2386
|
+
get state() {
|
|
2387
|
+
return this.current;
|
|
2388
|
+
}
|
|
2389
|
+
isReady() {
|
|
2390
|
+
return this.current.state === "ready";
|
|
2391
|
+
}
|
|
2392
|
+
select(runtimeId, device) {
|
|
2393
|
+
this.generation++;
|
|
2394
|
+
this.retryIndex = 0;
|
|
2395
|
+
this.clearTimer();
|
|
2396
|
+
this.transition({
|
|
2397
|
+
runtimeId,
|
|
2398
|
+
device,
|
|
2399
|
+
state: "installing",
|
|
2400
|
+
progress: 0
|
|
2401
|
+
});
|
|
2402
|
+
this.provision(this.generation, runtimeId, device);
|
|
2403
|
+
}
|
|
2404
|
+
dispose() {
|
|
2405
|
+
this.generation++;
|
|
2406
|
+
this.clearTimer();
|
|
2407
|
+
}
|
|
2408
|
+
clearTimer() {
|
|
2409
|
+
if (this.cancelTimer !== null) {
|
|
2410
|
+
this.cancelTimer();
|
|
2411
|
+
this.cancelTimer = null;
|
|
2230
2412
|
}
|
|
2231
|
-
out[key] = store[key];
|
|
2232
2413
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2414
|
+
transition(next) {
|
|
2415
|
+
this.current = next;
|
|
2416
|
+
this.fx.onChange(next);
|
|
2236
2417
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2418
|
+
async provision(gen, runtimeId, device) {
|
|
2419
|
+
try {
|
|
2420
|
+
await Promise.all([this.fx.installRequirements(this.fx.requirementsFor(runtimeId)), this.fx.ensureModelForFormat(this.fx.modelFormatFor(runtimeId))]);
|
|
2421
|
+
if (gen !== this.generation) return;
|
|
2422
|
+
this.transition({
|
|
2423
|
+
runtimeId,
|
|
2424
|
+
device,
|
|
2425
|
+
state: "verifying",
|
|
2426
|
+
progress: 100
|
|
2427
|
+
});
|
|
2428
|
+
await this.fx.verify(runtimeId, device);
|
|
2429
|
+
if (gen !== this.generation) return;
|
|
2430
|
+
this.retryIndex = 0;
|
|
2431
|
+
this.transition({
|
|
2432
|
+
runtimeId,
|
|
2433
|
+
device,
|
|
2434
|
+
state: "ready"
|
|
2435
|
+
});
|
|
2436
|
+
} catch (err) {
|
|
2437
|
+
if (gen !== this.generation) return;
|
|
2438
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2439
|
+
const delay = BACKOFF_SCHEDULE_MS[Math.min(this.retryIndex, BACKOFF_SCHEDULE_MS.length - 1)];
|
|
2440
|
+
this.retryIndex++;
|
|
2441
|
+
const nextRetryAt = this.fx.now() + delay;
|
|
2442
|
+
this.transition({
|
|
2443
|
+
runtimeId,
|
|
2444
|
+
device,
|
|
2445
|
+
state: "failed",
|
|
2446
|
+
error: message,
|
|
2447
|
+
nextRetryAt
|
|
2448
|
+
});
|
|
2449
|
+
this.cancelTimer = this.fx.setTimer(delay, () => {
|
|
2450
|
+
if (gen !== this.generation) return;
|
|
2451
|
+
this.cancelTimer = null;
|
|
2452
|
+
this.transition({
|
|
2453
|
+
runtimeId,
|
|
2454
|
+
device,
|
|
2455
|
+
state: "installing",
|
|
2456
|
+
progress: 0
|
|
2457
|
+
});
|
|
2458
|
+
this.provision(gen, runtimeId, device);
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
};
|
|
2239
2463
|
//#endregion
|
|
2240
2464
|
//#region src/detection-pipeline/postprocess/dispatch.ts
|
|
2241
2465
|
var VALID_KINDS = new Set([
|
|
@@ -3066,6 +3290,18 @@ function applyChildOutput(parent, childStep, output, stepLatencyMs, ctx) {
|
|
|
3066
3290
|
parent.mask = output.mask;
|
|
3067
3291
|
parent.maskWidth = output.maskWidth;
|
|
3068
3292
|
parent.maskHeight = output.maskHeight;
|
|
3293
|
+
if (output.maskBbox !== void 0 && output.maskWidth > 0 && output.maskHeight > 0) {
|
|
3294
|
+
const [px1, py1, px2, py2] = parent.bbox;
|
|
3295
|
+
const parentW = px2 - px1;
|
|
3296
|
+
const parentH = py2 - py1;
|
|
3297
|
+
const [mbx, mby, mbw, mbh] = output.maskBbox;
|
|
3298
|
+
parent.refinedBbox = [
|
|
3299
|
+
px1 + mbx / output.maskWidth * parentW,
|
|
3300
|
+
py1 + mby / output.maskHeight * parentH,
|
|
3301
|
+
px1 + (mbx + mbw) / output.maskWidth * parentW,
|
|
3302
|
+
py1 + (mby + mbh) / output.maskHeight * parentH
|
|
3303
|
+
];
|
|
3304
|
+
}
|
|
3069
3305
|
break;
|
|
3070
3306
|
}
|
|
3071
3307
|
}
|
|
@@ -3116,6 +3352,12 @@ function buildFrameResult(input) {
|
|
|
3116
3352
|
macroClass: m.macroClass,
|
|
3117
3353
|
score: m.score,
|
|
3118
3354
|
bbox,
|
|
3355
|
+
...m.refinedBbox !== void 0 ? { refinedBbox: bboxTupleToRect(m.refinedBbox) } : {},
|
|
3356
|
+
...m.mask !== void 0 && m.maskWidth !== void 0 && m.maskHeight !== void 0 ? {
|
|
3357
|
+
mask: m.mask,
|
|
3358
|
+
maskWidth: m.maskWidth,
|
|
3359
|
+
maskHeight: m.maskHeight
|
|
3360
|
+
} : {},
|
|
3119
3361
|
labels,
|
|
3120
3362
|
...m.parentId !== void 0 ? { parentId: m.parentId } : {},
|
|
3121
3363
|
...embedding !== void 0 ? {
|
|
@@ -3547,338 +3789,97 @@ var PipelineExecutor = class {
|
|
|
3547
3789
|
const perMacroThreshold = settings[`minConfidence${capitalize(macroClass)}`];
|
|
3548
3790
|
if (typeof perMacroThreshold === "number" && score < perMacroThreshold) return false;
|
|
3549
3791
|
return true;
|
|
3550
|
-
}
|
|
3551
|
-
};
|
|
3552
|
-
function capitalize(s) {
|
|
3553
|
-
if (s.length === 0) return s;
|
|
3554
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
3555
|
-
}
|
|
3556
|
-
//#endregion
|
|
3557
|
-
//#region src/detection-pipeline/pipeline/tree-builder.ts
|
|
3558
|
-
/**
|
|
3559
|
-
* Build an executable tree from user config.
|
|
3560
|
-
*
|
|
3561
|
-
* @param steps - User-configured pipeline steps (from PipelineDefaultStep[])
|
|
3562
|
-
* @param getEngine - Function that returns an IInferenceEngine for a step ID.
|
|
3563
|
-
* Throws if step not loaded.
|
|
3564
|
-
*/
|
|
3565
|
-
function buildExecutableTree(steps, getEngine) {
|
|
3566
|
-
return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine)) };
|
|
3567
|
-
}
|
|
3568
|
-
function buildNode(step, getEngine) {
|
|
3569
|
-
const definition = getStepDefinition(step.addonId);
|
|
3570
|
-
const engine = getEngine(step.addonId);
|
|
3571
|
-
const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine));
|
|
3572
|
-
const mergedSettings = {
|
|
3573
|
-
...collectSchemaDefaults(step.addonId),
|
|
3574
|
-
...step.settings
|
|
3575
|
-
};
|
|
3576
|
-
return {
|
|
3577
|
-
stepId: step.addonId,
|
|
3578
|
-
definition,
|
|
3579
|
-
engine,
|
|
3580
|
-
modelId: step.modelId,
|
|
3581
|
-
inputClasses: definition.inputClasses ?? [],
|
|
3582
|
-
enabled: step.enabled,
|
|
3583
|
-
children,
|
|
3584
|
-
...Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {}
|
|
3585
|
-
};
|
|
3586
|
-
}
|
|
3587
|
-
/**
|
|
3588
|
-
* Walk the step's declared `getConfigSchema()` and collect every field
|
|
3589
|
-
* with a primitive `default` value into a plain record. Group fields
|
|
3590
|
-
* are flattened — a nested `{ type: 'group', fields: [...] }` is
|
|
3591
|
-
* entered recursively. Fields without a declared default are skipped.
|
|
3592
|
-
*/
|
|
3593
|
-
function collectSchemaDefaults(stepId) {
|
|
3594
|
-
const schema = getStep(stepId).getConfigSchema();
|
|
3595
|
-
const out = {};
|
|
3596
|
-
walkFieldsForDefaults(schema, out);
|
|
3597
|
-
return out;
|
|
3598
|
-
}
|
|
3599
|
-
function walkFieldsForDefaults(fields, out) {
|
|
3600
|
-
for (const field of fields) {
|
|
3601
|
-
if ("type" in field && field.type === "group" && "fields" in field) {
|
|
3602
|
-
walkFieldsForDefaults(field.fields, out);
|
|
3603
|
-
continue;
|
|
3604
|
-
}
|
|
3605
|
-
if ("key" in field && "default" in field && field.default !== void 0) out[field.key] = field.default;
|
|
3606
|
-
}
|
|
3607
|
-
}
|
|
3608
|
-
//#endregion
|
|
3609
|
-
//#region src/detection-pipeline/runtimes.ts
|
|
3610
|
-
var KNOWN_PLATFORMS = [
|
|
3611
|
-
"darwin",
|
|
3612
|
-
"linux",
|
|
3613
|
-
"win32"
|
|
3614
|
-
];
|
|
3615
|
-
var KNOWN_ARCHES = ["arm64", "x64"];
|
|
3616
|
-
var KNOWN_GPU_TYPES = [
|
|
3617
|
-
"nvidia",
|
|
3618
|
-
"amd",
|
|
3619
|
-
"intel",
|
|
3620
|
-
"apple"
|
|
3621
|
-
];
|
|
3622
|
-
var KNOWN_NPU_TYPES = ["apple-ane", "intel-npu"];
|
|
3623
|
-
function toKnownPlatform(p) {
|
|
3624
|
-
return KNOWN_PLATFORMS.find((v) => v === p) ?? "linux";
|
|
3625
|
-
}
|
|
3626
|
-
function toKnownArch(a) {
|
|
3627
|
-
return KNOWN_ARCHES.find((v) => v === a) ?? "x64";
|
|
3628
|
-
}
|
|
3629
|
-
function gpuInfoFrom(hw) {
|
|
3630
|
-
if (!hw.gpu) return null;
|
|
3631
|
-
const type = KNOWN_GPU_TYPES.find((v) => v === hw.gpu?.type);
|
|
3632
|
-
if (!type) return null;
|
|
3633
|
-
return {
|
|
3634
|
-
type,
|
|
3635
|
-
name: ""
|
|
3636
|
-
};
|
|
3637
|
-
}
|
|
3638
|
-
function npuInfoFrom(hw) {
|
|
3639
|
-
if (!hw.npu) return null;
|
|
3640
|
-
const type = KNOWN_NPU_TYPES.find((v) => v === hw.npu?.type);
|
|
3641
|
-
if (!type) return null;
|
|
3642
|
-
return { type };
|
|
3643
|
-
}
|
|
3644
|
-
function envToHardwareInfo(env) {
|
|
3645
|
-
if (!env.hardware) return null;
|
|
3646
|
-
return {
|
|
3647
|
-
platform: toKnownPlatform(env.platform),
|
|
3648
|
-
arch: toKnownArch(env.arch),
|
|
3649
|
-
cpuModel: "",
|
|
3650
|
-
cpuCores: 0,
|
|
3651
|
-
totalRAM_MB: 0,
|
|
3652
|
-
availableRAM_MB: 0,
|
|
3653
|
-
gpu: gpuInfoFrom(env.hardware),
|
|
3654
|
-
npu: npuInfoFrom(env.hardware)
|
|
3655
|
-
};
|
|
3656
|
-
}
|
|
3657
|
-
function probedToHardwareInfo(hw) {
|
|
3658
|
-
if (!hw) return null;
|
|
3659
|
-
return {
|
|
3660
|
-
platform: toKnownPlatform(process.platform),
|
|
3661
|
-
arch: toKnownArch(process.arch),
|
|
3662
|
-
cpuModel: "",
|
|
3663
|
-
cpuCores: 0,
|
|
3664
|
-
totalRAM_MB: 0,
|
|
3665
|
-
availableRAM_MB: 0,
|
|
3666
|
-
gpu: gpuInfoFrom(hw),
|
|
3667
|
-
npu: npuInfoFrom(hw)
|
|
3668
|
-
};
|
|
3669
|
-
}
|
|
3670
|
-
var RUNTIME_DETAIL = {
|
|
3671
|
-
onnx: {
|
|
3672
|
-
label: "ONNX Runtime",
|
|
3673
|
-
pythonRequirements: ["requirements.txt", "requirements-onnxruntime.txt"],
|
|
3674
|
-
tuning: {
|
|
3675
|
-
concurrency: 4,
|
|
3676
|
-
batchMode: "list",
|
|
3677
|
-
maxBatchSize: 8,
|
|
3678
|
-
intraOpThreads: 0
|
|
3679
|
-
}
|
|
3680
|
-
},
|
|
3681
|
-
openvino: {
|
|
3682
|
-
label: "OpenVINO",
|
|
3683
|
-
pythonRequirements: ["requirements.txt", "requirements-openvino.txt"],
|
|
3684
|
-
tuning: {
|
|
3685
|
-
concurrency: 1,
|
|
3686
|
-
batchMode: "none",
|
|
3687
|
-
numStreams: 0
|
|
3688
|
-
}
|
|
3689
|
-
},
|
|
3690
|
-
coreml: {
|
|
3691
|
-
label: "CoreML",
|
|
3692
|
-
pythonRequirements: ["requirements.txt", "requirements-coreml.txt"],
|
|
3693
|
-
tuning: {
|
|
3694
|
-
concurrency: 1,
|
|
3695
|
-
batchMode: "none",
|
|
3696
|
-
windowMs: 8,
|
|
3697
|
-
maxBatchSize: 8,
|
|
3698
|
-
numWorkers: 1
|
|
3699
|
-
}
|
|
3700
|
-
}
|
|
3701
|
-
};
|
|
3702
|
-
/**
|
|
3703
|
-
* Returns the list of supported runtime IDs for the given hardware env.
|
|
3704
|
-
* Delegates to `@camstack/types` `supportedRuntimes`.
|
|
3705
|
-
*/
|
|
3706
|
-
function supportedRuntimes(env) {
|
|
3707
|
-
return require_dist.supportedRuntimes(envToHardwareInfo(env));
|
|
3792
|
+
}
|
|
3793
|
+
};
|
|
3794
|
+
function capitalize(s) {
|
|
3795
|
+
if (s.length === 0) return s;
|
|
3796
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
3708
3797
|
}
|
|
3798
|
+
//#endregion
|
|
3799
|
+
//#region src/detection-pipeline/pipeline/tree-builder.ts
|
|
3709
3800
|
/**
|
|
3710
|
-
*
|
|
3711
|
-
*
|
|
3801
|
+
* Build an executable tree from user config.
|
|
3802
|
+
*
|
|
3803
|
+
* @param steps - User-configured pipeline steps (from PipelineDefaultStep[])
|
|
3804
|
+
* @param getEngine - Function that returns an IInferenceEngine for a step ID.
|
|
3805
|
+
* Throws if step not loaded.
|
|
3712
3806
|
*/
|
|
3713
|
-
function
|
|
3714
|
-
return
|
|
3807
|
+
function buildExecutableTree(steps, getEngine) {
|
|
3808
|
+
return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine)) };
|
|
3715
3809
|
}
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3810
|
+
function buildNode(step, getEngine) {
|
|
3811
|
+
const definition = getStepDefinition(step.addonId);
|
|
3812
|
+
const engine = getEngine(step.addonId);
|
|
3813
|
+
const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine));
|
|
3814
|
+
const mergedSettings = {
|
|
3815
|
+
...collectSchemaDefaults(step.addonId),
|
|
3816
|
+
...step.settings
|
|
3817
|
+
};
|
|
3818
|
+
return {
|
|
3819
|
+
stepId: step.addonId,
|
|
3820
|
+
definition,
|
|
3821
|
+
engine,
|
|
3822
|
+
modelId: step.modelId,
|
|
3823
|
+
inputClasses: definition.inputClasses ?? [],
|
|
3824
|
+
enabled: step.enabled,
|
|
3825
|
+
children,
|
|
3826
|
+
...Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {}
|
|
3827
|
+
};
|
|
3722
3828
|
}
|
|
3723
|
-
/** Model format for each inference runtime supported by the detection pipeline. */
|
|
3724
|
-
var RUNTIME_FORMAT = {
|
|
3725
|
-
onnx: "onnx",
|
|
3726
|
-
openvino: "openvino",
|
|
3727
|
-
coreml: "coreml"
|
|
3728
|
-
};
|
|
3729
3829
|
/**
|
|
3730
|
-
*
|
|
3731
|
-
*
|
|
3732
|
-
*
|
|
3830
|
+
* Walk the step's declared `getConfigSchema()` and collect every field
|
|
3831
|
+
* with a primitive `default` value into a plain record. Group fields
|
|
3832
|
+
* are flattened — a nested `{ type: 'group', fields: [...] }` is
|
|
3833
|
+
* entered recursively. Fields without a declared default are skipped.
|
|
3733
3834
|
*/
|
|
3734
|
-
function
|
|
3735
|
-
|
|
3736
|
-
}
|
|
3737
|
-
|
|
3738
|
-
return
|
|
3739
|
-
}
|
|
3740
|
-
function tuningFor(id) {
|
|
3741
|
-
return RUNTIME_DETAIL[id].tuning;
|
|
3835
|
+
function collectSchemaDefaults(stepId) {
|
|
3836
|
+
const schema = getStep(stepId).getConfigSchema();
|
|
3837
|
+
const out = {};
|
|
3838
|
+
walkFieldsForDefaults(schema, out);
|
|
3839
|
+
return out;
|
|
3742
3840
|
}
|
|
3743
|
-
function
|
|
3744
|
-
|
|
3841
|
+
function walkFieldsForDefaults(fields, out) {
|
|
3842
|
+
for (const field of fields) {
|
|
3843
|
+
if ("type" in field && field.type === "group" && "fields" in field) {
|
|
3844
|
+
walkFieldsForDefaults(field.fields, out);
|
|
3845
|
+
continue;
|
|
3846
|
+
}
|
|
3847
|
+
if ("key" in field && "default" in field && field.default !== void 0) out[field.key] = field.default;
|
|
3848
|
+
}
|
|
3745
3849
|
}
|
|
3850
|
+
//#endregion
|
|
3851
|
+
//#region src/detection-pipeline/registry/custom-models.ts
|
|
3746
3852
|
/**
|
|
3747
|
-
*
|
|
3748
|
-
*
|
|
3749
|
-
*
|
|
3853
|
+
* Group a flat list of custom-model descriptors (as returned by the
|
|
3854
|
+
* `custom-model-registry` collection cap) into a `stepId → entries` map for
|
|
3855
|
+
* the picker / resolution union. Pure; order within a step preserved.
|
|
3750
3856
|
*/
|
|
3751
|
-
function
|
|
3752
|
-
|
|
3753
|
-
|
|
3857
|
+
function groupCustomModelsByStep(descriptors) {
|
|
3858
|
+
const byStep = /* @__PURE__ */ new Map();
|
|
3859
|
+
for (const d of descriptors) {
|
|
3860
|
+
const arr = byStep.get(d.stepId) ?? [];
|
|
3861
|
+
arr.push(d.entry);
|
|
3862
|
+
byStep.set(d.stepId, arr);
|
|
3863
|
+
}
|
|
3864
|
+
return byStep;
|
|
3754
3865
|
}
|
|
3755
|
-
//#endregion
|
|
3756
|
-
//#region src/detection-pipeline/engine-provisioner.ts
|
|
3757
|
-
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
3758
|
-
var BACKOFF_SCHEDULE_MS = [
|
|
3759
|
-
5e3,
|
|
3760
|
-
15e3,
|
|
3761
|
-
3e4,
|
|
3762
|
-
6e4,
|
|
3763
|
-
12e4,
|
|
3764
|
-
3e5
|
|
3765
|
-
];
|
|
3766
|
-
var IDLE_STATE = {
|
|
3767
|
-
runtimeId: null,
|
|
3768
|
-
device: null,
|
|
3769
|
-
state: "idle"
|
|
3770
|
-
};
|
|
3771
|
-
var EngineProvisioner = class {
|
|
3772
|
-
fx;
|
|
3773
|
-
current = IDLE_STATE;
|
|
3774
|
-
/** Bumped on every select/dispose — stale async results (old generation) are ignored. */
|
|
3775
|
-
generation = 0;
|
|
3776
|
-
cancelTimer = null;
|
|
3777
|
-
retryIndex = 0;
|
|
3778
|
-
constructor(fx) {
|
|
3779
|
-
this.fx = fx;
|
|
3780
|
-
}
|
|
3781
|
-
get state() {
|
|
3782
|
-
return this.current;
|
|
3783
|
-
}
|
|
3784
|
-
isReady() {
|
|
3785
|
-
return this.current.state === "ready";
|
|
3786
|
-
}
|
|
3787
|
-
select(runtimeId, device) {
|
|
3788
|
-
this.generation++;
|
|
3789
|
-
this.retryIndex = 0;
|
|
3790
|
-
this.clearTimer();
|
|
3791
|
-
this.transition({
|
|
3792
|
-
runtimeId,
|
|
3793
|
-
device,
|
|
3794
|
-
state: "installing",
|
|
3795
|
-
progress: 0
|
|
3796
|
-
});
|
|
3797
|
-
this.provision(this.generation, runtimeId, device);
|
|
3798
|
-
}
|
|
3799
|
-
dispose() {
|
|
3800
|
-
this.generation++;
|
|
3801
|
-
this.clearTimer();
|
|
3802
|
-
}
|
|
3803
|
-
clearTimer() {
|
|
3804
|
-
if (this.cancelTimer !== null) {
|
|
3805
|
-
this.cancelTimer();
|
|
3806
|
-
this.cancelTimer = null;
|
|
3807
|
-
}
|
|
3808
|
-
}
|
|
3809
|
-
transition(next) {
|
|
3810
|
-
this.current = next;
|
|
3811
|
-
this.fx.onChange(next);
|
|
3812
|
-
}
|
|
3813
|
-
async provision(gen, runtimeId, device) {
|
|
3814
|
-
try {
|
|
3815
|
-
await Promise.all([this.fx.installRequirements(this.fx.requirementsFor(runtimeId)), this.fx.ensureModelForFormat(this.fx.modelFormatFor(runtimeId))]);
|
|
3816
|
-
if (gen !== this.generation) return;
|
|
3817
|
-
this.transition({
|
|
3818
|
-
runtimeId,
|
|
3819
|
-
device,
|
|
3820
|
-
state: "verifying",
|
|
3821
|
-
progress: 100
|
|
3822
|
-
});
|
|
3823
|
-
await this.fx.verify(runtimeId, device);
|
|
3824
|
-
if (gen !== this.generation) return;
|
|
3825
|
-
this.retryIndex = 0;
|
|
3826
|
-
this.transition({
|
|
3827
|
-
runtimeId,
|
|
3828
|
-
device,
|
|
3829
|
-
state: "ready"
|
|
3830
|
-
});
|
|
3831
|
-
} catch (err) {
|
|
3832
|
-
if (gen !== this.generation) return;
|
|
3833
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3834
|
-
const delay = BACKOFF_SCHEDULE_MS[Math.min(this.retryIndex, BACKOFF_SCHEDULE_MS.length - 1)];
|
|
3835
|
-
this.retryIndex++;
|
|
3836
|
-
const nextRetryAt = this.fx.now() + delay;
|
|
3837
|
-
this.transition({
|
|
3838
|
-
runtimeId,
|
|
3839
|
-
device,
|
|
3840
|
-
state: "failed",
|
|
3841
|
-
error: message,
|
|
3842
|
-
nextRetryAt
|
|
3843
|
-
});
|
|
3844
|
-
this.cancelTimer = this.fx.setTimer(delay, () => {
|
|
3845
|
-
if (gen !== this.generation) return;
|
|
3846
|
-
this.cancelTimer = null;
|
|
3847
|
-
this.transition({
|
|
3848
|
-
runtimeId,
|
|
3849
|
-
device,
|
|
3850
|
-
state: "installing",
|
|
3851
|
-
progress: 0
|
|
3852
|
-
});
|
|
3853
|
-
this.provision(gen, runtimeId, device);
|
|
3854
|
-
});
|
|
3855
|
-
}
|
|
3856
|
-
}
|
|
3857
|
-
};
|
|
3858
|
-
//#endregion
|
|
3859
|
-
//#region src/detection-pipeline/auto-pick.ts
|
|
3860
|
-
var PREFERENCE = [
|
|
3861
|
-
"coreml",
|
|
3862
|
-
"openvino",
|
|
3863
|
-
"onnx"
|
|
3864
|
-
];
|
|
3865
3866
|
/**
|
|
3866
|
-
*
|
|
3867
|
-
*
|
|
3868
|
-
*
|
|
3869
|
-
* 1. If `bestBackendHint` is in the supported set, use it.
|
|
3870
|
-
* 2. Otherwise, walk PREFERENCE order and pick the first supported runtime.
|
|
3871
|
-
* 3. Floor to `'onnx'` (always supported).
|
|
3867
|
+
* Union a step's static catalog models with operator-registered custom
|
|
3868
|
+
* models. On an `id` collision the static catalog entry wins — a custom
|
|
3869
|
+
* model can never shadow a built-in one.
|
|
3872
3870
|
*
|
|
3873
|
-
*
|
|
3871
|
+
* Pure + side-effect-free so it can be unit-tested in isolation and called
|
|
3872
|
+
* from the (free) `buildSchemaSlots` builder without any addon context.
|
|
3874
3873
|
*/
|
|
3875
|
-
function
|
|
3876
|
-
const
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3874
|
+
function mergeCustomModels(staticModels, customModels) {
|
|
3875
|
+
const seen = new Set(staticModels.map((m) => m.id));
|
|
3876
|
+
const merged = [...staticModels];
|
|
3877
|
+
for (const m of customModels) {
|
|
3878
|
+
if (seen.has(m.id)) continue;
|
|
3879
|
+
seen.add(m.id);
|
|
3880
|
+
merged.push(m);
|
|
3881
|
+
}
|
|
3882
|
+
return merged;
|
|
3882
3883
|
}
|
|
3883
3884
|
//#endregion
|
|
3884
3885
|
//#region src/detection-pipeline/provider.ts
|
|
@@ -4116,6 +4117,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4116
4117
|
/** Addon context — ctx.api resolves lazily (direct caller created after boot) */
|
|
4117
4118
|
addonCtx = null;
|
|
4118
4119
|
/**
|
|
4120
|
+
* Short-lived cache of custom models pulled from the `custom-model-registry`
|
|
4121
|
+
* collection cap, grouped by step id. TTL-bounded so the picker / resolution
|
|
4122
|
+
* paths don't issue a cap round-trip on every call. Empty map = no provider
|
|
4123
|
+
* (or a query failure) → behaviour identical to the static-catalog-only path.
|
|
4124
|
+
*/
|
|
4125
|
+
customModelsCache = null;
|
|
4126
|
+
/**
|
|
4119
4127
|
* Per-device {@link DeviceProxy} cache used for zone gating at the
|
|
4120
4128
|
* runtime path. Reads `state.zones.value` + `state.zoneRules.value`
|
|
4121
4129
|
* synchronously per frame so detections inside an `exclude` zone
|
|
@@ -4161,6 +4169,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4161
4169
|
*/
|
|
4162
4170
|
needsAutoPick = false;
|
|
4163
4171
|
/**
|
|
4172
|
+
* Unsubscribe handle for the deferred-auto-pick `platform-probe` ready
|
|
4173
|
+
* listener (armed in `setApi` when the probe isn't ready yet). Cleared once
|
|
4174
|
+
* the engine is resolved — either by the listener firing or by the boot
|
|
4175
|
+
* safety-net `ensureBootEngineProvisioned`.
|
|
4176
|
+
*/
|
|
4177
|
+
deferredAutoPickUnsub = null;
|
|
4178
|
+
/**
|
|
4164
4179
|
* Warm cache for benchmark engine-override runs.
|
|
4165
4180
|
*
|
|
4166
4181
|
* Each override rebuild costs a full Python pool spin-up (~300-500ms)
|
|
@@ -4193,7 +4208,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4193
4208
|
this.writeStore = (patch) => settings.writeAddonStore(patch);
|
|
4194
4209
|
this.readDeviceStore = settings.readDeviceStore ?? (async () => ({}));
|
|
4195
4210
|
this.currentEngine = ONNX_FLOOR;
|
|
4196
|
-
this.log.info("Engine
|
|
4211
|
+
this.log.info("Engine pick pending (placeholder until probe / persisted selection)", { meta: {
|
|
4197
4212
|
runtime: this.currentEngine.runtime,
|
|
4198
4213
|
backend: this.currentEngine.backend,
|
|
4199
4214
|
format: this.currentEngine.format
|
|
@@ -4266,26 +4281,70 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4266
4281
|
this.needsAutoPick = false;
|
|
4267
4282
|
this.startProvisioningForCurrentEngine();
|
|
4268
4283
|
} else {
|
|
4269
|
-
this.startProvisioningForCurrentEngine();
|
|
4270
4284
|
const unsubscribe = this.addonCtx.onCapabilityStateChange("platform-probe", { type: "global" }, (state) => {
|
|
4271
4285
|
if (state !== "ready") return;
|
|
4272
|
-
|
|
4286
|
+
this.cancelDeferredAutoPick();
|
|
4273
4287
|
if (!this.needsAutoPick) return;
|
|
4274
4288
|
this.autoPickAndPersist().then(() => {
|
|
4275
4289
|
this.needsAutoPick = false;
|
|
4276
4290
|
this.startProvisioningForCurrentEngine();
|
|
4277
4291
|
});
|
|
4278
4292
|
});
|
|
4279
|
-
this.
|
|
4293
|
+
this.deferredAutoPickUnsub = unsubscribe;
|
|
4294
|
+
this.addonCtx.addDisposer(() => this.cancelDeferredAutoPick());
|
|
4280
4295
|
}
|
|
4281
4296
|
else this.startProvisioningForCurrentEngine();
|
|
4282
4297
|
}
|
|
4298
|
+
/** Tear down the deferred-auto-pick probe listener, if still armed. */
|
|
4299
|
+
cancelDeferredAutoPick() {
|
|
4300
|
+
if (this.deferredAutoPickUnsub) {
|
|
4301
|
+
this.deferredAutoPickUnsub();
|
|
4302
|
+
this.deferredAutoPickUnsub = null;
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
/**
|
|
4306
|
+
* Boot safety-net: deterministically provision the hardware-probed engine.
|
|
4307
|
+
*
|
|
4308
|
+
* Called from the addon's `onInitialize` right after `reprobeEngine` has
|
|
4309
|
+
* written this node's probe-driven selection (e.g. `engineBackend=openvino`)
|
|
4310
|
+
* to the store. When first-boot auto-pick was DEFERRED (probe not ready at
|
|
4311
|
+
* `setApi`), the engine would otherwise stay `idle` — selected but never
|
|
4312
|
+
* provisioned — because the `onCapabilityStateChange('platform-probe')` ready
|
|
4313
|
+
* edge can be missed when the cap is already 'ready' by the time we subscribe
|
|
4314
|
+
* (the old eager-onnx-floor masked this; removing it surfaced a node that
|
|
4315
|
+
* boots with no engine at all). This loads the now-persisted selection and
|
|
4316
|
+
* starts provisioning it, so the node reliably comes up on its real best
|
|
4317
|
+
* engine (openvino on Intel) with no onnx floor and no missed boot. No-op
|
|
4318
|
+
* when provisioning already started (persisted-engine path / listener fired)
|
|
4319
|
+
* or when nothing has been selected yet.
|
|
4320
|
+
*/
|
|
4321
|
+
async ensureBootEngineProvisioned() {
|
|
4322
|
+
if (this.getEngineProvisioning().state !== "idle") return;
|
|
4323
|
+
const stored = await this.loadEngine();
|
|
4324
|
+
if (!stored) return;
|
|
4325
|
+
this.currentEngine = stored;
|
|
4326
|
+
this.needsAutoPick = false;
|
|
4327
|
+
this.cancelDeferredAutoPick();
|
|
4328
|
+
this.log.info("Boot engine provisioning from probed selection", { meta: {
|
|
4329
|
+
runtime: stored.runtime,
|
|
4330
|
+
backend: stored.backend,
|
|
4331
|
+
device: stored.device ?? null
|
|
4332
|
+
} });
|
|
4333
|
+
this.startProvisioningForCurrentEngine();
|
|
4334
|
+
}
|
|
4283
4335
|
/**
|
|
4284
4336
|
* Auto-pick the best supported runtime at first boot (no stored engine).
|
|
4285
4337
|
* Uses the platform-probe cap's hardware + bestScore hint when available;
|
|
4286
4338
|
* falls back to platform/arch when the probe cap is not yet reachable.
|
|
4339
|
+
*
|
|
4287
4340
|
* Persists the selection as `engineBackend` + `engineDevice` so subsequent
|
|
4288
|
-
* boots load it via `loadEngine()` and skip this path
|
|
4341
|
+
* boots load it via `loadEngine()` and skip this path — but ONLY when the
|
|
4342
|
+
* probe actually answered (real `hardware` or a `bestScore` hint). If the
|
|
4343
|
+
* probe query failed (cold-start race, cap momentarily unreachable), the
|
|
4344
|
+
* pick floors to onnx purely for lack of information; persisting that would
|
|
4345
|
+
* LOCK onnx and skip auto-pick on every future boot even after the
|
|
4346
|
+
* accelerator surfaces. In that case we set the in-memory floor for liveness
|
|
4347
|
+
* but leave the store untouched so the next boot re-attempts the pick.
|
|
4289
4348
|
*/
|
|
4290
4349
|
async autoPickAndPersist() {
|
|
4291
4350
|
let hardware = null;
|
|
@@ -4307,6 +4366,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4307
4366
|
device: pick.device
|
|
4308
4367
|
};
|
|
4309
4368
|
this.currentEngine = engine;
|
|
4369
|
+
if (!(hardware !== null || bestBackendHint !== null)) {
|
|
4370
|
+
this.log.warn("Auto-pick: probe returned no hardware/hint — using onnx floor WITHOUT persisting", { meta: {
|
|
4371
|
+
backend: pick.runtimeId,
|
|
4372
|
+
device: pick.device
|
|
4373
|
+
} });
|
|
4374
|
+
return;
|
|
4375
|
+
}
|
|
4310
4376
|
const apNode = this.localProbeNodeId();
|
|
4311
4377
|
await this.writeStore({
|
|
4312
4378
|
[nodeEngineKey("engineBackend", apNode)]: pick.runtimeId,
|
|
@@ -4386,8 +4452,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4386
4452
|
* runtime through installing → verifying → ready.
|
|
4387
4453
|
*/
|
|
4388
4454
|
async onEngineSelectionChanged() {
|
|
4455
|
+
const prev = this.currentEngine;
|
|
4389
4456
|
const stored = await this.loadEngine();
|
|
4390
4457
|
if (stored) this.currentEngine = stored;
|
|
4458
|
+
if ((prev.runtime !== this.currentEngine.runtime || prev.backend !== this.currentEngine.backend || prev.format !== this.currentEngine.format || (prev.device ?? "") !== (this.currentEngine.device ?? "")) && this.engineFactory) {
|
|
4459
|
+
this.log.info("engine selection changed — rebuilding pool in place", { meta: {
|
|
4460
|
+
from: `${prev.backend}/${prev.device ?? "default"}`,
|
|
4461
|
+
to: `${this.currentEngine.backend}/${this.currentEngine.device ?? "default"}`
|
|
4462
|
+
} });
|
|
4463
|
+
try {
|
|
4464
|
+
await this.engineFactory.dispose();
|
|
4465
|
+
} catch {}
|
|
4466
|
+
this.engineFactory = null;
|
|
4467
|
+
this.executor = null;
|
|
4468
|
+
}
|
|
4391
4469
|
this.startProvisioningForCurrentEngine();
|
|
4392
4470
|
}
|
|
4393
4471
|
/**
|
|
@@ -4425,9 +4503,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4425
4503
|
if (!steps || steps.length === 0) return;
|
|
4426
4504
|
for (const step of flattenSteps(steps)) {
|
|
4427
4505
|
if (!step.enabled) continue;
|
|
4428
|
-
const modelEntry =
|
|
4506
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
4429
4507
|
if (!modelEntry) continue;
|
|
4430
|
-
if (
|
|
4508
|
+
if (require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, modelEntry, format)) continue;
|
|
4431
4509
|
await this.downloadWithRetry(modelEntry, format, 3);
|
|
4432
4510
|
}
|
|
4433
4511
|
}
|
|
@@ -4503,10 +4581,44 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4503
4581
|
return { hardware: null };
|
|
4504
4582
|
}
|
|
4505
4583
|
}
|
|
4584
|
+
/**
|
|
4585
|
+
* Pull custom models from the `custom-model-registry` collection cap,
|
|
4586
|
+
* grouped by step id. 5s TTL cache. Fully graceful: when no provider is
|
|
4587
|
+
* registered (or the query fails) it returns an empty map and logs at most
|
|
4588
|
+
* a warning — callers then behave exactly like the static-catalog path.
|
|
4589
|
+
*/
|
|
4590
|
+
async getCustomModels() {
|
|
4591
|
+
const now = Date.now();
|
|
4592
|
+
if (this.customModelsCache && now - this.customModelsCache.at < 5e3) return this.customModelsCache.byStep;
|
|
4593
|
+
let byStep = /* @__PURE__ */ new Map();
|
|
4594
|
+
try {
|
|
4595
|
+
const api = this.addonCtx?.api;
|
|
4596
|
+
if (api) {
|
|
4597
|
+
if ((await api.addons.listCapabilityProviders.query({ capName: "custom-model-registry" })).some((p) => p.isActive)) byStep = groupCustomModelsByStep(await api.customModelRegistry.listModels.query());
|
|
4598
|
+
}
|
|
4599
|
+
} catch (err) {
|
|
4600
|
+
this.log.warn("custom-model-registry query failed — using static catalog only", { meta: { error: require_dist.errMsg(err) } });
|
|
4601
|
+
}
|
|
4602
|
+
this.customModelsCache = {
|
|
4603
|
+
at: now,
|
|
4604
|
+
byStep
|
|
4605
|
+
};
|
|
4606
|
+
return byStep;
|
|
4607
|
+
}
|
|
4608
|
+
/**
|
|
4609
|
+
* Resolve a model id within a step to a catalog entry — static catalog
|
|
4610
|
+
* first, then the custom registry. Returns undefined if neither has it.
|
|
4611
|
+
*/
|
|
4612
|
+
async resolveModelEntry(addonId, modelId) {
|
|
4613
|
+
const fromCatalog = getStepDefinition(addonId).models.find((m) => m.id === modelId);
|
|
4614
|
+
if (fromCatalog) return fromCatalog;
|
|
4615
|
+
return (await this.getCustomModels()).get(addonId)?.find((m) => m.id === modelId);
|
|
4616
|
+
}
|
|
4506
4617
|
async getSchema(engine) {
|
|
4507
4618
|
if (!engine || !engine.runtime) engine = await this.getSelectedEngine();
|
|
4508
4619
|
const format = engine.format;
|
|
4509
|
-
const
|
|
4620
|
+
const customByStep = await this.getCustomModels();
|
|
4621
|
+
const slots = buildSchemaSlots(format, this.modelsDir, customByStep);
|
|
4510
4622
|
const { hardware } = await this.fetchProbeGatingData();
|
|
4511
4623
|
const env = runtimeEnvFromProcess(toProbedHardware(hardware));
|
|
4512
4624
|
return {
|
|
@@ -4635,7 +4747,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4635
4747
|
const formats = {};
|
|
4636
4748
|
for (const [formatKey, entry] of Object.entries(m.formats)) {
|
|
4637
4749
|
if (!entry) continue;
|
|
4638
|
-
const downloaded =
|
|
4750
|
+
const downloaded = require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, m, formatKey);
|
|
4639
4751
|
formats[formatKey] = {
|
|
4640
4752
|
url: entry.url,
|
|
4641
4753
|
sizeMB: entry.sizeMB,
|
|
@@ -4693,7 +4805,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4693
4805
|
}
|
|
4694
4806
|
async downloadModel(input) {
|
|
4695
4807
|
const { modelId, format, addonId } = input;
|
|
4696
|
-
const modelEntry =
|
|
4808
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4697
4809
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4698
4810
|
const formatEntry = modelEntry.formats[format];
|
|
4699
4811
|
if (!formatEntry) throw new Error(`Model "${modelId}" has no ${format} format. Available: ${Object.keys(modelEntry.formats).join(", ")}`);
|
|
@@ -4756,9 +4868,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4756
4868
|
}
|
|
4757
4869
|
async deleteModel(input) {
|
|
4758
4870
|
const { modelId, format, addonId } = input;
|
|
4759
|
-
const modelEntry =
|
|
4871
|
+
const modelEntry = await this.resolveModelEntry(addonId, modelId);
|
|
4760
4872
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4761
|
-
if (!
|
|
4873
|
+
if (!require_model_download_service_C_IHWnXx.deleteModelFromDisk(this.modelsDir, modelEntry, format)) throw new Error(`Model "${modelId}" (${format}) is not downloaded — nothing to delete`);
|
|
4762
4874
|
this.log.info("Model deleted from disk", { meta: {
|
|
4763
4875
|
modelId,
|
|
4764
4876
|
format
|
|
@@ -5212,8 +5324,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5212
5324
|
const work = (async () => {
|
|
5213
5325
|
const format = this.currentEngine?.format ?? "onnx";
|
|
5214
5326
|
for (const step of needed) {
|
|
5215
|
-
const modelEntry =
|
|
5216
|
-
if (modelEntry && !
|
|
5327
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5328
|
+
if (modelEntry && !require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, modelEntry, format)) {
|
|
5217
5329
|
this.log.info("Downloading model for step", { meta: {
|
|
5218
5330
|
modelId: step.modelId,
|
|
5219
5331
|
format,
|
|
@@ -5238,7 +5350,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5238
5350
|
/** Download a model with retry + exponential backoff */
|
|
5239
5351
|
async downloadWithRetry(entry, format, maxRetries, onProgress) {
|
|
5240
5352
|
for (let attempt = 1; attempt <= maxRetries; attempt++) try {
|
|
5241
|
-
await
|
|
5353
|
+
await require_model_download_service_C_IHWnXx.ensureModel(this.modelsDir, entry, format, onProgress);
|
|
5242
5354
|
this.log.info("Model downloaded successfully", { meta: { modelId: entry.id } });
|
|
5243
5355
|
return;
|
|
5244
5356
|
} catch (err) {
|
|
@@ -5707,7 +5819,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5707
5819
|
const format = this.currentEngine.format;
|
|
5708
5820
|
const downloads = [];
|
|
5709
5821
|
for (const step of flattenSteps(steps)) {
|
|
5710
|
-
const modelEntry =
|
|
5822
|
+
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5711
5823
|
if (!modelEntry) {
|
|
5712
5824
|
this.log.warn("Model not found in step catalog — skipping download", { meta: {
|
|
5713
5825
|
modelId: step.modelId,
|
|
@@ -5715,11 +5827,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5715
5827
|
} });
|
|
5716
5828
|
continue;
|
|
5717
5829
|
}
|
|
5718
|
-
if (!
|
|
5830
|
+
if (!require_model_download_service_C_IHWnXx.isModelDownloaded(this.modelsDir, modelEntry, format)) this.log.info("Downloading model", { meta: {
|
|
5719
5831
|
modelId: step.modelId,
|
|
5720
5832
|
format
|
|
5721
5833
|
} });
|
|
5722
|
-
downloads.push(
|
|
5834
|
+
downloads.push(require_model_download_service_C_IHWnXx.ensureModel(this.modelsDir, modelEntry, format).then(() => {}));
|
|
5723
5835
|
}
|
|
5724
5836
|
await Promise.all(downloads);
|
|
5725
5837
|
await this.ensureBackendDeps(this.currentEngine);
|
|
@@ -6110,11 +6222,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6110
6222
|
}
|
|
6111
6223
|
}
|
|
6112
6224
|
};
|
|
6113
|
-
function buildSchemaSlots(format, modelsDir) {
|
|
6225
|
+
function buildSchemaSlots(format, modelsDir, customByStep) {
|
|
6114
6226
|
const slotMap = /* @__PURE__ */ new Map();
|
|
6115
6227
|
for (const pipelineStep of ALL_PIPELINE_STEPS) {
|
|
6116
6228
|
const step = pipelineStep.definition;
|
|
6117
|
-
const availableModels = step.models.filter((m) => m.formats[format]);
|
|
6229
|
+
const availableModels = mergeCustomModels(step.models, customByStep?.get(step.id) ?? []).filter((m) => m.formats[format]);
|
|
6118
6230
|
if (availableModels.length === 0) continue;
|
|
6119
6231
|
const slot = step.slot;
|
|
6120
6232
|
if (!slotMap.has(slot)) slotMap.set(slot, []);
|
|
@@ -6130,7 +6242,7 @@ function buildSchemaSlots(format, modelsDir) {
|
|
|
6130
6242
|
id: m.id,
|
|
6131
6243
|
name: m.name,
|
|
6132
6244
|
formats: Object.fromEntries(Object.entries(m.formats).map(([f, entry]) => [f, {
|
|
6133
|
-
downloaded:
|
|
6245
|
+
downloaded: require_model_download_service_C_IHWnXx.isModelDownloaded(modelsDir, m, f),
|
|
6134
6246
|
sizeMB: entry.sizeMB
|
|
6135
6247
|
}]))
|
|
6136
6248
|
})),
|
|
@@ -6219,8 +6331,7 @@ function buildDefaultStepTree(format) {
|
|
|
6219
6331
|
makeStep("animal-classifier", [], { enabled: false }),
|
|
6220
6332
|
makeStep("bird-classifier", [], { enabled: false }),
|
|
6221
6333
|
makeStep("vehicle-classifier", [], { enabled: false }),
|
|
6222
|
-
makeStep("segmentation
|
|
6223
|
-
makeStep("instance-segmentation", [], { enabled: false })
|
|
6334
|
+
makeStep("segmentation", [], { enabled: false })
|
|
6224
6335
|
].filter((s) => s !== null));
|
|
6225
6336
|
const audioEngine = getDefaultModelForFormat("audio-classifier", format) === "apple-soundanalysis" ? {
|
|
6226
6337
|
runtime: "python",
|
|
@@ -6510,8 +6621,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6510
6621
|
label: "Execution provider",
|
|
6511
6622
|
options: [...STATIC_BACKEND_OPTIONS],
|
|
6512
6623
|
default: DEFAULT_CONFIG.engineBackend,
|
|
6513
|
-
immediate: true
|
|
6514
|
-
requiresRestart: true
|
|
6624
|
+
immediate: true
|
|
6515
6625
|
}),
|
|
6516
6626
|
this.field({
|
|
6517
6627
|
type: "select",
|
|
@@ -6519,8 +6629,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6519
6629
|
label: "Hardware device",
|
|
6520
6630
|
options: [...STATIC_DEFAULT_DEVICE_OPTIONS],
|
|
6521
6631
|
default: DEFAULT_CONFIG.engineDevice,
|
|
6522
|
-
immediate: true
|
|
6523
|
-
requiresRestart: true
|
|
6632
|
+
immediate: true
|
|
6524
6633
|
})
|
|
6525
6634
|
]
|
|
6526
6635
|
}, {
|
|
@@ -6559,8 +6668,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6559
6668
|
"onnx",
|
|
6560
6669
|
"cpu"
|
|
6561
6670
|
]
|
|
6562
|
-
}
|
|
6563
|
-
requiresRestart: true
|
|
6671
|
+
}
|
|
6564
6672
|
}),
|
|
6565
6673
|
this.field({
|
|
6566
6674
|
type: "slider",
|
|
@@ -6577,8 +6685,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6577
6685
|
showWhen: {
|
|
6578
6686
|
field: "batchMode",
|
|
6579
6687
|
notEquals: "none"
|
|
6580
|
-
}
|
|
6581
|
-
requiresRestart: true
|
|
6688
|
+
}
|
|
6582
6689
|
}),
|
|
6583
6690
|
this.field({
|
|
6584
6691
|
type: "slider",
|
|
@@ -6595,8 +6702,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6595
6702
|
showWhen: {
|
|
6596
6703
|
field: "batchMode",
|
|
6597
6704
|
notEquals: "none"
|
|
6598
|
-
}
|
|
6599
|
-
requiresRestart: true
|
|
6705
|
+
}
|
|
6600
6706
|
}),
|
|
6601
6707
|
this.field({
|
|
6602
6708
|
type: "slider",
|
|
@@ -6609,8 +6715,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6609
6715
|
default: void 0,
|
|
6610
6716
|
showValue: true,
|
|
6611
6717
|
nullable: true,
|
|
6612
|
-
nullLabel: "Auto"
|
|
6613
|
-
requiresRestart: true
|
|
6718
|
+
nullLabel: "Auto"
|
|
6614
6719
|
}),
|
|
6615
6720
|
this.field({
|
|
6616
6721
|
type: "slider",
|
|
@@ -6623,8 +6728,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6623
6728
|
default: void 0,
|
|
6624
6729
|
showValue: true,
|
|
6625
6730
|
nullable: true,
|
|
6626
|
-
nullLabel: "Auto"
|
|
6627
|
-
requiresRestart: true
|
|
6731
|
+
nullLabel: "Auto"
|
|
6628
6732
|
})
|
|
6629
6733
|
]
|
|
6630
6734
|
}] });
|
|
@@ -6887,6 +6991,9 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6887
6991
|
if (!this.nodeProbedBestEngine) await this.provider.reprobeEngine().catch((err) => {
|
|
6888
6992
|
this.ctx.logger.warn("auto-reprobe engine failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
6889
6993
|
});
|
|
6994
|
+
await this.provider.ensureBootEngineProvisioned().catch((err) => {
|
|
6995
|
+
this.ctx.logger.warn("ensureBootEngineProvisioned failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
6996
|
+
});
|
|
6890
6997
|
await this.provider.warmPool();
|
|
6891
6998
|
this.engineMetricsTimer = setInterval(() => this.emitEngineMetricsSnapshot(), ENGINE_METRICS_SNAPSHOT_INTERVAL_MS);
|
|
6892
6999
|
this.lastAppliedPoolConfig = this.snapshotPoolConfig();
|
|
@@ -6994,16 +7101,17 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6994
7101
|
return false;
|
|
6995
7102
|
}
|
|
6996
7103
|
/**
|
|
6997
|
-
* BaseAddon calls `onConfigChanged` after every settings write.
|
|
6998
|
-
*
|
|
6999
|
-
*
|
|
7000
|
-
*
|
|
7001
|
-
*
|
|
7002
|
-
* respawn the
|
|
7003
|
-
*
|
|
7004
|
-
*
|
|
7005
|
-
*
|
|
7006
|
-
*
|
|
7104
|
+
* BaseAddon calls `onConfigChanged` after every settings write. This is the
|
|
7105
|
+
* SOLE apply path for engine + tuning changes — none of those fields declare
|
|
7106
|
+
* `requiresRestart` anymore (an addon restart re-probes hardware + reloads
|
|
7107
|
+
* every model on every set, which is exactly what we want to avoid). Instead:
|
|
7108
|
+
* - Pool-bound tuning (`concurrency`/`batchMode`/`windowMs`/…) → in-place
|
|
7109
|
+
* pool respawn when the snapshot flips (`poolConfigChanged` below).
|
|
7110
|
+
* - Engine cascade (`engineBackend`/`engineDevice`) → the provider's
|
|
7111
|
+
* `onEngineSelectionChanged` disposes the device-bound factory so the next
|
|
7112
|
+
* runPipeline rebuilds the pool on the new selection, in place.
|
|
7113
|
+
* Both apply optimistically — the inference gate stays closed for the short
|
|
7114
|
+
* re-spin, frames are dropped (never crashed), no addon bounce.
|
|
7007
7115
|
*/
|
|
7008
7116
|
async onConfigChanged() {
|
|
7009
7117
|
await this.refreshNodeEngineFromStore();
|