@camstack/addon-pipeline 1.2.128 → 1.2.130
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/{addon-utils-BnprivSs.js → addon-utils-DH78p79i.js} +1 -1
- package/dist/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/detection-pipeline/index.js +241 -95
- package/dist/detection-pipeline/index.mjs +239 -93
- package/dist/{dist-BZUXb5S5.js → dist-DOw-izoI.js} +243 -75
- package/dist/{dist-HLAOw6Pe.mjs → dist-Wt81pN5Y.mjs} +243 -75
- package/dist/{event-loop-stall-monitor-BdArVUlx.js → event-loop-stall-monitor-Bld7jNCb.js} +1 -1
- package/dist/{event-loop-stall-monitor-BH-cL_hg.mjs → event-loop-stall-monitor-CtQCOZqm.mjs} +1 -1
- package/dist/{lazy-sharp-DTDXrhMG.js → lazy-sharp-BbNI2ZD-.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +4 -4
- package/dist/pipeline-runner/index.mjs +3 -3
- package/dist/{process-memory-CDPRLalN.mjs → process-memory-1_D3EZT9.mjs} +1 -1
- package/dist/{process-memory-Dh1SyzdJ.js → process-memory-D9H5qjDl.js} +1 -1
- package/dist/recorder/index.js +467 -29
- package/dist/recorder/index.mjs +465 -28
- package/dist/{segment-demux-js-D-TLcHW5.mjs → segment-demux-js-BW-Q-j2A.mjs} +1 -1
- package/dist/{segment-demux-js-DRutggd6.js → segment-demux-js-C2jBBqGW.js} +1 -1
- package/dist/session-decode/decode-worker-child.js +2 -2
- package/dist/session-decode/decode-worker-child.mjs +1 -1
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BDR5z0Z9.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Dw-iwP4y.mjs} +3 -3
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bme2OZQp.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-Do2DEtRt.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-D36MoVV1.mjs} +1 -1
- package/dist/stream-broker/demux-worker-child.js +1 -1
- package/dist/stream-broker/demux-worker-child.mjs +1 -1
- package/dist/stream-broker/{hostInit-Dmw5LWmP.mjs → hostInit-DEVCEYgu.mjs} +3 -3
- package/dist/stream-broker/index.js +906 -423
- package/dist/stream-broker/index.mjs +906 -423
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-BxkCoVjw.js → worker-protocol-nAZl_Rsv.js} +1 -1
- package/dist/{worker-protocol-h7Smb_0i.mjs → worker-protocol-rpnr7Ldd.mjs} +1 -1
- package/package.json +1 -1
- package/python/__pycache__/inference_pool.cpython-312.pyc +0 -0
- package/python/__pycache__/inference_pool.cpython-313.pyc +0 -0
- package/python/__pycache__/inference_pool.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_backpressure.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_backpressure.cpython-312.pyc +0 -0
- package/python/__pycache__/test_inference_pool_backpressure.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_coreml_cache.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_coreml_cache.cpython-312.pyc +0 -0
- package/python/__pycache__/test_inference_pool_coreml_cache.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_device_selection.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_device_selection.cpython-312.pyc +0 -0
- package/python/__pycache__/test_inference_pool_device_selection.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_layout.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_layout.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_memstats.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_memstats.cpython-312.pyc +0 -0
- package/python/__pycache__/test_inference_pool_memstats.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_ov_ppp.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_ov_ppp.cpython-312.pyc +0 -0
- package/python/__pycache__/test_inference_pool_ov_ppp.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_preprocess.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_preprocess.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_reconfigure.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_static_batch.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/__pycache__/test_inference_pool_static_batch.cpython-312.pyc +0 -0
- package/python/__pycache__/test_inference_pool_static_batch.cpython-314.pyc +0 -0
- package/python/__pycache__/yamnet_audio.cpython-314.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__/__init__.cpython-314.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__/arcface.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/clip.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/clip.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/clip.cpython-314.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__/ctc.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/plate_slots.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/plate_slots.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/rfdetr.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/rfdetr.cpython-314.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__/saliency.cpython-314.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__/scrfd.cpython-314.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__/softmax.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/ssd.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/ssd.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/ssd.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/test_clip.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_clip.cpython-313-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-313-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/test_plate_slots.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_rfdetr.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_rfdetr.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/test_saliency.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_saliency.cpython-313-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_saliency.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/test_softmax.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_softmax.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/test_yolo_nms.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_yolo_nms.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/test_yolo_nms.cpython-39.pyc +0 -0
- package/python/postprocessors/__pycache__/test_yolonas.cpython-312-pytest-9.1.1.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__/yamnet.cpython-314.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.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-39.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
- package/python/postprocessors/__pycache__/yolo_seg.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/yolonas.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yolonas.cpython-314.pyc +0 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cs8bmrWP.mjs +0 -26
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as __require } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { $ as pickClusterStepModels, D as YAMNET_TO_MACRO, Dt as hydrateSchema, Et as createEvent, Ft as sleep, G as evaluateZoneRules, Gt as string, I as defaultDeviceFor$1, Kt as union, Lt as array, Mt as parseJsonUnknown, St as BaseAddon, Ut as object, V as detectionPipelineCapability, W as enumerateInferenceDevices, Z as overlayClusterStepSettings, c as DEFAULT_CLUSTER_STEP_MODELS, dt as runtimeDevices$1, et as pickClusterStepSettings, gt as errMsg, jt as nodePin, l as DEFAULT_CLUSTER_STEP_SETTINGS, lt as resolvePoolMemoryPolicy, mt as supportedRuntimes$1, p as DEVICE_BACKEND_TO_FORMAT, q as inferModelProvider, qt as EventCategory, rt as pipelineExecutorCapability, st as resolveClusterStepModelId, t as APPLE_SA_TO_MACRO, x as PoolMemoryWatchdog } from "../dist-
|
|
3
|
-
import { a as ALL_PIPELINE_STEPS, c as getDefaultModelForFormatFromDef, d as resolveModelForFormat, f as landmarkPrecisionVerdict, l as getStep, n as startEventLoopStallMonitor, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as getStepDefinition } from "../event-loop-stall-monitor-
|
|
2
|
+
import { $ as pickClusterStepModels, D as YAMNET_TO_MACRO, Dt as hydrateSchema, Et as createEvent, Ft as sleep, G as evaluateZoneRules, Gt as string, I as defaultDeviceFor$1, Kt as union, Lt as array, Mt as parseJsonUnknown, St as BaseAddon, Ut as object, V as detectionPipelineCapability, W as enumerateInferenceDevices, Z as overlayClusterStepSettings, c as DEFAULT_CLUSTER_STEP_MODELS, dt as runtimeDevices$1, et as pickClusterStepSettings, gt as errMsg, jt as nodePin, l as DEFAULT_CLUSTER_STEP_SETTINGS, lt as resolvePoolMemoryPolicy, mt as supportedRuntimes$1, p as DEVICE_BACKEND_TO_FORMAT, q as inferModelProvider, qt as EventCategory, rt as pipelineExecutorCapability, st as resolveClusterStepModelId, t as APPLE_SA_TO_MACRO, x as PoolMemoryWatchdog } from "../dist-Wt81pN5Y.mjs";
|
|
3
|
+
import { a as ALL_PIPELINE_STEPS, c as getDefaultModelForFormatFromDef, d as resolveModelForFormat, f as landmarkPrecisionVerdict, l as getStep, n as startEventLoopStallMonitor, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as getStepDefinition } from "../event-loop-stall-monitor-CtQCOZqm.mjs";
|
|
4
4
|
import { t as getSharp } from "../lazy-sharp-BtT5x-qP.mjs";
|
|
5
|
-
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-
|
|
5
|
+
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-1_D3EZT9.mjs";
|
|
6
6
|
import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../addon-utils-CgBCF-mE.mjs";
|
|
7
7
|
import sharp from "sharp";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
@@ -228,6 +228,94 @@ function pickBestRuntime(env, bestBackendHint) {
|
|
|
228
228
|
};
|
|
229
229
|
}
|
|
230
230
|
//#endregion
|
|
231
|
+
//#region src/detection-pipeline/engine/pool-provisioning.ts
|
|
232
|
+
/** The fields a {@link PoolProvisioningOverride} may carry, in table order. */
|
|
233
|
+
var OVERRIDABLE_KEYS = [
|
|
234
|
+
"concurrency",
|
|
235
|
+
"numWorkers",
|
|
236
|
+
"batchMode",
|
|
237
|
+
"windowMs",
|
|
238
|
+
"maxBatchSize",
|
|
239
|
+
"numStreams",
|
|
240
|
+
"intraOpThreads"
|
|
241
|
+
];
|
|
242
|
+
/** Backends that are not a runtime of their own and run on onnxruntime. */
|
|
243
|
+
var BACKEND_TO_RUNTIME = {
|
|
244
|
+
onnx: "onnx",
|
|
245
|
+
cpu: "onnx",
|
|
246
|
+
cuda: "onnx",
|
|
247
|
+
openvino: "openvino",
|
|
248
|
+
coreml: "coreml",
|
|
249
|
+
edgetpu: "edgetpu"
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* The registry runtime a device-pool backend provisions from. Unknown backends
|
|
253
|
+
* floor to `onnx` — the same floor `resolveDeviceEngine` applies to the model
|
|
254
|
+
* format, so a pool never comes up unprovisioned.
|
|
255
|
+
*/
|
|
256
|
+
function runtimeIdForBackend(backend) {
|
|
257
|
+
return BACKEND_TO_RUNTIME[backend] ?? "onnx";
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Resolve the provisioning for a pool running `engine`, applying the precedence
|
|
261
|
+
* documented at the top of this module. `engine.device` is not consulted today
|
|
262
|
+
* (see the module doc) but is part of the signature so a bench-backed
|
|
263
|
+
* per-device row lands here and nowhere else.
|
|
264
|
+
*/
|
|
265
|
+
function resolvePoolProvisioning(engine, override = {}) {
|
|
266
|
+
const runtime = runtimeIdForBackend(engine.backend);
|
|
267
|
+
const profile = tuningFor(runtime);
|
|
268
|
+
return {
|
|
269
|
+
runtime,
|
|
270
|
+
overriddenFields: OVERRIDABLE_KEYS.filter((k) => override[k] !== void 0),
|
|
271
|
+
concurrency: Math.max(1, override.concurrency ?? profile.concurrency),
|
|
272
|
+
numWorkers: Math.max(1, override.numWorkers ?? profile.numWorkers),
|
|
273
|
+
batchMode: override.batchMode ?? profile.batchMode,
|
|
274
|
+
windowMs: override.windowMs ?? profile.windowMs,
|
|
275
|
+
maxBatchSize: override.maxBatchSize ?? profile.maxBatchSize,
|
|
276
|
+
numStreams: override.numStreams ?? profile.numStreams,
|
|
277
|
+
intraOpThreads: override.intraOpThreads ?? profile.intraOpThreads
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function positiveNumber(value) {
|
|
281
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
282
|
+
}
|
|
283
|
+
function batchMode(value) {
|
|
284
|
+
return value === "none" || value === "list" || value === "window" ? value : void 0;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Read the operator override out of the detection addon's settings. Only a
|
|
288
|
+
* NON-AUTO value counts: the sliders persist `0` and the select persists `''`
|
|
289
|
+
* for "Auto (backend default)", and those must resolve to the runtime's
|
|
290
|
+
* registry value, not to a literal 0 worker threads.
|
|
291
|
+
*
|
|
292
|
+
* The result is deliberately sparse — an empty object means "nothing
|
|
293
|
+
* overridden", which is what lets a per-device pool provision from its own
|
|
294
|
+
* runtime instead of inheriting the node's.
|
|
295
|
+
*/
|
|
296
|
+
function readOperatorOverride(settings) {
|
|
297
|
+
const override = {};
|
|
298
|
+
const concurrency = positiveNumber(settings.concurrency);
|
|
299
|
+
if (concurrency !== void 0) override.concurrency = concurrency;
|
|
300
|
+
const numWorkers = positiveNumber(settings.numWorkers);
|
|
301
|
+
if (numWorkers !== void 0) override.numWorkers = numWorkers;
|
|
302
|
+
const mode = batchMode(settings.batchMode);
|
|
303
|
+
if (mode !== void 0) override.batchMode = mode;
|
|
304
|
+
const windowMs = positiveNumber(settings.windowMs);
|
|
305
|
+
if (windowMs !== void 0) override.windowMs = windowMs;
|
|
306
|
+
const maxBatchSize = positiveNumber(settings.maxBatchSize);
|
|
307
|
+
if (maxBatchSize !== void 0) override.maxBatchSize = maxBatchSize;
|
|
308
|
+
const numStreams = positiveNumber(settings.numStreams);
|
|
309
|
+
if (numStreams !== void 0) override.numStreams = numStreams;
|
|
310
|
+
const intraOpThreads = positiveNumber(settings.intraOpThreads);
|
|
311
|
+
if (intraOpThreads !== void 0) override.intraOpThreads = intraOpThreads;
|
|
312
|
+
return override;
|
|
313
|
+
}
|
|
314
|
+
/** Two overrides carry the same fields with the same values. */
|
|
315
|
+
function overridesEqual(a, b) {
|
|
316
|
+
return OVERRIDABLE_KEYS.every((k) => a[k] === b[k]);
|
|
317
|
+
}
|
|
318
|
+
//#endregion
|
|
231
319
|
//#region src/detection-pipeline/engine-store-keys.ts
|
|
232
320
|
/**
|
|
233
321
|
* Node-id normalisation for the detection pipeline.
|
|
@@ -438,6 +526,122 @@ function resolveDefaultDetectionModel(backend, device, format, getStepDef = getS
|
|
|
438
526
|
return getDefaultModelForFormatFromDef(def, format);
|
|
439
527
|
}
|
|
440
528
|
//#endregion
|
|
529
|
+
//#region src/detection-pipeline/device-override-mirror.ts
|
|
530
|
+
/**
|
|
531
|
+
* The per-device detection overrides, kept fresh OFF the frame path.
|
|
532
|
+
*
|
|
533
|
+
* ## The read this replaces
|
|
534
|
+
*
|
|
535
|
+
* `runPipeline` resolved a device's overrides by calling
|
|
536
|
+
* `settings.readDeviceStore(deviceId)` on **every inference frame**. Measured
|
|
537
|
+
* on the live hub 2026-08-25: `get detection-pipeline:addon-devices`, 1 143
|
|
538
|
+
* calls a minute at 0.44 rows per call — the disk asked, nineteen times a
|
|
539
|
+
* second, whether an operator has ever changed a threshold.
|
|
540
|
+
*
|
|
541
|
+
* ## Why this is not "a cache in the consumer" (D224)
|
|
542
|
+
*
|
|
543
|
+
* D224 forbids a SECOND authority over a fact that belongs to somebody else:
|
|
544
|
+
* the failure it records is a consumer caching a value whose write path does
|
|
545
|
+
* not run through that consumer, so an operator's own change is the one read
|
|
546
|
+
* the cache gets wrong.
|
|
547
|
+
*
|
|
548
|
+
* This addon OWNS the row. `<addonId>:addon-devices` is detection-pipeline's
|
|
549
|
+
* own store, and the only writers are this addon's `updateDeviceSettings` and
|
|
550
|
+
* its `clearDeviceOverrides` — both of which call {@link invalidate}. A write
|
|
551
|
+
* is therefore its own invalidation, exactly as D224 requires, and the window
|
|
552
|
+
* below governs only the case nobody writes through this process.
|
|
553
|
+
*
|
|
554
|
+
* ## The window, declared
|
|
555
|
+
*
|
|
556
|
+
* {@link DEVICE_OVERRIDE_STALE_MS} is what an operator's change costs on a node
|
|
557
|
+
* that did NOT serve the write — this addon runs per node, and a settings write
|
|
558
|
+
* lands on one of them. Thirty seconds is the trade: it removes 99.9% of the
|
|
559
|
+
* reads (one per device per window instead of one per frame) and it is short
|
|
560
|
+
* enough that "I moved the threshold and nothing happened" is not a support
|
|
561
|
+
* question. Ten minutes — {@link OPERATOR_WRITTEN_STALE_MS}, the constant for a
|
|
562
|
+
* cap whose value only an operator changes — is right for a POLLED reader that
|
|
563
|
+
* costs a camera round trip; this read costs one local SQLite `get`, and the
|
|
564
|
+
* cheap read buys a short window.
|
|
565
|
+
*
|
|
566
|
+
* A stale entry is SERVED and revalidated behind the caller, never awaited: a
|
|
567
|
+
* frame that waits for the settings store is a frame the pipeline dropped, and
|
|
568
|
+
* the reader is by definition about to ask again.
|
|
569
|
+
*/
|
|
570
|
+
/**
|
|
571
|
+
* How long a mirrored entry is served before a refresh is started behind the
|
|
572
|
+
* next reader. See the module doc for why it is 30 s and not ten minutes.
|
|
573
|
+
*/
|
|
574
|
+
var DEVICE_OVERRIDE_STALE_MS = 3e4;
|
|
575
|
+
/**
|
|
576
|
+
* How long a FAILED refresh is not retried.
|
|
577
|
+
*
|
|
578
|
+
* A refresh that throws leaves `fetchedAt` where it was, so without a cooldown
|
|
579
|
+
* every subsequent frame would start another one — a broken settings store
|
|
580
|
+
* would be hammered at frame rate by the very mechanism built to stop that.
|
|
581
|
+
*/
|
|
582
|
+
var DEVICE_OVERRIDE_MISS_COOLDOWN_MS = 5e3;
|
|
583
|
+
var DeviceOverrideMirror = class {
|
|
584
|
+
deps;
|
|
585
|
+
staleMs;
|
|
586
|
+
missCooldownMs;
|
|
587
|
+
entries = /* @__PURE__ */ new Map();
|
|
588
|
+
/** In-flight reads, so a burst of frames on a cold device costs ONE read and
|
|
589
|
+
* not one per frame — the exact failure this class exists to remove. */
|
|
590
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
591
|
+
now;
|
|
592
|
+
constructor(deps, staleMs = DEVICE_OVERRIDE_STALE_MS, missCooldownMs = DEVICE_OVERRIDE_MISS_COOLDOWN_MS) {
|
|
593
|
+
this.deps = deps;
|
|
594
|
+
this.staleMs = staleMs;
|
|
595
|
+
this.missCooldownMs = missCooldownMs;
|
|
596
|
+
this.now = deps.now ?? (() => Date.now());
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* The device's overrides.
|
|
600
|
+
*
|
|
601
|
+
* COLD awaits — the first frame on a camera has to get the real answer, and a
|
|
602
|
+
* default is exactly what "the operator lowered this threshold" looks like.
|
|
603
|
+
* WARM returns immediately, stale or not, and starts the refresh behind the
|
|
604
|
+
* caller.
|
|
605
|
+
*/
|
|
606
|
+
async resolve(deviceId) {
|
|
607
|
+
const entry = this.entries.get(deviceId);
|
|
608
|
+
if (entry === void 0) return this.load(deviceId);
|
|
609
|
+
if (this.now() - entry.fetchedAt >= this.staleMs) this.load(deviceId);
|
|
610
|
+
return entry.value;
|
|
611
|
+
}
|
|
612
|
+
/** Drop a device's entry. Called by every writer in this process, so an
|
|
613
|
+
* operator's own change is never served from the mirror. */
|
|
614
|
+
invalidate(deviceId) {
|
|
615
|
+
this.entries.delete(deviceId);
|
|
616
|
+
}
|
|
617
|
+
invalidateAll() {
|
|
618
|
+
this.entries.clear();
|
|
619
|
+
}
|
|
620
|
+
async load(deviceId) {
|
|
621
|
+
const pending = this.inFlight.get(deviceId);
|
|
622
|
+
if (pending !== void 0) return pending;
|
|
623
|
+
const run = this.deps.read(deviceId).then((value) => {
|
|
624
|
+
this.entries.set(deviceId, {
|
|
625
|
+
value,
|
|
626
|
+
fetchedAt: this.now()
|
|
627
|
+
});
|
|
628
|
+
return value;
|
|
629
|
+
}).catch((err) => {
|
|
630
|
+
this.deps.onRefreshFailed?.(deviceId, err);
|
|
631
|
+
const existing = this.entries.get(deviceId);
|
|
632
|
+
if (existing !== void 0) {
|
|
633
|
+
existing.fetchedAt = this.now() - this.staleMs + this.missCooldownMs;
|
|
634
|
+
return existing.value;
|
|
635
|
+
}
|
|
636
|
+
return {};
|
|
637
|
+
}).finally(() => {
|
|
638
|
+
this.inFlight.delete(deviceId);
|
|
639
|
+
});
|
|
640
|
+
this.inFlight.set(deviceId, run);
|
|
641
|
+
return run;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
//#endregion
|
|
441
645
|
//#region src/detection-pipeline/engine/shared-inference-pool.ts
|
|
442
646
|
/**
|
|
443
647
|
* SharedInferencePool — TypeScript wrapper for inference_pool.py.
|
|
@@ -1980,94 +2184,6 @@ function deviceKeyOf(engine) {
|
|
|
1980
2184
|
return engine.device ? `${engine.backend}:${engine.device}` : engine.backend;
|
|
1981
2185
|
}
|
|
1982
2186
|
//#endregion
|
|
1983
|
-
//#region src/detection-pipeline/engine/pool-provisioning.ts
|
|
1984
|
-
/** The fields a {@link PoolProvisioningOverride} may carry, in table order. */
|
|
1985
|
-
var OVERRIDABLE_KEYS = [
|
|
1986
|
-
"concurrency",
|
|
1987
|
-
"numWorkers",
|
|
1988
|
-
"batchMode",
|
|
1989
|
-
"windowMs",
|
|
1990
|
-
"maxBatchSize",
|
|
1991
|
-
"numStreams",
|
|
1992
|
-
"intraOpThreads"
|
|
1993
|
-
];
|
|
1994
|
-
/** Backends that are not a runtime of their own and run on onnxruntime. */
|
|
1995
|
-
var BACKEND_TO_RUNTIME = {
|
|
1996
|
-
onnx: "onnx",
|
|
1997
|
-
cpu: "onnx",
|
|
1998
|
-
cuda: "onnx",
|
|
1999
|
-
openvino: "openvino",
|
|
2000
|
-
coreml: "coreml",
|
|
2001
|
-
edgetpu: "edgetpu"
|
|
2002
|
-
};
|
|
2003
|
-
/**
|
|
2004
|
-
* The registry runtime a device-pool backend provisions from. Unknown backends
|
|
2005
|
-
* floor to `onnx` — the same floor `resolveDeviceEngine` applies to the model
|
|
2006
|
-
* format, so a pool never comes up unprovisioned.
|
|
2007
|
-
*/
|
|
2008
|
-
function runtimeIdForBackend(backend) {
|
|
2009
|
-
return BACKEND_TO_RUNTIME[backend] ?? "onnx";
|
|
2010
|
-
}
|
|
2011
|
-
/**
|
|
2012
|
-
* Resolve the provisioning for a pool running `engine`, applying the precedence
|
|
2013
|
-
* documented at the top of this module. `engine.device` is not consulted today
|
|
2014
|
-
* (see the module doc) but is part of the signature so a bench-backed
|
|
2015
|
-
* per-device row lands here and nowhere else.
|
|
2016
|
-
*/
|
|
2017
|
-
function resolvePoolProvisioning(engine, override = {}) {
|
|
2018
|
-
const runtime = runtimeIdForBackend(engine.backend);
|
|
2019
|
-
const profile = tuningFor(runtime);
|
|
2020
|
-
return {
|
|
2021
|
-
runtime,
|
|
2022
|
-
overriddenFields: OVERRIDABLE_KEYS.filter((k) => override[k] !== void 0),
|
|
2023
|
-
concurrency: Math.max(1, override.concurrency ?? profile.concurrency),
|
|
2024
|
-
numWorkers: Math.max(1, override.numWorkers ?? profile.numWorkers),
|
|
2025
|
-
batchMode: override.batchMode ?? profile.batchMode,
|
|
2026
|
-
windowMs: override.windowMs ?? profile.windowMs,
|
|
2027
|
-
maxBatchSize: override.maxBatchSize ?? profile.maxBatchSize,
|
|
2028
|
-
numStreams: override.numStreams ?? profile.numStreams,
|
|
2029
|
-
intraOpThreads: override.intraOpThreads ?? profile.intraOpThreads
|
|
2030
|
-
};
|
|
2031
|
-
}
|
|
2032
|
-
function positiveNumber(value) {
|
|
2033
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2034
|
-
}
|
|
2035
|
-
function batchMode(value) {
|
|
2036
|
-
return value === "none" || value === "list" || value === "window" ? value : void 0;
|
|
2037
|
-
}
|
|
2038
|
-
/**
|
|
2039
|
-
* Read the operator override out of the detection addon's settings. Only a
|
|
2040
|
-
* NON-AUTO value counts: the sliders persist `0` and the select persists `''`
|
|
2041
|
-
* for "Auto (backend default)", and those must resolve to the runtime's
|
|
2042
|
-
* registry value, not to a literal 0 worker threads.
|
|
2043
|
-
*
|
|
2044
|
-
* The result is deliberately sparse — an empty object means "nothing
|
|
2045
|
-
* overridden", which is what lets a per-device pool provision from its own
|
|
2046
|
-
* runtime instead of inheriting the node's.
|
|
2047
|
-
*/
|
|
2048
|
-
function readOperatorOverride(settings) {
|
|
2049
|
-
const override = {};
|
|
2050
|
-
const concurrency = positiveNumber(settings.concurrency);
|
|
2051
|
-
if (concurrency !== void 0) override.concurrency = concurrency;
|
|
2052
|
-
const numWorkers = positiveNumber(settings.numWorkers);
|
|
2053
|
-
if (numWorkers !== void 0) override.numWorkers = numWorkers;
|
|
2054
|
-
const mode = batchMode(settings.batchMode);
|
|
2055
|
-
if (mode !== void 0) override.batchMode = mode;
|
|
2056
|
-
const windowMs = positiveNumber(settings.windowMs);
|
|
2057
|
-
if (windowMs !== void 0) override.windowMs = windowMs;
|
|
2058
|
-
const maxBatchSize = positiveNumber(settings.maxBatchSize);
|
|
2059
|
-
if (maxBatchSize !== void 0) override.maxBatchSize = maxBatchSize;
|
|
2060
|
-
const numStreams = positiveNumber(settings.numStreams);
|
|
2061
|
-
if (numStreams !== void 0) override.numStreams = numStreams;
|
|
2062
|
-
const intraOpThreads = positiveNumber(settings.intraOpThreads);
|
|
2063
|
-
if (intraOpThreads !== void 0) override.intraOpThreads = intraOpThreads;
|
|
2064
|
-
return override;
|
|
2065
|
-
}
|
|
2066
|
-
/** Two overrides carry the same fields with the same values. */
|
|
2067
|
-
function overridesEqual(a, b) {
|
|
2068
|
-
return OVERRIDABLE_KEYS.every((k) => a[k] === b[k]);
|
|
2069
|
-
}
|
|
2070
|
-
//#endregion
|
|
2071
2187
|
//#region src/detection-pipeline/engine/engine-factory.ts
|
|
2072
2188
|
var BACKEND_TO_POOL_RUNTIME = {
|
|
2073
2189
|
coreml: "coreml",
|
|
@@ -6220,6 +6336,14 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6220
6336
|
readDeviceStore;
|
|
6221
6337
|
/** Delete all per-device settings for a device (used by `clearDeviceOverrides`). */
|
|
6222
6338
|
clearDeviceStore;
|
|
6339
|
+
/**
|
|
6340
|
+
* The per-device overrides the FRAME path reads, kept fresh off that path.
|
|
6341
|
+
*
|
|
6342
|
+
* Public so the addon class can drop a device on its own
|
|
6343
|
+
* `updateDeviceSettings` — the write and the mirror have to be the same
|
|
6344
|
+
* process's, or the mirror becomes the second authority D224 forbids.
|
|
6345
|
+
*/
|
|
6346
|
+
deviceOverrides;
|
|
6223
6347
|
constructor(settings, modelsDir, logger, eventBus = null, detectionConfigSchemaSource = null, executorOptions = {}) {
|
|
6224
6348
|
this.modelsDir = modelsDir;
|
|
6225
6349
|
this.eventBus = eventBus;
|
|
@@ -6231,6 +6355,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6231
6355
|
this.readDeviceStore = settings.readDeviceStore ?? (async () => ({}));
|
|
6232
6356
|
const clearDeviceStore = settings.clearDeviceStore;
|
|
6233
6357
|
this.clearDeviceStore = clearDeviceStore ? (deviceId) => clearDeviceStore.call(settings, deviceId) : async () => void 0;
|
|
6358
|
+
this.deviceOverrides = new DeviceOverrideMirror({
|
|
6359
|
+
read: (deviceId) => this.readDeviceStore(deviceId),
|
|
6360
|
+
onRefreshFailed: (deviceId, err) => this.log.warn("per-device detection overrides refresh failed — serving the last value", {
|
|
6361
|
+
tags: { deviceId },
|
|
6362
|
+
meta: { error: errMsg(err) }
|
|
6363
|
+
})
|
|
6364
|
+
});
|
|
6234
6365
|
this.currentEngine = ONNX_FLOOR;
|
|
6235
6366
|
this.log.info("Engine pick pending (placeholder until probe / persisted selection)", { meta: {
|
|
6236
6367
|
runtime: this.currentEngine.runtime,
|
|
@@ -7415,7 +7546,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
7415
7546
|
setupMs = performance.now() - wallT0 - decodeMs;
|
|
7416
7547
|
const isRuntime = Boolean(runtimeFrame || runtimeFrameRef);
|
|
7417
7548
|
const effectiveDeviceId = input.deviceId ?? 0;
|
|
7418
|
-
const deviceOverrides = effectiveDeviceId > 0 ? await this.
|
|
7549
|
+
const deviceOverrides = effectiveDeviceId > 0 ? await this.deviceOverrides.resolve(effectiveDeviceId) : {};
|
|
7419
7550
|
const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
|
|
7420
7551
|
const nativeCropProvider = this.buildNativeCropProviderFromRef(input.nativeCropRef) ?? this.buildNativeFaceCropProvider(input.frameHandle);
|
|
7421
7552
|
let cropZoneBbox;
|
|
@@ -8484,7 +8615,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
8484
8615
|
if (!api) return 0;
|
|
8485
8616
|
let devices = [];
|
|
8486
8617
|
try {
|
|
8487
|
-
devices = await api.deviceManager.listAll.query({});
|
|
8618
|
+
devices = await api.deviceManager.listAll.query({ projection: "slim" });
|
|
8488
8619
|
} catch (err) {
|
|
8489
8620
|
this.log.warn("clearDeviceOverrides: device enumeration failed — skipping per-device clear", { meta: { error: errMsg(err) } });
|
|
8490
8621
|
return 0;
|
|
@@ -8492,6 +8623,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
8492
8623
|
let cleared = 0;
|
|
8493
8624
|
for (const device of devices) try {
|
|
8494
8625
|
await this.clearDeviceStore(device.id);
|
|
8626
|
+
this.deviceOverrides.invalidate(device.id);
|
|
8495
8627
|
cleared++;
|
|
8496
8628
|
} catch (err) {
|
|
8497
8629
|
this.log.warn("clearDeviceOverrides: failed to clear per-device overrides for device", {
|
|
@@ -9049,6 +9181,20 @@ function isProfileSliderKey(key) {
|
|
|
9049
9181
|
}
|
|
9050
9182
|
var DetectionPipelineAddon = class extends BaseAddon {
|
|
9051
9183
|
provider = null;
|
|
9184
|
+
/**
|
|
9185
|
+
* An operator's own write drops the mirror the frame path reads.
|
|
9186
|
+
*
|
|
9187
|
+
* This override is the half of `device-override-mirror.ts` that keeps it from
|
|
9188
|
+
* being the second authority D224 forbids: `BaseAddon.updateDeviceSettings`
|
|
9189
|
+
* writes THIS addon's `<addonId>:addon-devices` row, so the write and the
|
|
9190
|
+
* mirror are in one process and the write invalidates. Without it the
|
|
9191
|
+
* operator's own change would be the one read the mirror got wrong, which is
|
|
9192
|
+
* the exact failure D224 records.
|
|
9193
|
+
*/
|
|
9194
|
+
async updateDeviceSettings(deviceId, patch) {
|
|
9195
|
+
await super.updateDeviceSettings(deviceId, patch);
|
|
9196
|
+
this.provider?.deviceOverrides.invalidate(deviceId);
|
|
9197
|
+
}
|
|
9052
9198
|
/** Last non-null probed hardware PER NODE — reused when the probe transiently
|
|
9053
9199
|
* returns null so offered backends / device lists don't collapse. Keyed by
|
|
9054
9200
|
* node because the hub addon probes other nodes when serving their config.
|