@trillboards/edge-sdk 0.2.3 → 0.2.6
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/README.md +4 -3
- package/deploy/docker/Dockerfile.cloud-sense +117 -0
- package/deploy/docker/Dockerfile.cpu +30 -21
- package/deploy/docker/Dockerfile.cuda +30 -21
- package/deploy/docker/Dockerfile.openvino +30 -20
- package/deploy/docker/README.md +9 -4
- package/deploy/docker/connector-entrypoint.sh +122 -0
- package/deploy/helm/README.md +1 -1
- package/deploy/k8s/daemonset.yaml +1 -1
- package/deploy/macos/README.md +161 -0
- package/deploy/macos/TrillboardsSetupApp.m +603 -0
- package/deploy/macos/build-macos-pkg.sh +588 -0
- package/deploy/macos/com.trillboards.edge.plist +62 -0
- package/deploy/macos/com.trillboards.litertlm.plist +45 -0
- package/deploy/macos/pair.command +519 -0
- package/deploy/macos/scripts/ensure-litertlm.sh +62 -0
- package/deploy/macos/scripts/postinstall +153 -0
- package/deploy/macos/scripts/setup-litertlm.sh +272 -0
- package/deploy/macos/test-install-pair.sh +383 -0
- package/deploy/macos/test-setup-litertlm-reinstall.sh +109 -0
- package/deploy/systemd/install.sh +25 -15
- package/deploy/systemd/trillboards-edge.service +4 -1
- package/deploy/windows/README.md +70 -0
- package/deploy/windows/build-windows.ps1 +225 -0
- package/deploy/windows/bundle-windows-installer.ps1 +153 -0
- package/deploy/windows/check-feeds.js +26 -0
- package/deploy/windows/e2e/fixtures/person.jpg +0 -0
- package/deploy/windows/e2e/test-local-sense.ps1 +185 -0
- package/deploy/windows/install-update.ps1 +101 -0
- package/deploy/windows/installer.nsi +138 -0
- package/deploy/windows/register-litertlm-task.ps1 +42 -0
- package/deploy/windows/register-task.ps1 +44 -0
- package/deploy/windows/run-connector.ps1 +69 -0
- package/deploy/windows/setup-litertlm.ps1 +361 -0
- package/deploy/windows/stop-existing.ps1 +72 -0
- package/deploy/windows/trillboards-edge-task.vbs +25 -0
- package/deploy/windows/trillboards-edge.cmd +33 -0
- package/deploy/windows/trillboards-pair.cmd +276 -0
- package/deploy/windows/trillboards-setup.ps1 +456 -0
- package/deploy/windows/trillboards-setup.vbs +13 -0
- package/dist/AudienceAnalyzeClient.d.ts +136 -0
- package/dist/AudienceAnalyzeClient.d.ts.map +1 -0
- package/dist/AudienceAnalyzeClient.js +246 -0
- package/dist/AudienceAnalyzeClient.js.map +1 -0
- package/dist/ChannelProber.d.ts +150 -0
- package/dist/ChannelProber.d.ts.map +1 -0
- package/dist/ChannelProber.js +887 -0
- package/dist/ChannelProber.js.map +1 -0
- package/dist/CloudEscalationPolicy.d.ts +143 -0
- package/dist/CloudEscalationPolicy.d.ts.map +1 -0
- package/dist/CloudEscalationPolicy.js +137 -0
- package/dist/CloudEscalationPolicy.js.map +1 -0
- package/dist/CloudSensingManager.d.ts +533 -0
- package/dist/CloudSensingManager.d.ts.map +1 -0
- package/dist/CloudSensingManager.js +1254 -0
- package/dist/CloudSensingManager.js.map +1 -0
- package/dist/DiscoveryReportClient.d.ts +66 -0
- package/dist/DiscoveryReportClient.d.ts.map +1 -0
- package/dist/DiscoveryReportClient.js +85 -0
- package/dist/DiscoveryReportClient.js.map +1 -0
- package/dist/EdgeAgent.d.ts +12 -2
- package/dist/EdgeAgent.d.ts.map +1 -1
- package/dist/EdgeAgent.js +122 -65
- package/dist/EdgeAgent.js.map +1 -1
- package/dist/LiteRtVlmProvider.d.ts +193 -0
- package/dist/LiteRtVlmProvider.d.ts.map +1 -0
- package/dist/LiteRtVlmProvider.js +436 -0
- package/dist/LiteRtVlmProvider.js.map +1 -0
- package/dist/MosaicSource.d.ts +108 -0
- package/dist/MosaicSource.d.ts.map +1 -0
- package/dist/MosaicSource.js +274 -0
- package/dist/MosaicSource.js.map +1 -0
- package/dist/OnvifClient.d.ts +83 -0
- package/dist/OnvifClient.d.ts.map +1 -0
- package/dist/OnvifClient.js +209 -0
- package/dist/OnvifClient.js.map +1 -0
- package/dist/PairClient.d.ts +106 -0
- package/dist/PairClient.d.ts.map +1 -0
- package/dist/PairClient.js +186 -0
- package/dist/PairClient.js.map +1 -0
- package/dist/RecorderAddressRecovery.d.ts +53 -0
- package/dist/RecorderAddressRecovery.d.ts.map +1 -0
- package/dist/RecorderAddressRecovery.js +359 -0
- package/dist/RecorderAddressRecovery.js.map +1 -0
- package/dist/SceneChangeDetector.d.ts +291 -0
- package/dist/SceneChangeDetector.d.ts.map +1 -0
- package/dist/SceneChangeDetector.js +682 -0
- package/dist/SceneChangeDetector.js.map +1 -0
- package/dist/SensingProfileClient.d.ts +59 -0
- package/dist/SensingProfileClient.d.ts.map +1 -0
- package/dist/SensingProfileClient.js +78 -0
- package/dist/SensingProfileClient.js.map +1 -0
- package/dist/SetupSessionClient.d.ts +114 -0
- package/dist/SetupSessionClient.d.ts.map +1 -0
- package/dist/SetupSessionClient.js +95 -0
- package/dist/SetupSessionClient.js.map +1 -0
- package/dist/VlmAdmissionGate.d.ts +126 -0
- package/dist/VlmAdmissionGate.d.ts.map +1 -0
- package/dist/VlmAdmissionGate.js +166 -0
- package/dist/VlmAdmissionGate.js.map +1 -0
- package/dist/autoSense.d.ts +216 -0
- package/dist/autoSense.d.ts.map +1 -0
- package/dist/autoSense.js +521 -0
- package/dist/autoSense.js.map +1 -0
- package/dist/cli.d.ts +27 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +511 -21
- package/dist/cli.js.map +1 -1
- package/dist/combinedSense.d.ts +128 -0
- package/dist/combinedSense.d.ts.map +1 -0
- package/dist/combinedSense.js +261 -0
- package/dist/combinedSense.js.map +1 -0
- package/dist/config.d.ts +351 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +99 -2
- package/dist/config.js.map +1 -1
- package/dist/connectorCadence.d.ts +135 -0
- package/dist/connectorCadence.d.ts.map +1 -0
- package/dist/connectorCadence.js +151 -0
- package/dist/connectorCadence.js.map +1 -0
- package/dist/dataDir.d.ts +33 -0
- package/dist/dataDir.d.ts.map +1 -0
- package/dist/dataDir.js +76 -0
- package/dist/dataDir.js.map +1 -0
- package/dist/deployedCadence.d.ts +167 -0
- package/dist/deployedCadence.d.ts.map +1 -0
- package/dist/deployedCadence.js +209 -0
- package/dist/deployedCadence.js.map +1 -0
- package/dist/deployedCadenceSource.d.ts +48 -0
- package/dist/deployedCadenceSource.d.ts.map +1 -0
- package/dist/deployedCadenceSource.js +74 -0
- package/dist/deployedCadenceSource.js.map +1 -0
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.d.ts.map +1 -1
- package/dist/diagnose.js +13 -12
- package/dist/diagnose.js.map +1 -1
- package/dist/discovery.d.ts +131 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +499 -0
- package/dist/discovery.js.map +1 -0
- package/dist/gemmaModelOta.d.ts +118 -0
- package/dist/gemmaModelOta.d.ts.map +1 -0
- package/dist/gemmaModelOta.js +291 -0
- package/dist/gemmaModelOta.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +41 -18
- package/dist/init.js.map +1 -1
- package/dist/keepAwake.d.ts +33 -0
- package/dist/keepAwake.d.ts.map +1 -0
- package/dist/keepAwake.js +54 -0
- package/dist/keepAwake.js.map +1 -0
- package/dist/legacyRecorderProfile.d.ts +20 -0
- package/dist/legacyRecorderProfile.d.ts.map +1 -0
- package/dist/legacyRecorderProfile.js +27 -0
- package/dist/legacyRecorderProfile.js.map +1 -0
- package/dist/localInferenceSchema.d.ts +132 -0
- package/dist/localInferenceSchema.d.ts.map +1 -0
- package/dist/localInferenceSchema.js +274 -0
- package/dist/localInferenceSchema.js.map +1 -0
- package/dist/localSenseE2eHarness.d.ts +101 -0
- package/dist/localSenseE2eHarness.d.ts.map +1 -0
- package/dist/localSenseE2eHarness.js +279 -0
- package/dist/localSenseE2eHarness.js.map +1 -0
- package/dist/localSensing.d.ts +268 -0
- package/dist/localSensing.d.ts.map +1 -0
- package/dist/localSensing.js +492 -0
- package/dist/localSensing.js.map +1 -0
- package/dist/modelIntegrity.d.ts +59 -0
- package/dist/modelIntegrity.d.ts.map +1 -0
- package/dist/modelIntegrity.js +165 -0
- package/dist/modelIntegrity.js.map +1 -0
- package/dist/pairVerify.d.ts +100 -0
- package/dist/pairVerify.d.ts.map +1 -0
- package/dist/pairVerify.js +296 -0
- package/dist/pairVerify.js.map +1 -0
- package/dist/qrScanner.d.ts +33 -0
- package/dist/qrScanner.d.ts.map +1 -0
- package/dist/qrScanner.js +103 -0
- package/dist/qrScanner.js.map +1 -0
- package/dist/runtimePaths.d.ts +10 -0
- package/dist/runtimePaths.d.ts.map +1 -0
- package/dist/runtimePaths.js +79 -0
- package/dist/runtimePaths.js.map +1 -0
- package/dist/sensingRunMode.d.ts +68 -0
- package/dist/sensingRunMode.d.ts.map +1 -0
- package/dist/sensingRunMode.js +346 -0
- package/dist/sensingRunMode.js.map +1 -0
- package/dist/setupMode.d.ts +98 -0
- package/dist/setupMode.d.ts.map +1 -0
- package/dist/setupMode.js +828 -0
- package/dist/setupMode.js.map +1 -0
- package/dist/setupProgress.d.ts +31 -0
- package/dist/setupProgress.d.ts.map +1 -0
- package/dist/setupProgress.js +173 -0
- package/dist/setupProgress.js.map +1 -0
- package/package.json +13 -4
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.keepAwakeCommand = keepAwakeCommand;
|
|
4
|
+
exports.startKeepAwake = startKeepAwake;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
/** Build the platform command (exported for tests — pure). */
|
|
7
|
+
function keepAwakeCommand(platform, pid) {
|
|
8
|
+
if (platform === 'darwin') {
|
|
9
|
+
return { cmd: 'caffeinate', args: ['-i', '-w', String(pid)] };
|
|
10
|
+
}
|
|
11
|
+
if (platform === 'win32') {
|
|
12
|
+
const script = `$sig='[DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint esFlags);';` +
|
|
13
|
+
`Add-Type -MemberDefinition $sig -Name P -Namespace TrillboardsKeepAwake;` +
|
|
14
|
+
`while (Get-Process -Id ${pid} -ErrorAction SilentlyContinue) {` +
|
|
15
|
+
`[TrillboardsKeepAwake.P]::SetThreadExecutionState([uint32]'0x80000001') | Out-Null;` +
|
|
16
|
+
`Start-Sleep -Seconds 50 }`;
|
|
17
|
+
return { cmd: 'powershell', args: ['-NoProfile', '-NonInteractive', '-Command', script] };
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function startKeepAwake(platform = process.platform, pid = process.pid, spawner = child_process_1.spawn) {
|
|
22
|
+
const command = keepAwakeCommand(platform, pid);
|
|
23
|
+
if (!command)
|
|
24
|
+
return { child: null, stop: () => undefined };
|
|
25
|
+
let child = null;
|
|
26
|
+
try {
|
|
27
|
+
child = spawner(command.cmd, command.args, { stdio: 'ignore' });
|
|
28
|
+
// Never let the helper hold the event loop open or crash the connector.
|
|
29
|
+
child.unref();
|
|
30
|
+
child.on('error', () => {
|
|
31
|
+
child = null;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
child = null;
|
|
36
|
+
}
|
|
37
|
+
if (child) {
|
|
38
|
+
console.log('Keep-awake active: this computer will not sleep while sensing runs.');
|
|
39
|
+
}
|
|
40
|
+
const stop = () => {
|
|
41
|
+
if (child && !child.killed) {
|
|
42
|
+
try {
|
|
43
|
+
child.kill();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* already gone */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
// Release on clean exit; the pid-watch (win32) / -w pid (darwin) covers crashes.
|
|
51
|
+
process.once('exit', stop);
|
|
52
|
+
return { child, stop };
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=keepAwake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keepAwake.js","sourceRoot":"","sources":["../src/keepAwake.ts"],"names":[],"mappings":";;AA8BA,4CAiBC;AAED,wCAmCC;AApFD,iDAAyD;AA6BzD,8DAA8D;AAC9D,SAAgB,gBAAgB,CAC9B,QAAyB,EACzB,GAAW;IAEX,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;IAChE,CAAC;IACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,MAAM,MAAM,GACV,sGAAsG;YACtG,0EAA0E;YAC1E,0BAA0B,GAAG,mCAAmC;YAChE,qFAAqF;YACrF,2BAA2B,CAAC;QAC9B,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,cAAc,CAC5B,WAA4B,OAAO,CAAC,QAAQ,EAC5C,MAAc,OAAO,CAAC,GAAG,EACzB,UAAwB,qBAAK;IAE7B,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;IAE5D,IAAI,KAAK,GAAwB,IAAI,CAAC;IACtC,IAAI,CAAC;QACH,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChE,wEAAwE;QACxE,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,KAAK,GAAG,IAAI,CAAC;IACf,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,CAAC;YAAC,MAAM,CAAC;gBACP,kBAAkB;YACpB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,iFAAiF;IACjF,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3B,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conservative defaults for DVR/NVR feeds. Recorders tend to have fewer RTSP
|
|
3
|
+
* session slots and slower/noisier codec startup than standalone IP cameras,
|
|
4
|
+
* so setup marks them and runtime applies this profile automatically.
|
|
5
|
+
*/
|
|
6
|
+
export declare const LEGACY_RECORDER_RUNTIME_PROFILE: {
|
|
7
|
+
readonly timeoutUs: 18000000;
|
|
8
|
+
readonly openValidationAttempts: 6;
|
|
9
|
+
readonly transientRetryDelayMs: 2000;
|
|
10
|
+
readonly feedStaggerMs: 15000;
|
|
11
|
+
readonly maxFeedStaggerMs: 90000;
|
|
12
|
+
readonly cameraHealthFailureGraceCount: 5;
|
|
13
|
+
};
|
|
14
|
+
export type CameraSourceType = 'recorder' | 'direct_url' | undefined;
|
|
15
|
+
export declare function legacyRecorderRtspOptions(sourceType: CameraSourceType): {
|
|
16
|
+
timeoutUs?: number;
|
|
17
|
+
openValidationAttempts?: number;
|
|
18
|
+
transientRetryDelayMs?: number;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=legacyRecorderProfile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacyRecorderProfile.d.ts","sourceRoot":"","sources":["../src/legacyRecorderProfile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,+BAA+B;;;;;;;CAOlC,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;AAErE,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,gBAAgB,GAAG;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAOA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LEGACY_RECORDER_RUNTIME_PROFILE = void 0;
|
|
4
|
+
exports.legacyRecorderRtspOptions = legacyRecorderRtspOptions;
|
|
5
|
+
/**
|
|
6
|
+
* Conservative defaults for DVR/NVR feeds. Recorders tend to have fewer RTSP
|
|
7
|
+
* session slots and slower/noisier codec startup than standalone IP cameras,
|
|
8
|
+
* so setup marks them and runtime applies this profile automatically.
|
|
9
|
+
*/
|
|
10
|
+
exports.LEGACY_RECORDER_RUNTIME_PROFILE = {
|
|
11
|
+
timeoutUs: 18000000,
|
|
12
|
+
openValidationAttempts: 6,
|
|
13
|
+
transientRetryDelayMs: 2000,
|
|
14
|
+
feedStaggerMs: 15000,
|
|
15
|
+
maxFeedStaggerMs: 90000,
|
|
16
|
+
cameraHealthFailureGraceCount: 5,
|
|
17
|
+
};
|
|
18
|
+
function legacyRecorderRtspOptions(sourceType) {
|
|
19
|
+
if (sourceType !== 'recorder')
|
|
20
|
+
return {};
|
|
21
|
+
return {
|
|
22
|
+
timeoutUs: exports.LEGACY_RECORDER_RUNTIME_PROFILE.timeoutUs,
|
|
23
|
+
openValidationAttempts: exports.LEGACY_RECORDER_RUNTIME_PROFILE.openValidationAttempts,
|
|
24
|
+
transientRetryDelayMs: exports.LEGACY_RECORDER_RUNTIME_PROFILE.transientRetryDelayMs,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=legacyRecorderProfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacyRecorderProfile.js","sourceRoot":"","sources":["../src/legacyRecorderProfile.ts"],"names":[],"mappings":";;;AAgBA,8DAWC;AA3BD;;;;GAIG;AACU,QAAA,+BAA+B,GAAG;IAC7C,SAAS,EAAE,QAAU;IACrB,sBAAsB,EAAE,CAAC;IACzB,qBAAqB,EAAE,IAAK;IAC5B,aAAa,EAAE,KAAM;IACrB,gBAAgB,EAAE,KAAM;IACxB,6BAA6B,EAAE,CAAC;CACxB,CAAC;AAIX,SAAgB,yBAAyB,CAAC,UAA4B;IAKpE,IAAI,UAAU,KAAK,UAAU;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO;QACL,SAAS,EAAE,uCAA+B,CAAC,SAAS;QACpD,sBAAsB,EAAE,uCAA+B,CAAC,sBAAsB;QAC9E,qBAAqB,EAAE,uCAA+B,CAAC,qBAAqB;KAC7E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* localInferenceSchema — the route-addressable JSON Schema + prompt the
|
|
3
|
+
* on-device VLM (LiteRtVlmProvider) is constrained to. This is the subset of
|
|
4
|
+
* the cloud `buildProfileDrivenJsonSchema` contract that drives OFV →
|
|
5
|
+
* Sense-Anything routes → WhatsApp: the DEFAULT audience profile fields
|
|
6
|
+
* (sensingProfileService CLOUD_EMIT_AUDIENCE_FIELDS) + the scalar scene fields.
|
|
7
|
+
*
|
|
8
|
+
* ── WHY PLAIN-COMPLETION + NON-NULLABLE (the on-device runtime reality):
|
|
9
|
+
* LiteRT-LM (`litert-lm serve` 0.13.1) exposes constrained TOOL-CALLING, but its
|
|
10
|
+
* ANTLR FC tool-call parser 500s whenever Gemma 4 E2B serializes a string with
|
|
11
|
+
* the special `<|"|>` quote token instead of a literal `"`. That happens
|
|
12
|
+
* reproducibly on whole classes of frames — measured 0/5 on an empty-room frame
|
|
13
|
+
* across EVERY tool-call shape (full schema, no-free-text, temp 0.8, even
|
|
14
|
+
* `response_format`). Empty scenes dominate real CCTV, so tool-calling is not
|
|
15
|
+
* viable. The SAME frame returns clean, valid JSON via PLAIN completion (no
|
|
16
|
+
* tools) — measured 8/8 — because the `<|"|>` token is injected only by the
|
|
17
|
+
* tool-call/constrained path. So LiteRtVlmProvider renders THIS schema as a
|
|
18
|
+
* human-readable key list, asks for a bare JSON object, and validates the result
|
|
19
|
+
* client-side (required keys + enum membership) with a retry. That is where the
|
|
20
|
+
* structured-output GUARANTEE comes from now — client validation, not the
|
|
21
|
+
* (broken) server grammar. Keep every field NON-nullable with a sentinel value
|
|
22
|
+
* ('none'/'unclear'/''/0) for "no people"; the localInferenceSchema.test.ts
|
|
23
|
+
* guard fails the build if a nullable type/enum is reintroduced.
|
|
24
|
+
*
|
|
25
|
+
* ── WHY THE FIELD SET IS A PROPERTY OF THE PROFILE (not a global constant):
|
|
26
|
+
* an SOP/operator profile asks an operational question about the operator's OWN
|
|
27
|
+
* venue, and the 12 DOOH audience fields answer none of it — two of them
|
|
28
|
+
* (`scene_description`, `audience_engagement_narrative`) are free-text paragraphs
|
|
29
|
+
* the model must DECODE on every frame. MEASURED through this exact code on an
|
|
30
|
+
* M4 Max, real kitchen frame at production capture params (640x480 JPEG q70),
|
|
31
|
+
* arms INTERLEAVED so machine load lands on both equally:
|
|
32
|
+
*
|
|
33
|
+
* delegate 12 DOOH + 4 operator operator set (7 keys) ratio
|
|
34
|
+
* CPU 39.22 s/frame (n=4) 25.68 s/frame (n=4) 1.53x
|
|
35
|
+
* GPU 10.66 s/frame (n=5) 9.81 s/frame (n=5) 1.09x
|
|
36
|
+
*
|
|
37
|
+
* The GPU win is small because litert-lm pins the VISION encoder to CPU
|
|
38
|
+
* (openai_handler.py `vision_backend = Backend.CPU()`), so image encode is a
|
|
39
|
+
* fixed per-frame floor no schema can shrink; on the CPU delegate — every host
|
|
40
|
+
* with no usable GPU delegate — decode is the bill and the lean set is worth
|
|
41
|
+
* 1.53x. ACCURACY also moved the right way on that run: against the frame's
|
|
42
|
+
* ground truth the operator set scored 16/16 and the 16-key set 12/16, the
|
|
43
|
+
* 16-key arm answering `gloves_worn: true` on a scene where only SOME staff are
|
|
44
|
+
* gloved while the lean arm answered `false`. (On GPU both scored 15/20 — no
|
|
45
|
+
* accuracy difference there.) Latency is therefore a secondary reason to split
|
|
46
|
+
* the sets; the primary ones are asking the operator's question instead of a
|
|
47
|
+
* media buyer's, and keeping `audience_ethnicity` off a camera pointed at the
|
|
48
|
+
* customer's own staff. See LocalSchemaFieldSet.
|
|
49
|
+
*
|
|
50
|
+
* NOTE: the connector SUPPLIES `source='vlm_edge'` and `captured_at` itself
|
|
51
|
+
* (provenance + time are not the model's to decide) — they are deliberately NOT
|
|
52
|
+
* in this schema. The server additionally forces provenance `local_connector`
|
|
53
|
+
* on the preAnalyzed ingest path (gated OUT of the sold bidstream).
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* A custom operator (Sense-Anything) field from the deployed profile's
|
|
57
|
+
* `metrics_schema`. The connector fetches these from the server
|
|
58
|
+
* (`GET /v2/earner/cameras/sensing-profile`) so the on-device VLM emits the SAME
|
|
59
|
+
* operator-declared fields the cloud VLM does — reaching parity with
|
|
60
|
+
* `buildProfileDrivenJsonSchema` (otherwise an alert keyed on e.g. `queue_length`
|
|
61
|
+
* never fires on-device because Gemma was never told to emit it).
|
|
62
|
+
*/
|
|
63
|
+
export interface ProfileField {
|
|
64
|
+
field: string;
|
|
65
|
+
/** Profile field type (sensingIntentService MetricsSchemaEntrySchema). */
|
|
66
|
+
type: 'counter' | 'gauge' | 'boolean' | 'enum' | 'category' | 'string' | 'list' | 'distribution' | 'hidden';
|
|
67
|
+
enum_options?: string[];
|
|
68
|
+
description?: string;
|
|
69
|
+
example_outputs?: Array<string | number | boolean>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Which DEFAULT field block the on-device schema carries. This is a property of
|
|
73
|
+
* the DEPLOYED PROFILE, resolved from the profile's own self-describing type
|
|
74
|
+
* (`sensing_profiles.profile_type`) — see localSensing.fieldSetForProfileType.
|
|
75
|
+
*
|
|
76
|
+
* - `'audience'` — a DOOH screen/audience-measurement profile (the seeded
|
|
77
|
+
* builtins: retail_full_v1, convenience_store_v1, transit_hub_v1, …), and the
|
|
78
|
+
* default when NO profile is deployed. These exist to describe the AUDIENCE
|
|
79
|
+
* in front of a screen for a media buyer, so they carry the full cloud-parity
|
|
80
|
+
* audience block (the 12 fields below).
|
|
81
|
+
* - `'operator'` — a Sense-Anything profile (`profile_type='custom_intent'`;
|
|
82
|
+
* 147 of the 157 active profiles as of 2026-08-11). The operator asked a
|
|
83
|
+
* specific question about their OWN venue ("is everyone handling food wearing
|
|
84
|
+
* gloves?"). The DOOH audience block answers nothing they asked and costs
|
|
85
|
+
* 1.09-1.53x the per-frame latency depending on delegate (see the file
|
|
86
|
+
* header), so an operator profile emits its OWN declared fields plus the
|
|
87
|
+
* minimal presence primitives — nothing else.
|
|
88
|
+
*/
|
|
89
|
+
export type LocalSchemaFieldSet = 'audience' | 'operator';
|
|
90
|
+
export interface LocalSensingSchemaOptions {
|
|
91
|
+
/**
|
|
92
|
+
* Which default field block to emit. Defaults to `'audience'` — the DOOH
|
|
93
|
+
* contract, and the correct answer when no profile is deployed.
|
|
94
|
+
*/
|
|
95
|
+
fieldSet?: LocalSchemaFieldSet;
|
|
96
|
+
/** Extra operator (Sense-Anything) field keys to also request, e.g. ['queue_length']. */
|
|
97
|
+
extraBooleanFields?: string[];
|
|
98
|
+
extraCounterFields?: string[];
|
|
99
|
+
/**
|
|
100
|
+
* Typed custom profile fields (preferred) — the active Sense-Anything profile's
|
|
101
|
+
* `metrics_schema`. Each is appended to the constrained schema with the right
|
|
102
|
+
* JSON type so Gemma emits it and the operator's route can evaluate it.
|
|
103
|
+
*/
|
|
104
|
+
extraFields?: ProfileField[];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Build the JSON Schema the on-device model is asked to fill (rendered as a
|
|
108
|
+
* readable key list by LiteRtVlmProvider, validated client-side). All fields
|
|
109
|
+
* non-nullable + required — see the file header for why null is forbidden.
|
|
110
|
+
*
|
|
111
|
+
* ONE builder, parameterized by `fieldSet` — an audience/DOOH profile gets the
|
|
112
|
+
* full 12; a Sense-Anything (operator) profile gets the presence primitives plus
|
|
113
|
+
* its own declared fields, and never `audience_ethnicity`.
|
|
114
|
+
*/
|
|
115
|
+
export declare function buildLocalSensingSchema(options?: LocalSensingSchemaOptions): Record<string, unknown>;
|
|
116
|
+
/**
|
|
117
|
+
* The analysis prompt (mirrors audienceVisionService buildProfileDrivenPrompt's
|
|
118
|
+
* role). When the active Sense-Anything profile declares custom fields, append a
|
|
119
|
+
* terse instruction for each so Gemma knows what to observe — the on-device
|
|
120
|
+
* equivalent of the cloud profile-driven prompt. Kept lean (the verbose cloud
|
|
121
|
+
* phrasing reliably destabilized the on-device decode — see file header).
|
|
122
|
+
*
|
|
123
|
+
* `fieldSet` must match the schema's: the audience prompt tells Gemma which
|
|
124
|
+
* SENTINEL to use for each audience_* field on an empty scene, and on the
|
|
125
|
+
* operator path those fields do not exist, so repeating that instruction would
|
|
126
|
+
* spend decode budget describing keys the model must not emit.
|
|
127
|
+
*/
|
|
128
|
+
export declare function buildLocalSensingPrompt(options?: {
|
|
129
|
+
fieldSet?: LocalSchemaFieldSet;
|
|
130
|
+
extraFields?: ProfileField[];
|
|
131
|
+
}): string;
|
|
132
|
+
//# sourceMappingURL=localInferenceSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localInferenceSchema.d.ts","sourceRoot":"","sources":["../src/localInferenceSchema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,cAAc,GAAG,QAAQ,CAAC;IAC5G,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACpD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,CAAC;AAE1D,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,yFAAyF;IACzF,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;CAC9B;AAqID;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgCzB;AAyBD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAAC,WAAW,CAAC,EAAE,YAAY,EAAE,CAAA;CAAO,GAC7E,MAAM,CAeR"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* localInferenceSchema — the route-addressable JSON Schema + prompt the
|
|
4
|
+
* on-device VLM (LiteRtVlmProvider) is constrained to. This is the subset of
|
|
5
|
+
* the cloud `buildProfileDrivenJsonSchema` contract that drives OFV →
|
|
6
|
+
* Sense-Anything routes → WhatsApp: the DEFAULT audience profile fields
|
|
7
|
+
* (sensingProfileService CLOUD_EMIT_AUDIENCE_FIELDS) + the scalar scene fields.
|
|
8
|
+
*
|
|
9
|
+
* ── WHY PLAIN-COMPLETION + NON-NULLABLE (the on-device runtime reality):
|
|
10
|
+
* LiteRT-LM (`litert-lm serve` 0.13.1) exposes constrained TOOL-CALLING, but its
|
|
11
|
+
* ANTLR FC tool-call parser 500s whenever Gemma 4 E2B serializes a string with
|
|
12
|
+
* the special `<|"|>` quote token instead of a literal `"`. That happens
|
|
13
|
+
* reproducibly on whole classes of frames — measured 0/5 on an empty-room frame
|
|
14
|
+
* across EVERY tool-call shape (full schema, no-free-text, temp 0.8, even
|
|
15
|
+
* `response_format`). Empty scenes dominate real CCTV, so tool-calling is not
|
|
16
|
+
* viable. The SAME frame returns clean, valid JSON via PLAIN completion (no
|
|
17
|
+
* tools) — measured 8/8 — because the `<|"|>` token is injected only by the
|
|
18
|
+
* tool-call/constrained path. So LiteRtVlmProvider renders THIS schema as a
|
|
19
|
+
* human-readable key list, asks for a bare JSON object, and validates the result
|
|
20
|
+
* client-side (required keys + enum membership) with a retry. That is where the
|
|
21
|
+
* structured-output GUARANTEE comes from now — client validation, not the
|
|
22
|
+
* (broken) server grammar. Keep every field NON-nullable with a sentinel value
|
|
23
|
+
* ('none'/'unclear'/''/0) for "no people"; the localInferenceSchema.test.ts
|
|
24
|
+
* guard fails the build if a nullable type/enum is reintroduced.
|
|
25
|
+
*
|
|
26
|
+
* ── WHY THE FIELD SET IS A PROPERTY OF THE PROFILE (not a global constant):
|
|
27
|
+
* an SOP/operator profile asks an operational question about the operator's OWN
|
|
28
|
+
* venue, and the 12 DOOH audience fields answer none of it — two of them
|
|
29
|
+
* (`scene_description`, `audience_engagement_narrative`) are free-text paragraphs
|
|
30
|
+
* the model must DECODE on every frame. MEASURED through this exact code on an
|
|
31
|
+
* M4 Max, real kitchen frame at production capture params (640x480 JPEG q70),
|
|
32
|
+
* arms INTERLEAVED so machine load lands on both equally:
|
|
33
|
+
*
|
|
34
|
+
* delegate 12 DOOH + 4 operator operator set (7 keys) ratio
|
|
35
|
+
* CPU 39.22 s/frame (n=4) 25.68 s/frame (n=4) 1.53x
|
|
36
|
+
* GPU 10.66 s/frame (n=5) 9.81 s/frame (n=5) 1.09x
|
|
37
|
+
*
|
|
38
|
+
* The GPU win is small because litert-lm pins the VISION encoder to CPU
|
|
39
|
+
* (openai_handler.py `vision_backend = Backend.CPU()`), so image encode is a
|
|
40
|
+
* fixed per-frame floor no schema can shrink; on the CPU delegate — every host
|
|
41
|
+
* with no usable GPU delegate — decode is the bill and the lean set is worth
|
|
42
|
+
* 1.53x. ACCURACY also moved the right way on that run: against the frame's
|
|
43
|
+
* ground truth the operator set scored 16/16 and the 16-key set 12/16, the
|
|
44
|
+
* 16-key arm answering `gloves_worn: true` on a scene where only SOME staff are
|
|
45
|
+
* gloved while the lean arm answered `false`. (On GPU both scored 15/20 — no
|
|
46
|
+
* accuracy difference there.) Latency is therefore a secondary reason to split
|
|
47
|
+
* the sets; the primary ones are asking the operator's question instead of a
|
|
48
|
+
* media buyer's, and keeping `audience_ethnicity` off a camera pointed at the
|
|
49
|
+
* customer's own staff. See LocalSchemaFieldSet.
|
|
50
|
+
*
|
|
51
|
+
* NOTE: the connector SUPPLIES `source='vlm_edge'` and `captured_at` itself
|
|
52
|
+
* (provenance + time are not the model's to decide) — they are deliberately NOT
|
|
53
|
+
* in this schema. The server additionally forces provenance `local_connector`
|
|
54
|
+
* on the preAnalyzed ingest path (gated OUT of the sold bidstream).
|
|
55
|
+
*/
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.buildLocalSensingSchema = buildLocalSensingSchema;
|
|
58
|
+
exports.buildLocalSensingPrompt = buildLocalSensingPrompt;
|
|
59
|
+
/**
|
|
60
|
+
* The presence primitives EVERY profile reports, whatever its field set. They
|
|
61
|
+
* are the connector's floor:
|
|
62
|
+
* - `people_present` / `person_count` are what the server's preAnalyzed
|
|
63
|
+
* handler grades the observation on (earner-misc.analyzeAudienceFrame reads
|
|
64
|
+
* `preAnalyzed.people_present === true || Number(preAnalyzed.person_count) >= 1`
|
|
65
|
+
* to mark it decisionable `screen_audience`), so dropping them would
|
|
66
|
+
* silently downgrade every local observation;
|
|
67
|
+
* - `confidence` is the model's own certainty and rides the unified contract.
|
|
68
|
+
* They are also what keeps `localSensing.isAlertWorthy` able to tell an empty
|
|
69
|
+
* scene from a busy one on the lean operator path.
|
|
70
|
+
*/
|
|
71
|
+
const PRESENCE_PRIMITIVE_ORDER = ['people_present', 'person_count', 'confidence'];
|
|
72
|
+
/**
|
|
73
|
+
* The DOOH/audience field set, in the exact order the prompt renders it. Order
|
|
74
|
+
* is load-bearing only in that it is the wire order Gemma was benchmarked
|
|
75
|
+
* against — keep it stable unless you re-measure.
|
|
76
|
+
*/
|
|
77
|
+
const AUDIENCE_FIELD_ORDER = [
|
|
78
|
+
'audience_group_composition',
|
|
79
|
+
'audience_intent_stage',
|
|
80
|
+
'audience_activity_macro',
|
|
81
|
+
'audience_ethnicity',
|
|
82
|
+
'audience_engagement_narrative',
|
|
83
|
+
'person_count',
|
|
84
|
+
'crowd_density',
|
|
85
|
+
'safety_concern',
|
|
86
|
+
'scene_description',
|
|
87
|
+
'ad_receptivity_score',
|
|
88
|
+
'people_present',
|
|
89
|
+
'confidence',
|
|
90
|
+
];
|
|
91
|
+
/**
|
|
92
|
+
* The property spec for every DEFAULT field, declared ONCE. `buildLocalSensingSchema`
|
|
93
|
+
* composes a field set out of this table rather than forking into two builders.
|
|
94
|
+
* All non-nullable — see the file header for why null is forbidden.
|
|
95
|
+
*/
|
|
96
|
+
const DEFAULT_FIELD_PROPS = {
|
|
97
|
+
// DEFAULT audience profile fields (route-addressable enums). Each enum that
|
|
98
|
+
// had no "no people" member in the cloud contract gains a 'none' sentinel
|
|
99
|
+
// so the model never has to emit null when the scene is empty.
|
|
100
|
+
audience_group_composition: {
|
|
101
|
+
type: 'string',
|
|
102
|
+
enum: ['solo', 'couple', 'family', 'friend_group', 'work_team', 'mixed', 'none'],
|
|
103
|
+
},
|
|
104
|
+
audience_intent_stage: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
enum: ['browsing', 'researching', 'comparing', 'ready_to_buy', 'post_purchase', 'none'],
|
|
107
|
+
},
|
|
108
|
+
audience_activity_macro: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
enum: [
|
|
111
|
+
'focused_work', 'personal_downtime', 'social_dwell', 'transactional',
|
|
112
|
+
'decision_making', 'passing_through', 'queueing', 'none',
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
audience_ethnicity: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
enum: [
|
|
118
|
+
'asian', 'black', 'hispanic', 'middle_eastern', 'south_asian',
|
|
119
|
+
'southeast_asian', 'white', 'mixed', 'other', 'unclear',
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
// One-sentence advertiser-actionable summary; "" when no people present.
|
|
123
|
+
audience_engagement_narrative: { type: 'string' },
|
|
124
|
+
// Scene-level fields the CLOUD VLM ALWAYS emits (an empty room is
|
|
125
|
+
// scene_description="…", crowd_density="empty", person_count=0).
|
|
126
|
+
person_count: { type: 'integer' },
|
|
127
|
+
crowd_density: {
|
|
128
|
+
type: 'string',
|
|
129
|
+
enum: ['empty', 'sparse', 'moderate', 'crowded', 'very_crowded'],
|
|
130
|
+
},
|
|
131
|
+
safety_concern: { type: 'boolean' },
|
|
132
|
+
scene_description: { type: 'string' },
|
|
133
|
+
// Receptivity in [0,1]; 0 when no people present (non-nullable — see header).
|
|
134
|
+
ad_receptivity_score: { type: 'number' },
|
|
135
|
+
people_present: { type: 'boolean' },
|
|
136
|
+
confidence: { type: 'number' },
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* NEVER asked of an OPERATOR (Sense-Anything) profile, at any cost — not as a
|
|
140
|
+
* default field and not via a profile's own `metrics_schema`.
|
|
141
|
+
*
|
|
142
|
+
* `audience_ethnicity` is a DOOH REACH metric: it exists so a media buyer can
|
|
143
|
+
* describe the anonymous audience walking past a screen, and it is legal-approved
|
|
144
|
+
* for exactly that. An SOP/compliance deployment points the SAME camera at the
|
|
145
|
+
* customer's OWN EMPLOYEES, where an ethnicity estimate has no operator use and
|
|
146
|
+
* is a straight liability. So the operator path drops it entirely rather than
|
|
147
|
+
* relying on a profile never happening to declare it.
|
|
148
|
+
*/
|
|
149
|
+
const OPERATOR_FORBIDDEN_FIELDS = new Set(['audience_ethnicity']);
|
|
150
|
+
/** Map a profile field's type → its JSON-schema property (non-nullable). */
|
|
151
|
+
function profileFieldToSchemaProp(f) {
|
|
152
|
+
switch (f.type) {
|
|
153
|
+
case 'hidden':
|
|
154
|
+
return null; // internal — never asked of the model
|
|
155
|
+
case 'counter':
|
|
156
|
+
return { type: 'integer' };
|
|
157
|
+
case 'gauge':
|
|
158
|
+
return { type: 'number' };
|
|
159
|
+
case 'boolean':
|
|
160
|
+
return { type: 'boolean' };
|
|
161
|
+
case 'enum':
|
|
162
|
+
case 'category':
|
|
163
|
+
if (Array.isArray(f.enum_options) && f.enum_options.length >= 1) {
|
|
164
|
+
return { type: 'string', enum: f.enum_options };
|
|
165
|
+
}
|
|
166
|
+
return { type: 'string' };
|
|
167
|
+
case 'list':
|
|
168
|
+
return { type: 'array', items: { type: 'string' } };
|
|
169
|
+
case 'distribution':
|
|
170
|
+
// Cloud declares distribution as a record-of-number (z.record(string,number)),
|
|
171
|
+
// e.g. age_distribution / gender_split — keep it an object on-device so
|
|
172
|
+
// route/fan-out parity holds (NOT a scalar string).
|
|
173
|
+
return { type: 'object', additionalProperties: { type: 'number' } };
|
|
174
|
+
case 'string':
|
|
175
|
+
default:
|
|
176
|
+
return { type: 'string' };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Fresh copy of a default field spec (callers must never share the enum arrays). */
|
|
180
|
+
function cloneDefaultProp(key) {
|
|
181
|
+
const spec = DEFAULT_FIELD_PROPS[key];
|
|
182
|
+
return Array.isArray(spec.enum) ? { ...spec, enum: [...spec.enum] } : { ...spec };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Build the JSON Schema the on-device model is asked to fill (rendered as a
|
|
186
|
+
* readable key list by LiteRtVlmProvider, validated client-side). All fields
|
|
187
|
+
* non-nullable + required — see the file header for why null is forbidden.
|
|
188
|
+
*
|
|
189
|
+
* ONE builder, parameterized by `fieldSet` — an audience/DOOH profile gets the
|
|
190
|
+
* full 12; a Sense-Anything (operator) profile gets the presence primitives plus
|
|
191
|
+
* its own declared fields, and never `audience_ethnicity`.
|
|
192
|
+
*/
|
|
193
|
+
function buildLocalSensingSchema(options = {}) {
|
|
194
|
+
const fieldSet = options.fieldSet === 'operator' ? 'operator' : 'audience';
|
|
195
|
+
const defaults = fieldSet === 'operator' ? PRESENCE_PRIMITIVE_ORDER : AUDIENCE_FIELD_ORDER;
|
|
196
|
+
const properties = {};
|
|
197
|
+
// Every field is required — client-side validation rejects (and retries) any
|
|
198
|
+
// generation that drops a key or emits an out-of-enum value.
|
|
199
|
+
const required = [];
|
|
200
|
+
for (const key of defaults) {
|
|
201
|
+
properties[key] = cloneDefaultProp(key);
|
|
202
|
+
required.push(key);
|
|
203
|
+
}
|
|
204
|
+
// Operator (Sense-Anything) custom fields — also NON-nullable + required so a
|
|
205
|
+
// queue_length / shelf_empty style alert field survives the on-device grammar.
|
|
206
|
+
const addField = (key, prop) => {
|
|
207
|
+
if (!key || key in properties)
|
|
208
|
+
return; // never override a default field
|
|
209
|
+
if (fieldSet === 'operator' && OPERATOR_FORBIDDEN_FIELDS.has(key))
|
|
210
|
+
return;
|
|
211
|
+
properties[key] = prop;
|
|
212
|
+
required.push(key);
|
|
213
|
+
};
|
|
214
|
+
for (const f of options.extraBooleanFields || [])
|
|
215
|
+
addField(f, { type: 'boolean' });
|
|
216
|
+
for (const f of options.extraCounterFields || [])
|
|
217
|
+
addField(f, { type: 'integer' });
|
|
218
|
+
// Typed profile fields (the preferred path — from the deployed profile's metrics_schema).
|
|
219
|
+
for (const f of options.extraFields || []) {
|
|
220
|
+
if (!f || typeof f.field !== 'string')
|
|
221
|
+
continue;
|
|
222
|
+
const prop = profileFieldToSchemaProp(f);
|
|
223
|
+
if (prop)
|
|
224
|
+
addField(f.field, prop);
|
|
225
|
+
}
|
|
226
|
+
return { type: 'object', properties, required };
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Render the operator's custom-field instructions for the prompt (shared by both
|
|
230
|
+
* field sets). On the operator path a forbidden field is skipped here too, so the
|
|
231
|
+
* prompt can never ask for a key the schema refuses to carry.
|
|
232
|
+
*/
|
|
233
|
+
function renderCustomFieldInstructions(extraFields, fieldSet) {
|
|
234
|
+
const customs = (extraFields || []).filter((f) => f && f.field && f.type !== 'hidden'
|
|
235
|
+
&& !(fieldSet === 'operator' && OPERATOR_FORBIDDEN_FIELDS.has(f.field)));
|
|
236
|
+
if (customs.length === 0)
|
|
237
|
+
return null;
|
|
238
|
+
return customs
|
|
239
|
+
.map((f) => {
|
|
240
|
+
const desc = (f.description || '').replace(/\s+/g, ' ').trim().slice(0, 120);
|
|
241
|
+
return `- ${f.field}: ${desc || f.type}`;
|
|
242
|
+
})
|
|
243
|
+
.join(' ');
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The analysis prompt (mirrors audienceVisionService buildProfileDrivenPrompt's
|
|
247
|
+
* role). When the active Sense-Anything profile declares custom fields, append a
|
|
248
|
+
* terse instruction for each so Gemma knows what to observe — the on-device
|
|
249
|
+
* equivalent of the cloud profile-driven prompt. Kept lean (the verbose cloud
|
|
250
|
+
* phrasing reliably destabilized the on-device decode — see file header).
|
|
251
|
+
*
|
|
252
|
+
* `fieldSet` must match the schema's: the audience prompt tells Gemma which
|
|
253
|
+
* SENTINEL to use for each audience_* field on an empty scene, and on the
|
|
254
|
+
* operator path those fields do not exist, so repeating that instruction would
|
|
255
|
+
* spend decode budget describing keys the model must not emit.
|
|
256
|
+
*/
|
|
257
|
+
function buildLocalSensingPrompt(options = {}) {
|
|
258
|
+
const fieldSet = options.fieldSet === 'operator' ? 'operator' : 'audience';
|
|
259
|
+
const base = fieldSet === 'operator'
|
|
260
|
+
? 'You are watching one frame from a venue camera. Count distinct people ' +
|
|
261
|
+
'(person_count); people_present=true if any human is visible; confidence is your ' +
|
|
262
|
+
'0-1 certainty. Report only what is VISIBLE in this frame. Anonymous aggregate ' +
|
|
263
|
+
'measurement only — face DETECTION not recognition.'
|
|
264
|
+
: 'You are a behavioral intelligence analyst observing one frame from a screen at a retail ' +
|
|
265
|
+
'venue. Count distinct people (person_count); people_present=true if any human is visible. ' +
|
|
266
|
+
'When NO people are present use "none"/"unclear" for audience_* enums, "" for ' +
|
|
267
|
+
'audience_engagement_narrative, 0 for ad_receptivity_score. Anonymous aggregate measurement ' +
|
|
268
|
+
'only — face DETECTION not recognition.';
|
|
269
|
+
const lines = renderCustomFieldInstructions(options.extraFields, fieldSet);
|
|
270
|
+
if (!lines)
|
|
271
|
+
return base;
|
|
272
|
+
return `${base} Also report these operator-requested fields from the image: ${lines}`;
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=localInferenceSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localInferenceSchema.js","sourceRoot":"","sources":["../src/localInferenceSchema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** A schema-conforming, route-addressable default-profile result with a person. */
|
|
2
|
+
export interface StubGemmaResultOptions {
|
|
3
|
+
personCount?: number;
|
|
4
|
+
peoplePresent?: boolean;
|
|
5
|
+
/** Extra/override keys merged onto the base result (e.g. operator custom fields). */
|
|
6
|
+
extra?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build a DEFAULT-schema-conforming structured result with a person present.
|
|
10
|
+
* Matches buildLocalSensingSchema()'s required keys + enum members exactly, so
|
|
11
|
+
* LiteRtVlmProvider's client-side validation passes on the first attempt. This
|
|
12
|
+
* is the object the stub VLM "infers" — it must look like real Gemma output.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildStubGemmaResult(options?: StubGemmaResultOptions): Record<string, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* Wrap a structured result in the OpenAI chat-completions envelope litert-lm
|
|
17
|
+
* serve returns (choices[0].message.content is the minified JSON string).
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildStubChatCompletion(result: Record<string, unknown>): Record<string, unknown>;
|
|
20
|
+
export interface StubVlmServer {
|
|
21
|
+
/** The base URL the connector should point at (http://127.0.0.1:<port>). */
|
|
22
|
+
baseUrl: string;
|
|
23
|
+
port: number;
|
|
24
|
+
/** Number of /v1/chat/completions calls served (for assertions). */
|
|
25
|
+
getChatCalls(): number;
|
|
26
|
+
/** The `model` id each /v1/chat/completions call was posted under, in order. */
|
|
27
|
+
getChatModels(): string[];
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export interface CreateStubVlmServerOptions {
|
|
31
|
+
/** Result the stub "infers" for every frame. Default: a 2-person scene. */
|
|
32
|
+
result?: Record<string, unknown>;
|
|
33
|
+
/** Bind host. Default 127.0.0.1 (loopback only — never expose a fake model). */
|
|
34
|
+
host?: string;
|
|
35
|
+
/** Port. Default 0 (ephemeral). Pass 9379 to mirror the real serve port. */
|
|
36
|
+
port?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Model ids `GET /v1/models` advertises. Default: the single CPU id. Pass both
|
|
39
|
+
* ids (`['gemma-4-E2B-it.litertlm', 'gemma-4-E2B-it.litertlm,gpu']`) to mirror
|
|
40
|
+
* a real litert-lm 0.13.1 serve, which advertises its GPU delegate as a
|
|
41
|
+
* SEPARATE model id — that is what LiteRtVlmProvider resolves against.
|
|
42
|
+
*/
|
|
43
|
+
modelIds?: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Start a fake `litert-lm serve` (OpenAI-compatible) on loopback. Answers:
|
|
47
|
+
* GET /v1/models → 200 (health probe)
|
|
48
|
+
* POST /v1/chat/completions → 200 {choices:[{message:{content:<json>}}]}
|
|
49
|
+
* Any other path → 404. Resolves once listening (baseUrl/port populated).
|
|
50
|
+
*/
|
|
51
|
+
export declare function createStubVlmServer(options?: CreateStubVlmServerOptions): Promise<StubVlmServer>;
|
|
52
|
+
/** One captured preAnalyzed ingest POST. */
|
|
53
|
+
export interface CapturedIngest {
|
|
54
|
+
preAnalyzed: Record<string, unknown> | null;
|
|
55
|
+
deviceToken: string | null;
|
|
56
|
+
hasImage: boolean;
|
|
57
|
+
capturedAt: number | null;
|
|
58
|
+
raw: Record<string, unknown> | null;
|
|
59
|
+
}
|
|
60
|
+
export interface IngestCaptureServer {
|
|
61
|
+
/** The apiBaseUrl the connector should use (http://127.0.0.1:<port>). */
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
port: number;
|
|
64
|
+
/** Every captured /v2/earner/audience-analyze POST, in order. */
|
|
65
|
+
getCaptured(): CapturedIngest[];
|
|
66
|
+
close(): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
export interface CreateIngestCaptureServerOptions {
|
|
69
|
+
host?: string;
|
|
70
|
+
port?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Profile returned by GET /v2/earner/cameras/sensing-profile. Default: a body
|
|
73
|
+
* with no custom fields so the connector keeps the DEFAULT schema. (Shape
|
|
74
|
+
* mirrors SensingProfileClient's expected response.)
|
|
75
|
+
*/
|
|
76
|
+
profileBody?: Record<string, unknown>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Start a fake Trillboards API on loopback that the connector's
|
|
80
|
+
* AudienceAnalyzeClient + SensingProfileClient talk to. Captures preAnalyzed
|
|
81
|
+
* ingests; serves an (empty by default) sensing profile.
|
|
82
|
+
* POST /v2/earner/audience-analyze → 200, captures preAnalyzed
|
|
83
|
+
* GET /v2/earner/cameras/sensing-profile → 200 {profile}
|
|
84
|
+
* Any other path → 404.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createIngestCaptureServer(options?: CreateIngestCaptureServerOptions): Promise<IngestCaptureServer>;
|
|
87
|
+
export interface PersonAssertion {
|
|
88
|
+
ok: boolean;
|
|
89
|
+
reason: string;
|
|
90
|
+
personCount: number | null;
|
|
91
|
+
peoplePresent: boolean | null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* E2E acceptance over the captured ingests: at least one preAnalyzed result
|
|
95
|
+
* indicates a PERSON — person_count>=1 OR people_present===true. Returns a
|
|
96
|
+
* structured verdict (ok + a human-readable reason) the runner turns into an
|
|
97
|
+
* exit code. The provenance the connector stamps (source='vlm_edge') is also
|
|
98
|
+
* asserted so a result that bypassed the connector can't satisfy the gate.
|
|
99
|
+
*/
|
|
100
|
+
export declare function assertPreAnalyzedHasPerson(captured: CapturedIngest[]): PersonAssertion;
|
|
101
|
+
//# sourceMappingURL=localSenseE2eHarness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localSenseE2eHarness.d.ts","sourceRoot":"","sources":["../src/localSenseE2eHarness.ts"],"names":[],"mappings":"AA8BA,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAahG;AAED,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,YAAY,IAAI,MAAM,CAAC;IACvB,gFAAgF;IAChF,aAAa,IAAI,MAAM,EAAE,CAAC;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IACzC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,aAAa,CAAC,CAmDxB;AAED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,WAAW,IAAI,cAAc,EAAE,CAAC;IAChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,gCAAgC;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,OAAO,CAAC,mBAAmB,CAAC,CA6D9B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,eAAe,CAmCtF"}
|