@camstack/addon-pipeline 1.2.293 → 1.2.295

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/THIRD_PARTY_MODELS.md +233 -0
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +2 -2
  4. package/dist/{default-detection-model-DCQlW9fx.mjs → default-detection-model-CAUBVbgK.mjs} +53 -19
  5. package/dist/{default-detection-model-DTKHLLcC.js → default-detection-model-wNEISVA9.js} +58 -18
  6. package/dist/detection-pipeline/index.js +286 -458
  7. package/dist/detection-pipeline/index.mjs +286 -458
  8. package/dist/{dist-Ce8s7XqV.js → dist-BsVcf5wO.js} +1321 -833
  9. package/dist/{dist-C0Fr4hM5.mjs → dist-CuxSNLKW.mjs} +1316 -834
  10. package/dist/motion-wasm/index.js +1 -1
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-Xm7amG2e.mjs → node-Cqb1QiXk.mjs} +1 -1
  13. package/dist/{node-DpOrymbW.js → node-UFk6I2f6.js} +1 -1
  14. package/dist/pipeline-runner/index.js +266 -72
  15. package/dist/pipeline-runner/index.mjs +266 -72
  16. package/dist/{process-memory-CBRjuhyg.js → process-memory-CJV29sPf.js} +1 -1
  17. package/dist/{process-memory-BN5qYGsD.mjs → process-memory-D0Nvs9rr.mjs} +1 -1
  18. package/dist/recorder/index.js +12 -2
  19. package/dist/recorder/index.mjs +12 -2
  20. package/dist/{segment-demux-js-BhMlS_Ow.js → segment-demux-js-DGwmf5hu.js} +1 -1
  21. package/dist/{segment-demux-js-Bhwpj5Wl.mjs → segment-demux-js-DIDWw1iE.mjs} +1 -1
  22. package/dist/stream-broker/_stub.js +2 -2
  23. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DKd7oYba.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B7nBFqva.mjs} +2 -2
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CCIyBvRa.mjs +26 -0
  25. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DoyA71_q.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DeD_UFdb.mjs} +1 -1
  26. package/dist/stream-broker/demux-worker-child.js +1 -1
  27. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  28. package/dist/stream-broker/{hostInit-CCMIHjff.mjs → hostInit-BKlb3qac.mjs} +2 -2
  29. package/dist/stream-broker/index.js +3 -3
  30. package/dist/stream-broker/index.mjs +3 -3
  31. package/dist/stream-broker/remoteEntry.js +1 -1
  32. package/package.json +3 -2
  33. package/python/postprocessors/__init__.py +2 -0
  34. package/python/postprocessors/ssd.py +73 -17
  35. package/python/postprocessors/test_ssd.py +205 -0
  36. package/python/postprocessors/test_yunet.py +292 -0
  37. package/python/postprocessors/testdata/ssdlite_mobiledet_outputs.json +1 -0
  38. package/python/postprocessors/yunet.py +275 -0
  39. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6IHzlLJ_.mjs +0 -26
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-Ce8s7XqV.js");
5
+ const require_dist = require("../dist-BsVcf5wO.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Tt as motionDetectionCapability, ct as evaluateZoneRules, gn as hydrateSchema, on as BaseAddon, un as DeviceType } from "../dist-C0Fr4hM5.mjs";
1
+ import { Et as motionDetectionCapability, _n as hydrateSchema, dn as DeviceType, lt as evaluateZoneRules, sn as BaseAddon } from "../dist-CuxSNLKW.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import { $t as errMsg, in as isSoftwareDecode, nn as buildFfmpegArgs, tn as Fmp4BoxSplitter } from "./dist-C0Fr4hM5.mjs";
1
+ import { an as isSoftwareDecode, en as errMsg, nn as Fmp4BoxSplitter, rn as buildFfmpegArgs } from "./dist-CuxSNLKW.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-Ce8s7XqV.js");
2
+ const require_dist = require("./dist-BsVcf5wO.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs = require("node:fs");
5
5
  node_fs = require_chunk.__toESM(node_fs, 1);
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-Ce8s7XqV.js");
7
- const require_node = require("../node-DpOrymbW.js");
8
- const require_default_detection_model = require("../default-detection-model-DTKHLLcC.js");
6
+ const require_dist = require("../dist-BsVcf5wO.js");
7
+ const require_node = require("../node-UFk6I2f6.js");
8
+ const require_default_detection_model = require("../default-detection-model-wNEISVA9.js");
9
9
  const require_worker_protocol = require("../worker-protocol-B2MfQLlu.js");
10
10
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
11
11
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
@@ -1024,6 +1024,68 @@ var ParkedFrameStore = class {
1024
1024
  }
1025
1025
  }
1026
1026
  };
1027
+ /**
1028
+ * The key prefix the hub parks a face's SOURCE frame under (D644). The hub
1029
+ * owns the key; the runner reads only this prefix, to keep those parcels in
1030
+ * their own budget. Mirrors `FACE_SOURCE_PARK_PREFIX` in addon-post-analysis
1031
+ * (`runtime/face-source-park-port.ts`) — both specs pin the literal.
1032
+ */
1033
+ var FACE_SOURCE_PARCEL_PREFIX = "face-src:";
1034
+ /** Is this key a face source parcel? */
1035
+ function isFaceSourceParcelKey(trackId) {
1036
+ return trackId.startsWith(FACE_SOURCE_PARCEL_PREFIX);
1037
+ }
1038
+ /**
1039
+ * Track parcels and face source parcels in TWO stores with two budgets (D644).
1040
+ *
1041
+ * A face source parcel is a full native frame parked on every held best face.
1042
+ * In the shared store its eviction took the GLOBALLY oldest parcel — a
1043
+ * track's `keyFrame` or `thumbnail` — so a busy face scene could cost a track
1044
+ * its best frame (D58: media a later pass depends on must land). Here a face
1045
+ * parcel can only ever evict another face parcel.
1046
+ */
1047
+ var PartitionedParkedFrameStore = class {
1048
+ track;
1049
+ faceSource;
1050
+ constructor(options = {}) {
1051
+ this.track = new ParkedFrameStore(options.track);
1052
+ this.faceSource = new ParkedFrameStore({
1053
+ budgetBytes: 16 * 1024 * 1024,
1054
+ perDeviceMax: 4,
1055
+ ...options.faceSource
1056
+ });
1057
+ }
1058
+ storeFor(trackId) {
1059
+ return isFaceSourceParcelKey(trackId) ? this.faceSource : this.track;
1060
+ }
1061
+ get size() {
1062
+ return this.track.size + this.faceSource.size;
1063
+ }
1064
+ get bytes() {
1065
+ return this.track.bytes + this.faceSource.bytes;
1066
+ }
1067
+ park(deviceId, trackId, kind, frame) {
1068
+ return this.storeFor(trackId).park(deviceId, trackId, kind, frame);
1069
+ }
1070
+ get(deviceId, trackId, kind) {
1071
+ return this.storeFor(trackId).get(deviceId, trackId, kind);
1072
+ }
1073
+ release(deviceId, trackId) {
1074
+ return this.storeFor(trackId).release(deviceId, trackId);
1075
+ }
1076
+ statsByDevice(deviceId) {
1077
+ const a = this.track.statsByDevice(deviceId);
1078
+ const b = this.faceSource.statsByDevice(deviceId);
1079
+ return {
1080
+ parcels: a.parcels + b.parcels,
1081
+ bytes: a.bytes + b.bytes
1082
+ };
1083
+ }
1084
+ clear() {
1085
+ this.track.clear();
1086
+ this.faceSource.clear();
1087
+ }
1088
+ };
1027
1089
  /** {@link RETAINED_FRAME_BUDGET_MB} in bytes. */
1028
1090
  var RETAINED_FRAME_BUDGET_BYTES = (() => {
1029
1091
  const raw = Number(process.env["CAMSTACK_SESSION_RETAIN_MB"]);
@@ -2173,28 +2235,17 @@ function onboardRootDetections(slot, frame, gate) {
2173
2235
  //#endregion
2174
2236
  //#region src/pipeline-runner/bench-step-schema.ts
2175
2237
  /**
2176
- * Shared bench-facing schemas: the minimal recursive step tree and the
2177
- * engine choice. Extracted from `bench-actions.ts` so the capacity-ramp
2178
- * schemas (`capacity/capacity-schemas.ts`) can reuse them without a
2179
- * bench-actions ↔ capacity-schemas import cycle.
2180
- *
2181
- * Local mirror of pipeline-executor's shapes ON PURPOSE (not a cap-package
2182
- * coupling): the shape is fixed by the runtime contract
2183
- * (runtime + backend + format + optional device / addonId + modelId +
2184
- * enabled + children).
2185
- */
2186
- var BenchEngineChoiceSchema = require_dist.object({
2187
- runtime: require_dist._enum(["node", "python"]),
2188
- backend: require_dist.string(),
2189
- format: require_dist._enum([
2190
- "onnx",
2191
- "coreml",
2192
- "openvino",
2193
- "tflite",
2194
- "pt"
2195
- ]),
2196
- device: require_dist.string().optional()
2197
- });
2238
+ * Shared bench-facing schemas: the minimal recursive step tree. Extracted from
2239
+ * `bench-actions.ts` so the capacity-ramp schemas (`capacity/capacity-schemas.ts`)
2240
+ * can reuse it without a bench-actions ↔ capacity-schemas import cycle.
2241
+ *
2242
+ * Local mirror of pipeline-executor's step shape ON PURPOSE (not a cap-package
2243
+ * coupling): addonId + modelId + enabled + children.
2244
+ *
2245
+ * There is no engine choice here any more (D646). A bench names the pool it
2246
+ * measures by `deviceKey`; the engine override it used to carry was honoured by
2247
+ * swapping the executor's shared engine state under live cameras.
2248
+ */
2198
2249
  var BenchStepSchema = require_dist.lazy(() => require_dist.object({
2199
2250
  addonId: require_dist.string(),
2200
2251
  modelId: require_dist.string(),
@@ -2230,7 +2281,6 @@ var RunCapacityRampInputSchema = require_dist.object({
2230
2281
  sessionId: require_dist.string(),
2231
2282
  nodeId: require_dist.string().optional(),
2232
2283
  deviceKey: require_dist.string().optional(),
2233
- engine: BenchEngineChoiceSchema.optional(),
2234
2284
  slo: CapacitySloSchema.optional(),
2235
2285
  allowActiveCameras: require_dist.boolean().optional(),
2236
2286
  drainTimeoutSec: require_dist.number().min(1).max(300).optional()
@@ -2419,16 +2469,15 @@ var CacheBenchFrameResultSchema = require_dist.object({
2419
2469
  });
2420
2470
  var ReleaseBenchFrameInputSchema = require_dist.object({ frameId: require_dist.string() });
2421
2471
  var ReleaseBenchFrameResultSchema = require_dist.object({ released: require_dist.boolean() });
2422
- var RunSyntheticBenchInputSchema = require_dist.object({
2472
+ var StartSyntheticBenchInputSchema = require_dist.object({
2423
2473
  frameId: require_dist.string(),
2424
2474
  steps: require_dist.array(BenchStepSchema).min(1),
2425
2475
  parallel: require_dist.number().int().min(1).max(32),
2426
2476
  iterations: require_dist.number().int().min(1).max(1e4),
2427
2477
  warmup: require_dist.number().int().min(0).max(100).optional(),
2428
- sessionId: require_dist.string().optional(),
2478
+ sessionId: require_dist.string().min(1),
2429
2479
  simulatePipeline: require_dist.boolean().optional(),
2430
2480
  nodeId: require_dist.string().optional(),
2431
- engine: BenchEngineChoiceSchema.optional(),
2432
2481
  deviceKey: require_dist.string().optional(),
2433
2482
  plane: require_dist._enum(["full", "frame"]).optional()
2434
2483
  });
@@ -2438,7 +2487,16 @@ var TimingSplitSchema = require_dist.object({
2438
2487
  p95: require_dist.number(),
2439
2488
  p99: require_dist.number()
2440
2489
  });
2441
- var RunSyntheticBenchResultSchema = require_dist.object({
2490
+ var BenchPoolSchema = require_dist.object({
2491
+ nodeId: require_dist.string(),
2492
+ deviceKey: require_dist.string().nullable(),
2493
+ runtime: require_dist.string().nullable(),
2494
+ numWorkers: require_dist.number().nullable(),
2495
+ concurrency: require_dist.number().nullable(),
2496
+ batchMode: require_dist.string().nullable(),
2497
+ maxBatchSize: require_dist.number().nullable()
2498
+ });
2499
+ var SyntheticBenchResultSchema = require_dist.object({
2442
2500
  runs: require_dist.number(),
2443
2501
  wallSec: require_dist.number(),
2444
2502
  fps: require_dist.number(),
@@ -2450,23 +2508,52 @@ var RunSyntheticBenchResultSchema = require_dist.object({
2450
2508
  predictMs: require_dist.number(),
2451
2509
  batchSizeMean: require_dist.number(),
2452
2510
  batchSizeMax: require_dist.number(),
2453
- engine: require_dist.object({
2454
- runtime: require_dist.string(),
2455
- backend: require_dist.string(),
2456
- device: require_dist.string().optional()
2457
- }).optional(),
2458
- tuning: require_dist.object({
2459
- batchMode: require_dist.string(),
2460
- windowMs: require_dist.number(),
2461
- maxBatchSize: require_dist.number(),
2462
- concurrency: require_dist.number()
2463
- }).optional(),
2464
- path: require_dist.string().optional()
2511
+ pool: BenchPoolSchema,
2512
+ path: require_dist.string()
2513
+ });
2514
+ /** `startSyntheticBench` answers at once; the run continues in the background. */
2515
+ var StartSyntheticBenchAcceptedSchema = require_dist.object({
2516
+ accepted: require_dist.literal(true),
2517
+ sessionId: require_dist.string()
2465
2518
  });
2519
+ var SyntheticBenchKeySchema = require_dist.object({ sessionId: require_dist.string() });
2520
+ var SyntheticBenchStatusSchema = require_dist.object({
2521
+ sessionId: require_dist.string(),
2522
+ state: require_dist._enum([
2523
+ "running",
2524
+ "done",
2525
+ "failed",
2526
+ "aborted"
2527
+ ]),
2528
+ startedAtMs: require_dist.number(),
2529
+ finishedAtMs: require_dist.number().nullable(),
2530
+ progress: require_dist.object({
2531
+ runs: require_dist.number(),
2532
+ totalRuns: require_dist.number()
2533
+ }).nullable(),
2534
+ result: SyntheticBenchResultSchema.nullable(),
2535
+ error: require_dist.string().nullable()
2536
+ }).nullable();
2466
2537
  var pipelineRunnerBenchActions = require_dist.defineCustomActions({
2467
2538
  cacheBenchFrame: require_dist.customAction(CacheBenchFrameInputSchema, CacheBenchFrameResultSchema, { kind: "mutation" }),
2468
2539
  releaseBenchFrame: require_dist.customAction(ReleaseBenchFrameInputSchema, ReleaseBenchFrameResultSchema, { kind: "mutation" }),
2469
- runSyntheticBench: require_dist.customAction(RunSyntheticBenchInputSchema, RunSyntheticBenchResultSchema, { kind: "mutation" }),
2540
+ /**
2541
+ * START a synthetic bench and return at once (D646). Up to 32 parallel ×
2542
+ * 10 000 iterations does not fit the UDS addon-call plane's 60 s request
2543
+ * timeout, and a synchronous call was abandoned by its caller while it kept
2544
+ * loading the node. Poll `getSyntheticBenchStatus {sessionId}`; stop early
2545
+ * with `abortSyntheticBench {sessionId}`. One bench per runner at a time —
2546
+ * the measurement owns the pool.
2547
+ */
2548
+ startSyntheticBench: require_dist.customAction(StartSyntheticBenchInputSchema, StartSyntheticBenchAcceptedSchema, {
2549
+ kind: "mutation",
2550
+ auth: "admin"
2551
+ }),
2552
+ getSyntheticBenchStatus: require_dist.customAction(SyntheticBenchKeySchema, SyntheticBenchStatusSchema),
2553
+ abortSyntheticBench: require_dist.customAction(SyntheticBenchKeySchema, require_dist.object({ aborted: require_dist.boolean() }), {
2554
+ kind: "mutation",
2555
+ auth: "admin"
2556
+ }),
2470
2557
  /**
2471
2558
  * OPEN-loop capacity ramp (spec `2026-08-02-capacity-test-design.md` §4):
2472
2559
  * paced N×`fpsPerCamera` single-frame `runPipeline` calls with raw RGB —
@@ -2531,12 +2618,24 @@ function buildBenchRunPipelineInput(req) {
2531
2618
  return {
2532
2619
  steps: [...req.steps],
2533
2620
  frame: req.frame,
2534
- ...req.engine ? { engine: req.engine } : {},
2535
2621
  ...req.deviceKey ? { deviceKey: req.deviceKey } : {},
2536
2622
  ...req.plane ? { plane: req.plane } : {}
2537
2623
  };
2538
2624
  }
2539
2625
  /**
2626
+ * The tuning query for the pool a bench MEASURED (D646). It asked the node
2627
+ * default of whatever node answered first, so an NPU run was recorded beside
2628
+ * the onnx pool's `concurrency: 4`. Absent `deviceKey`, never
2629
+ * present-and-undefined, for the same `.loose()` reason as
2630
+ * {@link buildBenchRunPipelineInput}.
2631
+ */
2632
+ function buildBenchTuningQuery(target) {
2633
+ return {
2634
+ nodeId: target.nodeId,
2635
+ input: target.deviceKey ? { deviceKey: target.deviceKey } : {}
2636
+ };
2637
+ }
2638
+ /**
2540
2639
  * How long a box stays "seen".
2541
2640
  *
2542
2641
  * Sized on the gate's own clock, not on a guess: `maxDeferralMs` defaults to
@@ -3298,7 +3397,14 @@ function toDetailResult(detection, stepId, crop) {
3298
3397
  ...label.modelId !== void 0 ? { labelModelId: label.modelId } : {}
3299
3398
  } : {},
3300
3399
  ...detection.faceAlignedCrop !== void 0 ? { alignedCropJpeg: detection.faceAlignedCrop } : {},
3301
- ...detection.nativeFaceShortSidePx !== void 0 ? { nativeFaceShortSidePx: detection.nativeFaceShortSidePx } : {}
3400
+ ...detection.nativeFaceShortSidePx !== void 0 ? { nativeFaceShortSidePx: detection.nativeFaceShortSidePx } : {},
3401
+ ...detection.landmarks !== void 0 ? { landmarks: detection.landmarks.map((p) => mapPointToFrameSpace(p, crop)) } : {}
3402
+ };
3403
+ }
3404
+ function mapPointToFrameSpace(point, crop) {
3405
+ return {
3406
+ x: crop.frameSpace.x + point.x * (crop.frameSpace.w / crop.width),
3407
+ y: crop.frameSpace.y + point.y * (crop.frameSpace.h / crop.height)
3302
3408
  };
3303
3409
  }
3304
3410
  /**
@@ -6605,6 +6711,7 @@ var PipelineRunner = class {
6605
6711
  //#endregion
6606
6712
  //#region src/pipeline-runner/stateless-step.ts
6607
6713
  function modelServability(step, modelId, format) {
6714
+ if (require_default_detection_model.RETIRED_MODEL_IDS[step.id]?.includes(modelId) === true) return { kind: "retired" };
6608
6715
  const entry = step.models.find((m) => m.id === modelId);
6609
6716
  if (entry === void 0) return { kind: "custom-model" };
6610
6717
  if (entry.formats[format] !== void 0) return { kind: "catalog-build" };
@@ -6649,6 +6756,7 @@ async function runStatelessStep(deps, input) {
6649
6756
  const format = await deps.executionFormat();
6650
6757
  if (format === null) return refuse(deps, input, "model-not-servable", "this node could not report the engine format it would dispatch on, so the model pin cannot be verified — running anyway would risk a silent format substitution");
6651
6758
  const servability = modelServability(definition, input.modelId, format);
6759
+ if (servability.kind === "retired") return refuse(deps, input, "model-not-servable", `model "${input.modelId}" was retired from the ${definition.id} catalog — this node would have substituted the step default`);
6652
6760
  if (servability.kind === "no-build") return refuse(deps, input, "model-not-servable", `model "${input.modelId}" has no ${format} build (catalog ships: ${servability.availableFormats.join(", ") || "none"}) — this node would have substituted the ${format} default`);
6653
6761
  let decodeError = null;
6654
6762
  const crop = await cutFromFullFrame(input.frameJpeg, {
@@ -7504,6 +7612,26 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7504
7612
  benchFrameCache = /* @__PURE__ */ new Map();
7505
7613
  benchFrameSweeper = null;
7506
7614
  /**
7615
+ * Synthetic benches, start-then-poll (D646). One at a time on this runner —
7616
+ * the measurement owns the pool — and the last few finished ones kept for a
7617
+ * late poll. In RAM: a bench belongs to this process.
7618
+ */
7619
+ syntheticBenches = new require_dist.BackgroundRunRegistry({
7620
+ maxConcurrent: 1,
7621
+ keepFinished: 5,
7622
+ onSettled: (status) => {
7623
+ const line = { meta: {
7624
+ sessionId: status.runId,
7625
+ state: status.state,
7626
+ error: status.error,
7627
+ runs: status.result?.runs ?? null,
7628
+ fps: status.result?.fps ?? null
7629
+ } };
7630
+ if (status.state === "failed") this.ctx.logger.warn("synthetic bench failed", line);
7631
+ else this.ctx.logger.info("synthetic bench finished", line);
7632
+ }
7633
+ });
7634
+ /**
7507
7635
  * In-flight + recent capacity-ramp runs, keyed by sessionId. The ramp is
7508
7636
  * fire-and-poll (a full run outlives the UDS addon-call 60s timeout), so
7509
7637
  * completed results are kept here for `getCapacityRampStatus` to collect;
@@ -7582,7 +7710,11 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7582
7710
  nodeId: this.nodeId,
7583
7711
  continuousRetain: isContinuousRetainEnabled()
7584
7712
  });
7585
- this.parkedFrames = new ParkedFrameStore({ onEvict: (eviction) => this.noteParkedEviction(eviction.deviceId, eviction.cause) });
7713
+ const onEvict = (eviction) => this.noteParkedEviction(eviction.deviceId, eviction.cause);
7714
+ this.parkedFrames = new PartitionedParkedFrameStore({
7715
+ track: { onEvict },
7716
+ faceSource: { onEvict }
7717
+ });
7586
7718
  this.runner = new PipelineRunner({
7587
7719
  maxQueueDepth: this.config.maxQueueDepth,
7588
7720
  maxConcurrentInferences: this.config.maxConcurrentInferences,
@@ -7700,7 +7832,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7700
7832
  actionHandlers: {
7701
7833
  cacheBenchFrame: async (input) => this.cacheBenchFrame(input),
7702
7834
  releaseBenchFrame: async (input) => this.releaseBenchFrame(input),
7703
- runSyntheticBench: async (input) => this.runSyntheticBench(input),
7835
+ startSyntheticBench: async (input) => this.startSyntheticBench(input),
7836
+ getSyntheticBenchStatus: async (input) => this.getSyntheticBenchStatus(input.sessionId),
7837
+ abortSyntheticBench: async (input) => ({ aborted: this.syntheticBenches.abort(input.sessionId) }),
7704
7838
  runCapacityRamp: async (input) => this.runCapacityRampAction(input),
7705
7839
  getCapacityRampStatus: async (input) => this.getCapacityRampStatusAction(input),
7706
7840
  listCapacityRamps: async () => this.listCapacityRampsAction(),
@@ -7722,6 +7856,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7722
7856
  this.benchFrameSweeper = null;
7723
7857
  }
7724
7858
  this.benchFrameCache.clear();
7859
+ this.syntheticBenches.abortAll();
7725
7860
  for (const entry of this.capacityRuns.values()) entry.abortFlag.aborted = true;
7726
7861
  this.capacityRuns.clear();
7727
7862
  if (this.unsubMotionEvents) {
@@ -7801,7 +7936,34 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7801
7936
  const now = Date.now();
7802
7937
  for (const [id, entry] of this.benchFrameCache) if (entry.expiresAt < now) this.benchFrameCache.delete(id);
7803
7938
  }
7804
- async runSyntheticBench(input) {
7939
+ /**
7940
+ * Validate the cheap preconditions synchronously (so a bad frame id is a
7941
+ * refusal the caller sees at once), then run the bench in the background
7942
+ * and answer with the receipt (D646).
7943
+ */
7944
+ startSyntheticBench(input) {
7945
+ if (!this.ctx.api) throw new Error("startSyntheticBench: ctx.api unavailable");
7946
+ if (!this.benchFrameCache.has(input.frameId)) throw new Error(`startSyntheticBench: frameId ${input.frameId} not cached (call cacheBenchFrame first)`);
7947
+ this.syntheticBenches.start(input.sessionId, (run) => this.runSyntheticBench(input, run));
7948
+ return {
7949
+ accepted: true,
7950
+ sessionId: input.sessionId
7951
+ };
7952
+ }
7953
+ getSyntheticBenchStatus(sessionId) {
7954
+ const status = this.syntheticBenches.status(sessionId);
7955
+ if (status === null) return null;
7956
+ return {
7957
+ sessionId: status.runId,
7958
+ state: status.state,
7959
+ startedAtMs: status.startedAtMs,
7960
+ finishedAtMs: status.finishedAtMs,
7961
+ progress: status.progress,
7962
+ result: status.result,
7963
+ error: status.error
7964
+ };
7965
+ }
7966
+ async runSyntheticBench(input, run) {
7805
7967
  const ctx = this.ctx;
7806
7968
  const api = ctx.api;
7807
7969
  if (!api) throw new Error("runSyntheticBench: ctx.api unavailable");
@@ -7851,8 +8013,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7851
8013
  }, pin)).frameId;
7852
8014
  await api.pipelineExecutor.runPipeline.mutate({ ...buildBenchRunPipelineInput({
7853
8015
  steps: stepsToRun,
7854
- frame: sharedFrame,
7855
- ...input.engine ? { engine: input.engine } : {}
8016
+ frame: sharedFrame
7856
8017
  }) }, pin);
7857
8018
  const warmupCount = input.warmup ?? 1;
7858
8019
  for (let w = 0; w < warmupCount; w++) await api.pipelineExecutor.inferCached.mutate({
@@ -7866,11 +8027,12 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7866
8027
  const batchSizes = [];
7867
8028
  const detCounts = [];
7868
8029
  let _n = 0;
7869
- const sessionId = input.sessionId ?? `synth-${Date.now().toString(36)}`;
8030
+ const sessionId = input.sessionId;
7870
8031
  const totalRuns = input.parallel * input.iterations;
7871
8032
  const wallStart = performance.now();
7872
8033
  const worker = async () => {
7873
8034
  for (let i = 0; i < input.iterations; i++) {
8035
+ if (run.signal.aborted) break;
7874
8036
  const t0 = performance.now();
7875
8037
  const result = await api.pipelineExecutor.inferCached.mutate({
7876
8038
  stepId: rootStep.addonId,
@@ -7890,6 +8052,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7890
8052
  batchSizes.push(bs);
7891
8053
  detCounts.push(dets);
7892
8054
  const n = ++_n;
8055
+ run.report({
8056
+ runs: n,
8057
+ totalRuns
8058
+ });
7893
8059
  if (n <= 20) ctx.logger.info("bench call trace (cached)", { meta: {
7894
8060
  n,
7895
8061
  wallMs: Math.round(wallMs),
@@ -7942,7 +8108,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7942
8108
  await Promise.all(Array.from({ length: input.parallel }, () => worker()));
7943
8109
  const wallSec = (performance.now() - wallStart) / 1e3;
7944
8110
  await api.pipelineExecutor.uncacheFrame.mutate({ frameId: poolFrameId }, pin).catch(() => {});
7945
- return this.buildBenchResult(wallTimings, inferTimings, preprocessTimings, predictTimings, batchSizes, detCounts, wallSec, "cached");
8111
+ return this.buildBenchResult(wallTimings, inferTimings, preprocessTimings, predictTimings, batchSizes, detCounts, wallSec, "cached", { nodeId: benchNode });
7946
8112
  }
7947
8113
  ctx.logger.info("synthetic bench: using full runPipeline path", { meta: {
7948
8114
  steps: enabledSteps.length,
@@ -7956,7 +8122,6 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7956
8122
  const result = await api.pipelineExecutor.runPipeline.mutate({ ...buildBenchRunPipelineInput({
7957
8123
  steps: stepsToRun,
7958
8124
  frame: sharedFrame,
7959
- ...input.engine ? { engine: input.engine } : {},
7960
8125
  ...input.deviceKey ? { deviceKey: input.deviceKey } : {},
7961
8126
  ...input.plane ? { plane: input.plane } : {}
7962
8127
  }) }, pin);
@@ -7984,11 +8149,12 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7984
8149
  const predictTimings = [];
7985
8150
  const batchSizes = [];
7986
8151
  const detCounts = [];
7987
- const sessionId = input.sessionId ?? `synth-${Date.now().toString(36)}`;
8152
+ const sessionId = input.sessionId;
7988
8153
  const totalRuns = input.parallel * input.iterations;
7989
8154
  const wallStart = performance.now();
7990
8155
  const worker = async () => {
7991
8156
  for (let i = 0; i < input.iterations; i++) {
8157
+ if (run.signal.aborted) break;
7992
8158
  const { wallMs, result } = await callOnce();
7993
8159
  wallTimings.push(wallMs);
7994
8160
  serverWallTimings.push(result.debug?.wallMs ?? 0);
@@ -7998,6 +8164,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7998
8164
  batchSizes.push(result.debug?.batchSize ?? 1);
7999
8165
  detCounts.push(result.detections?.length ?? 0);
8000
8166
  const n = wallTimings.length;
8167
+ run.report({
8168
+ runs: n,
8169
+ totalRuns
8170
+ });
8001
8171
  if (n % Math.max(1, input.parallel) === 0 && ctx.eventBus) {
8002
8172
  const elapsed = (performance.now() - wallStart) / 1e3;
8003
8173
  const fps = elapsed > 0 ? n / elapsed : 0;
@@ -8038,9 +8208,12 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8038
8208
  };
8039
8209
  await Promise.all(Array.from({ length: input.parallel }, () => worker()));
8040
8210
  const wallSec = (performance.now() - wallStart) / 1e3;
8041
- return this.buildBenchResult(wallTimings, inferTimings, preprocessTimings, predictTimings, batchSizes, detCounts, wallSec, "pipeline");
8211
+ return this.buildBenchResult(wallTimings, inferTimings, preprocessTimings, predictTimings, batchSizes, detCounts, wallSec, "pipeline", {
8212
+ nodeId: benchNode,
8213
+ ...input.deviceKey ? { deviceKey: input.deviceKey } : {}
8214
+ });
8042
8215
  }
8043
- async buildBenchResult(wallTimings, inferTimings, preprocessTimings, predictTimings, batchSizes, detCounts, wallSec, path) {
8216
+ async buildBenchResult(wallTimings, inferTimings, preprocessTimings, predictTimings, batchSizes, detCounts, wallSec, path, target) {
8044
8217
  const meanOfArr = (xs) => xs.length > 0 ? xs.reduce((s, v) => s + v, 0) / xs.length : 0;
8045
8218
  this.ctx.logger.info("synthetic bench summary", { meta: {
8046
8219
  runs: wallTimings.length,
@@ -8076,24 +8249,47 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8076
8249
  batchSizeMean: Math.round(meanOf(batchSizes) * 100) / 100,
8077
8250
  batchSizeMax: batchSizes.length > 0 ? Math.max(...batchSizes) : 0,
8078
8251
  path,
8079
- ...await this.getEngineAndTuning()
8252
+ pool: await this.readBenchPool(target)
8080
8253
  };
8081
8254
  }
8082
- async getEngineAndTuning() {
8255
+ /**
8256
+ * The pool a bench measured, and its provisioning — asked of the node it
8257
+ * was pinned to, for the device pool it named (D646). It used to ask the
8258
+ * node DEFAULT of whichever node answered, so an NPU bench was recorded
8259
+ * beside the onnx pool's tuning. A failed read is `null` fields and a line,
8260
+ * never a default (D393).
8261
+ */
8262
+ async readBenchPool(target) {
8263
+ const unknown = {
8264
+ nodeId: target.nodeId,
8265
+ deviceKey: target.deviceKey ?? null,
8266
+ runtime: null,
8267
+ numWorkers: null,
8268
+ concurrency: null,
8269
+ batchMode: null,
8270
+ maxBatchSize: null
8271
+ };
8272
+ const api = this.ctx.api;
8273
+ if (!api) return unknown;
8274
+ const query = buildBenchTuningQuery(target);
8083
8275
  try {
8084
- const api = this.ctx.api;
8085
- if (!api) return {};
8086
- const [eng, tuning] = await Promise.all([api.pipelineExecutor.getSelectedEngine.query(), api.pipelineExecutor.getEffectiveTuning.query()]);
8276
+ const tuning = await api.pipelineExecutor.getEffectiveTuning.query({ ...query.input }, require_dist.nodePin(query.nodeId));
8087
8277
  return {
8088
- engine: eng ? {
8089
- runtime: eng.runtime,
8090
- backend: eng.backend,
8091
- device: eng.device
8092
- } : void 0,
8093
- tuning: tuning ?? void 0
8278
+ nodeId: target.nodeId,
8279
+ deviceKey: tuning.deviceKey,
8280
+ runtime: tuning.runtime,
8281
+ numWorkers: tuning.numWorkers,
8282
+ concurrency: tuning.concurrency,
8283
+ batchMode: tuning.batchMode,
8284
+ maxBatchSize: tuning.maxBatchSize
8094
8285
  };
8095
- } catch {
8096
- return {};
8286
+ } catch (err) {
8287
+ this.ctx.logger.warn("synthetic bench: pool tuning read failed — reported as unknown", { meta: {
8288
+ nodeId: target.nodeId,
8289
+ deviceKey: target.deviceKey ?? null,
8290
+ error: require_dist.errMsg(err)
8291
+ } });
8292
+ return unknown;
8097
8293
  }
8098
8294
  }
8099
8295
  /**
@@ -8216,7 +8412,6 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8216
8412
  settleSec,
8217
8413
  measureSec,
8218
8414
  deviceKey: input.deviceKey ?? null,
8219
- engine: input.engine ?? null,
8220
8415
  contaminatedByActiveCameras,
8221
8416
  steps: stepsToRun.map((s) => `${s.addonId}/${s.modelId}${s.enabled ? "" : "[off]"}`)
8222
8417
  } });
@@ -8233,8 +8428,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8233
8428
  },
8234
8429
  plane: "frame",
8235
8430
  sessionId,
8236
- ...input.deviceKey ? { deviceKey: input.deviceKey } : {},
8237
- ...input.engine ? { engine: input.engine } : {}
8431
+ ...input.deviceKey ? { deviceKey: input.deviceKey } : {}
8238
8432
  }, pin);
8239
8433
  if (result.debug?.engine !== void 0) engineLabel = result.debug.engine;
8240
8434
  if (result.shed === true) return {