@camstack/addon-pipeline 1.1.68 → 1.1.70
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 +155 -27
- package/dist/detection-pipeline/index.mjs +155 -27
- package/dist/{dist-BxxVKcnw.js → dist-BWn5k0A5.js} +37 -2
- package/dist/{dist-DkGHtAaK.mjs → dist-CurVt3xC.mjs} +37 -2
- package/dist/motion-wasm/index.js +1 -1
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +7 -3
- package/dist/pipeline-runner/index.mjs +7 -3
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.mjs +1 -1
- package/dist/{step-definitions-CAs0RD2N.js → step-definitions-B7e8rPe3.js} +114 -187
- package/dist/{step-definitions-Delhq28w.mjs → step-definitions-BKm17nce.mjs} +114 -187
- package/dist/stream-broker/_stub.js +1 -1
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BUXIIogo.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-yAAGMET5.mjs} +2 -2
- package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DlALQ_wp.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CDBALHSV.mjs} +1 -1
- package/dist/stream-broker/{hostInit-D7_95hNV.mjs → hostInit-DUzRsTmt.mjs} +2 -2
- 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-CRvzj7Of.js → MaskShapeCanvas-DI4BY7W2-D6bWMuew.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-CBgvPUB-.js → MotionZonesSettings-NcxxQN8r-C-1enV21.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DH07fe1k.js → PrivacyMaskSettings-APgPLF7p-By9pdGP7.js} +1 -1
- package/embed-dist/assets/{index-BkoPcGtQ.js → index-BPGh79kn.js} +5 -5
- package/embed-dist/index.html +1 -1
- package/package.json +1 -1
- package/python/inference_pool.py +38 -3
|
@@ -3,7 +3,7 @@ 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-
|
|
6
|
+
const require_dist = require("../dist-BWn5k0A5.js");
|
|
7
7
|
const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
|
|
8
8
|
const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-Dv-oFwjN.js");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { B as errMsg, K as hydrateSchema, T as mapAudioLabelToMacro, V as BaseAddon, _ as audioAnalyzerCapability, g as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, u as HF_BASE_URL } from "../dist-
|
|
2
|
+
import { B as errMsg, K as hydrateSchema, T as mapAudioLabelToMacro, V as BaseAddon, _ as audioAnalyzerCapability, g as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, u as HF_BASE_URL } from "../dist-CurVt3xC.mjs";
|
|
3
3
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
4
4
|
import { i as downloadFile } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -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-BWn5k0A5.js");
|
|
7
|
+
const require_step_definitions = require("../step-definitions-B7e8rPe3.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");
|
|
@@ -318,16 +318,16 @@ function projectNodeEngine(store, nodeId) {
|
|
|
318
318
|
/** The object-detection step id — the only slot this resolver applies to. */
|
|
319
319
|
var OBJECT_DETECTION_STEP_ID$1 = "object-detection";
|
|
320
320
|
/**
|
|
321
|
-
* Balanced default object-detection model per accelerator class
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
321
|
+
* Balanced default object-detection model per accelerator class. `'cpu'` maps to
|
|
322
|
+
* `null` → the caller substitutes the step's own `defaultModelId` (`yolo26n`).
|
|
323
|
+
* INTEL NPU + iGPU default to OUR YOLOv9 (int8 @320): yolo26 does NOT compile on
|
|
324
|
+
* the Intel NPU (arch incompat → 0 frames), while yolov9 runs on it (~298 fps
|
|
325
|
+
* measured). Apple ANE stays on yolo26 until the CoreML yolov9 build lands.
|
|
326
326
|
*/
|
|
327
327
|
var MODEL_BY_CLASS = {
|
|
328
|
-
"apple-ane": "
|
|
329
|
-
"intel-npu": "
|
|
330
|
-
"intel-gpu": "
|
|
328
|
+
"apple-ane": "yolov9m-320",
|
|
329
|
+
"intel-npu": "yolov9m-320-int8",
|
|
330
|
+
"intel-gpu": "yolov9m-320-int8",
|
|
331
331
|
cpu: null
|
|
332
332
|
};
|
|
333
333
|
/**
|
|
@@ -567,6 +567,7 @@ var PoolWorker = class {
|
|
|
567
567
|
concurrency: this.opts.concurrency,
|
|
568
568
|
models: initialModels.map((m) => serializeModelConfig(m))
|
|
569
569
|
};
|
|
570
|
+
if (this.opts.device) config["device"] = this.opts.device;
|
|
570
571
|
if (this.opts.tuning) {
|
|
571
572
|
config["batch_mode"] = this.opts.tuning.batchMode;
|
|
572
573
|
config["window_ms"] = this.opts.tuning.windowMs;
|
|
@@ -814,7 +815,9 @@ var SharedInferencePool = class {
|
|
|
814
815
|
this.concurrency = Math.max(1, options.concurrency ?? 1);
|
|
815
816
|
this.tuning = options.tuning ?? null;
|
|
816
817
|
this.numWorkers = Math.max(1, options.numWorkers ?? 1);
|
|
818
|
+
this.device = options.device;
|
|
817
819
|
}
|
|
820
|
+
device;
|
|
818
821
|
/** Pid of the first worker (for legacy callers). Use `getPids()` for all. */
|
|
819
822
|
getPid() {
|
|
820
823
|
return this.workers[0]?.getPid() ?? null;
|
|
@@ -836,7 +839,8 @@ var SharedInferencePool = class {
|
|
|
836
839
|
concurrency: this.concurrency,
|
|
837
840
|
tuning: this.tuning,
|
|
838
841
|
logger: this.log,
|
|
839
|
-
workerLabel: `w${i}
|
|
842
|
+
workerLabel: `w${i}`,
|
|
843
|
+
...this.device ? { device: this.device } : {}
|
|
840
844
|
}));
|
|
841
845
|
const t0 = performance.now();
|
|
842
846
|
const results = await Promise.all(this.workers.map((w) => w.initialize(initialModels)));
|
|
@@ -1495,7 +1499,8 @@ var EngineFactory = class {
|
|
|
1495
1499
|
this.pool = new SharedInferencePool(pythonPath, poolRuntime, this.log.child("pool"), {
|
|
1496
1500
|
concurrency,
|
|
1497
1501
|
tuning: resolvedTuning,
|
|
1498
|
-
numWorkers
|
|
1502
|
+
numWorkers,
|
|
1503
|
+
...this.opts.engine.device ? { device: this.opts.engine.device } : {}
|
|
1499
1504
|
});
|
|
1500
1505
|
this.poolManager = new PipelineModelManager(this.pool, (stepId, modelId) => this.buildPoolModelConfig(stepId, modelId, poolRuntime), this.log.child("model-mgr"));
|
|
1501
1506
|
await this.pool.initialize([]);
|
|
@@ -1543,6 +1548,39 @@ var EngineFactory = class {
|
|
|
1543
1548
|
}
|
|
1544
1549
|
};
|
|
1545
1550
|
//#endregion
|
|
1551
|
+
//#region src/detection-pipeline/engine/resolve-device-engine.ts
|
|
1552
|
+
/**
|
|
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
|
+
* Resolve a Phase-1 device key (`<backend>:<device>`, or bare `cpu`) into the
|
|
1566
|
+
* {@link PipelineEngineChoice} used to build that device's inference pool.
|
|
1567
|
+
* Throws on an unknown backend token so a bad `deviceKey` fails loud rather
|
|
1568
|
+
* than silently building a wrong pool.
|
|
1569
|
+
*/
|
|
1570
|
+
function resolveDeviceEngine(deviceKey) {
|
|
1571
|
+
const colon = deviceKey.indexOf(":");
|
|
1572
|
+
const backend = colon >= 0 ? deviceKey.slice(0, colon) : deviceKey;
|
|
1573
|
+
const device = colon >= 0 ? deviceKey.slice(colon + 1) : deviceKey;
|
|
1574
|
+
const format = BACKEND_FORMAT[backend];
|
|
1575
|
+
if (format === void 0) throw new Error(`resolveDeviceEngine: unknown backend "${backend}" in deviceKey "${deviceKey}"`);
|
|
1576
|
+
return {
|
|
1577
|
+
runtime: "python",
|
|
1578
|
+
backend,
|
|
1579
|
+
format,
|
|
1580
|
+
device
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
//#endregion
|
|
1546
1584
|
//#region src/detection-pipeline/engine-provisioner.ts
|
|
1547
1585
|
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
1548
1586
|
var BACKOFF_SCHEDULE_MS = [
|
|
@@ -3938,6 +3976,15 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
3938
3976
|
detectionConfigSchemaSource;
|
|
3939
3977
|
executorOptions;
|
|
3940
3978
|
engineFactory = null;
|
|
3979
|
+
/**
|
|
3980
|
+
* Per-device engine factories (Phase 2 multi-device), keyed by deviceKey
|
|
3981
|
+
* (`<backend>:<device>`, e.g. `openvino:npu`, `edgetpu:usb`). Each is its own
|
|
3982
|
+
* inference pool, warm CONCURRENTLY — so the node runs detection on NPU + iGPU
|
|
3983
|
+
* + Coral in parallel. The default (node current-engine) pool stays
|
|
3984
|
+
* `this.engineFactory`; device routing is a LOCAL per-dispatch choice that
|
|
3985
|
+
* never mutates shared state, so concurrent multi-device sessions don't race.
|
|
3986
|
+
*/
|
|
3987
|
+
factoriesByDevice = /* @__PURE__ */ new Map();
|
|
3941
3988
|
executor = null;
|
|
3942
3989
|
currentSteps = null;
|
|
3943
3990
|
currentEngine;
|
|
@@ -5056,8 +5103,12 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5056
5103
|
emit(`Image: ${imageWidth}×${imageHeight}`);
|
|
5057
5104
|
}
|
|
5058
5105
|
decodeMs = performance.now() - decodeT0;
|
|
5059
|
-
const
|
|
5060
|
-
const
|
|
5106
|
+
const deviceEngine = input.deviceKey ? resolveDeviceEngine(input.deviceKey) : void 0;
|
|
5107
|
+
const resolveFormat = deviceEngine?.format ?? input.engine?.format ?? this.currentEngine.format;
|
|
5108
|
+
const resolveEngine = deviceEngine ? {
|
|
5109
|
+
backend: deviceEngine.backend,
|
|
5110
|
+
device: deviceEngine.device ?? null
|
|
5111
|
+
} : input.engine ? {
|
|
5061
5112
|
backend: input.engine.backend,
|
|
5062
5113
|
device: input.engine.device ?? null
|
|
5063
5114
|
} : {
|
|
@@ -5158,7 +5209,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5158
5209
|
logger: this.log
|
|
5159
5210
|
});
|
|
5160
5211
|
this.executor = executor;
|
|
5161
|
-
const
|
|
5212
|
+
const dispatchEngine = input.deviceKey ? resolveDeviceEngine(input.deviceKey) : runEngine;
|
|
5213
|
+
const dispatchFactory = input.deviceKey ? await this.resolveDeviceFactory(input.deviceKey) : this.engineFactory;
|
|
5214
|
+
const needed = enabledSteps.filter((s) => !dispatchFactory.isLoadedWithModel(s.addonId, s.modelId));
|
|
5162
5215
|
if (needed.length > 0) {
|
|
5163
5216
|
this.log.info("Benchmark: models to load", { meta: {
|
|
5164
5217
|
count: needed.length,
|
|
@@ -5169,11 +5222,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5169
5222
|
addonId: s.addonId,
|
|
5170
5223
|
modelId: s.modelId
|
|
5171
5224
|
});
|
|
5172
|
-
await this.ensureModelsForSteps(benchmarkSteps);
|
|
5225
|
+
await this.ensureModelsForSteps(benchmarkSteps, dispatchFactory, dispatchEngine.format);
|
|
5173
5226
|
emit(`All models loaded`);
|
|
5174
5227
|
}
|
|
5175
5228
|
emit("Running inference...");
|
|
5176
|
-
const tree = buildExecutableTree(benchmarkSteps, (stepId) =>
|
|
5229
|
+
const tree = buildExecutableTree(benchmarkSteps, (stepId) => dispatchFactory.getEngine(stepId));
|
|
5177
5230
|
setupMs = performance.now() - wallT0 - decodeMs;
|
|
5178
5231
|
const isRuntime = Boolean(runtimeFrame);
|
|
5179
5232
|
const effectiveDeviceId = input.deviceId ?? 0;
|
|
@@ -5417,19 +5470,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5417
5470
|
* before any of them committed via `stepToLoaded.set`.
|
|
5418
5471
|
*/
|
|
5419
5472
|
modelLoadInFlight = null;
|
|
5420
|
-
/** Ensure all models needed by steps are downloaded and loaded in the engine pool.
|
|
5421
|
-
|
|
5473
|
+
/** Ensure all models needed by steps are downloaded and loaded in the engine pool.
|
|
5474
|
+
* `factory`/`format` default to the node's engine; a per-device dispatch passes
|
|
5475
|
+
* that device's factory + format (Phase 2 multi-device). */
|
|
5476
|
+
async ensureModelsForSteps(steps, factory = this.engineFactory, format = this.currentEngine?.format ?? "onnx") {
|
|
5422
5477
|
while (this.modelLoadInFlight) await this.modelLoadInFlight;
|
|
5423
5478
|
const allEnabled = flattenSteps(steps);
|
|
5424
5479
|
const needed = [];
|
|
5425
5480
|
for (const step of allEnabled) {
|
|
5426
5481
|
if (!step.enabled) continue;
|
|
5427
|
-
if (
|
|
5482
|
+
if (factory?.isLoadedWithModel(step.addonId, step.modelId)) continue;
|
|
5428
5483
|
needed.push(step);
|
|
5429
5484
|
}
|
|
5430
5485
|
if (needed.length === 0) return;
|
|
5431
5486
|
const work = (async () => {
|
|
5432
|
-
const format = this.currentEngine?.format ?? "onnx";
|
|
5433
5487
|
for (const step of needed) {
|
|
5434
5488
|
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5435
5489
|
if (modelEntry && !require_model_download_service_Cp9f4dk6.isModelDownloaded(this.modelsDir, modelEntry, format)) {
|
|
@@ -5445,7 +5499,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5445
5499
|
count: needed.length,
|
|
5446
5500
|
models: needed.map((s) => `${s.addonId}/${s.modelId}`)
|
|
5447
5501
|
} });
|
|
5448
|
-
await
|
|
5502
|
+
await factory.loadAdditional(needed);
|
|
5449
5503
|
})();
|
|
5450
5504
|
this.modelLoadInFlight = work;
|
|
5451
5505
|
try {
|
|
@@ -5550,8 +5604,12 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5550
5604
|
}
|
|
5551
5605
|
async runPipelineBatchImpl(input) {
|
|
5552
5606
|
if (input.frames.length === 0) return { results: [] };
|
|
5553
|
-
const
|
|
5554
|
-
const
|
|
5607
|
+
const deviceEngine = input.deviceKey ? resolveDeviceEngine(input.deviceKey) : void 0;
|
|
5608
|
+
const resolveFormat = deviceEngine?.format ?? input.engine?.format ?? this.currentEngine.format;
|
|
5609
|
+
const resolveEngine = deviceEngine ? {
|
|
5610
|
+
backend: deviceEngine.backend,
|
|
5611
|
+
device: deviceEngine.device ?? null
|
|
5612
|
+
} : input.engine ? {
|
|
5555
5613
|
backend: input.engine.backend,
|
|
5556
5614
|
device: input.engine.device ?? null
|
|
5557
5615
|
} : {
|
|
@@ -5580,9 +5638,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5580
5638
|
await this.ensureEngineFactory();
|
|
5581
5639
|
const restoreEngine = await this.applyEngineOverride(input.engine);
|
|
5582
5640
|
try {
|
|
5583
|
-
const factory = this.engineFactory;
|
|
5641
|
+
const factory = input.deviceKey ? await this.resolveDeviceFactory(input.deviceKey) : this.engineFactory;
|
|
5584
5642
|
if (!factory) throw new Error("runPipelineBatch: factory not initialised");
|
|
5585
|
-
if (enabledSteps.filter((s) => !factory.isLoadedWithModel(s.addonId, s.modelId)).length > 0) await this.ensureModelsForSteps(benchmarkSteps);
|
|
5643
|
+
if (enabledSteps.filter((s) => !factory.isLoadedWithModel(s.addonId, s.modelId)).length > 0) await this.ensureModelsForSteps(benchmarkSteps, factory, resolveFormat);
|
|
5586
5644
|
const canFastPath = singleRoot && allRaw && uniformDims && factory.supportsBatch();
|
|
5587
5645
|
this.log.info("runPipelineBatch path decision", { meta: {
|
|
5588
5646
|
phase: "batch",
|
|
@@ -5596,7 +5654,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5596
5654
|
steps: input.steps,
|
|
5597
5655
|
frame,
|
|
5598
5656
|
deviceId: input.deviceId,
|
|
5599
|
-
sessionId: input.sessionId
|
|
5657
|
+
sessionId: input.sessionId,
|
|
5658
|
+
deviceKey: input.deviceKey
|
|
5600
5659
|
}))) };
|
|
5601
5660
|
const items = input.frames.map((frame) => ({
|
|
5602
5661
|
raw: Buffer.from(frame.data),
|
|
@@ -5730,6 +5789,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5730
5789
|
this.engineFactory = null;
|
|
5731
5790
|
this.executor = null;
|
|
5732
5791
|
}
|
|
5792
|
+
const deviceFactories = Array.from(this.factoriesByDevice.values());
|
|
5793
|
+
this.factoriesByDevice.clear();
|
|
5794
|
+
await Promise.all(deviceFactories.map((f) => f.dispose().catch(() => void 0)));
|
|
5733
5795
|
for (const id of Array.from(this.deviceProxies.keys())) this.releaseDeviceProxy(id);
|
|
5734
5796
|
}
|
|
5735
5797
|
/**
|
|
@@ -5849,6 +5911,58 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5849
5911
|
await this.engineFactory.initialize([]);
|
|
5850
5912
|
}
|
|
5851
5913
|
/**
|
|
5914
|
+
* Get (or lazily build) the inference-pool factory for an explicit device
|
|
5915
|
+
* (`<backend>:<device>`, e.g. `openvino:npu`, `edgetpu:usb`) — Phase 2
|
|
5916
|
+
* multi-device. Each device runs its OWN pool concurrently. Never mutates
|
|
5917
|
+
* `this.engineFactory`; when the requested device equals the node's current
|
|
5918
|
+
* engine the default pool is reused (no duplicate pool for the same device).
|
|
5919
|
+
*/
|
|
5920
|
+
/**
|
|
5921
|
+
* In-flight device-pool creations, keyed by deviceKey. Single-flights
|
|
5922
|
+
* `resolveDeviceFactory` so two concurrent calls for the SAME device never
|
|
5923
|
+
* both spawn a pool — critical for single-context accelerators like the Coral
|
|
5924
|
+
* Edge TPU, where two pools racing to `load_delegate` on the one USB unit both
|
|
5925
|
+
* fail (the loser can't claim the already-held device).
|
|
5926
|
+
*/
|
|
5927
|
+
deviceFactoryInflight = /* @__PURE__ */ new Map();
|
|
5928
|
+
async resolveDeviceFactory(deviceKey) {
|
|
5929
|
+
const engine = resolveDeviceEngine(deviceKey);
|
|
5930
|
+
if (enginesEqual(engine, this.currentEngine)) {
|
|
5931
|
+
await this.ensureEngineFactory();
|
|
5932
|
+
return this.engineFactory;
|
|
5933
|
+
}
|
|
5934
|
+
const existing = this.factoriesByDevice.get(deviceKey);
|
|
5935
|
+
if (existing) return existing;
|
|
5936
|
+
const inflight = this.deviceFactoryInflight.get(deviceKey);
|
|
5937
|
+
if (inflight) return inflight;
|
|
5938
|
+
const creation = (async () => {
|
|
5939
|
+
await this.ensureBackendDeps(engine);
|
|
5940
|
+
const factory = new EngineFactory({
|
|
5941
|
+
engine,
|
|
5942
|
+
modelsDir: this.modelsDir,
|
|
5943
|
+
logger: this.log.child(`engine:${deviceKey}`),
|
|
5944
|
+
pythonPath: this.executorOptions.pythonPath ?? "",
|
|
5945
|
+
concurrency: this.executorOptions.concurrency,
|
|
5946
|
+
tuning: this.executorOptions.tuning,
|
|
5947
|
+
numWorkers: this.executorOptions.numWorkers
|
|
5948
|
+
});
|
|
5949
|
+
try {
|
|
5950
|
+
await factory.initialize([]);
|
|
5951
|
+
} catch (err) {
|
|
5952
|
+
await factory.dispose().catch(() => void 0);
|
|
5953
|
+
throw err;
|
|
5954
|
+
}
|
|
5955
|
+
this.factoriesByDevice.set(deviceKey, factory);
|
|
5956
|
+
return factory;
|
|
5957
|
+
})();
|
|
5958
|
+
this.deviceFactoryInflight.set(deviceKey, creation);
|
|
5959
|
+
try {
|
|
5960
|
+
return await creation;
|
|
5961
|
+
} finally {
|
|
5962
|
+
this.deviceFactoryInflight.delete(deviceKey);
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
/**
|
|
5852
5966
|
* Phase 2b — resolve the engine from the addon's new schema-backed
|
|
5853
5967
|
* fields (`engineRuntime`, `engineBackend`, `engineDevice`). The legacy
|
|
5854
5968
|
* `pipelineEngine` JSON-blob fallback was removed with the executor seed
|
|
@@ -6039,6 +6153,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6039
6153
|
idleTtlMs: DetectionPipelineProvider.OVERRIDE_CACHE_TTL_MS
|
|
6040
6154
|
});
|
|
6041
6155
|
}
|
|
6156
|
+
for (const [deviceKey, factory] of this.factoriesByDevice) {
|
|
6157
|
+
const eng = resolveDeviceEngine(deviceKey);
|
|
6158
|
+
const loaded = factory.listLoaded();
|
|
6159
|
+
out.push({
|
|
6160
|
+
engineKey: `${eng.runtime}/${eng.backend}/${eng.device ?? "default"} [${deviceKey}]`,
|
|
6161
|
+
engine: eng,
|
|
6162
|
+
modelsLoaded: loaded.map((l) => `${l.stepId}/${l.modelId}`),
|
|
6163
|
+
inUseByCameras: [],
|
|
6164
|
+
kind: "device-pool",
|
|
6165
|
+
poolPid: factory.getPoolPid(),
|
|
6166
|
+
idleMs: null,
|
|
6167
|
+
idleTtlMs: null
|
|
6168
|
+
});
|
|
6169
|
+
}
|
|
6042
6170
|
return out;
|
|
6043
6171
|
}
|
|
6044
6172
|
async spinEngine(input) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __require } from "../chunk-BdkLduGY.mjs";
|
|
2
|
-
import { $ as sleep, A as pipelineExecutorCapability, B as errMsg, C as evaluateZoneRules, F as runtimeDevices$1, G as createEvent, K as hydrateSchema, R as supportedRuntimes$1, S as detectionPipelineCapability, V as BaseAddon, X as parseJsonUnknown, b as defaultDeviceFor$1, dt as EventCategory, k as nodePin, lt as string, m as YAMNET_TO_MACRO, st as object, t as APPLE_SA_TO_MACRO, tt as array, ut as union } from "../dist-
|
|
3
|
-
import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-
|
|
2
|
+
import { $ as sleep, A as pipelineExecutorCapability, B as errMsg, C as evaluateZoneRules, F as runtimeDevices$1, G as createEvent, K as hydrateSchema, R as supportedRuntimes$1, S as detectionPipelineCapability, V as BaseAddon, X as parseJsonUnknown, b as defaultDeviceFor$1, dt as EventCategory, k as nodePin, lt as string, m as YAMNET_TO_MACRO, st as object, t as APPLE_SA_TO_MACRO, tt as array, ut as union } from "../dist-CurVt3xC.mjs";
|
|
3
|
+
import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-BKm17nce.mjs";
|
|
4
4
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
5
5
|
import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
@@ -310,16 +310,16 @@ function projectNodeEngine(store, nodeId) {
|
|
|
310
310
|
/** The object-detection step id — the only slot this resolver applies to. */
|
|
311
311
|
var OBJECT_DETECTION_STEP_ID$1 = "object-detection";
|
|
312
312
|
/**
|
|
313
|
-
* Balanced default object-detection model per accelerator class
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
313
|
+
* Balanced default object-detection model per accelerator class. `'cpu'` maps to
|
|
314
|
+
* `null` → the caller substitutes the step's own `defaultModelId` (`yolo26n`).
|
|
315
|
+
* INTEL NPU + iGPU default to OUR YOLOv9 (int8 @320): yolo26 does NOT compile on
|
|
316
|
+
* the Intel NPU (arch incompat → 0 frames), while yolov9 runs on it (~298 fps
|
|
317
|
+
* measured). Apple ANE stays on yolo26 until the CoreML yolov9 build lands.
|
|
318
318
|
*/
|
|
319
319
|
var MODEL_BY_CLASS = {
|
|
320
|
-
"apple-ane": "
|
|
321
|
-
"intel-npu": "
|
|
322
|
-
"intel-gpu": "
|
|
320
|
+
"apple-ane": "yolov9m-320",
|
|
321
|
+
"intel-npu": "yolov9m-320-int8",
|
|
322
|
+
"intel-gpu": "yolov9m-320-int8",
|
|
323
323
|
cpu: null
|
|
324
324
|
};
|
|
325
325
|
/**
|
|
@@ -559,6 +559,7 @@ var PoolWorker = class {
|
|
|
559
559
|
concurrency: this.opts.concurrency,
|
|
560
560
|
models: initialModels.map((m) => serializeModelConfig(m))
|
|
561
561
|
};
|
|
562
|
+
if (this.opts.device) config["device"] = this.opts.device;
|
|
562
563
|
if (this.opts.tuning) {
|
|
563
564
|
config["batch_mode"] = this.opts.tuning.batchMode;
|
|
564
565
|
config["window_ms"] = this.opts.tuning.windowMs;
|
|
@@ -806,7 +807,9 @@ var SharedInferencePool = class {
|
|
|
806
807
|
this.concurrency = Math.max(1, options.concurrency ?? 1);
|
|
807
808
|
this.tuning = options.tuning ?? null;
|
|
808
809
|
this.numWorkers = Math.max(1, options.numWorkers ?? 1);
|
|
810
|
+
this.device = options.device;
|
|
809
811
|
}
|
|
812
|
+
device;
|
|
810
813
|
/** Pid of the first worker (for legacy callers). Use `getPids()` for all. */
|
|
811
814
|
getPid() {
|
|
812
815
|
return this.workers[0]?.getPid() ?? null;
|
|
@@ -828,7 +831,8 @@ var SharedInferencePool = class {
|
|
|
828
831
|
concurrency: this.concurrency,
|
|
829
832
|
tuning: this.tuning,
|
|
830
833
|
logger: this.log,
|
|
831
|
-
workerLabel: `w${i}
|
|
834
|
+
workerLabel: `w${i}`,
|
|
835
|
+
...this.device ? { device: this.device } : {}
|
|
832
836
|
}));
|
|
833
837
|
const t0 = performance.now();
|
|
834
838
|
const results = await Promise.all(this.workers.map((w) => w.initialize(initialModels)));
|
|
@@ -1487,7 +1491,8 @@ var EngineFactory = class {
|
|
|
1487
1491
|
this.pool = new SharedInferencePool(pythonPath, poolRuntime, this.log.child("pool"), {
|
|
1488
1492
|
concurrency,
|
|
1489
1493
|
tuning: resolvedTuning,
|
|
1490
|
-
numWorkers
|
|
1494
|
+
numWorkers,
|
|
1495
|
+
...this.opts.engine.device ? { device: this.opts.engine.device } : {}
|
|
1491
1496
|
});
|
|
1492
1497
|
this.poolManager = new PipelineModelManager(this.pool, (stepId, modelId) => this.buildPoolModelConfig(stepId, modelId, poolRuntime), this.log.child("model-mgr"));
|
|
1493
1498
|
await this.pool.initialize([]);
|
|
@@ -1535,6 +1540,39 @@ var EngineFactory = class {
|
|
|
1535
1540
|
}
|
|
1536
1541
|
};
|
|
1537
1542
|
//#endregion
|
|
1543
|
+
//#region src/detection-pipeline/engine/resolve-device-engine.ts
|
|
1544
|
+
/**
|
|
1545
|
+
* Backend → model format for a device pool. Mirrors the Phase-1 device
|
|
1546
|
+
* enumeration (`enumerateInferenceDevices`) and the pool-runtime mapping.
|
|
1547
|
+
*/
|
|
1548
|
+
var BACKEND_FORMAT = {
|
|
1549
|
+
openvino: "openvino",
|
|
1550
|
+
edgetpu: "tflite",
|
|
1551
|
+
onnx: "onnx",
|
|
1552
|
+
cpu: "onnx",
|
|
1553
|
+
cuda: "onnx",
|
|
1554
|
+
coreml: "coreml"
|
|
1555
|
+
};
|
|
1556
|
+
/**
|
|
1557
|
+
* Resolve a Phase-1 device key (`<backend>:<device>`, or bare `cpu`) into the
|
|
1558
|
+
* {@link PipelineEngineChoice} used to build that device's inference pool.
|
|
1559
|
+
* Throws on an unknown backend token so a bad `deviceKey` fails loud rather
|
|
1560
|
+
* than silently building a wrong pool.
|
|
1561
|
+
*/
|
|
1562
|
+
function resolveDeviceEngine(deviceKey) {
|
|
1563
|
+
const colon = deviceKey.indexOf(":");
|
|
1564
|
+
const backend = colon >= 0 ? deviceKey.slice(0, colon) : deviceKey;
|
|
1565
|
+
const device = colon >= 0 ? deviceKey.slice(colon + 1) : deviceKey;
|
|
1566
|
+
const format = BACKEND_FORMAT[backend];
|
|
1567
|
+
if (format === void 0) throw new Error(`resolveDeviceEngine: unknown backend "${backend}" in deviceKey "${deviceKey}"`);
|
|
1568
|
+
return {
|
|
1569
|
+
runtime: "python",
|
|
1570
|
+
backend,
|
|
1571
|
+
format,
|
|
1572
|
+
device
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
//#endregion
|
|
1538
1576
|
//#region src/detection-pipeline/engine-provisioner.ts
|
|
1539
1577
|
/** Incremental backoff growing to a ~5 min cap; retries indefinitely at cap. */
|
|
1540
1578
|
var BACKOFF_SCHEDULE_MS = [
|
|
@@ -3930,6 +3968,15 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
3930
3968
|
detectionConfigSchemaSource;
|
|
3931
3969
|
executorOptions;
|
|
3932
3970
|
engineFactory = null;
|
|
3971
|
+
/**
|
|
3972
|
+
* Per-device engine factories (Phase 2 multi-device), keyed by deviceKey
|
|
3973
|
+
* (`<backend>:<device>`, e.g. `openvino:npu`, `edgetpu:usb`). Each is its own
|
|
3974
|
+
* inference pool, warm CONCURRENTLY — so the node runs detection on NPU + iGPU
|
|
3975
|
+
* + Coral in parallel. The default (node current-engine) pool stays
|
|
3976
|
+
* `this.engineFactory`; device routing is a LOCAL per-dispatch choice that
|
|
3977
|
+
* never mutates shared state, so concurrent multi-device sessions don't race.
|
|
3978
|
+
*/
|
|
3979
|
+
factoriesByDevice = /* @__PURE__ */ new Map();
|
|
3933
3980
|
executor = null;
|
|
3934
3981
|
currentSteps = null;
|
|
3935
3982
|
currentEngine;
|
|
@@ -5048,8 +5095,12 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5048
5095
|
emit(`Image: ${imageWidth}×${imageHeight}`);
|
|
5049
5096
|
}
|
|
5050
5097
|
decodeMs = performance.now() - decodeT0;
|
|
5051
|
-
const
|
|
5052
|
-
const
|
|
5098
|
+
const deviceEngine = input.deviceKey ? resolveDeviceEngine(input.deviceKey) : void 0;
|
|
5099
|
+
const resolveFormat = deviceEngine?.format ?? input.engine?.format ?? this.currentEngine.format;
|
|
5100
|
+
const resolveEngine = deviceEngine ? {
|
|
5101
|
+
backend: deviceEngine.backend,
|
|
5102
|
+
device: deviceEngine.device ?? null
|
|
5103
|
+
} : input.engine ? {
|
|
5053
5104
|
backend: input.engine.backend,
|
|
5054
5105
|
device: input.engine.device ?? null
|
|
5055
5106
|
} : {
|
|
@@ -5150,7 +5201,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5150
5201
|
logger: this.log
|
|
5151
5202
|
});
|
|
5152
5203
|
this.executor = executor;
|
|
5153
|
-
const
|
|
5204
|
+
const dispatchEngine = input.deviceKey ? resolveDeviceEngine(input.deviceKey) : runEngine;
|
|
5205
|
+
const dispatchFactory = input.deviceKey ? await this.resolveDeviceFactory(input.deviceKey) : this.engineFactory;
|
|
5206
|
+
const needed = enabledSteps.filter((s) => !dispatchFactory.isLoadedWithModel(s.addonId, s.modelId));
|
|
5154
5207
|
if (needed.length > 0) {
|
|
5155
5208
|
this.log.info("Benchmark: models to load", { meta: {
|
|
5156
5209
|
count: needed.length,
|
|
@@ -5161,11 +5214,11 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5161
5214
|
addonId: s.addonId,
|
|
5162
5215
|
modelId: s.modelId
|
|
5163
5216
|
});
|
|
5164
|
-
await this.ensureModelsForSteps(benchmarkSteps);
|
|
5217
|
+
await this.ensureModelsForSteps(benchmarkSteps, dispatchFactory, dispatchEngine.format);
|
|
5165
5218
|
emit(`All models loaded`);
|
|
5166
5219
|
}
|
|
5167
5220
|
emit("Running inference...");
|
|
5168
|
-
const tree = buildExecutableTree(benchmarkSteps, (stepId) =>
|
|
5221
|
+
const tree = buildExecutableTree(benchmarkSteps, (stepId) => dispatchFactory.getEngine(stepId));
|
|
5169
5222
|
setupMs = performance.now() - wallT0 - decodeMs;
|
|
5170
5223
|
const isRuntime = Boolean(runtimeFrame);
|
|
5171
5224
|
const effectiveDeviceId = input.deviceId ?? 0;
|
|
@@ -5409,19 +5462,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5409
5462
|
* before any of them committed via `stepToLoaded.set`.
|
|
5410
5463
|
*/
|
|
5411
5464
|
modelLoadInFlight = null;
|
|
5412
|
-
/** Ensure all models needed by steps are downloaded and loaded in the engine pool.
|
|
5413
|
-
|
|
5465
|
+
/** Ensure all models needed by steps are downloaded and loaded in the engine pool.
|
|
5466
|
+
* `factory`/`format` default to the node's engine; a per-device dispatch passes
|
|
5467
|
+
* that device's factory + format (Phase 2 multi-device). */
|
|
5468
|
+
async ensureModelsForSteps(steps, factory = this.engineFactory, format = this.currentEngine?.format ?? "onnx") {
|
|
5414
5469
|
while (this.modelLoadInFlight) await this.modelLoadInFlight;
|
|
5415
5470
|
const allEnabled = flattenSteps(steps);
|
|
5416
5471
|
const needed = [];
|
|
5417
5472
|
for (const step of allEnabled) {
|
|
5418
5473
|
if (!step.enabled) continue;
|
|
5419
|
-
if (
|
|
5474
|
+
if (factory?.isLoadedWithModel(step.addonId, step.modelId)) continue;
|
|
5420
5475
|
needed.push(step);
|
|
5421
5476
|
}
|
|
5422
5477
|
if (needed.length === 0) return;
|
|
5423
5478
|
const work = (async () => {
|
|
5424
|
-
const format = this.currentEngine?.format ?? "onnx";
|
|
5425
5479
|
for (const step of needed) {
|
|
5426
5480
|
const modelEntry = await this.resolveModelEntry(step.addonId, step.modelId);
|
|
5427
5481
|
if (modelEntry && !isModelDownloaded(this.modelsDir, modelEntry, format)) {
|
|
@@ -5437,7 +5491,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5437
5491
|
count: needed.length,
|
|
5438
5492
|
models: needed.map((s) => `${s.addonId}/${s.modelId}`)
|
|
5439
5493
|
} });
|
|
5440
|
-
await
|
|
5494
|
+
await factory.loadAdditional(needed);
|
|
5441
5495
|
})();
|
|
5442
5496
|
this.modelLoadInFlight = work;
|
|
5443
5497
|
try {
|
|
@@ -5542,8 +5596,12 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5542
5596
|
}
|
|
5543
5597
|
async runPipelineBatchImpl(input) {
|
|
5544
5598
|
if (input.frames.length === 0) return { results: [] };
|
|
5545
|
-
const
|
|
5546
|
-
const
|
|
5599
|
+
const deviceEngine = input.deviceKey ? resolveDeviceEngine(input.deviceKey) : void 0;
|
|
5600
|
+
const resolveFormat = deviceEngine?.format ?? input.engine?.format ?? this.currentEngine.format;
|
|
5601
|
+
const resolveEngine = deviceEngine ? {
|
|
5602
|
+
backend: deviceEngine.backend,
|
|
5603
|
+
device: deviceEngine.device ?? null
|
|
5604
|
+
} : input.engine ? {
|
|
5547
5605
|
backend: input.engine.backend,
|
|
5548
5606
|
device: input.engine.device ?? null
|
|
5549
5607
|
} : {
|
|
@@ -5572,9 +5630,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5572
5630
|
await this.ensureEngineFactory();
|
|
5573
5631
|
const restoreEngine = await this.applyEngineOverride(input.engine);
|
|
5574
5632
|
try {
|
|
5575
|
-
const factory = this.engineFactory;
|
|
5633
|
+
const factory = input.deviceKey ? await this.resolveDeviceFactory(input.deviceKey) : this.engineFactory;
|
|
5576
5634
|
if (!factory) throw new Error("runPipelineBatch: factory not initialised");
|
|
5577
|
-
if (enabledSteps.filter((s) => !factory.isLoadedWithModel(s.addonId, s.modelId)).length > 0) await this.ensureModelsForSteps(benchmarkSteps);
|
|
5635
|
+
if (enabledSteps.filter((s) => !factory.isLoadedWithModel(s.addonId, s.modelId)).length > 0) await this.ensureModelsForSteps(benchmarkSteps, factory, resolveFormat);
|
|
5578
5636
|
const canFastPath = singleRoot && allRaw && uniformDims && factory.supportsBatch();
|
|
5579
5637
|
this.log.info("runPipelineBatch path decision", { meta: {
|
|
5580
5638
|
phase: "batch",
|
|
@@ -5588,7 +5646,8 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5588
5646
|
steps: input.steps,
|
|
5589
5647
|
frame,
|
|
5590
5648
|
deviceId: input.deviceId,
|
|
5591
|
-
sessionId: input.sessionId
|
|
5649
|
+
sessionId: input.sessionId,
|
|
5650
|
+
deviceKey: input.deviceKey
|
|
5592
5651
|
}))) };
|
|
5593
5652
|
const items = input.frames.map((frame) => ({
|
|
5594
5653
|
raw: Buffer.from(frame.data),
|
|
@@ -5722,6 +5781,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5722
5781
|
this.engineFactory = null;
|
|
5723
5782
|
this.executor = null;
|
|
5724
5783
|
}
|
|
5784
|
+
const deviceFactories = Array.from(this.factoriesByDevice.values());
|
|
5785
|
+
this.factoriesByDevice.clear();
|
|
5786
|
+
await Promise.all(deviceFactories.map((f) => f.dispose().catch(() => void 0)));
|
|
5725
5787
|
for (const id of Array.from(this.deviceProxies.keys())) this.releaseDeviceProxy(id);
|
|
5726
5788
|
}
|
|
5727
5789
|
/**
|
|
@@ -5841,6 +5903,58 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5841
5903
|
await this.engineFactory.initialize([]);
|
|
5842
5904
|
}
|
|
5843
5905
|
/**
|
|
5906
|
+
* Get (or lazily build) the inference-pool factory for an explicit device
|
|
5907
|
+
* (`<backend>:<device>`, e.g. `openvino:npu`, `edgetpu:usb`) — Phase 2
|
|
5908
|
+
* multi-device. Each device runs its OWN pool concurrently. Never mutates
|
|
5909
|
+
* `this.engineFactory`; when the requested device equals the node's current
|
|
5910
|
+
* engine the default pool is reused (no duplicate pool for the same device).
|
|
5911
|
+
*/
|
|
5912
|
+
/**
|
|
5913
|
+
* In-flight device-pool creations, keyed by deviceKey. Single-flights
|
|
5914
|
+
* `resolveDeviceFactory` so two concurrent calls for the SAME device never
|
|
5915
|
+
* both spawn a pool — critical for single-context accelerators like the Coral
|
|
5916
|
+
* Edge TPU, where two pools racing to `load_delegate` on the one USB unit both
|
|
5917
|
+
* fail (the loser can't claim the already-held device).
|
|
5918
|
+
*/
|
|
5919
|
+
deviceFactoryInflight = /* @__PURE__ */ new Map();
|
|
5920
|
+
async resolveDeviceFactory(deviceKey) {
|
|
5921
|
+
const engine = resolveDeviceEngine(deviceKey);
|
|
5922
|
+
if (enginesEqual(engine, this.currentEngine)) {
|
|
5923
|
+
await this.ensureEngineFactory();
|
|
5924
|
+
return this.engineFactory;
|
|
5925
|
+
}
|
|
5926
|
+
const existing = this.factoriesByDevice.get(deviceKey);
|
|
5927
|
+
if (existing) return existing;
|
|
5928
|
+
const inflight = this.deviceFactoryInflight.get(deviceKey);
|
|
5929
|
+
if (inflight) return inflight;
|
|
5930
|
+
const creation = (async () => {
|
|
5931
|
+
await this.ensureBackendDeps(engine);
|
|
5932
|
+
const factory = new EngineFactory({
|
|
5933
|
+
engine,
|
|
5934
|
+
modelsDir: this.modelsDir,
|
|
5935
|
+
logger: this.log.child(`engine:${deviceKey}`),
|
|
5936
|
+
pythonPath: this.executorOptions.pythonPath ?? "",
|
|
5937
|
+
concurrency: this.executorOptions.concurrency,
|
|
5938
|
+
tuning: this.executorOptions.tuning,
|
|
5939
|
+
numWorkers: this.executorOptions.numWorkers
|
|
5940
|
+
});
|
|
5941
|
+
try {
|
|
5942
|
+
await factory.initialize([]);
|
|
5943
|
+
} catch (err) {
|
|
5944
|
+
await factory.dispose().catch(() => void 0);
|
|
5945
|
+
throw err;
|
|
5946
|
+
}
|
|
5947
|
+
this.factoriesByDevice.set(deviceKey, factory);
|
|
5948
|
+
return factory;
|
|
5949
|
+
})();
|
|
5950
|
+
this.deviceFactoryInflight.set(deviceKey, creation);
|
|
5951
|
+
try {
|
|
5952
|
+
return await creation;
|
|
5953
|
+
} finally {
|
|
5954
|
+
this.deviceFactoryInflight.delete(deviceKey);
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
/**
|
|
5844
5958
|
* Phase 2b — resolve the engine from the addon's new schema-backed
|
|
5845
5959
|
* fields (`engineRuntime`, `engineBackend`, `engineDevice`). The legacy
|
|
5846
5960
|
* `pipelineEngine` JSON-blob fallback was removed with the executor seed
|
|
@@ -6031,6 +6145,20 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6031
6145
|
idleTtlMs: DetectionPipelineProvider.OVERRIDE_CACHE_TTL_MS
|
|
6032
6146
|
});
|
|
6033
6147
|
}
|
|
6148
|
+
for (const [deviceKey, factory] of this.factoriesByDevice) {
|
|
6149
|
+
const eng = resolveDeviceEngine(deviceKey);
|
|
6150
|
+
const loaded = factory.listLoaded();
|
|
6151
|
+
out.push({
|
|
6152
|
+
engineKey: `${eng.runtime}/${eng.backend}/${eng.device ?? "default"} [${deviceKey}]`,
|
|
6153
|
+
engine: eng,
|
|
6154
|
+
modelsLoaded: loaded.map((l) => `${l.stepId}/${l.modelId}`),
|
|
6155
|
+
inUseByCameras: [],
|
|
6156
|
+
kind: "device-pool",
|
|
6157
|
+
poolPid: factory.getPoolPid(),
|
|
6158
|
+
idleMs: null,
|
|
6159
|
+
idleTtlMs: null
|
|
6160
|
+
});
|
|
6161
|
+
}
|
|
6034
6162
|
return out;
|
|
6035
6163
|
}
|
|
6036
6164
|
async spinEngine(input) {
|