@camstack/addon-pipeline 1.2.69 → 1.2.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.
Files changed (28) hide show
  1. package/dist/audio-analyzer/index.js +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/detection-pipeline/index.js +32 -30
  4. package/dist/detection-pipeline/index.mjs +32 -30
  5. package/dist/{dist-DGLqaonv.mjs → dist-BPaa4_z6.mjs} +508 -8
  6. package/dist/{dist-tJF2BzWi.js → dist-cR-pvD_z.js} +508 -8
  7. package/dist/{event-loop-stall-monitor-C6_VDupx.mjs → event-loop-stall-monitor-B79REtL2.mjs} +1 -1
  8. package/dist/{event-loop-stall-monitor-D9d2e49f.js → event-loop-stall-monitor-STRdnQWm.js} +1 -1
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/pipeline-runner/index.js +8 -5
  12. package/dist/pipeline-runner/index.mjs +8 -5
  13. package/dist/recorder/index.js +370 -32
  14. package/dist/recorder/index.mjs +370 -32
  15. package/dist/session-decode/decode-worker-child.js +1 -1
  16. package/dist/session-decode/decode-worker-child.mjs +1 -1
  17. package/dist/stream-broker/_stub.js +2 -2
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DCyQ7RKH.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B-HGBNad.mjs} +3 -3
  19. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D1CqbPrT.mjs +26 -0
  20. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DU7u9EJt.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-NjvjJWz-.mjs} +1 -1
  21. package/dist/stream-broker/{hostInit-DxqtIjAe.mjs → hostInit-B12DHOv7.mjs} +3 -3
  22. package/dist/stream-broker/index.js +501 -32
  23. package/dist/stream-broker/index.mjs +501 -32
  24. package/dist/stream-broker/remoteEntry.js +1 -1
  25. package/dist/{worker-protocol-D6tXz9Or.js → worker-protocol-C7V1qIIA.js} +1 -1
  26. package/dist/{worker-protocol-DuftUFEI.mjs → worker-protocol-CBO8nwQj.mjs} +1 -1
  27. package/package.json +1 -1
  28. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-ircnzt0s.mjs +0 -26
@@ -3,7 +3,7 @@ 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-tJF2BzWi.js");
6
+ const require_dist = require("../dist-cR-pvD_z.js");
7
7
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
8
8
  const require_addon_utils = require("../addon-utils-CQs-AjTJ.js");
9
9
  let node_fs = require("node:fs");
@@ -1,5 +1,5 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { B as mapAudioLabelToMacro, E as audioAnalyzerCapability, T as audioAnalysisCapability, ft as hydrateSchema, m as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, st as BaseAddon, tt as errMsg } from "../dist-DGLqaonv.mjs";
2
+ import { B as mapAudioLabelToMacro, E as audioAnalyzerCapability, T as audioAnalysisCapability, ft as hydrateSchema, m as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, st as BaseAddon, tt as errMsg } from "../dist-BPaa4_z6.mjs";
3
3
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
4
4
  import { i as downloadFile } from "../addon-utils-CZ2xo67g.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-emK7D4bc.js");
6
- const require_dist = require("../dist-tJF2BzWi.js");
7
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-D9d2e49f.js");
6
+ const require_dist = require("../dist-cR-pvD_z.js");
7
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-STRdnQWm.js");
8
8
  const require_lazy_sharp = require("../lazy-sharp-CcYuXtsa.js");
9
9
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
10
10
  const require_addon_utils = require("../addon-utils-CQs-AjTJ.js");
@@ -3505,17 +3505,6 @@ var NATIVE_FACE_CROP_MAX_WIDTH = 320;
3505
3505
  * would read, so the same floor applies honestly in both cases.
3506
3506
  */
3507
3507
  var MIN_PLATE_OCR_SHORT_SIDE_PX = 20;
3508
- /**
3509
- * StepOutput kinds that synthesize a full-frame detection when they run at
3510
- * the tree ROOT (see `synthesizeRootDetection`) — classifier/embedding/OCR
3511
- * steps promoted to root by `pipelineRunner.runDetailSubtree` (task 6b), e.g.
3512
- * `clip-embedding`, `animal-classifier`, `plate-ocr`. Detector (`'detections'`)
3513
- * roots take the normal path. `'text'` is included so a `plate-ocr` step run
3514
- * standalone on an already-cut plate crop (parent class `plate`) synthesizes a
3515
- * full-frame detection carrying the recognized text as a label, instead of
3516
- * silently dropping — the OCR chain otherwise only surfaces text when nested
3517
- * under `plate-detection`. `'mask'` roots remain out of scope.
3518
- */
3519
3508
  function isEnrichmentOutput(output) {
3520
3509
  return output.kind === "classifications" || output.kind === "embedding" || output.kind === "text";
3521
3510
  }
@@ -3800,12 +3789,13 @@ var PipelineExecutor = class {
3800
3789
  * falling back to `inputClasses[0]` when `outputClasses` is empty, then
3801
3790
  * the step id as a last resort so the field is never empty). `score` is
3802
3791
  * the output's own confidence when it has one (the classifier's top-1
3803
- * score) — an embedding carries no confidence, so it defaults to 1.
3792
+ * score, an OCR read's `confidence`) — an embedding carries no
3793
+ * confidence, so it defaults to 1.
3804
3794
  */
3805
3795
  synthesizeRootDetection(output, rootStep, idGen, rootStepLatencyMs, imageWidth, imageHeight) {
3806
3796
  return toMutableRootDetection({
3807
3797
  class: rootStep.definition.outputClasses[0] ?? rootStep.definition.inputClasses?.[0] ?? rootStep.stepId,
3808
- score: output.kind === "classifications" ? output.classifications[0]?.score ?? 1 : 1,
3798
+ score: output.kind === "classifications" ? output.classifications[0]?.score ?? 1 : output.kind === "text" ? output.confidence : 1,
3809
3799
  bbox: [
3810
3800
  0,
3811
3801
  0,
@@ -5687,7 +5677,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5687
5677
  bbox: roi,
5688
5678
  ...maxWidth !== void 0 ? { maxWidth } : {}
5689
5679
  }, require_dist.nodePin(handle.nodeId));
5690
- if (!native || native.width <= 0 || native.height <= 0) return null;
5680
+ if (!native || !native.bytes || native.width <= 0 || native.height <= 0) return null;
5691
5681
  return {
5692
5682
  bytes: native.bytes,
5693
5683
  width: native.width,
@@ -7605,7 +7595,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7605
7595
  type: "select",
7606
7596
  key: "batchMode",
7607
7597
  label: "Batch dispatch mode",
7608
- description: "How the Python pool dispatches predicts. `none` = one call per frame. `window` = per-model accumulator coalesces concurrent calls within Window (ms). Auto uses the matrix-sweep default for the current backend.",
7598
+ description: "How the Python pool dispatches predicts. `none` = one call per frame. `window` = per-model accumulator coalesces concurrent calls within Window (ms). Auto uses the matrix-sweep default for the current backend. Applies to THIS node only.",
7609
7599
  options: [
7610
7600
  {
7611
7601
  value: "",
@@ -7624,13 +7614,14 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7624
7614
  label: "Window (cross-camera accumulator)"
7625
7615
  }
7626
7616
  ],
7627
- default: ""
7617
+ default: "",
7618
+ perNode: true
7628
7619
  }),
7629
7620
  this.field({
7630
7621
  type: "slider",
7631
7622
  key: "windowMs",
7632
7623
  label: "Window flush (ms)",
7633
- description: "Window-mode accumulator: max time to wait for additional concurrent predicts before flushing. Lower = lower latency, higher = better throughput. Auto = backend default.",
7624
+ description: "Window-mode accumulator: max time to wait for additional concurrent predicts before flushing. Lower = lower latency, higher = better throughput. Auto = backend default. Applies to THIS node only.",
7634
7625
  min: 0,
7635
7626
  max: 50,
7636
7627
  step: 1,
@@ -7638,6 +7629,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7638
7629
  showValue: true,
7639
7630
  nullable: true,
7640
7631
  nullLabel: "Auto",
7632
+ perNode: true,
7641
7633
  showWhen: {
7642
7634
  field: "batchMode",
7643
7635
  notEquals: "none"
@@ -7647,7 +7639,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7647
7639
  type: "slider",
7648
7640
  key: "maxBatchSize",
7649
7641
  label: "Max batch size",
7650
- description: "Cap on items per batched predict call. Auto = backend default.",
7642
+ description: "Cap on items per batched predict call. Auto = backend default. Applies to THIS node only.",
7651
7643
  min: 1,
7652
7644
  max: 16,
7653
7645
  step: 1,
@@ -7655,6 +7647,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7655
7647
  showValue: true,
7656
7648
  nullable: true,
7657
7649
  nullLabel: "Auto",
7650
+ perNode: true,
7658
7651
  showWhen: {
7659
7652
  field: "batchMode",
7660
7653
  notEquals: "none"
@@ -7663,28 +7656,30 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7663
7656
  this.field({
7664
7657
  type: "slider",
7665
7658
  key: "concurrency",
7666
- label: "Pool workers",
7667
- description: "Python inference-pool prepare + predict threads. Auto = backend default (CoreML: 2, OpenVINO: 1, ONNX: 4).",
7659
+ label: "Pool threads",
7660
+ description: "Python inference-pool prepare + predict THREADS inside one pool subprocess — they share one model context. Auto = backend default (CoreML: 1, OpenVINO: 1, ONNX: 4). Raising this does not help CoreML (it serialises per model context) — use Pool subprocesses instead. Applies to THIS node only.",
7668
7661
  min: 1,
7669
7662
  max: 8,
7670
7663
  step: 1,
7671
7664
  default: void 0,
7672
7665
  showValue: true,
7673
7666
  nullable: true,
7674
- nullLabel: "Auto"
7667
+ nullLabel: "Auto",
7668
+ perNode: true
7675
7669
  }),
7676
7670
  this.field({
7677
7671
  type: "slider",
7678
7672
  key: "numWorkers",
7679
7673
  label: "Pool subprocesses",
7680
- description: "Number of independent Python pool subprocesses. Each holds its own MLModel copy + ANE/GPU/CPU dispatch context. Inference round-robins across them. RAM cost scales linearly. Auto = 2 for CoreML (lifts the single-context ANE cap on multi-camera workloads), 1 for ONNX/OpenVINO (they manage their own internal parallelism).",
7674
+ description: "Number of independent Python pool subprocesses. Each holds its own MLModel copy + ANE/GPU/CPU dispatch context, and its OWN in-flight cap — inference round-robins across them, so this is the knob that lifts a saturated single-context pool. RAM cost scales linearly with subprocesses × model size. Auto = 1 on every runtime. Applies to THIS node only.",
7681
7675
  min: 1,
7682
7676
  max: 8,
7683
7677
  step: 1,
7684
7678
  default: void 0,
7685
7679
  showValue: true,
7686
7680
  nullable: true,
7687
- nullLabel: "Auto"
7681
+ nullLabel: "Auto",
7682
+ perNode: true
7688
7683
  })
7689
7684
  ]
7690
7685
  }] });
@@ -7704,7 +7699,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7704
7699
  */
7705
7700
  async getGlobalSettings(overlay, _cap, nodeId) {
7706
7701
  const targetNode = nodeId ? normalizeEngineNodeId(nodeId) : this.localNodeId();
7707
- const rawStored = await this.resolveUiSettingsStore();
7702
+ const rawStored = await this.resolveUiSettingsStore(targetNode);
7708
7703
  if (rawStored === null) return null;
7709
7704
  const merged = overlay ? {
7710
7705
  ...rawStored,
@@ -7765,11 +7760,18 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
7765
7760
  * uses the null signal to avoid serving fabricated empty-store defaults
7766
7761
  * during the restart window. Seam kept protected so tests can drive the
7767
7762
  * unreadable vs empty vs populated cases without faking a full AddonContext.
7763
+ *
7764
+ * The store is PROJECTED onto `nodeId` (the node the UI is asking about, not
7765
+ * necessarily this instance — the hub addon answers for every node): every
7766
+ * `perNode: true` field of the Inference-executor section hydrates from that
7767
+ * node's `<key>@<nodeId>` value, and `<key>@<other-node>` never leaks onto the
7768
+ * bare field. `resolveGlobalStore` is the base's sanctioned seam for exactly
7769
+ * this — reading `ctx.settings.readAddonStore()` raw here would show the hub's
7770
+ * numbers on every node's form.
7768
7771
  */
7769
- async resolveUiSettingsStore() {
7770
- const settings = this.ctxIfReady?.settings;
7771
- if (!settings) return null;
7772
- return await settings.readAddonStore() ?? {};
7772
+ async resolveUiSettingsStore(nodeId) {
7773
+ if (!this.ctxIfReady?.settings) return null;
7774
+ return this.resolveGlobalStore(nodeId);
7773
7775
  }
7774
7776
  async probeHardwareEnv(nodeId) {
7775
7777
  const node = nodeId ?? this.localNodeId();
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $ as supportedRuntimes$1, A as defaultDeviceFor$1, At as string, C as YAMNET_TO_MACRO, G as pipelineExecutorCapability, L as enumerateInferenceDevices, Mt as EventCategory, Ot as object, P as detectionPipelineCapability, R as evaluateZoneRules, St as array, X as runtimeDevices$1, _t as parseJsonUnknown, bt as sleep, dt as createEvent, ft as hydrateSchema, gt as nodePin, jt as union, st as BaseAddon, t as APPLE_SA_TO_MACRO, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT } from "../dist-DGLqaonv.mjs";
3
- import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, n as startEventLoopStallMonitor, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep } from "../event-loop-stall-monitor-C6_VDupx.mjs";
2
+ import { $ as supportedRuntimes$1, A as defaultDeviceFor$1, At as string, C as YAMNET_TO_MACRO, G as pipelineExecutorCapability, L as enumerateInferenceDevices, Mt as EventCategory, Ot as object, P as detectionPipelineCapability, R as evaluateZoneRules, St as array, X as runtimeDevices$1, _t as parseJsonUnknown, bt as sleep, dt as createEvent, ft as hydrateSchema, gt as nodePin, jt as union, st as BaseAddon, t as APPLE_SA_TO_MACRO, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT } from "../dist-BPaa4_z6.mjs";
3
+ import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, n as startEventLoopStallMonitor, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep } from "../event-loop-stall-monitor-B79REtL2.mjs";
4
4
  import { t as getSharp } from "../lazy-sharp-B-mb8uWx.mjs";
5
5
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
6
6
  import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../addon-utils-CZ2xo67g.mjs";
@@ -3497,17 +3497,6 @@ var NATIVE_FACE_CROP_MAX_WIDTH = 320;
3497
3497
  * would read, so the same floor applies honestly in both cases.
3498
3498
  */
3499
3499
  var MIN_PLATE_OCR_SHORT_SIDE_PX = 20;
3500
- /**
3501
- * StepOutput kinds that synthesize a full-frame detection when they run at
3502
- * the tree ROOT (see `synthesizeRootDetection`) — classifier/embedding/OCR
3503
- * steps promoted to root by `pipelineRunner.runDetailSubtree` (task 6b), e.g.
3504
- * `clip-embedding`, `animal-classifier`, `plate-ocr`. Detector (`'detections'`)
3505
- * roots take the normal path. `'text'` is included so a `plate-ocr` step run
3506
- * standalone on an already-cut plate crop (parent class `plate`) synthesizes a
3507
- * full-frame detection carrying the recognized text as a label, instead of
3508
- * silently dropping — the OCR chain otherwise only surfaces text when nested
3509
- * under `plate-detection`. `'mask'` roots remain out of scope.
3510
- */
3511
3500
  function isEnrichmentOutput(output) {
3512
3501
  return output.kind === "classifications" || output.kind === "embedding" || output.kind === "text";
3513
3502
  }
@@ -3792,12 +3781,13 @@ var PipelineExecutor = class {
3792
3781
  * falling back to `inputClasses[0]` when `outputClasses` is empty, then
3793
3782
  * the step id as a last resort so the field is never empty). `score` is
3794
3783
  * the output's own confidence when it has one (the classifier's top-1
3795
- * score) — an embedding carries no confidence, so it defaults to 1.
3784
+ * score, an OCR read's `confidence`) — an embedding carries no
3785
+ * confidence, so it defaults to 1.
3796
3786
  */
3797
3787
  synthesizeRootDetection(output, rootStep, idGen, rootStepLatencyMs, imageWidth, imageHeight) {
3798
3788
  return toMutableRootDetection({
3799
3789
  class: rootStep.definition.outputClasses[0] ?? rootStep.definition.inputClasses?.[0] ?? rootStep.stepId,
3800
- score: output.kind === "classifications" ? output.classifications[0]?.score ?? 1 : 1,
3790
+ score: output.kind === "classifications" ? output.classifications[0]?.score ?? 1 : output.kind === "text" ? output.confidence : 1,
3801
3791
  bbox: [
3802
3792
  0,
3803
3793
  0,
@@ -5679,7 +5669,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
5679
5669
  bbox: roi,
5680
5670
  ...maxWidth !== void 0 ? { maxWidth } : {}
5681
5671
  }, nodePin(handle.nodeId));
5682
- if (!native || native.width <= 0 || native.height <= 0) return null;
5672
+ if (!native || !native.bytes || native.width <= 0 || native.height <= 0) return null;
5683
5673
  return {
5684
5674
  bytes: native.bytes,
5685
5675
  width: native.width,
@@ -7597,7 +7587,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
7597
7587
  type: "select",
7598
7588
  key: "batchMode",
7599
7589
  label: "Batch dispatch mode",
7600
- description: "How the Python pool dispatches predicts. `none` = one call per frame. `window` = per-model accumulator coalesces concurrent calls within Window (ms). Auto uses the matrix-sweep default for the current backend.",
7590
+ description: "How the Python pool dispatches predicts. `none` = one call per frame. `window` = per-model accumulator coalesces concurrent calls within Window (ms). Auto uses the matrix-sweep default for the current backend. Applies to THIS node only.",
7601
7591
  options: [
7602
7592
  {
7603
7593
  value: "",
@@ -7616,13 +7606,14 @@ var DetectionPipelineAddon = class extends BaseAddon {
7616
7606
  label: "Window (cross-camera accumulator)"
7617
7607
  }
7618
7608
  ],
7619
- default: ""
7609
+ default: "",
7610
+ perNode: true
7620
7611
  }),
7621
7612
  this.field({
7622
7613
  type: "slider",
7623
7614
  key: "windowMs",
7624
7615
  label: "Window flush (ms)",
7625
- description: "Window-mode accumulator: max time to wait for additional concurrent predicts before flushing. Lower = lower latency, higher = better throughput. Auto = backend default.",
7616
+ description: "Window-mode accumulator: max time to wait for additional concurrent predicts before flushing. Lower = lower latency, higher = better throughput. Auto = backend default. Applies to THIS node only.",
7626
7617
  min: 0,
7627
7618
  max: 50,
7628
7619
  step: 1,
@@ -7630,6 +7621,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
7630
7621
  showValue: true,
7631
7622
  nullable: true,
7632
7623
  nullLabel: "Auto",
7624
+ perNode: true,
7633
7625
  showWhen: {
7634
7626
  field: "batchMode",
7635
7627
  notEquals: "none"
@@ -7639,7 +7631,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
7639
7631
  type: "slider",
7640
7632
  key: "maxBatchSize",
7641
7633
  label: "Max batch size",
7642
- description: "Cap on items per batched predict call. Auto = backend default.",
7634
+ description: "Cap on items per batched predict call. Auto = backend default. Applies to THIS node only.",
7643
7635
  min: 1,
7644
7636
  max: 16,
7645
7637
  step: 1,
@@ -7647,6 +7639,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
7647
7639
  showValue: true,
7648
7640
  nullable: true,
7649
7641
  nullLabel: "Auto",
7642
+ perNode: true,
7650
7643
  showWhen: {
7651
7644
  field: "batchMode",
7652
7645
  notEquals: "none"
@@ -7655,28 +7648,30 @@ var DetectionPipelineAddon = class extends BaseAddon {
7655
7648
  this.field({
7656
7649
  type: "slider",
7657
7650
  key: "concurrency",
7658
- label: "Pool workers",
7659
- description: "Python inference-pool prepare + predict threads. Auto = backend default (CoreML: 2, OpenVINO: 1, ONNX: 4).",
7651
+ label: "Pool threads",
7652
+ description: "Python inference-pool prepare + predict THREADS inside one pool subprocess — they share one model context. Auto = backend default (CoreML: 1, OpenVINO: 1, ONNX: 4). Raising this does not help CoreML (it serialises per model context) — use Pool subprocesses instead. Applies to THIS node only.",
7660
7653
  min: 1,
7661
7654
  max: 8,
7662
7655
  step: 1,
7663
7656
  default: void 0,
7664
7657
  showValue: true,
7665
7658
  nullable: true,
7666
- nullLabel: "Auto"
7659
+ nullLabel: "Auto",
7660
+ perNode: true
7667
7661
  }),
7668
7662
  this.field({
7669
7663
  type: "slider",
7670
7664
  key: "numWorkers",
7671
7665
  label: "Pool subprocesses",
7672
- description: "Number of independent Python pool subprocesses. Each holds its own MLModel copy + ANE/GPU/CPU dispatch context. Inference round-robins across them. RAM cost scales linearly. Auto = 2 for CoreML (lifts the single-context ANE cap on multi-camera workloads), 1 for ONNX/OpenVINO (they manage their own internal parallelism).",
7666
+ description: "Number of independent Python pool subprocesses. Each holds its own MLModel copy + ANE/GPU/CPU dispatch context, and its OWN in-flight cap — inference round-robins across them, so this is the knob that lifts a saturated single-context pool. RAM cost scales linearly with subprocesses × model size. Auto = 1 on every runtime. Applies to THIS node only.",
7673
7667
  min: 1,
7674
7668
  max: 8,
7675
7669
  step: 1,
7676
7670
  default: void 0,
7677
7671
  showValue: true,
7678
7672
  nullable: true,
7679
- nullLabel: "Auto"
7673
+ nullLabel: "Auto",
7674
+ perNode: true
7680
7675
  })
7681
7676
  ]
7682
7677
  }] });
@@ -7696,7 +7691,7 @@ var DetectionPipelineAddon = class extends BaseAddon {
7696
7691
  */
7697
7692
  async getGlobalSettings(overlay, _cap, nodeId) {
7698
7693
  const targetNode = nodeId ? normalizeEngineNodeId(nodeId) : this.localNodeId();
7699
- const rawStored = await this.resolveUiSettingsStore();
7694
+ const rawStored = await this.resolveUiSettingsStore(targetNode);
7700
7695
  if (rawStored === null) return null;
7701
7696
  const merged = overlay ? {
7702
7697
  ...rawStored,
@@ -7757,11 +7752,18 @@ var DetectionPipelineAddon = class extends BaseAddon {
7757
7752
  * uses the null signal to avoid serving fabricated empty-store defaults
7758
7753
  * during the restart window. Seam kept protected so tests can drive the
7759
7754
  * unreadable vs empty vs populated cases without faking a full AddonContext.
7755
+ *
7756
+ * The store is PROJECTED onto `nodeId` (the node the UI is asking about, not
7757
+ * necessarily this instance — the hub addon answers for every node): every
7758
+ * `perNode: true` field of the Inference-executor section hydrates from that
7759
+ * node's `<key>@<nodeId>` value, and `<key>@<other-node>` never leaks onto the
7760
+ * bare field. `resolveGlobalStore` is the base's sanctioned seam for exactly
7761
+ * this — reading `ctx.settings.readAddonStore()` raw here would show the hub's
7762
+ * numbers on every node's form.
7760
7763
  */
7761
- async resolveUiSettingsStore() {
7762
- const settings = this.ctxIfReady?.settings;
7763
- if (!settings) return null;
7764
- return await settings.readAddonStore() ?? {};
7764
+ async resolveUiSettingsStore(nodeId) {
7765
+ if (!this.ctxIfReady?.settings) return null;
7766
+ return this.resolveGlobalStore(nodeId);
7765
7767
  }
7766
7768
  async probeHardwareEnv(nodeId) {
7767
7769
  const node = nodeId ?? this.localNodeId();