@camstack/addon-pipeline 1.1.70 → 1.2.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 +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +181 -469
- package/dist/detection-pipeline/index.mjs +182 -469
- package/dist/{dist-CurVt3xC.mjs → dist-DLWXeXC1.mjs} +239 -60
- package/dist/{dist-BWn5k0A5.js → dist-DWy-H6bo.js} +250 -59
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +24 -2
- package/dist/pipeline-runner/index.mjs +24 -2
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/session-decode/decode-worker-child.js +177 -12
- package/dist/session-decode/decode-worker-child.mjs +177 -12
- package/dist/{step-definitions-B7e8rPe3.js → step-definitions-BQG_6dmZ.js} +6 -2
- package/dist/{step-definitions-BKm17nce.mjs → step-definitions-CVk19Y0u.mjs} +6 -2
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-yAAGMET5.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CpB1jG0N.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BflyNuZv.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-5Zc9wj-A.mjs +26 -0
- package/dist/stream-broker/{hostInit-DUzRsTmt.mjs → hostInit--Zair8Av.mjs} +3 -3
- package/dist/stream-broker/index.js +1 -1
- package/dist/stream-broker/index.mjs +1 -1
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-D6bWMuew.js → MaskShapeCanvas-DI4BY7W2-sOOxiGvP.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-C-1enV21.js → MotionZonesSettings-NcxxQN8r-DdTQuT23.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-By9pdGP7.js → PrivacyMaskSettings-APgPLF7p-BrR0L3ql.js} +1 -1
- package/embed-dist/assets/index-D4MGuECB.css +2 -0
- package/embed-dist/assets/{index-BPGh79kn.js → index-Dk21l1_3.js} +14 -14
- package/embed-dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D68Lj69v.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CDBALHSV.mjs +0 -26
- package/embed-dist/assets/index-D2pF9Z3W.css +0 -2
|
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../chunk-D6vf50IK.js");
|
|
6
|
-
const require_dist = require("../dist-
|
|
7
|
-
const require_step_definitions = require("../step-definitions-
|
|
6
|
+
const require_dist = require("../dist-DWy-H6bo.js");
|
|
7
|
+
const require_step_definitions = require("../step-definitions-BQG_6dmZ.js");
|
|
8
8
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
9
9
|
const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Dv-oFwjN.js");
|
|
10
10
|
let node_child_process = require("node:child_process");
|
|
@@ -164,9 +164,6 @@ function pythonRequirementsFor(id) {
|
|
|
164
164
|
function tuningFor(id) {
|
|
165
165
|
return RUNTIME_DETAIL[id].tuning;
|
|
166
166
|
}
|
|
167
|
-
function runtimeLabel(id) {
|
|
168
|
-
return RUNTIME_DETAIL[id].label;
|
|
169
|
-
}
|
|
170
167
|
/**
|
|
171
168
|
* Proactive-install hint kept for back-compat (re-exported from index.ts).
|
|
172
169
|
* Intel on Linux warrants installing openvino; coremltools covers macOS;
|
|
@@ -202,117 +199,27 @@ function pickBestRuntime(env, bestBackendHint) {
|
|
|
202
199
|
device: defaultDeviceFor(chosen)
|
|
203
200
|
};
|
|
204
201
|
}
|
|
205
|
-
/**
|
|
206
|
-
* The ordered device candidates a runtime will try for an `'auto'`/`'all'`
|
|
207
|
-
* device pick — mirrors what the python inference pool actually resolves
|
|
208
|
-
* (openvino `_resolve_ov_devices`: NPU>GPU>CPU; coreml compute-units `all`:
|
|
209
|
-
* ANE>GPU>CPU). Surfaced in `probedBestEngine` so the UI shows the real
|
|
210
|
-
* accelerator order (`openvino/auto [NPU, GPU, CPU]`) instead of a bare `auto`
|
|
211
|
-
* tag. Returns `[]` for runtimes with a single fixed device (onnx → CPU) — the
|
|
212
|
-
* caller then keeps the plain `backend/device` label.
|
|
213
|
-
*/
|
|
214
|
-
function engineDeviceSequence(runtimeId, env) {
|
|
215
|
-
const hw = env.hardware;
|
|
216
|
-
switch (runtimeId) {
|
|
217
|
-
case "openvino": {
|
|
218
|
-
const seq = [];
|
|
219
|
-
if (hw?.npu?.type === "intel-npu") seq.push("NPU");
|
|
220
|
-
if (hw?.gpu?.type === "intel") seq.push("GPU");
|
|
221
|
-
seq.push("CPU");
|
|
222
|
-
return seq;
|
|
223
|
-
}
|
|
224
|
-
case "coreml": {
|
|
225
|
-
const seq = [];
|
|
226
|
-
if (hw?.npu?.type === "apple-ane") seq.push("ANE");
|
|
227
|
-
if (hw?.gpu != null) seq.push("GPU");
|
|
228
|
-
seq.push("CPU");
|
|
229
|
-
return seq;
|
|
230
|
-
}
|
|
231
|
-
default: return [];
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
202
|
//#endregion
|
|
235
203
|
//#region src/detection-pipeline/engine-store-keys.ts
|
|
236
204
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
* The detection addon's settings store is a single CLUSTER-SHARED blob (the
|
|
240
|
-
* settings-store cap is hub-resident; every node's detection instance reads and
|
|
241
|
-
* writes the same keys). That is correct for node-agnostic settings (pipeline
|
|
242
|
-
* steps, tuning) but WRONG for the engine cascade — `engineBackend` /
|
|
243
|
-
* `engineDevice` / `probedBestEngine` are hardware-specific, so the hub (NPU)
|
|
244
|
-
* and a remote agent (iGPU, or no accelerator at all) must hold INDEPENDENT
|
|
245
|
-
* selections. Sharing them lets one node's pick (e.g. `openvino/npu`) override
|
|
246
|
-
* another node that has no NPU.
|
|
205
|
+
* Node-id normalisation for the detection pipeline.
|
|
247
206
|
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* kernel each boot; `reprobeEngine` persists an explicit probe under
|
|
255
|
-
* `<key>@<node>`). Serving a bare value to any node
|
|
256
|
-
* would leak one node's hardware pick (e.g. `openvino/auto`) onto a node that
|
|
257
|
-
* cannot run it (the CB3 "Mac runs OpenVINO" / N100 AUTO-device bug). This
|
|
258
|
-
* mirrors the decoder's `readNodeDecoderBackend` no-fallback rule.
|
|
207
|
+
* The legacy per-node engine CASCADE keys (`engineBackend` / `engineDevice` /
|
|
208
|
+
* `probedBestEngine`) were removed together with the operator-facing engine
|
|
209
|
+
* election: each node now runs a device-array of inference pools balanced
|
|
210
|
+
* per-session, and the node-default pool is derived live from the node's own
|
|
211
|
+
* hardware (`resolveAutoEngine`). All that survives from the old cascade module
|
|
212
|
+
* is the node-id normaliser, still used to target the platform-probe per node.
|
|
259
213
|
*/
|
|
260
|
-
var ENGINE_CASCADE_KEYS = [
|
|
261
|
-
"engineBackend",
|
|
262
|
-
"engineDevice",
|
|
263
|
-
"probedBestEngine"
|
|
264
|
-
];
|
|
265
|
-
function isEngineCascadeKey(key) {
|
|
266
|
-
return ENGINE_CASCADE_KEYS.includes(key);
|
|
267
|
-
}
|
|
268
214
|
/**
|
|
269
|
-
* Normalise a raw kernel node id to the bare node id
|
|
270
|
-
*
|
|
271
|
-
*
|
|
215
|
+
* Normalise a raw kernel node id to the bare node id. `localNodeId` can carry a
|
|
216
|
+
* `<node>/<addon>` suffix; probe targeting is per-NODE, so strip the addon
|
|
217
|
+
* segment. Falls back to `hub`.
|
|
272
218
|
*/
|
|
273
219
|
function normalizeEngineNodeId(rawNodeId) {
|
|
274
220
|
const raw = rawNodeId ?? "hub";
|
|
275
221
|
return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
|
|
276
222
|
}
|
|
277
|
-
/** The node-scoped store key for an engine cascade field. */
|
|
278
|
-
function nodeEngineKey(base, nodeId) {
|
|
279
|
-
return `${base}@${normalizeEngineNodeId(nodeId)}`;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Read an engine cascade value for a node: the node-scoped key if present,
|
|
283
|
-
* otherwise `undefined` — for EVERY node, the hub included. There is no
|
|
284
|
-
* fallback to the legacy un-scoped key: a bare value in the store is inert.
|
|
285
|
-
* A node with no scoped key gets `undefined` so its own platform-driven
|
|
286
|
-
* auto-pick runs and persists a real per-node selection under `<key>@<node>`.
|
|
287
|
-
* (A bare fallback would leak one node's engine selection cluster-wide — CB3.)
|
|
288
|
-
*/
|
|
289
|
-
function readNodeEngineValue(store, base, nodeId) {
|
|
290
|
-
return store[nodeEngineKey(base, nodeId)];
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Project a raw store onto the plain engine cascade keys for THIS node, so the
|
|
294
|
-
* UI schema (whose field keys are the bare `engineBackend` etc.) hydrates from
|
|
295
|
-
* the node's own selection. Non-engine keys are left untouched. Node-scoped
|
|
296
|
-
* keys for OTHER nodes are dropped from the projection (not relevant to this
|
|
297
|
-
* node's form).
|
|
298
|
-
*/
|
|
299
|
-
function projectNodeEngine(store, nodeId) {
|
|
300
|
-
const out = {};
|
|
301
|
-
const scopedForAnyNode = /* @__PURE__ */ new Set();
|
|
302
|
-
for (const key of Object.keys(store)) {
|
|
303
|
-
const atIdx = key.indexOf("@");
|
|
304
|
-
if (isEngineCascadeKey(atIdx >= 0 ? key.slice(0, atIdx) : key)) {
|
|
305
|
-
scopedForAnyNode.add(key);
|
|
306
|
-
continue;
|
|
307
|
-
}
|
|
308
|
-
out[key] = store[key];
|
|
309
|
-
}
|
|
310
|
-
for (const base of ENGINE_CASCADE_KEYS) {
|
|
311
|
-
const value = readNodeEngineValue(store, base, nodeId);
|
|
312
|
-
if (value !== void 0) out[base] = value;
|
|
313
|
-
}
|
|
314
|
-
return out;
|
|
315
|
-
}
|
|
316
223
|
//#endregion
|
|
317
224
|
//#region src/detection-pipeline/default-detection-model.ts
|
|
318
225
|
/** The object-detection step id — the only slot this resolver applies to. */
|
|
@@ -1550,28 +1457,17 @@ var EngineFactory = class {
|
|
|
1550
1457
|
//#endregion
|
|
1551
1458
|
//#region src/detection-pipeline/engine/resolve-device-engine.ts
|
|
1552
1459
|
/**
|
|
1553
|
-
* Backend → model format for a device pool. Mirrors the Phase-1 device
|
|
1554
|
-
* enumeration (`enumerateInferenceDevices`) and the pool-runtime mapping.
|
|
1555
|
-
*/
|
|
1556
|
-
var BACKEND_FORMAT = {
|
|
1557
|
-
openvino: "openvino",
|
|
1558
|
-
edgetpu: "tflite",
|
|
1559
|
-
onnx: "onnx",
|
|
1560
|
-
cpu: "onnx",
|
|
1561
|
-
cuda: "onnx",
|
|
1562
|
-
coreml: "coreml"
|
|
1563
|
-
};
|
|
1564
|
-
/**
|
|
1565
1460
|
* Resolve a Phase-1 device key (`<backend>:<device>`, or bare `cpu`) into the
|
|
1566
1461
|
* {@link PipelineEngineChoice} used to build that device's inference pool.
|
|
1567
1462
|
* Throws on an unknown backend token so a bad `deviceKey` fails loud rather
|
|
1568
|
-
* than silently building a wrong pool.
|
|
1463
|
+
* than silently building a wrong pool. Format comes from the shared
|
|
1464
|
+
* {@link DEVICE_BACKEND_TO_FORMAT} SSOT (`@camstack/types`) — no local copy.
|
|
1569
1465
|
*/
|
|
1570
1466
|
function resolveDeviceEngine(deviceKey) {
|
|
1571
1467
|
const colon = deviceKey.indexOf(":");
|
|
1572
1468
|
const backend = colon >= 0 ? deviceKey.slice(0, colon) : deviceKey;
|
|
1573
1469
|
const device = colon >= 0 ? deviceKey.slice(colon + 1) : deviceKey;
|
|
1574
|
-
const format =
|
|
1470
|
+
const format = require_dist.DEVICE_BACKEND_TO_FORMAT[backend];
|
|
1575
1471
|
if (format === void 0) throw new Error(`resolveDeviceEngine: unknown backend "${backend}" in deviceKey "${deviceKey}"`);
|
|
1576
1472
|
return {
|
|
1577
1473
|
runtime: "python",
|
|
@@ -1581,6 +1477,69 @@ function resolveDeviceEngine(deviceKey) {
|
|
|
1581
1477
|
};
|
|
1582
1478
|
}
|
|
1583
1479
|
//#endregion
|
|
1480
|
+
//#region src/detection-pipeline/engine/idle-pool-reaper.ts
|
|
1481
|
+
var IdlePoolReaper = class {
|
|
1482
|
+
lastUsed = /* @__PURE__ */ new Map();
|
|
1483
|
+
timers = /* @__PURE__ */ new Map();
|
|
1484
|
+
ttlMs;
|
|
1485
|
+
disposeFn;
|
|
1486
|
+
now;
|
|
1487
|
+
setTimer;
|
|
1488
|
+
clearTimer;
|
|
1489
|
+
onError;
|
|
1490
|
+
constructor(opts) {
|
|
1491
|
+
this.ttlMs = opts.ttlMs;
|
|
1492
|
+
this.disposeFn = opts.dispose;
|
|
1493
|
+
this.now = opts.now ?? (() => Date.now());
|
|
1494
|
+
this.setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
1495
|
+
this.clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
|
|
1496
|
+
this.onError = opts.onError;
|
|
1497
|
+
}
|
|
1498
|
+
/** Record a use of `key` and (re)arm its idle-eviction timer. */
|
|
1499
|
+
touch(key) {
|
|
1500
|
+
this.lastUsed.set(key, this.now());
|
|
1501
|
+
this.arm(key);
|
|
1502
|
+
}
|
|
1503
|
+
/** Stop tracking `key` (the pool was disposed/removed elsewhere). */
|
|
1504
|
+
cancel(key) {
|
|
1505
|
+
const timer = this.timers.get(key);
|
|
1506
|
+
if (timer) this.clearTimer(timer);
|
|
1507
|
+
this.timers.delete(key);
|
|
1508
|
+
this.lastUsed.delete(key);
|
|
1509
|
+
}
|
|
1510
|
+
/** Clear every timer (shutdown). Does not dispose pools — the caller does. */
|
|
1511
|
+
disposeAll() {
|
|
1512
|
+
for (const timer of this.timers.values()) this.clearTimer(timer);
|
|
1513
|
+
this.timers.clear();
|
|
1514
|
+
this.lastUsed.clear();
|
|
1515
|
+
}
|
|
1516
|
+
arm(key) {
|
|
1517
|
+
const existing = this.timers.get(key);
|
|
1518
|
+
if (existing) this.clearTimer(existing);
|
|
1519
|
+
this.timers.set(key, this.setTimer(() => {
|
|
1520
|
+
this.reap(key);
|
|
1521
|
+
}, this.ttlMs));
|
|
1522
|
+
}
|
|
1523
|
+
async reap(key) {
|
|
1524
|
+
const last = this.lastUsed.get(key);
|
|
1525
|
+
if (last === void 0) return;
|
|
1526
|
+
const idleMs = this.now() - last;
|
|
1527
|
+
if (idleMs < this.ttlMs) {
|
|
1528
|
+
this.timers.set(key, this.setTimer(() => {
|
|
1529
|
+
this.reap(key);
|
|
1530
|
+
}, this.ttlMs - idleMs));
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
this.timers.delete(key);
|
|
1534
|
+
this.lastUsed.delete(key);
|
|
1535
|
+
try {
|
|
1536
|
+
await this.disposeFn(key);
|
|
1537
|
+
} catch (err) {
|
|
1538
|
+
this.onError?.(key, err);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
//#endregion
|
|
1584
1543
|
//#region src/detection-pipeline/engine-provisioner.ts
|
|
1585
1544
|
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
1586
1545
|
var BACKOFF_SCHEDULE_MS = [
|
|
@@ -3937,26 +3896,6 @@ var PROBE_DIRECT_RPC_TIMEOUT_MS = 6e4;
|
|
|
3937
3896
|
* result is identical (onnx / cpu) but is now derived through the shared rules
|
|
3938
3897
|
* instead of duplicated inline.
|
|
3939
3898
|
*/
|
|
3940
|
-
function onnxFloorPick() {
|
|
3941
|
-
return ONNX_FLOOR;
|
|
3942
|
-
}
|
|
3943
|
-
/**
|
|
3944
|
-
* The platform-deterministic engine pick computed SYNCHRONOUSLY from this node's
|
|
3945
|
-
* own `process.platform`/`arch` alone (no probe): darwin → coreml, else → onnx
|
|
3946
|
-
* (gpu-dependent openvino/cuda need the probe and converge via the auto-pick).
|
|
3947
|
-
* Used as the ENGINE fallback when a persisted selection is unsupported on this
|
|
3948
|
-
* node — so a stale GLOBAL engine config (e.g. the cluster's OpenVINO choice)
|
|
3949
|
-
* can never force an impossible engine onto a node whose platform rejects it.
|
|
3950
|
-
*/
|
|
3951
|
-
function platformDefaultPick() {
|
|
3952
|
-
const pick = pickBestRuntime(runtimeEnvFromProcess(null), null);
|
|
3953
|
-
return {
|
|
3954
|
-
runtime: "python",
|
|
3955
|
-
backend: pick.runtimeId,
|
|
3956
|
-
format: modelFormatFor(pick.runtimeId),
|
|
3957
|
-
device: pick.device
|
|
3958
|
-
};
|
|
3959
|
-
}
|
|
3960
3899
|
/**
|
|
3961
3900
|
* Is `backend` even POSSIBLE on this node's OS/arch (ignoring gpu detail)?
|
|
3962
3901
|
* coreml ⇒ darwin only; openvino ⇒ x64 non-darwin only; onnx ⇒ anywhere. Used to
|
|
@@ -4072,6 +4011,22 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4072
4011
|
*/
|
|
4073
4012
|
overrideCache = null;
|
|
4074
4013
|
overrideCacheTimer = null;
|
|
4014
|
+
/**
|
|
4015
|
+
* Idle-TTL for a per-device inference pool (multi-device C3). A device pool
|
|
4016
|
+
* with no dispatch for this long is disposed and recreated on next use — the
|
|
4017
|
+
* memory guardrail that keeps `factoriesByDevice` from accumulating warm pools
|
|
4018
|
+
* forever. Longer than the override cache (60s) since a device pool is heavier
|
|
4019
|
+
* to respin; still well above any real inter-session gap for an active camera.
|
|
4020
|
+
*/
|
|
4021
|
+
static DEVICE_POOL_IDLE_TTL_MS = 12e4;
|
|
4022
|
+
deviceReaper = new IdlePoolReaper({
|
|
4023
|
+
ttlMs: DetectionPipelineProvider.DEVICE_POOL_IDLE_TTL_MS,
|
|
4024
|
+
dispose: (deviceKey) => this.disposeDevicePool(deviceKey),
|
|
4025
|
+
onError: (deviceKey, err) => this.log.warn("Idle device pool dispose failed", { meta: {
|
|
4026
|
+
deviceKey,
|
|
4027
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4028
|
+
} })
|
|
4029
|
+
});
|
|
4075
4030
|
static OVERRIDE_CACHE_TTL_MS = 6e4;
|
|
4076
4031
|
/** Read the addon settings store (all keys). */
|
|
4077
4032
|
readStore;
|
|
@@ -4099,20 +4054,15 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4099
4054
|
format: this.currentEngine.format
|
|
4100
4055
|
} });
|
|
4101
4056
|
}
|
|
4102
|
-
/**
|
|
4057
|
+
/**
|
|
4058
|
+
* Call after construction. The node-default engine is always DERIVED live
|
|
4059
|
+
* from this node's own hardware (there is no persisted operator override
|
|
4060
|
+
* anymore — the elected single-engine cascade was removed). Defer the derive
|
|
4061
|
+
* to `setApi()` where the probe cap / kernel resolver is reachable.
|
|
4062
|
+
*/
|
|
4103
4063
|
async init() {
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
this.currentEngine = stored;
|
|
4107
|
-
this.log.info("Engine loaded (persisted)", { meta: {
|
|
4108
|
-
runtime: stored.runtime,
|
|
4109
|
-
backend: stored.backend,
|
|
4110
|
-
format: stored.format
|
|
4111
|
-
} });
|
|
4112
|
-
} else {
|
|
4113
|
-
this.needsAutoPick = true;
|
|
4114
|
-
this.log.info("No persisted engine — auto-pick deferred to setApi()");
|
|
4115
|
-
}
|
|
4064
|
+
this.needsAutoPick = true;
|
|
4065
|
+
this.log.info("Node-default engine derive deferred to setApi()");
|
|
4116
4066
|
}
|
|
4117
4067
|
/**
|
|
4118
4068
|
* Lazy-install the pip requirements file matching `engine.backend` into
|
|
@@ -4186,6 +4136,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4186
4136
|
await this.resolveAutoEngine();
|
|
4187
4137
|
this.needsAutoPick = false;
|
|
4188
4138
|
this.startProvisioningForCurrentEngine();
|
|
4139
|
+
await this.recomputeConfigIssues().catch((err) => {
|
|
4140
|
+
this.log.warn("config-issue recompute failed after node-default derive", { meta: { error: require_dist.errMsg(err) } });
|
|
4141
|
+
});
|
|
4189
4142
|
}
|
|
4190
4143
|
/**
|
|
4191
4144
|
* Authoritative platform-probe reconcile: query the probe provider
|
|
@@ -4213,34 +4166,38 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4213
4166
|
}
|
|
4214
4167
|
}
|
|
4215
4168
|
/**
|
|
4216
|
-
*
|
|
4169
|
+
* Derive the node-default engine LIVE from this node's own kernel inference
|
|
4217
4170
|
* resolver (`ctx.kernel.inferenceEngine`) — the runtime mirror of
|
|
4218
4171
|
* `ctx.kernel.hwaccel`. Each node (hub + forked worker + remote agent)
|
|
4219
4172
|
* resolves its OWN accelerators in-process, so a forked agent child never
|
|
4220
4173
|
* adopts the hub's runtime from the `platform-probe` singleton.
|
|
4221
4174
|
*
|
|
4222
|
-
*
|
|
4223
|
-
*
|
|
4224
|
-
*
|
|
4225
|
-
*
|
|
4226
|
-
*
|
|
4227
|
-
*
|
|
4175
|
+
* The default is the TOP-SCORED CONCRETE device from the SAME
|
|
4176
|
+
* `enumerateInferenceDevices` enumeration the orchestrator balances over
|
|
4177
|
+
* (e.g. `openvino:npu`, not `openvino:auto`) — NOT `pickBestRuntime`, whose
|
|
4178
|
+
* `defaultDeviceFor('openvino')` is the abstract `'auto'` sentinel. This is
|
|
4179
|
+
* load-bearing for pool COLLAPSE (D2/NEW-1): a `deviceKey`-less/lone session
|
|
4180
|
+
* routes through the default pool, and a balanced session that picks the same
|
|
4181
|
+
* top device (`openvino:npu`) must reuse THAT pool via `resolveDeviceFactory`'s
|
|
4182
|
+
* `enginesEqual(engine, currentEngine)` check — which only matches when the
|
|
4183
|
+
* default carries the concrete device, not `'auto'`. An abstract `'auto'`
|
|
4184
|
+
* default would spawn a THIRD pool contending with the per-device npu/gpu pools
|
|
4185
|
+
* on the same physical accelerators (double model load / VRAM). Falls back to
|
|
4186
|
+
* the onnx/cpu floor when the enumeration is empty. NEVER persists — recomputed
|
|
4187
|
+
* every boot from the local hardware, inherently self-healing.
|
|
4228
4188
|
*/
|
|
4229
4189
|
async resolveAutoEngine() {
|
|
4230
4190
|
let hardware = null;
|
|
4231
4191
|
try {
|
|
4232
4192
|
hardware = await this.addonCtx?.kernel?.inferenceEngine?.resolveHardware() ?? null;
|
|
4233
4193
|
} catch {}
|
|
4234
|
-
const
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
this.log.info("Resolved auto engine (local kernel)", { meta: {
|
|
4242
|
-
backend: pick.runtimeId,
|
|
4243
|
-
device: pick.device,
|
|
4194
|
+
const devices = require_dist.enumerateInferenceDevices(envToHardwareInfo(runtimeEnvFromProcess(hardware ? toProbedHardware(hardware) : null)));
|
|
4195
|
+
const top = devices.find((d) => d.available) ?? devices[0];
|
|
4196
|
+
this.currentEngine = top ? resolveDeviceEngine(top.key) : ONNX_FLOOR;
|
|
4197
|
+
this.log.info("Derived node-default engine (local kernel)", { meta: {
|
|
4198
|
+
deviceKey: top?.key ?? "onnx:cpu(floor)",
|
|
4199
|
+
backend: this.currentEngine.backend,
|
|
4200
|
+
device: this.currentEngine.device ?? null,
|
|
4244
4201
|
hadHardware: hardware !== null
|
|
4245
4202
|
} });
|
|
4246
4203
|
}
|
|
@@ -4319,33 +4276,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
4319
4276
|
this.provisioner.select(runtimeId, device);
|
|
4320
4277
|
}
|
|
4321
4278
|
/**
|
|
4322
|
-
* Re-trigger provisioning after the operator changes the engine
|
|
4323
|
-
* cascade (`engineBackend`/`engineDevice`). Reloads the persisted
|
|
4324
|
-
* selection into `this.currentEngine`, then `select()`s it — the
|
|
4325
|
-
* machine cancels any superseded provisioning and drives the new
|
|
4326
|
-
* runtime through installing → verifying → ready.
|
|
4327
|
-
*/
|
|
4328
|
-
async onEngineSelectionChanged() {
|
|
4329
|
-
const prev = this.currentEngine;
|
|
4330
|
-
const stored = await this.loadEngine();
|
|
4331
|
-
if (stored) this.currentEngine = stored;
|
|
4332
|
-
else await this.resolveAutoEngine();
|
|
4333
|
-
const engineChanged = prev.runtime !== this.currentEngine.runtime || prev.backend !== this.currentEngine.backend || prev.format !== this.currentEngine.format || (prev.device ?? "") !== (this.currentEngine.device ?? "");
|
|
4334
|
-
if (engineChanged && this.engineFactory) {
|
|
4335
|
-
this.log.info("engine selection changed — rebuilding pool in place", { meta: {
|
|
4336
|
-
from: `${prev.backend}/${prev.device ?? "default"}`,
|
|
4337
|
-
to: `${this.currentEngine.backend}/${this.currentEngine.device ?? "default"}`
|
|
4338
|
-
} });
|
|
4339
|
-
try {
|
|
4340
|
-
await this.engineFactory.dispose();
|
|
4341
|
-
} catch {}
|
|
4342
|
-
this.engineFactory = null;
|
|
4343
|
-
this.executor = null;
|
|
4344
|
-
}
|
|
4345
|
-
this.startProvisioningForCurrentEngine();
|
|
4346
|
-
if (engineChanged) await this.recomputeConfigIssues();
|
|
4347
|
-
}
|
|
4348
|
-
/**
|
|
4349
4279
|
* Gate A (config-correctness gate at engine change) — eagerly re-resolve
|
|
4350
4280
|
* the node-default step tree for `this.currentEngine.format` and compute
|
|
4351
4281
|
* human-readable config issues from the result:
|
|
@@ -5789,6 +5719,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5789
5719
|
this.engineFactory = null;
|
|
5790
5720
|
this.executor = null;
|
|
5791
5721
|
}
|
|
5722
|
+
this.deviceReaper.disposeAll();
|
|
5792
5723
|
const deviceFactories = Array.from(this.factoriesByDevice.values());
|
|
5793
5724
|
this.factoriesByDevice.clear();
|
|
5794
5725
|
await Promise.all(deviceFactories.map((f) => f.dispose().catch(() => void 0)));
|
|
@@ -5932,7 +5863,10 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5932
5863
|
return this.engineFactory;
|
|
5933
5864
|
}
|
|
5934
5865
|
const existing = this.factoriesByDevice.get(deviceKey);
|
|
5935
|
-
if (existing)
|
|
5866
|
+
if (existing) {
|
|
5867
|
+
this.deviceReaper.touch(deviceKey);
|
|
5868
|
+
return existing;
|
|
5869
|
+
}
|
|
5936
5870
|
const inflight = this.deviceFactoryInflight.get(deviceKey);
|
|
5937
5871
|
if (inflight) return inflight;
|
|
5938
5872
|
const creation = (async () => {
|
|
@@ -5953,6 +5887,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5953
5887
|
throw err;
|
|
5954
5888
|
}
|
|
5955
5889
|
this.factoriesByDevice.set(deviceKey, factory);
|
|
5890
|
+
this.deviceReaper.touch(deviceKey);
|
|
5956
5891
|
return factory;
|
|
5957
5892
|
})();
|
|
5958
5893
|
this.deviceFactoryInflight.set(deviceKey, creation);
|
|
@@ -5963,66 +5898,22 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5963
5898
|
}
|
|
5964
5899
|
}
|
|
5965
5900
|
/**
|
|
5966
|
-
*
|
|
5967
|
-
*
|
|
5968
|
-
* `
|
|
5969
|
-
*
|
|
5970
|
-
*
|
|
5971
|
-
* The `'auto'` sentinel is treated as NO override (returns null) so the
|
|
5972
|
-
* caller re-resolves LIVE via `resolveAutoEngine()` — `'auto'` is the
|
|
5973
|
-
* self-healing default, never a persisted concrete backend. An explicit
|
|
5974
|
-
* concrete backend (onnx/openvino/coreml) is honored as an operator override.
|
|
5975
|
-
* Returns null when neither source has a concrete selection; the caller keeps
|
|
5976
|
-
* the onnx floor set at construction until `resolveAutoEngine()` runs.
|
|
5901
|
+
* Dispose a single idle device pool (invoked by the {@link deviceReaper} when
|
|
5902
|
+
* `deviceKey` has had no dispatch for the idle-TTL). Removed from the map
|
|
5903
|
+
* FIRST so a concurrent `resolveDeviceFactory` recreates it fresh via the
|
|
5904
|
+
* single-flight guard rather than handing back the disposing instance.
|
|
5977
5905
|
*/
|
|
5978
|
-
async
|
|
5979
|
-
const
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
const storedDevice = typeof storedDeviceRaw === "string" ? storedDeviceRaw : "";
|
|
5988
|
-
const floor = onnxFloorPick();
|
|
5989
|
-
const migratedBackend = typeof storedRuntime === "string" && storedRuntime === "node" && backend === "cpu" ? "onnx" : backend;
|
|
5990
|
-
if (!backendPossibleOnPlatform(migratedBackend)) {
|
|
5991
|
-
const platformDefault = platformDefaultPick();
|
|
5992
|
-
this.log.warn("Stored engine backend impossible on this platform — using platform default", { meta: {
|
|
5993
|
-
stored: migratedBackend,
|
|
5994
|
-
fallback: platformDefault.backend
|
|
5995
|
-
} });
|
|
5996
|
-
this.writeStore({
|
|
5997
|
-
[nodeEngineKey("engineBackend", node)]: platformDefault.backend,
|
|
5998
|
-
...platformDefault.device !== void 0 ? { [nodeEngineKey("engineDevice", node)]: platformDefault.device } : {}
|
|
5999
|
-
}).catch((err) => {
|
|
6000
|
-
this.log.warn("Failed to persist platform-default engine after impossible stored pick", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
6001
|
-
});
|
|
6002
|
-
return platformDefault;
|
|
6003
|
-
}
|
|
6004
|
-
const device = storedDevice || floor.device;
|
|
6005
|
-
return {
|
|
6006
|
-
runtime: "python",
|
|
6007
|
-
backend: migratedBackend,
|
|
6008
|
-
format: backendToFormat(migratedBackend),
|
|
6009
|
-
...device ? { device } : {}
|
|
6010
|
-
};
|
|
6011
|
-
}
|
|
6012
|
-
return null;
|
|
5906
|
+
async disposeDevicePool(deviceKey) {
|
|
5907
|
+
const factory = this.factoriesByDevice.get(deviceKey);
|
|
5908
|
+
if (!factory) return;
|
|
5909
|
+
this.factoriesByDevice.delete(deviceKey);
|
|
5910
|
+
await factory.dispose();
|
|
5911
|
+
this.log.info("Idle device pool evicted", { meta: {
|
|
5912
|
+
deviceKey,
|
|
5913
|
+
idleTtlMs: DetectionPipelineProvider.DEVICE_POOL_IDLE_TTL_MS
|
|
5914
|
+
} });
|
|
6013
5915
|
}
|
|
6014
5916
|
/**
|
|
6015
|
-
* Re-run the platform probe for the inference engine and persist the
|
|
6016
|
-
* result into the addon's own global-settings keys. Because those
|
|
6017
|
-
* fields are declared `requiresRestart: true`, the
|
|
6018
|
-
* `updateGlobalSettings` pathway auto-schedules an addon restart —
|
|
6019
|
-
* here we do the write directly via the settings store and trigger
|
|
6020
|
-
* restart manually through the addons cap.
|
|
6021
|
-
*
|
|
6022
|
-
* Returns the chosen engine so the UI can confirm the pick without a
|
|
6023
|
-
* second round-trip.
|
|
6024
|
-
*/
|
|
6025
|
-
/**
|
|
6026
5917
|
* Phase 2c — flat per-addonId map holding the video-pipeline step
|
|
6027
5918
|
* config (`modelId` / `settings`). No `enabled` field: whether an
|
|
6028
5919
|
* addon step runs is a capability-binding concern, not a
|
|
@@ -6207,35 +6098,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6207
6098
|
} });
|
|
6208
6099
|
return { success: true };
|
|
6209
6100
|
}
|
|
6210
|
-
async reprobeEngine() {
|
|
6211
|
-
let hardware = null;
|
|
6212
|
-
try {
|
|
6213
|
-
hardware = await this.addonCtx?.kernel?.inferenceEngine?.resolveHardware() ?? null;
|
|
6214
|
-
} catch {}
|
|
6215
|
-
const env = runtimeEnvFromProcess(hardware ? toProbedHardware(hardware) : null);
|
|
6216
|
-
const pick = pickBestRuntime(env, null);
|
|
6217
|
-
const best = {
|
|
6218
|
-
runtime: "python",
|
|
6219
|
-
backend: pick.runtimeId,
|
|
6220
|
-
format: modelFormatFor(pick.runtimeId),
|
|
6221
|
-
device: pick.device
|
|
6222
|
-
};
|
|
6223
|
-
const deviceSeq = engineDeviceSequence(pick.runtimeId, env);
|
|
6224
|
-
const probedLabel = deviceSeq.length > 1 ? `${best.backend}/${best.device ?? "default"} [${deviceSeq.join(", ")}]` : `${best.backend}/${best.device ?? "default"}`;
|
|
6225
|
-
const rpNode = this.localProbeNodeId();
|
|
6226
|
-
if (pick.runtimeId !== "onnx" || hardware !== null) {
|
|
6227
|
-
await this.writeStore({ [nodeEngineKey("probedBestEngine", rpNode)]: probedLabel });
|
|
6228
|
-
this.log.info("Re-probed engine (local kernel) — updated probedBestEngine hint", { meta: {
|
|
6229
|
-
backend: best.backend,
|
|
6230
|
-
device: best.device ?? null,
|
|
6231
|
-
probedBestEngine: probedLabel
|
|
6232
|
-
} });
|
|
6233
|
-
} else this.log.info("Re-probe: onnx floor pending gpu probe — hint NOT persisted", { meta: {
|
|
6234
|
-
backend: best.backend,
|
|
6235
|
-
device: best.device ?? null
|
|
6236
|
-
} });
|
|
6237
|
-
return best;
|
|
6238
|
-
}
|
|
6239
6101
|
async getReferenceAudioFiles() {
|
|
6240
6102
|
const dir = resolveReferenceAudioDir();
|
|
6241
6103
|
if (!dir) return [];
|
|
@@ -6590,47 +6452,6 @@ function resolveAddonPythonDir() {
|
|
|
6590
6452
|
for (const c of candidates) if (node_fs.existsSync(node_path.join(c, "inference_pool.py"))) return c;
|
|
6591
6453
|
throw new Error(`addon-pipeline/detection-pipeline: python/ dir not found. Searched:\n${candidates.join("\n")}`);
|
|
6592
6454
|
}
|
|
6593
|
-
var BACKEND_TO_FORMAT = {
|
|
6594
|
-
cpu: "onnx",
|
|
6595
|
-
coreml: "coreml",
|
|
6596
|
-
openvino: "openvino",
|
|
6597
|
-
onnx: "onnx",
|
|
6598
|
-
edgetpu: "tflite"
|
|
6599
|
-
};
|
|
6600
|
-
/**
|
|
6601
|
-
* Every runtime the registry knows about, in offer order. Used only to build
|
|
6602
|
-
* the STATIC base schema (returned by `globalSettingsSchema()` before a live
|
|
6603
|
-
* ctx exists, e.g. addon-registry boot introspection). The live, hardware-
|
|
6604
|
-
* gated subset is injected by `getGlobalSettings()` via `supportedRuntimes(env)`.
|
|
6605
|
-
*/
|
|
6606
|
-
var ALL_RUNTIME_IDS = [
|
|
6607
|
-
"onnx",
|
|
6608
|
-
"openvino",
|
|
6609
|
-
"coreml",
|
|
6610
|
-
"edgetpu"
|
|
6611
|
-
];
|
|
6612
|
-
/**
|
|
6613
|
-
* Narrow an arbitrary stored backend string to a known `RuntimeId`. Legacy or
|
|
6614
|
-
* unknown values (e.g. the removed `'cpu'` backend) fall back to the `onnx`
|
|
6615
|
-
* floor so the registry lookup never throws.
|
|
6616
|
-
*/
|
|
6617
|
-
function toRuntimeId(backend) {
|
|
6618
|
-
return ALL_RUNTIME_IDS.find((id) => id === backend) ?? "onnx";
|
|
6619
|
-
}
|
|
6620
|
-
/** Static universe of backend options (every runtime). Labels from the registry. */
|
|
6621
|
-
var STATIC_BACKEND_OPTIONS = ALL_RUNTIME_IDS.map((id) => ({
|
|
6622
|
-
value: id,
|
|
6623
|
-
label: runtimeLabel(id)
|
|
6624
|
-
}));
|
|
6625
|
-
/**
|
|
6626
|
-
* Static device options for the schema's default backend. The live device
|
|
6627
|
-
* list per the selected backend + probed hardware is injected by
|
|
6628
|
-
* `getGlobalSettings()` via `runtimeDevices(backend, hardware)`.
|
|
6629
|
-
*/
|
|
6630
|
-
var STATIC_DEFAULT_DEVICE_OPTIONS = runtimeDevices(ALL_RUNTIME_IDS[0], null).map((d) => ({
|
|
6631
|
-
value: d.value,
|
|
6632
|
-
label: d.label
|
|
6633
|
-
}));
|
|
6634
6455
|
var DEFAULT_CONFIG = {
|
|
6635
6456
|
concurrency: 0,
|
|
6636
6457
|
batchMode: "",
|
|
@@ -6639,17 +6460,8 @@ var DEFAULT_CONFIG = {
|
|
|
6639
6460
|
numStreams: 0,
|
|
6640
6461
|
intraOpThreads: 0,
|
|
6641
6462
|
numWorkers: 0,
|
|
6642
|
-
engineRuntime: "python",
|
|
6643
|
-
engineBackend: "auto",
|
|
6644
|
-
engineDevice: "cpu",
|
|
6645
|
-
probedBestEngine: "",
|
|
6646
|
-
activeEngine: "",
|
|
6647
6463
|
activeModelSubstitutions: ""
|
|
6648
6464
|
};
|
|
6649
|
-
/** Derive the model-format from a backend value. Called by the provider. */
|
|
6650
|
-
function backendToFormat(backend) {
|
|
6651
|
-
return BACKEND_TO_FORMAT[backend] ?? "onnx";
|
|
6652
|
-
}
|
|
6653
6465
|
/**
|
|
6654
6466
|
* Cadence for the per-node engine-inventory snapshot. Engines change
|
|
6655
6467
|
* slowly (manual spin/kill, idle eviction, attach/detach). 5 s strikes
|
|
@@ -6742,63 +6554,18 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
6742
6554
|
tab: "engine",
|
|
6743
6555
|
order: 0,
|
|
6744
6556
|
description: "Vision-detection execution provider (object detection, face detection, plate OCR). Inference always runs via the embedded Python runtime. Provider and hardware options are gated by what the platform probe reports as available on this host. Changes restart the addon.",
|
|
6745
|
-
fields: [
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
tooltip: "Re-probe engine"
|
|
6758
|
-
}]
|
|
6759
|
-
}),
|
|
6760
|
-
this.field({
|
|
6761
|
-
type: "text",
|
|
6762
|
-
key: "activeEngine",
|
|
6763
|
-
label: "Active engine",
|
|
6764
|
-
description: "The runtime/device the inference pool ACTUALLY loaded on this node, with its provisioning state (format: provider/device (state)). If this differs from the selected provider below — e.g. \"onnx/cpu (ready)\" while OpenVINO is selected — the chosen accelerator could not load on this host and the engine fell back to the ONNX-CPU baseline.",
|
|
6765
|
-
readonlyField: true,
|
|
6766
|
-
default: ""
|
|
6767
|
-
}),
|
|
6768
|
-
this.field({
|
|
6769
|
-
type: "text",
|
|
6770
|
-
key: "activeModelSubstitutions",
|
|
6771
|
-
label: "Model substitutions",
|
|
6772
|
-
description: "Node-local model substitutions currently in effect: a step's chosen model has no build for this node's engine format, so a compatible default is running instead (format: \"step: chosen→running\"). Hidden when every step matches.",
|
|
6773
|
-
readonlyField: true,
|
|
6774
|
-
default: "",
|
|
6775
|
-
showWhen: {
|
|
6776
|
-
field: "activeModelSubstitutions",
|
|
6777
|
-
notEquals: ""
|
|
6778
|
-
}
|
|
6779
|
-
}),
|
|
6780
|
-
this.field({
|
|
6781
|
-
type: "select",
|
|
6782
|
-
key: "engineBackend",
|
|
6783
|
-
label: "Execution provider",
|
|
6784
|
-
options: [{
|
|
6785
|
-
value: "auto",
|
|
6786
|
-
label: "Auto (best for this node)"
|
|
6787
|
-
}, ...STATIC_BACKEND_OPTIONS],
|
|
6788
|
-
default: DEFAULT_CONFIG.engineBackend,
|
|
6789
|
-
immediate: true,
|
|
6790
|
-
perNode: true
|
|
6791
|
-
}),
|
|
6792
|
-
this.field({
|
|
6793
|
-
type: "select",
|
|
6794
|
-
key: "engineDevice",
|
|
6795
|
-
label: "Hardware device",
|
|
6796
|
-
options: [...STATIC_DEFAULT_DEVICE_OPTIONS],
|
|
6797
|
-
default: DEFAULT_CONFIG.engineDevice,
|
|
6798
|
-
immediate: true,
|
|
6799
|
-
perNode: true
|
|
6800
|
-
})
|
|
6801
|
-
]
|
|
6557
|
+
fields: [this.field({
|
|
6558
|
+
type: "text",
|
|
6559
|
+
key: "activeModelSubstitutions",
|
|
6560
|
+
label: "Model substitutions",
|
|
6561
|
+
description: "Node-local model substitutions currently in effect: a step's chosen model has no build for this node's engine format, so a compatible default is running instead (format: \"step: chosen→running\"). Hidden when every step matches.",
|
|
6562
|
+
readonlyField: true,
|
|
6563
|
+
default: "",
|
|
6564
|
+
showWhen: {
|
|
6565
|
+
field: "activeModelSubstitutions",
|
|
6566
|
+
notEquals: ""
|
|
6567
|
+
}
|
|
6568
|
+
})]
|
|
6802
6569
|
}, {
|
|
6803
6570
|
id: "inference-executor",
|
|
6804
6571
|
title: "Inference executor",
|
|
@@ -6827,15 +6594,7 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
6827
6594
|
label: "Window (cross-camera accumulator)"
|
|
6828
6595
|
}
|
|
6829
6596
|
],
|
|
6830
|
-
default: ""
|
|
6831
|
-
showWhen: {
|
|
6832
|
-
field: "engineBackend",
|
|
6833
|
-
in: [
|
|
6834
|
-
"coreml",
|
|
6835
|
-
"onnx",
|
|
6836
|
-
"cpu"
|
|
6837
|
-
]
|
|
6838
|
-
}
|
|
6597
|
+
default: ""
|
|
6839
6598
|
}),
|
|
6840
6599
|
this.field({
|
|
6841
6600
|
type: "slider",
|
|
@@ -6901,59 +6660,31 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
6901
6660
|
}] });
|
|
6902
6661
|
}
|
|
6903
6662
|
/**
|
|
6904
|
-
* Override to inject the
|
|
6663
|
+
* Override to inject the read-only model-substitution status + the
|
|
6664
|
+
* per-backend tuning-slider defaults.
|
|
6905
6665
|
*
|
|
6906
|
-
*
|
|
6907
|
-
*
|
|
6908
|
-
*
|
|
6909
|
-
*
|
|
6910
|
-
*
|
|
6911
|
-
*
|
|
6912
|
-
*
|
|
6666
|
+
* Engine SELECTION is no longer operator-facing (the elected single-engine
|
|
6667
|
+
* cascade was removed): each node runs a device-array of inference pools
|
|
6668
|
+
* balanced per-session, and the node-default pool is derived live from the
|
|
6669
|
+
* node's own hardware. So this override no longer offers a backend/device
|
|
6670
|
+
* dropdown or a probed/active-engine readout — it only:
|
|
6671
|
+
* 1. resolves the node's derived-default backend (via the probe) to suggest
|
|
6672
|
+
* the matrix-tuned slider defaults that won the bench for that backend, and
|
|
6673
|
+
* 2. surfaces the live `activeModelSubstitutions` readout.
|
|
6913
6674
|
*/
|
|
6914
6675
|
async getGlobalSettings(overlay, _cap, nodeId) {
|
|
6915
6676
|
const targetNode = nodeId ? normalizeEngineNodeId(nodeId) : this.localNodeId();
|
|
6916
6677
|
const rawStored = await this.resolveUiSettingsStore();
|
|
6917
6678
|
if (rawStored === null) return null;
|
|
6918
|
-
const stored = projectNodeEngine(rawStored, targetNode);
|
|
6919
|
-
const storedBackendRaw = stored["engineBackend"];
|
|
6920
|
-
if (typeof storedBackendRaw !== "string" || storedBackendRaw === "") return null;
|
|
6921
6679
|
const merged = overlay ? {
|
|
6922
|
-
...
|
|
6680
|
+
...rawStored,
|
|
6923
6681
|
...overlay
|
|
6924
|
-
} :
|
|
6925
|
-
const
|
|
6926
|
-
const hardware = env.hardware;
|
|
6927
|
-
const offered = supportedRuntimes(env);
|
|
6928
|
-
const runtimeBackends = [{
|
|
6929
|
-
value: "auto",
|
|
6930
|
-
label: "Auto (best for this node)"
|
|
6931
|
-
}, ...offered.map((id) => ({
|
|
6932
|
-
value: id,
|
|
6933
|
-
label: runtimeLabel(id)
|
|
6934
|
-
}))];
|
|
6935
|
-
const storedBackend = typeof merged.engineBackend === "string" ? merged.engineBackend : "";
|
|
6936
|
-
const isAuto = storedBackend === "auto";
|
|
6937
|
-
const backend = (() => {
|
|
6938
|
-
if (isAuto) return pickBestRuntime(env, null).runtimeId;
|
|
6939
|
-
const rid = toRuntimeId(storedBackend);
|
|
6940
|
-
if (rid === "onnx") return "onnx";
|
|
6941
|
-
if (offered.includes(rid)) return rid;
|
|
6942
|
-
return rid === "openvino" && env.platform !== "darwin" && env.arch === "x64" || rid === "coreml" && env.platform === "darwin" ? rid : offered[0] ?? "onnx";
|
|
6943
|
-
})();
|
|
6944
|
-
const deviceOptions = runtimeDevices(backend, hardware);
|
|
6945
|
-
const storedDevice = typeof merged.engineDevice === "string" ? merged.engineDevice : "";
|
|
6946
|
-
const device = deviceOptions.find((d) => d.value === storedDevice)?.value ?? defaultDeviceFor(backend);
|
|
6947
|
-
const prov = targetNode === this.localNodeId() ? this.provider?.getEngineProvisioning() : void 0;
|
|
6948
|
-
const activeEngine = prov && prov.runtimeId ? `${prov.runtimeId}/${prov.device ?? "default"} (${prov.state})` : "";
|
|
6682
|
+
} : rawStored;
|
|
6683
|
+
const backend = pickBestRuntime(await this.probeHardwareEnv(targetNode), null).runtimeId;
|
|
6949
6684
|
const substitutions = this.provider?.getActiveModelSubstitutions() ?? [];
|
|
6950
6685
|
const activeModelSubstitutions = substitutions.length === 0 ? "" : substitutions.map((s) => `${s.addonId}: ${s.chosen}→${s.running}`).join(", ");
|
|
6951
|
-
const displayBackend = isAuto ? "auto" : backend;
|
|
6952
6686
|
const raw = {
|
|
6953
6687
|
...merged,
|
|
6954
|
-
engineBackend: displayBackend,
|
|
6955
|
-
engineDevice: device,
|
|
6956
|
-
activeEngine,
|
|
6957
6688
|
activeModelSubstitutions
|
|
6958
6689
|
};
|
|
6959
6690
|
const schema = this.globalSettingsSchema();
|
|
@@ -6965,18 +6696,6 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
6965
6696
|
...section,
|
|
6966
6697
|
fields: section.fields.map((field) => {
|
|
6967
6698
|
if (field.type === "select") {
|
|
6968
|
-
if (field.key === "engineBackend") return {
|
|
6969
|
-
...field,
|
|
6970
|
-
options: [...runtimeBackends]
|
|
6971
|
-
};
|
|
6972
|
-
if (field.key === "engineDevice") return {
|
|
6973
|
-
...field,
|
|
6974
|
-
options: deviceOptions.map((d) => ({
|
|
6975
|
-
value: d.value,
|
|
6976
|
-
label: d.label
|
|
6977
|
-
})),
|
|
6978
|
-
default: defaultDeviceFor(backend)
|
|
6979
|
-
};
|
|
6980
6699
|
if (field.key === "batchMode" && tuning["batchMode"] !== void 0) return {
|
|
6981
6700
|
...field,
|
|
6982
6701
|
default: tuning["batchMode"]
|
|
@@ -7109,7 +6828,7 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
7109
6828
|
* a reason to disagree.
|
|
7110
6829
|
*/
|
|
7111
6830
|
resolveBackendTuning() {
|
|
7112
|
-
const t = tuningFor(this.
|
|
6831
|
+
const t = tuningFor(this.provider?.resolvedEngineBackend() ?? "onnx");
|
|
7113
6832
|
const num = (v, dflt) => typeof v === "number" && v > 0 ? v : dflt;
|
|
7114
6833
|
const batch = (v, dflt) => v === "none" || v === "list" || v === "window" ? v : dflt;
|
|
7115
6834
|
return {
|
|
@@ -7172,9 +6891,6 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
7172
6891
|
await this.provider.init();
|
|
7173
6892
|
await this.provider.setApi(this.ctx);
|
|
7174
6893
|
}
|
|
7175
|
-
if (!this.config.probedBestEngine) await this.provider.reprobeEngine().catch((err) => {
|
|
7176
|
-
this.ctx.logger.warn("auto-reprobe engine failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
7177
|
-
});
|
|
7178
6894
|
await this.provider.warmPool();
|
|
7179
6895
|
this.engineMetricsTimer = setInterval(() => this.emitEngineMetricsSnapshot(), ENGINE_METRICS_SNAPSHOT_INTERVAL_MS);
|
|
7180
6896
|
this.lastAppliedPoolConfig = this.snapshotPoolConfig();
|
|
@@ -7288,21 +7004,18 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
7288
7004
|
}
|
|
7289
7005
|
/**
|
|
7290
7006
|
* BaseAddon calls `onConfigChanged` after every settings write. This is the
|
|
7291
|
-
* SOLE apply path for
|
|
7007
|
+
* SOLE apply path for tuning changes — none of those fields declare
|
|
7292
7008
|
* `requiresRestart` anymore (an addon restart re-probes hardware + reloads
|
|
7293
|
-
* every model on every set, which is exactly what we want to avoid)
|
|
7294
|
-
*
|
|
7295
|
-
*
|
|
7296
|
-
*
|
|
7297
|
-
*
|
|
7298
|
-
*
|
|
7299
|
-
*
|
|
7300
|
-
*
|
|
7009
|
+
* every model on every set, which is exactly what we want to avoid): pool-bound
|
|
7010
|
+
* tuning (`concurrency`/`batchMode`/`windowMs`/…) drives an in-place pool
|
|
7011
|
+
* respawn when the snapshot flips (`poolConfigChanged` below). The engine
|
|
7012
|
+
* SELECTION is no longer operator-facing — the node-default backend is derived
|
|
7013
|
+
* once at boot (`setApi` → `resolveAutoEngine`) from static hardware, so there
|
|
7014
|
+
* is no per-write engine re-select. The respawn applies optimistically — the
|
|
7015
|
+
* inference gate stays closed for the short re-spin, frames are dropped (never
|
|
7016
|
+
* crashed), no addon bounce.
|
|
7301
7017
|
*/
|
|
7302
7018
|
async onConfigChanged() {
|
|
7303
|
-
if (this.provider) await this.provider.onEngineSelectionChanged().catch((err) => {
|
|
7304
|
-
this.ctx.logger.warn("engine provisioning re-select failed on config change", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
7305
|
-
});
|
|
7306
7019
|
if (!this.poolConfigChanged()) return;
|
|
7307
7020
|
if (!this.provider) {
|
|
7308
7021
|
this.lastAppliedPoolConfig = this.snapshotPoolConfig();
|
|
@@ -7329,7 +7042,6 @@ var DetectionPipelineAddon = class DetectionPipelineAddon extends require_dist.B
|
|
|
7329
7042
|
//#endregion
|
|
7330
7043
|
exports.ALL_STEPS = require_step_definitions.ALL_STEPS;
|
|
7331
7044
|
exports.DetectionPipelineProvider = DetectionPipelineProvider;
|
|
7332
|
-
exports.backendToFormat = backendToFormat;
|
|
7333
7045
|
exports.default = DetectionPipelineAddon;
|
|
7334
7046
|
exports.getDefaultModelForFormat = require_step_definitions.getDefaultModelForFormat;
|
|
7335
7047
|
exports.getStepDefinition = require_step_definitions.getStepDefinition;
|