@trillboards/edge-sdk 0.2.61 → 0.2.64
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/deploy/litertlm/test_trillboards_litertlm_shim.py +132 -0
- package/deploy/litertlm/trillboards-litertlm-shim.pth +1 -0
- package/deploy/litertlm/trillboards_litertlm_shim.py +214 -0
- package/deploy/macos/bench-local-vlm.sh +8 -4
- package/deploy/macos/build-macos-pkg.sh +4 -0
- package/deploy/macos/scripts/setup-litertlm.sh +99 -2
- package/deploy/systemd/setup-litertlm.sh +99 -2
- package/deploy/windows/build-windows.ps1 +4 -0
- package/deploy/windows/register-litertlm-task.ps1 +22 -0
- package/deploy/windows/setup-litertlm.ps1 +131 -3
- package/deploy/windows/trillboards-edge.cmd +19 -0
- package/dist/CloudSensingManager.d.ts +65 -0
- package/dist/CloudSensingManager.d.ts.map +1 -1
- package/dist/CloudSensingManager.js +65 -30
- package/dist/CloudSensingManager.js.map +1 -1
- package/dist/DurableVlmCandidateSpool.d.ts +21 -0
- package/dist/DurableVlmCandidateSpool.d.ts.map +1 -1
- package/dist/DurableVlmCandidateSpool.js +126 -1
- package/dist/DurableVlmCandidateSpool.js.map +1 -1
- package/dist/EdgeAgent.js +1 -1
- package/dist/LiteRtVlmProvider.d.ts +74 -7
- package/dist/LiteRtVlmProvider.d.ts.map +1 -1
- package/dist/LiteRtVlmProvider.js.map +1 -1
- package/dist/MosaicSource.d.ts +18 -0
- package/dist/MosaicSource.d.ts.map +1 -1
- package/dist/MosaicSource.js +23 -0
- package/dist/MosaicSource.js.map +1 -1
- package/dist/VlmCandidateBuffer.d.ts +45 -6
- package/dist/VlmCandidateBuffer.d.ts.map +1 -1
- package/dist/VlmCandidateBuffer.js +76 -7
- package/dist/VlmCandidateBuffer.js.map +1 -1
- package/dist/analyzeCostObserver.d.ts +96 -0
- package/dist/analyzeCostObserver.d.ts.map +1 -0
- package/dist/analyzeCostObserver.js +130 -0
- package/dist/analyzeCostObserver.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/connectorCadence.d.ts +286 -57
- package/dist/connectorCadence.d.ts.map +1 -1
- package/dist/connectorCadence.js +313 -61
- package/dist/connectorCadence.js.map +1 -1
- package/dist/deployedCadence.d.ts +8 -2
- package/dist/deployedCadence.d.ts.map +1 -1
- package/dist/deployedCadence.js +6 -3
- package/dist/deployedCadence.js.map +1 -1
- package/dist/deployedCadenceSource.d.ts +10 -0
- package/dist/deployedCadenceSource.d.ts.map +1 -1
- package/dist/deployedCadenceSource.js +11 -0
- package/dist/deployedCadenceSource.js.map +1 -1
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.js +1 -1
- package/dist/localInferenceSchema.d.ts +8 -3
- package/dist/localInferenceSchema.d.ts.map +1 -1
- package/dist/localInferenceSchema.js +8 -3
- package/dist/localInferenceSchema.js.map +1 -1
- package/dist/localSensing.d.ts +22 -1
- package/dist/localSensing.d.ts.map +1 -1
- package/dist/localSensing.js +101 -7
- package/dist/localSensing.js.map +1 -1
- package/dist/localVlmBench.d.ts +82 -0
- package/dist/localVlmBench.d.ts.map +1 -1
- package/dist/localVlmBench.js +88 -0
- package/dist/localVlmBench.js.map +1 -1
- package/dist/sensingCatchRate.d.ts.map +1 -1
- package/dist/sensingCatchRate.js +75 -10
- package/dist/sensingCatchRate.js.map +1 -1
- package/dist/sensingRunMode.d.ts.map +1 -1
- package/dist/sensingRunMode.js +38 -2
- package/dist/sensingRunMode.js.map +1 -1
- package/package.json +1 -1
|
@@ -44,11 +44,13 @@
|
|
|
44
44
|
* of 493 analyses/hour for the WHOLE connector, however many
|
|
45
45
|
* cameras it runs.
|
|
46
46
|
* STALE — see the block under MEASURED_LOCAL_ANALYZE_MS. The same
|
|
47
|
-
* harness measures 1.90 s
|
|
48
|
-
* config.json change
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
47
|
+
* harness measures 1.90 s, then 1.22 s with the 2026-08-19
|
|
48
|
+
* config.json change, then a further paired 2.42x with the 2026-08-19
|
|
49
|
+
* fast-conversation shim, on the pinned 0.16.0 — so the real ceiling
|
|
50
|
+
* on this machine class is ~1,895/hour, ~2,951/hour and higher again
|
|
51
|
+
* respectively. The constant is kept as a conservative FLEET default
|
|
52
|
+
* because the fleet varies ~7x; do not quote 493 as this machine's
|
|
53
|
+
* ceiling.
|
|
52
54
|
* (The DOOH audience schema measured 12.48 s median and ranged to
|
|
53
55
|
* 23.8 s because it emits a free-text narrative. The SOP field set
|
|
54
56
|
* is both cheaper and far tighter — 6.0-7.5 s — which is a second,
|
|
@@ -141,25 +143,81 @@ export declare const MEASURED_LOCAL_ANALYZE_MS = 7300;
|
|
|
141
143
|
* the box can afford, which costs hazard-persistence resolution against the
|
|
142
144
|
* 111 s constructive-notice bracket — the one thing this file exists to protect.
|
|
143
145
|
*
|
|
144
|
-
* ──
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
146
|
+
* ── AND IT MOVED AGAIN, 2026-08-19 (the shim) ──────────────────────────────
|
|
147
|
+
*
|
|
148
|
+
* `litert-lm serve` built `ConstrainedDecodingConfig(enable=True, …)` for every
|
|
149
|
+
* request even when nothing asked for a `response_format`, so every frame paid
|
|
150
|
+
* a constraint build for a grammar nothing used. Removing it (see
|
|
151
|
+
* `deploy/litertlm/trillboards_litertlm_shim.py`) is worth a further **paired
|
|
152
|
+
* 2.42x** on the shipped path — two stock serves on two ports, interleaved,
|
|
153
|
+
* n=16, 16/16 schema-valid and 16/16 BYTE-IDENTICAL generations on both arms.
|
|
154
|
+
* `bench-local-vlm.mjs --arms gpu-sop`, back to back in the same window, moves
|
|
155
|
+
* **1.32 -> 0.72 s/frame, i.e. 2,727 -> 5,000 analyses/hour**, at load 22-25
|
|
156
|
+
* where the same harness measures the stock arm at 1.32 s against the 1.22 s
|
|
157
|
+
* this file already records for it — so that window is close to the regime the
|
|
158
|
+
* 1.22 s was taken in, and 0.72 s is the nearest thing to a quotable per-frame
|
|
159
|
+
* number this machine class has.
|
|
160
|
+
*
|
|
161
|
+
* TWO CAVEATS THAT TRAVEL WITH IT. The ratio is itself load-dependent — 3.734x
|
|
162
|
+
* at 0.0% CPU idle against 2.42x at load 25 — because the removed work is
|
|
163
|
+
* CPU-bound and the retained work is GPU-bound, so a figure taken under
|
|
164
|
+
* contention flatters the change. And 0.72 s/frame is still BELOW the capacity
|
|
165
|
+
* target: 4 cameras resolving a 2 s bracket needs 0.5 s/frame (7,200
|
|
166
|
+
* analyses/hour), and this serves 2-3.
|
|
167
|
+
*
|
|
168
|
+
* ── IT IS NOT LOWERED HERE. IT IS NOW A BOOTSTRAP. (2026-08-19) ───────────
|
|
169
|
+
*
|
|
170
|
+
* This is ONE constant for a fleet whose measured spread on the SHIPPED path is
|
|
171
|
+
* 0.50 s/frame (M4 Max, post-#12567/#12594) against tens of seconds on a
|
|
172
|
+
* GPU-less x86 box. No constant is right for both: the fast desk's number would
|
|
173
|
+
* demand ~70x what the slow box can deliver, and the slow box's number makes
|
|
174
|
+
* every heartbeat worse than today. Picking the fast desk is exactly the error
|
|
175
|
+
* `.claude/lessons/2026-08-18-a-benchmark-with-no-machine-axis-is-a-spec-for-
|
|
176
|
+
* one-desk.md` was written about.
|
|
177
|
+
*
|
|
178
|
+
* So the fix was never a better constant, and as of 2026-08-19 it is built: the
|
|
179
|
+
* box DERIVES this for itself. `AnalyzeCostObserver` (analyzeCostObserver.ts)
|
|
180
|
+
* times every successful `analyze()` in `LocalSensingClient`, reports a rolling
|
|
181
|
+
* median over 16 frames, and `periodicIntervalMsForFeedCount` /
|
|
182
|
+
* `resolveDeployedCadence` take it as an optional input. A connector that has
|
|
183
|
+
* measured itself uses its OWN number; one that has not uses this one.
|
|
184
|
+
*
|
|
185
|
+
* NOTE WHAT THAT MAKES THIS CONSTANT. It is the value to assume BEFORE a machine
|
|
186
|
+
* has measured itself, so it should be conservative rather than accurate — and
|
|
187
|
+
* 7.30 s is exactly that. Guessing FAST on a slow box oversubscribes the single
|
|
188
|
+
* admission slot from the first minute; guessing slow costs only heartbeat
|
|
189
|
+
* resolution, and only until the observer settles (8 frames). It is therefore
|
|
190
|
+
* deliberately NOT lowered, and quoting it as a measurement of any machine is
|
|
191
|
+
* still what this block exists to stop.
|
|
192
|
+
*
|
|
193
|
+
* A CONSEQUENCE WORTH KNOWING: because this value does not move, neither does
|
|
194
|
+
* `sensingCatchRate.DEFAULT_SAMPLER.analyzeS`, so none of this required a
|
|
195
|
+
* corpus replay or a `.github/contracts/sensing-catch-rate-baseline.json`
|
|
196
|
+
* regeneration. If you ever DO move it, all of that becomes mandatory in the
|
|
197
|
+
* same change — and the catch-rate ratchet is shrink-only, so a baseline may
|
|
198
|
+
* never be regenerated merely to clear red.
|
|
199
|
+
*
|
|
200
|
+
* ── The idle-box objection, and why it does not block the derivation ────────
|
|
201
|
+
*
|
|
202
|
+
* The 2026-08-19 predecessor of this block held the per-machine derivation back
|
|
203
|
+
* for want of a per-frame number taken on an idle box, on the grounds that a
|
|
204
|
+
* figure measured at 0.0% CPU idle would hardcode one session's contention into
|
|
205
|
+
* every connector. That objection is answered twice over.
|
|
206
|
+
*
|
|
207
|
+
* First, it no longer applies to the DERIVATION at all: nothing global is
|
|
208
|
+
* derived from a measured number any more. Each box measures itself, in situ,
|
|
209
|
+
* under whatever load it actually runs at — which is the right load by
|
|
210
|
+
* definition, and is the one thing a bench on someone else's desk can never
|
|
211
|
+
* reproduce.
|
|
212
|
+
*
|
|
213
|
+
* Second, the verdict turned out not to depend on it. Three measurements of the
|
|
214
|
+
* shipped arm on one M4 Max across a 3x load range: 0.45 s at load 7.77, 0.50 s
|
|
215
|
+
* at load 11.87, 0.72 s at load 22-25. All three clear the 13.88 s/frame budget
|
|
216
|
+
* for 4 cameras by 19-31x, so "this class clears 4 cameras" is invariant across
|
|
217
|
+
* everything that could be measured. Load moves the quotable figure, not the
|
|
218
|
+
* verdict — and the post-merge path is markedly LESS load-sensitive than the one
|
|
219
|
+
* that motivated the objection, because the work #12594 removed was CPU-bound
|
|
220
|
+
* and what remains is GPU-bound.
|
|
163
221
|
*/
|
|
164
222
|
/**
|
|
165
223
|
* Share of the single admission slot the STILL-scene heartbeat floor may take.
|
|
@@ -213,8 +271,13 @@ export declare const STILL_FLOOR_SLOT_SHARE = 0.5;
|
|
|
213
271
|
*/
|
|
214
272
|
export declare const MIN_ANALYZE_INTERVAL_MS = 30000;
|
|
215
273
|
/**
|
|
216
|
-
* Measured wall-clock cost of ONE LOOK, in ms — a
|
|
217
|
-
* decode-and-detect that follows it.
|
|
274
|
+
* Measured wall-clock cost of ONE LOOK on the PER-GRAB capture path, in ms — a
|
|
275
|
+
* real RTSP grab, not the decode-and-detect that follows it.
|
|
276
|
+
*
|
|
277
|
+
* STILL LIVE, and still exactly right for the path it describes: the per-grab
|
|
278
|
+
* provider is what a feed runs when it cannot hold a persistent session (see
|
|
279
|
+
* `MEASURED_STREAM_LOOK_MS`), so this is the number `lookIntervalMsForFeedCount`
|
|
280
|
+
* uses in `'per_grab'` mode and nothing about it has changed.
|
|
218
281
|
*
|
|
219
282
|
* The cascade budget above quotes 3.6 ms for a look. That is the cost AFTER the
|
|
220
283
|
* frame is in hand: pull it down to ~178x100 grey and run the detector. Getting
|
|
@@ -247,6 +310,75 @@ export declare const MIN_ANALYZE_INTERVAL_MS = 30000;
|
|
|
247
310
|
* the connector is not running.
|
|
248
311
|
*/
|
|
249
312
|
export declare const MEASURED_LOOK_MS = 2000;
|
|
313
|
+
/**
|
|
314
|
+
* Measured wall-clock cost of ONE LOOK on the PERSISTENT capture path, in ms.
|
|
315
|
+
*
|
|
316
|
+
* `RTSPStreamCameraProvider` holds one long-lived ffmpeg per feed decoding into
|
|
317
|
+
* a bounded ring, so `getFrame()` is a memory read of the newest frame rather
|
|
318
|
+
* than a process start plus a DESCRIBE/SETUP/PLAY plus a wait for an IDR. The
|
|
319
|
+
* handshake is paid ONCE, at open.
|
|
320
|
+
*
|
|
321
|
+
* MEASURED 2026-08-18 against the same rig, same clip, same degraded profile
|
|
322
|
+
* (640x360 / 399 kbps / GOP 2 s), driving looks at the shipped cadence so a
|
|
323
|
+
* fresh frame is already in hand — which is the condition the connector runs
|
|
324
|
+
* in and the only one where "cost of a look" means anything:
|
|
325
|
+
*
|
|
326
|
+
* per-grab p50 2,000 ms
|
|
327
|
+
* persistent p50 0 ms (sub-millisecond; the ring read is a pointer)
|
|
328
|
+
*
|
|
329
|
+
* Reproduce: `node scripts/measure-persistent-capture.mjs --feeds 1
|
|
330
|
+
* --seconds 60 --pace-ms 500`. The rig is committed — see that file's header
|
|
331
|
+
* for why, and for why a `file://` fixture cannot stand in for it.
|
|
332
|
+
*
|
|
333
|
+
* It is 1 rather than 0 so that `lookIntervalMsForFeedCount` can never divide
|
|
334
|
+
* by it, and so nobody reads a zero as "free" — a look still costs the
|
|
335
|
+
* detector's `MEASURED_LOOK_DETECT_MS`, which is what actually bounds the
|
|
336
|
+
* persistent path.
|
|
337
|
+
*/
|
|
338
|
+
export declare const MEASURED_STREAM_LOOK_MS = 1;
|
|
339
|
+
/**
|
|
340
|
+
* Cost of a look AFTER the frame is in hand, in ms: pull it down to ~178x100
|
|
341
|
+
* grey and run `SceneChangeDetector`. Measured 3.6 ms median / 7.1 ms p95.
|
|
342
|
+
*
|
|
343
|
+
* This is the number the cascade budget has always quoted, and until the
|
|
344
|
+
* capture path changed it was irrelevant — a 3.6 ms stage behind a 2,000 ms
|
|
345
|
+
* one bounds nothing. On the persistent path it is the ONLY per-look cost
|
|
346
|
+
* left, so it becomes the binding term and gets named here rather than living
|
|
347
|
+
* in a comment.
|
|
348
|
+
*/
|
|
349
|
+
export declare const MEASURED_LOOK_DETECT_MS = 3.6;
|
|
350
|
+
/**
|
|
351
|
+
* Share of ONE core the motion detector may take across every feed.
|
|
352
|
+
*
|
|
353
|
+
* The one judgement in the persistent derivation, stated rather than hidden —
|
|
354
|
+
* the same shape as `STILL_FLOOR_SLOT_SHARE`. Detection is the cheapest stage
|
|
355
|
+
* on the box and it must not become the noisy neighbour: the connector also
|
|
356
|
+
* runs one ffmpeg decode per feed (measured on the rig at ~2.4% of a core per
|
|
357
|
+
* feed at this profile) and one local VLM slot, and the VLM is the thing the
|
|
358
|
+
* product is actually spending money on.
|
|
359
|
+
*
|
|
360
|
+
* A quarter of a core does not bind at any realistic feed count — see
|
|
361
|
+
* `lookIntervalMsForFeedCount` — which is the point: it is a guard rail, not a
|
|
362
|
+
* throttle, and it exists so that a future 64-feed host derives a cadence it
|
|
363
|
+
* can serve instead of one it merely asked for.
|
|
364
|
+
*/
|
|
365
|
+
export declare const LOOK_DETECT_CPU_SHARE = 0.25;
|
|
366
|
+
/**
|
|
367
|
+
* The persistent session's frame interval, in ms — the freshest a look can
|
|
368
|
+
* possibly be, because a look reads the newest DECODED frame.
|
|
369
|
+
*
|
|
370
|
+
* `RTSPStreamCameraProvider` defaults `streamFps` to 4, i.e. 250 ms. Restated
|
|
371
|
+
* here rather than imported because `edge-sdk` must not depend on a platform
|
|
372
|
+
* package, and asserted against the real default by `connectorCadence.test.ts`
|
|
373
|
+
* so the two cannot drift — the same treatment `LOOK_CAPTURE_CONCURRENCY` gets.
|
|
374
|
+
*
|
|
375
|
+
* `LOOK_INTERVAL_MS` must never be finer than this: asking for a 100 ms look
|
|
376
|
+
* cadence against a 250 ms stream would hand the same frame to the motion
|
|
377
|
+
* detector twice, which the provider refuses (it throws rather than repeat a
|
|
378
|
+
* frame) and which would report as a stale-stream fault on a perfectly healthy
|
|
379
|
+
* camera.
|
|
380
|
+
*/
|
|
381
|
+
export declare const MEASURED_STREAM_FRAME_INTERVAL_MS = 250;
|
|
250
382
|
/**
|
|
251
383
|
* Concurrent RTSP grabs one physical recorder will take —
|
|
252
384
|
* `DEFAULT_MAX_CONCURRENT_CAPTURES_PER_SOURCE` in `RTSPCameraProvider`, which
|
|
@@ -296,32 +428,59 @@ export declare const LEGACY_LOOK_INTERVAL_MS = 30000;
|
|
|
296
428
|
* T Instant Brief Sustained State
|
|
297
429
|
* 30 s 8.4% 18.6% 100% 100%
|
|
298
430
|
* 5 s 50.2% 55.1% 100% 100%
|
|
299
|
-
* 2.44 s 89.1% 71.2% 100% 100%
|
|
300
431
|
* 2 s 95.5% 75.2% 100% 100%
|
|
301
432
|
* 1 s 100.0% 86.6% 100% 100%
|
|
302
433
|
* 0.75 s 100.0% 90.1% 100% 100%
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* `
|
|
321
|
-
*
|
|
322
|
-
*
|
|
434
|
+
* 0.5 s 100.0% 93.2% 100% 100%
|
|
435
|
+
*
|
|
436
|
+
* ── 2 000 -> 500 ms ON 2026-08-18, AND THE REASON IS THE CAPTURE PATH ──────
|
|
437
|
+
*
|
|
438
|
+
* This constant was 2 000 ms, and the comment here said why in as many words:
|
|
439
|
+
* "the Brief group does NOT reach 90% until 0.75 s, and 0.75 s is well below
|
|
440
|
+
* the measured cost of a single grab. So 90% on every shape is NOT reachable by
|
|
441
|
+
* look cadence on the current capture architecture… Closing that gap needs a
|
|
442
|
+
* persistent RTSP session — one long-lived ffmpeg per feed instead of one per
|
|
443
|
+
* grab — which is an architecture change, not a number."
|
|
444
|
+
*
|
|
445
|
+
* That is now built (`RTSPStreamCameraProvider`), the constraint it named is
|
|
446
|
+
* gone, and this is the number that moves as a result. A look is a memory read
|
|
447
|
+
* of the newest decoded frame — `MEASURED_STREAM_LOOK_MS`, sub-millisecond —
|
|
448
|
+
* so the recorder no longer bounds the look cadence at all. Brief clears the
|
|
449
|
+
* 90% target at 0.75 s; 500 ms is the next value that is BOTH under it and a
|
|
450
|
+
* whole multiple of the persistent session's 250 ms frame interval
|
|
451
|
+
* (`MEASURED_STREAM_FRAME_INTERVAL_MS`), so a look always finds an unseen frame
|
|
452
|
+
* in hand and never blocks.
|
|
453
|
+
*
|
|
454
|
+
* ── WHY STOP AT 500 ms ────────────────────────────────────────────────────
|
|
455
|
+
*
|
|
456
|
+
* The same stopping rule that picked 2 000 ms: go until the last unmet target
|
|
457
|
+
* is met, and no further. At 500 ms every reporting group clears 90% — the
|
|
458
|
+
* first cadence in the product's history where that is true. Below it the
|
|
459
|
+
* corpus has nothing left to buy (Instant and Brief are saturated, Sustained
|
|
460
|
+
* and State were never look-limited) while the cost is real and linear: each
|
|
461
|
+
* halving doubles both the detector's CPU (`MEASURED_LOOK_DETECT_MS` per look
|
|
462
|
+
* per feed) and the session's JPEG-encode rate.
|
|
463
|
+
*
|
|
464
|
+
* ── AND IT MUST NOT COST ANALYSES ─────────────────────────────────────────
|
|
465
|
+
*
|
|
466
|
+
* Looking 4x more often must not analyse 4x more often, or the change has
|
|
467
|
+
* failed: one 7.30 s slot is 493 analyses/hour for the whole connector and
|
|
468
|
+
* there is no cloud path left to absorb an overflow. Three things hold that,
|
|
469
|
+
* and all three are asserted rather than assumed:
|
|
470
|
+
* - `MIN_ANALYZE_INTERVAL_MS` (30 s) is a SEPARATE constant and is what
|
|
471
|
+
* `MotionGate.minAnalyzeIntervalMs` floors on.
|
|
472
|
+
* - `periodicIntervalMsForFeedCount` floors on the ANALYZE floor, never on
|
|
473
|
+
* the look cadence, so the still-scene heartbeat does not move.
|
|
474
|
+
* - a still scene contributes exactly ZERO motion-triggered analyses, which
|
|
475
|
+
* is what makes the extra looks free at the model.
|
|
476
|
+
* The `sensing-catch-rate` guard's volume ratchet is the mechanical version:
|
|
477
|
+
* `volume.idleAnalyses` may only go DOWN.
|
|
478
|
+
*
|
|
479
|
+
* Do not raise or lower this without re-running the replay — the guard re-reads
|
|
480
|
+
* this constant and fails if it drifts from the cadence the corpus baseline was
|
|
481
|
+
* measured at.
|
|
323
482
|
*/
|
|
324
|
-
export declare const LOOK_INTERVAL_MS =
|
|
483
|
+
export declare const LOOK_INTERVAL_MS = 500;
|
|
325
484
|
/** The DOOH audience default (`DEFAULT_PERIODIC_MS`). The slow end of the clamp. */
|
|
326
485
|
export declare const DOOH_PERIODIC_MS = 300000;
|
|
327
486
|
/**
|
|
@@ -334,29 +493,99 @@ export declare const DOOH_PERIODIC_MS = 300000;
|
|
|
334
493
|
* Returns `null` when the count is not a positive integer, so a caller can omit
|
|
335
494
|
* the key entirely rather than write a cadence derived from a bad count.
|
|
336
495
|
*/
|
|
337
|
-
export declare function periodicIntervalMsForFeedCount(feedCount: unknown
|
|
496
|
+
export declare function periodicIntervalMsForFeedCount(feedCount: unknown,
|
|
338
497
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
498
|
+
* What a frame ACTUALLY costs on this box, in ms, when this box has measured
|
|
499
|
+
* itself (`AnalyzeCostObserver.medianMs()`). Omitted or `null` falls back to
|
|
500
|
+
* `MEASURED_LOCAL_ANALYZE_MS`, which is the BOOTSTRAP — deliberately
|
|
501
|
+
* conservative, and not a measurement of any machine.
|
|
502
|
+
*
|
|
503
|
+
* This parameter is the whole reason a fleet spanning 0.45 s/frame to tens of
|
|
504
|
+
* seconds can share one code path: the equation was always per-machine, and
|
|
505
|
+
* until 2026-08-19 only its input was global.
|
|
506
|
+
*/
|
|
507
|
+
analyzeMs?: number | null): number | null;
|
|
508
|
+
/**
|
|
509
|
+
* How a feed's frames are acquired. The look cadence a connector can SUSTAIN
|
|
510
|
+
* depends on this and on nothing else about the box.
|
|
511
|
+
*
|
|
512
|
+
* 'persistent' one long-lived ffmpeg per feed into a bounded ring
|
|
513
|
+
* (`RTSPStreamCameraProvider`). A look is a memory read.
|
|
514
|
+
* 'per_grab' one ffmpeg per grab (`RTSPCameraProvider`). A look is an RTSP
|
|
515
|
+
* handshake, and the RECORDER is the bound.
|
|
516
|
+
*/
|
|
517
|
+
export type CaptureMode = 'persistent' | 'per_grab';
|
|
518
|
+
/**
|
|
519
|
+
* The fastest look cadence a connector running `feedCount` feeds can actually
|
|
520
|
+
* SUSTAIN, in ms.
|
|
341
521
|
*
|
|
342
522
|
* Same shape as `periodicIntervalMsForFeedCount` and for the same reason: a
|
|
343
523
|
* cadence is only real if the machine can deliver it. That function divides one
|
|
344
|
-
* inference slot among N feeds; this one divides
|
|
345
|
-
*
|
|
524
|
+
* inference slot among N feeds; this one divides whatever the capture path's
|
|
525
|
+
* scarce resource is — and the whole point of the persistent session is that it
|
|
526
|
+
* CHANGES WHICH RESOURCE THAT IS.
|
|
527
|
+
*
|
|
528
|
+
* ── 'per_grab' — the RECORDER is the bound (unchanged) ────────────────────
|
|
346
529
|
*
|
|
347
530
|
* N feeds x (1 look / T) <= LOOK_CAPTURE_CONCURRENCY / MEASURED_LOOK_MS
|
|
348
531
|
* T >= N x MEASURED_LOOK_MS / LOOK_CAPTURE_CONCURRENCY
|
|
349
532
|
*
|
|
350
533
|
* Measured against that formula at 16 feeds on one recorder, GOP 2 s: predicted
|
|
351
|
-
* 16.0 s/feed, observed 16.0 s/feed.
|
|
534
|
+
* 16.0 s/feed, observed 16.0 s/feed. This is still exactly right for a feed that
|
|
535
|
+
* could not hold a persistent session and degraded — see
|
|
536
|
+
* `RTSPStreamCameraProvider`'s degrade path — so the branch stays live rather
|
|
537
|
+
* than being deleted as legacy.
|
|
538
|
+
*
|
|
539
|
+
* ── 'persistent' — the recorder drops out and the DETECTOR is the bound ───
|
|
540
|
+
*
|
|
541
|
+
* A look no longer touches the recorder, so `LOOK_CAPTURE_CONCURRENCY` and
|
|
542
|
+
* `MEASURED_LOOK_MS` have nothing to say about it. What is left is the cost
|
|
543
|
+
* AFTER the frame is in hand:
|
|
544
|
+
*
|
|
545
|
+
* N feeds x (1 look / T) x MEASURED_LOOK_DETECT_MS <= LOOK_DETECT_CPU_SHARE
|
|
546
|
+
* T >= N x 3.6 / 0.25 ms
|
|
547
|
+
*
|
|
548
|
+
* That is 230 ms at 16 feeds and 461 ms at 32 — both BELOW `LOOK_INTERVAL_MS`,
|
|
549
|
+
* so the clamp wins and a realistic connector simply gets the eval target. It
|
|
550
|
+
* begins to bind past ~34 feeds, which is the honest place for it to bind: a
|
|
551
|
+
* host with 34 cameras has other problems (its still-scene heartbeat pinned at
|
|
552
|
+
* `DOOH_PERIODIC_MS` since 21 feeds) and `getSensingHealth()` reports the
|
|
553
|
+
* effective interval either way.
|
|
352
554
|
*
|
|
353
555
|
* Never faster than `LOOK_INTERVAL_MS` (the eval target — looking finer than
|
|
354
556
|
* that buys nothing the corpus can see) and never slower than
|
|
355
557
|
* `LEGACY_LOOK_INTERVAL_MS`, so this can only make a connector look MORE often
|
|
356
|
-
* than it did, never less. Returns `null` on an uncountable feed count so a
|
|
357
|
-
* omit the key rather than derive a cadence from nothing.
|
|
558
|
+
* than it did, never less. Returns `null` on an uncountable feed count so a
|
|
559
|
+
* caller can omit the key rather than derive a cadence from nothing.
|
|
560
|
+
*/
|
|
561
|
+
export declare function lookIntervalMsForFeedCount(feedCount: unknown, captureMode?: CaptureMode): number | null;
|
|
562
|
+
/**
|
|
563
|
+
* Decoded frames a persistent session must produce per LOOK.
|
|
564
|
+
*
|
|
565
|
+
* TWO, and the second one is not waste. `RTSPStreamCameraProvider.getFrame()`
|
|
566
|
+
* refuses to hand back a frame it has already served — that refusal is the
|
|
567
|
+
* whole reason a frozen camera cannot report as healthy — so a look that
|
|
568
|
+
* arrives before the next frame does BLOCKS, and if it blocks past the
|
|
569
|
+
* staleness deadline it throws on a camera that is working perfectly.
|
|
570
|
+
*
|
|
571
|
+
* Running the stream at exactly the look rate makes that a coin flip on every
|
|
572
|
+
* tick, because neither clock is locked to the other: the session's frame
|
|
573
|
+
* cadence follows the CAMERA's timestamps and the look cadence follows a Node
|
|
574
|
+
* timer, and they drift. One frame of headroom removes the race for the cost of
|
|
575
|
+
* one extra scale+encode per look — the cheapest stage in the pipeline, and one
|
|
576
|
+
* that never touches the recorder or the model.
|
|
577
|
+
*/
|
|
578
|
+
export declare const STREAM_LOOK_HEADROOM = 2;
|
|
579
|
+
/**
|
|
580
|
+
* Frames per second a persistent session should decode to serve a given look
|
|
581
|
+
* cadence. Derived, so the two can never be set independently and drift into
|
|
582
|
+
* the blocking case above.
|
|
583
|
+
*
|
|
584
|
+
* At the shipped 500 ms look cadence this is 4 fps — which is also the
|
|
585
|
+
* provider's own default (`MEASURED_STREAM_FRAME_INTERVAL_MS`), and
|
|
586
|
+
* `connectorCadence.test.ts` asserts the agreement rather than trusting it.
|
|
358
587
|
*/
|
|
359
|
-
export declare function
|
|
588
|
+
export declare function streamFpsForLookIntervalMs(lookIntervalMs: unknown): number;
|
|
360
589
|
/**
|
|
361
590
|
* The still-scene analyses/hour a whole connector demands at a heartbeat, and
|
|
362
591
|
* what fraction of the single slot that is. Exposed so the derivation is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectorCadence.d.ts","sourceRoot":"","sources":["../src/connectorCadence.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"connectorCadence.d.ts","sourceRoot":"","sources":["../src/connectorCadence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkHG;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAU,CAAC;AAExC;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,OAAO;AAClB;;;;;;;;;GASG;AACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,MAAM,GAAG,IAAI,CAkCf;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,OAAO,EAClB,WAAW,GAAE,WAAwB,GACpC,MAAM,GAAG,IAAI,CA0Bf;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAI1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,OAAO,EAClB,kBAAkB,EAAE,OAAO,GAC1B;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAOhD"}
|