@trillboards/edge-sdk 0.2.53 → 0.2.54
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/macos/README.md +4 -4
- package/deploy/macos/bench-local-vlm.sh +140 -0
- package/deploy/macos/build-macos-pkg.sh +3 -2
- package/deploy/windows/README.md +7 -5
- package/deploy/windows/bundle-windows-installer.ps1 +1 -3
- package/deploy/windows/e2e/test-local-sense.ps1 +97 -6
- package/dist/EdgeAgent.js +1 -1
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.js +1 -1
- package/dist/localVlmBench.d.ts +102 -0
- package/dist/localVlmBench.d.ts.map +1 -1
- package/dist/localVlmBench.js +129 -0
- package/dist/localVlmBench.js.map +1 -1
- package/package.json +1 -1
package/deploy/macos/README.md
CHANGED
|
@@ -40,7 +40,7 @@ delivered **only by pairing** — it is never baked into the `.pkg`.
|
|
|
40
40
|
|
|
41
41
|
## Install (operator)
|
|
42
42
|
|
|
43
|
-
1. Download `
|
|
43
|
+
1. Download `SenseAnythingConnector.pkg`.
|
|
44
44
|
2. Double-click it. The installer offers **Install for me only** (home-domain) —
|
|
45
45
|
no admin password. (Equivalent CLI: `installer -pkg <pkg> -target
|
|
46
46
|
CurrentUserHomeDirectory`.)
|
|
@@ -75,7 +75,7 @@ launchctl kickstart -k "gui/$(id -u)/com.trillboards.edge"
|
|
|
75
75
|
cd trillboards-edge-sdk
|
|
76
76
|
npm install # ensures ffmpeg-static is fetched
|
|
77
77
|
cd packages/edge-sdk/deploy/macos
|
|
78
|
-
./build-macos-pkg.sh # → dist-installer/
|
|
78
|
+
./build-macos-pkg.sh # → dist-installer/SenseAnythingConnector.pkg
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
Options:
|
|
@@ -136,9 +136,9 @@ To ship a Gatekeeper-clean installer, on a machine that has the
|
|
|
136
136
|
# (or: export MAC_INSTALLER_IDENTITY="Developer ID Installer: …" && ./build-macos-pkg.sh)
|
|
137
137
|
|
|
138
138
|
# 2. Notarize + staple
|
|
139
|
-
xcrun notarytool submit dist-installer/
|
|
139
|
+
xcrun notarytool submit dist-installer/SenseAnythingConnector.pkg \
|
|
140
140
|
--keychain-profile <notary-profile> --wait
|
|
141
|
-
xcrun stapler staple dist-installer/
|
|
141
|
+
xcrun stapler staple dist-installer/SenseAnythingConnector.pkg
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
The bundled `bin/node` and `bin/ffmpeg` are third-party Mach-O binaries; for full
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# bench-local-vlm.sh — macOS twin of `deploy/windows/e2e/test-local-sense.ps1
|
|
4
|
+
# -Mode bench`: provision the PINNED litert-lm + Gemma 4 E2B, start a real
|
|
5
|
+
# serve, and MEASURE this machine's per-frame cost.
|
|
6
|
+
#
|
|
7
|
+
# WHY IT EXISTS. Local-only inference makes the box spec the product spec, and
|
|
8
|
+
# every latency number in the capacity plan came off one Apple M4 Max
|
|
9
|
+
# workstation — including the 7.30 s/frame in connectorCadence.ts that sizes the
|
|
10
|
+
# whole 493-analyses/hour budget. A 3-core / 7 GB Apple silicon runner is the
|
|
11
|
+
# closest CI has to the Mac mini we would actually recommend, and it is the only
|
|
12
|
+
# cheap way to test the "16 GB+ RAM" minimum the activation runbook publishes.
|
|
13
|
+
#
|
|
14
|
+
# It does NOT use setup-litertlm.sh's LaunchAgent path: that bootstraps a
|
|
15
|
+
# per-login agent, and a CI runner's session is not the session a customer logs
|
|
16
|
+
# into. The runtime, the pin and the model are identical; only the supervisor
|
|
17
|
+
# differs, and the supervisor is not what is being measured.
|
|
18
|
+
#
|
|
19
|
+
# NOTE ON BASH 3.2. macOS still ships bash 3.2 as /bin/bash and that is what a
|
|
20
|
+
# GitHub macOS runner executes. Its identifier scan is NOT UTF-8 aware, so an
|
|
21
|
+
# expansion written `$VAR…` swallows the ellipsis bytes into the variable NAME
|
|
22
|
+
# and `set -u` then reports an unbound variable whose name is the real one with
|
|
23
|
+
# mojibake stapled to it — which reads as "the variable is unset" when it is set.
|
|
24
|
+
# Every expansion here is braced and every log line is ASCII for that reason.
|
|
25
|
+
#
|
|
26
|
+
# Usage:
|
|
27
|
+
# deploy/macos/bench-local-vlm.sh [--port 9379] [--arms a,b] [--frames f]
|
|
28
|
+
# [--rounds 1] [--budget-min 40]
|
|
29
|
+
# [--frame-timeout-min 20] [--out FILE]
|
|
30
|
+
# [--model <non-default>.litertlm]
|
|
31
|
+
#
|
|
32
|
+
# Exit 0 = a matrix was written (even a partial one — a slow box is a RESULT).
|
|
33
|
+
# Exit 1 = nothing was measured at all.
|
|
34
|
+
set -uo pipefail
|
|
35
|
+
|
|
36
|
+
PORT=9379
|
|
37
|
+
ARMS='cpu-sop,cpu-dooh-sop,gpu-sop'
|
|
38
|
+
FRAMES='kitchen-5-people'
|
|
39
|
+
ROUNDS=1
|
|
40
|
+
BUDGET_MIN=40
|
|
41
|
+
FRAME_TIMEOUT_MIN=20
|
|
42
|
+
OUT="${TMPDIR:-/tmp}/bench-local-vlm.json"
|
|
43
|
+
NOTE=''
|
|
44
|
+
# A NON-default .litertlm id, for the per-silicon A/B. Empty = the generic file
|
|
45
|
+
# both provisioners import everywhere.
|
|
46
|
+
MODEL=''
|
|
47
|
+
|
|
48
|
+
while [ $# -gt 0 ]; do
|
|
49
|
+
case "$1" in
|
|
50
|
+
--port) PORT="$2"; shift 2 ;;
|
|
51
|
+
--arms) ARMS="$2"; shift 2 ;;
|
|
52
|
+
--frames) FRAMES="$2"; shift 2 ;;
|
|
53
|
+
--rounds) ROUNDS="$2"; shift 2 ;;
|
|
54
|
+
--budget-min) BUDGET_MIN="$2"; shift 2 ;;
|
|
55
|
+
--frame-timeout-min) FRAME_TIMEOUT_MIN="$2"; shift 2 ;;
|
|
56
|
+
--out) OUT="$2"; shift 2 ;;
|
|
57
|
+
--note) NOTE="$2"; shift 2 ;;
|
|
58
|
+
--model) MODEL="$2"; shift 2 ;;
|
|
59
|
+
*) echo "unknown flag: $1" >&2; exit 2 ;;
|
|
60
|
+
esac
|
|
61
|
+
done
|
|
62
|
+
|
|
63
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # <edge-sdk>/deploy/macos
|
|
64
|
+
PKG_ROOT="$(cd "$HERE/../.." && pwd)" # <edge-sdk>
|
|
65
|
+
WORKSPACE_ROOT="$(cd "$PKG_ROOT/../.." && pwd)" # <repo>/trillboards-edge-sdk
|
|
66
|
+
[ -f "$PKG_ROOT/scripts/bench-local-vlm.mjs" ] || { echo "FAIL - PKG_ROOT wrong: $PKG_ROOT" >&2; exit 1; }
|
|
67
|
+
[ -f "$WORKSPACE_ROOT/package.json" ] || { echo "FAIL - WORKSPACE_ROOT wrong: $WORKSPACE_ROOT" >&2; exit 1; }
|
|
68
|
+
log() { echo "[macos-bench] $*"; }
|
|
69
|
+
die() { echo "FAIL - $*" >&2; exit 1; }
|
|
70
|
+
|
|
71
|
+
# The PINNED version, read from the provisioner rather than restated here — the
|
|
72
|
+
# litert-lm-version-pin guard already keeps that one declaration honest, and a
|
|
73
|
+
# second copy would be a second thing to forget.
|
|
74
|
+
LITERT_LM_VERSION="$(sed -n 's/^LITERT_LM_VERSION="\${LITERT_LM_VERSION:-\([^}]*\)}"/\1/p' "$HERE/scripts/setup-litertlm.sh" | head -1)"
|
|
75
|
+
[ -n "$LITERT_LM_VERSION" ] || die "could not read the pinned litert-lm version from setup-litertlm.sh"
|
|
76
|
+
log "pinned litert-lm: $LITERT_LM_VERSION"
|
|
77
|
+
|
|
78
|
+
log "building the connector (tsc, all workspaces in dependency order) ..."
|
|
79
|
+
( cd "$WORKSPACE_ROOT" && npm run build ) || die "npm run build failed"
|
|
80
|
+
|
|
81
|
+
if ! command -v uv >/dev/null 2>&1; then
|
|
82
|
+
log "installing uv (home-domain, no sudo) ..."
|
|
83
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh || die "uv install failed"
|
|
84
|
+
fi
|
|
85
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
86
|
+
|
|
87
|
+
log "installing litert-lm==${LITERT_LM_VERSION} ..."
|
|
88
|
+
uv tool install --python 3.12 --managed-python --force "litert-lm==$LITERT_LM_VERSION" \
|
|
89
|
+
|| die "litert-lm install failed"
|
|
90
|
+
LITERTLM="$HOME/.local/bin/litert-lm"
|
|
91
|
+
[ -x "$LITERTLM" ] || die "litert-lm not installed at $LITERTLM"
|
|
92
|
+
|
|
93
|
+
MODEL_ID='gemma-4-E2B-it.litertlm'
|
|
94
|
+
HF_REPO='litert-community/gemma-4-E2B-it-litert-lm'
|
|
95
|
+
if ! "$LITERTLM" list 2>/dev/null | grep -q "$MODEL_ID"; then
|
|
96
|
+
# EXACT form setup-litertlm.sh uses. The positional argument is the FILE inside
|
|
97
|
+
# the repo, not the repo: passing the repo id alone gets
|
|
98
|
+
# "Source file not found: <repo>", because the bare form imports a LOCAL path.
|
|
99
|
+
log "importing ${MODEL_ID} from ${HF_REPO} (~2.4 GB) ..."
|
|
100
|
+
"$LITERTLM" import --from-huggingface-repo "$HF_REPO" "$MODEL_ID" || die "model import failed"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
SERVE_LOG="${TMPDIR:-/tmp}/litertlm-serve-bench.log"
|
|
104
|
+
if curl -fsS --max-time 3 "http://127.0.0.1:$PORT/v1/models" >/dev/null 2>&1; then
|
|
105
|
+
log "a serve is already healthy on :$PORT"
|
|
106
|
+
SERVE_PID=''
|
|
107
|
+
else
|
|
108
|
+
log "starting litert-lm serve on 127.0.0.1:$PORT (log: $SERVE_LOG)"
|
|
109
|
+
"$LITERTLM" serve --host 127.0.0.1 --port "$PORT" --verbose > "$SERVE_LOG" 2>&1 &
|
|
110
|
+
SERVE_PID=$!
|
|
111
|
+
fi
|
|
112
|
+
cleanup() { [ -n "${SERVE_PID:-}" ] && kill "$SERVE_PID" 2>/dev/null; }
|
|
113
|
+
trap cleanup EXIT
|
|
114
|
+
|
|
115
|
+
HEALTHY=0
|
|
116
|
+
for _ in $(seq 1 120); do
|
|
117
|
+
if curl -fsS --max-time 3 "http://127.0.0.1:$PORT/v1/models" >/dev/null 2>&1; then HEALTHY=1; break; fi
|
|
118
|
+
[ -n "${SERVE_PID:-}" ] && ! kill -0 "$SERVE_PID" 2>/dev/null && break
|
|
119
|
+
sleep 5
|
|
120
|
+
done
|
|
121
|
+
[ "$HEALTHY" = "1" ] || { tail -40 "$SERVE_LOG" 2>/dev/null; die "litert-lm serve never became healthy on :$PORT"; }
|
|
122
|
+
log "litert-lm serve healthy on :$PORT"
|
|
123
|
+
|
|
124
|
+
log "running the hardware matrix — arms=$ARMS frames=$FRAMES rounds=$ROUNDS budget=${BUDGET_MIN}m"
|
|
125
|
+
node "$PKG_ROOT/scripts/bench-local-vlm.mjs" \
|
|
126
|
+
--litert "http://127.0.0.1:$PORT" \
|
|
127
|
+
--arms "$ARMS" --frames "$FRAMES" --rounds "$ROUNDS" \
|
|
128
|
+
--budget-ms "$((BUDGET_MIN * 60000))" \
|
|
129
|
+
--timeout-ms "$((FRAME_TIMEOUT_MIN * 60000))" \
|
|
130
|
+
--machine-note "$NOTE" \
|
|
131
|
+
${MODEL:+--model "$MODEL"} \
|
|
132
|
+
--out "$OUT"
|
|
133
|
+
BENCH_EXIT=$?
|
|
134
|
+
|
|
135
|
+
# A bench REPORTS; it does not gate. Exiting non-zero because a machine is slow
|
|
136
|
+
# would turn "this box is too slow" into "the lane is broken" — the confusion
|
|
137
|
+
# that left windows-latest unmeasured for two months. Only producing NOTHING fails.
|
|
138
|
+
[ -f "$OUT" ] || die "bench produced no matrix at all (node exit $BENCH_EXIT)"
|
|
139
|
+
log "matrix written: $OUT"
|
|
140
|
+
exit 0
|
|
@@ -104,6 +104,7 @@ WORKSPACE_ROOT="$(cd "$PKG_ROOT/../.." && pwd)" # …/trillboards-edge-sdk
|
|
|
104
104
|
|
|
105
105
|
BUNDLE_ID="com.trillboards.edge"
|
|
106
106
|
APP_NAME="TrillboardsEdge"
|
|
107
|
+
INSTALLER_NAME="SenseAnythingConnector"
|
|
107
108
|
INSTALL_SUBDIR="Library/Trillboards/edge" # relative to the user home
|
|
108
109
|
|
|
109
110
|
# ─── Args ────────────────────────────────────────────────────────────────────
|
|
@@ -176,7 +177,7 @@ DIST_DIR="$PKG_ROOT/deploy/macos/dist-installer"
|
|
|
176
177
|
STAGE="$DIST_DIR/stage-$ARCH_LABEL" # the payload root (== the user home view)
|
|
177
178
|
PAYLOAD="$STAGE/$INSTALL_SUBDIR" # …/Library/Trillboards/edge
|
|
178
179
|
SCRATCH="$DIST_DIR/scratch-$ARCH_LABEL"
|
|
179
|
-
PKG_OUT="$DIST_DIR/${
|
|
180
|
+
PKG_OUT="$DIST_DIR/${INSTALLER_NAME}.pkg"
|
|
180
181
|
COMPONENT_PKG="$SCRATCH/${APP_NAME}-component.pkg"
|
|
181
182
|
SETUP_APP="$STAGE/Applications/Trillboards CCTV Setup.app"
|
|
182
183
|
|
|
@@ -590,7 +591,7 @@ productbuild \
|
|
|
590
591
|
# ─── 10. Optional sign ───────────────────────────────────────────────────────
|
|
591
592
|
if [ -n "$SIGN_IDENTITY" ]; then
|
|
592
593
|
log "productsign with '$SIGN_IDENTITY'…"
|
|
593
|
-
SIGNED="$DIST_DIR/${
|
|
594
|
+
SIGNED="$DIST_DIR/${INSTALLER_NAME}-signed.pkg"
|
|
594
595
|
productsign --sign "$SIGN_IDENTITY" "$PKG_OUT" "$SIGNED"
|
|
595
596
|
mv "$SIGNED" "$PKG_OUT"
|
|
596
597
|
log "Signed: $PKG_OUT"
|
package/deploy/windows/README.md
CHANGED
|
@@ -70,15 +70,17 @@ serves every Windows 10/11 — no per-arch builds.
|
|
|
70
70
|
./build-windows.ps1 -Offline # copy host node/ffmpeg (air-gapped)
|
|
71
71
|
|
|
72
72
|
# after the inner installer is signed, create the one-file customer artifact
|
|
73
|
-
./bundle-windows-installer.ps1 -InnerInstaller <signed-inner.exe>
|
|
73
|
+
./bundle-windows-installer.ps1 -InnerInstaller <signed-inner.exe> # -> dist-installer\customer\SenseAnythingConnector.exe
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
Requires NSIS and 7-Zip on PATH (`choco install nsis 7zip -y`). The release workflow publishes
|
|
77
|
-
the `.exe` to `s3://trillboards-screen-static/connector/
|
|
77
|
+
the `.exe` to `s3://trillboards-screen-static/connector/SenseAnythingConnector.exe`
|
|
78
78
|
plus `trillboards-connector-windows.version.json`, containing the exact connector
|
|
79
|
-
version, production commit, SHA-256, file size, and publication time.
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
version, production commit, SHA-256, file size, and publication time. The old
|
|
80
|
+
`trillboards-connector-windows.exe` object remains a compatibility alias, with a
|
|
81
|
+
content-disposition header that still downloads as `SenseAnythingConnector.exe`.
|
|
82
|
+
It then invalidates the `screen.trillboards.com` CloudFront distribution, so the
|
|
83
|
+
portal's stable download URL never changes while field tests remain artifact-verifiable.
|
|
82
84
|
|
|
83
85
|
## Notes
|
|
84
86
|
|
|
@@ -37,9 +37,7 @@ if (-not (Test-Path -LiteralPath $InnerInstaller -PathType Leaf)) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if ([string]::IsNullOrWhiteSpace($OutputPath)) {
|
|
40
|
-
$
|
|
41
|
-
if ([string]::IsNullOrWhiteSpace($version)) { $version = "unknown" }
|
|
42
|
-
$OutputPath = Join-Path (Join-Path $DistDir "customer") "TrillboardsEdgeSetup-$version-x64.exe"
|
|
40
|
+
$OutputPath = Join-Path (Join-Path $DistDir "customer") "SenseAnythingConnector.exe"
|
|
43
41
|
}
|
|
44
42
|
$OutputPath = [System.IO.Path]::GetFullPath($OutputPath)
|
|
45
43
|
|
|
@@ -18,6 +18,18 @@
|
|
|
18
18
|
connector → serve → preAnalyzed → assert plumbing WITHOUT the ~2.4 GB model.
|
|
19
19
|
This is a STUB-VLM proof — NOT a real-model proof.
|
|
20
20
|
|
|
21
|
+
bench (workflow_dispatch only):
|
|
22
|
+
Provisions exactly as 'real' does, then runs scripts/bench-local-vlm.mjs
|
|
23
|
+
instead of the pass/fail verification — so the run MEASURES this machine's
|
|
24
|
+
per-frame cost and grades it against the throughput floor, rather than
|
|
25
|
+
asserting a person was seen.
|
|
26
|
+
This is the only way a Windows number exists at all. windows-latest has
|
|
27
|
+
failed -Mode real 8 of 8 times since 2026-06-29 (each attempt: two 360 s
|
|
28
|
+
request timeouts, one per delegate) and therefore produced NO per-frame
|
|
29
|
+
figure, so every latency number in the capacity plan came off one Apple M4
|
|
30
|
+
Max. A bench run bounds itself with -BenchBudgetMinutes and writes its JSON
|
|
31
|
+
after every frame, because a process killed at the job cap writes nothing.
|
|
32
|
+
|
|
21
33
|
real (workflow_dispatch / scheduled only):
|
|
22
34
|
Provisions litert-lm + imports Gemma 4 E2B (setup-litertlm.ps1, model cached),
|
|
23
35
|
waits for the real `litert-lm serve` :9379 to be healthy, then runs the REAL
|
|
@@ -29,20 +41,44 @@
|
|
|
29
41
|
different schema and a different prompt, so the audience arm cannot catch a
|
|
30
42
|
regression in it; it is also the set 166 of the 176 active prod profiles run.
|
|
31
43
|
|
|
32
|
-
.PARAMETER Mode 'wiring' (default) or '
|
|
44
|
+
.PARAMETER Mode 'wiring' (default), 'real' or 'bench'.
|
|
33
45
|
.PARAMETER Frame Person JPEG fixture. Default: e2e/fixtures/person.jpg.
|
|
34
|
-
.PARAMETER Port litert-lm serve port (real mode). Default 9379.
|
|
46
|
+
.PARAMETER Port litert-lm serve port (real/bench mode). Default 9379.
|
|
35
47
|
.PARAMETER FfmpegPath ffmpeg for the connector's RTSP capture (wiring mode).
|
|
36
48
|
Default: the bundled bin\ffmpeg.exe if staged, else 'ffmpeg'.
|
|
49
|
+
.PARAMETER BenchArms bench mode: which arms to run, in priority order — the
|
|
50
|
+
budget cuts from the END, so the shipping config leads.
|
|
51
|
+
.PARAMETER BenchFrames bench mode: which committed CCTV frames to measure.
|
|
52
|
+
.PARAMETER BenchRounds bench mode: interleaved rounds per arm.
|
|
53
|
+
.PARAMETER BenchBudgetMinutes bench mode: wall-clock ceiling, set BELOW the job
|
|
54
|
+
cap so the run stops with results written instead of
|
|
55
|
+
being killed with none.
|
|
56
|
+
.PARAMETER BenchFrameTimeoutMinutes bench mode: per-frame deadline. Generous on
|
|
57
|
+
purpose: a frame that times out records the DEADLINE, not
|
|
58
|
+
the machine's speed, which is exactly how windows-latest
|
|
59
|
+
has produced no number for two months.
|
|
60
|
+
.PARAMETER BenchOut bench mode: where the JSON matrix lands.
|
|
61
|
+
.PARAMETER BenchModel bench mode: a NON-default .litertlm model id, for the
|
|
62
|
+
per-silicon A/B (litert-community ships _intel_LNL /
|
|
63
|
+
_intel_PTL builds alongside the generic file we import
|
|
64
|
+
everywhere). Import it first with
|
|
65
|
+
`litert-lm import --from-huggingface-repo ...`.
|
|
37
66
|
|
|
38
67
|
.OUTPUTS exit 0 = E2E passed; exit 1 = failed.
|
|
39
68
|
#>
|
|
40
69
|
[CmdletBinding()]
|
|
41
70
|
param(
|
|
42
|
-
[ValidateSet('wiring', 'real')][string]$Mode = 'wiring',
|
|
71
|
+
[ValidateSet('wiring', 'real', 'bench')][string]$Mode = 'wiring',
|
|
43
72
|
[string]$Frame,
|
|
44
73
|
[string]$Port = '9379',
|
|
45
|
-
[string]$FfmpegPath
|
|
74
|
+
[string]$FfmpegPath,
|
|
75
|
+
[string]$BenchArms = 'cpu-sop,gpu-sop,cpu-dooh-sop',
|
|
76
|
+
[string]$BenchFrames = 'kitchen-5-people',
|
|
77
|
+
[int]$BenchRounds = 1,
|
|
78
|
+
[int]$BenchBudgetMinutes = 40,
|
|
79
|
+
[int]$BenchFrameTimeoutMinutes = 20,
|
|
80
|
+
[string]$BenchOut,
|
|
81
|
+
[string]$BenchModel
|
|
46
82
|
)
|
|
47
83
|
$ErrorActionPreference = 'Stop'
|
|
48
84
|
Set-StrictMode -Version Latest
|
|
@@ -113,8 +149,15 @@ if ($Mode -eq 'wiring') {
|
|
|
113
149
|
exit 0
|
|
114
150
|
}
|
|
115
151
|
|
|
116
|
-
# ─── Mode: real (actual Gemma 4 E2B via litert-lm serve)
|
|
117
|
-
|
|
152
|
+
# ─── Mode: real / bench (actual Gemma 4 E2B via litert-lm serve) ──────────────
|
|
153
|
+
# Provisioning, serve start-up and health are IDENTICAL for both: 'real' then
|
|
154
|
+
# asserts, 'bench' then measures. One provisioning path, so a Windows number and
|
|
155
|
+
# a Windows pass/fail are statements about the same install.
|
|
156
|
+
if ($Mode -eq 'bench') {
|
|
157
|
+
Log "HARDWARE MATRIX — provisioning litert-lm + Gemma 4 E2B, then MEASURING this machine"
|
|
158
|
+
} else {
|
|
159
|
+
Log "REAL-MODEL SMOKE — provisioning litert-lm + Gemma 4 E2B, then real inference"
|
|
160
|
+
}
|
|
118
161
|
|
|
119
162
|
# 1. Provision litert-lm + import the model (idempotent; model may be cached by
|
|
120
163
|
# the workflow's actions/cache step before this runs).
|
|
@@ -174,6 +217,54 @@ if (-not $healthy) {
|
|
|
174
217
|
}
|
|
175
218
|
Log "litert-lm serve healthy on :$Port"
|
|
176
219
|
|
|
220
|
+
# 3b. BENCH: measure this machine instead of asserting on it.
|
|
221
|
+
if ($Mode -eq 'bench') {
|
|
222
|
+
if (-not $BenchOut) {
|
|
223
|
+
$benchBase = if ([string]::IsNullOrWhiteSpace($env:RUNNER_TEMP)) { $env:TEMP } else { $env:RUNNER_TEMP }
|
|
224
|
+
$BenchOut = Join-Path $benchBase 'bench-local-vlm.json'
|
|
225
|
+
}
|
|
226
|
+
$bench = Join-Path $PkgRoot 'scripts\bench-local-vlm.mjs'
|
|
227
|
+
if (-not (Test-Path $bench)) { Die "bench runner missing: $bench" }
|
|
228
|
+
# The machine itself is read from `os` by the bench; this note only records
|
|
229
|
+
# WHICH instance of that machine class ran, so a CI row is traceable to a run.
|
|
230
|
+
$note = if ($env:GITHUB_ACTIONS -eq 'true') {
|
|
231
|
+
"GitHub-hosted runner, image $($env:ImageOS)/$($env:ImageVersion), run $($env:GITHUB_RUN_ID)"
|
|
232
|
+
} else { 'local Windows machine' }
|
|
233
|
+
Log "running the hardware matrix — arms=$BenchArms frames=$BenchFrames rounds=$BenchRounds budget=${BenchBudgetMinutes}m"
|
|
234
|
+
# Initialised BEFORE the try: if `node` cannot start at all, the failure
|
|
235
|
+
# message below still has a value to report instead of tripping StrictMode.
|
|
236
|
+
$benchExit = -1
|
|
237
|
+
try {
|
|
238
|
+
# Built as an ARRAY and splatted rather than a backticked line ending in a
|
|
239
|
+
# conditional array: an empty @() as the trailing argument to a native
|
|
240
|
+
# command expands to an empty string under Windows PowerShell 5.1, which
|
|
241
|
+
# node would read as a stray positional argument.
|
|
242
|
+
$benchArgs = @(
|
|
243
|
+
$bench,
|
|
244
|
+
'--litert', "http://127.0.0.1:$Port",
|
|
245
|
+
'--arms', $BenchArms,
|
|
246
|
+
'--frames', $BenchFrames,
|
|
247
|
+
'--rounds', [string]$BenchRounds,
|
|
248
|
+
'--budget-ms', [string]($BenchBudgetMinutes * 60000),
|
|
249
|
+
'--timeout-ms', [string]($BenchFrameTimeoutMinutes * 60000),
|
|
250
|
+
'--machine-note', $note,
|
|
251
|
+
'--out', $BenchOut
|
|
252
|
+
)
|
|
253
|
+
if ($BenchModel) { $benchArgs += @('--model', $BenchModel) }
|
|
254
|
+
node @benchArgs
|
|
255
|
+
$benchExit = $LASTEXITCODE
|
|
256
|
+
} finally {
|
|
257
|
+
if ($serveProc) { try { $serveProc.Kill() } catch {} }
|
|
258
|
+
}
|
|
259
|
+
if (Test-Path $BenchOut) { Log "matrix written: $BenchOut" }
|
|
260
|
+
# A bench REPORTS; it does not gate. Exiting non-zero on a slow machine would
|
|
261
|
+
# turn "this box is too slow" into "the lane is broken", which is the confusion
|
|
262
|
+
# that left windows-latest unmeasured. Only a crash (no JSON at all) fails.
|
|
263
|
+
if (-not (Test-Path $BenchOut)) { Die "bench produced no matrix at all (node exit $benchExit)" }
|
|
264
|
+
Pass "hardware matrix measured on this machine (see $BenchOut)."
|
|
265
|
+
exit 0
|
|
266
|
+
}
|
|
267
|
+
|
|
177
268
|
# 4. REAL on-device inference on the person fixture + assert a person was seen.
|
|
178
269
|
# BOTH field sets are exercised, because the connector ships both and they are
|
|
179
270
|
# different prompts against a different schema:
|
package/dist/EdgeAgent.js
CHANGED
|
@@ -44,7 +44,7 @@ const edge_ads_1 = require("@trillboards/edge-ads");
|
|
|
44
44
|
const CommandRouter_1 = require("./CommandRouter");
|
|
45
45
|
const config_1 = require("./config");
|
|
46
46
|
const dataDir_1 = require("./dataDir");
|
|
47
|
-
const SDK_VERSION = '0.2.
|
|
47
|
+
const SDK_VERSION = '0.2.54';
|
|
48
48
|
class EdgeAgent extends events_1.EventEmitter {
|
|
49
49
|
constructor(config, deps) {
|
|
50
50
|
super();
|
package/dist/diagnose.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface DiagnosticReport {
|
|
|
31
31
|
skipped: number;
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
declare const SDK_VERSION = "0.2.
|
|
34
|
+
declare const SDK_VERSION = "0.2.54";
|
|
35
35
|
declare function checkNodeVersion(): CheckResult;
|
|
36
36
|
declare function checkPlatform(): CheckResult;
|
|
37
37
|
declare function checkMemory(): CheckResult;
|
package/dist/diagnose.js
CHANGED
|
@@ -63,7 +63,7 @@ const localNetworkAccess_1 = require("./localNetworkAccess");
|
|
|
63
63
|
// ---------------------------------------------------------------------------
|
|
64
64
|
// SDK version — mirrors package.json; kept in sync by CI
|
|
65
65
|
// ---------------------------------------------------------------------------
|
|
66
|
-
const SDK_VERSION = '0.2.
|
|
66
|
+
const SDK_VERSION = '0.2.54';
|
|
67
67
|
exports._SDK_VERSION = SDK_VERSION;
|
|
68
68
|
// ---------------------------------------------------------------------------
|
|
69
69
|
// ANSI helpers (no external deps)
|
package/dist/localVlmBench.d.ts
CHANGED
|
@@ -104,6 +104,108 @@ export declare function interleaveArms(arms: BenchArm[], rounds: number): Planne
|
|
|
104
104
|
* 0 s/frame reads as "infinitely fast" and a NaN silently poisons a table.
|
|
105
105
|
*/
|
|
106
106
|
export declare function median(values: number[]): number | null;
|
|
107
|
+
/** The machine a measurement came off. Read from `os`, never asserted. */
|
|
108
|
+
export interface MachineDescriptor {
|
|
109
|
+
platform: string;
|
|
110
|
+
arch: string;
|
|
111
|
+
release: string;
|
|
112
|
+
cpuModel: string;
|
|
113
|
+
cores: number;
|
|
114
|
+
memGb: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Minimal `os` surface this needs. Injectable ONLY so the unit tests can drive
|
|
118
|
+
* a Windows/Linux descriptor from a Mac — production always passes the real
|
|
119
|
+
* `os` module.
|
|
120
|
+
*/
|
|
121
|
+
export interface OsLike {
|
|
122
|
+
platform(): string;
|
|
123
|
+
arch(): string;
|
|
124
|
+
release(): string;
|
|
125
|
+
cpus(): Array<{
|
|
126
|
+
model?: string;
|
|
127
|
+
}>;
|
|
128
|
+
totalmem(): number;
|
|
129
|
+
}
|
|
130
|
+
export declare function describeMachine(osLike: OsLike): MachineDescriptor;
|
|
131
|
+
/**
|
|
132
|
+
* A STABLE slug for "machines of this kind" — the join key between a measured
|
|
133
|
+
* row and a box we might sell. Deliberately excludes OS version and hostname:
|
|
134
|
+
* two runs of the same silicon a point release apart are the same machine
|
|
135
|
+
* class, and a hostname would make every CI runner its own class (a matrix with
|
|
136
|
+
* 200 one-row classes measures nothing).
|
|
137
|
+
*/
|
|
138
|
+
export declare function machineClassId(m: MachineDescriptor): string;
|
|
139
|
+
/**
|
|
140
|
+
* Analyses per hour one serve sustains at a given per-frame cost.
|
|
141
|
+
*
|
|
142
|
+
* `litert-lm serve` admits ONE request at a time, so this is the machine's
|
|
143
|
+
* whole sensing capacity — not a per-camera figure. `null` (never 0, never
|
|
144
|
+
* Infinity) for a non-positive input, because a fabricated capacity is how a
|
|
145
|
+
* machine that measured nothing ends up in a spec sheet.
|
|
146
|
+
*/
|
|
147
|
+
export declare function analysesPerHour(secondsPerFrame: number): number | null;
|
|
148
|
+
/**
|
|
149
|
+
* How many cameras this per-frame cost serves, if every camera must be looked
|
|
150
|
+
* at once per `cadenceSeconds`. FLOOR, not round: 3.8 cameras is 3 cameras and
|
|
151
|
+
* one that misses its cadence.
|
|
152
|
+
*/
|
|
153
|
+
export declare function camerasAtCadence(secondsPerFrame: number, cadenceSeconds: number): number | null;
|
|
154
|
+
/**
|
|
155
|
+
* The throughput floor a box must clear to serve `cameras` at `cadenceSeconds`.
|
|
156
|
+
*
|
|
157
|
+
* This is NOT a new capacity model — it is `connectorCadence`'s, solved for the
|
|
158
|
+
* machine instead of for the interval. That file derives
|
|
159
|
+
*
|
|
160
|
+
* H >= N x ANALYZE / SHARE
|
|
161
|
+
*
|
|
162
|
+
* (N feeds, one admission slot, SHARE of it reserved for the still-scene
|
|
163
|
+
* heartbeat so the rest stays free for motion-triggered alerts). Fix H at the
|
|
164
|
+
* interval the product must resolve and it becomes a statement about hardware:
|
|
165
|
+
*
|
|
166
|
+
* ANALYZE <= H x SHARE / N
|
|
167
|
+
*/
|
|
168
|
+
export interface HardwareFloor {
|
|
169
|
+
cameras: number;
|
|
170
|
+
cadenceSeconds: number;
|
|
171
|
+
/** Fraction of the single admission slot the still-scene floor may consume. */
|
|
172
|
+
slotShare: number;
|
|
173
|
+
/** The per-frame budget. A machine slower than this cannot make the cadence. */
|
|
174
|
+
maxSecondsPerFrame: number;
|
|
175
|
+
/** The same statement in the unit the capacity plan quotes. */
|
|
176
|
+
minAnalysesPerHour: number;
|
|
177
|
+
}
|
|
178
|
+
export declare function floorFor(cameras: number, cadenceSeconds: number, slotShare?: number): HardwareFloor | null;
|
|
179
|
+
export interface FloorVerdict {
|
|
180
|
+
clears: boolean;
|
|
181
|
+
secondsPerFrame: number;
|
|
182
|
+
camerasSupported: number;
|
|
183
|
+
analysesPerHour: number;
|
|
184
|
+
/** >1 = headroom, <1 = short. Reported so "barely" and "10x" are distinguishable. */
|
|
185
|
+
headroom: number;
|
|
186
|
+
reason: string;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Does this measured per-frame cost clear the floor? A machine with NO measured
|
|
190
|
+
* number does not "fail" — it returns null, because unmeasured and too-slow are
|
|
191
|
+
* different facts and collapsing them is exactly how windows-latest spent two
|
|
192
|
+
* months looking like a slow box instead of an unmeasured one.
|
|
193
|
+
*/
|
|
194
|
+
export declare function gradeAgainstFloor(secondsPerFrame: number | null, floor: HardwareFloor): FloorVerdict | null;
|
|
195
|
+
/**
|
|
196
|
+
* A time budget for a whole bench run, so a run that cannot finish still
|
|
197
|
+
* REPORTS what it measured. The Windows lane's entire history is runs that
|
|
198
|
+
* measured frames and then died at the job cap having written nothing: a
|
|
199
|
+
* partial matrix is a datapoint, a killed process is not.
|
|
200
|
+
*/
|
|
201
|
+
export declare function budgetRemaining(startedAtMs: number, budgetMs: number, nowMs: number): number;
|
|
202
|
+
/**
|
|
203
|
+
* Should the runner attempt one more frame? Refuses when the time left is less
|
|
204
|
+
* than what the last frames actually cost — starting a frame we cannot finish
|
|
205
|
+
* both wastes the remaining budget and records a DEADLINE as if it were a
|
|
206
|
+
* machine's speed.
|
|
207
|
+
*/
|
|
208
|
+
export declare function shouldAttemptAnother(remainingMs: number, observedFrameMs: number[], fallbackFrameMs: number): boolean;
|
|
107
209
|
/**
|
|
108
210
|
* Nearest-rank percentile of a sample. `null` on empty, for the same reason
|
|
109
211
|
* `median` returns null.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localVlmBench.d.ts","sourceRoot":"","sources":["../src/localVlmBench.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAEhC,uFAAuF;AACvF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,oBAAyB,GAAG,gBAAgB,CAQrF;AASD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,EAAE,CAwBV;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACrE;AAaD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,UAAU,CAUZ;AAED,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IACpC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE,CAU7E;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAMtD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQrE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC"}
|
|
1
|
+
{"version":3,"file":"localVlmBench.d.ts","sourceRoot":"","sources":["../src/localVlmBench.ts"],"names":[],"mappings":"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"}
|
package/dist/localVlmBench.js
CHANGED
|
@@ -6,6 +6,14 @@ exports.validateAgainstContract = validateAgainstContract;
|
|
|
6
6
|
exports.scoreFields = scoreFields;
|
|
7
7
|
exports.interleaveArms = interleaveArms;
|
|
8
8
|
exports.median = median;
|
|
9
|
+
exports.describeMachine = describeMachine;
|
|
10
|
+
exports.machineClassId = machineClassId;
|
|
11
|
+
exports.analysesPerHour = analysesPerHour;
|
|
12
|
+
exports.camerasAtCadence = camerasAtCadence;
|
|
13
|
+
exports.floorFor = floorFor;
|
|
14
|
+
exports.gradeAgainstFloor = gradeAgainstFloor;
|
|
15
|
+
exports.budgetRemaining = budgetRemaining;
|
|
16
|
+
exports.shouldAttemptAnother = shouldAttemptAnother;
|
|
9
17
|
exports.percentile = percentile;
|
|
10
18
|
/**
|
|
11
19
|
* localVlmBench — the reusable, unit-tested pieces of the LIVE on-device VLM
|
|
@@ -170,6 +178,127 @@ function median(values) {
|
|
|
170
178
|
const mid = Math.floor(sorted.length / 2);
|
|
171
179
|
return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
|
172
180
|
}
|
|
181
|
+
/** Trim the vendor noise x86 CPU strings carry ('(R)', '(TM)', 'CPU @ 2.60GHz'). */
|
|
182
|
+
function normaliseCpuModel(raw) {
|
|
183
|
+
return String(raw || 'unknown')
|
|
184
|
+
.replace(/\((R|TM|r|tm)\)/g, '')
|
|
185
|
+
.replace(/\bCPU\b/g, '')
|
|
186
|
+
.replace(/@\s*[\d.]+\s*GHz/i, '')
|
|
187
|
+
.replace(/\s+/g, ' ')
|
|
188
|
+
.trim() || 'unknown';
|
|
189
|
+
}
|
|
190
|
+
function describeMachine(osLike) {
|
|
191
|
+
const cpus = osLike.cpus() || [];
|
|
192
|
+
return {
|
|
193
|
+
platform: osLike.platform(),
|
|
194
|
+
arch: osLike.arch(),
|
|
195
|
+
release: osLike.release(),
|
|
196
|
+
cpuModel: normaliseCpuModel(cpus[0] && cpus[0].model ? cpus[0].model : 'unknown'),
|
|
197
|
+
cores: cpus.length,
|
|
198
|
+
memGb: Math.round((osLike.totalmem() / 1024 ** 3) * 10) / 10,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* A STABLE slug for "machines of this kind" — the join key between a measured
|
|
203
|
+
* row and a box we might sell. Deliberately excludes OS version and hostname:
|
|
204
|
+
* two runs of the same silicon a point release apart are the same machine
|
|
205
|
+
* class, and a hostname would make every CI runner its own class (a matrix with
|
|
206
|
+
* 200 one-row classes measures nothing).
|
|
207
|
+
*/
|
|
208
|
+
function machineClassId(m) {
|
|
209
|
+
const cpu = String(m.cpuModel || 'unknown').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
210
|
+
return `${m.platform}-${m.arch}-${cpu}-${m.cores}c-${Math.round(m.memGb)}g`;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Analyses per hour one serve sustains at a given per-frame cost.
|
|
214
|
+
*
|
|
215
|
+
* `litert-lm serve` admits ONE request at a time, so this is the machine's
|
|
216
|
+
* whole sensing capacity — not a per-camera figure. `null` (never 0, never
|
|
217
|
+
* Infinity) for a non-positive input, because a fabricated capacity is how a
|
|
218
|
+
* machine that measured nothing ends up in a spec sheet.
|
|
219
|
+
*/
|
|
220
|
+
function analysesPerHour(secondsPerFrame) {
|
|
221
|
+
if (!Number.isFinite(secondsPerFrame) || secondsPerFrame <= 0)
|
|
222
|
+
return null;
|
|
223
|
+
return Number((3600 / secondsPerFrame).toFixed(1));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* How many cameras this per-frame cost serves, if every camera must be looked
|
|
227
|
+
* at once per `cadenceSeconds`. FLOOR, not round: 3.8 cameras is 3 cameras and
|
|
228
|
+
* one that misses its cadence.
|
|
229
|
+
*/
|
|
230
|
+
function camerasAtCadence(secondsPerFrame, cadenceSeconds) {
|
|
231
|
+
if (!Number.isFinite(secondsPerFrame) || secondsPerFrame <= 0)
|
|
232
|
+
return null;
|
|
233
|
+
if (!Number.isFinite(cadenceSeconds) || cadenceSeconds <= 0)
|
|
234
|
+
return null;
|
|
235
|
+
return Math.floor(cadenceSeconds / secondsPerFrame);
|
|
236
|
+
}
|
|
237
|
+
function floorFor(cameras, cadenceSeconds, slotShare = 1) {
|
|
238
|
+
if (!Number.isFinite(cameras) || cameras <= 0)
|
|
239
|
+
return null;
|
|
240
|
+
if (!Number.isFinite(cadenceSeconds) || cadenceSeconds <= 0)
|
|
241
|
+
return null;
|
|
242
|
+
if (!Number.isFinite(slotShare) || slotShare <= 0 || slotShare > 1)
|
|
243
|
+
return null;
|
|
244
|
+
const budget = cadenceSeconds * slotShare;
|
|
245
|
+
return {
|
|
246
|
+
cameras,
|
|
247
|
+
cadenceSeconds,
|
|
248
|
+
slotShare,
|
|
249
|
+
maxSecondsPerFrame: Number((budget / cameras).toFixed(2)),
|
|
250
|
+
minAnalysesPerHour: Number(((cameras * 3600) / budget).toFixed(1)),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Does this measured per-frame cost clear the floor? A machine with NO measured
|
|
255
|
+
* number does not "fail" — it returns null, because unmeasured and too-slow are
|
|
256
|
+
* different facts and collapsing them is exactly how windows-latest spent two
|
|
257
|
+
* months looking like a slow box instead of an unmeasured one.
|
|
258
|
+
*/
|
|
259
|
+
function gradeAgainstFloor(secondsPerFrame, floor) {
|
|
260
|
+
if (secondsPerFrame === null || !Number.isFinite(secondsPerFrame) || secondsPerFrame <= 0)
|
|
261
|
+
return null;
|
|
262
|
+
const cameras = camerasAtCadence(secondsPerFrame, floor.cadenceSeconds * floor.slotShare);
|
|
263
|
+
const perHour = analysesPerHour(secondsPerFrame);
|
|
264
|
+
const clears = secondsPerFrame <= floor.maxSecondsPerFrame;
|
|
265
|
+
return {
|
|
266
|
+
clears,
|
|
267
|
+
secondsPerFrame,
|
|
268
|
+
camerasSupported: cameras,
|
|
269
|
+
analysesPerHour: perHour,
|
|
270
|
+
headroom: Number((floor.maxSecondsPerFrame / secondsPerFrame).toFixed(2)),
|
|
271
|
+
reason: clears
|
|
272
|
+
? `${secondsPerFrame}s/frame <= ${floor.maxSecondsPerFrame}s budget: serves ${cameras} cameras at a ${floor.cadenceSeconds}s cadence (target ${floor.cameras})`
|
|
273
|
+
: `${secondsPerFrame}s/frame EXCEEDS the ${floor.maxSecondsPerFrame}s budget: serves ${cameras} camera(s) at a ${floor.cadenceSeconds}s cadence, not ${floor.cameras}`,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* A time budget for a whole bench run, so a run that cannot finish still
|
|
278
|
+
* REPORTS what it measured. The Windows lane's entire history is runs that
|
|
279
|
+
* measured frames and then died at the job cap having written nothing: a
|
|
280
|
+
* partial matrix is a datapoint, a killed process is not.
|
|
281
|
+
*/
|
|
282
|
+
function budgetRemaining(startedAtMs, budgetMs, nowMs) {
|
|
283
|
+
if (!Number.isFinite(budgetMs) || budgetMs <= 0)
|
|
284
|
+
return Number.POSITIVE_INFINITY;
|
|
285
|
+
return budgetMs - (nowMs - startedAtMs);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Should the runner attempt one more frame? Refuses when the time left is less
|
|
289
|
+
* than what the last frames actually cost — starting a frame we cannot finish
|
|
290
|
+
* both wastes the remaining budget and records a DEADLINE as if it were a
|
|
291
|
+
* machine's speed.
|
|
292
|
+
*/
|
|
293
|
+
function shouldAttemptAnother(remainingMs, observedFrameMs, fallbackFrameMs) {
|
|
294
|
+
if (remainingMs === Number.POSITIVE_INFINITY)
|
|
295
|
+
return true;
|
|
296
|
+
if (remainingMs <= 0)
|
|
297
|
+
return false;
|
|
298
|
+
const worst = (observedFrameMs || []).filter((n) => Number.isFinite(n) && n > 0);
|
|
299
|
+
const need = worst.length ? Math.max(...worst) : fallbackFrameMs;
|
|
300
|
+
return remainingMs >= need;
|
|
301
|
+
}
|
|
173
302
|
/**
|
|
174
303
|
* Nearest-rank percentile of a sample. `null` on empty, for the same reason
|
|
175
304
|
* `median` returns null.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localVlmBench.js","sourceRoot":"","sources":["../src/localVlmBench.ts"],"names":[],"mappings":";;;AAkEA,4CAQC;AAeD,0DA2BC;AA0BD,kCAaC;AAqBD,wCAUC;AAMD,wBAMC;
|
|
1
|
+
{"version":3,"file":"localVlmBench.js","sourceRoot":"","sources":["../src/localVlmBench.ts"],"names":[],"mappings":"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"}
|