@camstack/addon-pipeline 1.2.38 → 1.2.39
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.
- package/dist/audio-analyzer/index.js +1 -1
- package/dist/audio-analyzer/index.mjs +1 -1
- package/dist/detection-pipeline/index.js +145 -175
- package/dist/detection-pipeline/index.mjs +120 -150
- package/dist/{dist-BvFLZyhW.js → dist-BtAvtroH.js} +260 -108
- package/dist/{dist-Du7_SNiR.mjs → dist-COgGdFMv.mjs} +260 -108
- package/dist/{step-definitions-C8tk9fPJ.mjs → event-loop-stall-monitor-naFYvcOO.mjs} +139 -2
- package/dist/{step-definitions-i8O-hxyI.js → event-loop-stall-monitor-shfwi1_b.js} +150 -1
- package/dist/lazy-sharp-B-mb8uWx.mjs +20 -0
- package/dist/lazy-sharp-CcYuXtsa.js +25 -0
- package/dist/motion-wasm/index.js +3 -3
- package/dist/motion-wasm/index.mjs +3 -3
- package/dist/pipeline-runner/index.js +3316 -2428
- package/dist/pipeline-runner/index.mjs +3315 -2428
- package/dist/recorder/index.js +102 -664
- package/dist/recorder/index.mjs +102 -658
- package/dist/stream-broker/_stub.js +2 -2
- package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-ByuMjhL3.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CjrKd4xX.mjs} +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B59GNAJq.mjs +26 -0
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-83qNzMSL.mjs +26 -0
- package/dist/stream-broker/{hostInit-CfC64ye2.mjs → hostInit-BFH17kSk.mjs} +2 -2
- package/dist/stream-broker/index.js +717 -211
- package/dist/stream-broker/index.mjs +717 -211
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-Dx-Vrpyt.js → MaskShapeCanvas-DI4BY7W2-yeSHfpUy.js} +1 -1
- package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-9D-BWwQP.js → MotionZonesSettings-NcxxQN8r-Df4WxM6N.js} +1 -1
- package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-HvAetfA4.js → PrivacyMaskSettings-APgPLF7p-COHSiTGq.js} +1 -1
- package/embed-dist/assets/index-4fJzjj4H.css +2 -0
- package/embed-dist/assets/index-Ct9M2v5n.js +112 -0
- package/embed-dist/index.html +2 -2
- package/package.json +2 -2
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-ByHi7ZAe.mjs +0 -26
- package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CscpvIRR.mjs +0 -26
- package/embed-dist/assets/index-9Ob2YrIf.js +0 -343
- package/embed-dist/assets/index-CAe6Dg-X.css +0 -2
- package/python/__pycache__/inference_pool.cpython-312.pyc +0 -0
- package/python/__pycache__/inference_pool.cpython-313.pyc +0 -0
- package/python/__pycache__/inference_pool.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_backpressure.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_coreml_cache.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_device_selection.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_layout.cpython-314.pyc +0 -0
- package/python/__pycache__/test_inference_pool_preprocess.cpython-314.pyc +0 -0
- package/python/__pycache__/yamnet_audio.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/__init__.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/__init__.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/_safety.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/arcface.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/arcface.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/arcface.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/clip.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/clip.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/clip.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/ctc.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/ctc.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/ctc.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/saliency.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/saliency.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/saliency.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/scrfd.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/scrfd.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/scrfd.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/softmax.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/softmax.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/softmax.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/ssd.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/ssd.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/ssd.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/test_clip.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_clip.cpython-313-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-313-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_ctc.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/test_saliency.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_saliency.cpython-313-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_softmax.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_yolo_nms.cpython-312-pytest-9.1.1.pyc +0 -0
- package/python/postprocessors/__pycache__/test_yolo_nms.cpython-39.pyc +0 -0
- package/python/postprocessors/__pycache__/yamnet.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yamnet.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yamnet.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-314.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo.cpython-39.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo_seg.cpython-312.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo_seg.cpython-313.pyc +0 -0
- package/python/postprocessors/__pycache__/yolo_seg.cpython-314.pyc +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { n as __require } from "../chunk-DnnnRqeS.mjs";
|
|
2
|
-
import { B as errMsg, E as evaluateZoneRules, F as runtimeDevices$1, G as createEvent, K as hydrateSchema, R as supportedRuntimes$1, T as enumerateInferenceDevices, V as BaseAddon, X as nodePin, Z as parseJsonUnknown, ct as object, dt as union, et as sleep, ft as EventCategory, h as YAMNET_TO_MACRO, j as pipelineExecutorCapability, nt as array, s as DEVICE_BACKEND_TO_FORMAT, t as APPLE_SA_TO_MACRO, ut as string, w as detectionPipelineCapability, x as defaultDeviceFor$1 } from "../dist-
|
|
3
|
-
import { a as
|
|
2
|
+
import { B as errMsg, E as evaluateZoneRules, F as runtimeDevices$1, G as createEvent, K as hydrateSchema, R as supportedRuntimes$1, T as enumerateInferenceDevices, V as BaseAddon, X as nodePin, Z as parseJsonUnknown, ct as object, dt as union, et as sleep, ft as EventCategory, h as YAMNET_TO_MACRO, j as pipelineExecutorCapability, nt as array, s as DEVICE_BACKEND_TO_FORMAT, t as APPLE_SA_TO_MACRO, ut as string, w as detectionPipelineCapability, x as defaultDeviceFor$1 } from "../dist-COgGdFMv.mjs";
|
|
3
|
+
import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, n as startEventLoopStallMonitor, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep } from "../event-loop-stall-monitor-naFYvcOO.mjs";
|
|
4
|
+
import { t as getSharp } from "../lazy-sharp-B-mb8uWx.mjs";
|
|
4
5
|
import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
|
|
5
6
|
import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
|
|
6
|
-
import * as os from "node:os";
|
|
7
|
-
import { spawn } from "node:child_process";
|
|
8
7
|
import sharp from "sharp";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import * as os from "node:os";
|
|
9
10
|
import * as fs from "node:fs";
|
|
10
11
|
import * as path$1 from "node:path";
|
|
11
|
-
import { PerformanceObserver } from "node:perf_hooks";
|
|
12
12
|
//#region src/detection-pipeline/runtimes.ts
|
|
13
13
|
var KNOWN_PLATFORMS = [
|
|
14
14
|
"darwin",
|
|
@@ -2238,6 +2238,89 @@ function normalizeOutput(output) {
|
|
|
2238
2238
|
};
|
|
2239
2239
|
}
|
|
2240
2240
|
//#endregion
|
|
2241
|
+
//#region src/detection-pipeline/pipeline/drop-sampler.ts
|
|
2242
|
+
/**
|
|
2243
|
+
* drop-sampler — keep the record of a dropped detection without the flood.
|
|
2244
|
+
*
|
|
2245
|
+
* ## Why
|
|
2246
|
+
*
|
|
2247
|
+
* The full-frame phantom guard logs every detection it removes, on purpose: a
|
|
2248
|
+
* branch that drops work silently reads as "never happened", and this repo has
|
|
2249
|
+
* paid for that twice. Its own comment set the bound honestly — *"Bounded by
|
|
2250
|
+
* how rare full-frame phantoms are, which is the point: if this floods, that is
|
|
2251
|
+
* itself the finding."*
|
|
2252
|
+
*
|
|
2253
|
+
* It flooded. Camera 615 emits a persistent full-frame `vehicle` phantom
|
|
2254
|
+
* (score ~0.62, areaRatio 0.949) and the guard was writing **dozens of INFO
|
|
2255
|
+
* lines a second** on 2026-08-02. So the finding stands — that camera has a
|
|
2256
|
+
* standing phantom worth investigating — but the log volume is now costing more
|
|
2257
|
+
* than the record is worth, and it drowns everything else in the same stream.
|
|
2258
|
+
*
|
|
2259
|
+
* ## The shape
|
|
2260
|
+
*
|
|
2261
|
+
* FIRST occurrence of each `(deviceId, macroClass)` logs in full: the example
|
|
2262
|
+
* is what makes the finding actionable — the bbox, the score, the ratios.
|
|
2263
|
+
* After that the pair is counted and a SUMMARY is emitted per interval, so the
|
|
2264
|
+
* question "how often does this fire, and on what?" is still answerable — from
|
|
2265
|
+
* a counter rather than from N identical lines.
|
|
2266
|
+
*
|
|
2267
|
+
* Deliberately per `(device, class)` and not global: a phantom on one camera
|
|
2268
|
+
* must not mask a new one appearing on another, which a single global counter
|
|
2269
|
+
* would do.
|
|
2270
|
+
*/
|
|
2271
|
+
/** How often an active drop pattern re-reports, with its count. */
|
|
2272
|
+
var DROP_SUMMARY_INTERVAL_MS = 6e4;
|
|
2273
|
+
/**
|
|
2274
|
+
* Decides, per `(deviceId, class)`, whether this drop is worth a line.
|
|
2275
|
+
*
|
|
2276
|
+
* Pure-ish and clock-injected so the interval logic is testable without
|
|
2277
|
+
* waiting a minute.
|
|
2278
|
+
*/
|
|
2279
|
+
var DropSampler = class {
|
|
2280
|
+
seen = /* @__PURE__ */ new Map();
|
|
2281
|
+
now;
|
|
2282
|
+
intervalMs;
|
|
2283
|
+
constructor(intervalMs = DROP_SUMMARY_INTERVAL_MS, now = () => Date.now()) {
|
|
2284
|
+
this.intervalMs = intervalMs;
|
|
2285
|
+
this.now = now;
|
|
2286
|
+
}
|
|
2287
|
+
/**
|
|
2288
|
+
* Record one drop. Returns what to log.
|
|
2289
|
+
*
|
|
2290
|
+
* The first sighting of a pair yields the example. Afterwards the count
|
|
2291
|
+
* accumulates and a summary is released once per interval — carrying the
|
|
2292
|
+
* number suppressed, so nothing about the RATE is lost even though the lines
|
|
2293
|
+
* are.
|
|
2294
|
+
*/
|
|
2295
|
+
record(deviceId, macroClass) {
|
|
2296
|
+
const key = `${deviceId}:${macroClass}`;
|
|
2297
|
+
const at = this.now();
|
|
2298
|
+
const prev = this.seen.get(key);
|
|
2299
|
+
if (prev === void 0) {
|
|
2300
|
+
this.seen.set(key, {
|
|
2301
|
+
count: 0,
|
|
2302
|
+
lastReportedAt: at
|
|
2303
|
+
});
|
|
2304
|
+
return {
|
|
2305
|
+
logExample: true,
|
|
2306
|
+
summaryCount: null
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
prev.count += 1;
|
|
2310
|
+
if (at - prev.lastReportedAt < this.intervalMs) return {
|
|
2311
|
+
logExample: false,
|
|
2312
|
+
summaryCount: null
|
|
2313
|
+
};
|
|
2314
|
+
const n = prev.count;
|
|
2315
|
+
prev.count = 0;
|
|
2316
|
+
prev.lastReportedAt = at;
|
|
2317
|
+
return {
|
|
2318
|
+
logExample: false,
|
|
2319
|
+
summaryCount: n
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
};
|
|
2323
|
+
//#endregion
|
|
2241
2324
|
//#region src/detection-pipeline/pipeline/execution-trace.ts
|
|
2242
2325
|
var traceCounter = 0;
|
|
2243
2326
|
var ExecutionTraceBuilder = class {
|
|
@@ -3294,6 +3377,10 @@ function buildFrameResult(input) {
|
|
|
3294
3377
|
height: input.height,
|
|
3295
3378
|
detections,
|
|
3296
3379
|
...input.discarded !== void 0 && input.discarded.length > 0 ? { discarded: input.discarded } : {},
|
|
3380
|
+
...input.shed !== void 0 ? {
|
|
3381
|
+
shed: true,
|
|
3382
|
+
shedReason: input.shed.reason
|
|
3383
|
+
} : {},
|
|
3297
3384
|
debug: frameDebug
|
|
3298
3385
|
};
|
|
3299
3386
|
}
|
|
@@ -3416,6 +3503,9 @@ function reprojectCropZoneDetections(output, cropOrigin) {
|
|
|
3416
3503
|
}
|
|
3417
3504
|
var PipelineExecutor = class {
|
|
3418
3505
|
opts;
|
|
3506
|
+
/** Bounds the full-frame-guard drop log: first example per (device,
|
|
3507
|
+
* class), then a counted summary per minute. See `drop-sampler.ts`. */
|
|
3508
|
+
dropSampler = new DropSampler();
|
|
3419
3509
|
constructor(opts) {
|
|
3420
3510
|
this.opts = opts;
|
|
3421
3511
|
}
|
|
@@ -3487,11 +3577,15 @@ var PipelineExecutor = class {
|
|
|
3487
3577
|
details
|
|
3488
3578
|
};
|
|
3489
3579
|
let discarded;
|
|
3580
|
+
const rootShed = {
|
|
3581
|
+
count: 0,
|
|
3582
|
+
reasons: []
|
|
3583
|
+
};
|
|
3490
3584
|
for (const rootStep of tree.roots) {
|
|
3491
3585
|
if (debug) console.log(`[executor] rootStep=${rootStep.stepId} settings=${JSON.stringify(rootStep.settings ?? {})}`);
|
|
3492
3586
|
const cropPrep = rootStep.definition.extractMode === "crop-zone" && cropZoneBbox !== void 0 ? await this.prepareCropZoneInput(cropZoneBbox, fullFrameJpegProvider, imageWidth, imageHeight, deviceId) : null;
|
|
3493
3587
|
const rootStart = Date.now();
|
|
3494
|
-
const rawRootOutput = await this.executeStep(rootStep, cropPrep?.input ?? rootInput, cropPrep?.width ?? imageWidth, cropPrep?.height ?? imageHeight, "full-frame", void 0, void 0, traceBuilder, stepTimings, poolAgg);
|
|
3588
|
+
const rawRootOutput = await this.executeStep(rootStep, cropPrep?.input ?? rootInput, cropPrep?.width ?? imageWidth, cropPrep?.height ?? imageHeight, "full-frame", void 0, void 0, traceBuilder, stepTimings, poolAgg, rootShed);
|
|
3495
3589
|
const rootMs = Date.now() - rootStart;
|
|
3496
3590
|
if (rawRootOutput === null) continue;
|
|
3497
3591
|
const rootOutput = cropPrep && rawRootOutput.kind === "detections" ? reprojectCropZoneDetections(rawRootOutput, cropPrep.origin) : rawRootOutput;
|
|
@@ -3557,7 +3651,17 @@ var PipelineExecutor = class {
|
|
|
3557
3651
|
const fullFrameBar = rootStep.settings?.["fullFrameGuardMaxConfidence"];
|
|
3558
3652
|
const fullFrameHardArea = rootStep.settings?.["fullFrameGuardHardAreaRatio"];
|
|
3559
3653
|
if (isFullFramePhantomDetection(mutable.bbox, imageWidth, imageHeight, mutable.score, typeof fullFrameBar === "number" ? fullFrameBar : void 0, typeof fullFrameHardArea === "number" ? fullFrameHardArea : void 0)) {
|
|
3560
|
-
this.
|
|
3654
|
+
const sample = this.dropSampler.record(deviceId, String(mutable.macroClass));
|
|
3655
|
+
if (sample.summaryCount !== null) this.opts.logger?.info("full-frame phantom guard: still dropping", {
|
|
3656
|
+
tags: { deviceId },
|
|
3657
|
+
meta: {
|
|
3658
|
+
macroClass: mutable.macroClass,
|
|
3659
|
+
suppressed: sample.summaryCount,
|
|
3660
|
+
windowMs: DROP_SUMMARY_INTERVAL_MS,
|
|
3661
|
+
hint: "a standing full-frame phantom — check the camera framing / model, not the guard"
|
|
3662
|
+
}
|
|
3663
|
+
});
|
|
3664
|
+
if (sample.logExample) this.opts.logger?.info("full-frame phantom guard: detection dropped", {
|
|
3561
3665
|
tags: { deviceId },
|
|
3562
3666
|
meta: {
|
|
3563
3667
|
macroClass: mutable.macroClass,
|
|
@@ -3619,6 +3723,7 @@ var PipelineExecutor = class {
|
|
|
3619
3723
|
firstLevel,
|
|
3620
3724
|
details,
|
|
3621
3725
|
discarded,
|
|
3726
|
+
...rootShed.count > 0 ? { shed: { reason: rootShed.reasons[0] ?? "pool-overload" } } : {},
|
|
3622
3727
|
stepTimings,
|
|
3623
3728
|
totalMs,
|
|
3624
3729
|
engine: this.opts.engineRuntime,
|
|
@@ -3689,7 +3794,7 @@ var PipelineExecutor = class {
|
|
|
3689
3794
|
return null;
|
|
3690
3795
|
}
|
|
3691
3796
|
}
|
|
3692
|
-
async executeStep(step, input, inputWidth, inputHeight, inputType, _parentBbox, parentClass, traceBuilder, stepTimings, poolAgg) {
|
|
3797
|
+
async executeStep(step, input, inputWidth, inputHeight, inputType, _parentBbox, parentClass, traceBuilder, stepTimings, poolAgg, shedAgg) {
|
|
3693
3798
|
const preprocessStart = Date.now();
|
|
3694
3799
|
const preprocessMs = Date.now() - preprocessStart;
|
|
3695
3800
|
const inferenceStart = Date.now();
|
|
@@ -3726,6 +3831,10 @@ var PipelineExecutor = class {
|
|
|
3726
3831
|
throw new Error(`Inference failed for step "${step.stepId}": ${errorMsg}`, { cause: err });
|
|
3727
3832
|
}
|
|
3728
3833
|
if (engineOutput.dropped === true) {
|
|
3834
|
+
if (shedAgg) {
|
|
3835
|
+
shedAgg.count += 1;
|
|
3836
|
+
shedAgg.reasons.push(engineOutput.shedReason ?? "pool-overload");
|
|
3837
|
+
}
|
|
3729
3838
|
stepTimings.push({
|
|
3730
3839
|
source: step.stepId,
|
|
3731
3840
|
modelId: step.modelId,
|
|
@@ -5613,8 +5722,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5613
5722
|
jpegProvider = async () => data;
|
|
5614
5723
|
} else if (frame.format === "rgb" || frame.format === "bgr" || frame.format === "gray") {
|
|
5615
5724
|
const channels = frame.format === "gray" ? 1 : 3;
|
|
5616
|
-
const
|
|
5617
|
-
const jpeg = await sharp(data, { raw: {
|
|
5725
|
+
const jpeg = await (await getSharp())(data, { raw: {
|
|
5618
5726
|
width: frame.width,
|
|
5619
5727
|
height: frame.height,
|
|
5620
5728
|
channels
|
|
@@ -5628,8 +5736,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
5628
5736
|
};
|
|
5629
5737
|
jpegProvider = async () => jpeg;
|
|
5630
5738
|
} else {
|
|
5631
|
-
const
|
|
5632
|
-
const encoded = await sharp(data, { raw: {
|
|
5739
|
+
const encoded = await (await getSharp())(data, { raw: {
|
|
5633
5740
|
width: frame.width,
|
|
5634
5741
|
height: frame.height,
|
|
5635
5742
|
channels: 3
|
|
@@ -6242,8 +6349,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
6242
6349
|
/** Parse JPEG/PNG dimensions without decoding the full image */
|
|
6243
6350
|
async getJpegDimensions(buf) {
|
|
6244
6351
|
try {
|
|
6245
|
-
const
|
|
6246
|
-
const { width, height } = await sharp(buf).metadata();
|
|
6352
|
+
const { width, height } = await (await getSharp())(buf).metadata();
|
|
6247
6353
|
return {
|
|
6248
6354
|
width: width ?? 640,
|
|
6249
6355
|
height: height ?? 640
|
|
@@ -7198,142 +7304,6 @@ function applyDeviceOverridesToTree(tree, rootStepId, overrides) {
|
|
|
7198
7304
|
} : root) };
|
|
7199
7305
|
}
|
|
7200
7306
|
//#endregion
|
|
7201
|
-
//#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
|
|
7202
|
-
/**
|
|
7203
|
-
* event-loop-stall-monitor — names the thing that freezes the detection process.
|
|
7204
|
-
*
|
|
7205
|
-
* ## What this exists to settle
|
|
7206
|
-
*
|
|
7207
|
-
* On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
|
|
7208
|
-
* calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
|
|
7209
|
-
* and EVERY one of them eventually resolved. Six explanations were killed with
|
|
7210
|
-
* telemetry alone:
|
|
7211
|
-
*
|
|
7212
|
-
* - not a slow GPU or a wedged pool — the work completes, just enormously late;
|
|
7213
|
-
* - not CPU starvation — hosts sat at 8-48% during the freezes;
|
|
7214
|
-
* - not swap — `pswpin/pswpout` are flat zero on every host;
|
|
7215
|
-
* - not a logging artifact — Loki's timestamp trails the in-process one by
|
|
7216
|
-
* 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
|
|
7217
|
-
* - not the v1.2.30 release — the stalls predate it by a day;
|
|
7218
|
-
* - not only the idle-pool recompile — that is real and hub-side, but the
|
|
7219
|
-
* agent stalls with no eviction and no compile at all.
|
|
7220
|
-
*
|
|
7221
|
-
* What IS established: the 10s stats timer — which touches no inference code —
|
|
7222
|
-
* stops firing for 22-88s and resumes at the same instant the abandoned calls
|
|
7223
|
-
* settle. The event loop itself freezes. From outside the process, GC and a
|
|
7224
|
-
* synchronous block are indistinguishable, and guessing between them is how
|
|
7225
|
-
* this investigation would go wrong. So measure it.
|
|
7226
|
-
*
|
|
7227
|
-
* ## How
|
|
7228
|
-
*
|
|
7229
|
-
* A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
|
|
7230
|
-
* the loop was blocked ~39s, whatever blocked it. In the same window the GC
|
|
7231
|
-
* observer accumulates pause time. The two numbers together answer the question
|
|
7232
|
-
* outright:
|
|
7233
|
-
*
|
|
7234
|
-
* - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
|
|
7235
|
-
* - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
|
|
7236
|
-
*
|
|
7237
|
-
* Heap and RSS ride along because if it is GC, the next question is always
|
|
7238
|
-
* "growing since when?".
|
|
7239
|
-
*
|
|
7240
|
-
* Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
|
|
7241
|
-
* distribution, and the question here is about ONE outlier and what coincided
|
|
7242
|
-
* with it.
|
|
7243
|
-
*/
|
|
7244
|
-
/** Tick period. Small enough to bound attribution error, large enough to cost
|
|
7245
|
-
* nothing on a process already doing frame work. */
|
|
7246
|
-
var STALL_TICK_MS = 1e3;
|
|
7247
|
-
/**
|
|
7248
|
-
* Report a freeze only past this. The runner's own inference deadline is 20s
|
|
7249
|
-
* and the observed freezes run 22-88s, so this is far below the signal while
|
|
7250
|
-
* staying clear of ordinary scheduling jitter (a busy frame path routinely
|
|
7251
|
-
* overruns a 1s timer by tens of ms).
|
|
7252
|
-
*/
|
|
7253
|
-
var STALL_REPORT_MS = 2e3;
|
|
7254
|
-
/**
|
|
7255
|
-
* Attribute a freeze. Pure, because this is the ONE judgement the whole module
|
|
7256
|
-
* exists to make and it must be testable without freezing a real event loop.
|
|
7257
|
-
*
|
|
7258
|
-
* `gc` when garbage collection accounts for most of the stall; a
|
|
7259
|
-
* `synchronous-block` when it accounts for almost none — that verdict points
|
|
7260
|
-
* away from the heap entirely and at whatever ran without yielding.
|
|
7261
|
-
*/
|
|
7262
|
-
function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
|
|
7263
|
-
const share = stallMs > 0 ? gcMs / stallMs : 0;
|
|
7264
|
-
return {
|
|
7265
|
-
stallMs,
|
|
7266
|
-
gcMs,
|
|
7267
|
-
gcCount,
|
|
7268
|
-
longestGcMs,
|
|
7269
|
-
cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
|
|
7270
|
-
};
|
|
7271
|
-
}
|
|
7272
|
-
/**
|
|
7273
|
-
* Start watching. Returns a stop function; safe to call twice.
|
|
7274
|
-
*
|
|
7275
|
-
* Never throws into the caller: a diagnostic that can take the detection
|
|
7276
|
-
* process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
|
|
7277
|
-
* unavailable in some embedded runtimes, and that must degrade to
|
|
7278
|
-
* stall-detection-without-attribution rather than a boot failure.
|
|
7279
|
-
*/
|
|
7280
|
-
function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
|
|
7281
|
-
let gc = {
|
|
7282
|
-
totalMs: 0,
|
|
7283
|
-
count: 0,
|
|
7284
|
-
longestMs: 0
|
|
7285
|
-
};
|
|
7286
|
-
let observer = null;
|
|
7287
|
-
try {
|
|
7288
|
-
observer = new PerformanceObserver((list) => {
|
|
7289
|
-
for (const entry of list.getEntries()) {
|
|
7290
|
-
gc.totalMs += entry.duration;
|
|
7291
|
-
gc.count += 1;
|
|
7292
|
-
if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
|
|
7293
|
-
}
|
|
7294
|
-
});
|
|
7295
|
-
observer.observe({ entryTypes: ["gc"] });
|
|
7296
|
-
} catch {
|
|
7297
|
-
observer = null;
|
|
7298
|
-
}
|
|
7299
|
-
let expected = Date.now() + STALL_TICK_MS;
|
|
7300
|
-
const timer = setInterval(() => {
|
|
7301
|
-
const now = Date.now();
|
|
7302
|
-
const lateBy = now - expected;
|
|
7303
|
-
const window = gc;
|
|
7304
|
-
gc = {
|
|
7305
|
-
totalMs: 0,
|
|
7306
|
-
count: 0,
|
|
7307
|
-
longestMs: 0
|
|
7308
|
-
};
|
|
7309
|
-
expected = now + STALL_TICK_MS;
|
|
7310
|
-
if (lateBy < reportThresholdMs) return;
|
|
7311
|
-
const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
|
|
7312
|
-
const mem = process.memoryUsage();
|
|
7313
|
-
logger.warn("event loop stalled", { meta: {
|
|
7314
|
-
stallMs: verdict.stallMs,
|
|
7315
|
-
cause: verdict.cause,
|
|
7316
|
-
gcMs: verdict.gcMs,
|
|
7317
|
-
gcCount: verdict.gcCount,
|
|
7318
|
-
longestGcMs: verdict.longestGcMs,
|
|
7319
|
-
gcObserved: observer !== null,
|
|
7320
|
-
heapUsedMb: Math.round(mem.heapUsed / 1e6),
|
|
7321
|
-
heapTotalMb: Math.round(mem.heapTotal / 1e6),
|
|
7322
|
-
rssMb: Math.round(mem.rss / 1e6),
|
|
7323
|
-
externalMb: Math.round(mem.external / 1e6),
|
|
7324
|
-
arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
|
|
7325
|
-
} });
|
|
7326
|
-
}, STALL_TICK_MS);
|
|
7327
|
-
timer.unref?.();
|
|
7328
|
-
let stopped = false;
|
|
7329
|
-
return () => {
|
|
7330
|
-
if (stopped) return;
|
|
7331
|
-
stopped = true;
|
|
7332
|
-
clearInterval(timer);
|
|
7333
|
-
observer?.disconnect();
|
|
7334
|
-
};
|
|
7335
|
-
}
|
|
7336
|
-
//#endregion
|
|
7337
7307
|
//#region src/detection-pipeline/python-readiness.ts
|
|
7338
7308
|
/**
|
|
7339
7309
|
* Resolve the embedded Python once, turning the null/throw failure modes into
|