@camstack/addon-detection-pipeline 0.1.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../node_modules/tsup/assets/esm_shims.js","../src/index.ts","../src/provider.ts","../src/engine/engine-factory.ts","../src/engine/shared-inference-pool.ts","../src/engine/step-utils.ts","../src/engine/pipeline-model-manager.ts","../src/registry/step-definitions.ts","../src/registry/model-catalogs.ts","../src/engine/node-engine-manager.ts","../src/engine/model-shape-validator.ts","../src/postprocess/dispatch.ts","../src/pipeline/crop-utils.ts","../src/pipeline/result-assembler.ts","../src/pipeline/execution-trace.ts","../src/pipeline/executor.ts","../src/pipeline/tree-builder.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * addon-detection-pipeline — unified detection pipeline for CamStack.\n *\n * Single addon replaces 13 separate vision addon classes.\n * Each pipeline step is a class implementing IPipelineStep.\n */\nimport type {\n ConfigUISchemaWithValues,\n ModelCatalogEntry,\n ProviderRegistration,\n} from '@camstack/types'\nimport { BaseAddon, EventCategory, createEvent, pipelineExecutorCapability, detectionPipelineCapability, hydrateSchema } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { DetectionPipelineProvider } from './provider.js'\nimport { ALL_STEPS } from './registry/step-definitions.js'\n\n// ---------------------------------------------------------------------------\n// Embedded Python — file resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Locate the addon's bundled `python/` dir at runtime. Mirrors the\n * resolution done in `shared-inference-pool.ts:resolveScriptPath` —\n * tries the published package first (resolves through node_modules),\n * then a few `__dirname`-relative candidates for the in-tree dev build.\n *\n * Returns the directory containing `inference_pool.py` and the\n * `requirements*.txt` files installed via `ctx.deps.installPythonRequirements`.\n */\nfunction resolveAddonPythonDir(): string {\n const candidates: string[] = []\n try {\n const pkgPath = require.resolve('@camstack/addon-detection-pipeline/package.json')\n candidates.push(path.join(path.dirname(pkgPath), 'python'))\n } catch {\n // package not resolvable from here (in-tree build)\n }\n candidates.push(\n path.join(__dirname, '../python'),\n path.join(__dirname, '../../python'),\n path.join(__dirname, '../../../python'),\n )\n for (const c of candidates) {\n if (fs.existsSync(path.join(c, 'inference_pool.py'))) return c\n }\n throw new Error(\n `addon-detection-pipeline: python/ dir not found. Searched:\\n${candidates.join('\\n')}`,\n )\n}\n\n// Re-export for consumers\nexport { ALL_STEPS, getStepDefinition, getDefaultModelForFormat } from './registry/step-definitions.js'\nexport { DetectionPipelineProvider } from './provider.js'\nexport type { StepDefinition, PostprocessorType, PoolModelConfig } from '@camstack/types'\n\n// ---------------------------------------------------------------------------\n// Platform-aware default engine selection\n// ---------------------------------------------------------------------------\n\n/**\n * Select the best inference engine for the current platform.\n * Order: CoreML (macOS ARM) > CUDA (NVIDIA) > OpenVINO (Intel) > CPU\n */\n\n// ---------------------------------------------------------------------------\n// Addon class\n// ---------------------------------------------------------------------------\n\n// Phase 2a of the pipeline-settings migration (see\n// docs/pipeline-settings-migration.md) — start surfacing the addon's\n// own tunables. `concurrency` is the first field on the schema:\n// prepare + predict ThreadPoolExecutor pool size inside\n// inference_pool.py.\n//\n// Orchestration-level settings that used to live here\n// (maxConcurrentInferences / motionStreamProfile / motionFps / ...)\n// remain owned elsewhere:\n// - motion/detection stream profiles, FPS, cooldown → pipeline-orchestrator\n// - maxConcurrentInferences / maxQueueDepth / targetLoad / minThrottledFps\n// → pipeline-runner\n//\n// Coming in 2b–2f: engine choice, probedBestEngine, pipeline step tree.\n\n// Engine chain: runtime → backend → hardware device. Options for each\n// level depend on the current value of the level above, so the addon\n// overrides `getGlobalSettings()` to re-compute the select lists at\n// read time.\n\ntype EngineRuntime = 'node' | 'python'\n\nconst RUNTIMES: ReadonlyArray<{ value: EngineRuntime; label: string }> = [\n { value: 'python', label: 'Python (CoreML / OpenVINO / ONNX Runtime)' },\n { value: 'node', label: 'Node.js (onnxruntime-node)' },\n]\n\nconst BACKENDS_BY_RUNTIME: Record<EngineRuntime, ReadonlyArray<{ value: string; label: string }>> = {\n python: [\n { value: 'coreml', label: 'CoreML' },\n { value: 'openvino', label: 'OpenVINO' },\n { value: 'onnx', label: 'ONNX Runtime' },\n ],\n node: [\n { value: 'cpu', label: 'CPU (onnxruntime-node)' },\n ],\n}\n\nconst DEVICES_BY_BACKEND: Record<string, ReadonlyArray<{ value: string; label: string }>> = {\n coreml: [{ value: 'all', label: 'All (ANE + GPU + CPU)' }, { value: 'ane', label: 'Apple Neural Engine' }, { value: 'gpu', label: 'GPU' }, { value: 'cpu', label: 'CPU' }],\n openvino: [{ value: 'auto', label: 'Auto' }, { value: 'cpu', label: 'CPU' }, { value: 'gpu', label: 'GPU' }, { value: 'npu', label: 'NPU' }],\n onnx: [{ value: 'cpu', label: 'CPU' }, { value: 'cuda', label: 'CUDA' }, { value: 'coreml', label: 'CoreML EP' }],\n cpu: [{ value: 'cpu', label: 'CPU' }],\n}\n\nconst BACKEND_TO_FORMAT: Record<string, 'onnx' | 'coreml' | 'openvino'> = {\n cpu: 'onnx',\n coreml: 'coreml',\n openvino: 'openvino',\n onnx: 'onnx',\n}\n\ntype BatchMode = 'none' | 'list' | 'window'\n\ninterface DetectionPipelineConfig {\n /**\n * Python inference-pool worker concurrency.\n * `0` = runtime-aware auto default (resolved in engine-factory):\n * coreml: 1 — matrix bench shows >1 hurts (CoreML serializes per-context).\n * onnxruntime: 4 — InferenceSession.run is thread-safe; scales with cores.\n * openvino: 1 — OV runtime manages internal infer-request pool.\n * Non-zero values override the auto default.\n */\n readonly concurrency: number\n /**\n * Pool batch dispatch strategy. Set per-backend; defaults injected by\n * `getGlobalSettings(overlay)` based on `engineBackend`.\n * - 'none' : one predict_fn per item (no batching)\n * - 'list' : MSG_INFER_BATCH dispatched as `model.predict([{},{}...])`\n * - 'window' : per-model accumulator coalesces concurrent MSG_INFER_RAW\n * calls within `windowMs`, flushes via single batched predict.\n */\n readonly batchMode: BatchMode | ''\n /** Window-mode accumulator flush window (ms). Only honored when batchMode='window'. 0 = auto. */\n readonly windowMs: number\n /** Max items per batched predict call. Only honored when batchMode!='none'. 0 = auto. */\n readonly maxBatchSize: number\n /** OpenVINO num_streams hint. 0 = auto. */\n readonly numStreams: number\n /** ONNX Runtime intra-op thread count. 0 = auto. */\n readonly intraOpThreads: number\n /**\n * Number of independent Python subprocess workers in the inference\n * pool. Each worker holds its own MLModel copy + predict context;\n * inference dispatches round-robin across them. 0 = runtime-aware\n * default (CoreML: 2 — required to lift the single-context ANE cap\n * on multi-camera workloads; non-CoreML backends: 1, since they\n * manage their own internal parallelism). Model load/unload\n * propagate to all workers, so RAM cost scales linearly with\n * `numWorkers × modelSize`.\n */\n readonly numWorkers: number\n /** Chain level 1 — Python vs Node host runtime. */\n readonly engineRuntime: EngineRuntime\n /** Chain level 2 — backend within the runtime. */\n readonly engineBackend: string\n /** Chain level 3 — hardware device within the backend. */\n readonly engineDevice: string\n /**\n * Readable snapshot of the last-probed platform-best engine. Format:\n * `\"runtime/backend/device\"`. Set by `reprobeEngine()`; the UI shows\n * it next to the cascade as a hint + provides a \"Re-probe\" button.\n */\n readonly probedBestEngine: string\n}\n\n/**\n * Per-backend tuning defaults — derived from the matrix sweep\n * (`scripts/bench-pool-matrix.py` + `bench-coreml-patterns.py`).\n * Applied by `getGlobalSettings(overlay)` so the UI defaults track\n * the currently-selected backend instead of a single global value.\n */\nconst TUNING_DEFAULTS: Record<string, Partial<DetectionPipelineConfig>> = {\n // CoreML concurrency=2 lifts the single-pool cap from ~64 fps (cams=1\n // conc=1) to ~99 fps (cams=4 conc=2) per `bench-pool-matrix.py` 36-combo\n // sweep at 640×640 yolov9s. The window batch mode coalesces concurrent\n // inferRaw calls into one `model.predict([list])` regardless, so the\n // extra worker mostly accelerates the parallel preprocess stage that\n // becomes the bottleneck once the predict path is batched.\n // windowMs=8 + maxBatchSize=8: short window keeps tail latency low; cap=8\n // covers up to 8 concurrent cameras coalescing into a single CoreML predict\n // (live setups peak at 5-6 cams; ANE handles batch=8 in ~15-20ms wall).\n // With server-side raw-frame cache (cacheBenchFrame), workers fire in\n // near-lock-step after each batch return, so the window typically fills\n // within 1-2ms anyway — the 8ms timeout is a safety net for desync, not\n // the steady-state path.\n // numWorkers=2: each subprocess holds its own MLModel + ANE dispatch\n // context. With M-series ANE serializing inside a single process, two\n // independent contexts let predict + preprocess overlap across cameras\n // (~+30-90% on 4-8 simulated cams in `bench-pool-inprocess.mts`).\n // Higher counts saturate the single ANE channel and degrade — 2 is the\n // sweet spot on M-series.\n coreml: { concurrency: 2, batchMode: 'window', windowMs: 30, maxBatchSize: 8, numWorkers: 2 },\n openvino: { concurrency: 1, batchMode: 'none', numStreams: 0 },\n onnx: { concurrency: 4, batchMode: 'list', maxBatchSize: 8, intraOpThreads: 0 },\n cpu: { concurrency: 4, batchMode: 'list', maxBatchSize: 8, intraOpThreads: 0 },\n}\n\nconst DEFAULT_CONFIG: DetectionPipelineConfig = {\n concurrency: 0,\n batchMode: '',\n windowMs: 0,\n maxBatchSize: 0,\n numStreams: 0,\n intraOpThreads: 0,\n numWorkers: 0,\n engineRuntime: 'python',\n engineBackend: 'coreml',\n engineDevice: 'all',\n probedBestEngine: '',\n}\n\n/** Derive the model-format from a backend value. Called by the provider. */\nexport function backendToFormat(backend: string): 'onnx' | 'coreml' | 'openvino' {\n return BACKEND_TO_FORMAT[backend] ?? 'onnx'\n}\n\n/**\n * Cadence for the per-node engine-inventory snapshot. Engines change\n * slowly (manual spin/kill, idle eviction, attach/detach). 5 s strikes\n * a balance between dashboard freshness and bus traffic.\n */\nconst ENGINE_METRICS_SNAPSHOT_INTERVAL_MS = 5_000\n/** Heartbeat fallback when the engine inventory hasn't changed. */\nconst ENGINE_METRICS_HEARTBEAT_MS = 60_000\n\n/** Engine list dedup key — drops `idleMs` (monotonic counter on\n * idle engines) so the defer matches as long as the inventory\n * shape (engineKey / modelsLoaded / inUseByCameras / kind) is\n * stable. The bus-emitted payload still includes idleMs verbatim. */\nfunction stableEngineKey(engines: readonly unknown[]): string {\n const stripped = engines.map((e) => {\n if (!e || typeof e !== 'object') return e\n const obj = e as Record<string, unknown>\n const { idleMs: _idleMs, ...rest } = obj\n return rest\n })\n return JSON.stringify(stripped)\n}\n\n/**\n * Tuning fields that bind into the Python pool at construction. Any\n * change requires a pool respawn to take effect (the executor caches\n * `tuning` at engine-factory build time). `onConfigChanged` watches\n * these and rebuilds the provider when they flip.\n */\nconst POOL_BOUND_KEYS = [\n 'concurrency',\n 'batchMode',\n 'windowMs',\n 'maxBatchSize',\n 'numStreams',\n 'intraOpThreads',\n 'numWorkers',\n] as const\n\nexport default class DetectionPipelineAddon extends BaseAddon<DetectionPipelineConfig> {\n private provider: DetectionPipelineProvider | null = null\n private engineMetricsTimer: ReturnType<typeof setInterval> | null = null\n /** Snapshot-equality cache for engine-metrics emit. Most ticks\n * the engine inventory is unchanged (no model load/unload), so\n * we skip the bus emit and let the heartbeat re-emit at\n * ENGINE_METRICS_HEARTBEAT_MS for liveness. */\n private lastEmittedEngineSnapshot: { json: string; emittedAt: number } | null = null\n /**\n * Last-applied tuning snapshot, taken at the end of `onInitialize`\n * and refreshed at the end of `onConfigChanged`. Lets the change\n * handler diff the current config against the snapshot and decide\n * whether a pool respawn is necessary — most config edits (engine\n * cascade, audio settings) don't touch the tuning fields and don't\n * deserve a multi-second model reload.\n */\n private lastAppliedPoolConfig: Partial<DetectionPipelineConfig> = {}\n /**\n * Embedded Python path resolved once at boot via\n * `ctx.deps.ensurePython()`. Empty string means the download failed\n * or `runtime: 'node'` was selected — the provider's\n * `ensureBackendDeps` and EngineFactory's `initPythonPool` raise a\n * clear error in that case.\n */\n private pythonPath: string = ''\n /**\n * Absolute path to the addon's bundled `python/` dir (resolved once\n * at boot), passed through to the provider so per-backend\n * `requirements-<runtime>.txt` files can be located at lazy-install\n * time.\n */\n private pythonAddonDir: string = ''\n\n constructor() { super(DEFAULT_CONFIG) }\n\n protected globalSettingsSchema() {\n // Base (static) schema — the dynamic cascade options are injected\n // by the `getGlobalSettings()` override below based on the current\n // runtime + backend values. Keeping a static base lets the\n // addon-registry introspection machinery (which calls this method\n // without a live store) still see every field.\n return this.schema({\n sections: [\n {\n id: 'engine',\n title: 'Object detection',\n tab: 'engine',\n // Render order within the \"Inference Engine\" tab. Lower goes\n // first; detection sits at 0 (operator-priority section —\n // vision is the dominant load on the node), audio-analyzer's\n // `audio-engine` follows at 10.\n order: 0,\n // Co-located on the \"Inference Engine\" tab with `audio-analyzer`'s\n // `audio-engine` section. This one drives vision inference\n // (object detection + downstream face/plate steps that share the\n // same runtime); the audio-analyzer one drives audio\n // classification. The title is intentionally \"Object detection\"\n // (not \"Vision\" or \"Detection engine\") because every section\n // shown on this tab is an inference engine — the modality is\n // what disambiguates them.\n description:\n 'Vision-detection backend (object detection, face detection, plate OCR). ' +\n 'Chain: runtime → backend → hardware. Backend options depend on runtime; ' +\n 'hardware options depend on backend. All three restart the addon on change.',\n // Field order — `probedBestEngine` lives at the top so the\n // operator sees the auto-detected hint first and can compare\n // it against their override below at a glance. Same\n // convention as audio-analyzer's `audio-engine` section.\n fields: [\n this.field({\n type: 'text',\n key: 'probedBestEngine',\n label: 'Probed best',\n description:\n 'Auto-detected best engine for this host (format: runtime/backend/device). ' +\n 'Click the refresh icon to re-run the probe — the detected values get written back into the three fields below.',\n readonlyField: true,\n default: '',\n actions: [\n { action: 'reprobe-engine', icon: 'refresh-cw', tooltip: 'Re-probe engine' },\n ],\n }),\n this.field({\n type: 'select',\n key: 'engineRuntime',\n label: 'Runtime',\n options: [...RUNTIMES],\n default: DEFAULT_CONFIG.engineRuntime,\n immediate: true,\n requiresRestart: true,\n }),\n this.field({\n type: 'select',\n key: 'engineBackend',\n label: 'Backend',\n // Base schema uses the python backend list — the dynamic\n // options come from `getGlobalSettings()`.\n options: [...BACKENDS_BY_RUNTIME.python],\n default: DEFAULT_CONFIG.engineBackend,\n immediate: true,\n requiresRestart: true,\n }),\n this.field({\n type: 'select',\n key: 'engineDevice',\n label: 'Hardware device',\n options: [...DEVICES_BY_BACKEND.coreml!],\n default: DEFAULT_CONFIG.engineDevice,\n immediate: true,\n requiresRestart: true,\n }),\n ],\n },\n {\n id: 'inference-executor',\n title: 'Inference executor',\n tab: 'scheduler',\n fields: [\n this.field({\n type: 'select',\n key: 'batchMode',\n label: 'Batch dispatch mode',\n description:\n 'How the Python pool dispatches predicts. `none` = one call per frame. ' +\n '`window` = per-model accumulator coalesces concurrent calls within Window (ms). ' +\n 'Auto uses the matrix-sweep default for the current backend.',\n options: [\n { value: '', label: 'Auto (backend default)' },\n { value: 'none', label: 'None (one call per frame)' },\n { value: 'list', label: 'List (per-batch model.predict([list]))' },\n { value: 'window', label: 'Window (cross-camera accumulator)' },\n ],\n default: '',\n showWhen: { field: 'engineBackend', in: ['coreml', 'onnx', 'cpu'] },\n requiresRestart: true,\n }),\n this.field({\n type: 'slider',\n key: 'windowMs',\n label: 'Window flush (ms)',\n description:\n 'Window-mode accumulator: max time to wait for additional concurrent predicts before flushing. ' +\n 'Lower = lower latency, higher = better throughput. Auto = backend default.',\n min: 0,\n max: 50,\n step: 1,\n default: undefined,\n showValue: true,\n nullable: true,\n nullLabel: 'Auto',\n showWhen: { field: 'batchMode', notEquals: 'none' },\n requiresRestart: true,\n }),\n this.field({\n type: 'slider',\n key: 'maxBatchSize',\n label: 'Max batch size',\n description:\n 'Cap on items per batched predict call. Auto = backend default.',\n min: 1,\n max: 16,\n step: 1,\n default: undefined,\n showValue: true,\n nullable: true,\n nullLabel: 'Auto',\n showWhen: { field: 'batchMode', notEquals: 'none' },\n requiresRestart: true,\n }),\n this.field({\n type: 'slider',\n key: 'concurrency',\n label: 'Pool workers',\n description:\n 'Python inference-pool prepare + predict threads. ' +\n 'Auto = backend default (CoreML: 2, OpenVINO: 1, ONNX: 4).',\n min: 1,\n max: 8,\n step: 1,\n default: undefined,\n showValue: true,\n nullable: true,\n nullLabel: 'Auto',\n requiresRestart: true,\n }),\n this.field({\n type: 'slider',\n key: 'numWorkers',\n label: 'Pool subprocesses',\n description:\n 'Number of independent Python pool subprocesses. Each ' +\n 'holds its own MLModel copy + ANE/GPU/CPU dispatch context. ' +\n 'Inference round-robins across them. RAM cost scales linearly. ' +\n 'Auto = 2 for CoreML (lifts the single-context ANE cap on ' +\n 'multi-camera workloads), 1 for ONNX/OpenVINO (they manage ' +\n 'their own internal parallelism).',\n min: 1,\n max: 8,\n step: 1,\n default: undefined,\n showValue: true,\n nullable: true,\n nullLabel: 'Auto',\n requiresRestart: true,\n }),\n ],\n },\n ],\n })\n }\n\n /**\n * Override to inject dynamic backend + device options based on the\n * currently-stored runtime + backend. The base schema ships with\n * placeholder lists; here we replace them with the valid subset for\n * whatever the operator has picked. Combined with `immediate: true`\n * on the selects, the UI can refetch after each change and see the\n * next level's options narrow down.\n */\n override async getGlobalSettings(overlay?: Record<string, unknown>): Promise<ConfigUISchemaWithValues> {\n // Pre-boot introspection path: addon-registry may call this before\n // `initialize()` has seeded `this._ctx` (e.g. to advertise the\n // settings surface during boot discovery). Fall back to `ctxIfReady`\n // so a null ctx yields a schema hydrated against an empty store\n // (schema defaults) rather than throwing.\n const ctx = this.ctxIfReady\n const stored = ctx?.settings ? ((await ctx.settings.readAddonStore()) ?? {}) : {}\n // Overlay lets override-mode UIs ask \"hydrate as if these values\n // were persisted\" without writing. Drives the cascade dropdowns\n // (runtime → backend → device) off the operator's tentative\n // choice rather than the saved one.\n const merged = overlay ? { ...stored, ...overlay } : stored\n const runtime: EngineRuntime = merged.engineRuntime === 'node' ? 'node' : 'python'\n // Filter the static backend catalog by what the platform probe\n // actually reports as available on this node. Without this, an\n // operator could pick (e.g.) OpenVINO on a macOS box that never had\n // the Python module installed — selecting it then crashed the\n // inference-pool subprocess with `ModuleNotFoundError` which tore\n // the whole detection-pipeline worker down and knocked out the\n // pipeline-executor cap for every caller (Pipeline tab, Engine tab,\n // benchmark). Probe lookup is tolerant: when the capability isn't\n // wired yet we fall back to the full catalog so the UI still\n // renders options (mirrors the pre-filter behavior).\n const availableBackends = await this.resolveAvailableBackends(ctx, runtime)\n const runtimeBackends = availableBackends.length > 0\n ? BACKENDS_BY_RUNTIME[runtime].filter((b) => availableBackends.includes(b.value))\n : BACKENDS_BY_RUNTIME[runtime]\n const storedBackend = typeof merged.engineBackend === 'string' ? merged.engineBackend : ''\n const backend = runtimeBackends.find((b) => b.value === storedBackend)?.value ?? runtimeBackends[0]?.value ?? 'coreml'\n const deviceOptions = DEVICES_BY_BACKEND[backend] ?? [{ value: 'cpu', label: 'CPU' }]\n const storedDevice = typeof merged.engineDevice === 'string' ? merged.engineDevice : ''\n const device = deviceOptions.find((d) => d.value === storedDevice)?.value ?? deviceOptions[0]?.value ?? 'cpu'\n\n // Re-project raw values onto the current cascade: if backend /\n // device from the store are invalid for the active runtime →\n // backend pair, snap them to the first valid option. Prevents\n // the UI from rendering a stale select value (e.g. `coreml`\n // when runtime just flipped to `node` whose backends don't\n // include `coreml`) which shows as an empty dropdown.\n const raw = { ...merged, engineBackend: backend, engineDevice: device }\n\n const schema = this.globalSettingsSchema()\n if (!schema) return { sections: [] }\n\n // Per-backend tuning defaults — pulled from the matrix-derived\n // TUNING_DEFAULTS map so the UI suggests values that won the bench.\n // Only injected as field defaults; persisted values still take\n // precedence (hydrateSchema lays raw values over defaults).\n const tuning = TUNING_DEFAULTS[backend] ?? {}\n\n // Clone + inject dynamic options + per-backend defaults.\n const patched = {\n ...schema,\n sections: schema.sections.map((section) => ({\n ...section,\n fields: section.fields.map((field) => {\n if (field.type === 'select') {\n if (field.key === 'engineBackend') {\n return { ...field, options: [...runtimeBackends] }\n }\n if (field.key === 'engineDevice') {\n return { ...field, options: [...deviceOptions], default: deviceOptions[0]?.value ?? 'cpu' }\n }\n if (field.key === 'batchMode' && tuning.batchMode !== undefined) {\n return { ...field, default: tuning.batchMode }\n }\n }\n if (field.type === 'slider' && 'key' in field) {\n const tuned = (tuning as Record<string, unknown>)[field.key]\n // Per-backend slider max overrides — CoreML serializes per-context\n // so >4 prepare workers add overhead without throughput. OpenVINO/\n // ONNX inherit the schema default (32) which is already reasonable.\n const sliderField = field\n let patched = typeof tuned === 'number'\n ? { ...sliderField, default: tuned }\n : sliderField\n if (sliderField.key === 'concurrency' && backend === 'coreml') {\n patched = { ...patched, max: 4 }\n }\n return patched\n }\n return field\n }),\n })),\n }\n return hydrateSchema(patched, raw)\n }\n\n /**\n * Ask the platform-probe cap which backends are actually installable\n * on this node (checks for `openvino`, `coremltools`, `onnxruntime`\n * Python modules + hardware presence). Returns a subset of the static\n * catalog; an empty list signals the probe wasn't available so the\n * caller should fall back to the full catalog rather than hiding\n * every option.\n */\n private async resolveAvailableBackends(\n ctx: ReturnType<() => typeof this.ctxIfReady>,\n runtime: EngineRuntime,\n ): Promise<readonly string[]> {\n try {\n const api = ctx?.api\n if (!api) return []\n const caps = await api.platformProbe.getCapabilities.query()\n if (!caps?.scores) return []\n const out = new Set<string>()\n for (const s of caps.scores) {\n if (s.runtime !== runtime) continue\n if (!s.available) continue\n out.add(s.backend)\n }\n return [...out]\n } catch {\n return []\n }\n }\n\n /**\n * Resolve the effective pool tuning for the configured backend.\n *\n * Reads `TUNING_DEFAULTS[backend]` and ignores any persisted override\n * for `concurrency / batchMode / windowMs / maxBatchSize / numStreams /\n * intraOpThreads`. Stored values are quietly discarded so an old\n * suboptimal user-override (saved when the UI exposed these knobs)\n * cannot resurrect itself after a restart.\n *\n * The matrix sweep (`bench-pool-matrix.py` + `bench-coreml-patterns.py`)\n * already chose the best per-backend combo; the operator no longer has\n * a reason to disagree.\n */\n private resolveBackendTuning(): {\n concurrency: number\n batchMode: 'none' | 'list' | 'window'\n windowMs: number\n maxBatchSize: number\n numStreams: number\n intraOpThreads: number\n numWorkers: number\n } {\n const backend = this.config.engineBackend ?? DEFAULT_CONFIG.engineBackend\n const t = TUNING_DEFAULTS[backend] ?? TUNING_DEFAULTS['onnx']!\n // User overrides from settings UI take precedence when explicitly set.\n // null / undefined / '' / 0 → fall back to TUNING_DEFAULTS.\n const cfg = this.config as Record<string, unknown>\n const pickNum = (userVal: unknown, defaultVal: number): number => {\n if (typeof userVal !== 'number' || userVal <= 0) return defaultVal\n return userVal\n }\n const pickStr = <T extends string>(userVal: unknown, defaultVal: T): T => {\n if (typeof userVal !== 'string' || userVal === '') return defaultVal\n return userVal as T\n }\n return {\n concurrency: pickNum(cfg.concurrency, t.concurrency ?? 1),\n batchMode: pickStr(cfg.batchMode, t.batchMode ?? 'window') as 'none' | 'list' | 'window',\n windowMs: pickNum(cfg.windowMs, t.windowMs ?? 2),\n maxBatchSize: pickNum(cfg.maxBatchSize, t.maxBatchSize ?? 8),\n numStreams: pickNum(cfg.numStreams, t.numStreams ?? 0),\n intraOpThreads: pickNum(cfg.intraOpThreads, t.intraOpThreads ?? 0),\n numWorkers: pickNum(cfg.numWorkers, (t as Record<string, unknown>)['numWorkers'] as number ?? 1),\n }\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const modelsDir = await this.ctx.api.storage.resolve.query({ location: 'models', relativePath: '' })\n .catch(() => 'camstack-data/models')\n\n if (!this.ctx.settings) {\n throw new Error('DetectionPipelineAddon: ctx.settings not available')\n }\n\n // Embedded Python — base pip requirements (numpy + Pillow) are\n // installed by the kernel BEFORE `onInitialize` runs, driven by the\n // `python.requirements` field in this addon's package.json manifest\n // (see `kernel/src/deps/manifest-python-deps.ts`). Here we only\n // resolve the bin path + addon python/ dir for downstream wiring.\n // Backend-specific deps (coremltools / onnxruntime / openvino) are\n // installed lazily by the provider before the matching\n // `EngineFactory` is constructed (see `provider.ts:ensureBackendDeps`).\n this.pythonAddonDir = resolveAddonPythonDir()\n const pythonPath = await this.ctx.deps.ensurePython()\n if (pythonPath) {\n this.pythonPath = pythonPath\n } else {\n this.ctx.logger.warn(\n 'Embedded Python unavailable — runtime=\"python\" pipelines will fail until the download succeeds.',\n )\n }\n\n const effectiveTuning = this.resolveBackendTuning()\n this.provider = new DetectionPipelineProvider(\n this.ctx.settings,\n modelsDir,\n this.ctx.logger,\n this.ctx.eventBus ?? null,\n () => ({ sections: [] }),\n {\n concurrency: effectiveTuning.concurrency,\n tuning: {\n batchMode: effectiveTuning.batchMode,\n windowMs: effectiveTuning.windowMs,\n maxBatchSize: effectiveTuning.maxBatchSize,\n numStreams: effectiveTuning.numStreams,\n intraOpThreads: effectiveTuning.intraOpThreads,\n },\n numWorkers: effectiveTuning.numWorkers,\n pythonPath: this.pythonPath,\n pythonAddonDir: this.pythonAddonDir,\n },\n )\n await this.provider.init()\n await this.provider.setApi(this.ctx)\n\n // Auto-seed `probedBestEngine` if empty — informational hint only.\n // Operator cascade values (engineRuntime/Backend/Device) are left\n // untouched; see `reprobeEngine` impl.\n if (!this.config.probedBestEngine) {\n await this.provider.reprobeEngine().catch((err: unknown) => {\n this.ctx.logger.warn('auto-reprobe engine failed', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n // Pre-warm the Python inference pool before returning. `BaseAddon`\n // auto-emits `system.ready-state → ready` right after `onInitialize`\n // resolves, so consumers that `awaitReady('pipeline-executor', …)`\n // get a truly-callable signal and never hit\n // `SharedInferencePool: not initialized` on the first frame.\n await this.provider.warmPool()\n\n // Periodic engine-inventory snapshot. UI Engines tab subscribes to\n // `pipeline.engine-metrics-snapshot` instead of polling\n // `pipelineExecutor.listLoadedEngines`.\n this.engineMetricsTimer = setInterval(\n () => this.emitEngineMetricsSnapshot(),\n ENGINE_METRICS_SNAPSHOT_INTERVAL_MS,\n )\n\n this.lastAppliedPoolConfig = this.snapshotPoolConfig()\n\n // Split registration: the system cap holds the compute path\n // (engines / templates / models / runPipeline); the device-scoped\n // detection-pipeline cap is the bindable row every camera sees in\n // the device-manager bindings UI. The same provider instance serves\n // both — detection-pipeline's exposesDeviceSettings:true contract\n // (3 contribution methods) is already implemented on the provider.\n return [\n { capability: pipelineExecutorCapability, provider: this.provider },\n {\n capability: detectionPipelineCapability,\n provider: this.provider,\n kind: 'wrapper',\n defaultActive: true,\n },\n ]\n }\n\n /**\n * Emit a single `pipeline.engine-metrics-snapshot` event with the\n * current engine inventory for this node. Source of truth is the\n * provider's `listLoadedEngines` method (same data the cap returns).\n */\n private emitEngineMetricsSnapshot(): void {\n const provider = this.provider\n const eventBus = this.ctx.eventBus\n if (!provider || !eventBus) return\n void Promise.resolve(provider.listLoadedEngines()).then((engines) => {\n const rawNodeId = this.ctx.kernel.localNodeId ?? this.ctx.id\n const nodeId = rawNodeId.includes('/') ? rawNodeId.split('/')[0]! : rawNodeId\n const timestamp = Date.now()\n // Snapshot-equality skip — engine inventory rarely changes\n // (model load/unload). Same pattern as pipeline-runner's\n // emitMetricsSnapshot.\n //\n // `idleMs` ticks up every poll on idle engines — strip it\n // from the dedup key so the defer actually fires. Full\n // payload still carries the live value when an emit happens.\n const json = stableEngineKey(engines)\n const prev = this.lastEmittedEngineSnapshot\n const heartbeatDue = !prev || timestamp - prev.emittedAt >= ENGINE_METRICS_HEARTBEAT_MS\n if (prev && prev.json === json && !heartbeatDue) return\n this.lastEmittedEngineSnapshot = { json, emittedAt: timestamp }\n eventBus.emit(createEvent(\n EventCategory.PipelineEngineMetricsSnapshot,\n { type: 'node', id: nodeId, nodeId },\n { nodeId, engines, timestamp },\n ))\n }).catch(() => {\n // Soft-fail: dashboards tolerate a missing tick.\n })\n }\n\n getModelCatalog(): ModelCatalogEntry[] {\n return ALL_STEPS.flatMap(s => [...s.models])\n }\n\n protected async onShutdown(): Promise<void> {\n if (this.engineMetricsTimer) {\n clearInterval(this.engineMetricsTimer)\n this.engineMetricsTimer = null\n }\n if (this.provider) {\n await this.provider.shutdown()\n this.provider = null\n }\n }\n\n /**\n * Snapshot the pool-bound subset of the EFFECTIVE tuning (post-\n * `resolveBackendTuning`). Stored config values for these fields are\n * ignored, so this snapshot only changes when `engineBackend` flips\n * onto a different `TUNING_DEFAULTS` row.\n */\n private snapshotPoolConfig(): Partial<DetectionPipelineConfig> {\n const t = this.resolveBackendTuning()\n const snapshot: Partial<DetectionPipelineConfig> = {\n concurrency: t.concurrency ?? 0,\n batchMode: t.batchMode,\n windowMs: t.windowMs,\n maxBatchSize: t.maxBatchSize,\n numStreams: t.numStreams,\n intraOpThreads: t.intraOpThreads,\n numWorkers: t.numWorkers,\n }\n return snapshot\n }\n\n private poolConfigChanged(): boolean {\n const next: Record<string, unknown> = this.snapshotPoolConfig()\n const prev: Record<string, unknown> = this.lastAppliedPoolConfig\n for (const k of POOL_BOUND_KEYS) {\n if (next[k] !== prev[k]) return true\n }\n return false\n }\n\n /**\n * BaseAddon calls `onConfigChanged` after every settings write.\n * Group-runner addons can't honour the schema's `requiresRestart:\n * true` flag (the restart cap returns \"process not found\" for\n * processes spawned in a kernel group worker). To make tuning\n * changes actually take effect, watch the pool-bound subset and\n * respawn the provider in place when it flips.\n *\n * Engine cascade (`engineRuntime/Backend/Device`) and audio\n * settings don't require this — those still rely on the addon\n * lifecycle (engineFactory rebuild on next runPipeline).\n */\n protected async onConfigChanged(): Promise<void> {\n if (!this.poolConfigChanged()) return\n if (!this.provider) {\n this.lastAppliedPoolConfig = this.snapshotPoolConfig()\n return\n }\n this.ctx.logger.info('detection-pipeline tuning changed — respawning pool', {\n meta: {\n prev: this.lastAppliedPoolConfig,\n next: this.snapshotPoolConfig(),\n },\n })\n try {\n await this.provider.shutdown()\n } catch (err) {\n this.ctx.logger.warn('provider shutdown failed during tuning respawn', {\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n }\n // Rebuild provider with the fresh tuning snapshot. Mirrors the\n // construction path in `onInitialize` so the new pool comes up\n // with the same dependency wiring.\n const modelsDir = await this.ctx.api.storage.resolve.query({ location: 'models', relativePath: '' })\n .catch(() => 'camstack-data/models')\n if (!this.ctx.settings) {\n throw new Error('DetectionPipelineAddon: ctx.settings not available during respawn')\n }\n const effectiveTuning = this.resolveBackendTuning()\n this.provider = new DetectionPipelineProvider(\n this.ctx.settings,\n modelsDir,\n this.ctx.logger,\n this.ctx.eventBus ?? null,\n () => ({ sections: [] }),\n {\n concurrency: effectiveTuning.concurrency,\n tuning: {\n batchMode: effectiveTuning.batchMode,\n windowMs: effectiveTuning.windowMs,\n maxBatchSize: effectiveTuning.maxBatchSize,\n numStreams: effectiveTuning.numStreams,\n intraOpThreads: effectiveTuning.intraOpThreads,\n },\n numWorkers: effectiveTuning.numWorkers,\n pythonPath: this.pythonPath,\n pythonAddonDir: this.pythonAddonDir,\n },\n )\n await this.provider.init()\n await this.provider.setApi(this.ctx)\n await this.provider.warmPool()\n this.lastAppliedPoolConfig = this.snapshotPoolConfig()\n this.ctx.logger.info('detection-pipeline pool respawn complete')\n }\n}\n","/**\n * DetectionPipelineProvider — implements IPipelineExecutorProvider.\n *\n * This is the main provider that consumers (DetectionWiring, Benchmark, tRPC)\n * interact with. It manages the engine factory, pipeline executor, and config persistence.\n */\nimport type {\n IPipelineExecutorProvider,\n PipelineEngineChoice,\n PipelineSchema,\n AvailableEngine,\n PipelineDefaultStep,\n PipelineConfig,\n PipelineRunInput,\n FrameResult,\n DetectorOutput,\n FrameInput,\n ModelFormat,\n PipelineTemplate,\n PipelineTemplateStep,\n ConfigUISchema,\n ConfigUISchemaWithValues,\n InferenceCapabilities,\n ModelAvailability,\n ModelFormatStatus,\n AddonCapabilityInfo,\n BackendInfo,\n PythonBackendInfo,\n IAddonResolver,\n IScopedLogger,\n EngineDeviceInfo,\n AudioChunkInput,\n AddonContext,\n InferenceInput,\n RawFrameFormat,\n DeviceProxy,\n ObjectDetection,\n} from '@camstack/types'\nimport { EventCategory, parseJsonUnknown, YAMNET_TO_MACRO, APPLE_SA_TO_MACRO, createEvent, evaluateZoneRules, type IEventBus , errMsg } from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\n// eslint-disable-next-line no-restricted-imports -- model-download helpers are a build-time dep, not a runtime addon coupling. Same waiver `addon-embedding-encoder` uses for `ModelDownloadService`. The duplicate copy that lived at `./engine/model-downloader.ts` was deleted; canonical implementation lives in `@camstack/core/download/model-downloader`.\nimport { ensureModel, isModelDownloaded, deleteModelFromDisk } from '@camstack/core'\nimport { EngineFactory, BACKEND_TO_POOL_RUNTIME } from './engine/engine-factory.js'\nimport { PipelineExecutor } from './pipeline/executor.js'\nimport { buildExecutableTree } from './pipeline/tree-builder.js'\nimport { ALL_PIPELINE_STEPS, getStepDefinition, getDefaultModelForFormat } from './registry/step-definitions.js'\nimport { flattenEnabledVideoSteps } from './engine/step-utils.js'\nimport { backendToFormat } from './index.js'\n\n// ---------------------------------------------------------------------------\n// Config persistence keys (flat — no dot-path collision)\n// ---------------------------------------------------------------------------\n\nconst KEY_STEPS = 'pipelineSteps'\nconst KEY_ENGINE = 'pipelineEngine'\nconst KEY_TEMPLATES = 'pipelineTemplates'\n\n// ---------------------------------------------------------------------------\n// Python module name per inference backend (informational; surfaced via\n// `getCapabilities`). The actual subprocess pool checks for these at boot.\n// ---------------------------------------------------------------------------\n\nfunction pythonModuleForBackend(backend: string): string {\n switch (backend) {\n case 'coreml': return 'coremltools'\n case 'openvino': return 'openvino'\n case 'onnx': return 'onnxruntime'\n default: return backend\n }\n}\n\n/**\n * Minimal `FrameResult` builder for the batched fast path. Mirrors the\n * subset of fields produced by the full executor pipeline that the\n * Scrypted-equivalent benchmark cares about (one detection list per\n * frame). Trees with crop children, per-step settings filtering, or\n * non-detection step shapes always go through `runPipeline` and use\n * the canonical `result-assembler.ts` instead — this fast path is\n * intentionally narrow.\n */\nfunction assembleBatchedFrameResult(\n raw: Record<string, unknown>,\n frame: FrameInput,\n modelId: string,\n deviceId: number,\n inferenceMs: number,\n wallMs: number,\n): FrameResult {\n const dets = Array.isArray(raw['detections']) ? (raw['detections'] as Array<Record<string, unknown>>) : []\n const frameId = `batch-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n return {\n kind: 'frame',\n frameId,\n deviceId,\n timestamp: Date.now(),\n width: frame.width,\n height: frame.height,\n detections: dets.map((d, i) => {\n const cls = typeof d['class'] === 'string' ? d['class'] : 'unknown'\n const score = typeof d['score'] === 'number' ? d['score'] : 0\n const bbox = (d['bbox'] ?? {}) as Record<string, unknown>\n const x = typeof bbox['x'] === 'number' ? bbox['x'] : 0\n const y = typeof bbox['y'] === 'number' ? bbox['y'] : 0\n const w = typeof bbox['w'] === 'number' ? bbox['w'] : 0\n const h = typeof bbox['h'] === 'number' ? bbox['h'] : 0\n return {\n id: `${frameId}-${i}`,\n kind: 'first-level' as const,\n macroClass: cls,\n score,\n bbox: { x, y, width: w, height: h },\n labels: [],\n }\n }),\n debug: {\n totalInferenceMs: typeof raw['inferenceMs'] === 'number' ? raw['inferenceMs'] : inferenceMs,\n // Server-side wall — full provider-internal cost incl. preprocess +\n // predict + IPC roundtrip. UI uses this to attribute transport\n // overhead vs server work; without it the UI sees serverWallMs=0\n // and treats everything as transport.\n wallMs: Math.round(wallMs * 100) / 100,\n stepTimings: [{ source: modelId, modelId, ms: inferenceMs, detectionCount: dets.length }],\n // Python-pool stage split — surfaces preprocess vs predict separately\n // so the bench UI can pinpoint where the 50ms inference budget lands.\n ...(typeof raw['preprocessMs'] === 'number' ? { preprocessMs: raw['preprocessMs'] } : {}),\n ...(typeof raw['predictMs'] === 'number' ? { predictMs: raw['predictMs'] } : {}),\n ...(typeof raw['batchSize'] === 'number' ? { batchSize: raw['batchSize'] } : {}),\n },\n }\n}\n// KEY_DEVICE_STEPS_PREFIX removed in Phase 4b — per-device pipeline\n// content lives on the orchestrator (`pipelineOrchestrator.pipelines/\n// <deviceId>`) now. The executor keeps its global KEY_STEPS/KEY_ENGINE\n// for the one-shot benchmark and Settings bootstrap paths until Phase 6\n// fully removes the legacy pipeline page.\n\n/**\n * Typed stored-config reader — reads a JSON string this provider\n * itself wrote, performs a runtime shape check against the caller\n * predicate, and returns the narrowed value or null.\n *\n * All persisted shapes in this file are written by the provider, so\n * the predicates can be minimal (they're primarily a runtime assertion\n * that the on-disk state hasn't been corrupted).\n */\nfunction readStored<T>(\n raw: string | undefined,\n guard: (value: unknown) => value is T,\n): T | null {\n if (!raw) return null\n try {\n const parsed = parseJsonUnknown(raw)\n return guard(parsed) ? parsed : null\n } catch {\n return null\n }\n}\n\n// Minimal structural predicates for the shapes persisted by this provider.\n// They're intentionally loose — the goal is \"is this the right kind of\n// thing?\", not full schema validation.\nfunction isEngineChoice(v: unknown): v is PipelineEngineChoice {\n if (v === null || typeof v !== 'object' || Array.isArray(v)) return false\n const rec: Record<string, unknown> = { ...v }\n return typeof rec['runtime'] === 'string'\n && typeof rec['backend'] === 'string'\n && typeof rec['format'] === 'string'\n}\n\nfunction isPipelineStepArray(v: unknown): v is PipelineDefaultStep[] {\n return Array.isArray(v)\n}\n\n/**\n * Phase 7 (settings redesign) template migration.\n *\n * Walks every template → step tree and removes the obsolete generic\n * `confidence` key that existed on `PipelineTemplateStep` pre-Phase-7.\n * The step's threshold (if any) now lives in its own `configSchema`-\n * declared `minConfidence` field inside `step.settings`.\n *\n * Idempotent: running twice returns `changed: false` on the second\n * call, because after the first pass no `confidence` property remains.\n * Returns the cleaned template array + a flag indicating whether at\n * least one step actually carried the obsolete key, so `listTemplates`\n * can skip the rewrite when nothing changed.\n */\nfunction migrateTemplates(\n templates: readonly PipelineTemplate[],\n): { templates: PipelineTemplate[]; changed: boolean; stripped: number } {\n let stripped = 0\n\n function stripStep(step: PipelineTemplateStep): PipelineTemplateStep {\n // The persisted blob may carry legacy keys not in the current\n // `PipelineTemplateStep` type. Cast via `unknown` to access the\n // obsolete field without a compile error.\n const legacy = step as unknown as PipelineTemplateStep & { confidence?: unknown }\n const hasLegacyConfidence = 'confidence' in legacy\n const cleanedChildren = step.children.map(stripStep)\n const childrenChanged = cleanedChildren.some((c, i) => c !== step.children[i])\n if (!hasLegacyConfidence && !childrenChanged) return step\n if (hasLegacyConfidence) stripped++\n // Explicit rebuild without `confidence` — spread-first would keep it.\n return {\n addonId: step.addonId,\n enabled: step.enabled,\n modelId: step.modelId,\n children: cleanedChildren,\n ...(step.settings !== undefined ? { settings: step.settings } : {}),\n }\n }\n\n const nextTemplates: PipelineTemplate[] = []\n let anyChanged = false\n for (const t of templates) {\n const cleanedSteps = t.steps.map(stripStep)\n const stepsChanged = cleanedSteps.some((s, i) => s !== t.steps[i])\n if (stepsChanged) {\n anyChanged = true\n nextTemplates.push({ ...t, steps: cleanedSteps })\n } else {\n nextTemplates.push(t)\n }\n }\n return { templates: nextTemplates, changed: anyChanged, stripped }\n}\n\nfunction isTemplateArray(v: unknown): v is PipelineTemplate[] {\n return Array.isArray(v)\n}\n\n// `isOrchestratorSettings` removed — orchestration settings moved to the\n// `pipeline-orchestrator` addon in the pipeline page redesign. This addon\n// no longer reads/writes motion fps, detection fps, cooldown, or related\n// fields — those are queried via the orchestrator cap.\n//\n// Benchmark helpers (STEP_REFERENCE_IMAGES, computeLatencyStats,\n// loadReferenceImageBase64, isBenchmarkHistoryArray, resolveReferenceImagesDir)\n// moved to `@camstack/addon-benchmark` (2026-04-14) along with the runBenchmark\n// / history methods. This provider only keeps `runPipeline` + reference-image\n// listing, which addon-benchmark now consumes via the cap router.\n\nfunction resolveReferenceAudioDir(): string | null {\n const candidates = [\n path.resolve(process.cwd(), 'assets/reference-audio'),\n path.resolve(process.cwd(), '../assets/reference-audio'),\n path.resolve(process.cwd(), '../../assets/reference-audio'),\n ]\n return candidates.find((p) => fs.existsSync(p)) ?? null\n}\n\nfunction loadReferenceAudioBase64(filename: string): string {\n const dir = resolveReferenceAudioDir()\n if (!dir) throw new Error('Reference audio directory not found')\n const filePath = path.join(dir, filename)\n if (!fs.existsSync(filePath)) {\n throw new Error(`Reference audio file not found: ${filename}`)\n }\n return fs.readFileSync(filePath).toString('base64')\n}\n\n/** Parse a WAV file (16-bit PCM) into an AudioChunkInput for classification. */\nfunction parseWavToAudioChunk(filePath: string): AudioChunkInput {\n const buf = fs.readFileSync(filePath)\n // Standard 44-byte WAV header, 16-bit PCM samples\n const pcm = new Int16Array(buf.buffer, buf.byteOffset + 44, (buf.length - 44) / 2)\n const float32 = new Float32Array(pcm.length)\n for (let i = 0; i < pcm.length; i++) {\n float32[i] = pcm[i]! / 32768.0\n }\n return { data: float32, sampleRate: 16000, channels: 1, timestamp: Date.now() }\n}\n\n// `DEFAULT_ORCHESTRATOR` removed — orchestration defaults moved to the\n// `pipeline-orchestrator` addon's getConfigSchema.\n\n/** Structural equality for PipelineEngineChoice — ignores undefined `device`. */\nfunction enginesEqual(a: PipelineEngineChoice, b: PipelineEngineChoice): boolean {\n return a.runtime === b.runtime\n && a.backend === b.backend\n && a.format === b.format\n && (a.device ?? null) === (b.device ?? null)\n}\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport class DetectionPipelineProvider implements IPipelineExecutorProvider {\n private engineFactory: EngineFactory | null = null\n private executor: PipelineExecutor | null = null\n private currentSteps: PipelineDefaultStep[] | null = null\n private currentEngine: PipelineEngineChoice\n private readonly log: IScopedLogger\n\n /** Addon context — ctx.api resolves lazily (direct caller created after boot) */\n private addonCtx: AddonContext | null = null\n\n /**\n * Per-device {@link DeviceProxy} cache used for zone gating at the\n * runtime path. Reads `state.zones.value` + `state.zoneRules.value`\n * synchronously per frame so detections inside an `exclude` zone\n * never reach the tracker downstream — saves the tracking + event\n * cost on top of the analytics-side filter.\n */\n private readonly deviceProxies = new Map<number, DeviceProxy>()\n private readonly proxyUnsubs = new Map<number, ReadonlyArray<() => void>>()\n\n /** Initialization lock — ensures only one init runs at a time; concurrent callers await the same promise */\n private initPromise: Promise<void> | null = null\n /**\n * Last logged \"step configured\" signature — used to dedupe the\n * per-step debug trail so it only fires on actual config CHANGE\n * (not on every frame). `runPipeline` is called once per decoded\n * frame (up to detectionFps × N cameras), so even at debug level\n * the per-step dump produced ~50 lines/sec/camera and drowned the\n * Cluster log viewer.\n */\n private lastStepsSignature: string | null = null\n /**\n * True once the engine + models are fully ready for inference. No\n * longer gates runtime dispatch (Phase 4 removed the legacy `runFrame`\n * which read this flag); kept as a diagnostic the admin UI / tests\n * surface via the future `isReady()` helper.\n */\n private ready = false\n\n /**\n * Warm cache for benchmark engine-override runs.\n *\n * Each override rebuild costs a full Python pool spin-up (~300-500ms)\n * plus the per-model load. Benchmark tabs iterate up to 800× against\n * the same override (e.g. yolov9s / coreml / all) — paying that spin-up\n * each iteration dwarfs actual inference time.\n *\n * When the same override engine is requested within `OVERRIDE_CACHE_TTL_MS`,\n * we reuse the prior transient factory. Hitting a different override\n * or exceeding the TTL disposes the cache and spins a new factory.\n * The prior \"main\" factory (`priorFactory`) is still restored so\n * runtime dispatch (camera frames) keeps its own resident engine.\n */\n private overrideCache: {\n engine: PipelineEngineChoice\n factory: EngineFactory\n executor: PipelineExecutor | null\n lastUsedMs: number\n /** Resolves when factory.initialize() finishes; null after ready. */\n initPromise: Promise<void> | null\n } | null = null\n private overrideCacheTimer: ReturnType<typeof setTimeout> | null = null\n private static readonly OVERRIDE_CACHE_TTL_MS = 60_000\n\n /** Read the addon settings store (all keys). */\n private readonly readStore: () => Promise<Record<string, unknown>>\n /** Write a patch to the addon settings store. */\n private readonly writeStore: (patch: Record<string, unknown>) => Promise<void>\n /** Read per-device settings. */\n private readonly readDeviceStore: (deviceId: number) => Promise<Record<string, unknown>>\n\n constructor(\n settings: {\n readAddonStore(): Promise<Record<string, unknown>>\n writeAddonStore(patch: Record<string, unknown>): Promise<void>\n readDeviceStore?(deviceId: number): Promise<Record<string, unknown>>\n },\n private readonly modelsDir: string,\n logger: IScopedLogger,\n private readonly eventBus: IEventBus | null = null,\n /**\n * Closure that returns the addon's config schema. Injected by the addon\n * class at construction so the provider can serve `getDetectionConfigSchema`\n * directly. The addon owns both pieces — no public setter needed.\n */\n private readonly detectionConfigSchemaSource: (() => ConfigUISchema) | null = null,\n /**\n * Executor tuning forwarded to the `EngineFactory`. Exposes\n * `concurrency` (Python inference-pool worker threads) plus the\n * per-runtime batch tuning (`tuning`). Undefined fields fall back\n * to runtime defaults — see engine-factory.ts.\n *\n * `pythonPath` is the absolute path to the embedded Python (resolved\n * once by the addon class via `ctx.deps.ensurePython()`); required\n * whenever `runtime: 'python'` is selected. `pythonAddonDir` points\n * to this addon's `python/` dir at runtime — used to locate the\n * `requirements*.txt` files that drive lazy backend installs.\n */\n private readonly executorOptions: {\n concurrency?: number\n tuning?: import('./engine/engine-factory.js').PoolTuning\n pythonPath?: string\n pythonAddonDir?: string\n numWorkers?: number\n } = {},\n ) {\n this.log = logger\n this.readStore = () => settings.readAddonStore()\n this.writeStore = (patch) => settings.writeAddonStore(patch)\n this.readDeviceStore = settings.readDeviceStore ?? (async () => ({}))\n // Engine loaded asynchronously in init() — default to platform best until then\n this.currentEngine = DetectionPipelineProvider.detectBestEngine()\n this.log.info('Engine selected (default)', { meta: { runtime: this.currentEngine.runtime, backend: this.currentEngine.backend, format: this.currentEngine.format } })\n }\n\n /** Load persisted engine choice. Call after construction. */\n async init(): Promise<void> {\n const stored = await this.loadEngine()\n if (stored) {\n this.currentEngine = stored\n this.log.info('Engine loaded (persisted)', { meta: { runtime: stored.runtime, backend: stored.backend, format: stored.format } })\n }\n }\n\n /**\n * Lazy-install the pip requirements file matching `engine.backend` into\n * the embedded Python before the inference pool spawns. No-op for\n * `runtime: 'node'`, for backends with no requirements file on disk,\n * or before the addon context has been wired (warm-pool race window).\n *\n * Idempotent — `installPythonRequirements` short-circuits on a hash\n * marker, so back-to-back EngineFactory rebuilds (benchmark overrides,\n * tuning respawns) skip the pip subprocess after the first install.\n */\n private async ensureBackendDeps(engine: PipelineEngineChoice): Promise<void> {\n if (engine.runtime !== 'python') return\n if (!this.addonCtx) return\n const pythonAddonDir = this.executorOptions.pythonAddonDir\n if (!pythonAddonDir) return\n const poolRuntime = BACKEND_TO_POOL_RUNTIME[engine.backend]\n if (!poolRuntime) return\n const file = path.join(pythonAddonDir, `requirements-${poolRuntime}.txt`)\n if (!fs.existsSync(file)) return\n await this.addonCtx.deps.installPythonRequirements(file)\n }\n\n /**\n * Warm the Python inference pool up-front so consumers that hit\n * `runPipeline` on the first frame don't race the pool's spawn\n * (`SharedInferencePool: not initialized`). Called from the addon's\n * `onInitialize` so `BaseAddon.autoEmitReadiness` fires `ready`\n * only after the pool is actually callable — consumers that\n * `awaitReady('pipeline-executor', {node:…})` now get a true\n * \"callable\" signal, not a \"process started\" signal.\n *\n * Idempotent: delegates to `ensureEngineFactory` which short-circuits\n * on a warmed pool.\n */\n async warmPool(): Promise<void> {\n await this.ensureEngineFactory()\n }\n\n /** True when the engine + model pool are fully warmed and inference-ready. */\n isReady(): boolean {\n return this.ready\n }\n\n /** Detect the best inference engine for this platform. */\n private static detectBestEngine(): PipelineEngineChoice {\n const platform = process.platform\n const arch = process.arch\n\n // macOS Apple Silicon → CoreML (Neural Engine + GPU)\n if (platform === 'darwin' && arch === 'arm64') {\n return { runtime: 'python', backend: 'coreml', format: 'coreml', device: 'all' }\n }\n\n // macOS Intel → CoreML (GPU)\n if (platform === 'darwin') {\n return { runtime: 'python', backend: 'coreml', format: 'coreml', device: 'gpu' }\n }\n\n // Linux — check for NVIDIA GPU\n try {\n const { execFileSync } = require('node:child_process') as typeof import('node:child_process')\n execFileSync('nvidia-smi', ['--query-gpu=name', '--format=csv,noheader'], { timeout: 3000, stdio: 'pipe' })\n return { runtime: 'python', backend: 'onnx', format: 'onnx', device: 'cuda' }\n } catch {\n // No NVIDIA GPU\n }\n\n // Fallback: Node.js CPU (always available, no Python needed)\n return { runtime: 'node', backend: 'cpu', format: 'onnx' }\n }\n\n /** Store the addon context. ctx.api is a lazy getter resolved at call time. */\n async setApi(addonCtx: unknown): Promise<void> {\n this.addonCtx = addonCtx as AddonContext\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Schema & Configuration\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getSchema(engine?: PipelineEngineChoice): Promise<PipelineSchema> {\n if (!engine || !engine.runtime) engine = await this.getSelectedEngine()\n const format = engine.format\n const slots = buildSchemaSlots(format, this.modelsDir)\n return {\n availableEngines: this.getAvailableEnginesWithDevices(),\n selectedEngine: { ...engine },\n slots,\n }\n }\n\n async getAvailableEngines(): Promise<PipelineEngineChoice[]> {\n return this.getAvailableEnginesWithDevices().map((e) => e.engine)\n }\n\n getAvailableEnginesWithDevices(): AvailableEngine[] {\n const engines: AvailableEngine[] = [\n {\n engine: { runtime: 'node', backend: 'cpu', format: 'onnx' },\n devices: [{ id: 'cpu', label: 'CPU' }],\n defaultDevice: 'cpu',\n },\n ]\n\n if (process.platform === 'darwin') {\n engines.push({\n engine: { runtime: 'python', backend: 'coreml', format: 'coreml' },\n devices: COREML_DEVICES,\n defaultDevice: 'all',\n })\n }\n\n engines.push({\n engine: { runtime: 'python', backend: 'openvino', format: 'openvino' },\n devices: OPENVINO_DEVICES,\n defaultDevice: 'auto',\n })\n\n engines.push({\n engine: { runtime: 'python', backend: 'onnx', format: 'onnx' },\n devices: ONNX_PYTHON_DEVICES,\n defaultDevice: 'cpu',\n })\n\n return engines\n }\n\n async getDefaultSteps(engine: PipelineEngineChoice): Promise<PipelineDefaultStep[]> {\n return buildDefaultStepTree(engine.format)\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Global Pipeline State\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getGlobalSteps(): Promise<PipelineDefaultStep[] | null> {\n if (this.currentSteps) return this.currentSteps\n const store = await this.readStore()\n const raw = store[KEY_STEPS] as string | undefined\n if (!raw) {\n // Tempo zero — no persisted pipeline yet. Seed with the canonical\n // default tree so the UI and runtime see a sensible baseline.\n const defaults = buildDefaultStepTree(this.currentEngine.format)\n if (defaults.length === 0) return null\n this.currentSteps = defaults\n void this.writeStore({ [KEY_STEPS]: JSON.stringify(defaults) })\n this.log.info(\n 'Bootstrapped default pipeline — object-detection + face + plate recognition enabled by default',\n { meta: { rootSteps: defaults.length } },\n )\n return this.currentSteps\n }\n try {\n let steps: PipelineDefaultStep[] = readStored(raw, isPipelineStepArray) ?? []\n if (steps.length === 0) return null\n\n // Migration: add audio-classifier step if missing (added in v0.2)\n const hasAudio = steps.some(s => s.slot === 'audio-classifier')\n if (!hasAudio) {\n const format = this.currentEngine.format\n const audioDef = getStepDefinition('audio-classifier')\n const hasFormat = audioDef.models.some((m) => m.formats[format])\n if (hasFormat) {\n const modelId = getDefaultModelForFormat('audio-classifier', format)\n const audioEngine: PipelineEngineChoice =\n modelId === 'apple-soundanalysis'\n ? { runtime: 'node', backend: 'coreml', format: 'coreml' }\n : { runtime: 'node', backend: 'cpu', format: 'onnx' }\n const audioStep: PipelineDefaultStep = {\n addonId: audioDef.id,\n addonName: audioDef.name,\n slot: audioDef.slot,\n inputClasses: audioDef.inputClasses ?? [],\n outputClasses: audioDef.outputClasses,\n enabled: true,\n modelId,\n children: [],\n engine: audioEngine,\n }\n steps = [...steps, audioStep]\n void this.writeStore({ [KEY_STEPS]: JSON.stringify(steps) })\n this.log.info('Migration: added audio-classifier step to persisted pipeline config')\n }\n }\n\n this.currentSteps = steps\n return this.currentSteps\n } catch {\n throw new Error(`Failed to parse persisted pipeline steps: corrupt data in key \"${KEY_STEPS}\"`)\n }\n }\n\n // setGlobalSteps removed in Phase 6c — there is no \"global pipeline\"\n // in the stateless-pipeline model. Per-camera content lives on the\n // orchestrator (`pipelineOrchestrator.{get,set}CameraPipeline`). The\n // `ensureModelsAndApply` hot-swap helper was the write-path counterpart\n // and is removed along with it; Phase 4 engine-cache work re-introduces\n // per-engine model download on demand inside `runPipeline`.\n\n async getGlobalPipelineConfig(): Promise<PipelineConfig | null> {\n if (!this.currentSteps) return null\n return stepsToPipelineConfig(this.currentSteps, this.currentEngine)\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Engine Management\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getSelectedEngine(): Promise<PipelineEngineChoice> {\n return this.currentEngine\n }\n\n // setEngine removed in Phase 6c — engine is per-camera in the\n // stateless-pipeline model. The orchestrator's per-camera\n // `CameraPipelineConfig.engine` is the source of truth; Phase 4\n // (future) adds an engine cache on the executor keyed by engineKey\n // for fast per-frame dispatch.\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Orchestrator Settings / Config Schema\n // ═══════════════════════════════════════════════════════════════════════════\n //\n // Moved to `@camstack/addon-pipeline-orchestrator` as part of the pipeline\n // page redesign. That addon now owns:\n // - The ConfigUISchema sections for motion / detection / audio / balancer\n // / failover (via its `getConfigSchema()` method)\n // - The per-device settings storage under `addon:pipeline-orchestrator`\n // - The typed accessors `getDeviceOrchestrationSettings` and\n // `getGlobalOrchestrationSettings` on the pipeline-orchestrator cap\n //\n // The detection-pipeline addon no longer exposes orchestration settings\n // because they are not pipeline content — they are orchestration policy\n // (what stream to use, what fps to target, what classes to filter).\n // The pipeline-executor cap is intentionally blind to that layer.\n //\n // `getOrchestratorConfigSchema()` on IPipelineExecutorProvider still exists\n // as an interface method for backwards compatibility with hub dashboards\n // that query it during boot, but returns an empty schema from this addon.\n\n async getOrchestratorConfigSchema(): Promise<ConfigUISchema> {\n return { sections: [] }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Inference Capabilities\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getCapabilities(_forceRefresh?: boolean): Promise<InferenceCapabilities> {\n const broker = this.addonCtx?.kernel?.cluster?.broker\n const agentId = broker?.nodeID ?? os.hostname()\n const agentName = agentId\n\n // Step → addon mapping. Each pipeline step is surfaced as an\n // AddonCapabilityInfo with its catalog of models + per-format\n // download status.\n const addons = ALL_PIPELINE_STEPS.map((s): AddonCapabilityInfo => ({\n id: s.definition.id,\n name: s.definition.name,\n models: s.definition.models.map((m): ModelAvailability => {\n const formats: Record<string, ModelFormatStatus> = {}\n for (const [formatKey, entry] of Object.entries(m.formats)) {\n if (!entry) continue\n const downloaded = isModelDownloaded(this.modelsDir, m, formatKey as ModelFormat)\n const status: ModelFormatStatus = {\n url: entry.url,\n sizeMB: entry.sizeMB,\n downloaded,\n ...(downloaded ? { filePath: path.join(this.modelsDir, `${m.id}.${formatKey}`) } : {}),\n }\n formats[formatKey] = status\n }\n return {\n id: m.id,\n name: m.name,\n description: m.description,\n inputSize: m.inputSize,\n formats,\n }\n }),\n }))\n\n // Runtime listing. The static engine table from getAvailableEnginesWithDevices()\n // tells us which runtimes are reachable on this build of the agent;\n // actual reachability of the underlying Python interpreter / drivers\n // is the responsibility of platform-probe-native and is reflected via\n // PlatformScores (optional below).\n const engines = this.getAvailableEnginesWithDevices()\n const nodeBackends: BackendInfo[] = []\n const pythonBackends: PythonBackendInfo[] = []\n for (const e of engines) {\n if (e.engine.runtime === 'node') {\n nodeBackends.push({\n id: e.engine.backend,\n label: e.engine.backend.toUpperCase(),\n available: true,\n device: e.defaultDevice ?? e.devices[0]?.id ?? 'cpu',\n })\n } else {\n pythonBackends.push({\n id: e.engine.backend,\n label: e.engine.backend.toUpperCase(),\n available: true,\n device: e.defaultDevice ?? e.devices[0]?.id ?? 'cpu',\n modelFormat: e.engine.format,\n pythonModule: pythonModuleForBackend(e.engine.backend),\n })\n }\n }\n\n return {\n agentId,\n agentName,\n platform: os.platform(),\n arch: os.arch(),\n addons,\n runtimes: {\n node: {\n available: true,\n backends: nodeBackends,\n modelFormat: 'onnx',\n },\n python: {\n available: pythonBackends.length > 0,\n backends: pythonBackends,\n },\n },\n modelsDir: this.modelsDir,\n }\n }\n\n async getAddonModels(input: { addonId: string }): Promise<readonly ModelAvailability[]> {\n const caps = await this.getCapabilities()\n return caps.addons.find(a => a.id === input.addonId)?.models ?? []\n }\n\n async getDetectionConfigSchema(): Promise<ConfigUISchema | null> {\n return this.detectionConfigSchemaSource?.() ?? null\n }\n\n async downloadModel(input: { modelId: string; format: ModelFormat; addonId: string }): Promise<{ filePath: string; sizeMB: number; durationMs: number }> {\n const { modelId, format, addonId } = input\n const def = getStepDefinition(addonId)\n const modelEntry = def.models.find(m => m.id === modelId)\n if (!modelEntry) {\n throw new Error(`Model \"${modelId}\" not found in step \"${addonId}\" catalog`)\n }\n\n const formatEntry = modelEntry.formats[format]\n if (!formatEntry) {\n throw new Error(`Model \"${modelId}\" has no ${format} format. Available: ${Object.keys(modelEntry.formats).join(', ')}`)\n }\n\n const totalBytes = formatEntry.sizeMB * 1024 * 1024\n const source = { type: 'pipeline', id: 'model-download' } as const\n let lastEmittedPercent = -1\n\n const emitProgress = (progress: number) => {\n this.eventBus?.emit({\n id: `model-download-${Date.now()}`,\n timestamp: new Date(),\n source,\n category: EventCategory.ModelDownloadProgress,\n data: { modelId, progress, totalMB: formatEntry.sizeMB },\n })\n }\n\n // Throttled progress callback — emit at most every 2% to avoid flooding the EventBus\n const onProgress = (downloaded: number, total: number | undefined) => {\n const effectiveTotal = total ?? totalBytes\n const percent = effectiveTotal > 0 ? Math.min(99, Math.round((downloaded / effectiveTotal) * 100)) : 0\n if (percent > lastEmittedPercent + 1) {\n lastEmittedPercent = percent\n emitProgress(percent)\n }\n }\n\n emitProgress(0)\n\n const startMs = Date.now()\n try {\n await this.downloadWithRetry(modelEntry, format, 3, onProgress)\n const durationMs = Date.now() - startMs\n\n const urlParts = formatEntry.url.split('/')\n const filename = urlParts[urlParts.length - 1] ?? `${modelEntry.id}.${format}`\n const filePath = path.join(this.modelsDir, filename)\n\n this.log.info('Model downloaded', { meta: { modelId, format, durationMs } })\n emitProgress(100)\n\n return { filePath, sizeMB: formatEntry.sizeMB, durationMs }\n } catch (err: unknown) {\n const msg = errMsg(err)\n this.log.error('Model download failed', { meta: { error: msg, modelId, format } })\n emitProgress(-1)\n throw err\n }\n }\n\n async deleteModel(input: { modelId: string; format: ModelFormat; addonId: string }): Promise<{ success: true }> {\n const { modelId, format, addonId } = input\n const def = getStepDefinition(addonId)\n const modelEntry = def.models.find(m => m.id === modelId)\n if (!modelEntry) {\n throw new Error(`Model \"${modelId}\" not found in step \"${addonId}\" catalog`)\n }\n\n const deleted = deleteModelFromDisk(this.modelsDir, modelEntry, format)\n if (!deleted) {\n throw new Error(`Model \"${modelId}\" (${format}) is not downloaded — nothing to delete`)\n }\n\n this.log.info('Model deleted from disk', { meta: { modelId, format } })\n return { success: true }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Pipeline Execution\n // ═══════════════════════════════════════════════════════════════════════════\n\n async runPipeline(\n input: PipelineRunInput,\n onProgress?: (message: string) => void,\n ): Promise<FrameResult> {\n // Every progress milestone is forwarded both to the in-process\n // callback (legacy) and to the event bus as `pipeline.progress`.\n // The admin UI's Static Pipeline tab (ImageTab) subscribes to this\n // category and renders each `message` as a status line. Emitting on\n // the bus is the only way the UI can observe progress — the\n // `onProgress` closure can't cross a tRPC boundary, and runPipeline\n // is always invoked via tRPC from the benchmark worker.\n //\n // Every emission tags:\n // - `nodeId`: the node that ran the pipeline (distinguishes emitters\n // when multiple nodes run benchmarks concurrently)\n // - `sessionId`: a unique per-run id so the UI can correlate a burst\n // of messages into a single pipeline run. When the caller passes\n // `input.sessionId` (UI-initiated one-shot), the server reuses it\n // verbatim so the UI can filter `pipeline.progress` to its own run\n // and ignore concurrent runtime dispatches from camera frames.\n const nodeId = this.addonCtx?.kernel?.localNodeId ?? 'hub'\n const sessionId = input.sessionId ?? `run-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`\n const emit = (\n message: string,\n extra?: { step?: string; addonId?: string; modelId?: string; ms?: number; engine?: { runtime: string; backend: string; device?: string } },\n ): void => {\n onProgress?.(message)\n this.eventBus?.emit(createEvent(\n EventCategory.PipelineProgress,\n { type: 'pipeline', id: 'pipeline-test' },\n {\n nodeId,\n sessionId,\n step: extra?.step ?? '-',\n addonId: extra?.addonId,\n modelId: extra?.modelId,\n ms: extra?.ms,\n ...(extra?.engine ? { engine: extra.engine } : {}),\n message,\n },\n ))\n }\n\n // Per-phase latency breakdown surfaced in result.debug. Only populated\n // for benchmark / reference-image paths — runtime path returns early\n // with the raw executor result (no debug augmentation).\n const wallT0 = performance.now()\n let decodeMs: number\n let setupMs: number\n\n // Resolve image from frame (runtime), base64 (benchmark), binary\n // image (benchmark fast path), or reference. Exactly one source is\n // accepted; ambiguous input throws so we never silently prefer one\n // path over another.\n let rootInput: InferenceInput\n let imageWidth: number\n let imageHeight: number\n /**\n * Lazy JPEG accessor for crop-children execution. Encodes the raw\n * frame to JPEG once on first call and caches the result. JPEG-\n * source frames return their own buffer with no encode work. Trees\n * without crop children never invoke this — the raw fast path\n * stays JPEG-free end to end.\n */\n let jpegProvider: () => Promise<Buffer>\n const sources = [\n input.frame ? 'frame' : null,\n input.image ? 'image' : null,\n input.imageBase64 ? 'imageBase64' : null,\n input.referenceImage ? 'referenceImage' : null,\n ].filter((s): s is string => s !== null)\n if (sources.length === 0) {\n throw new Error('runPipeline requires exactly one of: frame, image, imageBase64, referenceImage')\n }\n if (sources.length > 1) {\n throw new Error(`runPipeline received conflicting image sources: ${sources.join(', ')}`)\n }\n\n const decodeT0 = performance.now()\n if (input.frame) {\n // Runtime dispatch path (pipeline-runner → broker decoded frame).\n // Dimensions come from the FrameInput; no JPEG probe needed.\n const frame = input.frame\n imageWidth = frame.width\n imageHeight = frame.height\n // VIEW the underlying ArrayBuffer instead of copying — Buffer.from(Uint8Array)\n // copies the data, which on the raw-RGB hot path means cloning ~1.2MB\n // (640×640×3 native or 1280×720×3) on every call. The pool writes the\n // bytes into the python stdin pipe and never mutates them, so a view is\n // safe. Bench at parallel=4 dropped from 50fps to 20fps just from the\n // copy + GC pressure of this single line.\n const data = Buffer.from(\n frame.data.buffer,\n frame.data.byteOffset,\n frame.data.byteLength,\n )\n if (frame.format === 'jpeg') {\n rootInput = { kind: 'jpeg', data }\n\n jpegProvider = async () => data\n } else if (frame.format === 'rgb' || frame.format === 'bgr' || frame.format === 'gray') {\n // Encode raw → JPEG before sending to the Python pool. The JPEG\n // is ~200KB vs 1.2MB raw RGB — saves ~30ms of pipe transfer per\n // call at the cost of ~3-5ms sharp encode. Net gain: ~25ms/call.\n // This is the production hot path (camera frames from broker).\n const channels = frame.format === 'gray' ? 1 : 3\n const sharp = (await import('sharp')).default\n const jpeg = await sharp(data, { raw: { width: frame.width, height: frame.height, channels } })\n .jpeg({ quality: 85, mozjpeg: false })\n .toBuffer()\n rootInput = { kind: 'jpeg', data: jpeg }\n\n jpegProvider = async () => jpeg\n } else {\n // yuv420 (and any future planar format) — encode to JPEG\n // upfront. Not on the hot path today: detection always\n // requests rgb/jpeg/gray.\n const sharp = (await import('sharp')).default\n const encoded = await sharp(data, { raw: { width: frame.width, height: frame.height, channels: 3 } })\n .jpeg({ quality: 80, mozjpeg: false })\n .toBuffer()\n rootInput = { kind: 'jpeg', data: encoded }\n\n jpegProvider = async () => encoded\n }\n emit(`Frame: ${imageWidth}×${imageHeight} (${frame.format})`)\n } else if (input.image) {\n // Binary fast path — caller already decoded base64 (or never had\n // it to begin with). Saves ~5ms base64 decode per call vs the\n // imageBase64 path, plus 33% payload on the Moleculer MsgPack hop.\n emit('Decoding image...')\n const data = Buffer.from(input.image.buffer, input.image.byteOffset, input.image.byteLength)\n const dims = await this.getJpegDimensions(data)\n imageWidth = dims.width\n imageHeight = dims.height\n rootInput = { kind: 'jpeg', data }\n jpegProvider = async () => data\n emit(`Image: ${imageWidth}×${imageHeight}`)\n } else if (input.imageBase64) {\n emit('Decoding image...')\n const data = Buffer.from(input.imageBase64, 'base64')\n const dims = await this.getJpegDimensions(data)\n imageWidth = dims.width\n imageHeight = dims.height\n rootInput = { kind: 'jpeg', data }\n jpegProvider = async () => data\n emit(`Image: ${imageWidth}×${imageHeight}`)\n } else {\n // referenceImage path (the last remaining source after the\n // exclusivity check above).\n emit(`Loading reference image: ${input.referenceImage!}`)\n const img = await this.getReferenceImage({ filename: input.referenceImage! })\n if (!img) throw new Error(`Reference image not found: ${input.referenceImage!}`)\n const data = Buffer.from(img.base64, 'base64')\n const dims = await this.getJpegDimensions(data)\n imageWidth = dims.width\n imageHeight = dims.height\n rootInput = { kind: 'jpeg', data }\n jpegProvider = async () => data\n emit(`Image: ${imageWidth}×${imageHeight}`)\n }\n decodeMs = performance.now() - decodeT0\n\n // Convert input steps → PipelineDefaultStep (enriching from StepDefinitions)\n const benchmarkSteps = this.inputStepsToPipelineSteps(input.steps)\n // Per-step debug-level logging — runPipeline fires once per decoded\n // frame (up to `detectionFps` per camera). Dedupe via a signature\n // so the per-step dump only fires when the steps config actually\n // changes (operator edit / new camera attach), not on every frame.\n const stepsSignature = JSON.stringify(benchmarkSteps.map(s => ({ id: s.addonId, settings: s.settings ?? {} })))\n if (stepsSignature !== this.lastStepsSignature) {\n this.lastStepsSignature = stepsSignature\n for (const s of benchmarkSteps) {\n this.log.debug('step configured', { meta: { phase: 'runPipeline', step: s.addonId, settings: s.settings ?? {} } })\n }\n }\n const enabledSteps = flattenSteps(benchmarkSteps)\n emit(`Pipeline: ${enabledSteps.length} step(s) — ${enabledSteps.map(s => s.addonId).join(' → ')}`)\n\n // Ensure engine factory is initialized (do NOT require global steps — benchmark uses explicit steps)\n await this.ensureEngineFactory()\n // Per-run engine override: benchmark's override mode sends\n // `input.engine`, meaning \"run this invocation against the\n // overridden engine WITHOUT mutating the saved addon store\".\n // We swap `currentEngine` + rebuild `engineFactory` for the\n // call, then restore the prior state in `finally` so repeated\n // bench runs don't pollute each other. The persisted\n // `engineRuntime/Backend/Device` fields in the store are NOT\n // written either way.\n const priorEngine = this.currentEngine\n const priorFactory = this.engineFactory\n const priorExecutor = this.executor\n const runEngine: PipelineEngineChoice = input.engine\n ? {\n runtime: input.engine.runtime,\n backend: input.engine.backend,\n format: input.engine.format,\n // Preserve prior device when the override omits one — bench runs\n // that change only runtime/backend shouldn't lose the device label.\n ...(input.engine.device || priorEngine.device\n ? { device: input.engine.device ?? priorEngine.device }\n : {}),\n }\n : priorEngine\n const differs = input.engine && (\n priorEngine.runtime !== runEngine.runtime ||\n priorEngine.backend !== runEngine.backend ||\n priorEngine.format !== runEngine.format ||\n (priorEngine.device ?? '') !== (runEngine.device ?? '')\n )\n if (differs) {\n const cacheHit = this.overrideCache && enginesEqual(this.overrideCache.engine, runEngine)\n if (cacheHit) {\n // Cache hit — even when init is still in flight, await it before\n // using the factory. Multiple parallel benchmark calls converge\n // on the same singleflight promise; only the first triggers\n // pool spawn.\n const cached = this.overrideCache!\n if (cached.initPromise) await cached.initPromise\n this.log.debug('Benchmark: reusing warm override factory', {\n meta: { engine: `${runEngine.runtime}/${runEngine.backend}/${runEngine.device ?? 'default'}` },\n })\n this.currentEngine = runEngine\n this.engineFactory = cached.factory\n this.executor = cached.executor\n } else {\n // Different override than cache (or first run) — dispose the stale\n // cache (if any) and spin a fresh factory. Pre-populate the cache\n // BEFORE awaiting initialize so concurrent benchmark calls see\n // the in-flight entry and converge on its `initPromise` instead\n // of each spawning their own pool (the bug: 4 parallel runs each\n // built a fresh SharedInferencePool, the unlucky callers saw\n // \"not initialized\" because `this.engineFactory` got overwritten\n // mid-flight).\n if (this.overrideCache) {\n try { await this.overrideCache.factory.dispose() } catch {\n /* stale cache dispose error — ignore */\n }\n this.overrideCache = null\n }\n this.log.info('Benchmark: engine override', {\n meta: {\n from: `${priorEngine.runtime}/${priorEngine.backend}`,\n to: `${runEngine.runtime}/${runEngine.backend}`,\n },\n })\n await this.ensureBackendDeps(runEngine)\n const newFactory = new EngineFactory({\n engine: runEngine,\n modelsDir: this.modelsDir,\n logger: this.log.child('engine-override'),\n pythonPath: this.executorOptions.pythonPath ?? '',\n concurrency: this.executorOptions.concurrency,\n tuning: this.executorOptions.tuning,\n numWorkers: this.executorOptions.numWorkers,\n })\n const initPromise = newFactory.initialize([])\n this.overrideCache = {\n engine: runEngine,\n factory: newFactory,\n executor: null,\n lastUsedMs: Date.now(),\n initPromise,\n }\n // Schedule eviction immediately so if the run throws before the\n // finally fires, the warm cache still has a TTL guard.\n this.scheduleOverrideCacheEviction()\n try {\n await initPromise\n this.overrideCache.initPromise = null\n } catch (err) {\n // Init failed — drop cache so next caller retries.\n this.overrideCache = null\n if (this.overrideCacheTimer) {\n clearTimeout(this.overrideCacheTimer)\n this.overrideCacheTimer = null\n }\n throw err\n }\n this.currentEngine = runEngine\n this.engineFactory = newFactory\n this.executor = null\n }\n }\n try {\n // `device` falls back to 'default' in the label only — the event\n // payload keeps the actual value (which is always populated after\n // loadEngine fallback, but the label must never render as bare\n // `runtime/backend` for operator log parsing).\n const deviceLabel = runEngine.device ?? 'default'\n emit(\n `Engine: ${runEngine.runtime}/${runEngine.backend}/${deviceLabel}${input.engine ? ' (override)' : ''}`,\n {\n step: 'engine',\n engine: {\n runtime: runEngine.runtime,\n backend: runEngine.backend,\n device: deviceLabel,\n },\n },\n )\n const runtimeStr = `${runEngine.runtime}+${runEngine.backend}`\n const executor = this.executor ?? new PipelineExecutor({ engineRuntime: runtimeStr, logger: this.log })\n this.executor = executor\n const needed = enabledSteps.filter(s => !this.engineFactory?.isLoadedWithModel(s.addonId, s.modelId))\n if (needed.length > 0) {\n this.log.info('Benchmark: models to load', { meta: { count: needed.length, models: needed.map(s => `${s.addonId}/${s.modelId}`) } })\n for (const s of needed) {\n emit(`Loading model: ${s.addonName} (${s.modelId})...`, { step: s.addonId, addonId: s.addonId, modelId: s.modelId })\n }\n await this.ensureModelsForSteps(benchmarkSteps)\n emit(`All models loaded`)\n }\n\n // Build a custom tree from the benchmark steps (not the global config)\n emit('Running inference...')\n const tree = buildExecutableTree(\n benchmarkSteps,\n (stepId) => this.engineFactory!.getEngine(stepId),\n )\n // Setup phase ends at tree-build (last sync work before inference).\n // Excludes decode (already recorded) and the executor.run call itself.\n setupMs = performance.now() - wallT0 - decodeMs\n\n // Runtime (per-frame) dispatch wants minimal tracing; one-shot paths\n // (benchmark / reference-image) run on demand and can afford full\n // stepTimings. `summary` in runtime mode keeps detection-pipeline's\n // emit of PipelineInferenceResult informative without the per-step\n // trace overhead. Apply per-device overrides when deviceId is known.\n const isRuntime = Boolean(input.frame)\n const effectiveDeviceId = input.deviceId ?? 0\n const deviceOverrides = effectiveDeviceId > 0 ? await this.readDeviceStore(effectiveDeviceId) : {}\n const effectiveTree = Object.keys(deviceOverrides).length > 0\n ? applyDeviceOverridesToTree(tree, 'object-detection', deviceOverrides)\n : tree\n const { result, trace } = await executor.run(\n effectiveTree,\n rootInput,\n jpegProvider,\n imageWidth,\n imageHeight,\n effectiveDeviceId,\n { traceVerbosity: isRuntime ? (this.eventBus ? 'summary' : 'off') : 'full' },\n )\n\n if (isRuntime) {\n // Runtime path mirrors the legacy `runFrame` behaviour: emit a\n // `pipeline.trace` event (so the admin UI's trace consumer sees\n // per-camera runs) and skip the benchmark-only per-step progress\n // spam.\n if (trace && this.eventBus) {\n this.eventBus.emit(createEvent(\n EventCategory.PipelineTrace,\n { type: 'device', id: trace.deviceId, nodeId: 'hub' },\n trace,\n ))\n }\n // Source-level zone gate — reads zones + detection rules off the\n // device-state mirror via the cached DeviceProxy and drops\n // detections in operator-excluded zones BEFORE they reach the\n // tracker. Async ensureDeviceProxy primes the cache on the\n // first frame; subsequent frames hit the gate sync. Skipping\n // the gate when proxy is unavailable preserves the legacy\n // \"no filtering\" behaviour for non-runtime callers + boot.\n if (effectiveDeviceId > 0) {\n await this.ensureDeviceProxy(effectiveDeviceId)\n return this.gateDetectionsByZoneRules(effectiveDeviceId, result)\n }\n return result\n }\n\n // Emit one pipeline.progress per executed step so the UI can show a\n // per-step timing breakdown even though the executor doesn't stream\n // progress callbacks mid-run. stepTimings is populated in `full`\n // trace mode (always true for benchmark / reference-image runs).\n for (const t of result.debug?.stepTimings ?? []) {\n emit(`${t.source}${t.modelId ? ` (${t.modelId})` : ''} → ${t.ms}ms`, {\n step: t.source,\n addonId: t.source,\n modelId: t.modelId ?? undefined,\n ms: t.ms,\n })\n }\n\n emit(`Done — ${result.detections.length} detection(s) in ${result.debug?.totalInferenceMs ?? 0}ms`, {\n ms: result.debug?.totalInferenceMs,\n })\n const wallMs = performance.now() - wallT0\n const inferMs = result.debug?.totalInferenceMs ?? 0\n const overheadMs = Math.max(0, wallMs - decodeMs - setupMs - inferMs)\n const augmented: FrameResult = {\n ...result,\n debug: {\n ...(result.debug ?? {}),\n decodeMs: Math.round(decodeMs * 100) / 100,\n setupMs: Math.round(setupMs * 100) / 100,\n wallMs: Math.round(wallMs * 100) / 100,\n overheadMs: Math.round(overheadMs * 100) / 100,\n },\n }\n return augmented\n } finally {\n // Restore pre-override state so runtime camera dispatch keeps\n // running on `priorFactory`. Override factory stays alive in\n // `overrideCache` until the idle TTL expires, so the next same-\n // engine call avoids the spin-up cost.\n if (differs && this.overrideCache) {\n // Update cache executor + lastUsedMs (we may have built executor\n // after cache pre-populate during the inference phase).\n this.overrideCache.executor = this.executor\n this.overrideCache.lastUsedMs = Date.now()\n this.scheduleOverrideCacheEviction()\n }\n if (differs) {\n this.currentEngine = priorEngine\n this.engineFactory = priorFactory\n this.executor = priorExecutor\n }\n }\n }\n\n /**\n * Apply a benchmark engine override (mirroring the body of `runPipeline`\n * lines 863-953). Swaps `currentEngine` / `engineFactory` / `executor`\n * to the override's warm factory and returns a restore function the\n * caller MUST invoke in `finally`. When `override` is undefined or\n * matches the current engine, this is a no-op and the restore function\n * does nothing — same shape so callers don't need a conditional path.\n *\n * Used by both `runPipeline` (single-frame benchmark) and\n * `runPipelineBatch` (batched fast path). Without this, the batch\n * path silently ignored `input.engine` and ran the user's override\n * against the addon's saved engine — making the override matrix in\n * the bench UI a no-op for batchSize > 1.\n */\n private async applyEngineOverride(\n override: PipelineEngineChoice | undefined,\n ): Promise<() => void> {\n const priorEngine = this.currentEngine\n const priorFactory = this.engineFactory\n const priorExecutor = this.executor\n if (!override) return () => { /* no override — nothing to restore */ }\n\n const runEngine: PipelineEngineChoice = {\n runtime: override.runtime,\n backend: override.backend,\n format: override.format,\n ...(override.device || priorEngine.device\n ? { device: override.device ?? priorEngine.device }\n : {}),\n }\n const differs =\n priorEngine.runtime !== runEngine.runtime ||\n priorEngine.backend !== runEngine.backend ||\n priorEngine.format !== runEngine.format ||\n (priorEngine.device ?? '') !== (runEngine.device ?? '')\n if (!differs) return () => { /* same engine — nothing to swap */ }\n\n const cacheHit = this.overrideCache && enginesEqual(this.overrideCache.engine, runEngine)\n if (cacheHit) {\n const cached = this.overrideCache!\n if (cached.initPromise) await cached.initPromise\n this.log.debug('Benchmark: reusing warm override factory', {\n meta: { engine: `${runEngine.runtime}/${runEngine.backend}/${runEngine.device ?? 'default'}` },\n })\n this.currentEngine = runEngine\n this.engineFactory = cached.factory\n this.executor = cached.executor\n } else {\n if (this.overrideCache) {\n try { await this.overrideCache.factory.dispose() } catch {\n /* stale cache dispose error — ignore */\n }\n this.overrideCache = null\n }\n this.log.info('Benchmark: engine override', {\n meta: {\n from: `${priorEngine.runtime}/${priorEngine.backend}`,\n to: `${runEngine.runtime}/${runEngine.backend}`,\n },\n })\n await this.ensureBackendDeps(runEngine)\n const newFactory = new EngineFactory({\n engine: runEngine,\n modelsDir: this.modelsDir,\n logger: this.log.child('engine-override'),\n pythonPath: this.executorOptions.pythonPath ?? '',\n concurrency: this.executorOptions.concurrency,\n tuning: this.executorOptions.tuning,\n numWorkers: this.executorOptions.numWorkers,\n })\n const initPromise = newFactory.initialize([])\n this.overrideCache = {\n engine: runEngine,\n factory: newFactory,\n executor: null,\n lastUsedMs: Date.now(),\n initPromise,\n }\n this.scheduleOverrideCacheEviction()\n try {\n await initPromise\n this.overrideCache.initPromise = null\n } catch (err) {\n this.overrideCache = null\n if (this.overrideCacheTimer) {\n clearTimeout(this.overrideCacheTimer)\n this.overrideCacheTimer = null\n }\n throw err\n }\n this.currentEngine = runEngine\n this.engineFactory = newFactory\n this.executor = null\n }\n return () => {\n if (this.overrideCache) {\n this.overrideCache.executor = this.executor\n this.overrideCache.lastUsedMs = Date.now()\n this.scheduleOverrideCacheEviction()\n }\n this.currentEngine = priorEngine\n this.engineFactory = priorFactory\n this.executor = priorExecutor\n }\n }\n\n /**\n * Schedule eviction of the override cache after `OVERRIDE_CACHE_TTL_MS`\n * of idleness. Rescheduling resets the timer so an active benchmark\n * keeps its warm factory alive until iterations stop arriving.\n */\n private scheduleOverrideCacheEviction(): void {\n if (this.overrideCacheTimer) clearTimeout(this.overrideCacheTimer)\n this.overrideCacheTimer = setTimeout(() => {\n void this.evictOverrideCache('idle TTL')\n }, DetectionPipelineProvider.OVERRIDE_CACHE_TTL_MS)\n }\n\n private async evictOverrideCache(reason: string): Promise<void> {\n if (!this.overrideCache) return\n const entry = this.overrideCache\n this.overrideCache = null\n if (this.overrideCacheTimer) {\n clearTimeout(this.overrideCacheTimer)\n this.overrideCacheTimer = null\n }\n try {\n await entry.factory.dispose()\n this.log.info('Override cache evicted', {\n meta: {\n reason,\n engine: `${entry.engine.runtime}/${entry.engine.backend}/${entry.engine.device ?? 'default'}`,\n ageMs: Date.now() - entry.lastUsedMs,\n },\n })\n } catch (err) {\n this.log.warn('Override cache dispose failed', { meta: { error: err instanceof Error ? err.message : String(err) } })\n }\n }\n\n /** Convert PipelineStepInput[] → PipelineDefaultStep[] by enriching from StepDefinitions */\n private inputStepsToPipelineSteps(steps: readonly import('@camstack/types').PipelineStepInput[]): PipelineDefaultStep[] {\n return steps.map((s) => {\n const def = getStepDefinition(s.addonId)\n return {\n addonId: s.addonId,\n addonName: def.name,\n slot: def.slot,\n inputClasses: def.inputClasses ?? [],\n outputClasses: def.outputClasses,\n enabled: s.enabled,\n modelId: s.modelId,\n children: s.children ? this.inputStepsToPipelineSteps(s.children) : [],\n ...(s.settings ? { settings: { ...s.settings } } : {}),\n }\n })\n }\n\n /**\n * Single-flight gate around `ensureModelsForSteps`. Concurrent\n * callers (every camera that fires motion in the same window calls\n * `runPipeline` → `ensureModelsForSteps`) share the same in-flight\n * promise instead of each racing into `loadAdditional`. Without this\n * the pool gets the SAME model loaded N times under N distinct\n * pool indices because every caller saw `isLoadedWithModel === false`\n * before any of them committed via `stepToLoaded.set`.\n */\n private modelLoadInFlight: Promise<void> | null = null\n\n /** Ensure all models needed by steps are downloaded and loaded in the engine pool. */\n private async ensureModelsForSteps(steps: readonly PipelineDefaultStep[]): Promise<void> {\n // Serialize. Subsequent callers await the in-flight promise; once\n // it resolves they re-check `isLoadedWithModel` (state may have\n // been advanced by the leader), and only the actual leader\n // performs the download + load.\n while (this.modelLoadInFlight) {\n await this.modelLoadInFlight\n }\n const allEnabled = flattenSteps(steps)\n const needed: PipelineDefaultStep[] = []\n for (const step of allEnabled) {\n if (!step.enabled) continue\n if (this.engineFactory?.isLoadedWithModel(step.addonId, step.modelId)) continue\n needed.push(step)\n }\n if (needed.length === 0) return\n\n const work = (async () => {\n // Download any missing models before loading\n const format = this.currentEngine?.format ?? 'onnx'\n for (const step of needed) {\n const def = getStepDefinition(step.addonId)\n const modelEntry = def.models.find(m => m.id === step.modelId)\n if (modelEntry && !isModelDownloaded(this.modelsDir, modelEntry, format)) {\n this.log.info('Downloading model for step', { meta: { modelId: step.modelId, format, step: step.addonId } })\n await this.downloadWithRetry(modelEntry, format, 3)\n }\n }\n\n this.log.info('Loading additional models for benchmark', { meta: { count: needed.length, models: needed.map(s => `${s.addonId}/${s.modelId}`) } })\n await this.engineFactory!.loadAdditional(needed)\n })()\n this.modelLoadInFlight = work\n try {\n await work\n } finally {\n if (this.modelLoadInFlight === work) this.modelLoadInFlight = null\n }\n }\n\n /** Download a model with retry + exponential backoff */\n private async downloadWithRetry(\n entry: import('@camstack/types').ModelCatalogEntry,\n format: import('@camstack/types').ModelFormat,\n maxRetries: number,\n onProgress?: import('@camstack/core').DownloadProgressCallback,\n ): Promise<void> {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n await ensureModel(this.modelsDir, entry, format, onProgress)\n this.log.info('Model downloaded successfully', { meta: { modelId: entry.id } })\n return\n } catch (err: unknown) {\n const msg = errMsg(err)\n if (attempt < maxRetries) {\n const delayMs = 2000 * Math.pow(2, attempt - 1) // 2s, 4s, 8s\n this.log.warn('Model download failed — retrying', { meta: { modelId: entry.id, attempt, maxRetries, error: msg, retryDelayMs: delayMs } })\n await new Promise(resolve => setTimeout(resolve, delayMs))\n } else {\n throw new Error(`Failed to download model \"${entry.id}\" after ${maxRetries} attempts: ${msg}`, { cause: err })\n }\n }\n }\n }\n\n /** Parse JPEG/PNG dimensions without decoding the full image */\n private async getJpegDimensions(buf: Buffer): Promise<{ width: number; height: number }> {\n // Use sharp metadata (fast, reads only header)\n try {\n const sharp = (await import('sharp')).default\n const { width, height } = await sharp(buf).metadata()\n return { width: width ?? 640, height: height ?? 640 }\n } catch {\n // Fallback: JPEG SOF marker parsing\n return this.parseJpegDimensions(buf)\n }\n }\n\n /** Fast JPEG dimension parsing from SOF marker (no external deps) */\n private parseJpegDimensions(buf: Buffer): { width: number; height: number } {\n let i = 2 // skip SOI\n while (i < buf.length - 1) {\n if (buf[i] !== 0xFF) break\n const marker = buf[i + 1]!\n if (marker >= 0xC0 && marker <= 0xCF && marker !== 0xC4 && marker !== 0xC8 && marker !== 0xCC) {\n const height = buf.readUInt16BE(i + 5)\n const width = buf.readUInt16BE(i + 7)\n return { width, height }\n }\n const segLen = buf.readUInt16BE(i + 2)\n i += 2 + segLen\n }\n return { width: 640, height: 640 }\n }\n\n async detect(input: { addonId: string; frame: FrameInput; config?: Record<string, unknown> }): Promise<DetectorOutput> {\n const { addonId, frame } = input\n const { executor, tree } = await this.ensureExecutor()\n\n const data = Buffer.from(frame.data)\n let rootInput: InferenceInput\n let jpegProvider: () => Promise<Buffer>\n if (frame.format === 'jpeg') {\n rootInput = { kind: 'jpeg', data }\n jpegProvider = async () => data\n } else if (frame.format === 'rgb' || frame.format === 'bgr' || frame.format === 'gray') {\n const channels = frame.format === 'gray' ? 1 : 3\n const sharp = (await import('sharp')).default\n const jpeg = await sharp(data, { raw: { width: frame.width, height: frame.height, channels } })\n .jpeg({ quality: 85, mozjpeg: false })\n .toBuffer()\n rootInput = { kind: 'jpeg', data: jpeg }\n jpegProvider = async () => jpeg\n } else {\n const sharp = (await import('sharp')).default\n const encoded = await sharp(data, { raw: { width: frame.width, height: frame.height, channels: 3 } })\n .jpeg({ quality: 80, mozjpeg: false })\n .toBuffer()\n rootInput = { kind: 'jpeg', data: encoded }\n jpegProvider = async () => encoded\n }\n const { result } = await executor.run(tree, rootInput, jpegProvider, frame.width, frame.height, 0)\n\n // The single-addon `detect()` cap returns the legacy\n // `DetectorOutput` shape (used by device-level engines that don't\n // participate in the full pipeline). Project the new `FrameResult`\n // first-level entries into that shape.\n return {\n detections: result.detections\n .filter((d) => d.kind === 'first-level')\n .map((d) => ({\n class: d.macroClass,\n originalClass: d.debug?.originalClass ?? d.macroClass,\n score: d.score,\n bbox: { x: d.bbox.x, y: d.bbox.y, w: d.bbox.width, h: d.bbox.height },\n })),\n inferenceMs: result.debug?.totalInferenceMs ?? 0,\n modelId: addonId,\n }\n }\n\n /**\n * Batched run — dispatches N raw frames against the same loaded\n * single-step model in one IPC round-trip via\n * `EngineFactory.batchInferRaw`. Falls back to N parallel\n * `runPipeline` calls when the fast path's preconditions don't hold\n * (multi-step tree, crop children, JPEG-only frames, Node ONNX\n * factory). Returns one minimally-shaped `FrameResult` per input\n * frame in input order.\n *\n * Used by `scripts/bench-scrypted-style.mts` for the\n * Scrypted-equivalent benchmark — Scrypted's `detectObjects(media,\n * {batch})` collapses N requests into one call too, so bypassing\n * the per-call IPC overhead is what makes the comparison fair.\n */\n async runPipelineBatch(input: {\n readonly steps: readonly import('@camstack/types').PipelineStepInput[]\n readonly engine?: PipelineEngineChoice\n readonly frames: readonly FrameInput[]\n readonly deviceId?: number\n readonly sessionId?: string\n }): Promise<{ readonly results: readonly FrameResult[] }> {\n this.log.debug('runPipelineBatch entry', {\n meta: {\n phase: 'batch',\n frames: input.frames.length,\n steps: input.steps.length,\n firstFrame: input.frames[0] ? { w: input.frames[0].width, h: input.frames[0].height, fmt: input.frames[0].format, bytes: input.frames[0].data.byteLength } : null,\n },\n })\n try {\n return await this.runPipelineBatchImpl(input)\n } catch (err: unknown) {\n const msg = errMsg(err)\n const stack = err instanceof Error ? err.stack : undefined\n this.log.error('runPipelineBatch failed', { meta: { phase: 'batch', error: msg, stack } })\n throw err\n }\n }\n\n private async runPipelineBatchImpl(input: {\n readonly steps: readonly import('@camstack/types').PipelineStepInput[]\n readonly engine?: PipelineEngineChoice\n readonly frames: readonly FrameInput[]\n readonly deviceId?: number\n readonly sessionId?: string\n }): Promise<{ readonly results: readonly FrameResult[] }> {\n if (input.frames.length === 0) return { results: [] }\n\n const benchmarkSteps = this.inputStepsToPipelineSteps(input.steps)\n const enabledSteps = flattenEnabledVideoSteps(benchmarkSteps)\n if (enabledSteps.length === 0) {\n throw new Error('runPipelineBatch: no enabled steps')\n }\n const rootStep = enabledSteps[0]\n if (!rootStep) {\n throw new Error('runPipelineBatch: empty enabled-steps after flatten')\n }\n this.log.debug('runPipelineBatch enabled steps', {\n meta: {\n phase: 'batch',\n count: enabledSteps.length,\n first: { addonId: rootStep.addonId, modelId: rootStep.modelId, hasChildren: (rootStep.children?.length ?? 0) > 0 },\n },\n })\n\n // Fast path requirements:\n // - exactly one root step (no children produced by tree-builder)\n // - every frame raw rgb/bgr/gray with the same dimensions\n // - factory exposes batch (Python pool path)\n const allRaw = input.frames.every((f) => f.format === 'rgb' || f.format === 'bgr' || f.format === 'gray')\n const firstFrame = input.frames[0]\n if (!firstFrame) {\n throw new Error('runPipelineBatch: empty frames after length check')\n }\n const uniformDims = input.frames.every((f) => f.width === firstFrame.width && f.height === firstFrame.height)\n // `enabledSteps` is the DFS-flattened ENABLED-only list. If it has\n // exactly one entry, the root has no enabled descendants — the\n // structural `rootStep.children` may still hold the detector's\n // disabled cropper/classifier tree the UI ships verbatim from\n // PipelineBuilder, so checking `rootStep.children.length === 0`\n // would wrongly reject the fast path on every benchmark run.\n const singleRoot = enabledSteps.length === 1\n\n // Bootstrap on the existing executor singleton — same code path as\n // benchmark's `runPipeline`. Models are loaded if missing.\n await this.ensureEngineFactory()\n\n // Apply per-run engine override (benchmark UI's override matrix).\n // Mirrors `runPipeline`'s override-cache pattern so batchSize > 1\n // honors the same `input.engine` payload as single-frame runs.\n // Without this, the fast path silently ran every batch on the\n // addon's saved engine and the override was a UI no-op.\n const restoreEngine = await this.applyEngineOverride(input.engine)\n try {\n const factory = this.engineFactory\n if (!factory) throw new Error('runPipelineBatch: factory not initialised')\n\n const needed = enabledSteps.filter((s) => !factory.isLoadedWithModel(s.addonId, s.modelId))\n if (needed.length > 0) {\n await this.ensureModelsForSteps(benchmarkSteps)\n }\n\n const canFastPath = singleRoot && allRaw && uniformDims && factory.supportsBatch()\n this.log.info('runPipelineBatch path decision', {\n meta: { phase: 'batch', canFastPath, singleRoot, allRaw, uniformDims, supportsBatch: factory.supportsBatch() },\n })\n\n if (!canFastPath) {\n // Correctness fallback — runs the full executor per frame in\n // parallel. Loses the IPC amortisation but keeps the cap usable\n // for any tree shape. Override already applied at the provider\n // level, so per-frame runPipeline calls inherit the swapped\n // factory; we do NOT pass `input.engine` again to avoid a\n // double-swap on every frame.\n const results = await Promise.all(\n input.frames.map((frame) => this.runPipeline({\n steps: input.steps,\n frame,\n deviceId: input.deviceId,\n sessionId: input.sessionId,\n })),\n )\n return { results }\n }\n\n // Fast path — single batched IPC frame.\n const items = input.frames.map((frame) => ({\n raw: Buffer.from(frame.data),\n width: frame.width,\n height: frame.height,\n format: frame.format as RawFrameFormat,\n }))\n // Pass the bench-requested `modelId` explicitly so multi-slot\n // resolves to the warm bench variant instead of the runtime\n // active model. Before multi-slot, `loadAdditional` swapped\n // models inside a single slot — now it allocates a separate\n // index per (stepId, modelId), and we have to address it by\n // the same key here.\n this.log.info('runPipelineBatch fast path dispatch', {\n meta: { phase: 'batch', stepId: rootStep.addonId, modelId: rootStep.modelId, items: items.length, totalRawBytes: items.reduce((s, it) => s + it.raw.length, 0) },\n })\n const start = performance.now()\n const rawResults = await factory.batchInferRaw(rootStep.addonId, items, rootStep.modelId)\n const totalMs = performance.now() - start\n this.log.info('runPipelineBatch fast path returned', {\n meta: { phase: 'batch', resultCount: rawResults.length, ms: Math.round(totalMs) },\n })\n\n const perFrameMs = totalMs / rawResults.length\n return {\n results: rawResults.map((raw, i) => assembleBatchedFrameResult(\n raw,\n input.frames[i]!,\n rootStep.addonId,\n input.deviceId ?? 0,\n perFrameMs,\n perFrameMs,\n )),\n }\n } finally {\n restoreEngine()\n }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Reference Images\n // ═══════════════════════════════════════════════════════════════════════════\n\n async listReferenceImages(): Promise<Array<{ id: string; filename: string; sizeKB: number }>> {\n const dir = this.resolveRefImagesDir()\n if (!dir) return []\n return fs.readdirSync(dir)\n .filter((f) => /\\.(jpg|jpeg|png)$/i.test(f))\n .sort()\n .map((f) => {\n const stat = fs.statSync(path.join(dir, f))\n return { id: f.replace(/\\.[^.]+$/, ''), filename: f, sizeKB: Math.round(stat.size / 1024) }\n })\n }\n\n async getReferenceImage(input: { filename: string }): Promise<{ base64: string; filename: string } | null> {\n const { filename } = input\n const dir = this.resolveRefImagesDir()\n if (!dir) return null\n const filePath = path.join(dir, filename)\n if (!fs.existsSync(filePath)) return null\n return { base64: fs.readFileSync(filePath).toString('base64'), filename }\n }\n\n private resolveRefImagesDir(): string | null {\n const candidates = [\n path.resolve(process.cwd(), 'assets/reference-images'),\n path.resolve(process.cwd(), '../assets/reference-images'),\n path.resolve(process.cwd(), '../../assets/reference-images'),\n ]\n return candidates.find((p) => fs.existsSync(p)) ?? null\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Templates\n // ═══════════════════════════════════════════════════════════════════════════\n\n async listTemplates(): Promise<PipelineTemplate[]> {\n const store = await this.readStore()\n const raw = store[KEY_TEMPLATES] as string | undefined\n const templates = readStored(raw, isTemplateArray) ?? []\n const migrated = migrateTemplates(templates)\n if (migrated.changed) {\n void this.writeStore({ [KEY_TEMPLATES]: JSON.stringify(migrated.templates) })\n this.log.info('Migrated stored template steps — removed obsolete \"confidence\" field', { meta: { stripped: migrated.stripped } })\n }\n return migrated.templates\n }\n\n async saveTemplate(input: { name: string; steps: readonly PipelineTemplateStep[]; engine: PipelineEngineChoice }): Promise<PipelineTemplate> {\n const { name, steps, engine } = input\n const templates = await this.listTemplates()\n const template: PipelineTemplate = {\n id: `tpl-${Date.now()}`,\n name,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n engine,\n steps: [...steps],\n }\n templates.push(template)\n await this.writeStore({ [KEY_TEMPLATES]: JSON.stringify(templates) })\n return template\n }\n\n async updateTemplate(input: { id: string; name?: string; steps?: readonly PipelineTemplateStep[] }): Promise<PipelineTemplate> {\n const { id, ...update } = input\n const templates = await this.listTemplates()\n const idx = templates.findIndex((t) => t.id === id)\n if (idx === -1) throw new Error(`Template not found: ${id}`)\n\n const existing = templates[idx]!\n const updated: PipelineTemplate = {\n ...existing,\n ...(update.name !== undefined ? { name: update.name } : {}),\n ...(update.steps !== undefined ? { steps: [...update.steps] } : {}),\n updatedAt: new Date().toISOString(),\n }\n templates[idx] = updated\n await this.writeStore({ [KEY_TEMPLATES]: JSON.stringify(templates) })\n return updated\n }\n\n async deleteTemplate(input: { id: string }): Promise<void> {\n const { id } = input\n const templates = (await this.listTemplates()).filter((t) => t.id !== id)\n await this.writeStore({ [KEY_TEMPLATES]: JSON.stringify(templates) })\n }\n\n // `orchestratorStatus` and `cameraDetectionStatus` shims removed with the\n // pipeline page redesign — UI consumers now query runner metrics directly\n // via `perNodePipeline.getLocalMetrics` / `getCameraMetrics`, which forward\n // to the `pipeline-runner` cap on the target node.\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Per-device detection settings — moved into the Orchestrator tab.\n //\n // Object-detection step settings (enabled classes, per-class confidence\n // thresholds) live on the audio/video pipeline step's `getConfigSchema()`\n // and are edited via the orchestrator's per-step override mechanism. No\n // device-level surface remains on this addon.\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getDeviceSettingsContribution(_input: { deviceId: number }): Promise<ConfigUISchemaWithValues | null> {\n return null\n }\n\n async getDeviceLiveContribution(_input: { deviceId: number }): Promise<ConfigUISchemaWithValues | null> {\n return null\n }\n\n async applyDeviceSettingsPatch(_input: { deviceId: number; patch: Record<string, unknown> }): Promise<{ success: true }> {\n return { success: true }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Per-device pipeline step overrides\n //\n // Each device can override the global pipeline with its own step array.\n // Storage lives inside this addon's own IElementConfig namespace under\n // `devicePipelineSteps:<deviceId>` keys — no backend ConfigService\n // involvement. This makes the addon self-sufficient for per-device\n // pipeline overrides (Fase H.2 / capability self-sufficiency rule).\n // ═══════════════════════════════════════════════════════════════════════════\n\n // Per-device pipeline step CRUD removed in Phase 4b. The orchestrator\n // owns `pipelines/<deviceId>` — consumers call\n // `pipelineOrchestrator.{get,set,list}CameraPipeline` instead.\n\n // ═══════════════════════════════════════════════════════════════════════════\n // Addon Resolver\n // ═══════════════════════════════════════════════════════════════════════════\n\n async getAddonResolver(): Promise<IAddonResolver> {\n // The new pipeline doesn't use IAddonResolver — engines are managed directly.\n // Return a stub that throws to make it clear.\n return {\n resolve: async () => {\n throw new Error('IAddonResolver is deprecated in addon-detection-pipeline. Use EngineFactory directly.')\n },\n shutdownAll: async () => {\n if (this.engineFactory) {\n await this.engineFactory.dispose()\n }\n },\n }\n }\n\n\n async shutdown(): Promise<void> {\n await this.evictOverrideCache('shutdown')\n if (this.engineFactory) {\n await this.engineFactory.dispose()\n this.engineFactory = null\n this.executor = null\n }\n for (const id of [...this.deviceProxies.keys()]) this.releaseDeviceProxy(id)\n }\n\n /**\n * Resolve and cache a {@link DeviceProxy} for the given camera. Pins\n * the `state.zones` + `state.zoneRules` slice handles so `.value`\n * stays warm via the kernel runtime-state mirror, and runtime gate\n * reads in `runPipeline` are sync (zero per-frame round-trip).\n * Returns null when the addon context isn't available — the gate\n * falls back to \"no filtering\" instead of blocking inference.\n */\n private async ensureDeviceProxy(deviceId: number): Promise<DeviceProxy | null> {\n const cached = this.deviceProxies.get(deviceId)\n if (cached) return cached\n const ctx = this.addonCtx\n if (!ctx?.fetchDevice) return null\n try {\n const proxy = await ctx.fetchDevice(deviceId)\n this.deviceProxies.set(deviceId, proxy)\n const unsubs = [\n proxy.state.zones.subscribe(() => { /* keep slice warm */ }),\n proxy.state.zoneRules.subscribe(() => { /* keep slice warm */ }),\n ]\n this.proxyUnsubs.set(deviceId, unsubs)\n return proxy\n } catch (err: unknown) {\n this.log.debug('detection-pipeline ensureDeviceProxy failed — gating skipped', {\n tags: { deviceId },\n meta: { error: err instanceof Error ? err.message : String(err) },\n })\n return null\n }\n }\n\n private releaseDeviceProxy(deviceId: number): void {\n const unsubs = this.proxyUnsubs.get(deviceId)\n if (unsubs) {\n for (const u of unsubs) {\n try { u() } catch { /* swallow */ }\n }\n }\n this.proxyUnsubs.delete(deviceId)\n this.deviceProxies.delete(deviceId)\n }\n\n /**\n * Apply detection-stage zone rules to a {@link FrameResult}. Drops\n * first-level detections whose center lies in an `exclude` rule's\n * zones (or, in whitelist mode, isn't in any `include` rule's\n * zones), and any `detail` children whose `parentId` references a\n * dropped first-level. Returns the original result unchanged when\n * the gate has nothing to filter (no zones, no rules, or no\n * detections).\n */\n private gateDetectionsByZoneRules(deviceId: number, result: FrameResult): FrameResult {\n if (deviceId <= 0 || result.detections.length === 0) return result\n const proxy = this.deviceProxies.get(deviceId)\n if (!proxy) return result\n const zones = proxy.state.zones.value?.zones ?? []\n const rules = proxy.state.zoneRules.value?.detection ?? []\n if (zones.length === 0 || rules.length === 0) return result\n const frameW = result.width\n const frameH = result.height\n if (frameW === 0 || frameH === 0) return result\n\n // Partition first-level detections; details follow their parent.\n const firstLevel = result.detections.filter((d): d is ObjectDetection => d.kind === 'first-level')\n const details = result.detections.filter((d) => d.kind === 'detail')\n const { passed } = evaluateZoneRules(\n firstLevel,\n zones,\n rules,\n (det) => ({\n x: (det.bbox.x + det.bbox.width / 2) / frameW,\n y: (det.bbox.y + det.bbox.height / 2) / frameH,\n }),\n (det) => det.macroClass,\n )\n if (passed.length === firstLevel.length) return result\n const passedIds = new Set(passed.map((d) => d.id))\n const filteredDetails = details.filter((d) => {\n const parentId = (d as ObjectDetection).parentId\n return parentId === undefined || passedIds.has(parentId)\n })\n return {\n ...result,\n detections: [...passed, ...filteredDetails],\n }\n }\n\n // ═══════════════════════════════════════════════════════════════════════════\n // ── Bench frame cache (Python pool-side) ───────────────────────\n // Exposes the Python pool's frame cache to callers (pipeline-runner\n // bench actions). Caches 1.2MB once, subsequent infer calls send\n // only 5 bytes through the pipe.\n\n async cacheFrameInPool(input: {\n readonly data: Uint8Array\n readonly width: number\n readonly height: number\n readonly format: 'rgb' | 'bgr' | 'gray'\n }): Promise<{ frameId: number; width: number; height: number }> {\n await this.ensureEngineFactory()\n if (!this.engineFactory) throw new Error('cacheFrameInPool: factory not initialized')\n const buf = Buffer.from(input.data.buffer, input.data.byteOffset, input.data.byteLength)\n return this.engineFactory.cacheFrameInPool(buf, input.width, input.height, input.format)\n }\n\n async inferCached(input: {\n readonly stepId: string\n readonly frameId: number\n }): Promise<Record<string, unknown>> {\n await this.ensureEngineFactory()\n if (!this.engineFactory) throw new Error('inferCached: factory not initialized')\n return this.engineFactory.inferCached(input.stepId, input.frameId)\n }\n\n async uncacheFrame(input: { readonly frameId: number }): Promise<void> {\n if (!this.engineFactory) return\n await this.engineFactory.uncacheFrame(input.frameId)\n }\n\n async getEffectiveTuning(): Promise<{\n batchMode: string\n windowMs: number\n maxBatchSize: number\n concurrency: number\n }> {\n const t = this.executorOptions.tuning\n return {\n batchMode: t?.batchMode ?? 'none',\n windowMs: t?.windowMs ?? 0,\n maxBatchSize: t?.maxBatchSize ?? 1,\n concurrency: this.executorOptions.concurrency ?? 1,\n }\n }\n\n // Private\n // ═══════════════════════════════════════════════════════════════════════════\n\n /**\n * Ensure the engine factory + inference pool is initialized.\n * Does NOT require global steps — used by benchmark/test paths that provide their own steps.\n * If global steps exist, initializes with them. Otherwise, creates an empty pool.\n */\n private async ensureEngineFactory(): Promise<void> {\n if (this.initPromise) await this.initPromise\n if (this.engineFactory) return\n\n await this.ensureBackendDeps(this.currentEngine)\n this.engineFactory = new EngineFactory({\n engine: this.currentEngine,\n modelsDir: this.modelsDir,\n logger: this.log.child('engine'),\n pythonPath: this.executorOptions.pythonPath ?? '',\n concurrency: this.executorOptions.concurrency,\n tuning: this.executorOptions.tuning,\n numWorkers: this.executorOptions.numWorkers,\n })\n // Initialize with empty steps — just creates the pool/runtime\n await this.engineFactory.initialize([])\n }\n\n private async ensureExecutor(): Promise<{ executor: PipelineExecutor; tree: ReturnType<typeof buildExecutableTree> }> {\n // If already initializing, wait for the same promise (no duplicate init)\n if (this.initPromise) {\n await this.initPromise\n }\n\n if (!this.engineFactory) {\n this.initPromise = this.doInitialize()\n try {\n await this.initPromise\n } finally {\n this.initPromise = null\n }\n }\n\n if (!this.currentSteps) {\n throw new Error('No pipeline steps configured')\n }\n\n const tree = buildExecutableTree(\n this.currentSteps,\n (stepId) => this.engineFactory!.getEngine(stepId),\n )\n\n return { executor: this.executor!, tree }\n }\n\n /** Actual initialization — download models, create engine, load pool. Called once. */\n private async doInitialize(): Promise<void> {\n const steps = await this.getGlobalSteps()\n if (!steps) {\n throw new Error('No pipeline steps configured. Use setGlobalSteps() first.')\n }\n\n this.log.info('Pipeline initializing — frames will be dropped until ready')\n\n // Auto-download required models in parallel (IO-bound, no reason to serialize)\n const format = this.currentEngine.format\n const downloads: Array<Promise<void>> = []\n for (const step of flattenSteps(steps)) {\n const def = getStepDefinition(step.addonId)\n const modelEntry = def.models.find(m => m.id === step.modelId)\n if (!modelEntry) {\n this.log.warn('Model not found in step catalog — skipping download', { meta: { modelId: step.modelId, step: step.addonId } })\n continue\n }\n if (!isModelDownloaded(this.modelsDir, modelEntry, format)) {\n this.log.info('Downloading model', { meta: { modelId: step.modelId, format } })\n }\n downloads.push(ensureModel(this.modelsDir, modelEntry, format).then(() => {}))\n }\n await Promise.all(downloads)\n\n await this.ensureBackendDeps(this.currentEngine)\n this.engineFactory = new EngineFactory({\n engine: this.currentEngine,\n modelsDir: this.modelsDir,\n logger: this.log.child('engine'),\n pythonPath: this.executorOptions.pythonPath ?? '',\n concurrency: this.executorOptions.concurrency,\n tuning: this.executorOptions.tuning,\n numWorkers: this.executorOptions.numWorkers,\n })\n\n await this.engineFactory.initialize(steps)\n this.currentSteps = steps\n\n const runtimeStr = `${this.currentEngine.runtime}+${this.currentEngine.backend}`\n this.executor = new PipelineExecutor({ engineRuntime: runtimeStr, logger: this.log })\n this.ready = true\n this.log.info('Pipeline ready — accepting frames')\n }\n\n /**\n * Phase 2b — resolve the engine from the addon's new schema-backed\n * fields (`engineRuntime`, `engineBackend`, `engineDevice`). Fallback\n * to the legacy `KEY_ENGINE` JSON blob for pre-migration stores.\n * Returns null when neither source has anything; the caller keeps\n * whatever `detectBestEngine()` picked at construction.\n */\n private async loadEngine(): Promise<PipelineEngineChoice | null> {\n const store = await this.readStore()\n const runtime = store['engineRuntime']\n const backend = store['engineBackend']\n if (typeof runtime === 'string' && typeof backend === 'string' && runtime && backend) {\n const storedDevice = typeof store['engineDevice'] === 'string' ? String(store['engineDevice']) : ''\n const detected = DetectionPipelineProvider.detectBestEngine()\n // Guard against a persisted choice that can't boot on this node\n // (e.g. operator picked OpenVINO on a macOS without the Python\n // module, then the addon subprocess crashes on import and the\n // worker never registers its cap — knocking out the Pipeline +\n // Engine tabs). If the stored backend is unavailable, fall back\n // to the detected best so the worker boots and the UI can show\n // the dropdown for a fresh pick.\n if (runtime === 'python' && !DetectionPipelineProvider.isPythonBackendAvailable(backend)) {\n this.log.warn('Stored engine backend unavailable on this node — falling back to detected best', {\n meta: { stored: `${runtime}/${backend}`, fallback: `${detected.runtime}/${detected.backend}` },\n })\n return detected\n }\n const device = storedDevice || detected.device\n return {\n runtime: runtime === 'node' ? 'node' : 'python',\n backend,\n format: backendToFormat(backend),\n ...(device ? { device } : {}),\n }\n }\n const raw = store[KEY_ENGINE] as string | undefined\n return readStored(raw, isEngineChoice)\n }\n\n /**\n * Synchronous availability probe for a Python inference backend. Runs a\n * short `python3 -c \"import <mod>\"` with a 3s timeout. Used at\n * `loadEngine` time to reject a persisted backend choice the Python\n * interpreter on this host can't actually import — without this the\n * detection-pipeline child process exits with code 1 the moment\n * `inference_pool.py` hits its `from openvino.runtime import Core`.\n */\n private static isPythonBackendAvailable(backend: string): boolean {\n const moduleByBackend: Record<string, string | null> = {\n coreml: 'coremltools',\n openvino: 'openvino.runtime',\n onnx: 'onnxruntime',\n }\n const mod = moduleByBackend[backend]\n if (mod === null) return true // nothing to probe — treat as available\n if (!mod) return false\n try {\n const { execFileSync } = require('node:child_process') as typeof import('node:child_process')\n execFileSync('python3', ['-c', `import ${mod}`], { timeout: 3000, stdio: 'pipe' })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Re-run the platform probe for the inference engine and persist the\n * result into the addon's own global-settings keys. Because those\n * fields are declared `requiresRestart: true`, the\n * `updateGlobalSettings` pathway auto-schedules an addon restart —\n * here we do the write directly via the settings store and trigger\n * restart manually through the addons cap.\n *\n * Returns the chosen engine so the UI can confirm the pick without a\n * second round-trip.\n */\n /**\n * Phase 2c — flat per-addonId map holding the video-pipeline step\n * config (`modelId` / `settings`). No `enabled` field: whether an\n * addon step runs is a capability-binding concern, not a\n * pipeline-config one. Replaces the orchestrator's per-agent\n * `addonDefaults`. The map is the source of truth; the tree consumed\n * by the executor (`PipelineDefaultStep[]`) is materialised from\n * this map + catalog compat at read time (not done in this commit —\n * phase 2f wires the resolver through).\n */\n async getVideoPipelineSteps(): Promise<Record<string, {\n modelId: string\n settings: Readonly<Record<string, unknown>>\n }>> {\n const store = await this.readStore()\n const raw = store['videoPipelineSteps']\n if (raw === undefined || raw === null) return {}\n if (typeof raw !== 'object') return {}\n const result: Record<string, {\n modelId: string\n settings: Readonly<Record<string, unknown>>\n }> = {}\n for (const [addonId, cfg] of Object.entries(raw as Record<string, unknown>)) {\n if (!cfg || typeof cfg !== 'object') continue\n const c = cfg as Record<string, unknown>\n if (typeof c.modelId !== 'string') continue\n result[addonId] = {\n modelId: c.modelId,\n settings: (c.settings && typeof c.settings === 'object') ? (c.settings as Record<string, unknown>) : {},\n }\n }\n return result\n }\n\n async setVideoPipelineSteps(input: {\n readonly steps: Record<string, {\n modelId: string\n settings: Readonly<Record<string, unknown>>\n }>\n }): Promise<{ success: true }> {\n // Strip any legacy `enabled` field that a pre-migration caller may\n // still be sending — storage is `enabled`-free.\n const sanitized: Record<string, unknown> = {}\n for (const [addonId, cfg] of Object.entries(input.steps)) {\n sanitized[addonId] = {\n modelId: cfg.modelId,\n settings: { ...cfg.settings },\n }\n }\n await this.writeStore({ videoPipelineSteps: sanitized })\n this.log.info('videoPipelineSteps updated', { meta: { addons: Object.keys(sanitized).length } })\n return { success: true as const }\n }\n\n // ── Engine lifecycle (pipeline-executor cap) ─────────────────────────\n //\n // The detection-pipeline provider holds at most ONE engine at a time\n // (`currentEngine` + lazy `engineFactory`). These methods surface that\n // engine to the Pipeline → Engines tab, so the operator can see what's\n // resident in RAM and evict it without a full addon restart.\n //\n // `inUseByCameras` is not tracked here — it depends on pipeline-runner\n // attach state which lives in a different process. The UI joins this\n // list with runner.listAttached() to compute the \"in use\" column.\n\n async listLoadedEngines(): Promise<readonly {\n engineKey: string\n engine: PipelineEngineChoice\n modelsLoaded: readonly string[]\n inUseByCameras: readonly number[]\n kind: 'runtime' | 'warm-override'\n poolPid: number | null\n idleMs: number | null\n idleTtlMs: number | null\n }[]> {\n const out: {\n engineKey: string\n engine: PipelineEngineChoice\n modelsLoaded: readonly string[]\n inUseByCameras: readonly number[]\n kind: 'runtime' | 'warm-override'\n poolPid: number | null\n idleMs: number | null\n idleTtlMs: number | null\n }[] = []\n\n // Dedupe: during an override run `this.engineFactory` is temporarily\n // re-pointed at the override factory (until the `finally` restores\n // priorFactory). A poll landing in that window would see TWO entries\n // backed by the same factory — runtime row flickers between\n // `[loaded models]` and `[empty]` on each refresh as the pointer\n // hops. Treat the override-factory case as \"warm only\" so the UI\n // shows one stable row per resident factory.\n const overrideFactory = this.overrideCache?.factory ?? null\n const runtimeFactoryIsOverride =\n this.engineFactory !== null && this.engineFactory === overrideFactory\n\n if (this.engineFactory && !runtimeFactoryIsOverride) {\n const eng = this.currentEngine\n const engineKey = `${eng.runtime}/${eng.backend}/${eng.device ?? 'default'}`\n const loaded = this.engineFactory.listLoaded()\n out.push({\n engineKey,\n engine: eng,\n modelsLoaded: loaded.map((l) => `${l.stepId}/${l.modelId}`),\n inUseByCameras: [],\n kind: 'runtime',\n poolPid: this.engineFactory.getPoolPid(),\n idleMs: null,\n idleTtlMs: null,\n })\n }\n if (this.overrideCache) {\n const eng = this.overrideCache.engine\n const engineKey = `${eng.runtime}/${eng.backend}/${eng.device ?? 'default'} (warm)`\n const loaded = this.overrideCache.factory.listLoaded()\n const idleMs = Math.max(0, Date.now() - this.overrideCache.lastUsedMs)\n // Defensive: if the eviction timer somehow drifted past the TTL,\n // suppress the row so the UI doesn't show a \"stuck at 59s\" entry.\n // The next runPipeline call (or the timer fire on the next tick)\n // will properly dispose the factory.\n if (idleMs <= DetectionPipelineProvider.OVERRIDE_CACHE_TTL_MS) {\n out.push({\n engineKey,\n engine: eng,\n modelsLoaded: loaded.map((l) => `${l.stepId}/${l.modelId}`),\n inUseByCameras: [],\n kind: 'warm-override',\n poolPid: this.overrideCache.factory.getPoolPid(),\n idleMs,\n idleTtlMs: DetectionPipelineProvider.OVERRIDE_CACHE_TTL_MS,\n })\n }\n }\n return out\n }\n\n async spinEngine(input: { engine: PipelineEngineChoice }): Promise<{ success: true }> {\n // Tear down any pre-existing factory that targets a different engine,\n // then warm up an empty pool for the requested engine. Models load\n // lazily on the first `runPipeline` call that references them.\n const same = this.engineFactory\n && this.currentEngine.runtime === input.engine.runtime\n && this.currentEngine.backend === input.engine.backend\n && (this.currentEngine.device ?? null) === (input.engine.device ?? null)\n if (same) return { success: true }\n if (this.engineFactory) {\n await this.engineFactory.dispose()\n this.engineFactory = null\n this.executor = null\n }\n this.currentEngine = input.engine\n await this.ensureEngineFactory()\n return { success: true }\n }\n\n async killEngine(input: {\n engine: PipelineEngineChoice\n force?: boolean\n }): Promise<{ success: boolean; reason?: string }> {\n // First check the override cache — benchmark runs park their\n // transient factory here. Operator \"Kill engine\" should tear down\n // whichever factory holds the named engine.\n if (this.overrideCache && enginesEqual(this.overrideCache.engine, input.engine)) {\n await this.evictOverrideCache('killEngine')\n this.log.info('Override engine killed', { meta: { ...input.engine, force: input.force ?? false } })\n return { success: true }\n }\n if (!this.engineFactory) {\n return { success: false, reason: 'not loaded' }\n }\n const target = input.engine\n const matches = enginesEqual(this.currentEngine, target)\n if (!matches) {\n return { success: false, reason: 'engine mismatch' }\n }\n // inUse check deferred to caller — detection-pipeline cannot see\n // pipeline-runner attachments without crossing the transport layer.\n // UI invokes killEngine with `force:true` only after confirming no\n // attached cameras via runner.listAttached().\n await this.engineFactory.dispose()\n this.engineFactory = null\n this.executor = null\n this.log.info('Engine killed', { meta: { ...target, force: input.force ?? false } })\n return { success: true }\n }\n\n async reprobeEngine(): Promise<PipelineEngineChoice> {\n const best = DetectionPipelineProvider.detectBestEngine()\n const probedLabel = `${best.runtime}/${best.backend}/${best.device ?? 'default'}`\n // Only persist the hint — operator's engineRuntime/engineBackend/\n // engineDevice stays untouched. Re-probe is informational; the\n // cascade is never auto-overwritten. (Previous behaviour reset\n // operator choices every time Re-probe ran.)\n await this.writeStore({ probedBestEngine: probedLabel })\n this.log.info('Re-probed engine', {\n meta: { runtime: best.runtime, backend: best.backend, device: best.device ?? null, probedBestEngine: probedLabel },\n })\n return best\n }\n\n // ── Reference audio ──\n\n async getReferenceAudioFiles(): Promise<readonly { filename: string; sizeKb: number }[]> {\n const dir = resolveReferenceAudioDir()\n if (!dir) return []\n return fs.readdirSync(dir)\n .filter((f) => /\\.(wav|mp3|ogg|flac)$/i.test(f))\n .sort()\n .map((filename) => {\n const stats = fs.statSync(path.join(dir, filename))\n return { filename, sizeKb: Math.round(stats.size / 1024) }\n })\n }\n\n async getReferenceAudio(input: { filename: string }): Promise<{ base64: string }> {\n const { filename } = input\n return { base64: loadReferenceAudioBase64(filename) }\n }\n\n async getAudioCapabilities(): Promise<{\n activeBackend: string\n availableBackends: readonly { id: string; name: string; description: string; available: boolean; rawLabels?: readonly string[] }[]\n sampleRate: number\n chunkDurationMs: number\n }> {\n const isMac = process.platform === 'darwin'\n // Source the per-backend raw label sets from the macro mapping\n // tables — same data the classifier uses to aggregate to macros.\n // Sorted alphabetically for stable UI ordering.\n const yamnetLabels = Object.keys(YAMNET_TO_MACRO.mapping).sort((a, b) => a.localeCompare(b))\n const appleLabels = Object.keys(APPLE_SA_TO_MACRO.mapping).sort((a, b) => a.localeCompare(b))\n return {\n activeBackend: isMac ? 'apple-soundanalysis' : 'yamnet-onnx',\n availableBackends: [\n { id: 'yamnet-onnx', name: 'YAMNet ONNX', description: 'Google YAMNet — 521 classes, CPU', available: true, rawLabels: yamnetLabels },\n { id: 'apple-soundanalysis', name: 'Apple SoundAnalysis', description: 'macOS built-in — 303 classes, Neural Engine', available: isMac, rawLabels: appleLabels },\n ].filter(b => b.available),\n sampleRate: 16000,\n chunkDurationMs: 500,\n }\n }\n\n async runAudioTest(input: {\n addonId: string\n modelId: string\n filename?: string\n settings?: Record<string, unknown>\n }): Promise<{\n success: boolean\n error?: string\n frame?: import('@camstack/types').AudioResult\n }> {\n const api = this.addonCtx?.api\n if (!api) {\n return { success: false, error: 'API not available — addon not fully initialized' }\n }\n\n const dir = resolveReferenceAudioDir()\n if (!dir) {\n return { success: false, error: 'Reference audio directory not found' }\n }\n\n // Use requested filename or fall back to first WAV\n let targetFilename = input.filename\n if (!targetFilename) {\n const audioFiles = await this.getReferenceAudioFiles()\n targetFilename = audioFiles.find((f) => f.filename.endsWith('.wav'))?.filename\n }\n if (!targetFilename) {\n return { success: false, error: 'No WAV reference audio file found' }\n }\n\n const filePath = path.join(dir, targetFilename)\n if (!fs.existsSync(filePath)) {\n return { success: false, error: `Audio file not found: ${targetFilename}` }\n }\n const start = performance.now()\n try {\n const chunk = parseWavToAudioChunk(filePath)\n // AddonApi exposes every procedure as `{ query, mutate, subscribe }` —\n // the direct caller is wrapped in a proxy by AddonApiFactory so the\n // shape matches the WSS client transparently.\n const result = await api.audioAnalyzer.classify.query(chunk)\n const durationMs = Math.round(performance.now() - start)\n\n // ── Per-step settings from AudioAnalyzerStep.getConfigSchema() ──\n // Phase 7 (settings redesign): unified naming — the slider key is\n // now `minConfidence` for every threshold field across the\n // pipeline (was `minScore` in Phase 6). The full top-N ranked\n // list is surfaced via `debug.alternateLabels['audio-analyzer']`;\n // the user's `minConfidence` gates which of those become\n // AudioDetection entries.\n const settings = input.settings ?? {}\n const minConfidenceRaw = settings['minConfidence']\n const minConfidence = typeof minConfidenceRaw === 'number' ? minConfidenceRaw : 0\n\n // Raw ranked labels from the classifier (already sorted desc).\n const ranked = (result.labels ?? [])\n .slice()\n .sort((a, b) => b.score - a.score)\n\n // Build the top-N alternate list (fixed cap at 10 for the debug\n // panel — one entry is enough for most UIs, but top-10 fits the\n // benchmark inspector without bloating the payload).\n const alternateTopN = ranked.slice(0, 10).map((l) => ({\n label: l.className,\n score: l.score,\n }))\n\n // Each class above `minConfidence` becomes one AudioDetection\n // entry. The window spans the full classified chunk — compute\n // the duration from the Float32 sample buffer length divided by\n // the sample rate. `data` is typed as a numeric array on\n // `AudioChunkInput` so the arithmetic stays well-typed.\n const sampleCount = (chunk.data as Float32Array).length\n const startMs = 0\n const endMs = (sampleCount / chunk.sampleRate) * 1000\n const detections = ranked\n .filter((l) => l.score >= minConfidence)\n .map((l, i): import('@camstack/types').AudioDetection => ({\n id: `a${i + 1}`,\n kind: 'audio',\n startMs,\n endMs,\n macroClass: l.className,\n score: l.score,\n labels: [],\n debug: {\n stepLatencyMs: { 'audio-analyzer': result.inferenceMs },\n modelIds: { 'audio-analyzer': input.modelId },\n alternateLabels: {\n 'audio-analyzer': alternateTopN,\n },\n ...(l.originalClass && l.originalClass !== l.className\n ? { originalClass: l.originalClass }\n : {}),\n },\n }))\n\n const frame: import('@camstack/types').AudioResult = {\n kind: 'audio-window',\n windowId: `audio-test-${Date.now()}`,\n deviceId: 0,\n timestamp: Date.now(),\n startMs,\n endMs,\n detections,\n debug: {\n totalInferenceMs: durationMs,\n stepTimings: [\n {\n source: 'audio-analyzer',\n modelId: input.modelId,\n ms: result.inferenceMs,\n detectionCount: detections.length,\n },\n ],\n engine: input.addonId,\n },\n }\n\n this.log.info(\n '[audio-test] completed',\n {\n meta: {\n backend: input.addonId,\n file: targetFilename,\n detections: detections.length,\n ranked: ranked.length,\n minConfidence,\n inferenceMs: Number(result.inferenceMs.toFixed(1)),\n totalMs: durationMs,\n },\n },\n )\n\n return { success: true, frame }\n } catch (err: unknown) {\n const msg = errMsg(err)\n this.log.error('failed', { meta: { phase: 'audio-test', error: msg } })\n return { success: false, error: msg }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Schema builders (from step definitions)\n// ---------------------------------------------------------------------------\n\nfunction buildSchemaSlots(format: ModelFormat, modelsDir: string): import('@camstack/types').PipelineSlotSchema[] {\n const slotMap = new Map<string, import('@camstack/types').PipelineAddonSchema[]>()\n\n for (const pipelineStep of ALL_PIPELINE_STEPS) {\n const step = pipelineStep.definition\n // Only include steps that have the selected format\n const availableModels = step.models.filter((m) => m.formats[format])\n if (availableModels.length === 0) continue\n\n const slot = step.slot\n if (!slotMap.has(slot)) slotMap.set(slot, [])\n\n const configSchema = pipelineStep.getConfigSchema()\n\n slotMap.get(slot)!.push({\n id: step.id,\n name: step.name,\n slot: step.slot,\n inputClasses: step.inputClasses ?? [],\n outputClasses: step.outputClasses,\n childSlots: resolveChildSlots(step.slot),\n models: availableModels.map((m) => ({\n id: m.id,\n name: m.name,\n formats: Object.fromEntries(\n Object.entries(m.formats).map(([f, entry]) => [f, {\n downloaded: isModelDownloaded(modelsDir, m, f as ModelFormat),\n sizeMB: entry!.sizeMB,\n }]),\n ),\n })),\n defaultModelId: step.defaultModelId,\n ...(step.defaultModelIdByFormat ? { defaultModelIdByFormat: { ...step.defaultModelIdByFormat } } : {}),\n ...(step.enabledByDefault !== undefined ? { enabledByDefault: step.enabledByDefault } : {}),\n defaultConfidence: step.defaultConfidence,\n ...(step.group ? { group: step.group } : {}),\n ...(configSchema.length > 0 ? { configSchema: [...configSchema] } : {}),\n })\n }\n\n const slotPriority: Record<string, number> = { detector: 0, cropper: 1, classifier: 2, refiner: 3, 'audio-classifier': 4 }\n const parentSlot: Record<string, string | null> = { detector: null, cropper: 'detector', classifier: 'cropper', refiner: 'detector', 'audio-classifier': null }\n\n const slotLabels: Record<string, string> = {\n detector: 'Detector', cropper: 'Cropper', classifier: 'Classifier',\n refiner: 'Refiner', 'audio-classifier': 'Audio Classifier',\n }\n\n return Array.from(slotMap.entries()).map(([slot, addons]) => ({\n id: slot as import('@camstack/types').PipelineSlot,\n label: slotLabels[slot] ?? slot,\n priority: slotPriority[slot] ?? 99,\n parentSlot: (parentSlot[slot] ?? null) as import('@camstack/types').PipelineSlot | null,\n addons,\n }))\n}\n\nfunction resolveChildSlots(slot: string): import('@camstack/types').PipelineSlot[] {\n switch (slot) {\n case 'detector': return ['cropper', 'classifier', 'refiner']\n case 'cropper': return ['classifier', 'refiner']\n default: return []\n }\n}\n\n/**\n * V1 default: all steps enabled, heavy models. Kept for rollback.\n * Total model footprint: ~600 MB (ONNX), ~300 MB (CoreML)\n */\n/** @internal Exported for rollback — use buildDefaultStepTree (v2) for production */\nexport function buildDefaultStepTreeV1(format: ModelFormat): PipelineDefaultStep[] {\n const makeStep = (stepId: string, children: PipelineDefaultStep[], opts?: { enabled?: boolean; modelId?: string }): PipelineDefaultStep | null => {\n const def = getStepDefinition(stepId)\n const hasFormat = def.models.some((m) => m.formats[format])\n if (!hasFormat) return null\n\n return {\n addonId: def.id,\n addonName: def.name,\n slot: def.slot,\n inputClasses: def.inputClasses ?? [],\n outputClasses: def.outputClasses,\n enabled: opts?.enabled ?? true,\n modelId: opts?.modelId ?? getDefaultModelForFormat(stepId, format),\n children,\n ...(def.group ? { group: def.group } : {}),\n }\n }\n\n const faceEmbedding = makeStep('face-embedding', [])\n const faceDetection = makeStep('face-detection', faceEmbedding ? [faceEmbedding] : [])\n\n const plateOcr = makeStep('plate-ocr', [])\n const plateDetection = makeStep('plate-detection', plateOcr ? [plateOcr] : [])\n\n const animalClassifier = makeStep('animal-classifier', [])\n const vehicleClassifier = makeStep('vehicle-classifier', [])\n\n const rootChildren: PipelineDefaultStep[] = [\n faceDetection,\n plateDetection,\n animalClassifier,\n vehicleClassifier,\n ].filter((s): s is PipelineDefaultStep => s !== null)\n\n const root = makeStep('object-detection', rootChildren)\n return root ? [root] : []\n}\n\n/**\n * V2 default: lightweight pipeline inspired by Scrypted.\n *\n * Only object detection (yolov9t, 4-8 MB) + face detection (scrfd, 1.7 MB) enabled.\n * All other steps are present in the tree but DISABLED — user enables via UI.\n * Total model footprint: ~10 MB (CoreML), ~11 MB (ONNX)\n *\n * Why:\n * - Fast startup (seconds, not minutes of model downloads)\n * - Covers 90% of NVR use cases (person/vehicle/animal detection + face detection)\n * - Users opt-in to heavy classifiers (vehicle type, plate OCR, bird species) when needed\n */\nfunction buildDefaultStepTree(format: ModelFormat): PipelineDefaultStep[] {\n const makeStep = (stepId: string, children: PipelineDefaultStep[], opts?: { enabled?: boolean; modelId?: string }): PipelineDefaultStep | null => {\n const def = getStepDefinition(stepId)\n const hasFormat = def.models.some((m) => m.formats[format])\n if (!hasFormat) return null\n\n return {\n addonId: def.id,\n addonName: def.name,\n slot: def.slot,\n inputClasses: def.inputClasses ?? [],\n outputClasses: def.outputClasses,\n enabled: opts?.enabled ?? true,\n modelId: opts?.modelId ?? getDefaultModelForFormat(stepId, format),\n children,\n ...(def.group ? { group: def.group } : {}),\n }\n }\n\n // Enabled: core detection + recognition pipeline\n const faceEmbedding = makeStep('face-embedding', [])\n const faceDetection = makeStep('face-detection', faceEmbedding ? [faceEmbedding] : [])\n const plateOcr = makeStep('plate-ocr', [])\n const plateDetection = makeStep('plate-detection', plateOcr ? [plateOcr] : [])\n\n // Disabled: heavy classifiers and refiner — user enables via UI when needed\n const animalClassifier = makeStep('animal-classifier', [], { enabled: false })\n const birdClassifier = makeStep('bird-classifier', [], { enabled: false })\n const vehicleClassifier = makeStep('vehicle-classifier', [], { enabled: false })\n const segmentationRefiner = makeStep('segmentation-refiner', [], { enabled: false })\n const instanceSegmentation = makeStep('instance-segmentation', [], { enabled: false })\n\n const rootChildren: PipelineDefaultStep[] = [\n faceDetection,\n plateDetection,\n animalClassifier,\n birdClassifier,\n vehicleClassifier,\n segmentationRefiner,\n instanceSegmentation,\n ].filter((s): s is PipelineDefaultStep => s !== null)\n\n // getDefaultModelForFormat picks the smallest model for the current format\n const root = makeStep('object-detection', rootChildren)\n\n // Audio classifier — root-level step with independent engine.\n // Audio pipeline runs separately from video; engine is chosen based on platform:\n // macOS + coreml video → apple-soundanalysis via Swift CLI (Neural Engine)\n // everything else → yamnet-onnx via onnxruntime-node (CPU)\n const audioModelId = getDefaultModelForFormat('audio-classifier', format)\n const audioEngine: import('@camstack/types').PipelineEngineChoice =\n audioModelId === 'apple-soundanalysis'\n ? { runtime: 'node', backend: 'coreml', format: 'coreml' }\n : { runtime: 'node', backend: 'cpu', format: 'onnx' }\n\n const audioStep = makeStep('audio-classifier', [])\n const audioClassifier: import('@camstack/types').PipelineDefaultStep | null = audioStep\n ? { ...audioStep, engine: audioEngine }\n : null\n\n const steps: PipelineDefaultStep[] = []\n if (root) steps.push(root)\n if (audioClassifier) steps.push(audioClassifier)\n return steps\n}\n\nfunction stepsToPipelineConfig(steps: readonly PipelineDefaultStep[], engine: PipelineEngineChoice): PipelineConfig {\n const toNode = (step: PipelineDefaultStep): import('@camstack/types').PipelineNode => ({\n step: step.addonId,\n addon: step.addonId,\n // Phase 7: the generic `confidence` override is gone — the inference\n // pool reads the step-definition default directly. `configOverride`\n // only carries user-chosen `modelId` now (and whatever fields the\n // step's own configSchema introduces via `step.settings`).\n configOverride: {\n modelId: step.modelId,\n ...(step.settings ?? {}),\n },\n children: step.children.map(toNode),\n })\n\n // Separate audio-classifier steps from video steps\n const videoSteps = steps.filter(s => s.slot !== 'audio-classifier')\n const audioStep = steps.find(s => s.slot === 'audio-classifier' && s.enabled)\n\n return {\n engine,\n video: videoSteps.map(toNode),\n ...(audioStep ? {\n audio: toNode(audioStep),\n audioEngine: audioStep.engine ?? engine,\n } : {}),\n }\n}\n\n/** Flatten a step tree into a flat list (DFS). */\n/** Flatten a step tree into a flat list (DFS) — delegates to shared utility. */\nfunction flattenSteps(steps: readonly PipelineDefaultStep[]): readonly PipelineDefaultStep[] {\n return flattenEnabledVideoSteps(steps)\n}\n\n/**\n * Return a shallow-cloned executable tree with `rootStepId`'s `settings`\n * merged with `overrides` (per-device values win). Non-matching root steps\n * pass through unchanged. Used by `runFrame` to apply per-device detection\n * settings without rebuilding the tree per frame.\n */\nfunction applyDeviceOverridesToTree(\n tree: import('./pipeline/tree-builder.js').ExecutableTree,\n rootStepId: string,\n overrides: Record<string, unknown>,\n): import('./pipeline/tree-builder.js').ExecutableTree {\n return {\n roots: tree.roots.map(root =>\n root.stepId === rootStepId\n ? { ...root, settings: { ...(root.settings ?? {}), ...overrides } }\n : root,\n ),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Device definitions per backend\n// ---------------------------------------------------------------------------\n\nconst COREML_DEVICES: readonly EngineDeviceInfo[] = [\n { id: 'all', label: 'All (CPU + GPU + Neural Engine)', description: 'CoreML auto-selects the best available compute unit' },\n { id: 'ane', label: 'Neural Engine', description: 'Apple Neural Engine — fastest, lowest power' },\n { id: 'gpu', label: 'GPU', description: 'Apple GPU — good for larger models' },\n { id: 'cpu', label: 'CPU only', description: 'CPU fallback — slowest but always available' },\n]\n\nconst OPENVINO_DEVICES: readonly EngineDeviceInfo[] = [\n { id: 'auto', label: 'Auto', description: 'OpenVINO auto-selects best device (recommended)' },\n { id: 'cpu', label: 'CPU', description: 'Intel CPU with AVX/SSE optimizations' },\n { id: 'gpu', label: 'GPU (Intel)', description: 'Intel integrated/discrete GPU via OpenCL' },\n { id: 'npu', label: 'NPU (Intel)', description: 'Intel Neural Processing Unit (Meteor Lake+)' },\n]\n\nconst ONNX_PYTHON_DEVICES: readonly EngineDeviceInfo[] = [\n { id: 'cpu', label: 'CPU', description: 'ONNX Runtime CPU provider' },\n { id: 'cuda', label: 'CUDA (NVIDIA)', description: 'NVIDIA GPU via CUDA' },\n { id: 'coreml', label: 'CoreML', description: 'Apple CoreML execution provider (macOS)' },\n]\n","/**\n * EngineFactory — creates and manages inference engines for the pipeline.\n *\n * Routes to either:\n * - SharedInferencePool (python+coreml/openvino/onnxruntime) via PipelineModelManager\n * - NodeEngineManager (nodejs+onnx) for direct ONNX Runtime in Node.js\n */\nimport type {\n IInferenceEngine,\n IScopedLogger,\n PipelineEngineChoice,\n PipelineDefaultStep,\n PoolModelConfig,\n ModelFormat,\n} from '@camstack/types'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { SharedInferencePool } from './shared-inference-pool.js'\nimport { PipelineModelManager } from './pipeline-model-manager.js'\nimport { NodeEngineManager } from './node-engine-manager.js'\nimport { getStepDefinition } from '../registry/step-definitions.js'\nimport { checkAndLogModelShape } from './model-shape-validator.js'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype PoolRuntime = 'coreml' | 'openvino' | 'onnxruntime'\n\nexport type BatchMode = 'none' | 'list' | 'window'\n\n/**\n * Per-runtime pool tuning surfaced through addon-detection-pipeline\n * settings. All fields optional — undefined means \"use the runtime\n * default resolved in `engine-factory`\".\n */\nexport interface PoolTuning {\n readonly batchMode?: BatchMode\n readonly windowMs?: number\n readonly maxBatchSize?: number\n readonly numStreams?: number\n readonly intraOpThreads?: number\n}\n\ninterface EngineFactoryOptions {\n readonly engine: PipelineEngineChoice\n readonly modelsDir: string\n /**\n * Path to the Python interpreter used by `runtime: 'python'` engines.\n * Required: the addon resolves it once via `ctx.deps.ensurePython()`\n * and threads it through. No PATH lookup here — the embedded portable\n * Python is the single source of truth for the inference pool.\n */\n readonly pythonPath: string\n readonly logger: IScopedLogger\n /** Function to create a NodeInferenceEngine (injected to avoid hard dependency on onnxruntime-node) */\n readonly createNodeEngine?: (modelPath: string, backend: string, meta: import('@camstack/types').ModelInputMeta, logger?: IScopedLogger) => Promise<IInferenceEngine>\n /** Worker concurrency for the Python pool — honoured only by onnxruntime\n * (one InferenceSession per worker thread). coreml/openvino runs run\n * single-threaded regardless because the runtime drives its own\n * internal parallelism. Defaults to 4 for onnxruntime. */\n readonly concurrency?: number\n /** Per-runtime batch dispatch tuning (see `PoolTuning`). */\n readonly tuning?: PoolTuning\n /**\n * Number of independent Python pool subprocesses. Each holds its\n * own model copy + ANE/GPU/CPU dispatch context. Inference round-\n * robins across them. Default 1.\n */\n readonly numWorkers?: number\n}\n\n// ---------------------------------------------------------------------------\n// Runtime → Pool runtime mapping\n// ---------------------------------------------------------------------------\n\nexport const BACKEND_TO_POOL_RUNTIME: Record<string, PoolRuntime> = {\n coreml: 'coreml',\n openvino: 'openvino',\n onnx: 'onnxruntime',\n cpu: 'onnxruntime',\n cuda: 'onnxruntime',\n}\n\nconst RUNTIME_TO_FORMAT: Record<string, ModelFormat> = {\n coreml: 'coreml',\n openvino: 'openvino',\n onnxruntime: 'onnx',\n}\n\n// ---------------------------------------------------------------------------\n// EngineFactory\n// ---------------------------------------------------------------------------\n\nexport class EngineFactory {\n private pool: SharedInferencePool | null = null\n private poolManager: PipelineModelManager | null = null\n private nodeManager: NodeEngineManager | null = null\n private readonly log: IScopedLogger\n private readonly opts: EngineFactoryOptions\n\n constructor(opts: EngineFactoryOptions) {\n this.opts = opts\n this.log = opts.logger\n }\n\n /** Whether this factory uses a Python pool (vs Node.js ONNX). */\n get usesPythonPool(): boolean {\n return this.opts.engine.runtime === 'python'\n }\n\n /**\n * Initialize the engine layer and apply initial pipeline config.\n */\n async initialize(steps: readonly PipelineDefaultStep[]): Promise<void> {\n if (this.usesPythonPool) {\n await this.initPythonPool(steps)\n } else {\n await this.initNodeEngines(steps)\n }\n }\n\n /**\n * Apply a new pipeline config (hot swap).\n * Only loads/unloads models that changed.\n */\n async applyConfig(steps: readonly PipelineDefaultStep[]): Promise<void> {\n if (this.poolManager) {\n await this.poolManager.applyConfig(steps)\n } else if (this.nodeManager) {\n await this.nodeManager.applyConfig(steps)\n } else {\n throw new Error('EngineFactory not initialized')\n }\n }\n\n /**\n * Get an IInferenceEngine for a pipeline step. Without `modelId`,\n * resolves to the active model (camera-runtime hot path); pass\n * `modelId` to grab a specific warm variant (benchmark / override\n * paths picking a non-runtime model).\n *\n * @throws if step not loaded with the requested (or active) model.\n */\n getEngine(stepId: string, modelId?: string): IInferenceEngine {\n if (this.poolManager) {\n return this.poolManager.getHandle(stepId, modelId)\n }\n if (this.nodeManager) {\n // Node ONNX manager does not yet support multi-slot — falls back\n // to the active variant. Bench callers using the Node runtime\n // get the same behavior they used to (single slot, replace on\n // mismatch) until that path catches up.\n return this.nodeManager.getEngine(stepId)\n }\n throw new Error('EngineFactory not initialized')\n }\n\n /** Check if a step is loaded (any variant). */\n isLoaded(stepId: string): boolean {\n if (this.poolManager) return this.poolManager.isLoaded(stepId)\n if (this.nodeManager) return this.nodeManager.isLoaded(stepId)\n return false\n }\n\n /** Check if a specific (stepId, modelId) variant is resident. */\n isLoadedWithModel(stepId: string, modelId: string): boolean {\n if (this.poolManager) return this.poolManager.isLoadedWithModel(stepId, modelId)\n if (this.nodeManager) return this.nodeManager.isLoadedWithModel(stepId, modelId)\n return false\n }\n\n /**\n * List every loaded (stepId, modelId) variant — the pool path\n * surfaces each warm slot independently (including bench overrides);\n * the Node path surfaces just the active model per step.\n */\n listLoaded(): readonly { stepId: string; modelId: string; active: boolean }[] {\n if (this.poolManager) {\n return this.poolManager.getLoadedSteps().map((l) => ({\n stepId: l.stepId,\n modelId: l.modelId,\n active: l.active,\n }))\n }\n if (this.nodeManager) {\n return this.nodeManager.listLoaded().map((l) => ({ ...l, active: true }))\n }\n return []\n }\n\n /** Native pid of the underlying Python pool, if any. */\n getPoolPid(): number | null {\n return this.pool?.getPid() ?? null\n }\n\n /**\n * Whether this factory exposes the batched fast path\n * (`batchInferRaw`). Only the Python pool path supports it today —\n * Node.js ONNX engines run one inference per call with their own\n * thread pool inside InferenceSession, so batching at this layer\n * would just queue serially.\n */\n supportsBatch(): boolean {\n return this.poolManager !== null\n }\n\n /**\n * Run N raw-frame inferences against the same loaded step in a\n * single IPC round-trip. Returns the structured Python detection\n * dicts in input order. Throws when this factory has no pool path.\n *\n * Used by `runPipelineBatch` for fair benchmarking against\n * Scrypted's `detectObjects(media, {batch})`.\n */\n async batchInferRaw(\n stepId: string,\n items: readonly { readonly raw: Buffer; readonly width: number; readonly height: number; readonly format: 'rgb' | 'bgr' | 'gray' }[],\n modelId?: string,\n ): Promise<readonly Record<string, unknown>[]> {\n if (!this.poolManager) {\n throw new Error('EngineFactory.batchInferRaw: pool path not available (Node ONNX factory)')\n }\n const idx = this.poolManager.getPoolIndex(stepId, modelId)\n if (idx === null) {\n throw new Error(`EngineFactory.batchInferRaw: step \"${stepId}\"${modelId ? ` (model \"${modelId}\")` : ''} is not loaded`)\n }\n const pool = this.poolManager.getPool()\n return pool.inferBatch(idx, items)\n }\n\n // ── Bench frame cache (Python-side) ──────────────────────────────\n\n async cacheFrameInPool(\n raw: Buffer,\n width: number,\n height: number,\n format: 'rgb' | 'bgr' | 'gray',\n ): Promise<{ frameId: number; width: number; height: number }> {\n if (!this.poolManager) throw new Error('cacheFrameInPool: pool not available')\n return this.poolManager.getPool().cacheFrame(raw, width, height, format)\n }\n\n async inferCached(\n stepId: string,\n frameId: number,\n modelId?: string,\n ): Promise<Record<string, unknown>> {\n if (!this.poolManager) throw new Error('inferCached: pool not available')\n const idx = this.poolManager.getPoolIndex(stepId, modelId)\n if (idx === null) throw new Error(`inferCached: step \"${stepId}\"${modelId ? ` (model \"${modelId}\")` : ''} not loaded`)\n return this.poolManager.getPool().inferCached(idx, frameId)\n }\n\n async uncacheFrame(frameId: number): Promise<void> {\n if (!this.poolManager) return\n await this.poolManager.getPool().uncacheFrame(frameId)\n }\n\n /** Load additional models without unloading existing ones (for benchmark/test). */\n async loadAdditional(steps: readonly PipelineDefaultStep[]): Promise<void> {\n if (this.poolManager) {\n await this.poolManager.loadAdditional(steps)\n } else if (this.nodeManager) {\n await this.nodeManager.loadAdditional(steps)\n }\n }\n\n /** Shut down all engines and the pool process. */\n async dispose(): Promise<void> {\n if (this.pool) {\n await this.pool.dispose()\n this.pool = null\n this.poolManager = null\n }\n if (this.nodeManager) {\n await this.nodeManager.disposeAll()\n this.nodeManager = null\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: Python pool initialization\n // -------------------------------------------------------------------------\n\n private async initPythonPool(steps: readonly PipelineDefaultStep[]): Promise<void> {\n const pythonPath = this.opts.pythonPath\n if (!pythonPath) {\n throw new Error(\n 'EngineFactory: pythonPath is required for runtime=\"python\" — the addon must call ' +\n 'ctx.deps.ensurePython() and pass the result. The embedded portable Python download ' +\n 'likely failed; check the addon logs for the download error.',\n )\n }\n const poolRuntime = BACKEND_TO_POOL_RUNTIME[this.opts.engine.backend]\n if (!poolRuntime) {\n throw new Error(`No pool runtime mapping for backend \"${this.opts.engine.backend}\"`)\n }\n\n // Default worker concurrency per runtime — re-tuned 2026-04-26 from\n // matrix sweep (`scripts/bench-pool-matrix.py` + `bench-coreml-patterns.py`):\n // coreml: 1 — CoreML serializes per-model-context; >1 worker\n // just adds thread overhead. Cross-camera throughput\n // comes from `batchMode='window'` accumulator instead.\n // openvino: 1 — the runtime manages internal infer-request\n // pools already; more Python workers just add overhead.\n // onnxruntime: 4 — InferenceSession.run is thread-safe; the native\n // thread pool schedules concurrent runs across cores.\n const defaultConcurrency: Record<PoolRuntime, number> = {\n coreml: 1,\n openvino: 1,\n onnxruntime: 4,\n }\n const concurrency = this.opts.concurrency ?? defaultConcurrency[poolRuntime]\n const isAutoDefault = this.opts.concurrency === undefined\n\n // Default batch dispatch mode per runtime — matrix-derived. Operator\n // overrides via the addon settings UI (see `TUNING_DEFAULTS` in\n // `addon-detection-pipeline/src/index.ts`).\n const defaultBatchMode: Record<PoolRuntime, BatchMode> = {\n coreml: 'window',\n openvino: 'none',\n onnxruntime: 'list',\n }\n const tuning: PoolTuning = this.opts.tuning ?? {}\n const resolvedTuning: Required<PoolTuning> = {\n batchMode: tuning.batchMode ?? defaultBatchMode[poolRuntime],\n windowMs: tuning.windowMs ?? 8,\n maxBatchSize: tuning.maxBatchSize ?? 8,\n numStreams: tuning.numStreams ?? 0,\n intraOpThreads: tuning.intraOpThreads ?? 0,\n }\n const numWorkers = Math.max(1, this.opts.numWorkers ?? 1)\n this.log.info('Python inference pool', {\n meta: {\n runtime: poolRuntime,\n concurrency,\n numWorkers,\n source: isAutoDefault ? 'auto-default' : 'user-override',\n tuning: resolvedTuning,\n },\n })\n this.pool = new SharedInferencePool(\n pythonPath,\n poolRuntime,\n this.log.child('pool'),\n { concurrency, tuning: resolvedTuning, numWorkers },\n )\n\n this.poolManager = new PipelineModelManager(\n this.pool,\n (stepId, modelId) => this.buildPoolModelConfig(stepId, modelId, poolRuntime),\n this.log.child('model-mgr'),\n )\n\n // Start pool with empty models — then load via manager for clean index tracking\n await this.pool.initialize([])\n await this.poolManager.applyConfig(steps)\n }\n\n private buildPoolModelConfig(stepId: string, modelId: string, poolRuntime: PoolRuntime): PoolModelConfig {\n const def = getStepDefinition(stepId)\n const modelEntry = def.models.find((m) => m.id === modelId)\n if (!modelEntry) {\n throw new Error(`Model \"${modelId}\" not found in step \"${stepId}\" catalog`)\n }\n\n const format = RUNTIME_TO_FORMAT[poolRuntime]\n if (!format) {\n throw new Error(`No format mapping for pool runtime \"${poolRuntime}\"`)\n }\n\n const formatEntry = modelEntry.formats[format]\n if (!formatEntry) {\n throw new Error(`Model \"${modelId}\" has no ${format} format. Available: ${Object.keys(modelEntry.formats).join(', ')}`)\n }\n\n // Derive model file path from URL (same logic as existing model download service)\n const urlParts = formatEntry.url.split('/')\n const filename = urlParts[urlParts.length - 1] ?? `${modelId}.${format}`\n const modelPath = `${this.opts.modelsDir}/${filename}`\n\n const inputSize = Math.max(modelEntry.inputSize.width, modelEntry.inputSize.height)\n\n // Best-effort runtime sanity check — if the local ONNX file disagrees\n // with the catalog declaration, log a warning so the discrepancy\n // surfaces early instead of producing silently-wrong inferences.\n // Always validates against the ONNX file (single source of truth);\n // CoreML / OpenVINO are derived from the same upstream so a passing\n // ONNX check covers all formats.\n const onnxEntry = modelEntry.formats.onnx\n if (onnxEntry) {\n const onnxUrlParts = onnxEntry.url.split('/')\n const onnxFilename = onnxUrlParts[onnxUrlParts.length - 1] ?? `${modelId}.onnx`\n const onnxPath = `${this.opts.modelsDir}/${onnxFilename}`\n void checkAndLogModelShape(\n { modelId, modelPath: onnxPath, expected: modelEntry.inputSize },\n this.log,\n )\n }\n\n // Load labels: prefer inline labels from StepDefinition, fall back to extraFiles JSON\n let labels = def.labels\n if (!labels && modelEntry.extraFiles) {\n const labelsFile = modelEntry.extraFiles.find((f) => f.filename.endsWith('-labels.json'))\n if (labelsFile) {\n const labelsPath = path.join(this.opts.modelsDir, labelsFile.filename)\n if (fs.existsSync(labelsPath)) {\n labels = JSON.parse(fs.readFileSync(labelsPath, 'utf8')) as string[]\n }\n }\n }\n\n return {\n path: modelPath,\n inputSize,\n preprocessMode: modelEntry.preprocessMode ?? 'letterbox',\n postprocessor: def.postprocessor,\n // Per-step confidence floor forwarded to the Python inference pool.\n //\n // Background: Phase 7 (settings redesign) originally passed `0`\n // here on the theory that the TS executor would apply every filter.\n // In practice CoreML NMS emits ~800 zero-score phantom boxes per\n // 1280×1024 frame and raw YOLO logit grids carry hundreds of\n // near-zero anchors — saturating the JSON payload and bypassing\n // the executor's macro filters. The post-rollout hotfix\n // (`61bae2e3`) added a hard-coded `SAFETY_FLOOR = 0.05` in\n // `python/postprocessors/_safety.py`, which worked but was\n // explicitly marked as a workaround.\n //\n // The proper fix (this change, `[PYTHON-FLOOR]` in the 2026-04-12\n // handover) is to send the step's own `defaultConfidence` as the\n // numerical floor. Object-detection ships with 0.5, classifiers\n // with 0.2, etc. — all well above the numerical-garbage band.\n // The executor continues to apply `matchesMacroFilter` and the\n // per-macro `minConfidence*` sliders on top of whatever survives\n // the pool-side floor, so user-facing filters remain the single\n // source of semantic truth.\n confidence: def.defaultConfidence,\n labels,\n charset: def.charset,\n numClasses: labels?.length,\n strides: def.postprocessor === 'scrfd' ? [8, 16, 32] : undefined,\n maskThreshold: def.postprocessor === 'saliency' ? 0.5 : undefined,\n device: this.opts.engine.device ?? (poolRuntime === 'coreml' ? 'all' : undefined),\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: Node.js ONNX initialization\n // -------------------------------------------------------------------------\n\n private async initNodeEngines(steps: readonly PipelineDefaultStep[]): Promise<void> {\n if (!this.opts.createNodeEngine) {\n throw new Error('createNodeEngine function required for nodejs+onnx backend')\n }\n\n this.nodeManager = new NodeEngineManager(\n this.opts.engine.backend,\n (stepId, modelId) => this.resolveOnnxModelPath(stepId, modelId),\n this.opts.createNodeEngine,\n this.log.child('onnx-mgr'),\n )\n\n await this.nodeManager.applyConfig(steps)\n }\n\n private resolveOnnxModelPath(stepId: string, modelId: string): string {\n const def = getStepDefinition(stepId)\n const modelEntry = def.models.find((m) => m.id === modelId)\n if (!modelEntry) {\n throw new Error(`Model \"${modelId}\" not found in step \"${stepId}\" catalog`)\n }\n const onnxFormat = modelEntry.formats['onnx']\n if (!onnxFormat) {\n throw new Error(`Model \"${modelId}\" has no ONNX format`)\n }\n const urlParts = onnxFormat.url.split('/')\n const filename = urlParts[urlParts.length - 1] ?? `${modelId}.onnx`\n return `${this.opts.modelsDir}/${filename}`\n }\n\n}\n","/**\n * SharedInferencePool — TypeScript wrapper for inference_pool.py.\n *\n * Multi-process orchestrator. Owns N `PoolWorker` instances, each\n * holding its own Python subprocess + MLModel copy, and round-robins\n * inference dispatches across them. Model management (load/unload/\n * replace) propagates to all workers so every subprocess holds the\n * same model set at the same indices.\n *\n * Wire format (per worker):\n * Request: [4B total_len][4B req_id][1B msg_type][payload]\n * Response: [4B total_len][4B req_id][JSON payload]\n */\nimport type { IInferenceEngine, InferenceInput, EngineOutput, IScopedLogger, PoolModelConfig, RawFrameFormat } from '@camstack/types'\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\n\n// ── Wire-protocol constants ────────────────────────────────────────────────\n\nconst MSG_COMMAND = 0x00\nconst MSG_INFER_JPEG = 0x01\nconst MSG_INFER_RAW = 0x02\nconst MSG_CACHE_FRAME = 0x04\nconst MSG_INFER_CACHED = 0x05\n/**\n * Cross-camera batching opcode — N items packed into a single IPC\n * frame. Wire shape:\n * [1B model_idx][1B count][N × (4B w | 4B h | 1B fmt | 4B size | raw bytes)]\n * Python pool dispatches each item via `dispatcher.run` (asyncio.gather)\n * and returns a JSON array of detections in the same order.\n */\nconst MSG_INFER_BATCH = 0x03\n\nconst PREFIX_LEN = 4 + 4 + 1 // total_len + req_id + msg_type\n\n/**\n * Wire-level enum for the raw-frame fast path. Values are append-only:\n * the Python pool reads the byte directly off the IPC frame; reordering\n * existing entries breaks all in-flight workers.\n */\nconst RAW_FMT_CODE: Record<RawFrameFormat, number> = {\n rgb: 0x00,\n bgr: 0x01,\n gray: 0x02,\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype PoolRuntime = 'coreml' | 'openvino' | 'onnxruntime'\n\ninterface PendingRequest {\n readonly resolve: (value: Record<string, unknown>) => void\n readonly reject: (reason: Error) => void\n}\n\ninterface CommandResponse {\n readonly cmd: string\n readonly status: string\n readonly index?: number\n readonly loadMs?: number\n readonly error?: string\n readonly models?: readonly { index: number; path: string | null; loaded: boolean; postprocessor: string | null }[]\n}\n\nexport interface ModelSlotStatus {\n readonly index: number\n readonly path: string | null\n readonly loaded: boolean\n readonly postprocessor: string | null\n}\n\nexport type PoolBatchMode = 'none' | 'list' | 'window'\n\nexport interface PoolTuning {\n readonly batchMode: PoolBatchMode\n readonly windowMs: number\n readonly maxBatchSize: number\n readonly numStreams: number\n readonly intraOpThreads: number\n}\n\nexport interface SharedInferencePoolOptions {\n /** Worker concurrency — only meaningful for onnxruntime (thread pool size). */\n readonly concurrency?: number\n /** Per-runtime batch dispatch tuning forwarded to the Python pool startup. */\n readonly tuning?: PoolTuning\n /**\n * Number of independent Python subprocess workers. Each worker\n * owns its own MLModel copy and an independent ANE/GPU/CPU\n * dispatch context. Inference round-robins across workers; model\n * management commands propagate to all. Default 1 (legacy\n * single-process behaviour).\n */\n readonly numWorkers?: number\n}\n\n// ---------------------------------------------------------------------------\n// Pool handle — implements IInferenceEngine for a single model slot\n// ---------------------------------------------------------------------------\n\nclass PoolHandle implements IInferenceEngine {\n readonly runtime = 'onnx' as const // interface-only; the pool's own runtime is authoritative\n readonly device = 'cpu' as const\n\n constructor(\n private readonly pool: SharedInferencePool,\n private readonly modelIndex: number,\n ) {}\n\n async infer(input: InferenceInput): Promise<EngineOutput> {\n const start = performance.now()\n const result = input.kind === 'raw'\n ? await this.pool.inferRaw(this.modelIndex, input.data, input.width, input.height, input.format)\n : await this.pool.infer(this.modelIndex, input.data)\n const elapsed = performance.now() - start\n return { structured: result, inferenceMs: elapsed }\n }\n\n /**\n * Inference on a frame previously cached in the Python pool via\n * `pool.cacheFrame()`. Sends only 5 bytes through the pipe instead\n * of 1.2MB raw data — eliminates the pipe transfer bottleneck.\n */\n async inferFromCache(frameId: number): Promise<EngineOutput> {\n const start = performance.now()\n const result = await this.pool.inferCached(this.modelIndex, frameId)\n const elapsed = performance.now() - start\n return { structured: result, inferenceMs: elapsed }\n }\n\n async dispose(): Promise<void> {\n // Handles don't own the pool — disposal is a no-op.\n }\n}\n\n// ---------------------------------------------------------------------------\n// PoolWorker — single Python subprocess (private to this module)\n// ---------------------------------------------------------------------------\n\ninterface PoolWorkerOptions {\n readonly pythonPath: string\n readonly scriptPath: string\n readonly poolRuntime: PoolRuntime\n readonly concurrency: number\n readonly tuning: PoolTuning | null\n readonly logger: IScopedLogger\n readonly workerLabel: string\n}\n\nclass PoolWorker {\n private process: ChildProcess | null = null\n private receiveBuffer: Buffer = Buffer.alloc(0)\n private readonly pending = new Map<number, PendingRequest>()\n private nextRequestId = 1\n private ready = false\n private readonly log: IScopedLogger\n private readonly opts: PoolWorkerOptions\n\n constructor(opts: PoolWorkerOptions) {\n this.opts = opts\n this.log = opts.logger\n }\n\n getPid(): number | null {\n return this.process?.pid ?? null\n }\n\n isReady(): boolean {\n return this.ready\n }\n\n async initialize(initialModels: readonly PoolModelConfig[]): Promise<{ startupMs: number; loadedCount: number; workers: number }> {\n this.process = spawn(this.opts.pythonPath, [this.opts.scriptPath], {\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n if (!this.process.stdout || !this.process.stdin) {\n throw new Error('PoolWorker: failed to create process pipes')\n }\n\n this.process.stderr?.on('data', (chunk: Buffer) => {\n for (const line of chunk.toString().split('\\n')) {\n const trimmed = line.trim()\n if (trimmed) this.log.warn(`[${this.opts.workerLabel}] ${trimmed}`)\n }\n })\n\n this.process.on('error', (err) => {\n this.log.error('Worker process error', { meta: { worker: this.opts.workerLabel, error: err.message } })\n this.rejectAll(err)\n })\n\n this.process.on('exit', (code) => {\n if (code !== 0 && code !== null) {\n this.log.error('Worker process exited', { meta: { worker: this.opts.workerLabel, code } })\n this.rejectAll(new Error(`Worker process exited with code ${code}`))\n }\n this.ready = false\n })\n\n this.process.stdout.on('data', (chunk: Buffer) => {\n this.receiveBuffer = Buffer.concat([this.receiveBuffer, chunk])\n this.drainResponses()\n })\n\n const config: Record<string, unknown> = {\n runtime: this.opts.poolRuntime,\n concurrency: this.opts.concurrency,\n models: initialModels.map((m) => serializeModelConfig(m)),\n }\n if (this.opts.tuning) {\n config['batch_mode'] = this.opts.tuning.batchMode\n config['window_ms'] = this.opts.tuning.windowMs\n config['max_batch_size'] = this.opts.tuning.maxBatchSize\n config['num_streams'] = this.opts.tuning.numStreams\n config['intra_op_threads'] = this.opts.tuning.intraOpThreads\n }\n const configBuf = Buffer.from(JSON.stringify(config), 'utf8')\n this.writeFrame(0, MSG_COMMAND, configBuf)\n\n return new Promise<{ startupMs: number; loadedCount: number; workers: number }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(0)\n reject(new Error(`PoolWorker[${this.opts.workerLabel}]: timeout waiting for ready (120s)`))\n }, 120_000)\n\n this.pending.set(0, {\n resolve: (result) => {\n clearTimeout(timeout)\n if (result['status'] === 'ready') {\n this.ready = true\n const loadedCount = result['models'] as number\n const startupMs = result['startupMs'] as number\n const workers = (result['workers'] as number | undefined) ?? 1\n resolve({ startupMs, loadedCount, workers })\n } else {\n reject(new Error(`Unexpected pool status: ${JSON.stringify(result)}`))\n }\n },\n reject: (err) => {\n clearTimeout(timeout)\n reject(err)\n },\n })\n })\n }\n\n async infer(modelByte: number, jpeg: Buffer): Promise<Record<string, unknown>> {\n this.ensureReady()\n const payload = Buffer.concat([Buffer.from([modelByte]), jpeg])\n return this.dispatch(MSG_INFER_JPEG, payload)\n }\n\n async inferRaw(\n modelByte: number,\n raw: Buffer,\n width: number,\n height: number,\n format: RawFrameFormat,\n ): Promise<Record<string, unknown>> {\n this.ensureReady()\n const fmtCode = RAW_FMT_CODE[format]\n const rawHeader = Buffer.allocUnsafe(10)\n rawHeader[0] = modelByte\n rawHeader.writeUInt32LE(width, 1)\n rawHeader.writeUInt32LE(height, 5)\n rawHeader[9] = fmtCode\n return this.dispatchMulti(MSG_INFER_RAW, rawHeader.length + raw.length, [rawHeader, raw])\n }\n\n async inferBatch(\n modelByte: number,\n items: readonly { readonly raw: Buffer; readonly width: number; readonly height: number; readonly format: RawFrameFormat }[],\n ): Promise<readonly Record<string, unknown>[]> {\n this.ensureReady()\n if (items.length === 0) return []\n const headerSize = 2 + items.length * 13\n const totalRawSize = items.reduce((sum, it) => sum + it.raw.length, 0)\n const payload = Buffer.allocUnsafe(headerSize + totalRawSize)\n payload[0] = modelByte\n payload[1] = items.length\n let offset = 2\n for (const item of items) {\n payload.writeUInt32LE(item.width, offset)\n payload.writeUInt32LE(item.height, offset + 4)\n payload[offset + 8] = RAW_FMT_CODE[item.format]\n payload.writeUInt32LE(item.raw.length, offset + 9)\n offset += 13\n item.raw.copy(payload, offset)\n offset += item.raw.length\n }\n const response = await this.dispatch(MSG_INFER_BATCH, payload)\n const results = response['results']\n if (!Array.isArray(results)) {\n throw new Error(`PoolWorker.inferBatch: malformed response: ${JSON.stringify(response)}`)\n }\n return results as readonly Record<string, unknown>[]\n }\n\n async cacheFrame(\n frameId: number,\n raw: Buffer,\n width: number,\n height: number,\n format: RawFrameFormat,\n ): Promise<void> {\n this.ensureReady()\n const fmtCode = RAW_FMT_CODE[format]\n const header = Buffer.allocUnsafe(13)\n header.writeUInt32LE(frameId, 0)\n header.writeUInt32LE(width, 4)\n header.writeUInt32LE(height, 8)\n header[12] = fmtCode\n const payloadLen = header.length + raw.length\n const result = await this.dispatchMulti(MSG_CACHE_FRAME, payloadLen, [header, raw])\n if (result['error']) throw new Error(String(result['error']))\n }\n\n async inferCached(modelByte: number, frameId: number): Promise<Record<string, unknown>> {\n this.ensureReady()\n const payload = Buffer.allocUnsafe(5)\n payload[0] = modelByte\n payload.writeUInt32LE(frameId, 1)\n return this.dispatch(MSG_INFER_CACHED, payload)\n }\n\n async sendCommand(cmd: Record<string, unknown>): Promise<CommandResponse> {\n this.ensureReady()\n const payload = Buffer.from(JSON.stringify(cmd), 'utf8')\n const raw = await this.dispatch(MSG_COMMAND, payload)\n return raw as unknown as CommandResponse\n }\n\n async dispose(): Promise<void> {\n if (this.process) {\n this.process.stdin?.end()\n this.process.kill('SIGTERM')\n this.process = null\n this.ready = false\n }\n }\n\n // ── Private — wire-level ──────────────────────────────────────────\n\n private dispatch(msgType: number, payload: Buffer): Promise<Record<string, unknown>> {\n const reqId = this.allocRequestId()\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pending.set(reqId, { resolve, reject })\n try {\n this.writeFrame(reqId, msgType, payload)\n } catch (err) {\n this.pending.delete(reqId)\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n }\n\n private dispatchMulti(\n msgType: number,\n payloadLen: number,\n parts: readonly Buffer[],\n ): Promise<Record<string, unknown>> {\n const reqId = this.allocRequestId()\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pending.set(reqId, { resolve, reject })\n try {\n if (!this.process?.stdin) throw new Error('PoolWorker: not initialized')\n const prefix = Buffer.allocUnsafe(PREFIX_LEN)\n prefix.writeUInt32LE(4 + 1 + payloadLen, 0)\n prefix.writeUInt32LE(reqId, 4)\n prefix[8] = msgType\n this.process.stdin.write(prefix)\n for (const part of parts) {\n this.process.stdin.write(part)\n }\n } catch (err) {\n this.pending.delete(reqId)\n reject(err instanceof Error ? err : new Error(String(err)))\n }\n })\n }\n\n private allocRequestId(): number {\n let id = this.nextRequestId\n this.nextRequestId = id >= 0xFFFFFFFF ? 1 : id + 1\n while (this.pending.has(id)) {\n id = this.nextRequestId\n this.nextRequestId = id >= 0xFFFFFFFF ? 1 : id + 1\n }\n return id\n }\n\n private writeFrame(reqId: number, msgType: number, payload: Buffer): void {\n if (!this.process?.stdin) {\n throw new Error('PoolWorker: not initialized')\n }\n const totalLen = 4 + 1 + payload.length\n const header = Buffer.allocUnsafe(PREFIX_LEN)\n header.writeUInt32LE(totalLen, 0)\n header.writeUInt32LE(reqId, 4)\n header[8] = msgType\n this.process.stdin.write(header)\n this.process.stdin.write(payload)\n }\n\n private ensureReady(): void {\n if (!this.ready || !this.process?.stdin) {\n throw new Error(`PoolWorker[${this.opts.workerLabel}]: not initialized`)\n }\n }\n\n private drainResponses(): void {\n while (this.receiveBuffer.length >= 8) {\n const totalLen = this.receiveBuffer.readUInt32LE(0)\n if (this.receiveBuffer.length < 4 + totalLen) break\n const reqId = this.receiveBuffer.readUInt32LE(4)\n const jsonBytes = this.receiveBuffer.subarray(8, 4 + totalLen)\n this.receiveBuffer = this.receiveBuffer.subarray(4 + totalLen)\n\n const entry = this.pending.get(reqId)\n if (!entry) {\n this.log.warn('Response for unknown request id', { meta: { worker: this.opts.workerLabel, reqId } })\n continue\n }\n this.pending.delete(reqId)\n\n try {\n const parsed = JSON.parse(jsonBytes.toString('utf8')) as Record<string, unknown>\n entry.resolve(parsed)\n } catch (err) {\n entry.reject(err instanceof Error ? err : new Error(String(err)))\n }\n }\n }\n\n private rejectAll(err: Error): void {\n const entries = [...this.pending.values()]\n this.pending.clear()\n for (const entry of entries) {\n entry.reject(err)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// SharedInferencePool — multi-process orchestrator\n// ---------------------------------------------------------------------------\n\nexport class SharedInferencePool {\n private readonly workers: PoolWorker[] = []\n private nextDispatchIdx = 0\n private nextFreeIndex = 0\n private nextFrameId = 1\n private readonly log: IScopedLogger\n private readonly concurrency: number\n private readonly tuning: PoolTuning | null\n private readonly numWorkers: number\n\n constructor(\n private readonly pythonPath: string,\n private readonly poolRuntime: PoolRuntime,\n logger: IScopedLogger,\n options: SharedInferencePoolOptions = {},\n ) {\n this.log = logger\n this.concurrency = Math.max(1, options.concurrency ?? 1)\n this.tuning = options.tuning ?? null\n this.numWorkers = Math.max(1, options.numWorkers ?? 1)\n }\n\n /** Pid of the first worker (for legacy callers). Use `getPids()` for all. */\n getPid(): number | null {\n return this.workers[0]?.getPid() ?? null\n }\n\n getPids(): readonly (number | null)[] {\n return this.workers.map((w) => w.getPid())\n }\n\n /**\n * Start N pool workers in parallel. Each worker loads the same\n * initial model set so subsequent inference calls land on a worker\n * with the requested model already in its slot map.\n */\n async initialize(initialModels: readonly PoolModelConfig[] = []): Promise<{ startupMs: number; loadedCount: number; workers: number }> {\n const scriptPath = this.resolveScriptPath()\n\n for (let i = 0; i < this.numWorkers; i++) {\n this.workers.push(new PoolWorker({\n pythonPath: this.pythonPath,\n scriptPath,\n poolRuntime: this.poolRuntime,\n concurrency: this.concurrency,\n tuning: this.tuning,\n logger: this.log,\n workerLabel: `w${i}`,\n }))\n }\n\n const t0 = performance.now()\n const results = await Promise.all(this.workers.map((w) => w.initialize(initialModels)))\n const startupMs = Math.round(performance.now() - t0)\n\n this.nextFreeIndex = initialModels.length\n const loadedCount = results[0]?.loadedCount ?? 0\n const innerWorkers = results.reduce((s, r) => s + r.workers, 0)\n this.log.info('Pool ready', {\n meta: {\n loadedCount,\n startupMs,\n runtime: this.poolRuntime,\n workers: this.numWorkers,\n pythonWorkers: innerWorkers,\n pids: this.getPids(),\n },\n })\n return { startupMs, loadedCount, workers: this.numWorkers }\n }\n\n // -------------------------------------------------------------------------\n // Inference (hot path) — round-robin across workers\n // -------------------------------------------------------------------------\n\n async infer(modelIndex: number, jpeg: Buffer): Promise<Record<string, unknown>> {\n const w = this.pickWorker()\n return w.infer(this.encodeModelByte(modelIndex), jpeg)\n }\n\n async inferRaw(\n modelIndex: number,\n raw: Buffer,\n width: number,\n height: number,\n format: RawFrameFormat,\n ): Promise<Record<string, unknown>> {\n const w = this.pickWorker()\n return w.inferRaw(this.encodeModelByte(modelIndex), raw, width, height, format)\n }\n\n async inferBatch(\n modelIndex: number,\n items: readonly { readonly raw: Buffer; readonly width: number; readonly height: number; readonly format: RawFrameFormat }[],\n ): Promise<readonly Record<string, unknown>[]> {\n if (items.length > 255) {\n throw new Error(`SharedInferencePool.inferBatch: max 255 items per call, got ${items.length}`)\n }\n const w = this.pickWorker()\n return w.inferBatch(this.encodeModelByte(modelIndex), items)\n }\n\n async inferCached(modelIndex: number, frameId: number): Promise<Record<string, unknown>> {\n // Cached frames are propagated to ALL workers at cache time, so any\n // worker can serve the inferCached call.\n const w = this.pickWorker()\n return w.inferCached(this.encodeModelByte(modelIndex), frameId)\n }\n\n getHandle(modelIndex: number): IInferenceEngine {\n return new PoolHandle(this, modelIndex)\n }\n\n // -------------------------------------------------------------------------\n // Bench frame cache — propagated to all workers\n // -------------------------------------------------------------------------\n\n async cacheFrame(\n raw: Buffer,\n width: number,\n height: number,\n format: RawFrameFormat,\n ): Promise<{ frameId: number; width: number; height: number }> {\n const frameId = this.nextFrameId++\n await Promise.all(\n this.workers.map((w) => w.cacheFrame(frameId, raw, width, height, format)),\n )\n return { frameId, width, height }\n }\n\n async uncacheFrame(frameId: number): Promise<void> {\n await Promise.all(\n this.workers.map((w) => w.sendCommand({ cmd: 'uncache_frame', frameId })),\n )\n }\n\n // -------------------------------------------------------------------------\n // Model management — propagated to ALL workers (synchronous waterfall)\n // -------------------------------------------------------------------------\n\n async loadModel(index: number, config: PoolModelConfig): Promise<{ loadMs: number }> {\n const responses = await Promise.all(this.workers.map((w) => w.sendCommand({\n cmd: 'load',\n index,\n config: serializeModelConfig(config),\n })))\n for (const resp of responses) {\n if (resp.status !== 'ok') {\n throw new Error(`Failed to load model at index ${index}: ${resp.error ?? 'unknown'}`)\n }\n }\n if (index >= this.nextFreeIndex) {\n this.nextFreeIndex = index + 1\n }\n const maxLoadMs = Math.max(...responses.map((r) => r.loadMs ?? 0))\n return { loadMs: maxLoadMs }\n }\n\n async unloadModel(index: number): Promise<void> {\n const responses = await Promise.all(this.workers.map((w) => w.sendCommand({ cmd: 'unload', index })))\n for (const resp of responses) {\n if (resp.status !== 'ok') {\n throw new Error(`Failed to unload model at index ${index}: ${resp.error ?? 'unknown'}`)\n }\n }\n }\n\n async replaceModel(index: number, config: PoolModelConfig): Promise<{ loadMs: number }> {\n const responses = await Promise.all(this.workers.map((w) => w.sendCommand({\n cmd: 'replace',\n index,\n config: serializeModelConfig(config),\n })))\n for (const resp of responses) {\n if (resp.status !== 'ok') {\n throw new Error(`Failed to replace model at index ${index}: ${resp.error ?? 'unknown'}`)\n }\n }\n const maxLoadMs = Math.max(...responses.map((r) => r.loadMs ?? 0))\n return { loadMs: maxLoadMs }\n }\n\n async getStatus(): Promise<readonly ModelSlotStatus[]> {\n // Worker 0 is authoritative — model indices are kept in sync across\n // all workers by the propagation in load/unload/replace.\n if (this.workers.length === 0) return []\n const resp = await this.workers[0]!.sendCommand({ cmd: 'status' })\n return (resp.models ?? []) as ModelSlotStatus[]\n }\n\n allocateIndex(): number {\n return this.nextFreeIndex++\n }\n\n isReady(): boolean {\n return this.workers.length > 0 && this.workers.every((w) => w.isReady())\n }\n\n async dispose(): Promise<void> {\n await Promise.all(this.workers.map((w) => w.dispose()))\n this.workers.length = 0\n this.log.info('Pool process(es) terminated')\n }\n\n // -------------------------------------------------------------------------\n // Private\n // -------------------------------------------------------------------------\n\n private pickWorker(): PoolWorker {\n if (this.workers.length === 0) {\n throw new Error('SharedInferencePool: not initialized')\n }\n const idx = this.nextDispatchIdx\n this.nextDispatchIdx = (this.nextDispatchIdx + 1) % this.workers.length\n return this.workers[idx]!\n }\n\n private encodeModelByte(modelIndex: number): number {\n if (modelIndex < 0 || modelIndex > 254) {\n throw new Error(`Model index ${modelIndex} out of range (0-254)`)\n }\n return modelIndex\n }\n\n private resolveScriptPath(): string {\n const candidates = [\n path.join(__dirname, '../../python/inference_pool.py'),\n path.join(__dirname, '../python/inference_pool.py'),\n path.join(__dirname, '../../../python/inference_pool.py'),\n ]\n\n try {\n const pkgPath = require.resolve('@camstack/addon-detection-pipeline/package.json')\n candidates.unshift(path.join(path.dirname(pkgPath), 'python', 'inference_pool.py'))\n } catch {\n // Package not resolvable — use relative paths\n }\n\n const found = candidates.find((p) => fs.existsSync(p))\n if (!found) {\n throw new Error(`inference_pool.py not found. Searched:\\n${candidates.join('\\n')}`)\n }\n return found\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction serializeModelConfig(config: PoolModelConfig): Record<string, unknown> {\n const result: Record<string, unknown> = {\n path: config.path,\n inputSize: config.inputSize,\n preprocessMode: config.preprocessMode,\n postprocessor: config.postprocessor,\n confidence: config.confidence,\n }\n if (config.labels) result['labels'] = config.labels\n if (config.charset) result['charset'] = config.charset\n if (config.numClasses) result['numClasses'] = config.numClasses\n if (config.strides) result['strides'] = config.strides\n if (config.maskThreshold !== undefined) result['maskThreshold'] = config.maskThreshold\n if (config.device) result['device'] = config.device\n return result\n}\n","import type { PipelineDefaultStep } from '@camstack/types'\n\n/**\n * Flatten a step tree into a list of enabled video steps (DFS).\n * Audio-classifier steps are excluded — they run independently\n * via the audio pipeline, not the video inference pool.\n */\nexport function flattenEnabledVideoSteps(steps: readonly PipelineDefaultStep[]): readonly PipelineDefaultStep[] {\n const result: PipelineDefaultStep[] = []\n const walk = (nodes: readonly PipelineDefaultStep[]): void => {\n for (const step of nodes) {\n if (!step.enabled) continue\n if (step.slot === 'audio-classifier') continue\n result.push(step)\n if (step.children?.length) {\n walk(step.children)\n }\n }\n }\n walk(steps)\n return result\n}\n","/**\n * PipelineModelManager — maps (stepId, modelId) ↔ pool model index.\n *\n * Multi-slot semantics: each `stepId` (e.g. `object-detection`) can host\n * multiple loaded models simultaneously. One of them is the *active*\n * model — the one returned to lookups that don't specify a modelId\n * (the camera-runtime hot path). The others are \"warm\" extras held\n * for benchmark / override use.\n *\n * Without this, the bench's override model would replace the runtime\n * model in the same slot, and the next camera dispatch would replace\n * it back — burning ~1.4s of `replaceModel` per ping-pong cycle on\n * coreml. See the perf investigation log\n * (`scripts/bench-pipeline-investigation.mts`) for the smoking-gun\n * trace.\n *\n * LRU policy:\n * - At most `maxModelsPerStep` models live per stepId (default 4).\n * - Eviction picks the least-recently-touched non-active entry.\n * - The active model is NEVER evicted — that would tank live cameras.\n * - Touch happens on `getHandle` / `getPoolIndex` lookups.\n */\nimport type { IInferenceEngine, IScopedLogger, PoolModelConfig, PipelineDefaultStep } from '@camstack/types'\nimport type { SharedInferencePool } from './shared-inference-pool.js'\nimport { flattenEnabledVideoSteps } from './step-utils.js'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface LoadedStep {\n readonly stepId: string\n readonly modelId: string\n readonly poolIndex: number\n /** Monotonic LRU counter — bumped on every `getHandle`/`getPoolIndex`. */\n lruTick: number\n}\n\ninterface StepDiff {\n readonly added: readonly PipelineDefaultStep[]\n readonly removed: readonly LoadedStep[]\n readonly modelChanged: readonly { step: PipelineDefaultStep; oldModelId: string }[]\n readonly unchanged: readonly LoadedStep[]\n}\n\nexport interface PipelineModelManagerOptions {\n /** Hard cap on warm models per stepId (active + extras). Default 4. */\n readonly maxModelsPerStep?: number\n}\n\n// ---------------------------------------------------------------------------\n// PipelineModelManager\n// ---------------------------------------------------------------------------\n\nexport class PipelineModelManager {\n /** stepId → modelId → loaded entry. */\n private readonly loaded = new Map<string, Map<string, LoadedStep>>()\n /** stepId → currently-active modelId (the runtime choice). */\n private readonly activeByStep = new Map<string, string>()\n /** Monotonic counter feeding the LRU tick on every touch. */\n private lruClock = 0\n private readonly log: IScopedLogger\n private readonly maxModelsPerStep: number\n\n constructor(\n private readonly pool: SharedInferencePool,\n private readonly buildPoolConfig: (stepId: string, modelId: string) => PoolModelConfig,\n logger: IScopedLogger,\n options?: PipelineModelManagerOptions,\n ) {\n this.log = logger\n this.maxModelsPerStep = options?.maxModelsPerStep ?? 4\n }\n\n /**\n * Apply a new pipeline configuration — driven by the runtime config\n * (camera-serving pipeline). Rewrites the *active* model per step,\n * keeping prior actives warm in extra slots up to `maxModelsPerStep`.\n *\n * Steps that disappear from `newSteps` get fully unloaded (every\n * model variant for that stepId).\n */\n async applyConfig(newSteps: readonly PipelineDefaultStep[]): Promise<void> {\n const enabledSteps = flattenEnabledVideoSteps(newSteps)\n const diff = this.computeDiff(enabledSteps)\n\n // Unload removed steps entirely (every model variant).\n for (const removed of diff.removed) {\n await this.unloadEntry(removed)\n this.log.info('Unloading step', { meta: { step: removed.stepId, modelId: removed.modelId, poolIndex: removed.poolIndex } })\n }\n\n // Load brand-new steps — first model becomes the active one.\n for (const added of diff.added) {\n await this.ensureLoaded(added.addonId, added.modelId)\n this.activeByStep.set(added.addonId, added.modelId)\n this.log.info('Step loaded', { meta: { step: added.addonId, modelId: added.modelId, active: true } })\n }\n\n // Active model swap for already-loaded steps.\n for (const { step, oldModelId } of diff.modelChanged) {\n await this.ensureLoaded(step.addonId, step.modelId)\n this.activeByStep.set(step.addonId, step.modelId)\n this.log.info('Active model switched', {\n meta: { step: step.addonId, fromModelId: oldModelId, toModelId: step.modelId },\n })\n // Old active is now eviction-eligible; LRU sweep below trims if needed.\n await this.enforceLruCap(step.addonId)\n }\n }\n\n /**\n * Get an `IInferenceEngine` handle. Without `modelId`, returns the\n * active model for the step (camera-runtime hot path). Pass\n * `modelId` to grab a specific warm variant (benchmark path).\n * Touches the LRU clock so the looked-up model survives eviction\n * pressure from concurrent loads.\n */\n getHandle(stepId: string, modelId?: string): IInferenceEngine {\n const entry = this.resolveOrThrow(stepId, modelId)\n this.touch(entry)\n return this.pool.getHandle(entry.poolIndex)\n }\n\n /** True iff the step has any model loaded. */\n isLoaded(stepId: string): boolean {\n return this.loaded.has(stepId) && (this.loaded.get(stepId)?.size ?? 0) > 0\n }\n\n /** True iff the specific (stepId, modelId) pair is resident. */\n isLoadedWithModel(stepId: string, modelId: string): boolean {\n return this.loaded.get(stepId)?.has(modelId) ?? false\n }\n\n /** ModelId currently designated as active for the step. */\n getLoadedModelId(stepId: string): string | undefined {\n return this.activeByStep.get(stepId)\n }\n\n /**\n * Pool index for a given (stepId, modelId). Without `modelId`,\n * returns the active model's index. Used by the inference fast\n * paths that bypass `getHandle` and call `pool.inferBatch` directly.\n */\n getPoolIndex(stepId: string, modelId?: string): number | null {\n const entry = this.resolve(stepId, modelId)\n if (!entry) return null\n this.touch(entry)\n return entry.poolIndex\n }\n\n /**\n * Direct access to the underlying pool — exposed so the provider can\n * call `inferBatch` without re-fetching individual handles. Pool\n * lifetime is owned by `EngineFactory`, so callers must not dispose.\n */\n getPool(): SharedInferencePool {\n return this.pool\n }\n\n /**\n * Snapshot of every (stepId, modelId, poolIndex) currently resident.\n * Used by the `Warm instances` UI tab + cap-level `listLoadedEngines`\n * to surface both the runtime model and the warm extras.\n */\n getLoadedSteps(): readonly { readonly stepId: string; readonly modelId: string; readonly poolIndex: number; readonly active: boolean }[] {\n const out: { stepId: string; modelId: string; poolIndex: number; active: boolean }[] = []\n for (const [stepId, models] of this.loaded) {\n const activeModelId = this.activeByStep.get(stepId)\n for (const entry of models.values()) {\n out.push({\n stepId: entry.stepId,\n modelId: entry.modelId,\n poolIndex: entry.poolIndex,\n active: entry.modelId === activeModelId,\n })\n }\n }\n return out\n }\n\n /**\n * Load extra models without touching the active runtime model.\n * Idempotent — already-loaded (stepId, modelId) pairs are no-op'd.\n * If `maxModelsPerStep` is exceeded, the LRU non-active variant is\n * evicted (NOT the active one — that would knock the runtime cameras\n * offline). The \"active\" designation is unchanged: callers without\n * a `modelId` argument keep hitting the runtime model.\n */\n async loadAdditional(steps: readonly PipelineDefaultStep[]): Promise<void> {\n for (const step of steps) {\n if (this.isLoadedWithModel(step.addonId, step.modelId)) continue\n await this.ensureLoaded(step.addonId, step.modelId)\n // First-ever model for this step becomes the active one — covers\n // the case where the bench is the first caller after boot.\n if (!this.activeByStep.has(step.addonId)) {\n this.activeByStep.set(step.addonId, step.modelId)\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Private — load / evict / lookup\n // -------------------------------------------------------------------------\n\n /**\n * Load `(stepId, modelId)` into the pool if not already resident.\n * Honors `maxModelsPerStep` — evicts a non-active LRU variant first\n * when the cap would otherwise be exceeded.\n */\n private async ensureLoaded(stepId: string, modelId: string): Promise<LoadedStep> {\n let perStep = this.loaded.get(stepId)\n if (!perStep) {\n perStep = new Map<string, LoadedStep>()\n this.loaded.set(stepId, perStep)\n }\n const existing = perStep.get(modelId)\n if (existing) {\n this.touch(existing)\n return existing\n }\n // Make room if at capacity.\n while (perStep.size >= this.maxModelsPerStep) {\n const evicted = this.pickEvictionTarget(stepId)\n if (!evicted) break\n await this.unloadEntry(evicted)\n this.log.info('LRU evicted warm model', {\n meta: { step: stepId, modelId: evicted.modelId, poolIndex: evicted.poolIndex, cap: this.maxModelsPerStep },\n })\n }\n const index = this.pool.allocateIndex()\n const config = this.buildPoolConfig(stepId, modelId)\n this.log.info('Loading step variant', { meta: { step: stepId, modelId, poolIndex: index } })\n const { loadMs } = await this.pool.loadModel(index, config)\n this.log.info('Step variant loaded', { meta: { step: stepId, modelId, poolIndex: index, loadMs } })\n const entry: LoadedStep = { stepId, modelId, poolIndex: index, lruTick: ++this.lruClock }\n perStep.set(modelId, entry)\n return entry\n }\n\n private async unloadEntry(entry: LoadedStep): Promise<void> {\n await this.pool.unloadModel(entry.poolIndex)\n const perStep = this.loaded.get(entry.stepId)\n perStep?.delete(entry.modelId)\n if (perStep && perStep.size === 0) {\n this.loaded.delete(entry.stepId)\n this.activeByStep.delete(entry.stepId)\n }\n }\n\n /** Select the LRU non-active entry for a step. Returns null when only the active one is present. */\n private pickEvictionTarget(stepId: string): LoadedStep | null {\n const perStep = this.loaded.get(stepId)\n if (!perStep) return null\n const activeModelId = this.activeByStep.get(stepId)\n let oldest: LoadedStep | null = null\n for (const entry of perStep.values()) {\n if (entry.modelId === activeModelId) continue\n if (!oldest || entry.lruTick < oldest.lruTick) oldest = entry\n }\n return oldest\n }\n\n /** Trim the step down to `maxModelsPerStep` entries, evicting LRU non-actives. */\n private async enforceLruCap(stepId: string): Promise<void> {\n const perStep = this.loaded.get(stepId)\n if (!perStep) return\n while (perStep.size > this.maxModelsPerStep) {\n const evicted = this.pickEvictionTarget(stepId)\n if (!evicted) break\n await this.unloadEntry(evicted)\n }\n }\n\n private resolve(stepId: string, modelId?: string): LoadedStep | null {\n const perStep = this.loaded.get(stepId)\n if (!perStep) return null\n const targetModelId = modelId ?? this.activeByStep.get(stepId)\n if (!targetModelId) return null\n return perStep.get(targetModelId) ?? null\n }\n\n private resolveOrThrow(stepId: string, modelId?: string): LoadedStep {\n const entry = this.resolve(stepId, modelId)\n if (!entry) {\n throw new Error(\n `Step \"${stepId}\"${modelId ? ` (model \"${modelId}\")` : ''} is not loaded in the inference pool`,\n )\n }\n return entry\n }\n\n private touch(entry: LoadedStep): void {\n entry.lruTick = ++this.lruClock\n }\n\n // -------------------------------------------------------------------------\n // Diff (active-model granularity — bench extras don't enter the diff)\n // -------------------------------------------------------------------------\n\n /** Compute diff between currently-active steps and desired steps. */\n private computeDiff(desiredSteps: readonly PipelineDefaultStep[]): StepDiff {\n const desiredMap = new Map<string, PipelineDefaultStep>()\n for (const step of desiredSteps) desiredMap.set(step.addonId, step)\n\n const added: PipelineDefaultStep[] = []\n const removed: LoadedStep[] = []\n const modelChanged: { step: PipelineDefaultStep; oldModelId: string }[] = []\n const unchanged: LoadedStep[] = []\n\n // Walk the active (runtime) set — `loaded` may carry warm extras\n // that aren't part of the runtime config and shouldn't enter the\n // diff. The diff drives runtime swaps; warm extras live alongside\n // and are pruned only by LRU.\n for (const [stepId, activeModelId] of this.activeByStep) {\n const activeEntry = this.loaded.get(stepId)?.get(activeModelId)\n if (!activeEntry) continue\n const desired = desiredMap.get(stepId)\n if (!desired) {\n // Step no longer wanted at runtime → remove every variant.\n const perStep = this.loaded.get(stepId)\n if (perStep) for (const entry of perStep.values()) removed.push(entry)\n } else if (desired.modelId !== activeModelId) {\n modelChanged.push({ step: desired, oldModelId: activeModelId })\n } else {\n unchanged.push(activeEntry)\n }\n }\n\n for (const [stepId, step] of desiredMap) {\n if (!this.activeByStep.has(stepId)) added.push(step)\n }\n\n return { added, removed, modelChanged, unchanged }\n }\n}\n","/**\n * Pipeline step classes — each step implements IPipelineStep.\n *\n * `definition` holds the static metadata (models, labels, postprocessor, etc.).\n * `getConfigSchema()` declares per-step settings (class filters, thresholds)\n * that the UI renders dynamically and that can be overridden per-run.\n */\nimport type { StepDefinition, IPipelineStep, ModelFormat, ConfigField } from '@camstack/types'\nimport { COCO_80_LABELS, COCO_TO_MACRO, AUDIO_MACRO_LABELS } from '@camstack/types'\nimport {\n OBJECT_DETECTION_MODELS,\n FACE_DETECTION_MODELS,\n FACE_EMBEDDING_MODELS,\n PLATE_DETECTION_MODELS,\n PLATE_OCR_MODELS,\n ANIMAL_CLASSIFIER_MODELS,\n BIRD_CLASSIFIER_MODELS,\n VEHICLE_CLASSIFIER_MODELS,\n SEGMENTATION_REFINER_MODELS,\n INSTANCE_SEGMENTATION_MODELS,\n AUDIO_CLASSIFIER_MODELS,\n} from './model-catalogs.js'\n\n// ---------------------------------------------------------------------------\n// Label arrays for softmax-based classifiers (loaded into Python pool config)\n// ---------------------------------------------------------------------------\n\nconst ANIMAL_10_LABELS = [\n 'cat', 'cow', 'dog', 'dolphin', 'eagle',\n 'giant panda', 'horse', 'monkey', 'sheep', 'spider',\n] as const\n\n// VGG English G2 charset (index 0 = CTC blank token)\nconst VGG_G2_CHARSET = [\n '', // blank\n ...'0123456789!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''),\n] as const\n\n// Bird and vehicle labels are loaded from JSON files at runtime (too large for inline)\n// The engine factory reads them from extraFiles and passes to the pool config\n\n// ---------------------------------------------------------------------------\n// Base class — steps with no config return empty array\n// ---------------------------------------------------------------------------\n\nclass PipelineStepBase implements IPipelineStep {\n constructor(readonly definition: StepDefinition) {}\n\n getConfigSchema(): readonly ConfigField[] {\n return []\n }\n}\n\n// ---------------------------------------------------------------------------\n// Step classes\n// ---------------------------------------------------------------------------\n\nclass ObjectDetectionStep implements IPipelineStep {\n readonly definition: StepDefinition = {\n id: 'object-detection',\n name: 'Object Detection',\n slot: 'detector',\n postprocessor: 'yolo',\n extractMode: 'full-frame',\n inputClasses: null,\n outputClasses: ['person', 'vehicle', 'animal'],\n models: [...OBJECT_DETECTION_MODELS],\n defaultModelId: 'yolov9s',\n defaultConfidence: 0.5,\n labels: COCO_80_LABELS.map(l => l.id),\n classMap: COCO_TO_MACRO,\n }\n\n getConfigSchema(): readonly ConfigField[] {\n // Rework (Phase 6):\n // - Drop the macro-vs-COCO switch. Macro is the only mode —\n // person / vehicle / animal, mapped internally by the postprocessor\n // (see MACRO_LABELS / COCO_TO_MACRO).\n // - Single macro multiselect with default = all three.\n // - Per-macro minConfidence sliders so each class gets its own\n // sensitivity. Device-scoped so each camera can tune independently\n // with the global default as fallback.\n return [\n {\n type: 'multiselect' as const,\n key: 'enabledMacroClasses',\n label: 'Enabled Classes',\n description: 'Which macro classes to detect (empty = all three).',\n options: [\n { value: 'person', label: 'Person' },\n { value: 'vehicle', label: 'Vehicle' },\n { value: 'animal', label: 'Animal' },\n ],\n default: ['person', 'vehicle', 'animal'],\n },\n {\n type: 'slider' as const,\n key: 'minConfidencePerson',\n label: 'Min Confidence — Person',\n description: 'Drop person predictions below this score.',\n min: 0,\n max: 1,\n step: 0.05,\n default: 0.5,\n showValue: true,\n },\n {\n type: 'slider' as const,\n key: 'minConfidenceVehicle',\n label: 'Min Confidence — Vehicle',\n description: 'Drop vehicle predictions below this score.',\n min: 0,\n max: 1,\n step: 0.05,\n default: 0.5,\n showValue: true,\n },\n {\n type: 'slider' as const,\n key: 'minConfidenceAnimal',\n label: 'Min Confidence — Animal',\n description: 'Drop animal predictions below this score.',\n min: 0,\n max: 1,\n step: 0.05,\n default: 0.5,\n showValue: true,\n },\n ]\n }\n}\n\nclass AudioClassifierStep implements IPipelineStep {\n readonly definition: StepDefinition = {\n id: 'audio-classifier',\n name: 'Audio Classifier',\n slot: 'audio-classifier',\n postprocessor: 'yamnet',\n extractMode: 'full-frame',\n inputClasses: null,\n outputClasses: ['audio'],\n models: [...AUDIO_CLASSIFIER_MODELS],\n // `yamnet-onnx` runs everywhere (onnx/openvino). macOS agents (coreml\n // format) get upgraded to `apple-soundanalysis` at agent-seed time —\n // it's ANE-accelerated and zero-download, which beats yamnet-onnx on\n // Apple Silicon. See `AgentPipelineSettings.addonDefaults` seed logic\n // in the orchestrator.\n defaultModelId: 'yamnet-onnx',\n defaultModelIdByFormat: {\n coreml: 'apple-soundanalysis',\n },\n enabledByDefault: true,\n defaultConfidence: 0.3,\n }\n\n getConfigSchema(): readonly ConfigField[] {\n // Phase 7 (settings redesign): unified naming — the field was\n // called `minScore` in Phase 6; every step that exposes a\n // threshold now uses `minConfidence` as the canonical key so the\n // `FormBuilder v2` + `NodeAddonsSettingsPanel` can render them\n // uniformly. The audio pipeline runtime reads this key at\n // execution time and drops classifications whose score is below\n // the threshold — `dispatch.ts`'s yamnet postprocessor no longer\n // filters internally.\n const defaultClasses = [\n 'scream', 'crying', 'dog', 'alarm', 'doorbell',\n 'glass_breaking', 'gunshot', 'siren', 'footsteps',\n ]\n return [\n {\n type: 'multiselect' as const,\n key: 'enabledAudioClasses',\n label: 'Audio Classes',\n description: 'Which audio macro classes to surface (empty = all).',\n options: AUDIO_MACRO_LABELS.map(l => ({ value: l.id, label: `${l.icon ?? ''} ${l.name}`.trim() })),\n default: defaultClasses,\n },\n {\n type: 'slider' as const,\n key: 'minConfidence',\n label: 'Min Confidence',\n description: 'Drop predictions whose score is below this threshold.',\n default: 0.3,\n min: 0,\n max: 1,\n step: 0.05,\n showValue: true,\n },\n ]\n }\n}\n\nclass AnimalClassifierStep implements IPipelineStep {\n readonly definition: StepDefinition = {\n id: 'animal-classifier',\n name: 'Animal Classifier',\n slot: 'classifier',\n postprocessor: 'softmax',\n extractMode: 'crop-roi',\n inputClasses: ['animal'],\n outputClasses: ['animal-type'],\n models: [...ANIMAL_CLASSIFIER_MODELS],\n defaultModelId: 'animals-10',\n // Specialized — opt-in.\n enabledByDefault: false,\n defaultConfidence: 0.2,\n labels: [...ANIMAL_10_LABELS],\n }\n\n getConfigSchema(): readonly ConfigField[] {\n return [\n {\n type: 'multiselect' as const,\n key: 'enabledClasses',\n label: 'Species',\n description: 'Which animal species to keep (empty = all). The classifier still scores every label but only the selected classes are returned on the detection.',\n options: ANIMAL_10_LABELS.map(l => ({ value: l, label: l })),\n default: [],\n },\n {\n type: 'slider' as const,\n key: 'minConfidence',\n label: 'Min Confidence',\n description: 'Drop classifications whose score is below this threshold.',\n default: this.definition.defaultConfidence,\n min: 0,\n max: 1,\n step: 0.05,\n showValue: true,\n },\n ]\n }\n}\n\n// ---------------------------------------------------------------------------\n// Phase 7 (settings redesign): classifier steps with only a `minConfidence`\n// slider. The base class with empty configSchema is replaced by this thin\n// helper so vehicle-classifier / bird-classifier can declare their\n// threshold consistently without each one spelling out the same class body.\n// ---------------------------------------------------------------------------\n\nclass ClassifierWithMinConfidence extends PipelineStepBase {\n getConfigSchema(): readonly ConfigField[] {\n return [\n {\n type: 'slider' as const,\n key: 'minConfidence',\n label: 'Min Confidence',\n description: 'Drop classifications whose score is below this threshold.',\n default: this.definition.defaultConfidence,\n min: 0,\n max: 1,\n step: 0.05,\n showValue: true,\n },\n ]\n }\n}\n\n// ---------------------------------------------------------------------------\n// Steps with no per-step config (use PipelineStepBase)\n// ---------------------------------------------------------------------------\n\nconst STEP_FACE_DETECTION = new PipelineStepBase({\n id: 'face-detection',\n name: 'Face Detection',\n slot: 'cropper',\n postprocessor: 'scrfd',\n extractMode: 'crop-roi',\n inputClasses: ['person'],\n outputClasses: ['face'],\n models: [...FACE_DETECTION_MODELS],\n defaultModelId: 'scrfd-2.5g',\n defaultConfidence: 0.5,\n defaultMinParentScore: 0.7,\n})\n\nconst STEP_FACE_EMBEDDING = new PipelineStepBase({\n id: 'face-embedding',\n name: 'Face Embedding',\n slot: 'classifier',\n postprocessor: 'arcface',\n extractMode: 'crop-roi',\n inputClasses: ['face'],\n outputClasses: ['identity'],\n models: [...FACE_EMBEDDING_MODELS],\n defaultModelId: 'arcface-r100',\n defaultConfidence: 0,\n})\n\nconst STEP_PLATE_DETECTION = new PipelineStepBase({\n id: 'plate-detection',\n name: 'Plate Detection',\n slot: 'cropper',\n postprocessor: 'yolo',\n extractMode: 'crop-roi',\n inputClasses: ['vehicle'],\n outputClasses: ['plate'],\n models: [...PLATE_DETECTION_MODELS],\n defaultModelId: 'yolov8n-plate',\n defaultConfidence: 0.5,\n defaultMinParentScore: 0.7,\n labels: ['plate'],\n})\n\nconst STEP_PLATE_OCR = new PipelineStepBase({\n id: 'plate-ocr',\n name: 'Plate OCR',\n slot: 'classifier',\n postprocessor: 'ctc',\n extractMode: 'crop-roi',\n inputClasses: ['plate'],\n outputClasses: ['plate-text'],\n models: [...PLATE_OCR_MODELS],\n defaultModelId: 'vgg-english-g2',\n defaultConfidence: 0,\n charset: [...VGG_G2_CHARSET],\n})\n\nconst STEP_BIRD_CLASSIFIER = new ClassifierWithMinConfidence({\n id: 'bird-classifier',\n name: 'Bird Classifier',\n slot: 'classifier',\n postprocessor: 'softmax',\n extractMode: 'crop-roi',\n inputClasses: ['animal'],\n outputClasses: ['species'],\n models: [...BIRD_CLASSIFIER_MODELS],\n defaultModelId: 'bird-nabirds-404',\n // Specialized: operator opts in per deployment (not every install\n // wants bird-species telemetry out of the box).\n enabledByDefault: false,\n defaultConfidence: 0.2,\n})\n\nconst STEP_VEHICLE_CLASSIFIER = new ClassifierWithMinConfidence({\n id: 'vehicle-classifier',\n name: 'Vehicle Classifier',\n slot: 'classifier',\n postprocessor: 'softmax',\n extractMode: 'crop-roi',\n inputClasses: ['vehicle'],\n outputClasses: ['vehicle-type'],\n models: [...VEHICLE_CLASSIFIER_MODELS],\n defaultModelId: 'vehicle-type-efficientnet',\n // Specialized — opt-in.\n enabledByDefault: false,\n defaultConfidence: 0.3,\n})\n\nconst STEP_SEGMENTATION_REFINER = new PipelineStepBase({\n id: 'segmentation-refiner',\n name: 'Saliency Segmentation',\n slot: 'refiner',\n postprocessor: 'saliency',\n extractMode: 'crop-roi',\n inputClasses: null,\n outputClasses: ['mask'],\n models: [...SEGMENTATION_REFINER_MODELS],\n defaultModelId: 'u2netp',\n // Refiners are opt-in: expensive, rarely needed by default.\n enabledByDefault: false,\n defaultConfidence: 0,\n group: 'Segmentation',\n})\n\nconst STEP_INSTANCE_SEGMENTATION = new PipelineStepBase({\n id: 'instance-segmentation',\n name: 'Instance Segmentation',\n slot: 'refiner',\n postprocessor: 'yolo-seg',\n extractMode: 'full-frame',\n inputClasses: null,\n outputClasses: ['mask'],\n models: [...INSTANCE_SEGMENTATION_MODELS],\n defaultModelId: 'yolo26n-seg',\n // Instance segmentation is expensive — default OFF.\n enabledByDefault: false,\n defaultConfidence: 0.25,\n labels: COCO_80_LABELS.map(l => l.id),\n classMap: COCO_TO_MACRO,\n group: 'Segmentation',\n})\n\n// ---------------------------------------------------------------------------\n// Registry — all steps indexed by ID\n// ---------------------------------------------------------------------------\n\nconst STEP_OBJECT_DETECTION_INSTANCE = new ObjectDetectionStep()\nconst STEP_AUDIO_CLASSIFIER_INSTANCE = new AudioClassifierStep()\nconst STEP_ANIMAL_CLASSIFIER_INSTANCE = new AnimalClassifierStep()\n\nexport const ALL_PIPELINE_STEPS: readonly IPipelineStep[] = [\n STEP_OBJECT_DETECTION_INSTANCE,\n STEP_FACE_DETECTION,\n STEP_FACE_EMBEDDING,\n STEP_PLATE_DETECTION,\n STEP_PLATE_OCR,\n STEP_ANIMAL_CLASSIFIER_INSTANCE,\n STEP_BIRD_CLASSIFIER,\n STEP_VEHICLE_CLASSIFIER,\n STEP_SEGMENTATION_REFINER,\n STEP_INSTANCE_SEGMENTATION,\n STEP_AUDIO_CLASSIFIER_INSTANCE,\n]\n\n/** Compat: flat array of StepDefinition for existing consumers */\nexport const ALL_STEPS: readonly StepDefinition[] = ALL_PIPELINE_STEPS.map(s => s.definition)\n\nconst STEP_MAP = new Map(ALL_PIPELINE_STEPS.map(s => [s.definition.id, s]))\n\n/**\n * Look up a pipeline step by ID (class instance with getConfigSchema).\n * @throws if the step ID is not registered.\n */\nexport function getStep(stepId: string): IPipelineStep {\n const step = STEP_MAP.get(stepId)\n if (!step) {\n throw new Error(`Unknown pipeline step: \"${stepId}\". Available: ${ALL_PIPELINE_STEPS.map(s => s.definition.id).join(', ')}`)\n }\n return step\n}\n\n/**\n * Look up a step definition by ID (compat shortcut).\n * @throws if the step ID is not registered.\n */\nexport function getStepDefinition(stepId: string): StepDefinition {\n return getStep(stepId).definition\n}\n\n/**\n * Get the default model ID for a step given the current model format.\n * Selects the smallest model that supports the format.\n * Falls back to StepDefinition.defaultModelId if no format-specific model found.\n */\nexport function getDefaultModelForFormat(stepId: string, format: ModelFormat): string {\n const def = getStepDefinition(stepId)\n const available = def.models.filter((m) => m.formats[format])\n if (available.length === 0) return def.defaultModelId\n\n const sorted = [...available].sort((a, b) => {\n const sizeA = a.formats[format]?.sizeMB ?? Infinity\n const sizeB = b.formats[format]?.sizeMB ?? Infinity\n return sizeA - sizeB\n })\n return sorted[0]!.id\n}\n","/**\n * Consolidated model catalogs for the detection pipeline.\n * Each step references models from here — single source of truth.\n */\nimport type { ModelCatalogEntry } from '@camstack/types'\nimport { hfModelUrl } from '@camstack/types'\n\nconst HF_REPO = 'camstack/camstack-models'\nconst HF_SCRYPTED = 'scrypted/plugin-models'\nconst hf = (path: string) => hfModelUrl(HF_REPO, path)\nconst hfScrypted = (path: string) => hfModelUrl(HF_SCRYPTED, path)\n\nconst MLPACKAGE_FILES = [\n 'Manifest.json',\n 'Data/com.apple.CoreML/model.mlmodel',\n 'Data/com.apple.CoreML/weights/weight.bin',\n] as const\n\n// ---------------------------------------------------------------------------\n// Object Detection — YOLOv9\n// ---------------------------------------------------------------------------\n\nexport const OBJECT_DETECTION_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'yolov9t',\n name: 'YOLOv9 Tiny',\n description: 'YOLOv9 Tiny — ultra-lightweight next-gen detector',\n inputSize: { width: 640, height: 640 },\n labels: [], // COCO labels injected by step definition\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolov9/onnx/camstack-yolov9t.onnx'), sizeMB: 8 },\n coreml: { url: hf('objectDetection/yolov9/coreml/camstack-yolov9t.mlpackage'), sizeMB: 4, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolov9/openvino/camstack-yolov9t.xml'), sizeMB: 6, runtimes: ['python'] },\n },\n },\n {\n id: 'yolov9s',\n name: 'YOLOv9 Small',\n description: 'YOLOv9 Small — improved efficiency over YOLOv8s',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolov9/onnx/camstack-yolov9s.onnx'), sizeMB: 28 },\n coreml: { url: hf('objectDetection/yolov9/coreml/camstack-yolov9s.mlpackage'), sizeMB: 14, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolov9/openvino/camstack-yolov9s.xml'), sizeMB: 16, runtimes: ['python'] },\n },\n },\n {\n id: 'yolov9c',\n name: 'YOLOv9 C',\n description: 'YOLOv9 C — high-accuracy compact model',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolov9/onnx/camstack-yolov9c.onnx'), sizeMB: 97 },\n coreml: { url: hf('objectDetection/yolov9/coreml/camstack-yolov9c.mlpackage'), sizeMB: 48, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolov9/openvino/camstack-yolov9c.xml'), sizeMB: 49, runtimes: ['python'] },\n },\n },\n // ── YOLO26 — latest generation (2025) ──\n {\n id: 'yolo26n',\n name: 'YOLO26 Nano',\n description: 'YOLO26 Nano — latest generation ultra-lightweight detector with attention mechanism',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolo26/onnx/camstack-yolo26n.onnx'), sizeMB: 10 },\n coreml: { url: hf('objectDetection/yolo26/coreml/camstack-yolo26n.mlpackage'), sizeMB: 5, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolo26/openvino/camstack-yolo26n.xml'), sizeMB: 9, runtimes: ['python'] },\n },\n },\n {\n id: 'yolo26s',\n name: 'YOLO26 Small',\n description: 'YOLO26 Small — balanced speed/accuracy with attention-based architecture',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolo26/onnx/camstack-yolo26s.onnx'), sizeMB: 37 },\n coreml: { url: hf('objectDetection/yolo26/coreml/camstack-yolo26s.mlpackage'), sizeMB: 18, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolo26/openvino/camstack-yolo26s.xml'), sizeMB: 36, runtimes: ['python'] },\n },\n },\n {\n id: 'yolo26m',\n name: 'YOLO26 Medium',\n description: 'YOLO26 Medium — high accuracy general-purpose detector',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolo26/onnx/camstack-yolo26m.onnx'), sizeMB: 78 },\n coreml: { url: hf('objectDetection/yolo26/coreml/camstack-yolo26m.mlpackage'), sizeMB: 39, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolo26/openvino/camstack-yolo26m.xml'), sizeMB: 78, runtimes: ['python'] },\n },\n },\n {\n id: 'yolo26l',\n name: 'YOLO26 Large',\n description: 'YOLO26 Large — maximum accuracy for demanding deployments',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolo26/onnx/camstack-yolo26l.onnx'), sizeMB: 95 },\n coreml: { url: hf('objectDetection/yolo26/coreml/camstack-yolo26l.mlpackage'), sizeMB: 48, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolo26/openvino/camstack-yolo26l.xml'), sizeMB: 95, runtimes: ['python'] },\n },\n },\n {\n id: 'yolo26x',\n name: 'YOLO26 XLarge',\n description: 'YOLO26 XLarge — highest accuracy, attention-based architecture',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('objectDetection/yolo26/onnx/camstack-yolo26x.onnx'), sizeMB: 213 },\n coreml: { url: hf('objectDetection/yolo26/coreml/camstack-yolo26x.mlpackage'), sizeMB: 107, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('objectDetection/yolo26/openvino/camstack-yolo26x.xml'), sizeMB: 213, runtimes: ['python'] },\n },\n },\n // ── Scrypted ReLU variants (optimized for int8 quantization) ──\n // Note: Scrypted CoreML models are compiled with 320x320 fixed input size\n {\n id: 'scrypted-yolov9t-relu',\n name: 'YOLOv9 Tiny ReLU (Scrypted)',\n description: 'Scrypted YOLOv9t ReLU — optimized for int8 quantization',\n inputSize: { width: 320, height: 320 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hfScrypted('onnx/scrypted_yolov9t_relu/scrypted_yolov9t_relu.onnx'), sizeMB: 8.1 },\n coreml: { url: hfScrypted('coreml/scrypted_yolov9t_relu/scrypted_yolov9t_relu.mlpackage'), sizeMB: 4, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hfScrypted('openvino/scrypted_yolov9t_relu/best.xml'), sizeMB: 6, runtimes: ['python'] },\n },\n },\n {\n id: 'scrypted-yolov9s-relu',\n name: 'YOLOv9 Small ReLU (Scrypted)',\n description: 'Scrypted YOLOv9s ReLU — improved efficiency, int8 ready',\n inputSize: { width: 320, height: 320 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hfScrypted('onnx/scrypted_yolov9s_relu/scrypted_yolov9s_relu.onnx'), sizeMB: 27.9 },\n coreml: { url: hfScrypted('coreml/scrypted_yolov9s_relu/scrypted_yolov9s_relu.mlpackage'), sizeMB: 14, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hfScrypted('openvino/scrypted_yolov9s_relu/best.xml'), sizeMB: 16, runtimes: ['python'] },\n },\n },\n {\n id: 'scrypted-yolov9c-relu',\n name: 'YOLOv9 C ReLU (Scrypted)',\n description: 'Scrypted YOLOv9c ReLU — high-accuracy, int8 ready',\n // Scrypted ReLU variants are all compiled with fixed 320x320 input\n // (see the section header above). Validated at engine load via\n // `validateOnnxInputShape` — mismatches warn, never silently miscompute.\n inputSize: { width: 320, height: 320 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hfScrypted('onnx/scrypted_yolov9c_relu/scrypted_yolov9c_relu.onnx'), sizeMB: 96.9 },\n coreml: { url: hfScrypted('coreml/scrypted_yolov9c_relu/scrypted_yolov9c_relu.mlpackage'), sizeMB: 48, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hfScrypted('openvino/scrypted_yolov9c_relu/best.xml'), sizeMB: 49, runtimes: ['python'] },\n },\n },\n {\n id: 'scrypted-yolov9m-relu',\n name: 'YOLOv9 M ReLU (Scrypted)',\n description: 'Scrypted YOLOv9m ReLU — medium, int8 ready',\n // Scrypted ReLU variants are all compiled with fixed 320x320 input\n // (see the section header above). Validated at engine load via\n // `validateOnnxInputShape` — mismatches warn, never silently miscompute.\n inputSize: { width: 320, height: 320 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hfScrypted('onnx/scrypted_yolov9m_relu/scrypted_yolov9m_relu.onnx'), sizeMB: 76.6 },\n coreml: { url: hfScrypted('coreml/scrypted_yolov9m_relu/scrypted_yolov9m_relu.mlpackage'), sizeMB: 38, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hfScrypted('openvino/scrypted_yolov9m_relu/best.xml'), sizeMB: 38, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Face Detection — SCRFD\n// ---------------------------------------------------------------------------\n\nexport const FACE_DETECTION_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'scrfd-2.5g',\n name: 'SCRFD 2.5G',\n description: 'SCRFD 2.5G — balanced face detection model',\n inputSize: { width: 640, height: 640 },\n labels: [{ id: 'face', name: 'Face' }],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('faceDetection/scrfd/onnx/camstack-scrfd-2.5g.onnx'), sizeMB: 3.1 },\n coreml: { url: hf('faceDetection/scrfd/coreml/camstack-scrfd-2.5g.mlpackage'), sizeMB: 1.7, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('faceDetection/scrfd/openvino/camstack-scrfd-2.5g.xml'), sizeMB: 1.8, runtimes: ['python'] },\n },\n },\n {\n id: 'scrypted-yolov9t-face',\n name: 'YOLOv9t Face ReLU (Scrypted)',\n description: 'Scrypted YOLOv9t face detection — YOLO-based, fast',\n // Scrypted YOLO face variants follow the 320x320 ReLU convention.\n // Validated at engine load via `validateOnnxInputShape`.\n inputSize: { width: 320, height: 320 },\n labels: [{ id: 'face', name: 'Face' }],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hfScrypted('onnx/scrypted_yolov9t_relu_face/scrypted_yolov9t_relu_face.onnx'), sizeMB: 8.1 },\n coreml: { url: hfScrypted('coreml/scrypted_yolov9t_relu_face/scrypted_yolov9t_relu_face.mlpackage'), sizeMB: 4, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hfScrypted('openvino/scrypted_yolov9t_relu_face/best.xml'), sizeMB: 6, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Face Embedding — ArcFace\n// ---------------------------------------------------------------------------\n\nexport const FACE_EMBEDDING_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'arcface-r100',\n name: 'ArcFace R100',\n description: 'ArcFace ResNet-100 — high-accuracy face recognition embeddings (512-d)',\n inputSize: { width: 112, height: 112 },\n inputLayout: 'nhwc',\n labels: [{ id: 'embedding', name: 'Face Embedding' }],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hf('faceRecognition/arcface/onnx/camstack-arcface-arcface.onnx'), sizeMB: 130 },\n coreml: { url: hf('faceRecognition/arcface/coreml/camstack-arcface-r100.mlpackage'), sizeMB: 65, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('faceRecognition/arcface/openvino/camstack-arcface-r100.xml'), sizeMB: 65, runtimes: ['python'] },\n },\n },\n {\n id: 'inception-resnet-v1',\n name: 'Inception ResNet V1 (Scrypted)',\n description: 'FaceNet-style face recognition embeddings (512-d) — Scrypted model',\n inputSize: { width: 160, height: 160 },\n labels: [{ id: 'embedding', name: 'Face Embedding' }],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hfScrypted('onnx/inception_resnet_v1/inception_resnet_v1.onnx'), sizeMB: 89.6 },\n coreml: { url: hfScrypted('coreml/inception_resnet_v1/inception_resnet_v1.mlpackage'), sizeMB: 45, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hfScrypted('openvino/inception_resnet_v1/best.xml'), sizeMB: 45, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Plate Detection — YOLOv8n-plate\n// ---------------------------------------------------------------------------\n\nexport const PLATE_DETECTION_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'yolov8n-plate',\n name: 'YOLOv8 Nano — License Plate',\n description: 'YOLOv8 Nano fine-tuned for license plate detection',\n inputSize: { width: 640, height: 640 },\n labels: [{ id: 'plate', name: 'License Plate' }],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('plateDetection/yolov8-plate/onnx/camstack-yolov8n-plate.onnx'), sizeMB: 12 },\n coreml: { url: hf('plateDetection/yolov8-plate/coreml/camstack-yolov8n-plate.mlpackage'), sizeMB: 5.9, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('plateDetection/yolov8-plate/openvino/camstack-yolov8n-plate.xml'), sizeMB: 6.1, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Plate OCR — vgg_english_g2 (from Scrypted, MIT license)\n// ---------------------------------------------------------------------------\n\nexport const PLATE_OCR_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'vgg-english-g2',\n name: 'VGG English G2',\n description: 'EasyOCR VGG English G2 — text recognition for license plates and scene text',\n inputSize: { width: 384, height: 64 },\n labels: [{ id: 'text', name: 'Plate Text' }],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hfScrypted('onnx/vgg_english_g2/vgg_english_g2.onnx'), sizeMB: 14.4 },\n coreml: { url: hfScrypted('coreml/vgg_english_g2/vgg_english_g2.mlpackage'), sizeMB: 11.2, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hfScrypted('openvino/vgg_english_g2/best.xml'), sizeMB: 7.2, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Animal Classifier — animals-10\n// ---------------------------------------------------------------------------\n\nexport const ANIMAL_CLASSIFIER_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'animals-10',\n name: 'Animal Classifier (10)',\n description: 'ViT-based animal type classifier — cat, cow, dog, dolphin, eagle, panda, horse, monkey, sheep, spider',\n inputSize: { width: 224, height: 224 },\n inputNormalization: 'imagenet',\n labels: [{ id: 'animal-type', name: 'Animal Type' }],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hf('animalClassification/animals-10/onnx/camstack-animals-10.onnx'), sizeMB: 328 },\n coreml: { url: hf('animalClassification/animals-10/coreml/camstack-animals-10.mlpackage'), sizeMB: 164, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('animalClassification/animals-10/openvino/camstack-animals-10.xml'), sizeMB: 164, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Bird Classifier — NABirds 404\n// ---------------------------------------------------------------------------\n\nexport const BIRD_CLASSIFIER_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'bird-nabirds-404',\n name: 'NABirds (404 species)',\n description: 'ResNet50 trained on NABirds — 404 North American species',\n inputSize: { width: 224, height: 224 },\n inputNormalization: 'imagenet',\n labels: [{ id: 'species', name: 'Bird Species' }],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hf('animalClassification/bird-nabirds/onnx/camstack-bird-nabirds-404.onnx'), sizeMB: 93 },\n coreml: { url: hf('animalClassification/bird-nabirds/coreml/camstack-bird-nabirds-404.mlpackage'), sizeMB: 47, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('animalClassification/bird-nabirds/openvino/camstack-bird-nabirds-404.xml'), sizeMB: 47, runtimes: ['python'] },\n },\n extraFiles: [\n { url: hf('animalClassification/bird-nabirds/onnx/camstack-bird-nabirds-404-labels.json'), filename: 'camstack-bird-nabirds-404-labels.json', sizeMB: 0.02 },\n ],\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Vehicle Classifier — EfficientNet\n// ---------------------------------------------------------------------------\n\nexport const VEHICLE_CLASSIFIER_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'vehicle-type-efficientnet',\n name: 'Vehicle Type (EfficientNet)',\n description: 'EfficientNet-B4 vehicle make/model/year classifier — 8,949 classes from VMMRdb',\n inputSize: { width: 380, height: 380 },\n inputNormalization: 'imagenet',\n labels: [{ id: 'vehicle-type', name: 'Vehicle Type' }],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hf('vehicleClassification/efficientnet/onnx/camstack-vehicle-type-efficientnet.onnx'), sizeMB: 135 },\n coreml: { url: hf('vehicleClassification/efficientnet/coreml/camstack-vehicle-type-efficientnet.mlpackage'), sizeMB: 10, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('vehicleClassification/efficientnet/openvino/camstack-vehicle-type-efficientnet.xml'), sizeMB: 68, runtimes: ['python'] },\n },\n extraFiles: [\n { url: hf('vehicleClassification/efficientnet/camstack-vehicle-type-labels.json'), filename: 'camstack-vehicle-type-labels.json', sizeMB: 0.2 },\n ],\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Segmentation Refiner — U2-Net Portable\n// ---------------------------------------------------------------------------\n\nexport const SEGMENTATION_REFINER_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'u2netp',\n name: 'U2-Net Portable',\n description: 'U2-Net-P — ultra-lightweight salient object segmentation (4.7 MB)',\n inputSize: { width: 320, height: 320 },\n labels: [{ id: 'mask', name: 'Segmentation Mask' }],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hf('segmentationRefiner/u2netp/onnx/camstack-u2netp.onnx'), sizeMB: 5 },\n coreml: { url: hf('segmentationRefiner/u2netp/coreml/camstack-u2netp.mlpackage'), sizeMB: 3, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('segmentationRefiner/u2netp/openvino/camstack-u2netp.xml'), sizeMB: 2.5, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Instance Segmentation — YOLO26 Seg (per-instance masks via prototype matmul)\n// ---------------------------------------------------------------------------\n\nexport const INSTANCE_SEGMENTATION_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'yolo26n-seg',\n name: 'YOLO26 Nano Seg',\n description: 'YOLO26 Nano Segmentation — ultra-lightweight instance segmentation with masks',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('segmentation/yolo26-seg/onnx/camstack-yolo26n-seg.onnx'), sizeMB: 11 },\n coreml: { url: hf('segmentation/yolo26-seg/coreml/camstack-yolo26n-seg.mlpackage'), sizeMB: 6, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('segmentation/yolo26-seg/openvino/camstack-yolo26n-seg.xml'), sizeMB: 11, runtimes: ['python'] },\n },\n },\n {\n id: 'yolo26s-seg',\n name: 'YOLO26 Small Seg',\n description: 'YOLO26 Small Segmentation — balanced instance segmentation',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('segmentation/yolo26-seg/onnx/camstack-yolo26s-seg.onnx'), sizeMB: 40 },\n coreml: { url: hf('segmentation/yolo26-seg/coreml/camstack-yolo26s-seg.mlpackage'), sizeMB: 20, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('segmentation/yolo26-seg/openvino/camstack-yolo26s-seg.xml'), sizeMB: 40, runtimes: ['python'] },\n },\n },\n {\n id: 'yolo26m-seg',\n name: 'YOLO26 Medium Seg',\n description: 'YOLO26 Medium Segmentation — high-accuracy instance segmentation',\n inputSize: { width: 640, height: 640 },\n labels: [],\n preprocessMode: 'letterbox',\n formats: {\n onnx: { url: hf('segmentation/yolo26-seg/onnx/camstack-yolo26m-seg.onnx'), sizeMB: 90 },\n coreml: { url: hf('segmentation/yolo26-seg/coreml/camstack-yolo26m-seg.mlpackage'), sizeMB: 45, isDirectory: true, files: [...MLPACKAGE_FILES], runtimes: ['python'] },\n openvino: { url: hf('segmentation/yolo26-seg/openvino/camstack-yolo26m-seg.xml'), sizeMB: 90, runtimes: ['python'] },\n },\n },\n] as const\n\n// ---------------------------------------------------------------------------\n// Audio Classifier — YAMNet (ONNX) + Apple SoundAnalysis (CoreML, zero download)\n// ---------------------------------------------------------------------------\n\nexport const AUDIO_CLASSIFIER_MODELS: readonly ModelCatalogEntry[] = [\n {\n id: 'yamnet-onnx',\n name: 'YAMNet',\n description: 'Google YAMNet — 521-class audio event classifier (3.2 MB ONNX, runs on any platform)',\n inputSize: { width: 1, height: 16000 },\n labels: [],\n preprocessMode: 'resize',\n formats: {\n onnx: { url: hf('audioClassifier/yamnet/onnx/camstack-yamnet.onnx'), sizeMB: 3.2 },\n openvino: { url: hf('audioClassifier/yamnet/onnx/camstack-yamnet.onnx'), sizeMB: 3.2, runtimes: ['python'] },\n coreml: { url: hf('audioClassifier/yamnet/onnx/camstack-yamnet.onnx'), sizeMB: 3.2, runtimes: ['python'] },\n },\n },\n {\n id: 'apple-soundanalysis',\n name: 'Apple SoundAnalysis',\n description: 'macOS built-in — 303 sound categories, Neural Engine accelerated, zero download',\n inputSize: { width: 1, height: 16000 },\n labels: [],\n preprocessMode: 'resize',\n formats: {\n coreml: { url: '', sizeMB: 0 },\n },\n },\n] as const\n","/**\n * NodeEngineManager — manages NodeInferenceEngine instances for nodejs+onnx backend.\n *\n * Each step gets its own ONNX engine. Hot swap = dispose old + create new.\n * No Python process, no mutex — each engine is independent.\n */\nimport type { IInferenceEngine, IScopedLogger, ModelInputMeta, PipelineDefaultStep } from '@camstack/types'\nimport { getStepDefinition } from '../registry/step-definitions.js'\nimport { flattenEnabledVideoSteps } from './step-utils.js'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface LoadedEngine {\n readonly stepId: string\n readonly modelId: string\n readonly engine: IInferenceEngine\n}\n\ninterface EngineCreateFn {\n (modelPath: string, backend: string, meta: ModelInputMeta, logger?: IScopedLogger): Promise<IInferenceEngine>\n}\n\n// ---------------------------------------------------------------------------\n// NodeEngineManager\n// ---------------------------------------------------------------------------\n\nexport class NodeEngineManager {\n private readonly engines = new Map<string, LoadedEngine>()\n private readonly log: IScopedLogger\n\n constructor(\n private readonly backend: string,\n private readonly resolveModelPath: (stepId: string, modelId: string) => string,\n private readonly createEngine: EngineCreateFn,\n logger: IScopedLogger,\n ) {\n this.log = logger\n }\n\n /**\n * Apply a new pipeline configuration.\n * Loads/unloads engines for steps that changed.\n */\n async applyConfig(newSteps: readonly PipelineDefaultStep[]): Promise<void> {\n const enabledSteps = flattenEnabledVideoSteps(newSteps)\n const desiredMap = new Map<string, PipelineDefaultStep>()\n for (const step of enabledSteps) {\n desiredMap.set(step.addonId, step)\n }\n\n // Unload removed steps\n for (const [stepId, loaded] of this.engines) {\n if (!desiredMap.has(stepId)) {\n this.log.info('Unloading ONNX engine for step', { meta: { step: stepId } })\n await loaded.engine.dispose()\n this.engines.delete(stepId)\n }\n }\n\n // Load added or changed steps\n for (const [stepId, step] of desiredMap) {\n const existing = this.engines.get(stepId)\n if (existing && existing.modelId === step.modelId) {\n continue // unchanged\n }\n\n if (existing) {\n this.log.info('Replacing ONNX engine for step', { meta: { step: stepId, fromModelId: existing.modelId, toModelId: step.modelId } })\n await existing.engine.dispose()\n } else {\n this.log.info('Loading ONNX engine for step', { meta: { step: stepId, modelId: step.modelId } })\n }\n\n const def = getStepDefinition(stepId)\n const modelEntry = def.models.find((m) => m.id === step.modelId)\n if (!modelEntry) {\n throw new Error(`Model \"${step.modelId}\" not found in step \"${stepId}\" catalog`)\n }\n\n const modelPath = this.resolveModelPath(stepId, step.modelId)\n const meta: ModelInputMeta = {\n inputSize: modelEntry.inputSize,\n inputNormalization: modelEntry.inputNormalization ?? 'zero-one',\n inputLayout: modelEntry.inputLayout ?? 'nchw',\n preprocessMode: modelEntry.preprocessMode ?? 'letterbox',\n }\n\n const engine = await this.createEngine(modelPath, this.backend, meta, this.log.child(stepId))\n this.engines.set(stepId, { stepId, modelId: step.modelId, engine })\n }\n }\n\n /**\n * Get an IInferenceEngine for a step.\n * @throws if the step is not loaded.\n */\n getEngine(stepId: string): IInferenceEngine {\n const loaded = this.engines.get(stepId)\n if (!loaded) {\n throw new Error(`ONNX engine for step \"${stepId}\" is not loaded`)\n }\n return loaded.engine\n }\n\n isLoaded(stepId: string): boolean {\n return this.engines.has(stepId)\n }\n\n isLoadedWithModel(stepId: string, modelId: string): boolean {\n const loaded = this.engines.get(stepId)\n return loaded !== undefined && loaded.modelId === modelId\n }\n\n async loadAdditional(steps: readonly PipelineDefaultStep[]): Promise<void> {\n const enabledSteps = flattenEnabledVideoSteps(steps)\n for (const step of enabledSteps) {\n const existing = this.engines.get(step.addonId)\n if (existing && existing.modelId === step.modelId) continue\n\n if (existing) {\n this.log.info('Replacing ONNX engine for step', { meta: { step: step.addonId, fromModelId: existing.modelId, toModelId: step.modelId } })\n await existing.engine.dispose()\n } else {\n this.log.info('Loading additional ONNX engine for step', { meta: { step: step.addonId, modelId: step.modelId } })\n }\n\n const def = getStepDefinition(step.addonId)\n const modelEntry = def.models.find((m) => m.id === step.modelId)\n if (!modelEntry) {\n throw new Error(`Model \"${step.modelId}\" not found in step \"${step.addonId}\" catalog`)\n }\n\n const modelPath = this.resolveModelPath(step.addonId, step.modelId)\n const meta: ModelInputMeta = {\n inputSize: modelEntry.inputSize,\n inputNormalization: modelEntry.inputNormalization ?? 'zero-one',\n inputLayout: modelEntry.inputLayout ?? 'nchw',\n preprocessMode: modelEntry.preprocessMode ?? 'letterbox',\n }\n\n const engine = await this.createEngine(modelPath, this.backend, meta, this.log.child(step.addonId))\n this.engines.set(step.addonId, { stepId: step.addonId, modelId: step.modelId, engine })\n }\n }\n\n listLoaded(): readonly { stepId: string; modelId: string }[] {\n return [...this.engines.values()].map((l) => ({ stepId: l.stepId, modelId: l.modelId }))\n }\n\n async disposeAll(): Promise<void> {\n for (const [, loaded] of this.engines) {\n await loaded.engine.dispose()\n }\n this.engines.clear()\n }\n\n // -------------------------------------------------------------------------\n}\n","/**\n * Runtime input-shape validator for ONNX models.\n *\n * Reads the actual graph input dimensions from a locally-downloaded\n * ONNX model and compares them against the catalog declaration. Used\n * by the engine factory at load time so a converter regression or a\n * stale catalog entry surfaces as a one-line warning rather than\n * silent incorrect inference (preprocess uses one size, the model\n * expects another → garbage detections).\n *\n * Only ONNX is validated here. CoreML and OpenVINO converters in our\n * pipeline derive shape from the same source ONNX, so a passing ONNX\n * check covers all three formats. If they ever diverge, extend with a\n * format-specific reader (Manifest.json for .mlpackage, root <input>\n * tag for OpenVINO XML).\n */\nimport * as fs from 'node:fs'\nimport type { IScopedLogger } from '@camstack/types'\n\nexport interface ExpectedInputSize {\n readonly width: number\n readonly height: number\n}\n\nexport interface ShapeMismatch {\n readonly modelId: string\n readonly modelPath: string\n readonly expected: ExpectedInputSize\n readonly actual: { readonly width: number | null; readonly height: number | null }\n}\n\n/**\n * Probe an ONNX file for its first input's shape. Returns null if the\n * file is missing or unparseable — callers should treat null as\n * \"validation skipped\" (not a mismatch).\n *\n * Implementation: uses `onnxruntime-node` to create a session and read\n * `inputMetadata`. The session is released immediately after probing.\n */\nexport async function readOnnxInputShape(\n onnxPath: string,\n): Promise<{ width: number | null; height: number | null } | null> {\n if (!fs.existsSync(onnxPath)) return null\n try {\n const ort = await import('onnxruntime-node')\n const session = await ort.InferenceSession.create(onnxPath)\n const firstInputName = session.inputNames[0]\n if (!firstInputName) return null\n // The session does not expose dimensions on inputMetadata in a\n // typed way across versions — use the lower-level metadata map.\n const meta = (session as unknown as {\n inputMetadata?: Readonly<Record<string, { dimensions?: ReadonlyArray<number | string> }>>\n }).inputMetadata\n const dims = meta?.[firstInputName]?.dimensions ?? []\n // Standard NCHW: [batch, channels, height, width]\n // Some exports use NHWC: [batch, height, width, channels]\n const numeric = dims.map((d) => (typeof d === 'number' ? d : null))\n let height: number | null = null\n let width: number | null = null\n if (numeric.length === 4) {\n // Heuristic: pick the two largest numeric dims as H/W.\n const candidates = numeric\n .map((v, i) => ({ v, i }))\n .filter((e): e is { v: number; i: number } => typeof e.v === 'number' && e.v > 4)\n .sort((a, b) => b.v - a.v)\n if (candidates.length >= 2) {\n // Order by index — earlier dim is H in NCHW, later is W.\n const sorted = candidates.slice(0, 2).sort((a, b) => a.i - b.i)\n height = sorted[0]?.v ?? null\n width = sorted[1]?.v ?? null\n }\n }\n // Release the session — we only needed the metadata.\n if (typeof (session as unknown as { release?: () => Promise<void> }).release === 'function') {\n await (session as unknown as { release: () => Promise<void> }).release()\n }\n return { height, width }\n } catch {\n return null\n }\n}\n\n/**\n * Check the catalog `expected` size against the actual ONNX input shape.\n * Returns a ShapeMismatch when the actual shape is known and disagrees;\n * returns null when the file is missing, unparseable, or matches.\n */\nexport async function validateOnnxInputShape(opts: {\n readonly modelId: string\n readonly modelPath: string\n readonly expected: ExpectedInputSize\n}): Promise<ShapeMismatch | null> {\n const actual = await readOnnxInputShape(opts.modelPath)\n if (!actual) return null\n const matchesH = actual.height === null || actual.height === opts.expected.height\n const matchesW = actual.width === null || actual.width === opts.expected.width\n if (matchesH && matchesW) return null\n return {\n modelId: opts.modelId,\n modelPath: opts.modelPath,\n expected: opts.expected,\n actual,\n }\n}\n\n/**\n * Convenience wrapper: validate, log on mismatch via the supplied\n * logger. Returns true when validation passed (or was skipped) so\n * callers can keep loading; mismatches do not throw — they warn.\n */\nexport async function checkAndLogModelShape(\n opts: {\n readonly modelId: string\n readonly modelPath: string\n readonly expected: ExpectedInputSize\n },\n logger: IScopedLogger,\n): Promise<boolean> {\n const mismatch = await validateOnnxInputShape(opts)\n if (!mismatch) return true\n logger.warn('Model input shape mismatch — catalog declaration disagrees with model file', {\n meta: {\n modelId: mismatch.modelId,\n expectedWidth: mismatch.expected.width,\n expectedHeight: mismatch.expected.height,\n actualWidth: mismatch.actual.width,\n actualHeight: mismatch.actual.height,\n modelPath: mismatch.modelPath,\n },\n })\n return false\n}\n","/**\n * TypeScript postprocessor dispatch — used for nodejs+onnx path.\n *\n * For Python backends, postprocessing happens in inference_pool.py.\n * For Node.js ONNX, the engine returns raw tensors and we postprocess here.\n *\n * Each postprocessor converts EngineOutput → StepOutput (discriminated union with 'kind').\n */\nimport type { EngineOutput, StepOutput, StepDefinition, PostprocessorType } from '@camstack/types'\n\n// Re-export postprocessor types for consumers\nexport type { StepOutput }\n\n/** Postprocessor function signature */\ntype PostprocessFn = (output: EngineOutput, stepDef: StepDefinition) => StepOutput\n\n// ---------------------------------------------------------------------------\n// Type guard for Python-path structured output\n// ---------------------------------------------------------------------------\n\nconst VALID_KINDS = new Set<string>(['detections', 'classifications', 'embedding', 'text', 'mask'])\n\n/**\n * Type guard: validates that a structured payload from the Python pool is a\n * well-formed StepOutput discriminated union.\n *\n * The Python inference_pool.py always sets `output.structured` with a `kind`\n * field. This guard narrows `Record<string, unknown>` to `StepOutput` without\n * resorting to double-cast.\n */\nfunction isStepOutput(value: unknown): value is StepOutput {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Record<string, unknown>)['kind'] === 'string' &&\n VALID_KINDS.has((value as Record<string, unknown>)['kind'] as string)\n )\n}\n\n/**\n * Return the structured payload as `StepOutput`, or null if it fails validation.\n * Callers must handle null by proceeding to raw-tensor postprocessing.\n */\nfunction tryStructured(output: { readonly structured?: Record<string, unknown> }): StepOutput | null {\n if (!output.structured) return null\n if (!isStepOutput(output.structured)) {\n throw new Error(\n `Python pool returned structured output with unexpected kind: ${JSON.stringify(output.structured['kind'])}`,\n )\n }\n return output.structured\n}\n\n\n// ---------------------------------------------------------------------------\n// Individual postprocessors\n// ---------------------------------------------------------------------------\n\nfunction postprocessYolo(output: EngineOutput, stepDef: StepDefinition): StepOutput {\n // For Python path, output.structured is already postprocessed by inference_pool.py\n const structured = tryStructured(output)\n if (structured) return structured\n\n // For ONNX path, raw tensor postprocessing\n const tensor = output.tensor\n if (!tensor) {\n throw new Error('YOLO postprocessor: no tensor in engine output')\n }\n\n const labels = stepDef.labels ?? []\n const numClasses = labels.length || 80\n const numBoxes = tensor.length / (4 + numClasses)\n const letterbox = output.letterbox\n\n // Phase 7 (settings redesign): no internal score floor. All decoded\n // boxes flow through NMS and up to the executor; user-facing filters\n // live in the executor and `applyChildOutput` reading from the step's\n // settings. We still drop boxes with non-positive scores (numerical\n // garbage from the tensor) because they cannot survive any downstream\n // filter anyway.\n const dets: Array<{ class: string; score: number; bbox: [number, number, number, number] }> = []\n\n for (let i = 0; i < numBoxes; i++) {\n const cx = tensor[0 * numBoxes + i]!\n const cy = tensor[1 * numBoxes + i]!\n const w = tensor[2 * numBoxes + i]!\n const h = tensor[3 * numBoxes + i]!\n\n let bestScore = -Infinity\n let bestClass = 0\n for (let j = 0; j < numClasses; j++) {\n const score = tensor[(4 + j) * numBoxes + i]!\n if (score > bestScore) {\n bestScore = score\n bestClass = j\n }\n }\n\n if (bestScore <= 0) continue\n\n let x1 = cx - w / 2\n let y1 = cy - h / 2\n let x2 = cx + w / 2\n let y2 = cy + h / 2\n\n if (letterbox) {\n x1 = (x1 - letterbox.padX) / letterbox.scale\n y1 = (y1 - letterbox.padY) / letterbox.scale\n x2 = (x2 - letterbox.padX) / letterbox.scale\n y2 = (y2 - letterbox.padY) / letterbox.scale\n }\n\n const label = labels[bestClass] ?? String(bestClass)\n dets.push({ class: label, score: bestScore, bbox: [x1, y1, x2, y2] })\n }\n\n // Simple NMS\n const kept = simpleNms(dets, 0.45)\n return { kind: 'detections', detections: kept }\n}\n\nfunction postprocessScrfd(output: EngineOutput, stepDef: StepDefinition): StepOutput {\n const structured = tryStructured(output)\n if (structured) return structured\n\n if (!output.tensors) {\n throw new Error('SCRFD postprocessor: no tensors in engine output')\n }\n\n // Phase 7 (settings redesign): no internal score floor. Face-detection\n // is a cropper and has no user-facing threshold — all anchor hits\n // with a positive score flow through to NMS and then to the\n // downstream face-embedding child step.\n const strides = [8, 16, 32]\n const anchorsPerStride = 2\n const inputSize = Math.max(stepDef.models[0]?.inputSize.width ?? 640, stepDef.models[0]?.inputSize.height ?? 640)\n\n // Match tensors to strides by size: scores (N,1 flat), bboxes (N,4 flat), landmarks (N,10 flat)\n const strideData = matchTensorsToStrides(output.tensors, strides, inputSize, anchorsPerStride)\n\n const letterbox = output.letterbox\n const scale = letterbox?.scale ?? 1\n const padX = letterbox?.padX ?? 0\n const padY = letterbox?.padY ?? 0\n const origW = letterbox?.originalWidth ?? inputSize\n const origH = letterbox?.originalHeight ?? inputSize\n\n const candidates: Array<{\n class: string; score: number\n bbox: [number, number, number, number]\n _xyxy: [number, number, number, number]\n landmarks?: Array<{ x: number; y: number }>\n }> = []\n\n for (const stride of strides) {\n const data = strideData.get(stride)\n if (!data) continue\n\n const { scores, bboxes, landmarks } = data\n const featSize = Math.ceil(inputSize / stride)\n const numAnchors = featSize * featSize * anchorsPerStride\n\n for (let i = 0; i < numAnchors; i++) {\n if (i >= scores.length) break\n const score = scores[i]!\n if (score <= 0) continue\n\n // Anchor center\n const gridIdx = Math.floor(i / anchorsPerStride)\n const cx = ((gridIdx % featSize) + 0.5) * stride\n const cy = (Math.floor(gridIdx / featSize) + 0.5) * stride\n\n // Bbox offsets scaled by stride\n const x1 = cx - bboxes[i * 4]! * stride\n const y1 = cy - bboxes[i * 4 + 1]! * stride\n const x2 = cx + bboxes[i * 4 + 2]! * stride\n const y2 = cy + bboxes[i * 4 + 3]! * stride\n\n // Transform from letterbox to original image\n const ox1 = Math.max(0, Math.min(origW, (x1 - padX) / scale))\n const oy1 = Math.max(0, Math.min(origH, (y1 - padY) / scale))\n const ox2 = Math.max(0, Math.min(origW, (x2 - padX) / scale))\n const oy2 = Math.max(0, Math.min(origH, (y2 - padY) / scale))\n\n if (ox2 - ox1 < 1 || oy2 - oy1 < 1) continue\n\n const det: typeof candidates[number] = {\n class: 'face',\n score: Math.round(score * 10000) / 10000,\n bbox: [Math.round(ox1 * 10) / 10, Math.round(oy1 * 10) / 10, Math.round(ox2 * 10) / 10, Math.round(oy2 * 10) / 10],\n _xyxy: [ox1, oy1, ox2, oy2],\n }\n\n // Landmarks (5 points × 2 coords)\n if (landmarks && (i * 10 + 9) < landmarks.length) {\n det.landmarks = []\n for (let p = 0; p < 5; p++) {\n const lx = (cx + landmarks[i * 10 + p * 2]! * stride - padX) / scale\n const ly = (cy + landmarks[i * 10 + p * 2 + 1]! * stride - padY) / scale\n det.landmarks.push({ x: Math.round(lx * 10) / 10, y: Math.round(ly * 10) / 10 })\n }\n }\n\n candidates.push(det)\n }\n }\n\n // NMS\n const kept = simpleNms(\n candidates.map((c) => ({ class: c.class, score: c.score, bbox: c.bbox })),\n 0.45,\n )\n\n return { kind: 'detections', detections: kept }\n}\n\n/** Match ONNX output tensors to SCRFD strides by expected anchor count. */\nfunction matchTensorsToStrides(\n tensors: Record<string, Float32Array>,\n strides: number[],\n inputSize: number,\n anchorsPerStride: number,\n): Map<number, { scores: Float32Array; bboxes: Float32Array; landmarks?: Float32Array }> {\n // Group tensors by element count per anchor: 1=scores, 4=bboxes, 10=landmarks\n const scoresByN = new Map<number, Float32Array>()\n const bboxesByN = new Map<number, Float32Array>()\n const landmarksByN = new Map<number, Float32Array>()\n\n for (const [, tensor] of Object.entries(tensors)) {\n for (const stride of strides) {\n const featSize = Math.ceil(inputSize / stride)\n const expectedN = featSize * featSize * anchorsPerStride\n if (tensor.length === expectedN) scoresByN.set(expectedN, tensor)\n else if (tensor.length === expectedN * 4) bboxesByN.set(expectedN, tensor)\n else if (tensor.length === expectedN * 10) landmarksByN.set(expectedN, tensor)\n }\n }\n\n const result = new Map<number, { scores: Float32Array; bboxes: Float32Array; landmarks?: Float32Array }>()\n for (const stride of strides) {\n const featSize = Math.ceil(inputSize / stride)\n const n = featSize * featSize * anchorsPerStride\n const scores = scoresByN.get(n)\n const bboxes = bboxesByN.get(n)\n if (scores && bboxes) {\n result.set(stride, { scores, bboxes, landmarks: landmarksByN.get(n) })\n }\n }\n return result\n}\n\nfunction postprocessArcface(output: EngineOutput, _stepDef: StepDefinition): StepOutput {\n const structured = tryStructured(output)\n if (structured) return structured\n\n const tensor = output.tensor\n if (!tensor) throw new Error('ArcFace postprocessor: no tensor in engine output')\n\n // L2 normalize\n let sumSq = 0\n for (let i = 0; i < tensor.length; i++) sumSq += tensor[i]! * tensor[i]!\n const norm = Math.sqrt(sumSq)\n\n const normalized = new Array<number>(tensor.length)\n for (let i = 0; i < tensor.length; i++) {\n normalized[i] = norm === 0 ? 0 : tensor[i]! / norm\n }\n\n return {\n kind: 'embedding',\n embedding: Array.from(tensor),\n embeddingNorm: normalized,\n }\n}\n\nfunction postprocessSoftmax(output: EngineOutput, stepDef: StepDefinition): StepOutput {\n const structured = tryStructured(output)\n if (structured) return structured\n\n const tensor = output.tensor\n if (!tensor) throw new Error('Softmax postprocessor: no tensor in engine output')\n\n const labels = stepDef.labels ?? []\n\n // Stable softmax\n let max = -Infinity\n for (let i = 0; i < tensor.length; i++) {\n if (tensor[i]! > max) max = tensor[i]!\n }\n const exps = new Float32Array(tensor.length)\n let sum = 0\n for (let i = 0; i < tensor.length; i++) {\n exps[i] = Math.exp(tensor[i]! - max)\n sum += exps[i]!\n }\n\n const classifications: Array<{ class: string; score: number }> = []\n for (let i = 0; i < exps.length; i++) {\n const score = exps[i]! / sum\n if (score >= 0.01) {\n classifications.push({ class: labels[i] ?? String(i), score })\n }\n }\n classifications.sort((a, b) => b.score - a.score)\n\n // Return top-5 — normalizeOutput() in dispatcher will trim to top-1 + alternates\n return {\n kind: 'classifications',\n classifications: classifications.slice(0, 5),\n }\n}\n\nfunction postprocessCtc(output: EngineOutput, stepDef: StepDefinition): StepOutput {\n const structured = tryStructured(output)\n if (structured) return structured\n\n const tensor = output.tensor\n if (!tensor) throw new Error('CTC postprocessor: no tensor in engine output')\n\n const charset = stepDef.charset ?? []\n // Assume shape [seqLen, numChars] flattened\n const numChars = charset.length || 97\n const seqLen = Math.floor(tensor.length / numChars)\n\n const chars: string[] = []\n let totalScore = 0\n let prev = -1\n\n for (let t = 0; t < seqLen; t++) {\n let bestIdx = 0\n let bestVal = tensor[t * numChars]!\n for (let c = 1; c < numChars; c++) {\n const val = tensor[t * numChars + c]!\n if (val > bestVal) { bestVal = val; bestIdx = c }\n }\n totalScore += bestVal\n if (bestIdx !== 0 && bestIdx !== prev) {\n chars.push(charset[bestIdx] ?? '')\n }\n prev = bestIdx\n }\n\n return {\n kind: 'text',\n text: chars.join(''),\n confidence: seqLen > 0 ? totalScore / seqLen : 0,\n }\n}\n\nfunction postprocessSaliency(output: EngineOutput, _stepDef: StepDefinition): StepOutput {\n const structured = tryStructured(output)\n if (structured) return structured\n\n const tensor = output.tensor\n if (!tensor) throw new Error('Saliency postprocessor: no tensor in engine output')\n\n // Sigmoid + threshold + base64\n const binary = new Uint8Array(tensor.length)\n for (let i = 0; i < tensor.length; i++) {\n const sigmoid = 1 / (1 + Math.exp(-tensor[i]!))\n binary[i] = sigmoid > 0.5 ? 255 : 0\n }\n\n // Assume square mask\n const side = Math.round(Math.sqrt(tensor.length))\n const mask = Buffer.from(binary).toString('base64')\n\n return { kind: 'mask', mask, maskWidth: side, maskHeight: side }\n}\n\nfunction postprocessYoloSeg(output: EngineOutput, stepDef: StepDefinition): StepOutput {\n // Python path: structured payload already contains full detections with masks\n const structured = tryStructured(output)\n if (structured) return structured\n\n // ONNX path: we need both detection tensor and prototype tensor\n const tensors = output.tensors\n if (!tensors) {\n throw new Error('YOLO-seg postprocessor: no tensors in engine output')\n }\n\n const labels = stepDef.labels ?? []\n const numClasses = labels.length || 80\n const numMaskCoeffs = 32\n const protoSize = 160\n // Phase 7: no internal score floor. instance-segmentation is used\n // only as a post-detection segmenter in this project — no user-facing\n // threshold. All decoded candidates flow through.\n const letterbox = output.letterbox\n\n // Find detection tensor and prototype tensor by size heuristics\n // Detection: [1, 300, 6+32] = flattened to [300 * 38] or stored as named output\n // Prototype: [1, 32, 160, 160] = flattened to [32 * 160 * 160 = 819200]\n let detTensor: Float32Array | undefined\n let protoTensor: Float32Array | undefined\n let detRows = 300 // default max_det for NMS-enabled export\n let detCols = 0\n\n for (const [, tensor] of Object.entries(tensors)) {\n // Prototype tensor: 32 * 160 * 160 = 819200\n if (tensor.length === numMaskCoeffs * protoSize * protoSize) {\n protoTensor = tensor\n continue\n }\n // Detection tensor: try to find N * (6 + 32) = N * 38\n // NMS-enabled: [x1, y1, x2, y2, conf, class_id, coeff_0..coeff_31] = 38\n const cols = 6 + numMaskCoeffs // 38\n if (tensor.length % cols === 0) {\n detTensor = tensor\n detRows = tensor.length / cols\n detCols = cols\n continue\n }\n // Raw (no NMS): [4 + numClasses + 32, numBoxes] — try this too\n const rawCols = 4 + numClasses + numMaskCoeffs\n if (tensor.length % rawCols === 0 && !detTensor) {\n detTensor = tensor\n detRows = tensor.length / rawCols\n detCols = rawCols\n }\n }\n\n if (!detTensor) throw new Error('YOLO-seg postprocessor: could not find detection tensor')\n if (!protoTensor) throw new Error('YOLO-seg postprocessor: could not find prototype tensor')\n\n const isNmsFormat = detCols === 6 + numMaskCoeffs\n\n const dets: Array<{\n class: string\n score: number\n bbox: [number, number, number, number]\n mask: string\n maskWidth: number\n maskHeight: number\n }> = []\n\n for (let i = 0; i < detRows; i++) {\n const offset = i * detCols\n\n let x1: number, y1: number, x2: number, y2: number\n let bestScore: number\n let bestClass: number\n let coeffStart: number\n\n if (isNmsFormat) {\n // NMS format: [x1, y1, x2, y2, confidence, class_id, coeff_0..coeff_31]\n x1 = detTensor[offset]!\n y1 = detTensor[offset + 1]!\n x2 = detTensor[offset + 2]!\n y2 = detTensor[offset + 3]!\n bestScore = detTensor[offset + 4]!\n bestClass = detTensor[offset + 5]!\n coeffStart = offset + 6\n\n if (bestScore <= 0) continue\n // A class_id of -1 means padding\n if (bestClass < 0) continue\n } else {\n // Raw format: [cx, cy, w, h, class_scores..., coeff_0..coeff_31]\n const cx = detTensor[offset]!\n const cy = detTensor[offset + 1]!\n const w = detTensor[offset + 2]!\n const h = detTensor[offset + 3]!\n\n bestScore = -Infinity\n bestClass = 0\n for (let j = 0; j < numClasses; j++) {\n const score = detTensor[offset + 4 + j]!\n if (score > bestScore) {\n bestScore = score\n bestClass = j\n }\n }\n if (bestScore <= 0) continue\n\n x1 = cx - w / 2\n y1 = cy - h / 2\n x2 = cx + w / 2\n y2 = cy + h / 2\n coeffStart = offset + 4 + numClasses\n }\n\n // Extract mask coefficients\n const coeffs = new Float32Array(numMaskCoeffs)\n for (let j = 0; j < numMaskCoeffs; j++) {\n coeffs[j] = detTensor[coeffStart + j]!\n }\n\n // Compute instance mask: sigmoid(coefficients @ prototypes)\n // protoTensor is [32, 160, 160] flattened\n const maskPixels = protoSize * protoSize\n const maskRaw = new Float32Array(maskPixels)\n for (let p = 0; p < maskPixels; p++) {\n let sum = 0\n for (let c = 0; c < numMaskCoeffs; c++) {\n sum += coeffs[c]! * protoTensor[c * maskPixels + p]!\n }\n maskRaw[p] = 1 / (1 + Math.exp(-sum)) // sigmoid\n }\n\n // Bbox in prototype-space (prototype is at model input resolution / 4)\n // Model input is typically 640, prototype is 160\n const protoScale = protoSize / (stepDef.models[0]?.inputSize.width ?? 640)\n const px1 = Math.max(0, Math.floor(x1 * protoScale))\n const py1 = Math.max(0, Math.floor(y1 * protoScale))\n const px2 = Math.min(protoSize, Math.ceil(x2 * protoScale))\n const py2 = Math.min(protoSize, Math.ceil(y2 * protoScale))\n const cropW = Math.max(1, px2 - px1)\n const cropH = Math.max(1, py2 - py1)\n\n // Crop and threshold mask to bbox region\n const cropped = new Uint8Array(cropW * cropH)\n for (let row = 0; row < cropH; row++) {\n for (let col = 0; col < cropW; col++) {\n const srcIdx = (py1 + row) * protoSize + (px1 + col)\n cropped[row * cropW + col] = maskRaw[srcIdx]! > 0.5 ? 255 : 0\n }\n }\n\n // Transform bbox from model coords to original image coords\n let ox1 = x1\n let oy1 = y1\n let ox2 = x2\n let oy2 = y2\n if (letterbox) {\n ox1 = (x1 - letterbox.padX) / letterbox.scale\n oy1 = (y1 - letterbox.padY) / letterbox.scale\n ox2 = (x2 - letterbox.padX) / letterbox.scale\n oy2 = (y2 - letterbox.padY) / letterbox.scale\n }\n\n const label = labels[bestClass] ?? String(bestClass)\n const maskB64 = Buffer.from(cropped).toString('base64')\n\n dets.push({\n class: label,\n score: Math.round(bestScore * 10000) / 10000,\n bbox: [\n Math.round(ox1 * 10) / 10,\n Math.round(oy1 * 10) / 10,\n Math.round(ox2 * 10) / 10,\n Math.round(oy2 * 10) / 10,\n ],\n mask: maskB64,\n maskWidth: cropW,\n maskHeight: cropH,\n })\n }\n\n // NMS on the detections (NMS-enabled exports may still benefit from a light pass)\n const keptBoxes = simpleNms(\n dets.map((d) => ({ class: d.class, score: d.score, bbox: d.bbox })),\n 0.45,\n )\n const keptSet = new Set(keptBoxes.map((d) => `${d.bbox[0]}_${d.bbox[1]}_${d.bbox[2]}_${d.bbox[3]}_${d.score}`))\n const kept = dets.filter(\n (d) => keptSet.has(`${d.bbox[0]}_${d.bbox[1]}_${d.bbox[2]}_${d.bbox[3]}_${d.score}`),\n )\n\n return { kind: 'detections', detections: kept }\n}\n\nfunction postprocessYamnet(output: EngineOutput, _stepDef: StepDefinition): StepOutput {\n const structured = tryStructured(output)\n if (structured) return structured\n throw new Error('YAMNet TypeScript postprocessing not implemented. Use Python backend or Apple SoundAnalysis.')\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch table\n// ---------------------------------------------------------------------------\n\nconst DISPATCH: Record<PostprocessorType, PostprocessFn> = {\n 'yolo': postprocessYolo,\n 'yolo-seg': postprocessYoloSeg,\n 'scrfd': postprocessScrfd,\n 'arcface': postprocessArcface,\n 'softmax': postprocessSoftmax,\n 'ctc': postprocessCtc,\n 'saliency': postprocessSaliency,\n 'yamnet': postprocessYamnet,\n}\n\n/**\n * Dispatch postprocessing based on step definition's postprocessor type.\n *\n * For Python backends: output.structured is already populated → passthrough.\n * For Node.js ONNX: raw tensors → postprocess in TypeScript.\n *\n * Both paths pass through normalizeOutput() to ensure consistent format\n * (e.g., classification top-K, alternates).\n */\nexport function dispatchPostprocess(output: EngineOutput, stepDef: StepDefinition): StepOutput {\n const fn = DISPATCH[stepDef.postprocessor]\n if (!fn) {\n throw new Error(`Unknown postprocessor type: \"${stepDef.postprocessor}\"`)\n }\n return normalizeOutput(fn(output, stepDef))\n}\n\n/**\n * Normalize any StepOutput to a consistent format.\n * Single funnel for both Python and TypeScript postprocessor paths.\n *\n * Classifications: keep only top-1 in classifications, move full list to alternates.\n */\nfunction normalizeOutput(output: StepOutput): StepOutput {\n if (output.kind !== 'classifications') return output\n\n const all = output.classifications\n if (all.length <= 1) return output\n\n return {\n kind: 'classifications',\n classifications: all.slice(0, 1),\n alternates: all,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Simple NMS (shared utility)\n// ---------------------------------------------------------------------------\n\nfunction simpleNms(\n dets: Array<{ class: string; score: number; bbox: [number, number, number, number] }>,\n iouThreshold: number,\n): typeof dets {\n if (dets.length === 0) return []\n const sorted = [...dets].sort((a, b) => b.score - a.score)\n const kept: typeof dets = []\n const suppressed = new Set<number>()\n\n for (let i = 0; i < sorted.length; i++) {\n if (suppressed.has(i)) continue\n kept.push(sorted[i]!)\n for (let j = i + 1; j < sorted.length; j++) {\n if (suppressed.has(j)) continue\n if (iou(sorted[i]!.bbox, sorted[j]!.bbox) > iouThreshold) {\n suppressed.add(j)\n }\n }\n }\n return kept\n}\n\nfunction iou(a: [number, number, number, number], b: [number, number, number, number]): number {\n const ix1 = Math.max(a[0], b[0])\n const iy1 = Math.max(a[1], b[1])\n const ix2 = Math.min(a[2], b[2])\n const iy2 = Math.min(a[3], b[3])\n const inter = Math.max(0, ix2 - ix1) * Math.max(0, iy2 - iy1)\n if (inter === 0) return 0\n const areaA = (a[2] - a[0]) * (a[3] - a[1])\n const areaB = (b[2] - b[0]) * (b[3] - b[1])\n return inter / (areaA + areaB - inter)\n}\n","/**\n * Crop utilities — ROI extraction and bbox coordinate transforms.\n */\nimport sharp from 'sharp'\n\n/** Bounding box in absolute image coordinates [x1, y1, x2, y2]. */\nexport type AbsoluteBbox = readonly [number, number, number, number]\n\n/**\n * Extract a JPEG crop from a full-frame JPEG buffer.\n * Clamps to image boundaries and returns the cropped JPEG + dimensions.\n */\nexport async function cropJpeg(\n jpeg: Buffer,\n bbox: AbsoluteBbox,\n imageWidth: number,\n imageHeight: number,\n): Promise<{ jpeg: Buffer; width: number; height: number }> {\n const x1 = Math.max(0, Math.round(bbox[0]))\n const y1 = Math.max(0, Math.round(bbox[1]))\n const x2 = Math.min(imageWidth, Math.round(bbox[2]))\n const y2 = Math.min(imageHeight, Math.round(bbox[3]))\n const w = x2 - x1\n const h = y2 - y1\n\n if (w < 1 || h < 1) {\n throw new Error(`Degenerate crop: [${x1},${y1},${x2},${y2}] on ${imageWidth}x${imageHeight}`)\n }\n\n const cropped = await sharp(jpeg)\n .extract({ left: x1, top: y1, width: w, height: h })\n .jpeg({ quality: 90 })\n .toBuffer()\n\n return { jpeg: cropped, width: w, height: h }\n}\n\n/**\n * Transform a child detection's bbox from ROI-relative to image-absolute coordinates.\n *\n * When a child step (e.g., face-detection) runs on a crop of the parent (e.g., person),\n * its bbox is relative to the crop. This function maps it back to the full image space.\n */\nexport function transformBboxToImageSpace(\n childBbox: AbsoluteBbox,\n parentBbox: AbsoluteBbox,\n): AbsoluteBbox {\n const [px1, py1] = parentBbox\n\n return [\n px1 + childBbox[0],\n py1 + childBbox[1],\n px1 + childBbox[2],\n py1 + childBbox[3],\n ]\n}\n\n/**\n * Clamp a bbox to image boundaries.\n */\nexport function clampBbox(\n bbox: AbsoluteBbox,\n imageWidth: number,\n imageHeight: number,\n): AbsoluteBbox {\n return [\n Math.max(0, Math.min(imageWidth, bbox[0])),\n Math.max(0, Math.min(imageHeight, bbox[1])),\n Math.max(0, Math.min(imageWidth, bbox[2])),\n Math.max(0, Math.min(imageHeight, bbox[3])),\n ]\n}\n\n/**\n * Check if a bbox is degenerate (zero or negative area).\n */\nexport function isBboxDegenerate(bbox: AbsoluteBbox): boolean {\n return bbox[2] - bbox[0] < 1 || bbox[3] - bbox[1] < 1\n}\n","/**\n * ResultAssembler — converts per-step StepOutputs into the canonical\n * `FrameResult` shape consumed by every downstream surface (benchmark UI,\n * live view, analysis pipeline, notifications, event bus).\n *\n * Rules enforced here — see `@camstack/types/types/detection.ts` for the\n * full data model spec:\n *\n * 1. The executor walks the tree and hands each \"root\" output through\n * `toMutableObjectDetection` to build a `MutableObjectDetection`.\n * 2. Child step outputs are routed via `applyChildOutput`:\n * - classifier/embedding/text outputs add `ScoredLabel`s to the\n * current detection's `labels[]` (own enrichments);\n * - cropper outputs create NEW detail `MutableObjectDetection`\n * entries that reference their parent via `parentId`;\n * - refiner/mask outputs attach mask data to the parent's debug.\n * 3. After the whole tree is walked, `buildFrameResult` flattens:\n * - first-level detections at root,\n * - detail detections at root (with their `parentId` set),\n * and inherits every detail's labels onto its parent (each carrying\n * `detectionId = <detail id>`), so consumers can render a person's\n * name directly from the root person entry.\n * 4. The assembler owns detection-id generation and the `originalClass`\n * → macroClass mapping that the wiring service and postprocessor\n * previously duplicated.\n *\n * The mutable intermediate keeps per-step latency / modelId maps so that\n * the final detection can surface them under `debug.stepLatencyMs` /\n * `debug.modelIds` when detection debug is enabled.\n */\nimport type {\n FrameResult,\n FrameDebug,\n FrameStepTiming,\n ObjectDetection,\n ScoredLabel,\n DetectionDebug,\n MacroClass,\n StepOutput,\n} from '@camstack/types'\nimport type { AbsoluteBbox } from './crop-utils.js'\nimport type { ExecutableStep } from './tree-builder.js'\n\n// ---------------------------------------------------------------------------\n// Mutable intermediate shapes (used only inside the executor; never leak)\n// ---------------------------------------------------------------------------\n\n/**\n * Mutable per-detection accumulator. One of these is created for every\n * root-level detection returned by a detector step, and for every detail\n * detection (face / plate) spawned by a cropper child. The executor\n * feeds per-step outputs into it via `applyChildOutput`, and the\n * assembler eventually converts them into immutable `ObjectDetection`s.\n */\nexport interface MutableObjectDetection {\n /** Unique id within the frame — `d1`, `d2`, … assigned sequentially by the executor. */\n id: string\n kind: 'first-level' | 'detail'\n /** Macro class carried at root (after classMap application). */\n macroClass: MacroClass\n /** Raw detector class before any macro mapping — populated on debug. */\n originalClass?: string\n /** Score of the macro class itself (from the detector that emitted this). */\n score: number\n /** Absolute pixel coordinates [x1, y1, x2, y2] in the source frame. */\n bbox: AbsoluteBbox\n /** Id of the first-level parent when this is a detail entry. */\n parentId?: string\n /** Own enrichment labels produced by classifier / recognizer / OCR steps. */\n ownLabels: ScoredLabel[]\n /** Top-N ranked candidates per source (classifier addon id → candidates). */\n alternateLabels: Record<string, readonly ScoredLabel[]>\n /** Per-step latency contributions on THIS detection. */\n stepLatencyMs: Record<string, number>\n /** Per-step model ids that touched THIS detection. */\n modelIds: Record<string, string>\n /** Extract mode of the detection's producing step (for debug). */\n extractMode?: 'full-frame' | 'crop-roi'\n /** Base64 binary segmentation mask attached by a seg/refiner step. */\n mask?: string\n maskWidth?: number\n maskHeight?: number\n /** Embedding id stored by a recognition step, when any. */\n embeddingId?: string\n}\n\n// ---------------------------------------------------------------------------\n// ID allocation\n// ---------------------------------------------------------------------------\n\n/**\n * Small deterministic id generator scoped to a single frame. Each call\n * returns the next `d{n}` id so detail parentId references can be set\n * at the time of creation.\n */\nexport class FrameIdGenerator {\n private counter = 0\n next(): string {\n this.counter += 1\n return `d${this.counter}`\n }\n}\n\n// ---------------------------------------------------------------------------\n// Apply child step output to a parent detection\n// ---------------------------------------------------------------------------\n\n/**\n * Per-frame context passed to `applyChildOutput`:\n * - `idGen` — used to allocate ids for new detail entries\n * - `details` — bucket where NEW detail `MutableObjectDetection`s are\n * pushed (cropper outputs). They'll be flattened at root\n * level by `buildFrameResult` after the walk finishes.\n * - `parentStepLatencyMs` — partial latency map the executor fills\n * from `stepTimings` (so child enrichers can\n * add their ms here for the parent).\n */\nexport interface AssemblyContext {\n readonly idGen: FrameIdGenerator\n readonly details: MutableObjectDetection[]\n}\n\n/**\n * Apply a child step's output to its parent detection. Uses\n * `switch(output.kind)` for type-safe routing.\n *\n * - detections + refiner slot → mask attached to parent.debug\n * - detections + cropper slot → spawn detail MutableObjectDetection\n * as a sibling at root level\n * - classifications → push ScoredLabels to parent.ownLabels,\n * store alternates under addon id\n * - embedding → push an opaque \"embedding\" label\n * - text → push plate-OCR text as a ScoredLabel\n * - mask → attach mask to parent.debug\n */\nexport function applyChildOutput(\n parent: MutableObjectDetection,\n childStep: ExecutableStep,\n output: StepOutput,\n stepLatencyMs: number,\n ctx: AssemblyContext,\n): void {\n // Record per-detection step latency + modelId for debug panels.\n parent.stepLatencyMs[childStep.stepId] = stepLatencyMs\n parent.modelIds[childStep.stepId] = childStep.modelId\n\n switch (output.kind) {\n case 'detections': {\n // Refiner slot with mask data → attach best mask to parent.\n // Instance-segmentation models (YOLO-seg) output kind:'detections'\n // with per-instance masks. When running as a refiner child step,\n // pick the highest-confidence mask whose class maps to the parent's\n // macro class.\n if (childStep.definition.slot === 'refiner') {\n const classMap = childStep.definition.classMap?.mapping\n const parentMacro = parent.macroClass\n\n const candidates = output.detections.filter((d) => {\n if (d.mask === undefined) return false\n if (classMap) {\n const mapped = classMap[d.class]\n return mapped === parentMacro\n }\n return true\n })\n\n const best = candidates.sort((a, b) => b.score - a.score)[0]\n if (best?.mask !== undefined) {\n parent.mask = best.mask\n parent.maskWidth = best.maskWidth\n parent.maskHeight = best.maskHeight\n }\n break\n }\n\n // Cropper: each detection becomes a NEW detail entry at root\n // level, referencing `parent.id` as its parentId.\n for (const det of output.detections) {\n const detail: MutableObjectDetection = {\n id: ctx.idGen.next(),\n kind: 'detail',\n macroClass: mapCropperClassToDetailMacro(childStep.stepId, det.class),\n originalClass: det.class !== mapCropperClassToDetailMacro(childStep.stepId, det.class) ? det.class : undefined,\n score: det.score,\n bbox: det.bbox,\n parentId: parent.id,\n ownLabels: [],\n alternateLabels: {},\n stepLatencyMs: { [childStep.stepId]: stepLatencyMs },\n modelIds: { [childStep.stepId]: childStep.modelId },\n extractMode: 'crop-roi',\n ...(det.mask !== undefined\n ? { mask: det.mask, maskWidth: det.maskWidth, maskHeight: det.maskHeight }\n : {}),\n }\n ctx.details.push(detail)\n }\n break\n }\n\n case 'classifications': {\n // Classifier / recognizer: attach all top-N candidates as own labels,\n // store the same set under alternateLabels[stepId] for debug compat.\n //\n // `minConfidence` is intentionally NOT applied here — threshold must\n // never gate label visibility. The operator reads the score on each\n // ScoredLabel; downstream consumers (alerts, tracking) apply their\n // own threshold. Raising the slider must never blank the candidates.\n //\n // `enabledClasses` (multiselect, optional): class allow-list — when\n // set to a non-empty array, keep only predictions whose class is in\n // the list. Empty / missing = all classes allowed.\n const rawEnabled = childStep.settings?.['enabledClasses']\n const enabledSet: ReadonlySet<string> | null =\n Array.isArray(rawEnabled) && rawEnabled.length > 0\n ? new Set(rawEnabled.filter((v): v is string => typeof v === 'string'))\n : null\n\n const passesClass = (c: { class: string; score: number }): boolean =>\n enabledSet === null || enabledSet.has(c.class)\n\n // ownLabels: top-1 winner, gated by minConfidence threshold.\n // If the winner's score is below the threshold, no label is emitted\n // (the detection is \"uncertain\" — downstream consumers treat it as\n // unclassified). enabledClasses class allow-list also applies.\n // `output.classifications` is always top-1 after normalizeOutput.\n const minConfidence = typeof childStep.settings?.['minConfidence'] === 'number'\n ? childStep.settings['minConfidence'] as number\n : 0\n const winner = output.classifications[0]\n if (winner && passesClass(winner) && winner.score >= minConfidence) {\n parent.ownLabels.push({ label: winner.class, score: winner.score })\n }\n\n // alternateLabels: full top-N list — never gated by minConfidence so\n // the UI can always show all candidates and their scores even when the\n // threshold filters the winner out of labels[].\n const topN = output.alternates && output.alternates.length > 0\n ? output.alternates\n : output.classifications\n const allAlternates: ScoredLabel[] = topN\n .filter(passesClass)\n .map((cls) => ({ label: cls.class, score: cls.score }))\n if (allAlternates.length > 0) {\n parent.alternateLabels[childStep.stepId] = allAlternates\n }\n break\n }\n\n case 'embedding': {\n parent.ownLabels.push({\n label: `embedding:${output.embedding.length}d`,\n score: 1.0,\n })\n break\n }\n\n case 'text': {\n parent.ownLabels.push({\n label: output.text,\n score: output.confidence,\n })\n break\n }\n\n case 'mask': {\n parent.mask = output.mask\n parent.maskWidth = output.maskWidth\n parent.maskHeight = output.maskHeight\n break\n }\n }\n}\n\n/**\n * Map a cropper step's raw output class to a detail macro class.\n * e.g. `face-detection` → `'face'`, `plate-detection` → `'plate'`.\n * Falls back to the raw class if the step id doesn't match a known\n * detail detector.\n */\nfunction mapCropperClassToDetailMacro(stepId: string, rawClass: string): MacroClass {\n if (stepId.includes('face')) return 'face'\n if (stepId.includes('plate')) return 'plate'\n return rawClass\n}\n\n// ---------------------------------------------------------------------------\n// Build the final FrameResult\n// ---------------------------------------------------------------------------\n\nexport interface BuildFrameResultInput {\n readonly deviceId: number\n readonly width: number\n readonly height: number\n /** First-level detections walked from root detector outputs. */\n readonly firstLevel: readonly MutableObjectDetection[]\n /** Detail detections spawned by cropper outputs (face / plate). */\n readonly details: readonly MutableObjectDetection[]\n readonly stepTimings: readonly FrameStepTiming[]\n readonly totalMs: number\n readonly engine: string\n readonly pipelineId?: string\n /** When true, populates the `debug` field on both frame and detection level. */\n readonly debug: boolean\n /** Aggregated python-pool stage timings across all steps. Surfaces preprocess vs predict split for benchmarking. */\n readonly poolTimings?: {\n readonly preprocessMs: number\n readonly predictMs: number\n readonly batchSize: number\n }\n /** Wall-clock timestamp of frame capture / emission. */\n readonly timestamp: number\n /** Optional frame id override — the assembler generates a UUID-ish default if omitted. */\n readonly frameId?: string\n}\n\nexport function buildFrameResult(input: BuildFrameResultInput): FrameResult {\n // Index details by parentId so we can inherit their labels onto the\n // first-level entries before emitting.\n const detailsByParent = new Map<string, MutableObjectDetection[]>()\n for (const detail of input.details) {\n if (!detail.parentId) continue\n const arr = detailsByParent.get(detail.parentId) ?? []\n arr.push(detail)\n detailsByParent.set(detail.parentId, arr)\n }\n\n const toImmutableObject = (m: MutableObjectDetection): ObjectDetection => {\n // Compose labels: own enrichments first, then inherited from details.\n const labels: ScoredLabel[] = [...m.ownLabels]\n const children = detailsByParent.get(m.id) ?? []\n for (const child of children) {\n for (const childLabel of child.ownLabels) {\n labels.push({\n label: childLabel.label,\n score: childLabel.score,\n detectionId: child.id,\n })\n }\n }\n\n const bbox = bboxTupleToRect(m.bbox)\n\n const base: ObjectDetection = {\n id: m.id,\n kind: m.kind,\n macroClass: m.macroClass,\n score: m.score,\n bbox,\n labels,\n ...(m.parentId !== undefined ? { parentId: m.parentId } : {}),\n }\n\n if (input.debug) {\n const debug: DetectionDebug = {\n stepLatencyMs: Object.keys(m.stepLatencyMs).length > 0 ? { ...m.stepLatencyMs } : undefined,\n modelIds: Object.keys(m.modelIds).length > 0 ? { ...m.modelIds } : undefined,\n extractMode: m.extractMode,\n originalClass: m.originalClass,\n alternateLabels: Object.keys(m.alternateLabels).length > 0 ? { ...m.alternateLabels } : undefined,\n mask: m.mask,\n maskWidth: m.maskWidth,\n maskHeight: m.maskHeight,\n embeddingId: m.embeddingId,\n }\n // Strip undefined keys so the payload stays clean when the section\n // has nothing meaningful to report. We project into a Record first\n // because `DetectionDebug` is a strict interface with no index\n // signature, so `pruneUndefined<DetectionDebug>` can't type-check.\n const asRecord: Record<string, unknown> = { ...debug }\n const cleanDebug = pruneUndefined(asRecord) as DetectionDebug\n if (Object.keys(cleanDebug).length > 0) {\n return { ...base, debug: cleanDebug }\n }\n }\n return base\n }\n\n const detections: ObjectDetection[] = []\n for (const d of input.firstLevel) detections.push(toImmutableObject(d))\n for (const d of input.details) detections.push(toImmutableObject(d))\n\n // Frame-level debug (totalInferenceMs, stepTimings, engine, pipelineId)\n // is always populated because the metrics it carries are cheap and\n // downstream consumers (benchmark UI, metrics dashboard, e2e tests,\n // runner timing sampler) rely on them on every frame. The `input.debug`\n // flag above still gates per-detection verbose fields (alternateLabels,\n // mask, stepLatencyMs, …) which ARE expensive and should only be\n // emitted in full-verbosity trace mode.\n const frameDebug: FrameDebug = {\n totalInferenceMs: input.totalMs,\n stepTimings: input.stepTimings,\n engine: input.engine,\n pipelineId: input.pipelineId,\n ...(input.poolTimings ? {\n preprocessMs: input.poolTimings.preprocessMs,\n predictMs: input.poolTimings.predictMs,\n batchSize: input.poolTimings.batchSize,\n } : {}),\n }\n\n return {\n kind: 'frame',\n frameId: input.frameId ?? generateFrameId(),\n deviceId: input.deviceId,\n timestamp: input.timestamp,\n width: input.width,\n height: input.height,\n detections,\n debug: frameDebug,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction bboxTupleToRect(\n bbox: AbsoluteBbox,\n): { readonly x: number; readonly y: number; readonly width: number; readonly height: number } {\n const [x1, y1, x2, y2] = bbox\n return { x: x1, y: y1, width: x2 - x1, height: y2 - y1 }\n}\n\nfunction pruneUndefined<T extends Record<string, unknown>>(obj: T): Partial<T> {\n const out: Partial<T> = {}\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) {\n ;(out as Record<string, unknown>)[k] = v\n }\n }\n return out\n}\n\nfunction generateFrameId(): string {\n return `frame-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/** Seed a MutableObjectDetection from a root-level detector output. */\nexport function toMutableRootDetection(\n det: {\n readonly class: string\n readonly score: number\n readonly bbox: readonly [number, number, number, number]\n readonly mask?: string\n readonly maskWidth?: number\n readonly maskHeight?: number\n },\n rootStep: ExecutableStep,\n idGen: FrameIdGenerator,\n rootStepLatencyMs: number,\n): MutableObjectDetection {\n return {\n id: idGen.next(),\n kind: 'first-level',\n macroClass: det.class,\n originalClass: det.class,\n score: det.score,\n bbox: [...det.bbox] as AbsoluteBbox,\n ownLabels: [],\n alternateLabels: {},\n stepLatencyMs: { [rootStep.stepId]: rootStepLatencyMs },\n modelIds: { [rootStep.stepId]: rootStep.modelId },\n extractMode: 'full-frame',\n ...(det.mask !== undefined\n ? { mask: det.mask, maskWidth: det.maskWidth, maskHeight: det.maskHeight }\n : {}),\n }\n}\n","/**\n * Execution trace builder — creates StepTrace records for observability.\n *\n * Three verbosity levels:\n * - 'off': no trace created (zero overhead)\n * - 'summary': timing only (minimal overhead)\n * - 'full': timing + input/output metadata (for benchmark/test)\n */\nimport type {\n TraceVerbosity,\n StepTrace,\n PipelineExecutionTrace,\n StepOutput,\n PipelineSlot,\n PostprocessorType,\n} from '@camstack/types'\n\nlet traceCounter = 0\n\nexport class ExecutionTraceBuilder {\n private readonly steps: StepTrace[] = []\n private readonly startTime: number\n\n constructor(\n private readonly verbosity: TraceVerbosity,\n private readonly deviceId: number,\n private readonly frameWidth: number,\n private readonly frameHeight: number,\n private readonly engineRuntime: string,\n ) {\n this.startTime = Date.now()\n }\n\n /** Whether trace collection is active. */\n get isActive(): boolean {\n return this.verbosity !== 'off'\n }\n\n /**\n * Record a completed step execution.\n * No-op if verbosity is 'off'.\n */\n addStep(params: {\n stepId: string\n modelId: string\n slot: PipelineSlot\n postprocessor: PostprocessorType\n preprocessMs: number\n inferenceMs: number\n postprocessMs: number\n inputType: 'full-frame' | 'crop-roi'\n inputWidth: number\n inputHeight: number\n parentDetection?: string\n output: StepOutput\n error?: string\n }): void {\n if (this.verbosity === 'off') return\n\n const totalMs = params.preprocessMs + params.inferenceMs + params.postprocessMs\n\n const trace: StepTrace = {\n stepId: params.stepId,\n modelId: params.modelId,\n slot: params.slot,\n postprocessor: params.postprocessor,\n preprocessMs: Math.round(params.preprocessMs * 100) / 100,\n inferenceMs: Math.round(params.inferenceMs * 100) / 100,\n postprocessMs: Math.round(params.postprocessMs * 100) / 100,\n totalMs: Math.round(totalMs * 100) / 100,\n inputType: params.inputType,\n inputSize: { width: params.inputWidth, height: params.inputHeight },\n parentDetection: this.verbosity === 'full' ? params.parentDetection : undefined,\n outputKind: params.output.kind,\n outputCount: countOutput(params.output),\n topResult: this.verbosity === 'full' ? formatTopResult(params.output) : undefined,\n error: params.error,\n }\n\n this.steps.push(trace)\n }\n\n /**\n * Finalize and return the complete execution trace.\n * Returns null if verbosity is 'off'.\n */\n build(detectionCount: number): PipelineExecutionTrace | null {\n if (this.verbosity === 'off') return null\n\n const totalMs = Date.now() - this.startTime\n\n return {\n traceId: `trace-${Date.now()}-${++traceCounter}`,\n deviceId: this.deviceId,\n timestamp: this.startTime,\n frameSize: { width: this.frameWidth, height: this.frameHeight },\n engineRuntime: this.engineRuntime,\n steps: this.steps,\n totalMs,\n detectionCount,\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction countOutput(output: StepOutput): number {\n switch (output.kind) {\n case 'detections':\n return output.detections.length\n case 'classifications':\n return output.classifications.length\n case 'embedding':\n return 1\n case 'text':\n return output.text.length > 0 ? 1 : 0\n case 'mask':\n return 1\n }\n}\n\nfunction formatTopResult(output: StepOutput): string | undefined {\n switch (output.kind) {\n case 'detections': {\n const top = output.detections[0]\n return top ? `${top.class} ${top.score.toFixed(2)}` : undefined\n }\n case 'classifications': {\n const top = output.classifications[0]\n return top ? `${top.class} ${top.score.toFixed(2)}` : undefined\n }\n case 'embedding':\n return `embedding[${output.embedding.length}]`\n case 'text':\n return output.text || undefined\n case 'mask':\n return `mask ${output.maskWidth}×${output.maskHeight}`\n }\n}\n","/**\n * PipelineExecutor — recursive tree execution on a single frame.\n *\n * Walks the ExecutableTree, runs each step against the engine, and feeds\n * the outputs into the `ResultAssembler` which produces the canonical\n * `FrameResult` shape consumed by every downstream surface.\n *\n * Responsibilities:\n * 1. Run root steps on the full frame\n * 2. For each detection, match children by classFilter\n * 3. Crop the ROI and run the child step\n * 4. Recurse into grandchildren\n * 5. Hand outputs to `applyChildOutput`, which routes them into the\n * current detection's labels / alternateLabels / mask fields, OR\n * spawns new detail entries (face / plate) when the child is a\n * cropper.\n * 6. Call `buildFrameResult` at the end to produce the final\n * immutable `FrameResult` with flattened first-level + detail\n * entries.\n */\nimport type {\n FrameResult,\n FrameStepTiming,\n PipelineRunOptions,\n PipelineExecutionTrace,\n StepOutput,\n InferenceInput,\n IScopedLogger,\n} from '@camstack/types'\nimport type { ExecutableStep, ExecutableTree } from './tree-builder.js'\nimport { dispatchPostprocess } from '../postprocess/dispatch.js'\nimport { transformBboxToImageSpace, isBboxDegenerate, cropJpeg, type AbsoluteBbox } from './crop-utils.js'\nimport {\n type MutableObjectDetection,\n FrameIdGenerator,\n applyChildOutput,\n buildFrameResult,\n toMutableRootDetection,\n type AssemblyContext,\n} from './result-assembler.js'\nimport { ExecutionTraceBuilder } from './execution-trace.js'\nimport { errMsg } from '@camstack/types'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface PipelineExecutorOptions {\n /** Engine runtime string for trace metadata (e.g. `\"python+coreml\"`). */\n readonly engineRuntime: string\n /**\n * Optional logger — used to surface child-step failures. When unset,\n * child failures are swallowed to keep parent detections in the result\n * (legacy behaviour); when set, each failure is logged at warn so the\n * pipeline isn't silently degraded.\n */\n readonly logger?: IScopedLogger\n}\n\n/**\n * Accumulator for python-pool per-stage wall timings — populated as each\n * `executeStep` reads the engine output. Surfaces preprocess vs predict\n * split, plus the actual batch size used by the window accumulator.\n */\ninterface PoolStageAccumulator {\n preprocessMs: number\n predictMs: number\n batchSize: number\n}\n\n// ---------------------------------------------------------------------------\n// PipelineExecutor\n// ---------------------------------------------------------------------------\n\nexport class PipelineExecutor {\n constructor(private readonly opts: PipelineExecutorOptions) {}\n\n /**\n * Execute the full pipeline tree against a frame.\n *\n * `rootInput` carries the frame in its source representation\n * (jpeg | raw). The root step receives this as-is. Child steps that\n * crop a parent detection always need a JPEG buffer (cropJpeg is\n * JPEG-only); when the root frame is raw, `fullFrameJpegProvider`\n * is called lazily on the first crop and its result is cached. Trees\n * without crop children pay zero JPEG encode cost on the raw fast\n * path.\n *\n * @returns FrameResult + optional trace\n */\n async run(\n tree: ExecutableTree,\n rootInput: InferenceInput,\n fullFrameJpegProvider: () => Promise<Buffer>,\n imageWidth: number,\n imageHeight: number,\n deviceId: number,\n runOpts?: PipelineRunOptions,\n ): Promise<{ result: FrameResult; trace: PipelineExecutionTrace | null }> {\n const startMs = Date.now()\n const verbosity = runOpts?.traceVerbosity ?? 'off'\n\n const traceBuilder = new ExecutionTraceBuilder(\n verbosity,\n deviceId,\n imageWidth,\n imageHeight,\n this.opts.engineRuntime,\n )\n\n const firstLevel: MutableObjectDetection[] = []\n const details: MutableObjectDetection[] = []\n const stepTimings: FrameStepTiming[] = []\n // Aggregator for python-pool stage timings — preprocess + predict split.\n // Surfaced via FrameDebug.preprocessMs/predictMs/batchSize for benchmark UI.\n const poolAgg: PoolStageAccumulator = { preprocessMs: 0, predictMs: 0, batchSize: 1 }\n const debug = verbosity === 'full'\n\n const idGen = new FrameIdGenerator()\n const ctx: AssemblyContext = { idGen, details }\n\n // Execute each root step\n for (const rootStep of tree.roots) {\n // Phase 7 (settings redesign): video pipeline has `object-detection`\n // as its only root. No generic score filter is applied at root —\n // `matchesMacroFilter` below reads the per-macro thresholds from\n // `rootStep.settings` (`minConfidencePerson/Vehicle/Animal`) and\n // enforces them per-detection. Any future root step that also\n // needs a threshold declares its own `minConfidence` field in\n // `getConfigSchema()` and adds an equivalent filter at this site.\n if (debug) console.log(`[executor] rootStep=${rootStep.stepId} settings=${JSON.stringify(rootStep.settings ?? {})}`)\n const rootStart = Date.now()\n const rootOutput = await this.executeStep(\n rootStep,\n rootInput,\n imageWidth,\n imageHeight,\n 'full-frame',\n undefined, // no parent bbox\n undefined, // no parent class\n traceBuilder,\n stepTimings,\n poolAgg,\n )\n const rootMs = Date.now() - rootStart\n\n if (rootOutput.kind !== 'detections') {\n continue // Root steps should be detectors\n }\n\n // Process each root detection through children\n for (const det of rootOutput.detections) {\n // Phase 7: no generic score filter here. `matchesMacroFilter`\n // below applies the per-macro thresholds from settings for\n // object-detection. Other root steps would do their own filter\n // in a similar way — but today object-detection is the only\n // video root.\n\n const mutable = toMutableRootDetection(det, rootStep, idGen, rootMs)\n\n // Apply classMap for child routing (always) and for the macroClass\n // carried at root. The new model drops the macro-vs-COCO split;\n // macro is the only display mode.\n if (rootStep.definition.classMap) {\n const mapped = rootStep.definition.classMap.mapping[det.class]\n if (mapped) {\n mutable.originalClass = det.class\n mutable.macroClass = mapped\n } else if (!rootStep.definition.classMap.preserveOriginal) {\n continue // Unmapped class, skip entirely\n }\n }\n\n // Apply per-step macro class filter from settings.enabledMacroClasses\n // + per-macro minConfidence sliders (Phase 6 step rework).\n if (!this.matchesMacroFilter(mutable.macroClass, mutable.score, rootStep.settings)) {\n if (debug) console.log(`[executor] drop det macro=${mutable.macroClass} score=${mutable.score} (class or confidence filter)`)\n continue\n }\n\n // Execute children for this detection. Child failures are\n // logged but do not drop the parent detection from the frame —\n // partial enrichment is preferable to losing the root entirely.\n try {\n await this.executeChildren(\n rootStep.children,\n mutable,\n fullFrameJpegProvider,\n imageWidth,\n imageHeight,\n traceBuilder,\n stepTimings,\n ctx,\n poolAgg,\n )\n } catch (err) {\n this.opts.logger?.warn('Pipeline child execution failed — keeping parent detection', {\n meta: {\n rootStepId: rootStep.stepId,\n parentClass: mutable.macroClass,\n parentScore: mutable.score,\n error: errMsg(err),\n },\n })\n }\n\n firstLevel.push(mutable)\n }\n }\n\n const totalMs = Date.now() - startMs\n\n const result = buildFrameResult({\n deviceId,\n width: imageWidth,\n height: imageHeight,\n firstLevel,\n details,\n stepTimings,\n totalMs,\n engine: this.opts.engineRuntime,\n debug,\n timestamp: Date.now(),\n poolTimings: {\n preprocessMs: Math.round(poolAgg.preprocessMs * 100) / 100,\n predictMs: Math.round(poolAgg.predictMs * 100) / 100,\n batchSize: poolAgg.batchSize,\n },\n })\n\n const trace = traceBuilder.build(firstLevel.length + details.length)\n\n return { result, trace }\n }\n\n // -------------------------------------------------------------------------\n // Private: step execution\n // -------------------------------------------------------------------------\n\n private async executeStep(\n step: ExecutableStep,\n input: InferenceInput,\n inputWidth: number,\n inputHeight: number,\n inputType: 'full-frame' | 'crop-roi',\n _parentBbox: AbsoluteBbox | undefined,\n parentClass: string | undefined,\n traceBuilder: ExecutionTraceBuilder,\n stepTimings: FrameStepTiming[],\n poolAgg: PoolStageAccumulator,\n ): Promise<StepOutput> {\n const preprocessStart = Date.now()\n const preprocessMs = Date.now() - preprocessStart\n\n // Inference\n const inferenceStart = Date.now()\n let engineOutput: import('@camstack/types').EngineOutput\n\n try {\n engineOutput = await step.engine.infer(input)\n } catch (err) {\n const errorMsg = errMsg(err)\n const errorOutput: StepOutput = { kind: 'detections', detections: [] }\n\n if (traceBuilder.isActive) {\n traceBuilder.addStep({\n stepId: step.stepId,\n modelId: step.modelId,\n slot: step.definition.slot,\n postprocessor: step.definition.postprocessor,\n preprocessMs,\n inferenceMs: Date.now() - inferenceStart,\n postprocessMs: 0,\n inputType,\n inputWidth,\n inputHeight,\n parentDetection: parentClass,\n output: errorOutput,\n error: errorMsg,\n })\n }\n\n stepTimings.push({\n source: step.stepId,\n modelId: step.modelId,\n ms: Date.now() - preprocessStart,\n detectionCount: 0,\n })\n\n throw new Error(`Inference failed for step \"${step.stepId}\": ${errorMsg}`, { cause: err })\n }\n\n // Prefer python-reported `inferenceMs` (wall measured inside the\n // pool: python preprocess + predict, NOT IPC). The python pool dict\n // is exposed by PoolHandle.infer under `engineOutput.structured`;\n // the top-level `engineOutput.inferenceMs` is the TS-measured wall\n // (includes IPC stdin/stdout + window-accumulator wait), which\n // inflates the metric by 30-60ms per call. Falls back to the TS\n // wall only when the structured payload is absent (non-pool engines).\n const structured = (engineOutput.structured ?? {}) as {\n readonly inferenceMs?: number\n readonly preprocessMs?: number\n readonly predictMs?: number\n readonly batchSize?: number\n }\n const inferenceMs = typeof structured.inferenceMs === 'number'\n ? structured.inferenceMs\n : Date.now() - inferenceStart\n\n // Capture python-pool stage timings — surfaced via FrameDebug for the\n // benchmark UI. Multi-step pipelines accumulate; single-step (benchmark)\n // gets the actual values directly.\n if (typeof structured.preprocessMs === 'number') {\n poolAgg.preprocessMs += structured.preprocessMs\n }\n if (typeof structured.predictMs === 'number') {\n poolAgg.predictMs += structured.predictMs\n }\n if (typeof structured.batchSize === 'number' && structured.batchSize > poolAgg.batchSize) {\n poolAgg.batchSize = structured.batchSize\n }\n\n // Postprocess\n const postprocessStart = Date.now()\n const output = dispatchPostprocess(engineOutput, step.definition)\n const postprocessMs = Date.now() - postprocessStart\n\n // Record timing\n stepTimings.push({\n source: step.stepId,\n modelId: step.modelId,\n ms: preprocessMs + inferenceMs + postprocessMs,\n detectionCount: output.kind === 'detections' ? output.detections.length : 0,\n })\n\n // Record trace\n if (traceBuilder.isActive) {\n traceBuilder.addStep({\n stepId: step.stepId,\n modelId: step.modelId,\n slot: step.definition.slot,\n postprocessor: step.definition.postprocessor,\n preprocessMs,\n inferenceMs,\n postprocessMs,\n inputType,\n inputWidth,\n inputHeight,\n parentDetection: parentClass,\n output,\n })\n }\n\n return output\n }\n\n // -------------------------------------------------------------------------\n // Private: children execution\n // -------------------------------------------------------------------------\n\n private async executeChildren(\n children: readonly ExecutableStep[],\n parentDetection: MutableObjectDetection,\n fullFrameJpegProvider: () => Promise<Buffer>,\n imageWidth: number,\n imageHeight: number,\n traceBuilder: ExecutionTraceBuilder,\n stepTimings: FrameStepTiming[],\n ctx: AssemblyContext,\n poolAgg: PoolStageAccumulator,\n ): Promise<void> {\n for (const child of children) {\n // Schema-derived inputClasses route by parent's macroClass.\n // Empty/null inputClasses = root step, no filter at this level.\n if (!this.matchesInputClasses(parentDetection.macroClass, child.inputClasses)) {\n continue\n }\n\n // Per-step minimum parent score — skip child if the parent\n // detection's confidence is below the threshold. Avoids running\n // expensive child models (face-det, plate-det) on low-confidence\n // root detections. Configurable per-step via settings.minParentScore,\n // falls back to the step definition's defaultMinParentScore.\n const minParentScore = (child.settings?.minParentScore as number | undefined)\n ?? child.definition?.defaultMinParentScore\n if (typeof minParentScore === 'number' && parentDetection.score < minParentScore) {\n continue\n }\n\n // Skip degenerate bboxes\n if (isBboxDegenerate(parentDetection.bbox)) {\n continue\n }\n\n try {\n const childStart = Date.now()\n\n // Lazily realize the JPEG buffer (cached by the provider) only\n // once a child crop is actually requested. Detection-only trees\n // pay nothing for the encode.\n const fullFrameJpeg = await fullFrameJpegProvider()\n const crop = await cropJpeg(fullFrameJpeg, parentDetection.bbox, imageWidth, imageHeight)\n\n const childOutput = await this.executeStep(\n child,\n { kind: 'jpeg', data: crop.jpeg },\n crop.width,\n crop.height,\n 'crop-roi',\n parentDetection.bbox,\n parentDetection.macroClass,\n traceBuilder,\n stepTimings,\n poolAgg,\n )\n const childMs = Date.now() - childStart\n\n // Apply child output to parent detection (may spawn detail\n // entries into ctx.details when the child is a cropper).\n const detailsBefore = ctx.details.length\n applyChildOutput(parentDetection, child, childOutput, childMs, ctx)\n\n // For cropper outputs (detections), recurse into grandchildren\n // on the freshly-created detail entries.\n if (childOutput.kind === 'detections' && child.children.length > 0) {\n const newDetails = ctx.details.slice(detailsBefore)\n for (const detail of newDetails) {\n // Transform detail bbox to image space (was produced in\n // crop coordinates by the cropper child).\n detail.bbox = transformBboxToImageSpace(detail.bbox, parentDetection.bbox)\n\n // Recurse: grandchildren run directly on the detail.\n await this.executeChildren(\n child.children,\n detail,\n fullFrameJpegProvider,\n imageWidth,\n imageHeight,\n traceBuilder,\n stepTimings,\n ctx,\n poolAgg,\n )\n }\n }\n } catch {\n // Step failure: the error is already recorded in the trace via\n // `executeStep`. We do NOT silently swallow — the step threw,\n // but we let sibling children continue so one bad model doesn't\n // break the whole frame.\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Private: helpers\n // -------------------------------------------------------------------------\n\n private matchesInputClasses(className: string, inputClasses: readonly string[]): boolean {\n if (inputClasses.length === 0) return true\n return inputClasses.includes(className)\n }\n\n /**\n * Apply the object-detection step's macro filter + per-macro\n * minConfidence sliders introduced in the Phase 6 step rework.\n *\n * Expected `settings` shape:\n * - `enabledMacroClasses`: readonly string[] (e.g. ['person','vehicle','animal']).\n * Empty array = all allowed (legacy behaviour).\n * - `minConfidence<Macro>`: number (e.g. `minConfidencePerson: 0.5`)\n */\n private matchesMacroFilter(\n macroClass: string,\n score: number,\n settings?: Readonly<Record<string, unknown>>,\n ): boolean {\n if (!settings) return true\n\n const enabled = settings['enabledMacroClasses']\n if (Array.isArray(enabled) && enabled.length > 0) {\n if (!enabled.includes(macroClass)) return false\n }\n\n const perMacroKey = `minConfidence${capitalize(macroClass)}`\n const perMacroThreshold = settings[perMacroKey]\n if (typeof perMacroThreshold === 'number' && score < perMacroThreshold) {\n return false\n }\n\n return true\n }\n}\n\nfunction capitalize(s: string): string {\n if (s.length === 0) return s\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n","/**\n * TreeBuilder — converts user pipeline config + step definitions + engines into\n * an executable tree that the PipelineExecutor can traverse.\n */\nimport type {\n PipelineDefaultStep,\n StepDefinition,\n IInferenceEngine,\n ConfigField,\n} from '@camstack/types'\nimport { getStep, getStepDefinition } from '../registry/step-definitions.js'\n\n// ---------------------------------------------------------------------------\n// Executable tree types\n// ---------------------------------------------------------------------------\n\nexport interface ExecutableStep {\n readonly stepId: string\n readonly definition: StepDefinition\n readonly engine: IInferenceEngine\n readonly modelId: string\n readonly inputClasses: readonly string[]\n readonly enabled: boolean\n readonly children: readonly ExecutableStep[]\n readonly settings?: Readonly<Record<string, unknown>>\n // Phase 7 (settings redesign): the runtime threshold used by\n // `PipelineExecutor` now comes from `definition.defaultConfidence`\n // directly. Per-step configurable thresholds live inside `settings`\n // (keyed by whatever name the step declares in its configSchema).\n}\n\nexport interface ExecutableTree {\n readonly roots: readonly ExecutableStep[]\n}\n\n// ---------------------------------------------------------------------------\n// Builder\n// ---------------------------------------------------------------------------\n\n/**\n * Build an executable tree from user config.\n *\n * @param steps - User-configured pipeline steps (from PipelineDefaultStep[])\n * @param getEngine - Function that returns an IInferenceEngine for a step ID.\n * Throws if step not loaded.\n */\nexport function buildExecutableTree(\n steps: readonly PipelineDefaultStep[],\n getEngine: (stepId: string) => IInferenceEngine,\n): ExecutableTree {\n const roots = steps\n .filter((s) => s.enabled)\n // Audio classifier runs independently — not part of the video execution tree\n .filter((s) => s.slot !== 'audio-classifier')\n .map((s) => buildNode(s, getEngine))\n\n return { roots }\n}\n\nfunction buildNode(\n step: PipelineDefaultStep,\n getEngine: (stepId: string) => IInferenceEngine,\n): ExecutableStep {\n const definition = getStepDefinition(step.addonId)\n const engine = getEngine(step.addonId)\n\n const children = (step.children ?? [])\n .filter((c) => c.enabled)\n .map((c) => buildNode(c, getEngine))\n\n // Hydrate the step's configSchema defaults into `settings` so downstream\n // filters (executor.matchesMacroFilter, classifier-children minConfidence,\n // etc.) always have a numeric baseline to compare against.\n //\n // Without this step, a caller that doesn't pass `step.settings` (benchmark\n // runner, first-boot with no per-device overrides, admin UI before first\n // Save) leaves `rootStep.settings === undefined`, and the executor bypass\n // at `matchesMacroFilter` lets every candidate box through the per-macro\n // filter. Combined with the Python postprocessor returning all candidates\n // (Phase 7, \"no internal filtering\"), this produced ~900 phantom boxes\n // per frame in the benchmark.\n const schemaDefaults = collectSchemaDefaults(step.addonId)\n const mergedSettings = { ...schemaDefaults, ...(step.settings ?? {}) }\n\n return {\n stepId: step.addonId,\n definition,\n engine,\n modelId: step.modelId,\n inputClasses: definition.inputClasses ?? [],\n enabled: step.enabled,\n children,\n ...(Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {}),\n }\n}\n\n/**\n * Walk the step's declared `getConfigSchema()` and collect every field\n * with a primitive `default` value into a plain record. Group fields\n * are flattened — a nested `{ type: 'group', fields: [...] }` is\n * entered recursively. Fields without a declared default are skipped.\n */\nfunction collectSchemaDefaults(stepId: string): Record<string, unknown> {\n const schema = getStep(stepId).getConfigSchema()\n const out: Record<string, unknown> = {}\n walkFieldsForDefaults(schema, out)\n return out\n}\n\nfunction walkFieldsForDefaults(\n fields: readonly ConfigField[],\n out: Record<string, unknown>,\n): void {\n for (const field of fields) {\n if ('type' in field && field.type === 'group' && 'fields' in field) {\n walkFieldsForDefaults(field.fields, out)\n continue\n }\n if ('key' in field && 'default' in field && field.default !== undefined) {\n out[field.key] = field.default\n }\n }\n}\n"],"mappings":"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","names":["EventCategory","createEvent","fs","path","errMsg","fs","path","fs","path","path","resolve","path","fs","platform","arch","errMsg","sharp","resolve","patched","createEvent","EventCategory"]}