@camstack/addon-pipeline 1.2.615 → 1.2.616

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.
Files changed (38) hide show
  1. package/THIRD_PARTY_MODELS.md +2 -2
  2. package/dist/audio-analyzer/index.js +3 -3
  3. package/dist/audio-analyzer/index.mjs +3 -3
  4. package/dist/{custom-model-catalog-DFuM_m3D.js → custom-model-catalog-D0EZW4Xb.js} +3 -3
  5. package/dist/{custom-model-catalog-BmJXsT0k.mjs → custom-model-catalog-D3NopOC9.mjs} +3 -3
  6. package/dist/detection-pipeline/index.js +114 -6
  7. package/dist/detection-pipeline/index.mjs +114 -6
  8. package/dist/{dist-CfOv146z.mjs → dist-BWPVIuD_.mjs} +114 -61
  9. package/dist/{dist-B_1t8oSg.js → dist-D2aJhYm7.js} +114 -61
  10. package/dist/{model-catalogs-Dr-_ANpG.js → model-catalogs-BDJEmiKm.js} +14 -4
  11. package/dist/{model-catalogs-Dnj7Cr3q.mjs → model-catalogs-CYoIeMo4.mjs} +14 -4
  12. package/dist/motion-wasm/index.js +1 -1
  13. package/dist/motion-wasm/index.mjs +1 -1
  14. package/dist/{node-D82_5p1M.js → node-BKs5_GlD.js} +1 -1
  15. package/dist/{node-tn9-iwDj.mjs → node-D48F38hW.mjs} +1 -1
  16. package/dist/pipeline-runner/index.js +3 -3
  17. package/dist/pipeline-runner/index.mjs +3 -3
  18. package/dist/{process-memory-CD5f3ehN.mjs → process-memory-58pkBnTk.mjs} +1 -1
  19. package/dist/{process-memory-BlkUi_7x.js → process-memory-BY-1p6rF.js} +1 -1
  20. package/dist/recorder/index.js +80 -5
  21. package/dist/recorder/index.mjs +80 -5
  22. package/dist/{segment-demux-js-uWcwFqRL.js → segment-demux-js-DbM8w1dc.js} +1 -1
  23. package/dist/{segment-demux-js-Cx61fdp7.mjs → segment-demux-js-VeHvPIJe.mjs} +1 -1
  24. package/dist/stream-broker/_stub.js +2 -2
  25. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-dldjR7yB.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B6vm1OlX.mjs} +3 -3
  26. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-qcb2Czgh.mjs +26 -0
  27. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BvJiBUdN.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-TnEvW5tZ.mjs} +1 -1
  28. package/dist/stream-broker/demux-worker-child.js +1 -1
  29. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  30. package/dist/stream-broker/{hostInit-Bo0bh2FZ.mjs → hostInit-BnK9Yje5.mjs} +3 -3
  31. package/dist/stream-broker/index.js +38 -5
  32. package/dist/stream-broker/index.mjs +38 -5
  33. package/dist/stream-broker/remoteEntry.js +1 -1
  34. package/package.json +1 -1
  35. package/python/inference_pool.py +33 -0
  36. package/python/openvino_compile_probe.py +245 -0
  37. package/python/test_openvino_compile_probe.py +298 -0
  38. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D4kDfIKO.mjs +0 -26
@@ -123,8 +123,8 @@ entry kept resolvable for a saved selection but no longer offered.
123
123
  | --- | --- | --- | --- | --- | --- |
124
124
  | `yolo26n-seg`, `yolo26s-seg`, `yolo26m-seg` | [Ultralytics](https://github.com/ultralytics/ultralytics) | AGPL-3.0 / AGPL-3.0 (`AGPL-3.0.txt`) | COCO 2017: annotations CC-BY-4.0; images under their Flickr owners' terms | exported to ONNX, OpenVINO IR and CoreML | mirror `segmentation/yolo26-seg` |
125
125
  | `yolo26n` (default), `yolo26s`, `yolo26m`, `yolo26l`, `yolo26x` (hidden), `yolo26n-fp16` (hidden), `yolo26n-int8`, `yolo26s-fp16` (hidden), `yolo26s-int8`, `yolo26m-fp16` (hidden), `yolo26m-int8`, `yolo26l-fp16` (hidden), `yolo26l-int8`, `yolo26n-320`, `yolo26n-320-int8`, `yolo26n-256`, `yolo26n-256-int8`, `yolo26s-320`, `yolo26s-320-int8`, `yolo26s-256`, `yolo26s-256-int8`, `yolo26m-320`, `yolo26m-320-int8`, `yolo26m-256`, `yolo26m-256-int8`, `yolo26l-320`, `yolo26l-320-int8`, `yolo26l-256`, `yolo26l-256-int8`, `yolo26x-fp16` (hidden), `yolo26x-int8` (hidden) | [Ultralytics](https://github.com/ultralytics/ultralytics) | AGPL-3.0 / AGPL-3.0 (`AGPL-3.0.txt`) | COCO 2017: annotations CC-BY-4.0; images under their Flickr owners' terms | exported to ONNX, OpenVINO IR and CoreML (scripts/build-camstack-models.py); OpenVINO FP16 export; exported to OpenVINO IR, then OpenVINO NNCF INT8 post-training quantisation; re-exported at 320×320 to ONNX, OpenVINO IR and CoreML; re-exported at 320×320 to OpenVINO IR; OpenVINO NNCF INT8 post-training quantisation; re-exported at 256×256 to ONNX, OpenVINO IR and CoreML; re-exported at 256×256 to OpenVINO IR; OpenVINO NNCF INT8 post-training quantisation | mirror `objectDetection/yolo26` |
126
- | `ssd-mobilenet-v2-coco-edgetpu` (default) | [Google Coral test_data](https://github.com/google-coral/test_data) | Apache-2.0 / Apache-2.0 (`Apache-2.0.txt`) | COCO 2017: annotations CC-BY-4.0; images under their Flickr owners' terms | none | https://github.com/google-coral/test_data |
127
- | `yolov9s-320-edgetpu`, `yolov9s-512-edgetpu` | [YOLOv9-s EdgeTPU (Dan Brown)](https://github.com/dbro/frigate-detector-edgetpu-yolo9) | GPL-3.0 / GPL-3.0 (`GPL-3.0.txt`) | COCO 2017: annotations CC-BY-4.0; images under their Flickr owners' terms | none: the publisher release files are mirrored byte-identical | mirror `objectDetection/yolov9` |
126
+ | `ssd-mobilenet-v2-coco-edgetpu` | [Google Coral test_data](https://github.com/google-coral/test_data) | Apache-2.0 / Apache-2.0 (`Apache-2.0.txt`) | COCO 2017: annotations CC-BY-4.0; images under their Flickr owners' terms | none | https://github.com/google-coral/test_data |
127
+ | `yolov9s-320-edgetpu` (default), `yolov9s-512-edgetpu` | [YOLOv9-s EdgeTPU (Dan Brown)](https://github.com/dbro/frigate-detector-edgetpu-yolo9) | GPL-3.0 / GPL-3.0 (`GPL-3.0.txt`) | COCO 2017: annotations CC-BY-4.0; images under their Flickr owners' terms | none: the publisher release files are mirrored byte-identical | mirror `objectDetection/yolov9` |
128
128
  | `yolov9t-320`, `yolov9t-320-int8`, `yolov9t-640`, `yolov9t-640-int8`, `yolov9s-320`, `yolov9s-320-int8`, `yolov9s-640`, `yolov9s-640-int8`, `yolov9m-320`, `yolov9m-320-int8`, `yolov9m-640`, `yolov9m-640-int8`, `yolov9c-320`, `yolov9c-320-int8`, `yolov9c-640`, `yolov9c-640-int8` | [Ultralytics](https://github.com/ultralytics/ultralytics) | AGPL-3.0 / AGPL-3.0 (`AGPL-3.0.txt`) | COCO 2017: annotations CC-BY-4.0; images under their Flickr owners' terms | exported at 320×320 to ONNX, OpenVINO IR FP16 and CoreML; exported at 320×320 to OpenVINO IR; OpenVINO NNCF INT8 post-training quantisation; exported at 640×640 to ONNX, OpenVINO IR FP16 and CoreML; exported at 640×640 to OpenVINO IR; OpenVINO NNCF INT8 post-training quantisation | mirror `objectDetection/yolov9` |
129
129
 
130
130
  ### Package detection
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-B_1t8oSg.js");
7
- const require_model_catalogs = require("../model-catalogs-Dr-_ANpG.js");
8
- const require_process_memory = require("../process-memory-BlkUi_7x.js");
6
+ const require_dist = require("../dist-D2aJhYm7.js");
7
+ const require_model_catalogs = require("../model-catalogs-BDJEmiKm.js");
8
+ const require_process_memory = require("../process-memory-BY-1p6rF.js");
9
9
  let node_fs = require("node:fs");
10
10
  node_fs = require_chunk.__toESM(node_fs);
11
11
  let node_path = require("node:path");
@@ -1,7 +1,7 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $ as audioAnalyzerCapability, F as PoolMemoryWatchdog, Kn as hydrateSchema, Mn as errMsg, Pn as BaseAddon, Q as audioAnalysisCapability, Qn as nodePin, Rt as mapAudioLabelToMacro, Wn as expandAudioChunkToF32le, h as DEFAULT_AUDIO_ANALYZER_CONFIG, hr as EventCategory, ln as resolvePoolMemoryPolicy, r as AUDIO_BACKEND_CHOICES, zn as audioChunkBytesPerSample } from "../dist-CfOv146z.mjs";
3
- import { n as AUDIO_CLASSIFIER_MODELS } from "../model-catalogs-Dnj7Cr3q.mjs";
4
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-CD5f3ehN.mjs";
2
+ import { $ as audioAnalyzerCapability, F as PoolMemoryWatchdog, Kn as hydrateSchema, Mn as errMsg, Pn as BaseAddon, Q as audioAnalysisCapability, Qn as nodePin, Rt as mapAudioLabelToMacro, Wn as expandAudioChunkToF32le, h as DEFAULT_AUDIO_ANALYZER_CONFIG, hr as EventCategory, ln as resolvePoolMemoryPolicy, r as AUDIO_BACKEND_CHOICES, zn as audioChunkBytesPerSample } from "../dist-BWPVIuD_.mjs";
3
+ import { n as AUDIO_CLASSIFIER_MODELS } from "../model-catalogs-CYoIeMo4.mjs";
4
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-58pkBnTk.mjs";
5
5
  import * as fs from "node:fs";
6
6
  import * as path$1 from "node:path";
7
7
  import { ensureModel } from "@camstack/system/addon-utils";
@@ -1,5 +1,5 @@
1
- const require_dist = require("./dist-B_1t8oSg.js");
2
- const require_model_catalogs = require("./model-catalogs-Dr-_ANpG.js");
1
+ const require_dist = require("./dist-D2aJhYm7.js");
2
+ const require_model_catalogs = require("./model-catalogs-BDJEmiKm.js");
3
3
  const require_worker_protocol = require("./worker-protocol-C-G8qmye.js");
4
4
  let node_crypto = require("node:crypto");
5
5
  //#region src/detection-pipeline/registry/effective-confidence.ts
@@ -99,7 +99,7 @@ var ObjectDetectionStep = class {
99
99
  labelTier: 1,
100
100
  models: [...require_model_catalogs.OBJECT_DETECTION_MODELS],
101
101
  defaultModelId: "yolo26n",
102
- defaultModelIdByFormat: { tflite: "ssd-mobilenet-v2-coco-edgetpu" },
102
+ defaultModelIdByFormat: { tflite: "yolov9s-320-edgetpu" },
103
103
  defaultConfidence: .5,
104
104
  labels: require_dist.COCO_80_LABELS.map((l) => l.id),
105
105
  classMap: require_dist.COCO_TO_MACRO
@@ -1,5 +1,5 @@
1
- import { Mt as inferModelProvider, b as DEVICE_BACKEND_TO_FORMAT, et as audioKindId, f as COCO_80_LABELS, hn as stepModelRunsOnFormat, i as AUDIO_MACRO_LABELS, k as LABEL_SUPPLY_VOCABULARIES, p as COCO_TO_MACRO } from "./dist-CfOv146z.mjs";
2
- import { a as FACE_DETECTION_MODELS, c as OBJECT_DETECTION_MODELS, d as PLATE_OCR_MODELS, f as SEGMENTATION_REFINER_MODELS, i as CLIP_EMBEDDING_MODELS, l as PACKAGE_DETECTION_MODELS, n as AUDIO_CLASSIFIER_MODELS, o as FACE_EMBEDDING_MODELS, p as VEHICLE_CLASSIFIER_MODELS, r as BIRD_CLASSIFIER_MODELS, s as INSTANCE_SEGMENTATION_MODELS, t as ANIMAL_CLASSIFIER_MODELS, u as PLATE_DETECTION_MODELS } from "./model-catalogs-Dnj7Cr3q.mjs";
1
+ import { Mt as inferModelProvider, b as DEVICE_BACKEND_TO_FORMAT, et as audioKindId, f as COCO_80_LABELS, hn as stepModelRunsOnFormat, i as AUDIO_MACRO_LABELS, k as LABEL_SUPPLY_VOCABULARIES, p as COCO_TO_MACRO } from "./dist-BWPVIuD_.mjs";
2
+ import { a as FACE_DETECTION_MODELS, c as OBJECT_DETECTION_MODELS, d as PLATE_OCR_MODELS, f as SEGMENTATION_REFINER_MODELS, i as CLIP_EMBEDDING_MODELS, l as PACKAGE_DETECTION_MODELS, n as AUDIO_CLASSIFIER_MODELS, o as FACE_EMBEDDING_MODELS, p as VEHICLE_CLASSIFIER_MODELS, r as BIRD_CLASSIFIER_MODELS, s as INSTANCE_SEGMENTATION_MODELS, t as ANIMAL_CLASSIFIER_MODELS, u as PLATE_DETECTION_MODELS } from "./model-catalogs-CYoIeMo4.mjs";
3
3
  import { t as isWorkerReply } from "./worker-protocol-D_NzPcnh.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  //#region src/detection-pipeline/registry/effective-confidence.ts
@@ -99,7 +99,7 @@ var ObjectDetectionStep = class {
99
99
  labelTier: 1,
100
100
  models: [...OBJECT_DETECTION_MODELS],
101
101
  defaultModelId: "yolo26n",
102
- defaultModelIdByFormat: { tflite: "ssd-mobilenet-v2-coco-edgetpu" },
102
+ defaultModelIdByFormat: { tflite: "yolov9s-320-edgetpu" },
103
103
  defaultConfidence: .5,
104
104
  labels: COCO_80_LABELS.map((l) => l.id),
105
105
  classMap: COCO_TO_MACRO
@@ -3,11 +3,11 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-B_1t8oSg.js");
7
- const require_node = require("../node-D82_5p1M.js");
8
- const require_custom_model_catalog = require("../custom-model-catalog-DFuM_m3D.js");
6
+ const require_dist = require("../dist-D2aJhYm7.js");
7
+ const require_node = require("../node-BKs5_GlD.js");
8
+ const require_custom_model_catalog = require("../custom-model-catalog-D0EZW4Xb.js");
9
9
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
10
- const require_process_memory = require("../process-memory-BlkUi_7x.js");
10
+ const require_process_memory = require("../process-memory-BY-1p6rF.js");
11
11
  let node_os = require("node:os");
12
12
  node_os = require_chunk.__toESM(node_os);
13
13
  let node_fs = require("node:fs");
@@ -4164,6 +4164,72 @@ var IdlePoolReaper = class {
4164
4164
  }
4165
4165
  }
4166
4166
  };
4167
+ /** The key a benchmark's model loads are tracked under, apart from the live device's. */
4168
+ var BENCHMARK_KEY_PREFIX = "bench:";
4169
+ function benchmarkKeyOf(deviceKey) {
4170
+ return `${BENCHMARK_KEY_PREFIX}${deviceKey}`;
4171
+ }
4172
+ /** True for the one-shot UI benchmark call — see the file header for why the shape is enough. */
4173
+ function isBenchmarkRun(input) {
4174
+ return input.frame === void 0 && input.frameRef === void 0 && input.deviceId === void 0 && input.replay !== true;
4175
+ }
4176
+ var BenchmarkPools = class {
4177
+ options;
4178
+ pools = /* @__PURE__ */ new Map();
4179
+ inflight = /* @__PURE__ */ new Map();
4180
+ reaper;
4181
+ constructor(options) {
4182
+ this.options = options;
4183
+ this.reaper = new IdlePoolReaper({
4184
+ ttlMs: options.ttlMs ?? 3e5,
4185
+ dispose: (key) => this.disposeOne(key),
4186
+ onError: (key, err) => options.onDisposeError?.(key, err)
4187
+ });
4188
+ }
4189
+ /** The benchmark pool for `deviceKey`: warm if one is up, otherwise built (single-flight). */
4190
+ async acquire(deviceKey, engine) {
4191
+ const existing = this.pools.get(deviceKey);
4192
+ if (existing) {
4193
+ if (existing.isReady()) {
4194
+ this.reaper.touch(deviceKey);
4195
+ return existing;
4196
+ }
4197
+ this.pools.delete(deviceKey);
4198
+ this.reaper.cancel(deviceKey);
4199
+ await existing.dispose().catch(() => void 0);
4200
+ }
4201
+ const pending = this.inflight.get(deviceKey);
4202
+ if (pending) return pending;
4203
+ const creation = (async () => {
4204
+ const pool = await this.options.create(deviceKey, engine);
4205
+ this.pools.set(deviceKey, pool);
4206
+ this.reaper.touch(deviceKey);
4207
+ return pool;
4208
+ })();
4209
+ this.inflight.set(deviceKey, creation);
4210
+ try {
4211
+ return await creation;
4212
+ } finally {
4213
+ this.inflight.delete(deviceKey);
4214
+ }
4215
+ }
4216
+ /** The device keys that currently hold a benchmark pool. */
4217
+ deviceKeys() {
4218
+ return [...this.pools.keys()];
4219
+ }
4220
+ async disposeAll() {
4221
+ this.reaper.disposeAll();
4222
+ const pools = [...this.pools.values()];
4223
+ this.pools.clear();
4224
+ await Promise.all(pools.map((p) => p.dispose().catch(() => void 0)));
4225
+ }
4226
+ async disposeOne(deviceKey) {
4227
+ const pool = this.pools.get(deviceKey);
4228
+ if (!pool) return;
4229
+ this.pools.delete(deviceKey);
4230
+ await pool.dispose();
4231
+ }
4232
+ };
4167
4233
  //#endregion
4168
4234
  //#region src/detection-pipeline/engine/inference-device-liveness.ts
4169
4235
  /**
@@ -8266,6 +8332,18 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
8266
8332
  executorOptions;
8267
8333
  engineFactory = null;
8268
8334
  /**
8335
+ * The pools a benchmark call loads its models into (D910): one separate
8336
+ * worker per device, never `factoriesByDevice` and never the node-default
8337
+ * pool, so a compile that kills its worker cannot take live detection down.
8338
+ */
8339
+ benchmarkPools = new BenchmarkPools({
8340
+ create: (deviceKey, engine) => this.createBenchmarkFactory(deviceKey, engine),
8341
+ onDisposeError: (deviceKey, err) => this.log.warn("Idle benchmark pool dispose failed", { meta: {
8342
+ deviceKey,
8343
+ error: err instanceof Error ? err.message : String(err)
8344
+ } })
8345
+ });
8346
+ /**
8269
8347
  * Per-device engine factories (Phase 2 multi-device), keyed by deviceKey
8270
8348
  * (`<backend>:<device>`, e.g. `openvino:npu`, `edgetpu:usb`). Each is its own
8271
8349
  * inference pool, warm CONCURRENTLY — so the node runs detection on NPU + iGPU
@@ -9712,12 +9790,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9712
9790
  });
9713
9791
  this.executor = executor;
9714
9792
  const dispatchEngine = dispatchDeviceKey ? require_custom_model_catalog.resolveDeviceEngine(dispatchDeviceKey) : runEngine;
9715
- const dispatchFactory = dispatchDeviceKey ? await this.resolveDeviceFactory(dispatchDeviceKey) : this.engineFactory;
9793
+ const benchmark = isBenchmarkRun(input);
9794
+ const dispatchFactory = benchmark ? await this.benchmarkPools.acquire(require_custom_model_catalog.deviceKeyOf(dispatchEngine), dispatchEngine) : dispatchDeviceKey ? await this.resolveDeviceFactory(dispatchDeviceKey) : this.engineFactory;
9716
9795
  const needed = enabledSteps.filter((s) => dispatchFactory.needsPoolUpdate(s));
9717
9796
  let runSteps = benchmarkSteps;
9718
9797
  const loadPolicy = dispatchLoadPolicyOf(input);
9719
9798
  if (needed.length > 0) {
9720
- const dispatchDeviceKeyLabel = require_custom_model_catalog.deviceKeyOf(dispatchEngine);
9799
+ const dispatchDeviceKeyLabel = benchmark ? benchmarkKeyOf(require_custom_model_catalog.deviceKeyOf(dispatchEngine)) : require_custom_model_catalog.deviceKeyOf(dispatchEngine);
9721
9800
  const toIssue = needed.filter((s) => this.loadGovernor.check(dispatchFactory, dispatchDeviceKeyLabel, [stepModelKey(s)]).kind === "go");
9722
9801
  if (toIssue.length > 0) this.log.info("Benchmark: models to load", { meta: {
9723
9802
  count: toIssue.length,
@@ -10583,6 +10662,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10583
10662
  this.engineFactory = null;
10584
10663
  this.executor = null;
10585
10664
  }
10665
+ await this.benchmarkPools.disposeAll();
10586
10666
  this.deviceReaper.disposeAll();
10587
10667
  const deviceFactories = Array.from(this.factoriesByDevice.values());
10588
10668
  this.factoriesByDevice.clear();
@@ -10919,6 +10999,34 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10919
10999
  } });
10920
11000
  return { rearmed };
10921
11001
  }
11002
+ /**
11003
+ * Build the pool a benchmark loads into: a fresh worker for the engine of
11004
+ * `deviceKey`, outside the live pools' liveness budget and late-compile
11005
+ * bookkeeping (a benchmark's failure is a verdict about the benchmark).
11006
+ */
11007
+ async createBenchmarkFactory(deviceKey, engine) {
11008
+ await this.ensureBackendDeps(engine);
11009
+ const factory = new EngineFactory({
11010
+ engine,
11011
+ modelsDir: this.modelsDir,
11012
+ logger: this.log.child(`bench:${deviceKey}`),
11013
+ pythonPath: this.executorOptions.pythonPath ?? "",
11014
+ provisioning: this.executorOptions.provisioning,
11015
+ resolveCustomModel: this.customModelResolver
11016
+ });
11017
+ try {
11018
+ await factory.initialize([]);
11019
+ } catch (err) {
11020
+ await factory.dispose().catch(() => void 0);
11021
+ this.log.error("Benchmark pool failed to initialize", { meta: {
11022
+ deviceKey,
11023
+ error: require_dist.errMsg(err)
11024
+ } });
11025
+ throw err;
11026
+ }
11027
+ this.log.info("Benchmark pool started — its loads stay out of the live pool", { meta: { deviceKey } });
11028
+ return factory;
11029
+ }
10922
11030
  async resolveDeviceFactory(deviceKey) {
10923
11031
  const engine = require_custom_model_catalog.resolveDeviceEngine(deviceKey);
10924
11032
  if (enginesEqual(engine, this.currentEngine)) {
@@ -1,9 +1,9 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { Bn as createEvent, Ct as evaluateZoneRules, F as PoolMemoryWatchdog, Gn as hfModelUrl, It as loadContributionCapability, Jt as pickClusterStepSettings, Kn as hydrateSchema, Kt as overlayClusterStepSettings, Mn as errMsg$1, O as LABEL_SUPPLY_MACRO, Pn as BaseAddon, Pt as linkedLabelStepId, Qn as nodePin, St as enumerateInferenceDevices, Wt as modelBindingKey, X as applyModelBoundSettings, Zt as pipelineExecutorCapability, _t as detectionPipelineCapability, bn as supportedRuntimes$1, ct as containingParentIndex, d as CLUSTER_MODEL_SCOPED_STEPS, fn as rootSuppliesOf, ft as defaultDeviceFor$1, g as DEFAULT_CLUSTER_STEP_SETTINGS, hn as stepModelRunsOnFormat, hr as EventCategory, in as resolveClusterStepModelId, ir as array, ln as resolvePoolMemoryPolicy, mr as union, n as APPLE_SA_TO_MACRO, pn as runtimeDevices$1, pr as string, q as YAMNET_TO_MACRO, st as collectModelBoundKeys, tr as sleep, ur as object, yt as edgetpuDelegateDevice } from "../dist-CfOv146z.mjs";
3
- import { c as readProcessCost } from "../node-tn9-iwDj.mjs";
4
- import { C as resolveModelDefaultConfidence, S as landmarkPrecisionVerdict, _ as ALL_STEPS, b as getStepDefinition, f as localFrameRegistry, g as ALL_PIPELINE_STEPS, l as resolveDefaultDetectionModel, n as mergeCustomModels, o as deviceKeyOf, r as buildSubtreeCanRun, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, v as getDefaultModelForFormat, x as resolveModelForFormat, y as getStep } from "../custom-model-catalog-BmJXsT0k.mjs";
2
+ import { Bn as createEvent, Ct as evaluateZoneRules, F as PoolMemoryWatchdog, Gn as hfModelUrl, It as loadContributionCapability, Jt as pickClusterStepSettings, Kn as hydrateSchema, Kt as overlayClusterStepSettings, Mn as errMsg$1, O as LABEL_SUPPLY_MACRO, Pn as BaseAddon, Pt as linkedLabelStepId, Qn as nodePin, St as enumerateInferenceDevices, Wt as modelBindingKey, X as applyModelBoundSettings, Zt as pipelineExecutorCapability, _t as detectionPipelineCapability, bn as supportedRuntimes$1, ct as containingParentIndex, d as CLUSTER_MODEL_SCOPED_STEPS, fn as rootSuppliesOf, ft as defaultDeviceFor$1, g as DEFAULT_CLUSTER_STEP_SETTINGS, hn as stepModelRunsOnFormat, hr as EventCategory, in as resolveClusterStepModelId, ir as array, ln as resolvePoolMemoryPolicy, mr as union, n as APPLE_SA_TO_MACRO, pn as runtimeDevices$1, pr as string, q as YAMNET_TO_MACRO, st as collectModelBoundKeys, tr as sleep, ur as object, yt as edgetpuDelegateDevice } from "../dist-BWPVIuD_.mjs";
3
+ import { c as readProcessCost } from "../node-D48F38hW.mjs";
4
+ import { C as resolveModelDefaultConfidence, S as landmarkPrecisionVerdict, _ as ALL_STEPS, b as getStepDefinition, f as localFrameRegistry, g as ALL_PIPELINE_STEPS, l as resolveDefaultDetectionModel, n as mergeCustomModels, o as deviceKeyOf, r as buildSubtreeCanRun, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, v as getDefaultModelForFormat, x as resolveModelForFormat, y as getStep } from "../custom-model-catalog-D3NopOC9.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-CD5f3ehN.mjs";
6
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-58pkBnTk.mjs";
7
7
  import * as os$1 from "node:os";
8
8
  import * as fs from "node:fs";
9
9
  import * as path$1 from "node:path";
@@ -4157,6 +4157,72 @@ var IdlePoolReaper = class {
4157
4157
  }
4158
4158
  }
4159
4159
  };
4160
+ /** The key a benchmark's model loads are tracked under, apart from the live device's. */
4161
+ var BENCHMARK_KEY_PREFIX = "bench:";
4162
+ function benchmarkKeyOf(deviceKey) {
4163
+ return `${BENCHMARK_KEY_PREFIX}${deviceKey}`;
4164
+ }
4165
+ /** True for the one-shot UI benchmark call — see the file header for why the shape is enough. */
4166
+ function isBenchmarkRun(input) {
4167
+ return input.frame === void 0 && input.frameRef === void 0 && input.deviceId === void 0 && input.replay !== true;
4168
+ }
4169
+ var BenchmarkPools = class {
4170
+ options;
4171
+ pools = /* @__PURE__ */ new Map();
4172
+ inflight = /* @__PURE__ */ new Map();
4173
+ reaper;
4174
+ constructor(options) {
4175
+ this.options = options;
4176
+ this.reaper = new IdlePoolReaper({
4177
+ ttlMs: options.ttlMs ?? 3e5,
4178
+ dispose: (key) => this.disposeOne(key),
4179
+ onError: (key, err) => options.onDisposeError?.(key, err)
4180
+ });
4181
+ }
4182
+ /** The benchmark pool for `deviceKey`: warm if one is up, otherwise built (single-flight). */
4183
+ async acquire(deviceKey, engine) {
4184
+ const existing = this.pools.get(deviceKey);
4185
+ if (existing) {
4186
+ if (existing.isReady()) {
4187
+ this.reaper.touch(deviceKey);
4188
+ return existing;
4189
+ }
4190
+ this.pools.delete(deviceKey);
4191
+ this.reaper.cancel(deviceKey);
4192
+ await existing.dispose().catch(() => void 0);
4193
+ }
4194
+ const pending = this.inflight.get(deviceKey);
4195
+ if (pending) return pending;
4196
+ const creation = (async () => {
4197
+ const pool = await this.options.create(deviceKey, engine);
4198
+ this.pools.set(deviceKey, pool);
4199
+ this.reaper.touch(deviceKey);
4200
+ return pool;
4201
+ })();
4202
+ this.inflight.set(deviceKey, creation);
4203
+ try {
4204
+ return await creation;
4205
+ } finally {
4206
+ this.inflight.delete(deviceKey);
4207
+ }
4208
+ }
4209
+ /** The device keys that currently hold a benchmark pool. */
4210
+ deviceKeys() {
4211
+ return [...this.pools.keys()];
4212
+ }
4213
+ async disposeAll() {
4214
+ this.reaper.disposeAll();
4215
+ const pools = [...this.pools.values()];
4216
+ this.pools.clear();
4217
+ await Promise.all(pools.map((p) => p.dispose().catch(() => void 0)));
4218
+ }
4219
+ async disposeOne(deviceKey) {
4220
+ const pool = this.pools.get(deviceKey);
4221
+ if (!pool) return;
4222
+ this.pools.delete(deviceKey);
4223
+ await pool.dispose();
4224
+ }
4225
+ };
4160
4226
  //#endregion
4161
4227
  //#region src/detection-pipeline/engine/inference-device-liveness.ts
4162
4228
  /**
@@ -8259,6 +8325,18 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
8259
8325
  executorOptions;
8260
8326
  engineFactory = null;
8261
8327
  /**
8328
+ * The pools a benchmark call loads its models into (D910): one separate
8329
+ * worker per device, never `factoriesByDevice` and never the node-default
8330
+ * pool, so a compile that kills its worker cannot take live detection down.
8331
+ */
8332
+ benchmarkPools = new BenchmarkPools({
8333
+ create: (deviceKey, engine) => this.createBenchmarkFactory(deviceKey, engine),
8334
+ onDisposeError: (deviceKey, err) => this.log.warn("Idle benchmark pool dispose failed", { meta: {
8335
+ deviceKey,
8336
+ error: err instanceof Error ? err.message : String(err)
8337
+ } })
8338
+ });
8339
+ /**
8262
8340
  * Per-device engine factories (Phase 2 multi-device), keyed by deviceKey
8263
8341
  * (`<backend>:<device>`, e.g. `openvino:npu`, `edgetpu:usb`). Each is its own
8264
8342
  * inference pool, warm CONCURRENTLY — so the node runs detection on NPU + iGPU
@@ -9705,12 +9783,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9705
9783
  });
9706
9784
  this.executor = executor;
9707
9785
  const dispatchEngine = dispatchDeviceKey ? resolveDeviceEngine(dispatchDeviceKey) : runEngine;
9708
- const dispatchFactory = dispatchDeviceKey ? await this.resolveDeviceFactory(dispatchDeviceKey) : this.engineFactory;
9786
+ const benchmark = isBenchmarkRun(input);
9787
+ const dispatchFactory = benchmark ? await this.benchmarkPools.acquire(deviceKeyOf(dispatchEngine), dispatchEngine) : dispatchDeviceKey ? await this.resolveDeviceFactory(dispatchDeviceKey) : this.engineFactory;
9709
9788
  const needed = enabledSteps.filter((s) => dispatchFactory.needsPoolUpdate(s));
9710
9789
  let runSteps = benchmarkSteps;
9711
9790
  const loadPolicy = dispatchLoadPolicyOf(input);
9712
9791
  if (needed.length > 0) {
9713
- const dispatchDeviceKeyLabel = deviceKeyOf(dispatchEngine);
9792
+ const dispatchDeviceKeyLabel = benchmark ? benchmarkKeyOf(deviceKeyOf(dispatchEngine)) : deviceKeyOf(dispatchEngine);
9714
9793
  const toIssue = needed.filter((s) => this.loadGovernor.check(dispatchFactory, dispatchDeviceKeyLabel, [stepModelKey(s)]).kind === "go");
9715
9794
  if (toIssue.length > 0) this.log.info("Benchmark: models to load", { meta: {
9716
9795
  count: toIssue.length,
@@ -10576,6 +10655,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10576
10655
  this.engineFactory = null;
10577
10656
  this.executor = null;
10578
10657
  }
10658
+ await this.benchmarkPools.disposeAll();
10579
10659
  this.deviceReaper.disposeAll();
10580
10660
  const deviceFactories = Array.from(this.factoriesByDevice.values());
10581
10661
  this.factoriesByDevice.clear();
@@ -10912,6 +10992,34 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10912
10992
  } });
10913
10993
  return { rearmed };
10914
10994
  }
10995
+ /**
10996
+ * Build the pool a benchmark loads into: a fresh worker for the engine of
10997
+ * `deviceKey`, outside the live pools' liveness budget and late-compile
10998
+ * bookkeeping (a benchmark's failure is a verdict about the benchmark).
10999
+ */
11000
+ async createBenchmarkFactory(deviceKey, engine) {
11001
+ await this.ensureBackendDeps(engine);
11002
+ const factory = new EngineFactory({
11003
+ engine,
11004
+ modelsDir: this.modelsDir,
11005
+ logger: this.log.child(`bench:${deviceKey}`),
11006
+ pythonPath: this.executorOptions.pythonPath ?? "",
11007
+ provisioning: this.executorOptions.provisioning,
11008
+ resolveCustomModel: this.customModelResolver
11009
+ });
11010
+ try {
11011
+ await factory.initialize([]);
11012
+ } catch (err) {
11013
+ await factory.dispose().catch(() => void 0);
11014
+ this.log.error("Benchmark pool failed to initialize", { meta: {
11015
+ deviceKey,
11016
+ error: errMsg$1(err)
11017
+ } });
11018
+ throw err;
11019
+ }
11020
+ this.log.info("Benchmark pool started — its loads stay out of the live pool", { meta: { deviceKey } });
11021
+ return factory;
11022
+ }
10915
11023
  async resolveDeviceFactory(deviceKey) {
10916
11024
  const engine = resolveDeviceEngine(deviceKey);
10917
11025
  if (enginesEqual(engine, this.currentEngine)) {