@camstack/addon-pipeline 1.2.334 → 1.2.336

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 (36) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{custom-model-catalog-DoOVE7kz.mjs → custom-model-catalog-DSN-2fxO.mjs} +298 -18
  4. package/dist/{custom-model-catalog-CVeosmYm.js → custom-model-catalog-jyP-7H7P.js} +298 -18
  5. package/dist/detection-pipeline/index.js +121 -21
  6. package/dist/detection-pipeline/index.mjs +121 -21
  7. package/dist/{dist-C7v30DMP.js → dist-C5DlS_lW.js} +145 -0
  8. package/dist/{dist-Dn3knu_K.mjs → dist-CPhDwlSN.mjs} +134 -1
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-DWdekhW3.js → node-BUafKArW.js} +1 -1
  12. package/dist/{node-_BruMF4L.mjs → node-DYfEOwjW.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +23 -5
  14. package/dist/pipeline-runner/index.mjs +23 -5
  15. package/dist/{process-memory-CaDpfUdM.js → process-memory-DWOQCbg5.js} +1 -1
  16. package/dist/{process-memory-Dp-4bFOp.mjs → process-memory-WPRYnN8c.mjs} +1 -1
  17. package/dist/recorder/index.js +2 -2
  18. package/dist/recorder/index.mjs +2 -2
  19. package/dist/{segment-demux-js-BqPTLFZG.js → segment-demux-js-DWIgUPW1.js} +1 -1
  20. package/dist/{segment-demux-js-B_-zBECg.mjs → segment-demux-js-Kz4EnmOr.mjs} +1 -1
  21. package/dist/stream-broker/_stub.js +1 -1
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BP8FFU2i.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-RPk9F8CY.mjs} +2 -2
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-1Y-WK-Zy.mjs +26 -0
  24. package/dist/stream-broker/demux-worker-child.js +1 -1
  25. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  26. package/dist/stream-broker/{hostInit-BEh-hm1x.mjs → hostInit-DXXdQdLd.mjs} +2 -2
  27. package/dist/stream-broker/index.js +3 -3
  28. package/dist/stream-broker/index.mjs +3 -3
  29. package/dist/stream-broker/remoteEntry.js +1 -1
  30. package/package.json +1 -1
  31. package/python/coreml_load_probe.py +223 -0
  32. package/python/inference_pool.py +81 -5
  33. package/python/test_coreml_load_probe.py +295 -0
  34. package/python/test_inference_pool_coreml_cache.py +37 -1
  35. package/python/test_inference_pool_ov_precision.py +98 -0
  36. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CpjKK9II.mjs +0 -26
@@ -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-C7v30DMP.js");
7
- const require_node = require("../node-DWdekhW3.js");
8
- const require_custom_model_catalog = require("../custom-model-catalog-CVeosmYm.js");
6
+ const require_dist = require("../dist-C5DlS_lW.js");
7
+ const require_node = require("../node-BUafKArW.js");
8
+ const require_custom_model_catalog = require("../custom-model-catalog-jyP-7H7P.js");
9
9
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
10
- const require_process_memory = require("../process-memory-CaDpfUdM.js");
10
+ const require_process_memory = require("../process-memory-DWOQCbg5.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");
@@ -2250,6 +2250,7 @@ function describeCommand(cmd) {
2250
2250
  model: typeof modelPath === "string" && modelPath.length > 0 ? node_path.basename(modelPath, node_path.extname(modelPath)) : null
2251
2251
  };
2252
2252
  }
2253
+ /** The wire form of a model config — the keys the Python pool reads. Exported for tests. */
2253
2254
  function serializeModelConfig(config) {
2254
2255
  const result = {
2255
2256
  path: config.path,
@@ -2261,6 +2262,7 @@ function serializeModelConfig(config) {
2261
2262
  if (config.nmsIouThreshold !== void 0) result["nmsIouThreshold"] = config.nmsIouThreshold;
2262
2263
  if (config.inputNormalization !== void 0) result["inputNormalization"] = config.inputNormalization;
2263
2264
  if (config.outputProbabilities !== void 0) result["outputProbabilities"] = config.outputProbabilities;
2265
+ if (config.gpuInferencePrecision !== void 0) result["gpuInferencePrecision"] = config.gpuInferencePrecision;
2264
2266
  if (config.inputWidth !== void 0) result["inputWidth"] = config.inputWidth;
2265
2267
  if (config.inputHeight !== void 0) result["inputHeight"] = config.inputHeight;
2266
2268
  if (config.inputChannels !== void 0) result["inputChannels"] = config.inputChannels;
@@ -2451,25 +2453,43 @@ function collectClassMapDropWarning(def, entry) {
2451
2453
  function toApplicability(acc) {
2452
2454
  const hasInapplicable = Object.keys(acc.inapplicable).length > 0;
2453
2455
  const hasOptions = Object.keys(acc.optionsByModel).length > 0;
2454
- if (!hasInapplicable && !hasOptions) return void 0;
2456
+ const hasDefaults = Object.keys(acc.defaultByModel).length > 0;
2457
+ if (!hasInapplicable && !hasOptions && !hasDefaults) return void 0;
2455
2458
  return {
2456
2459
  ...hasInapplicable ? { inapplicable: acc.inapplicable } : {},
2457
- ...hasOptions ? { optionsByModel: acc.optionsByModel } : {}
2460
+ ...hasOptions ? { optionsByModel: acc.optionsByModel } : {},
2461
+ ...hasDefaults ? { defaultByModel: acc.defaultByModel } : {}
2458
2462
  };
2459
2463
  }
2464
+ /** The `default` of `key` in a schema, groups flattened; `undefined` when absent. */
2465
+ function defaultOfKey(fields, key) {
2466
+ for (const field of fields) {
2467
+ if (field.type === "group") {
2468
+ const nested = defaultOfKey(field.fields, key);
2469
+ if (nested !== void 0) return nested;
2470
+ continue;
2471
+ }
2472
+ if ("key" in field && field.key === key && "default" in field) return field.default;
2473
+ }
2474
+ }
2460
2475
  function isAnnotatable(field) {
2461
2476
  return field.type === "slider" || field.type === "number" || field.type === "multiselect" || field.type === "select" || field.type === "boolean";
2462
2477
  }
2463
2478
  function annotateField(field, inputs) {
2464
2479
  if (!isAnnotatable(field)) return field;
2465
- const { def, models, customModelIds } = inputs;
2480
+ const { def, models, customModelIds, schemaForModel } = inputs;
2466
2481
  const acc = {
2467
2482
  inapplicable: {},
2468
- optionsByModel: {}
2483
+ optionsByModel: {},
2484
+ defaultByModel: {}
2469
2485
  };
2470
2486
  const macroKey = macroOfMinConfidenceKey(field.key);
2471
2487
  for (const entry of models) {
2472
2488
  const postprocessor = resolveEffectivePostprocessorForEntry(def, entry);
2489
+ if (field.bindsToModel === true && schemaForModel !== void 0) {
2490
+ const modelDefault = defaultOfKey(schemaForModel(entry), field.key);
2491
+ if (modelDefault !== void 0 && modelDefault !== field.default) acc.defaultByModel[entry.id] = modelDefault;
2492
+ }
2473
2493
  if (field.key === "nmsIouThreshold" && !postprocessorConsumesNms(postprocessor)) {
2474
2494
  acc.inapplicable[entry.id] = `NMS is fused inside this model's "${postprocessor}" export — the pipeline IoU threshold is not consulted.`;
2475
2495
  continue;
@@ -2518,6 +2538,55 @@ function annotateField(field, inputs) {
2518
2538
  function annotateConfigSchemaForModels(inputs) {
2519
2539
  return inputs.configSchema.map((field) => annotateField(field, inputs));
2520
2540
  }
2541
+ /** The slider that governs a root-supplied fine label (D741) — the step's own threshold. */
2542
+ var SUPPLIED_LABEL_FLOOR_KEY = "minConfidence";
2543
+ /**
2544
+ * D741 — when a ROOT detector supplies a step's fine label (species, vehicle
2545
+ * type), the step's `minConfidence` slider is the floor on the root's
2546
+ * DETECTION score for that label, bound to the root model. So for each root
2547
+ * model that supplies `stepId`'s labels, the slider's default under that model
2548
+ * is the root's `suppliedLabelFloors[stepId]`, or 0 (no floor beyond the macro
2549
+ * floor). Added to `modelApplicability.defaultByModel`, keyed by root model id,
2550
+ * so the editor — which shows the slider under the ROOT's id while the root
2551
+ * supplies the step — shows the floor the runner applies. A step no root
2552
+ * supplies a fine label for is returned untouched.
2553
+ */
2554
+ function withRootSuppliedLabelDefaults(stepId, configSchema, rootModels) {
2555
+ if (!isFineLabelSupplyStep(stepId)) return [...configSchema];
2556
+ const rootDef = require_custom_model_catalog.getStepDefinition(ROOT_STEP_ID);
2557
+ const defaults = {};
2558
+ for (const root of rootModels) {
2559
+ if (!require_dist.rootSuppliesOf(root, {
2560
+ labels: rootDef.labels ?? [],
2561
+ ...rootDef.classMap !== void 0 ? { classMap: rootDef.classMap } : {}
2562
+ }).some((s) => s.kind === "label" && s.stepId === stepId)) continue;
2563
+ defaults[root.id] = root.suppliedLabelFloors?.[stepId] ?? 0;
2564
+ }
2565
+ if (Object.keys(defaults).length === 0) return [...configSchema];
2566
+ return configSchema.map((field) => {
2567
+ if (field.type !== "slider" || field.key !== SUPPLIED_LABEL_FLOOR_KEY) return field;
2568
+ return {
2569
+ ...field,
2570
+ modelApplicability: {
2571
+ ...field.modelApplicability,
2572
+ defaultByModel: {
2573
+ ...field.modelApplicability?.defaultByModel,
2574
+ ...defaults
2575
+ }
2576
+ }
2577
+ };
2578
+ });
2579
+ }
2580
+ /** The camera root the supplies are read from. */
2581
+ var ROOT_STEP_ID = "object-detection";
2582
+ /**
2583
+ * A label-supply step whose effect is FINER than its macro (species, vehicle
2584
+ * type). `package-detection` is a label supply too, but its effect IS its
2585
+ * macro: it has no fine label and its threshold is `minConfidencePackage`.
2586
+ */
2587
+ function isFineLabelSupplyStep(stepId) {
2588
+ return Object.keys(require_dist.LABEL_SUPPLY_MACRO).includes(stepId) && stepId !== "package-detection";
2589
+ }
2521
2590
  /**
2522
2591
  * A persisted settings blob is external data — it survives schema changes,
2523
2592
  * hand edits and migrations. Accept only a finite IoU in the open (0,1)
@@ -3695,6 +3764,7 @@ function buildPoolModelConfigForStep(inputs) {
3695
3764
  preprocessMode: modelEntry.preprocessMode ?? "letterbox",
3696
3765
  inputNormalization: modelEntry.inputNormalization,
3697
3766
  outputProbabilities: modelEntry.outputProbabilities,
3767
+ ...modelEntry.gpuInferencePrecision !== void 0 ? { gpuInferencePrecision: modelEntry.gpuInferencePrecision } : {},
3698
3768
  postprocessor: resolveEffectivePostprocessorForEntry(def, modelEntry),
3699
3769
  confidence: resolvePoolConfidenceFloor(stepId, require_custom_model_catalog.resolveModelDefaultConfidence(def, modelEntry)),
3700
3770
  nmsIouThreshold: resolvePoolDecodeSettings({
@@ -7150,7 +7220,15 @@ var PipelineExecutor = class {
7150
7220
  if (plane === "frame" && child.definition.inputClasses !== null) continue;
7151
7221
  if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
7152
7222
  const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
7153
- if (typeof minParentScore === "number" && parentDetection.score < minParentScore) continue;
7223
+ if (typeof minParentScore === "number" && parentDetection.score < minParentScore) {
7224
+ if (child.definition.models.find((m) => m.id === child.modelId)?.faceAlignment === true) this.throttledWarn(`face-embed-score-floor:${child.stepId}:${deviceId}`, 3e4, "face detected but below the embedding score floor — no embedding", {
7225
+ step: child.stepId,
7226
+ model: child.modelId,
7227
+ faceScore: parentDetection.score,
7228
+ minParentScore
7229
+ }, deviceId);
7230
+ continue;
7231
+ }
7154
7232
  if (isBboxDegenerate(parentDetection.bbox)) continue;
7155
7233
  try {
7156
7234
  const childStart = Date.now();
@@ -7441,9 +7519,12 @@ function buildNode(step, getEngine, resolveModel, onMissingVariant, resolved) {
7441
7519
  const variant = resolved ?? getEngine(step.addonId, step.modelId);
7442
7520
  const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildChild(c, step.addonId, getEngine, resolveModel, onMissingVariant)).filter((c) => c !== null);
7443
7521
  const modelEntry = resolveModelEntry(definition, variant.modelId, resolveModel);
7522
+ const schema = require_custom_model_catalog.getStep(step.addonId).getConfigSchema(modelEntry);
7523
+ const schemaDefaults = collectSchemaDefaults(schema);
7524
+ const operatorSettings = require_dist.applyModelBoundSettings(step.settings, collectModelBoundKeys(schema), variant.modelId);
7444
7525
  const mergedSettings = {
7445
- ...collectSchemaDefaults(step.addonId, modelEntry),
7446
- ...step.settings
7526
+ ...schemaDefaults,
7527
+ ...operatorSettings
7447
7528
  };
7448
7529
  const effectiveClassMap = modelEntry?.classMap ?? definition.classMap;
7449
7530
  return {
@@ -7469,17 +7550,33 @@ function rootSuppliesField(definition, modelEntry) {
7469
7550
  return supplies.length > 0 ? { rootSupplies: supplies } : {};
7470
7551
  }
7471
7552
  /**
7472
- * Walk the step's declared `getConfigSchema()` and collect every field
7473
- * with a primitive `default` value into a plain record. Group fields
7474
- * are flattened — a nested `{ type: 'group', fields: [...] }` is
7475
- * entered recursively. Fields without a declared default are skipped.
7553
+ * Collect every field of a step's `getConfigSchema(model)` with a primitive
7554
+ * `default` value into a plain record. Group fields are flattened — a nested
7555
+ * `{ type: 'group', fields: [...] }` is entered recursively. Fields without a
7556
+ * declared default are skipped.
7476
7557
  */
7477
- function collectSchemaDefaults(stepId, model) {
7478
- const schema = require_custom_model_catalog.getStep(stepId).getConfigSchema(model);
7558
+ function collectSchemaDefaults(schema) {
7479
7559
  const out = {};
7480
7560
  walkFieldsForDefaults(schema, out);
7481
7561
  return out;
7482
7562
  }
7563
+ /** The keys of every `bindsToModel` field of a schema, groups flattened (D738). */
7564
+ function collectModelBoundKeys(fields) {
7565
+ return fields.flatMap((field) => {
7566
+ if (field.type === "group") return collectModelBoundKeys(field.fields);
7567
+ return "key" in field && "bindsToModel" in field && field.bindsToModel === true ? [field.key] : [];
7568
+ });
7569
+ }
7570
+ /**
7571
+ * The operator settings of `stepId` as the model that RUNS sees them (D738):
7572
+ * a model-bound value made for another model is dropped and binding keys are
7573
+ * stripped. For a settings layer merged AFTER the tree is built (the
7574
+ * executor's per-device store, `applyDeviceOverridesToTree`), so it obeys the
7575
+ * same binding as the layers `buildNode` merges.
7576
+ */
7577
+ function resolveOperatorSettingsForModel(stepId, settings, runningModelId) {
7578
+ return require_dist.applyModelBoundSettings(settings, collectModelBoundKeys(require_custom_model_catalog.getStep(stepId).getConfigSchema()), runningModelId) ?? {};
7579
+ }
7483
7580
  function walkFieldsForDefaults(fields, out) {
7484
7581
  for (const field of fields) {
7485
7582
  if ("type" in field && field.type === "group" && "fields" in field) {
@@ -11155,8 +11252,10 @@ function buildSchemaSlots(formats, modelsDir, customByStep) {
11155
11252
  def: step,
11156
11253
  configSchema: pipelineStep.getConfigSchema(),
11157
11254
  models: availableModels,
11158
- customModelIds: customIds
11255
+ customModelIds: customIds,
11256
+ schemaForModel: (entry) => pipelineStep.getConfigSchema(entry)
11159
11257
  });
11258
+ const configSchemaWithRoots = withRootSuppliedLabelDefaults(step.id, configSchema, require_custom_model_catalog.mergeCustomModels(require_custom_model_catalog.getStepDefinition("object-detection").models, customByStep?.get("object-detection") ?? []));
11160
11259
  slotMap.get(slot).push({
11161
11260
  id: step.id,
11162
11261
  name: step.name,
@@ -11183,7 +11282,7 @@ function buildSchemaSlots(formats, modelsDir, customByStep) {
11183
11282
  ...step.backfillIntoExistingOverrides !== void 0 ? { backfillIntoExistingOverrides: step.backfillIntoExistingOverrides } : {},
11184
11283
  defaultConfidence: step.defaultConfidence,
11185
11284
  ...step.group ? { group: step.group } : {},
11186
- ...configSchema.length > 0 ? { configSchema: [...configSchema] } : {}
11285
+ ...configSchemaWithRoots.length > 0 ? { configSchema: configSchemaWithRoots } : {}
11187
11286
  });
11188
11287
  }
11189
11288
  const slotPriority = {
@@ -11355,14 +11454,15 @@ function availableFormatsForStep(addonId) {
11355
11454
  * Return a shallow-cloned executable tree with `rootStepId`'s `settings`
11356
11455
  * merged with `overrides` (per-device values win). Non-matching root steps
11357
11456
  * pass through unchanged. Used by `runFrame` to apply per-device detection
11358
- * settings without rebuilding the tree per frame.
11457
+ * settings without rebuilding the tree per frame. A model-bound value (D738)
11458
+ * applies only while the model it was made for is the one the root RUNS.
11359
11459
  */
11360
11460
  function applyDeviceOverridesToTree(tree, rootStepId, overrides) {
11361
11461
  return { roots: tree.roots.map((root) => root.stepId === rootStepId ? {
11362
11462
  ...root,
11363
11463
  settings: {
11364
11464
  ...root.settings,
11365
- ...overrides
11465
+ ...resolveOperatorSettingsForModel(rootStepId, overrides, root.modelId)
11366
11466
  }
11367
11467
  } : root) };
11368
11468
  }
@@ -1,9 +1,9 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $n as union, Cn as errMsg, Dt as linkedLabelStepId, G as YAMNET_TO_MACRO, Ln as nodePin, N as PoolMemoryWatchdog, Nn as hydrateSchema, O as LABEL_SUPPLY_MACRO, Qn as string, Rn as parseJsonUnknown, Tn as BaseAddon, Un as array, Vn as sleep, Vt as pickClusterStepSettings, Wt as pipelineExecutorCapability, Yn as object, Zt as resolveClusterStepModelId, an as runtimeDevices$1, d as CLUSTER_MODEL_SCOPED_STEPS, dn as supportedRuntimes$1, dt as detectionPipelineCapability, er as EventCategory, g as DEFAULT_CLUSTER_STEP_SETTINGS, ht as evaluateZoneRules, in as rootSuppliesOf, kn as createEvent, kt as loadContributionCapability, mt as enumerateInferenceDevices, n as APPLE_SA_TO_MACRO, nt as containingParentIndex, on as stepModelRunsOnFormat, ot as defaultDeviceFor$1, tn as resolvePoolMemoryPolicy, wt as hfModelUrl, zt as overlayClusterStepSettings } from "../dist-Dn3knu_K.mjs";
3
- import { s as readProcessCost } from "../node-_BruMF4L.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-DoOVE7kz.mjs";
2
+ import { At as loadContributionCapability, Bn as parseJsonUnknown, Bt as overlayClusterStepSettings, Dn as BaseAddon, Fn as hydrateSchema, G as YAMNET_TO_MACRO, Gn as array, Gt as pipelineExecutorCapability, Ht as pickClusterStepSettings, J as applyModelBoundSettings, N as PoolMemoryWatchdog, O as LABEL_SUPPLY_MACRO, Ot as linkedLabelStepId, Qt as resolveClusterStepModelId, Tn as errMsg, Tt as hfModelUrl, Un as sleep, Zn as object, cn as stepModelRunsOnFormat, d as CLUSTER_MODEL_SCOPED_STEPS, er as string, ft as detectionPipelineCapability, g as DEFAULT_CLUSTER_STEP_SETTINGS, gt as evaluateZoneRules, ht as enumerateInferenceDevices, jn as createEvent, n as APPLE_SA_TO_MACRO, nr as EventCategory, on as rootSuppliesOf, pn as supportedRuntimes$1, rn as resolvePoolMemoryPolicy, rt as containingParentIndex, sn as runtimeDevices$1, st as defaultDeviceFor$1, tr as union, zn as nodePin } from "../dist-CPhDwlSN.mjs";
3
+ import { s as readProcessCost } from "../node-DYfEOwjW.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-DSN-2fxO.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-Dp-4bFOp.mjs";
6
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-WPRYnN8c.mjs";
7
7
  import * as os from "node:os";
8
8
  import * as fs from "node:fs";
9
9
  import * as path$1 from "node:path";
@@ -2243,6 +2243,7 @@ function describeCommand(cmd) {
2243
2243
  model: typeof modelPath === "string" && modelPath.length > 0 ? path$1.basename(modelPath, path$1.extname(modelPath)) : null
2244
2244
  };
2245
2245
  }
2246
+ /** The wire form of a model config — the keys the Python pool reads. Exported for tests. */
2246
2247
  function serializeModelConfig(config) {
2247
2248
  const result = {
2248
2249
  path: config.path,
@@ -2254,6 +2255,7 @@ function serializeModelConfig(config) {
2254
2255
  if (config.nmsIouThreshold !== void 0) result["nmsIouThreshold"] = config.nmsIouThreshold;
2255
2256
  if (config.inputNormalization !== void 0) result["inputNormalization"] = config.inputNormalization;
2256
2257
  if (config.outputProbabilities !== void 0) result["outputProbabilities"] = config.outputProbabilities;
2258
+ if (config.gpuInferencePrecision !== void 0) result["gpuInferencePrecision"] = config.gpuInferencePrecision;
2257
2259
  if (config.inputWidth !== void 0) result["inputWidth"] = config.inputWidth;
2258
2260
  if (config.inputHeight !== void 0) result["inputHeight"] = config.inputHeight;
2259
2261
  if (config.inputChannels !== void 0) result["inputChannels"] = config.inputChannels;
@@ -2444,25 +2446,43 @@ function collectClassMapDropWarning(def, entry) {
2444
2446
  function toApplicability(acc) {
2445
2447
  const hasInapplicable = Object.keys(acc.inapplicable).length > 0;
2446
2448
  const hasOptions = Object.keys(acc.optionsByModel).length > 0;
2447
- if (!hasInapplicable && !hasOptions) return void 0;
2449
+ const hasDefaults = Object.keys(acc.defaultByModel).length > 0;
2450
+ if (!hasInapplicable && !hasOptions && !hasDefaults) return void 0;
2448
2451
  return {
2449
2452
  ...hasInapplicable ? { inapplicable: acc.inapplicable } : {},
2450
- ...hasOptions ? { optionsByModel: acc.optionsByModel } : {}
2453
+ ...hasOptions ? { optionsByModel: acc.optionsByModel } : {},
2454
+ ...hasDefaults ? { defaultByModel: acc.defaultByModel } : {}
2451
2455
  };
2452
2456
  }
2457
+ /** The `default` of `key` in a schema, groups flattened; `undefined` when absent. */
2458
+ function defaultOfKey(fields, key) {
2459
+ for (const field of fields) {
2460
+ if (field.type === "group") {
2461
+ const nested = defaultOfKey(field.fields, key);
2462
+ if (nested !== void 0) return nested;
2463
+ continue;
2464
+ }
2465
+ if ("key" in field && field.key === key && "default" in field) return field.default;
2466
+ }
2467
+ }
2453
2468
  function isAnnotatable(field) {
2454
2469
  return field.type === "slider" || field.type === "number" || field.type === "multiselect" || field.type === "select" || field.type === "boolean";
2455
2470
  }
2456
2471
  function annotateField(field, inputs) {
2457
2472
  if (!isAnnotatable(field)) return field;
2458
- const { def, models, customModelIds } = inputs;
2473
+ const { def, models, customModelIds, schemaForModel } = inputs;
2459
2474
  const acc = {
2460
2475
  inapplicable: {},
2461
- optionsByModel: {}
2476
+ optionsByModel: {},
2477
+ defaultByModel: {}
2462
2478
  };
2463
2479
  const macroKey = macroOfMinConfidenceKey(field.key);
2464
2480
  for (const entry of models) {
2465
2481
  const postprocessor = resolveEffectivePostprocessorForEntry(def, entry);
2482
+ if (field.bindsToModel === true && schemaForModel !== void 0) {
2483
+ const modelDefault = defaultOfKey(schemaForModel(entry), field.key);
2484
+ if (modelDefault !== void 0 && modelDefault !== field.default) acc.defaultByModel[entry.id] = modelDefault;
2485
+ }
2466
2486
  if (field.key === "nmsIouThreshold" && !postprocessorConsumesNms(postprocessor)) {
2467
2487
  acc.inapplicable[entry.id] = `NMS is fused inside this model's "${postprocessor}" export — the pipeline IoU threshold is not consulted.`;
2468
2488
  continue;
@@ -2511,6 +2531,55 @@ function annotateField(field, inputs) {
2511
2531
  function annotateConfigSchemaForModels(inputs) {
2512
2532
  return inputs.configSchema.map((field) => annotateField(field, inputs));
2513
2533
  }
2534
+ /** The slider that governs a root-supplied fine label (D741) — the step's own threshold. */
2535
+ var SUPPLIED_LABEL_FLOOR_KEY = "minConfidence";
2536
+ /**
2537
+ * D741 — when a ROOT detector supplies a step's fine label (species, vehicle
2538
+ * type), the step's `minConfidence` slider is the floor on the root's
2539
+ * DETECTION score for that label, bound to the root model. So for each root
2540
+ * model that supplies `stepId`'s labels, the slider's default under that model
2541
+ * is the root's `suppliedLabelFloors[stepId]`, or 0 (no floor beyond the macro
2542
+ * floor). Added to `modelApplicability.defaultByModel`, keyed by root model id,
2543
+ * so the editor — which shows the slider under the ROOT's id while the root
2544
+ * supplies the step — shows the floor the runner applies. A step no root
2545
+ * supplies a fine label for is returned untouched.
2546
+ */
2547
+ function withRootSuppliedLabelDefaults(stepId, configSchema, rootModels) {
2548
+ if (!isFineLabelSupplyStep(stepId)) return [...configSchema];
2549
+ const rootDef = getStepDefinition(ROOT_STEP_ID);
2550
+ const defaults = {};
2551
+ for (const root of rootModels) {
2552
+ if (!rootSuppliesOf(root, {
2553
+ labels: rootDef.labels ?? [],
2554
+ ...rootDef.classMap !== void 0 ? { classMap: rootDef.classMap } : {}
2555
+ }).some((s) => s.kind === "label" && s.stepId === stepId)) continue;
2556
+ defaults[root.id] = root.suppliedLabelFloors?.[stepId] ?? 0;
2557
+ }
2558
+ if (Object.keys(defaults).length === 0) return [...configSchema];
2559
+ return configSchema.map((field) => {
2560
+ if (field.type !== "slider" || field.key !== SUPPLIED_LABEL_FLOOR_KEY) return field;
2561
+ return {
2562
+ ...field,
2563
+ modelApplicability: {
2564
+ ...field.modelApplicability,
2565
+ defaultByModel: {
2566
+ ...field.modelApplicability?.defaultByModel,
2567
+ ...defaults
2568
+ }
2569
+ }
2570
+ };
2571
+ });
2572
+ }
2573
+ /** The camera root the supplies are read from. */
2574
+ var ROOT_STEP_ID = "object-detection";
2575
+ /**
2576
+ * A label-supply step whose effect is FINER than its macro (species, vehicle
2577
+ * type). `package-detection` is a label supply too, but its effect IS its
2578
+ * macro: it has no fine label and its threshold is `minConfidencePackage`.
2579
+ */
2580
+ function isFineLabelSupplyStep(stepId) {
2581
+ return Object.keys(LABEL_SUPPLY_MACRO).includes(stepId) && stepId !== "package-detection";
2582
+ }
2514
2583
  /**
2515
2584
  * A persisted settings blob is external data — it survives schema changes,
2516
2585
  * hand edits and migrations. Accept only a finite IoU in the open (0,1)
@@ -3688,6 +3757,7 @@ function buildPoolModelConfigForStep(inputs) {
3688
3757
  preprocessMode: modelEntry.preprocessMode ?? "letterbox",
3689
3758
  inputNormalization: modelEntry.inputNormalization,
3690
3759
  outputProbabilities: modelEntry.outputProbabilities,
3760
+ ...modelEntry.gpuInferencePrecision !== void 0 ? { gpuInferencePrecision: modelEntry.gpuInferencePrecision } : {},
3691
3761
  postprocessor: resolveEffectivePostprocessorForEntry(def, modelEntry),
3692
3762
  confidence: resolvePoolConfidenceFloor(stepId, resolveModelDefaultConfidence(def, modelEntry)),
3693
3763
  nmsIouThreshold: resolvePoolDecodeSettings({
@@ -7143,7 +7213,15 @@ var PipelineExecutor = class {
7143
7213
  if (plane === "frame" && child.definition.inputClasses !== null) continue;
7144
7214
  if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) continue;
7145
7215
  const minParentScore = child.settings?.minParentScore ?? child.definition?.defaultMinParentScore;
7146
- if (typeof minParentScore === "number" && parentDetection.score < minParentScore) continue;
7216
+ if (typeof minParentScore === "number" && parentDetection.score < minParentScore) {
7217
+ if (child.definition.models.find((m) => m.id === child.modelId)?.faceAlignment === true) this.throttledWarn(`face-embed-score-floor:${child.stepId}:${deviceId}`, 3e4, "face detected but below the embedding score floor — no embedding", {
7218
+ step: child.stepId,
7219
+ model: child.modelId,
7220
+ faceScore: parentDetection.score,
7221
+ minParentScore
7222
+ }, deviceId);
7223
+ continue;
7224
+ }
7147
7225
  if (isBboxDegenerate(parentDetection.bbox)) continue;
7148
7226
  try {
7149
7227
  const childStart = Date.now();
@@ -7434,9 +7512,12 @@ function buildNode(step, getEngine, resolveModel, onMissingVariant, resolved) {
7434
7512
  const variant = resolved ?? getEngine(step.addonId, step.modelId);
7435
7513
  const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildChild(c, step.addonId, getEngine, resolveModel, onMissingVariant)).filter((c) => c !== null);
7436
7514
  const modelEntry = resolveModelEntry(definition, variant.modelId, resolveModel);
7515
+ const schema = getStep(step.addonId).getConfigSchema(modelEntry);
7516
+ const schemaDefaults = collectSchemaDefaults(schema);
7517
+ const operatorSettings = applyModelBoundSettings(step.settings, collectModelBoundKeys(schema), variant.modelId);
7437
7518
  const mergedSettings = {
7438
- ...collectSchemaDefaults(step.addonId, modelEntry),
7439
- ...step.settings
7519
+ ...schemaDefaults,
7520
+ ...operatorSettings
7440
7521
  };
7441
7522
  const effectiveClassMap = modelEntry?.classMap ?? definition.classMap;
7442
7523
  return {
@@ -7462,17 +7543,33 @@ function rootSuppliesField(definition, modelEntry) {
7462
7543
  return supplies.length > 0 ? { rootSupplies: supplies } : {};
7463
7544
  }
7464
7545
  /**
7465
- * Walk the step's declared `getConfigSchema()` and collect every field
7466
- * with a primitive `default` value into a plain record. Group fields
7467
- * are flattened — a nested `{ type: 'group', fields: [...] }` is
7468
- * entered recursively. Fields without a declared default are skipped.
7546
+ * Collect every field of a step's `getConfigSchema(model)` with a primitive
7547
+ * `default` value into a plain record. Group fields are flattened — a nested
7548
+ * `{ type: 'group', fields: [...] }` is entered recursively. Fields without a
7549
+ * declared default are skipped.
7469
7550
  */
7470
- function collectSchemaDefaults(stepId, model) {
7471
- const schema = getStep(stepId).getConfigSchema(model);
7551
+ function collectSchemaDefaults(schema) {
7472
7552
  const out = {};
7473
7553
  walkFieldsForDefaults(schema, out);
7474
7554
  return out;
7475
7555
  }
7556
+ /** The keys of every `bindsToModel` field of a schema, groups flattened (D738). */
7557
+ function collectModelBoundKeys(fields) {
7558
+ return fields.flatMap((field) => {
7559
+ if (field.type === "group") return collectModelBoundKeys(field.fields);
7560
+ return "key" in field && "bindsToModel" in field && field.bindsToModel === true ? [field.key] : [];
7561
+ });
7562
+ }
7563
+ /**
7564
+ * The operator settings of `stepId` as the model that RUNS sees them (D738):
7565
+ * a model-bound value made for another model is dropped and binding keys are
7566
+ * stripped. For a settings layer merged AFTER the tree is built (the
7567
+ * executor's per-device store, `applyDeviceOverridesToTree`), so it obeys the
7568
+ * same binding as the layers `buildNode` merges.
7569
+ */
7570
+ function resolveOperatorSettingsForModel(stepId, settings, runningModelId) {
7571
+ return applyModelBoundSettings(settings, collectModelBoundKeys(getStep(stepId).getConfigSchema()), runningModelId) ?? {};
7572
+ }
7476
7573
  function walkFieldsForDefaults(fields, out) {
7477
7574
  for (const field of fields) {
7478
7575
  if ("type" in field && field.type === "group" && "fields" in field) {
@@ -11148,8 +11245,10 @@ function buildSchemaSlots(formats, modelsDir, customByStep) {
11148
11245
  def: step,
11149
11246
  configSchema: pipelineStep.getConfigSchema(),
11150
11247
  models: availableModels,
11151
- customModelIds: customIds
11248
+ customModelIds: customIds,
11249
+ schemaForModel: (entry) => pipelineStep.getConfigSchema(entry)
11152
11250
  });
11251
+ const configSchemaWithRoots = withRootSuppliedLabelDefaults(step.id, configSchema, mergeCustomModels(getStepDefinition("object-detection").models, customByStep?.get("object-detection") ?? []));
11153
11252
  slotMap.get(slot).push({
11154
11253
  id: step.id,
11155
11254
  name: step.name,
@@ -11176,7 +11275,7 @@ function buildSchemaSlots(formats, modelsDir, customByStep) {
11176
11275
  ...step.backfillIntoExistingOverrides !== void 0 ? { backfillIntoExistingOverrides: step.backfillIntoExistingOverrides } : {},
11177
11276
  defaultConfidence: step.defaultConfidence,
11178
11277
  ...step.group ? { group: step.group } : {},
11179
- ...configSchema.length > 0 ? { configSchema: [...configSchema] } : {}
11278
+ ...configSchemaWithRoots.length > 0 ? { configSchema: configSchemaWithRoots } : {}
11180
11279
  });
11181
11280
  }
11182
11281
  const slotPriority = {
@@ -11348,14 +11447,15 @@ function availableFormatsForStep(addonId) {
11348
11447
  * Return a shallow-cloned executable tree with `rootStepId`'s `settings`
11349
11448
  * merged with `overrides` (per-device values win). Non-matching root steps
11350
11449
  * pass through unchanged. Used by `runFrame` to apply per-device detection
11351
- * settings without rebuilding the tree per frame.
11450
+ * settings without rebuilding the tree per frame. A model-bound value (D738)
11451
+ * applies only while the model it was made for is the one the root RUNS.
11352
11452
  */
11353
11453
  function applyDeviceOverridesToTree(tree, rootStepId, overrides) {
11354
11454
  return { roots: tree.roots.map((root) => root.stepId === rootStepId ? {
11355
11455
  ...root,
11356
11456
  settings: {
11357
11457
  ...root.settings,
11358
- ...overrides
11458
+ ...resolveOperatorSettingsForModel(rootStepId, overrides, root.modelId)
11359
11459
  }
11360
11460
  } : root) };
11361
11461
  }