@camstack/addon-pipeline 1.0.8 → 1.1.0
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 +5 -5
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +636 -659
- package/dist/detection-pipeline/index.mjs +624 -647
- package/dist/{model-download-service-C7AjBsX9-rXY-VFDk.js → model-download-service-RxAOiYvX-C8rTRJy_.js} +36 -6
- package/dist/{model-download-service-C7AjBsX9-B0ekM6dF.mjs → model-download-service-RxAOiYvX-CMAvhgO7.mjs} +36 -6
- package/dist/recorder/index.js +3 -3
- package/dist/recorder/index.mjs +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-DrohyZ5L.mjs} +3 -3
- package/dist/stream-broker/{hostInit-zRy9SzlX.mjs → hostInit-zLZbYJcg.mjs} +3 -3
- package/dist/stream-broker/index.js +7 -7
- package/dist/stream-broker/index.mjs +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/python/inference_pool.py +65 -6
- 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
|
|
5
|
+
const require_model_download_service_RxAOiYvX = require("../model-download-service-RxAOiYvX-C8rTRJy_.js");
|
|
6
6
|
const require_dist = require("../dist-BLcTVvol.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
|
-
node_fs =
|
|
8
|
+
node_fs = require_model_download_service_RxAOiYvX.__toESM(node_fs);
|
|
9
9
|
let node_path = require("node:path");
|
|
10
|
-
node_path =
|
|
10
|
+
node_path = require_model_download_service_RxAOiYvX.__toESM(node_path);
|
|
11
11
|
let node_os = require("node:os");
|
|
12
|
-
node_os =
|
|
12
|
+
node_os = require_model_download_service_RxAOiYvX.__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_RxAOiYvX.__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,26 @@ 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
|
+
};
|
|
782
1046
|
var MLPACKAGE_FILES = [
|
|
783
1047
|
"Manifest.json",
|
|
784
1048
|
"Data/com.apple.CoreML/model.mlmodel",
|
|
@@ -807,11 +1071,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
807
1071
|
files: [...MLPACKAGE_FILES],
|
|
808
1072
|
runtimes: ["python"]
|
|
809
1073
|
},
|
|
810
|
-
openvino:
|
|
811
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9t.xml"),
|
|
812
|
-
sizeMB: 6,
|
|
813
|
-
runtimes: ["python"]
|
|
814
|
-
}
|
|
1074
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9t.xml"), 6)
|
|
815
1075
|
}
|
|
816
1076
|
},
|
|
817
1077
|
{
|
|
@@ -836,11 +1096,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
836
1096
|
files: [...MLPACKAGE_FILES],
|
|
837
1097
|
runtimes: ["python"]
|
|
838
1098
|
},
|
|
839
|
-
openvino:
|
|
840
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9s.xml"),
|
|
841
|
-
sizeMB: 16,
|
|
842
|
-
runtimes: ["python"]
|
|
843
|
-
}
|
|
1099
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9s.xml"), 16)
|
|
844
1100
|
}
|
|
845
1101
|
},
|
|
846
1102
|
{
|
|
@@ -865,11 +1121,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
865
1121
|
files: [...MLPACKAGE_FILES],
|
|
866
1122
|
runtimes: ["python"]
|
|
867
1123
|
},
|
|
868
|
-
openvino:
|
|
869
|
-
url: hf("objectDetection/yolov9/openvino/camstack-yolov9c.xml"),
|
|
870
|
-
sizeMB: 49,
|
|
871
|
-
runtimes: ["python"]
|
|
872
|
-
}
|
|
1124
|
+
openvino: ovFormat(hf("objectDetection/yolov9/openvino/camstack-yolov9c.xml"), 49)
|
|
873
1125
|
}
|
|
874
1126
|
},
|
|
875
1127
|
{
|
|
@@ -894,11 +1146,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
894
1146
|
files: [...MLPACKAGE_FILES],
|
|
895
1147
|
runtimes: ["python"]
|
|
896
1148
|
},
|
|
897
|
-
openvino:
|
|
898
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26n.xml"),
|
|
899
|
-
sizeMB: 9,
|
|
900
|
-
runtimes: ["python"]
|
|
901
|
-
}
|
|
1149
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26n.xml"), 9)
|
|
902
1150
|
}
|
|
903
1151
|
},
|
|
904
1152
|
{
|
|
@@ -923,11 +1171,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
923
1171
|
files: [...MLPACKAGE_FILES],
|
|
924
1172
|
runtimes: ["python"]
|
|
925
1173
|
},
|
|
926
|
-
openvino:
|
|
927
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26s.xml"),
|
|
928
|
-
sizeMB: 36,
|
|
929
|
-
runtimes: ["python"]
|
|
930
|
-
}
|
|
1174
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26s.xml"), 36)
|
|
931
1175
|
}
|
|
932
1176
|
},
|
|
933
1177
|
{
|
|
@@ -952,11 +1196,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
952
1196
|
files: [...MLPACKAGE_FILES],
|
|
953
1197
|
runtimes: ["python"]
|
|
954
1198
|
},
|
|
955
|
-
openvino:
|
|
956
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26m.xml"),
|
|
957
|
-
sizeMB: 78,
|
|
958
|
-
runtimes: ["python"]
|
|
959
|
-
}
|
|
1199
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26m.xml"), 78)
|
|
960
1200
|
}
|
|
961
1201
|
},
|
|
962
1202
|
{
|
|
@@ -981,11 +1221,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
981
1221
|
files: [...MLPACKAGE_FILES],
|
|
982
1222
|
runtimes: ["python"]
|
|
983
1223
|
},
|
|
984
|
-
openvino:
|
|
985
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26l.xml"),
|
|
986
|
-
sizeMB: 95,
|
|
987
|
-
runtimes: ["python"]
|
|
988
|
-
}
|
|
1224
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26l.xml"), 95)
|
|
989
1225
|
}
|
|
990
1226
|
},
|
|
991
1227
|
{
|
|
@@ -1010,11 +1246,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1010
1246
|
files: [...MLPACKAGE_FILES],
|
|
1011
1247
|
runtimes: ["python"]
|
|
1012
1248
|
},
|
|
1013
|
-
openvino:
|
|
1014
|
-
url: hf("objectDetection/yolo26/openvino/camstack-yolo26x.xml"),
|
|
1015
|
-
sizeMB: 213,
|
|
1016
|
-
runtimes: ["python"]
|
|
1017
|
-
}
|
|
1249
|
+
openvino: ovFormat(hf("objectDetection/yolo26/openvino/camstack-yolo26x.xml"), 213)
|
|
1018
1250
|
}
|
|
1019
1251
|
},
|
|
1020
1252
|
{
|
|
@@ -1039,11 +1271,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1039
1271
|
files: [...MLPACKAGE_FILES],
|
|
1040
1272
|
runtimes: ["python"]
|
|
1041
1273
|
},
|
|
1042
|
-
openvino:
|
|
1043
|
-
url: hfScrypted("openvino/scrypted_yolov9t_relu/best.xml"),
|
|
1044
|
-
sizeMB: 6,
|
|
1045
|
-
runtimes: ["python"]
|
|
1046
|
-
}
|
|
1274
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9t_relu.xml"), 6)
|
|
1047
1275
|
}
|
|
1048
1276
|
},
|
|
1049
1277
|
{
|
|
@@ -1068,11 +1296,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1068
1296
|
files: [...MLPACKAGE_FILES],
|
|
1069
1297
|
runtimes: ["python"]
|
|
1070
1298
|
},
|
|
1071
|
-
openvino:
|
|
1072
|
-
url: hfScrypted("openvino/scrypted_yolov9s_relu/best.xml"),
|
|
1073
|
-
sizeMB: 16,
|
|
1074
|
-
runtimes: ["python"]
|
|
1075
|
-
}
|
|
1299
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9s_relu.xml"), 16)
|
|
1076
1300
|
}
|
|
1077
1301
|
},
|
|
1078
1302
|
{
|
|
@@ -1097,11 +1321,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1097
1321
|
files: [...MLPACKAGE_FILES],
|
|
1098
1322
|
runtimes: ["python"]
|
|
1099
1323
|
},
|
|
1100
|
-
openvino:
|
|
1101
|
-
url: hfScrypted("openvino/scrypted_yolov9c_relu/best.xml"),
|
|
1102
|
-
sizeMB: 49,
|
|
1103
|
-
runtimes: ["python"]
|
|
1104
|
-
}
|
|
1324
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9c_relu.xml"), 49)
|
|
1105
1325
|
}
|
|
1106
1326
|
},
|
|
1107
1327
|
{
|
|
@@ -1126,11 +1346,7 @@ var OBJECT_DETECTION_MODELS = [
|
|
|
1126
1346
|
files: [...MLPACKAGE_FILES],
|
|
1127
1347
|
runtimes: ["python"]
|
|
1128
1348
|
},
|
|
1129
|
-
openvino:
|
|
1130
|
-
url: hfScrypted("openvino/scrypted_yolov9m_relu/best.xml"),
|
|
1131
|
-
sizeMB: 38,
|
|
1132
|
-
runtimes: ["python"]
|
|
1133
|
-
}
|
|
1349
|
+
openvino: ovFormat(hf("objectDetection/scrypted-yolov9-relu/openvino/scrypted_yolov9m_relu.xml"), 38)
|
|
1134
1350
|
}
|
|
1135
1351
|
}
|
|
1136
1352
|
];
|
|
@@ -1159,11 +1375,7 @@ var FACE_DETECTION_MODELS = [{
|
|
|
1159
1375
|
files: [...MLPACKAGE_FILES],
|
|
1160
1376
|
runtimes: ["python"]
|
|
1161
1377
|
},
|
|
1162
|
-
openvino:
|
|
1163
|
-
url: hf("faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"),
|
|
1164
|
-
sizeMB: 1.8,
|
|
1165
|
-
runtimes: ["python"]
|
|
1166
|
-
}
|
|
1378
|
+
openvino: ovFormat(hf("faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml"), 1.8)
|
|
1167
1379
|
}
|
|
1168
1380
|
}, {
|
|
1169
1381
|
id: "scrypted-yolov9t-face",
|
|
@@ -1190,11 +1402,7 @@ var FACE_DETECTION_MODELS = [{
|
|
|
1190
1402
|
files: [...MLPACKAGE_FILES],
|
|
1191
1403
|
runtimes: ["python"]
|
|
1192
1404
|
},
|
|
1193
|
-
openvino:
|
|
1194
|
-
url: hfScrypted("openvino/scrypted_yolov9t_relu_face/best.xml"),
|
|
1195
|
-
sizeMB: 6,
|
|
1196
|
-
runtimes: ["python"]
|
|
1197
|
-
}
|
|
1405
|
+
openvino: ovFormat(hf("faceDetection/scrypted-yolov9-face/openvino/scrypted_yolov9t_relu_face.xml"), 6)
|
|
1198
1406
|
}
|
|
1199
1407
|
}];
|
|
1200
1408
|
var FACE_EMBEDDING_MODELS = [{
|
|
@@ -1224,11 +1432,7 @@ var FACE_EMBEDDING_MODELS = [{
|
|
|
1224
1432
|
files: [...MLPACKAGE_FILES],
|
|
1225
1433
|
runtimes: ["python"]
|
|
1226
1434
|
},
|
|
1227
|
-
openvino:
|
|
1228
|
-
url: hf("faceRecognition/arcface/openvino/camstack-arcface-r100.xml"),
|
|
1229
|
-
sizeMB: 65,
|
|
1230
|
-
runtimes: ["python"]
|
|
1231
|
-
}
|
|
1435
|
+
openvino: ovFormat(hf("faceRecognition/arcface/openvino/camstack-arcface-r100.xml"), 65)
|
|
1232
1436
|
}
|
|
1233
1437
|
}, {
|
|
1234
1438
|
id: "inception-resnet-v1",
|
|
@@ -1255,11 +1459,7 @@ var FACE_EMBEDDING_MODELS = [{
|
|
|
1255
1459
|
files: [...MLPACKAGE_FILES],
|
|
1256
1460
|
runtimes: ["python"]
|
|
1257
1461
|
},
|
|
1258
|
-
openvino:
|
|
1259
|
-
url: hfScrypted("openvino/inception_resnet_v1/best.xml"),
|
|
1260
|
-
sizeMB: 45,
|
|
1261
|
-
runtimes: ["python"]
|
|
1262
|
-
}
|
|
1462
|
+
openvino: ovFormat(hf("faceRecognition/inception-resnet-v1/openvino/camstack-inception-resnet-v1.xml"), 45)
|
|
1263
1463
|
}
|
|
1264
1464
|
}];
|
|
1265
1465
|
var PLATE_DETECTION_MODELS = [{
|
|
@@ -1287,11 +1487,7 @@ var PLATE_DETECTION_MODELS = [{
|
|
|
1287
1487
|
files: [...MLPACKAGE_FILES],
|
|
1288
1488
|
runtimes: ["python"]
|
|
1289
1489
|
},
|
|
1290
|
-
openvino:
|
|
1291
|
-
url: hf("plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"),
|
|
1292
|
-
sizeMB: 6.1,
|
|
1293
|
-
runtimes: ["python"]
|
|
1294
|
-
}
|
|
1490
|
+
openvino: ovFormat(hf("plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml"), 6.1)
|
|
1295
1491
|
}
|
|
1296
1492
|
}];
|
|
1297
1493
|
var PLATE_OCR_MODELS = [{
|
|
@@ -1319,11 +1515,7 @@ var PLATE_OCR_MODELS = [{
|
|
|
1319
1515
|
files: [...MLPACKAGE_FILES],
|
|
1320
1516
|
runtimes: ["python"]
|
|
1321
1517
|
},
|
|
1322
|
-
openvino:
|
|
1323
|
-
url: hfScrypted("openvino/vgg_english_g2/best.xml"),
|
|
1324
|
-
sizeMB: 7.2,
|
|
1325
|
-
runtimes: ["python"]
|
|
1326
|
-
}
|
|
1518
|
+
openvino: ovFormat(hf("plateRecognition/vgg_english_g2/openvino/vgg_english_g2.xml"), 7.2)
|
|
1327
1519
|
}
|
|
1328
1520
|
}];
|
|
1329
1521
|
var ANIMAL_CLASSIFIER_MODELS = [{
|
|
@@ -1352,11 +1544,7 @@ var ANIMAL_CLASSIFIER_MODELS = [{
|
|
|
1352
1544
|
files: [...MLPACKAGE_FILES],
|
|
1353
1545
|
runtimes: ["python"]
|
|
1354
1546
|
},
|
|
1355
|
-
openvino:
|
|
1356
|
-
url: hf("animalClassification/animals-10/openvino/camstack-animals-10.xml"),
|
|
1357
|
-
sizeMB: 164,
|
|
1358
|
-
runtimes: ["python"]
|
|
1359
|
-
}
|
|
1547
|
+
openvino: ovFormat(hf("animalClassification/animals-10/openvino/camstack-animals-10.xml"), 164)
|
|
1360
1548
|
}
|
|
1361
1549
|
}];
|
|
1362
1550
|
var BIRD_CLASSIFIER_MODELS = [{
|
|
@@ -1385,11 +1573,7 @@ var BIRD_CLASSIFIER_MODELS = [{
|
|
|
1385
1573
|
files: [...MLPACKAGE_FILES],
|
|
1386
1574
|
runtimes: ["python"]
|
|
1387
1575
|
},
|
|
1388
|
-
openvino:
|
|
1389
|
-
url: hf("animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml"),
|
|
1390
|
-
sizeMB: 47,
|
|
1391
|
-
runtimes: ["python"]
|
|
1392
|
-
}
|
|
1576
|
+
openvino: ovFormat(hf("animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml"), 47)
|
|
1393
1577
|
},
|
|
1394
1578
|
extraFiles: [{
|
|
1395
1579
|
url: hf("animalClassification/bird-nabirds/onnx/camstack-bird-nabirds-404-labels.json"),
|
|
@@ -1423,11 +1607,7 @@ var VEHICLE_CLASSIFIER_MODELS = [{
|
|
|
1423
1607
|
files: [...MLPACKAGE_FILES],
|
|
1424
1608
|
runtimes: ["python"]
|
|
1425
1609
|
},
|
|
1426
|
-
openvino:
|
|
1427
|
-
url: hf("vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml"),
|
|
1428
|
-
sizeMB: 68,
|
|
1429
|
-
runtimes: ["python"]
|
|
1430
|
-
}
|
|
1610
|
+
openvino: ovFormat(hf("vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml"), 68)
|
|
1431
1611
|
},
|
|
1432
1612
|
extraFiles: [{
|
|
1433
1613
|
url: hf("vehicleClassification/efficientnet/camstack-vehicle-type-labels.json"),
|
|
@@ -1460,11 +1640,7 @@ var SEGMENTATION_REFINER_MODELS = [{
|
|
|
1460
1640
|
files: [...MLPACKAGE_FILES],
|
|
1461
1641
|
runtimes: ["python"]
|
|
1462
1642
|
},
|
|
1463
|
-
openvino:
|
|
1464
|
-
url: hf("segmentationRefiner/u2netp/openvino/camstack-u2netp.xml"),
|
|
1465
|
-
sizeMB: 2.5,
|
|
1466
|
-
runtimes: ["python"]
|
|
1467
|
-
}
|
|
1643
|
+
openvino: ovFormat(hf("segmentationRefiner/u2netp/openvino/camstack-u2netp.xml"), 2.5)
|
|
1468
1644
|
}
|
|
1469
1645
|
}];
|
|
1470
1646
|
var INSTANCE_SEGMENTATION_MODELS = [
|
|
@@ -1490,11 +1666,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1490
1666
|
files: [...MLPACKAGE_FILES],
|
|
1491
1667
|
runtimes: ["python"]
|
|
1492
1668
|
},
|
|
1493
|
-
openvino:
|
|
1494
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml"),
|
|
1495
|
-
sizeMB: 11,
|
|
1496
|
-
runtimes: ["python"]
|
|
1497
|
-
}
|
|
1669
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml"), 11)
|
|
1498
1670
|
}
|
|
1499
1671
|
},
|
|
1500
1672
|
{
|
|
@@ -1519,11 +1691,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1519
1691
|
files: [...MLPACKAGE_FILES],
|
|
1520
1692
|
runtimes: ["python"]
|
|
1521
1693
|
},
|
|
1522
|
-
openvino:
|
|
1523
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml"),
|
|
1524
|
-
sizeMB: 40,
|
|
1525
|
-
runtimes: ["python"]
|
|
1526
|
-
}
|
|
1694
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml"), 40)
|
|
1527
1695
|
}
|
|
1528
1696
|
},
|
|
1529
1697
|
{
|
|
@@ -1548,11 +1716,7 @@ var INSTANCE_SEGMENTATION_MODELS = [
|
|
|
1548
1716
|
files: [...MLPACKAGE_FILES],
|
|
1549
1717
|
runtimes: ["python"]
|
|
1550
1718
|
},
|
|
1551
|
-
openvino:
|
|
1552
|
-
url: hf("segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml"),
|
|
1553
|
-
sizeMB: 90,
|
|
1554
|
-
runtimes: ["python"]
|
|
1555
|
-
}
|
|
1719
|
+
openvino: ovFormat(hf("segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml"), 90)
|
|
1556
1720
|
}
|
|
1557
1721
|
}
|
|
1558
1722
|
];
|
|
@@ -1568,16 +1732,12 @@ var AUDIO_CLASSIFIER_MODELS = [{
|
|
|
1568
1732
|
preprocessMode: "resize",
|
|
1569
1733
|
formats: {
|
|
1570
1734
|
onnx: {
|
|
1571
|
-
url: hf("
|
|
1735
|
+
url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1572
1736
|
sizeMB: 3.2
|
|
1573
1737
|
},
|
|
1574
|
-
openvino:
|
|
1575
|
-
url: hf("audioClassifier/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1576
|
-
sizeMB: 3.2,
|
|
1577
|
-
runtimes: ["python"]
|
|
1578
|
-
},
|
|
1738
|
+
openvino: ovFormat(hf("audioClassification/yamnet/openvino/camstack-yamnet.xml"), 3.2),
|
|
1579
1739
|
coreml: {
|
|
1580
|
-
url: hf("
|
|
1740
|
+
url: hf("audioClassification/yamnet/onnx/camstack-yamnet.onnx"),
|
|
1581
1741
|
sizeMB: 3.2,
|
|
1582
1742
|
runtimes: ["python"]
|
|
1583
1743
|
}
|
|
@@ -2165,77 +2325,108 @@ var EngineFactory = class {
|
|
|
2165
2325
|
}
|
|
2166
2326
|
};
|
|
2167
2327
|
//#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"
|
|
2328
|
+
//#region src/detection-pipeline/engine-provisioner.ts
|
|
2329
|
+
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
2330
|
+
var BACKOFF_SCHEDULE_MS = [
|
|
2331
|
+
5e3,
|
|
2332
|
+
15e3,
|
|
2333
|
+
3e4,
|
|
2334
|
+
6e4,
|
|
2335
|
+
12e4,
|
|
2336
|
+
3e5
|
|
2190
2337
|
];
|
|
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
|
-
|
|
2338
|
+
var IDLE_STATE = {
|
|
2339
|
+
runtimeId: null,
|
|
2340
|
+
device: null,
|
|
2341
|
+
state: "idle"
|
|
2342
|
+
};
|
|
2343
|
+
var EngineProvisioner = class {
|
|
2344
|
+
fx;
|
|
2345
|
+
current = IDLE_STATE;
|
|
2346
|
+
/** Bumped on every select/dispose — stale async results (old generation) are ignored. */
|
|
2347
|
+
generation = 0;
|
|
2348
|
+
cancelTimer = null;
|
|
2349
|
+
retryIndex = 0;
|
|
2350
|
+
constructor(fx) {
|
|
2351
|
+
this.fx = fx;
|
|
2352
|
+
}
|
|
2353
|
+
get state() {
|
|
2354
|
+
return this.current;
|
|
2355
|
+
}
|
|
2356
|
+
isReady() {
|
|
2357
|
+
return this.current.state === "ready";
|
|
2358
|
+
}
|
|
2359
|
+
select(runtimeId, device) {
|
|
2360
|
+
this.generation++;
|
|
2361
|
+
this.retryIndex = 0;
|
|
2362
|
+
this.clearTimer();
|
|
2363
|
+
this.transition({
|
|
2364
|
+
runtimeId,
|
|
2365
|
+
device,
|
|
2366
|
+
state: "installing",
|
|
2367
|
+
progress: 0
|
|
2368
|
+
});
|
|
2369
|
+
this.provision(this.generation, runtimeId, device);
|
|
2370
|
+
}
|
|
2371
|
+
dispose() {
|
|
2372
|
+
this.generation++;
|
|
2373
|
+
this.clearTimer();
|
|
2374
|
+
}
|
|
2375
|
+
clearTimer() {
|
|
2376
|
+
if (this.cancelTimer !== null) {
|
|
2377
|
+
this.cancelTimer();
|
|
2378
|
+
this.cancelTimer = null;
|
|
2230
2379
|
}
|
|
2231
|
-
out[key] = store[key];
|
|
2232
2380
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2381
|
+
transition(next) {
|
|
2382
|
+
this.current = next;
|
|
2383
|
+
this.fx.onChange(next);
|
|
2236
2384
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2385
|
+
async provision(gen, runtimeId, device) {
|
|
2386
|
+
try {
|
|
2387
|
+
await Promise.all([this.fx.installRequirements(this.fx.requirementsFor(runtimeId)), this.fx.ensureModelForFormat(this.fx.modelFormatFor(runtimeId))]);
|
|
2388
|
+
if (gen !== this.generation) return;
|
|
2389
|
+
this.transition({
|
|
2390
|
+
runtimeId,
|
|
2391
|
+
device,
|
|
2392
|
+
state: "verifying",
|
|
2393
|
+
progress: 100
|
|
2394
|
+
});
|
|
2395
|
+
await this.fx.verify(runtimeId, device);
|
|
2396
|
+
if (gen !== this.generation) return;
|
|
2397
|
+
this.retryIndex = 0;
|
|
2398
|
+
this.transition({
|
|
2399
|
+
runtimeId,
|
|
2400
|
+
device,
|
|
2401
|
+
state: "ready"
|
|
2402
|
+
});
|
|
2403
|
+
} catch (err) {
|
|
2404
|
+
if (gen !== this.generation) return;
|
|
2405
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2406
|
+
const delay = BACKOFF_SCHEDULE_MS[Math.min(this.retryIndex, BACKOFF_SCHEDULE_MS.length - 1)];
|
|
2407
|
+
this.retryIndex++;
|
|
2408
|
+
const nextRetryAt = this.fx.now() + delay;
|
|
2409
|
+
this.transition({
|
|
2410
|
+
runtimeId,
|
|
2411
|
+
device,
|
|
2412
|
+
state: "failed",
|
|
2413
|
+
error: message,
|
|
2414
|
+
nextRetryAt
|
|
2415
|
+
});
|
|
2416
|
+
this.cancelTimer = this.fx.setTimer(delay, () => {
|
|
2417
|
+
if (gen !== this.generation) return;
|
|
2418
|
+
this.cancelTimer = null;
|
|
2419
|
+
this.transition({
|
|
2420
|
+
runtimeId,
|
|
2421
|
+
device,
|
|
2422
|
+
state: "installing",
|
|
2423
|
+
progress: 0
|
|
2424
|
+
});
|
|
2425
|
+
this.provision(gen, runtimeId, device);
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2239
2430
|
//#endregion
|
|
2240
2431
|
//#region src/detection-pipeline/postprocess/dispatch.ts
|
|
2241
2432
|
var VALID_KINDS = new Set([
|
|
@@ -3423,463 +3614,188 @@ var PipelineExecutor = class {
|
|
|
3423
3614
|
inferenceMs: Date.now() - inferenceStart,
|
|
3424
3615
|
postprocessMs: 0,
|
|
3425
3616
|
inputType,
|
|
3426
|
-
inputWidth,
|
|
3427
|
-
inputHeight,
|
|
3428
|
-
parentDetection: parentClass,
|
|
3429
|
-
output: errorOutput,
|
|
3430
|
-
error: errorMsg
|
|
3431
|
-
});
|
|
3432
|
-
stepTimings.push({
|
|
3433
|
-
source: step.stepId,
|
|
3434
|
-
modelId: step.modelId,
|
|
3435
|
-
ms: Date.now() - preprocessStart,
|
|
3436
|
-
detectionCount: 0
|
|
3437
|
-
});
|
|
3438
|
-
throw new Error(`Inference failed for step "${step.stepId}": ${errorMsg}`, { cause: err });
|
|
3439
|
-
}
|
|
3440
|
-
const structured = engineOutput.structured ?? {};
|
|
3441
|
-
const inferenceMs = typeof structured.inferenceMs === "number" ? structured.inferenceMs : Date.now() - inferenceStart;
|
|
3442
|
-
if (typeof structured.preprocessMs === "number") poolAgg.preprocessMs += structured.preprocessMs;
|
|
3443
|
-
if (typeof structured.predictMs === "number") poolAgg.predictMs += structured.predictMs;
|
|
3444
|
-
if (typeof structured.batchSize === "number" && structured.batchSize > poolAgg.batchSize) poolAgg.batchSize = structured.batchSize;
|
|
3445
|
-
const postprocessStart = Date.now();
|
|
3446
|
-
const output = dispatchPostprocess(engineOutput, step.definition);
|
|
3447
|
-
const postprocessMs = Date.now() - postprocessStart;
|
|
3448
|
-
stepTimings.push({
|
|
3449
|
-
source: step.stepId,
|
|
3450
|
-
modelId: step.modelId,
|
|
3451
|
-
ms: preprocessMs + inferenceMs + postprocessMs,
|
|
3452
|
-
detectionCount: output.kind === "detections" ? output.detections.length : 0
|
|
3453
|
-
});
|
|
3454
|
-
if (traceBuilder.isActive) traceBuilder.addStep({
|
|
3455
|
-
stepId: step.stepId,
|
|
3456
|
-
modelId: step.modelId,
|
|
3457
|
-
slot: step.definition.slot,
|
|
3458
|
-
postprocessor: step.definition.postprocessor,
|
|
3459
|
-
preprocessMs,
|
|
3460
|
-
inferenceMs,
|
|
3461
|
-
postprocessMs,
|
|
3462
|
-
inputType,
|
|
3463
|
-
inputWidth,
|
|
3464
|
-
inputHeight,
|
|
3465
|
-
parentDetection: parentClass,
|
|
3466
|
-
output
|
|
3467
|
-
});
|
|
3468
|
-
return output;
|
|
3469
|
-
}
|
|
3470
|
-
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg) {
|
|
3471
|
-
for (const child of children) {
|
|
3472
|
-
if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
|
|
3473
|
-
const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
|
|
3474
|
-
if (typeof minParentScore === "number" && parentDetection.score < minParentScore) continue;
|
|
3475
|
-
if (isBboxDegenerate(parentDetection.bbox)) continue;
|
|
3476
|
-
try {
|
|
3477
|
-
const childStart = Date.now();
|
|
3478
|
-
const fullFrameJpeg = await fullFrameJpegProvider();
|
|
3479
|
-
const modelEntry = child.definition.models.find((m) => m.id === child.modelId);
|
|
3480
|
-
let cropJpegBuf;
|
|
3481
|
-
let cropW;
|
|
3482
|
-
let cropH;
|
|
3483
|
-
if (modelEntry?.faceAlignment) {
|
|
3484
|
-
const minFaceSize = typeof child.settings?.["minFaceSize"] === "number" ? child.settings["minFaceSize"] : DEFAULT_MIN_FACE_SIZE_PX;
|
|
3485
|
-
if (bboxShortSide(parentDetection.bbox) < minFaceSize) continue;
|
|
3486
|
-
const lms = parentDetection.landmarks;
|
|
3487
|
-
if (lms && lms.length >= 5) {
|
|
3488
|
-
const aligned = await alignFaceCrop(fullFrameJpeg, parentDetection.bbox, lms, imageWidth, imageHeight, { outSize: modelEntry.inputSize.width });
|
|
3489
|
-
cropJpegBuf = aligned.jpeg;
|
|
3490
|
-
cropW = aligned.width;
|
|
3491
|
-
cropH = aligned.height;
|
|
3492
|
-
} else {
|
|
3493
|
-
const crop = await cropJpeg(fullFrameJpeg, parentDetection.bbox, imageWidth, imageHeight);
|
|
3494
|
-
cropJpegBuf = crop.jpeg;
|
|
3495
|
-
cropW = crop.width;
|
|
3496
|
-
cropH = crop.height;
|
|
3497
|
-
}
|
|
3498
|
-
} else {
|
|
3499
|
-
const crop = await cropJpeg(fullFrameJpeg, parentDetection.bbox, imageWidth, imageHeight);
|
|
3500
|
-
cropJpegBuf = crop.jpeg;
|
|
3501
|
-
cropW = crop.width;
|
|
3502
|
-
cropH = crop.height;
|
|
3503
|
-
}
|
|
3504
|
-
const childOutput = await this.executeStep(child, {
|
|
3505
|
-
kind: "jpeg",
|
|
3506
|
-
data: cropJpegBuf
|
|
3507
|
-
}, cropW, cropH, "crop-roi", parentDetection.bbox, parentDetection.macroClass, traceBuilder, stepTimings, poolAgg);
|
|
3508
|
-
const childMs = Date.now() - childStart;
|
|
3509
|
-
const detailsBefore = ctx.details.length;
|
|
3510
|
-
applyChildOutput(parentDetection, child, childOutput, childMs, ctx);
|
|
3511
|
-
if (childOutput.kind === "detections" && child.children.length > 0) {
|
|
3512
|
-
const newDetails = ctx.details.slice(detailsBefore);
|
|
3513
|
-
for (const detail of newDetails) {
|
|
3514
|
-
detail.bbox = transformBboxToImageSpace(detail.bbox, parentDetection.bbox);
|
|
3515
|
-
if (detail.landmarks) {
|
|
3516
|
-
const [px1, py1] = parentDetection.bbox;
|
|
3517
|
-
detail.landmarks = detail.landmarks.map((l) => ({
|
|
3518
|
-
x: l.x + px1,
|
|
3519
|
-
y: l.y + py1
|
|
3520
|
-
}));
|
|
3521
|
-
}
|
|
3522
|
-
await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg);
|
|
3523
|
-
}
|
|
3524
|
-
}
|
|
3525
|
-
} catch {}
|
|
3526
|
-
}
|
|
3527
|
-
}
|
|
3528
|
-
matchesInputClasses(className, inputClasses) {
|
|
3529
|
-
if (inputClasses.length === 0) return true;
|
|
3530
|
-
return inputClasses.includes(className);
|
|
3531
|
-
}
|
|
3532
|
-
/**
|
|
3533
|
-
* Apply the object-detection step's macro filter + per-macro
|
|
3534
|
-
* minConfidence sliders introduced in the Phase 6 step rework.
|
|
3535
|
-
*
|
|
3536
|
-
* Expected `settings` shape:
|
|
3537
|
-
* - `enabledMacroClasses`: readonly string[] (e.g. ['person','vehicle','animal']).
|
|
3538
|
-
* Empty array = all allowed (legacy behaviour).
|
|
3539
|
-
* - `minConfidence<Macro>`: number (e.g. `minConfidencePerson: 0.5`)
|
|
3540
|
-
*/
|
|
3541
|
-
matchesMacroFilter(macroClass, score, settings) {
|
|
3542
|
-
if (!settings) return true;
|
|
3543
|
-
const enabled = settings["enabledMacroClasses"];
|
|
3544
|
-
if (Array.isArray(enabled) && enabled.length > 0) {
|
|
3545
|
-
if (!enabled.includes(macroClass)) return false;
|
|
3546
|
-
}
|
|
3547
|
-
const perMacroThreshold = settings[`minConfidence${capitalize(macroClass)}`];
|
|
3548
|
-
if (typeof perMacroThreshold === "number" && score < perMacroThreshold) return false;
|
|
3549
|
-
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));
|
|
3708
|
-
}
|
|
3709
|
-
/**
|
|
3710
|
-
* Returns the device options for a given runtime and probed hardware.
|
|
3711
|
-
* Delegates to `@camstack/types` `runtimeDevices`.
|
|
3712
|
-
*/
|
|
3713
|
-
function runtimeDevices(id, hardware) {
|
|
3714
|
-
return require_dist.runtimeDevices(id, probedToHardwareInfo(hardware));
|
|
3715
|
-
}
|
|
3716
|
-
/**
|
|
3717
|
-
* Returns the default device string for a given runtime.
|
|
3718
|
-
* Delegates to `@camstack/types` `defaultDeviceFor`.
|
|
3719
|
-
*/
|
|
3720
|
-
function defaultDeviceFor(id) {
|
|
3721
|
-
return require_dist.defaultDeviceFor(id);
|
|
3722
|
-
}
|
|
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
|
-
/**
|
|
3730
|
-
* Returns the model format required for a given runtime.
|
|
3731
|
-
* Returns the locally-typed format string ('onnx' | 'openvino' | 'coreml')
|
|
3732
|
-
* matching the engine-provisioner's own ModelFormat type.
|
|
3733
|
-
*/
|
|
3734
|
-
function modelFormatFor(id) {
|
|
3735
|
-
return RUNTIME_FORMAT[id];
|
|
3736
|
-
}
|
|
3737
|
-
function pythonRequirementsFor(id) {
|
|
3738
|
-
return RUNTIME_DETAIL[id].pythonRequirements;
|
|
3739
|
-
}
|
|
3740
|
-
function tuningFor(id) {
|
|
3741
|
-
return RUNTIME_DETAIL[id].tuning;
|
|
3742
|
-
}
|
|
3743
|
-
function runtimeLabel(id) {
|
|
3744
|
-
return RUNTIME_DETAIL[id].label;
|
|
3745
|
-
}
|
|
3746
|
-
/**
|
|
3747
|
-
* Proactive-install hint kept for back-compat (re-exported from index.ts).
|
|
3748
|
-
* Intel on Linux warrants installing openvino; coremltools covers macOS;
|
|
3749
|
-
* openvino has no AMD/NVIDIA backend.
|
|
3750
|
-
*/
|
|
3751
|
-
function shouldInstallOpenvino(env) {
|
|
3752
|
-
if (env.platform === "darwin") return false;
|
|
3753
|
-
return env.gpu?.type === "intel" || env.npu?.type === "intel-npu";
|
|
3754
|
-
}
|
|
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
|
|
3617
|
+
inputWidth,
|
|
3618
|
+
inputHeight,
|
|
3619
|
+
parentDetection: parentClass,
|
|
3620
|
+
output: errorOutput,
|
|
3621
|
+
error: errorMsg
|
|
3622
|
+
});
|
|
3623
|
+
stepTimings.push({
|
|
3624
|
+
source: step.stepId,
|
|
3625
|
+
modelId: step.modelId,
|
|
3626
|
+
ms: Date.now() - preprocessStart,
|
|
3627
|
+
detectionCount: 0
|
|
3628
|
+
});
|
|
3629
|
+
throw new Error(`Inference failed for step "${step.stepId}": ${errorMsg}`, { cause: err });
|
|
3630
|
+
}
|
|
3631
|
+
const structured = engineOutput.structured ?? {};
|
|
3632
|
+
const inferenceMs = typeof structured.inferenceMs === "number" ? structured.inferenceMs : Date.now() - inferenceStart;
|
|
3633
|
+
if (typeof structured.preprocessMs === "number") poolAgg.preprocessMs += structured.preprocessMs;
|
|
3634
|
+
if (typeof structured.predictMs === "number") poolAgg.predictMs += structured.predictMs;
|
|
3635
|
+
if (typeof structured.batchSize === "number" && structured.batchSize > poolAgg.batchSize) poolAgg.batchSize = structured.batchSize;
|
|
3636
|
+
const postprocessStart = Date.now();
|
|
3637
|
+
const output = dispatchPostprocess(engineOutput, step.definition);
|
|
3638
|
+
const postprocessMs = Date.now() - postprocessStart;
|
|
3639
|
+
stepTimings.push({
|
|
3640
|
+
source: step.stepId,
|
|
3641
|
+
modelId: step.modelId,
|
|
3642
|
+
ms: preprocessMs + inferenceMs + postprocessMs,
|
|
3643
|
+
detectionCount: output.kind === "detections" ? output.detections.length : 0
|
|
3796
3644
|
});
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3645
|
+
if (traceBuilder.isActive) traceBuilder.addStep({
|
|
3646
|
+
stepId: step.stepId,
|
|
3647
|
+
modelId: step.modelId,
|
|
3648
|
+
slot: step.definition.slot,
|
|
3649
|
+
postprocessor: step.definition.postprocessor,
|
|
3650
|
+
preprocessMs,
|
|
3651
|
+
inferenceMs,
|
|
3652
|
+
postprocessMs,
|
|
3653
|
+
inputType,
|
|
3654
|
+
inputWidth,
|
|
3655
|
+
inputHeight,
|
|
3656
|
+
parentDetection: parentClass,
|
|
3657
|
+
output
|
|
3658
|
+
});
|
|
3659
|
+
return output;
|
|
3802
3660
|
}
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
this.
|
|
3806
|
-
|
|
3661
|
+
async executeChildren(children, parentDetection, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg) {
|
|
3662
|
+
for (const child of children) {
|
|
3663
|
+
if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
|
|
3664
|
+
const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
|
|
3665
|
+
if (typeof minParentScore === "number" && parentDetection.score < minParentScore) continue;
|
|
3666
|
+
if (isBboxDegenerate(parentDetection.bbox)) continue;
|
|
3667
|
+
try {
|
|
3668
|
+
const childStart = Date.now();
|
|
3669
|
+
const fullFrameJpeg = await fullFrameJpegProvider();
|
|
3670
|
+
const modelEntry = child.definition.models.find((m) => m.id === child.modelId);
|
|
3671
|
+
let cropJpegBuf;
|
|
3672
|
+
let cropW;
|
|
3673
|
+
let cropH;
|
|
3674
|
+
if (modelEntry?.faceAlignment) {
|
|
3675
|
+
const minFaceSize = typeof child.settings?.["minFaceSize"] === "number" ? child.settings["minFaceSize"] : DEFAULT_MIN_FACE_SIZE_PX;
|
|
3676
|
+
if (bboxShortSide(parentDetection.bbox) < minFaceSize) continue;
|
|
3677
|
+
const lms = parentDetection.landmarks;
|
|
3678
|
+
if (lms && lms.length >= 5) {
|
|
3679
|
+
const aligned = await alignFaceCrop(fullFrameJpeg, parentDetection.bbox, lms, imageWidth, imageHeight, { outSize: modelEntry.inputSize.width });
|
|
3680
|
+
cropJpegBuf = aligned.jpeg;
|
|
3681
|
+
cropW = aligned.width;
|
|
3682
|
+
cropH = aligned.height;
|
|
3683
|
+
} else {
|
|
3684
|
+
const crop = await cropJpeg(fullFrameJpeg, parentDetection.bbox, imageWidth, imageHeight);
|
|
3685
|
+
cropJpegBuf = crop.jpeg;
|
|
3686
|
+
cropW = crop.width;
|
|
3687
|
+
cropH = crop.height;
|
|
3688
|
+
}
|
|
3689
|
+
} else {
|
|
3690
|
+
const crop = await cropJpeg(fullFrameJpeg, parentDetection.bbox, imageWidth, imageHeight);
|
|
3691
|
+
cropJpegBuf = crop.jpeg;
|
|
3692
|
+
cropW = crop.width;
|
|
3693
|
+
cropH = crop.height;
|
|
3694
|
+
}
|
|
3695
|
+
const childOutput = await this.executeStep(child, {
|
|
3696
|
+
kind: "jpeg",
|
|
3697
|
+
data: cropJpegBuf
|
|
3698
|
+
}, cropW, cropH, "crop-roi", parentDetection.bbox, parentDetection.macroClass, traceBuilder, stepTimings, poolAgg);
|
|
3699
|
+
const childMs = Date.now() - childStart;
|
|
3700
|
+
const detailsBefore = ctx.details.length;
|
|
3701
|
+
applyChildOutput(parentDetection, child, childOutput, childMs, ctx);
|
|
3702
|
+
if (childOutput.kind === "detections" && child.children.length > 0) {
|
|
3703
|
+
const newDetails = ctx.details.slice(detailsBefore);
|
|
3704
|
+
for (const detail of newDetails) {
|
|
3705
|
+
detail.bbox = transformBboxToImageSpace(detail.bbox, parentDetection.bbox);
|
|
3706
|
+
if (detail.landmarks) {
|
|
3707
|
+
const [px1, py1] = parentDetection.bbox;
|
|
3708
|
+
detail.landmarks = detail.landmarks.map((l) => ({
|
|
3709
|
+
x: l.x + px1,
|
|
3710
|
+
y: l.y + py1
|
|
3711
|
+
}));
|
|
3712
|
+
}
|
|
3713
|
+
await this.executeChildren(child.children, detail, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg);
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
} catch {}
|
|
3807
3717
|
}
|
|
3808
3718
|
}
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3719
|
+
matchesInputClasses(className, inputClasses) {
|
|
3720
|
+
if (inputClasses.length === 0) return true;
|
|
3721
|
+
return inputClasses.includes(className);
|
|
3812
3722
|
}
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
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
|
-
});
|
|
3723
|
+
/**
|
|
3724
|
+
* Apply the object-detection step's macro filter + per-macro
|
|
3725
|
+
* minConfidence sliders introduced in the Phase 6 step rework.
|
|
3726
|
+
*
|
|
3727
|
+
* Expected `settings` shape:
|
|
3728
|
+
* - `enabledMacroClasses`: readonly string[] (e.g. ['person','vehicle','animal']).
|
|
3729
|
+
* Empty array = all allowed (legacy behaviour).
|
|
3730
|
+
* - `minConfidence<Macro>`: number (e.g. `minConfidencePerson: 0.5`)
|
|
3731
|
+
*/
|
|
3732
|
+
matchesMacroFilter(macroClass, score, settings) {
|
|
3733
|
+
if (!settings) return true;
|
|
3734
|
+
const enabled = settings["enabledMacroClasses"];
|
|
3735
|
+
if (Array.isArray(enabled) && enabled.length > 0) {
|
|
3736
|
+
if (!enabled.includes(macroClass)) return false;
|
|
3855
3737
|
}
|
|
3738
|
+
const perMacroThreshold = settings[`minConfidence${capitalize(macroClass)}`];
|
|
3739
|
+
if (typeof perMacroThreshold === "number" && score < perMacroThreshold) return false;
|
|
3740
|
+
return true;
|
|
3856
3741
|
}
|
|
3857
3742
|
};
|
|
3743
|
+
function capitalize(s) {
|
|
3744
|
+
if (s.length === 0) return s;
|
|
3745
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
3746
|
+
}
|
|
3858
3747
|
//#endregion
|
|
3859
|
-
//#region src/detection-pipeline/
|
|
3860
|
-
var PREFERENCE = [
|
|
3861
|
-
"coreml",
|
|
3862
|
-
"openvino",
|
|
3863
|
-
"onnx"
|
|
3864
|
-
];
|
|
3748
|
+
//#region src/detection-pipeline/pipeline/tree-builder.ts
|
|
3865
3749
|
/**
|
|
3866
|
-
*
|
|
3867
|
-
*
|
|
3868
|
-
* Logic:
|
|
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).
|
|
3750
|
+
* Build an executable tree from user config.
|
|
3872
3751
|
*
|
|
3873
|
-
*
|
|
3752
|
+
* @param steps - User-configured pipeline steps (from PipelineDefaultStep[])
|
|
3753
|
+
* @param getEngine - Function that returns an IInferenceEngine for a step ID.
|
|
3754
|
+
* Throws if step not loaded.
|
|
3874
3755
|
*/
|
|
3875
|
-
function
|
|
3876
|
-
|
|
3877
|
-
|
|
3756
|
+
function buildExecutableTree(steps, getEngine) {
|
|
3757
|
+
return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine)) };
|
|
3758
|
+
}
|
|
3759
|
+
function buildNode(step, getEngine) {
|
|
3760
|
+
const definition = getStepDefinition(step.addonId);
|
|
3761
|
+
const engine = getEngine(step.addonId);
|
|
3762
|
+
const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine));
|
|
3763
|
+
const mergedSettings = {
|
|
3764
|
+
...collectSchemaDefaults(step.addonId),
|
|
3765
|
+
...step.settings
|
|
3766
|
+
};
|
|
3878
3767
|
return {
|
|
3879
|
-
|
|
3880
|
-
|
|
3768
|
+
stepId: step.addonId,
|
|
3769
|
+
definition,
|
|
3770
|
+
engine,
|
|
3771
|
+
modelId: step.modelId,
|
|
3772
|
+
inputClasses: definition.inputClasses ?? [],
|
|
3773
|
+
enabled: step.enabled,
|
|
3774
|
+
children,
|
|
3775
|
+
...Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {}
|
|
3881
3776
|
};
|
|
3882
3777
|
}
|
|
3778
|
+
/**
|
|
3779
|
+
* Walk the step's declared `getConfigSchema()` and collect every field
|
|
3780
|
+
* with a primitive `default` value into a plain record. Group fields
|
|
3781
|
+
* are flattened — a nested `{ type: 'group', fields: [...] }` is
|
|
3782
|
+
* entered recursively. Fields without a declared default are skipped.
|
|
3783
|
+
*/
|
|
3784
|
+
function collectSchemaDefaults(stepId) {
|
|
3785
|
+
const schema = getStep(stepId).getConfigSchema();
|
|
3786
|
+
const out = {};
|
|
3787
|
+
walkFieldsForDefaults(schema, out);
|
|
3788
|
+
return out;
|
|
3789
|
+
}
|
|
3790
|
+
function walkFieldsForDefaults(fields, out) {
|
|
3791
|
+
for (const field of fields) {
|
|
3792
|
+
if ("type" in field && field.type === "group" && "fields" in field) {
|
|
3793
|
+
walkFieldsForDefaults(field.fields, out);
|
|
3794
|
+
continue;
|
|
3795
|
+
}
|
|
3796
|
+
if ("key" in field && "default" in field && field.default !== void 0) out[field.key] = field.default;
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3883
3799
|
//#endregion
|
|
3884
3800
|
//#region src/detection-pipeline/provider.ts
|
|
3885
3801
|
/**
|
|
@@ -4161,6 +4077,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4161
4077
|
*/
|
|
4162
4078
|
needsAutoPick = false;
|
|
4163
4079
|
/**
|
|
4080
|
+
* Unsubscribe handle for the deferred-auto-pick `platform-probe` ready
|
|
4081
|
+
* listener (armed in `setApi` when the probe isn't ready yet). Cleared once
|
|
4082
|
+
* the engine is resolved — either by the listener firing or by the boot
|
|
4083
|
+
* safety-net `ensureBootEngineProvisioned`.
|
|
4084
|
+
*/
|
|
4085
|
+
deferredAutoPickUnsub = null;
|
|
4086
|
+
/**
|
|
4164
4087
|
* Warm cache for benchmark engine-override runs.
|
|
4165
4088
|
*
|
|
4166
4089
|
* Each override rebuild costs a full Python pool spin-up (~300-500ms)
|
|
@@ -4193,7 +4116,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4193
4116
|
this.writeStore = (patch) => settings.writeAddonStore(patch);
|
|
4194
4117
|
this.readDeviceStore = settings.readDeviceStore ?? (async () => ({}));
|
|
4195
4118
|
this.currentEngine = ONNX_FLOOR;
|
|
4196
|
-
this.log.info("Engine
|
|
4119
|
+
this.log.info("Engine pick pending (placeholder until probe / persisted selection)", { meta: {
|
|
4197
4120
|
runtime: this.currentEngine.runtime,
|
|
4198
4121
|
backend: this.currentEngine.backend,
|
|
4199
4122
|
format: this.currentEngine.format
|
|
@@ -4266,26 +4189,70 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4266
4189
|
this.needsAutoPick = false;
|
|
4267
4190
|
this.startProvisioningForCurrentEngine();
|
|
4268
4191
|
} else {
|
|
4269
|
-
this.startProvisioningForCurrentEngine();
|
|
4270
4192
|
const unsubscribe = this.addonCtx.onCapabilityStateChange("platform-probe", { type: "global" }, (state) => {
|
|
4271
4193
|
if (state !== "ready") return;
|
|
4272
|
-
|
|
4194
|
+
this.cancelDeferredAutoPick();
|
|
4273
4195
|
if (!this.needsAutoPick) return;
|
|
4274
4196
|
this.autoPickAndPersist().then(() => {
|
|
4275
4197
|
this.needsAutoPick = false;
|
|
4276
4198
|
this.startProvisioningForCurrentEngine();
|
|
4277
4199
|
});
|
|
4278
4200
|
});
|
|
4279
|
-
this.
|
|
4201
|
+
this.deferredAutoPickUnsub = unsubscribe;
|
|
4202
|
+
this.addonCtx.addDisposer(() => this.cancelDeferredAutoPick());
|
|
4280
4203
|
}
|
|
4281
4204
|
else this.startProvisioningForCurrentEngine();
|
|
4282
4205
|
}
|
|
4206
|
+
/** Tear down the deferred-auto-pick probe listener, if still armed. */
|
|
4207
|
+
cancelDeferredAutoPick() {
|
|
4208
|
+
if (this.deferredAutoPickUnsub) {
|
|
4209
|
+
this.deferredAutoPickUnsub();
|
|
4210
|
+
this.deferredAutoPickUnsub = null;
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
/**
|
|
4214
|
+
* Boot safety-net: deterministically provision the hardware-probed engine.
|
|
4215
|
+
*
|
|
4216
|
+
* Called from the addon's `onInitialize` right after `reprobeEngine` has
|
|
4217
|
+
* written this node's probe-driven selection (e.g. `engineBackend=openvino`)
|
|
4218
|
+
* to the store. When first-boot auto-pick was DEFERRED (probe not ready at
|
|
4219
|
+
* `setApi`), the engine would otherwise stay `idle` — selected but never
|
|
4220
|
+
* provisioned — because the `onCapabilityStateChange('platform-probe')` ready
|
|
4221
|
+
* edge can be missed when the cap is already 'ready' by the time we subscribe
|
|
4222
|
+
* (the old eager-onnx-floor masked this; removing it surfaced a node that
|
|
4223
|
+
* boots with no engine at all). This loads the now-persisted selection and
|
|
4224
|
+
* starts provisioning it, so the node reliably comes up on its real best
|
|
4225
|
+
* engine (openvino on Intel) with no onnx floor and no missed boot. No-op
|
|
4226
|
+
* when provisioning already started (persisted-engine path / listener fired)
|
|
4227
|
+
* or when nothing has been selected yet.
|
|
4228
|
+
*/
|
|
4229
|
+
async ensureBootEngineProvisioned() {
|
|
4230
|
+
if (this.getEngineProvisioning().state !== "idle") return;
|
|
4231
|
+
const stored = await this.loadEngine();
|
|
4232
|
+
if (!stored) return;
|
|
4233
|
+
this.currentEngine = stored;
|
|
4234
|
+
this.needsAutoPick = false;
|
|
4235
|
+
this.cancelDeferredAutoPick();
|
|
4236
|
+
this.log.info("Boot engine provisioning from probed selection", { meta: {
|
|
4237
|
+
runtime: stored.runtime,
|
|
4238
|
+
backend: stored.backend,
|
|
4239
|
+
device: stored.device ?? null
|
|
4240
|
+
} });
|
|
4241
|
+
this.startProvisioningForCurrentEngine();
|
|
4242
|
+
}
|
|
4283
4243
|
/**
|
|
4284
4244
|
* Auto-pick the best supported runtime at first boot (no stored engine).
|
|
4285
4245
|
* Uses the platform-probe cap's hardware + bestScore hint when available;
|
|
4286
4246
|
* falls back to platform/arch when the probe cap is not yet reachable.
|
|
4247
|
+
*
|
|
4287
4248
|
* Persists the selection as `engineBackend` + `engineDevice` so subsequent
|
|
4288
|
-
* boots load it via `loadEngine()` and skip this path
|
|
4249
|
+
* boots load it via `loadEngine()` and skip this path — but ONLY when the
|
|
4250
|
+
* probe actually answered (real `hardware` or a `bestScore` hint). If the
|
|
4251
|
+
* probe query failed (cold-start race, cap momentarily unreachable), the
|
|
4252
|
+
* pick floors to onnx purely for lack of information; persisting that would
|
|
4253
|
+
* LOCK onnx and skip auto-pick on every future boot even after the
|
|
4254
|
+
* accelerator surfaces. In that case we set the in-memory floor for liveness
|
|
4255
|
+
* but leave the store untouched so the next boot re-attempts the pick.
|
|
4289
4256
|
*/
|
|
4290
4257
|
async autoPickAndPersist() {
|
|
4291
4258
|
let hardware = null;
|
|
@@ -4307,6 +4274,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4307
4274
|
device: pick.device
|
|
4308
4275
|
};
|
|
4309
4276
|
this.currentEngine = engine;
|
|
4277
|
+
if (!(hardware !== null || bestBackendHint !== null)) {
|
|
4278
|
+
this.log.warn("Auto-pick: probe returned no hardware/hint — using onnx floor WITHOUT persisting", { meta: {
|
|
4279
|
+
backend: pick.runtimeId,
|
|
4280
|
+
device: pick.device
|
|
4281
|
+
} });
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4310
4284
|
const apNode = this.localProbeNodeId();
|
|
4311
4285
|
await this.writeStore({
|
|
4312
4286
|
[nodeEngineKey("engineBackend", apNode)]: pick.runtimeId,
|
|
@@ -4427,7 +4401,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4427
4401
|
if (!step.enabled) continue;
|
|
4428
4402
|
const modelEntry = getStepDefinition(step.addonId).models.find((m) => m.id === step.modelId);
|
|
4429
4403
|
if (!modelEntry) continue;
|
|
4430
|
-
if (
|
|
4404
|
+
if (require_model_download_service_RxAOiYvX.isModelDownloaded(this.modelsDir, modelEntry, format)) continue;
|
|
4431
4405
|
await this.downloadWithRetry(modelEntry, format, 3);
|
|
4432
4406
|
}
|
|
4433
4407
|
}
|
|
@@ -4635,7 +4609,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4635
4609
|
const formats = {};
|
|
4636
4610
|
for (const [formatKey, entry] of Object.entries(m.formats)) {
|
|
4637
4611
|
if (!entry) continue;
|
|
4638
|
-
const downloaded =
|
|
4612
|
+
const downloaded = require_model_download_service_RxAOiYvX.isModelDownloaded(this.modelsDir, m, formatKey);
|
|
4639
4613
|
formats[formatKey] = {
|
|
4640
4614
|
url: entry.url,
|
|
4641
4615
|
sizeMB: entry.sizeMB,
|
|
@@ -4758,7 +4732,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4758
4732
|
const { modelId, format, addonId } = input;
|
|
4759
4733
|
const modelEntry = getStepDefinition(addonId).models.find((m) => m.id === modelId);
|
|
4760
4734
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${addonId}" catalog`);
|
|
4761
|
-
if (!
|
|
4735
|
+
if (!require_model_download_service_RxAOiYvX.deleteModelFromDisk(this.modelsDir, modelEntry, format)) throw new Error(`Model "${modelId}" (${format}) is not downloaded — nothing to delete`);
|
|
4762
4736
|
this.log.info("Model deleted from disk", { meta: {
|
|
4763
4737
|
modelId,
|
|
4764
4738
|
format
|
|
@@ -5213,7 +5187,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5213
5187
|
const format = this.currentEngine?.format ?? "onnx";
|
|
5214
5188
|
for (const step of needed) {
|
|
5215
5189
|
const modelEntry = getStepDefinition(step.addonId).models.find((m) => m.id === step.modelId);
|
|
5216
|
-
if (modelEntry && !
|
|
5190
|
+
if (modelEntry && !require_model_download_service_RxAOiYvX.isModelDownloaded(this.modelsDir, modelEntry, format)) {
|
|
5217
5191
|
this.log.info("Downloading model for step", { meta: {
|
|
5218
5192
|
modelId: step.modelId,
|
|
5219
5193
|
format,
|
|
@@ -5238,7 +5212,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5238
5212
|
/** Download a model with retry + exponential backoff */
|
|
5239
5213
|
async downloadWithRetry(entry, format, maxRetries, onProgress) {
|
|
5240
5214
|
for (let attempt = 1; attempt <= maxRetries; attempt++) try {
|
|
5241
|
-
await
|
|
5215
|
+
await require_model_download_service_RxAOiYvX.ensureModel(this.modelsDir, entry, format, onProgress);
|
|
5242
5216
|
this.log.info("Model downloaded successfully", { meta: { modelId: entry.id } });
|
|
5243
5217
|
return;
|
|
5244
5218
|
} catch (err) {
|
|
@@ -5715,11 +5689,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5715
5689
|
} });
|
|
5716
5690
|
continue;
|
|
5717
5691
|
}
|
|
5718
|
-
if (!
|
|
5692
|
+
if (!require_model_download_service_RxAOiYvX.isModelDownloaded(this.modelsDir, modelEntry, format)) this.log.info("Downloading model", { meta: {
|
|
5719
5693
|
modelId: step.modelId,
|
|
5720
5694
|
format
|
|
5721
5695
|
} });
|
|
5722
|
-
downloads.push(
|
|
5696
|
+
downloads.push(require_model_download_service_RxAOiYvX.ensureModel(this.modelsDir, modelEntry, format).then(() => {}));
|
|
5723
5697
|
}
|
|
5724
5698
|
await Promise.all(downloads);
|
|
5725
5699
|
await this.ensureBackendDeps(this.currentEngine);
|
|
@@ -6130,7 +6104,7 @@ function buildSchemaSlots(format, modelsDir) {
|
|
|
6130
6104
|
id: m.id,
|
|
6131
6105
|
name: m.name,
|
|
6132
6106
|
formats: Object.fromEntries(Object.entries(m.formats).map(([f, entry]) => [f, {
|
|
6133
|
-
downloaded:
|
|
6107
|
+
downloaded: require_model_download_service_RxAOiYvX.isModelDownloaded(modelsDir, m, f),
|
|
6134
6108
|
sizeMB: entry.sizeMB
|
|
6135
6109
|
}]))
|
|
6136
6110
|
})),
|
|
@@ -6887,6 +6861,9 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
6887
6861
|
if (!this.nodeProbedBestEngine) await this.provider.reprobeEngine().catch((err) => {
|
|
6888
6862
|
this.ctx.logger.warn("auto-reprobe engine failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
6889
6863
|
});
|
|
6864
|
+
await this.provider.ensureBootEngineProvisioned().catch((err) => {
|
|
6865
|
+
this.ctx.logger.warn("ensureBootEngineProvisioned failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
6866
|
+
});
|
|
6890
6867
|
await this.provider.warmPool();
|
|
6891
6868
|
this.engineMetricsTimer = setInterval(() => this.emitEngineMetricsSnapshot(), ENGINE_METRICS_SNAPSHOT_INTERVAL_MS);
|
|
6892
6869
|
this.lastAppliedPoolConfig = this.snapshotPoolConfig();
|