@blueharford/scrypted-spatial-awareness 0.6.13 → 0.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.nodejs.js +1 -1
- package/dist/main.nodejs.js.map +1 -1
- package/dist/plugin.zip +0 -0
- package/out/main.nodejs.js +69 -19
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/core/tracking-engine.ts +73 -31
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/out/main.nodejs.js
CHANGED
|
@@ -36946,6 +36946,8 @@ class TrackingEngine {
|
|
|
36946
36946
|
// ==================== Snapshot Cache ====================
|
|
36947
36947
|
/** Cached snapshots for tracked objects (for faster notifications) */
|
|
36948
36948
|
snapshotCache = new Map();
|
|
36949
|
+
/** Pending LLM description promises (started when snapshot is captured) */
|
|
36950
|
+
pendingDescriptions = new Map();
|
|
36949
36951
|
constructor(topology, state, alertManager, config, console) {
|
|
36950
36952
|
this.topology = topology;
|
|
36951
36953
|
this.state = state;
|
|
@@ -37256,13 +37258,28 @@ class TrackingEngine {
|
|
|
37256
37258
|
// Check if we've already alerted for this object
|
|
37257
37259
|
if (this.isInAlertCooldown(globalId))
|
|
37258
37260
|
return;
|
|
37259
|
-
// Use
|
|
37260
|
-
let
|
|
37261
|
-
this.
|
|
37262
|
-
|
|
37263
|
-
|
|
37264
|
-
|
|
37265
|
-
|
|
37261
|
+
// Use prefetched LLM result if available (started when snapshot was captured)
|
|
37262
|
+
let spatialResult;
|
|
37263
|
+
const pendingDescription = this.pendingDescriptions.get(globalId);
|
|
37264
|
+
if (pendingDescription) {
|
|
37265
|
+
this.console.log(`[Entry Alert] Using prefetched LLM result for ${globalId.slice(0, 8)}`);
|
|
37266
|
+
try {
|
|
37267
|
+
spatialResult = await pendingDescription;
|
|
37268
|
+
this.console.log(`[Entry Alert] Prefetch result: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
37269
|
+
}
|
|
37270
|
+
catch (e) {
|
|
37271
|
+
this.console.warn(`[Entry Alert] Prefetch failed, generating fallback: ${e}`);
|
|
37272
|
+
spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId);
|
|
37273
|
+
}
|
|
37274
|
+
this.pendingDescriptions.delete(globalId);
|
|
37275
|
+
}
|
|
37276
|
+
else {
|
|
37277
|
+
// Fallback: generate description now (slower path)
|
|
37278
|
+
this.console.log(`[Entry Alert] No prefetch available, generating now`);
|
|
37279
|
+
const mediaObject = this.snapshotCache.get(globalId);
|
|
37280
|
+
spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId, mediaObject);
|
|
37281
|
+
this.console.log(`[Entry Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
37282
|
+
}
|
|
37266
37283
|
if (isEntryPoint) {
|
|
37267
37284
|
// Entry point - generate property entry alert
|
|
37268
37285
|
await this.alertManager.checkAndAlert('property_entry', tracked, {
|
|
@@ -37293,8 +37310,8 @@ class TrackingEngine {
|
|
|
37293
37310
|
this.recordAlertTime(globalId);
|
|
37294
37311
|
}, this.config.loiteringThreshold);
|
|
37295
37312
|
}
|
|
37296
|
-
/** Capture and cache a snapshot for a tracked object */
|
|
37297
|
-
async captureAndCacheSnapshot(globalId, cameraId) {
|
|
37313
|
+
/** Capture and cache a snapshot for a tracked object, and start LLM analysis immediately */
|
|
37314
|
+
async captureAndCacheSnapshot(globalId, cameraId, eventType = 'entry') {
|
|
37298
37315
|
try {
|
|
37299
37316
|
const camera = systemManager.getDeviceById(cameraId);
|
|
37300
37317
|
if (camera?.interfaces?.includes(sdk_1.ScryptedInterface.Camera)) {
|
|
@@ -37302,6 +37319,21 @@ class TrackingEngine {
|
|
|
37302
37319
|
if (mediaObject) {
|
|
37303
37320
|
this.snapshotCache.set(globalId, mediaObject);
|
|
37304
37321
|
this.console.log(`[Snapshot] Cached snapshot for ${globalId.slice(0, 8)} from ${cameraId}`);
|
|
37322
|
+
// Start LLM analysis immediately in parallel (don't await)
|
|
37323
|
+
const tracked = this.state.getObject(globalId);
|
|
37324
|
+
if (tracked && this.config.useLlmDescriptions) {
|
|
37325
|
+
this.console.log(`[LLM Prefetch] Starting ${eventType} analysis for ${globalId.slice(0, 8)}`);
|
|
37326
|
+
const descriptionPromise = eventType === 'exit'
|
|
37327
|
+
? this.spatialReasoning.generateExitDescription(tracked, cameraId, mediaObject)
|
|
37328
|
+
: this.spatialReasoning.generateEntryDescription(tracked, cameraId, mediaObject);
|
|
37329
|
+
this.pendingDescriptions.set(globalId, descriptionPromise);
|
|
37330
|
+
// Log when complete (for debugging)
|
|
37331
|
+
descriptionPromise.then(result => {
|
|
37332
|
+
this.console.log(`[LLM Prefetch] ${eventType} analysis ready for ${globalId.slice(0, 8)}: "${result.description.substring(0, 40)}..."`);
|
|
37333
|
+
}).catch(e => {
|
|
37334
|
+
this.console.warn(`[LLM Prefetch] Failed for ${globalId.slice(0, 8)}: ${e}`);
|
|
37335
|
+
});
|
|
37336
|
+
}
|
|
37305
37337
|
}
|
|
37306
37338
|
}
|
|
37307
37339
|
}
|
|
@@ -37349,8 +37381,9 @@ class TrackingEngine {
|
|
|
37349
37381
|
// Mark as pending and set timer
|
|
37350
37382
|
this.state.markPending(tracked.globalId);
|
|
37351
37383
|
// Capture a fresh snapshot now while object is still visible (before they leave)
|
|
37384
|
+
// Also starts LLM analysis immediately in parallel
|
|
37352
37385
|
if (this.config.useLlmDescriptions) {
|
|
37353
|
-
this.captureAndCacheSnapshot(tracked.globalId, sighting.cameraId).catch(e => {
|
|
37386
|
+
this.captureAndCacheSnapshot(tracked.globalId, sighting.cameraId, 'exit').catch(e => {
|
|
37354
37387
|
this.console.warn(`[Exit Snapshot] Failed to update snapshot: ${e}`);
|
|
37355
37388
|
});
|
|
37356
37389
|
}
|
|
@@ -37359,13 +37392,28 @@ class TrackingEngine {
|
|
|
37359
37392
|
const current = this.state.getObject(tracked.globalId);
|
|
37360
37393
|
if (current && current.state === 'pending') {
|
|
37361
37394
|
this.state.markExited(tracked.globalId, sighting.cameraId, sighting.cameraName);
|
|
37362
|
-
// Use
|
|
37363
|
-
let
|
|
37364
|
-
this.
|
|
37365
|
-
|
|
37366
|
-
|
|
37367
|
-
|
|
37368
|
-
|
|
37395
|
+
// Use prefetched LLM result if available (started when exit was first detected)
|
|
37396
|
+
let spatialResult;
|
|
37397
|
+
const pendingDescription = this.pendingDescriptions.get(tracked.globalId);
|
|
37398
|
+
if (pendingDescription) {
|
|
37399
|
+
this.console.log(`[Exit Alert] Using prefetched LLM result for ${tracked.globalId.slice(0, 8)}`);
|
|
37400
|
+
try {
|
|
37401
|
+
spatialResult = await pendingDescription;
|
|
37402
|
+
this.console.log(`[Exit Alert] Prefetch result: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
37403
|
+
}
|
|
37404
|
+
catch (e) {
|
|
37405
|
+
this.console.warn(`[Exit Alert] Prefetch failed, generating fallback: ${e}`);
|
|
37406
|
+
spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId);
|
|
37407
|
+
}
|
|
37408
|
+
this.pendingDescriptions.delete(tracked.globalId);
|
|
37409
|
+
}
|
|
37410
|
+
else {
|
|
37411
|
+
// Fallback: generate description now (slower path)
|
|
37412
|
+
this.console.log(`[Exit Alert] No prefetch available, generating now`);
|
|
37413
|
+
const mediaObject = this.snapshotCache.get(tracked.globalId);
|
|
37414
|
+
spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId, mediaObject);
|
|
37415
|
+
this.console.log(`[Exit Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
37416
|
+
}
|
|
37369
37417
|
await this.alertManager.checkAndAlert('property_exit', current, {
|
|
37370
37418
|
cameraId: sighting.cameraId,
|
|
37371
37419
|
cameraName: sighting.cameraName,
|
|
@@ -37374,8 +37422,9 @@ class TrackingEngine {
|
|
|
37374
37422
|
involvedLandmarks: spatialResult.involvedLandmarks?.map(l => l.name),
|
|
37375
37423
|
usedLlm: spatialResult.usedLlm,
|
|
37376
37424
|
});
|
|
37377
|
-
// Clean up cached snapshot after exit alert
|
|
37425
|
+
// Clean up cached snapshot and pending descriptions after exit alert
|
|
37378
37426
|
this.snapshotCache.delete(tracked.globalId);
|
|
37427
|
+
this.pendingDescriptions.delete(tracked.globalId);
|
|
37379
37428
|
}
|
|
37380
37429
|
this.pendingTimers.delete(tracked.globalId);
|
|
37381
37430
|
}, this.config.correlationWindow);
|
|
@@ -37391,8 +37440,9 @@ class TrackingEngine {
|
|
|
37391
37440
|
this.state.markLost(tracked.globalId);
|
|
37392
37441
|
this.console.log(`Object ${tracked.globalId.slice(0, 8)} marked as lost ` +
|
|
37393
37442
|
`(not seen for ${Math.round(timeSinceSeen / 1000)}s)`);
|
|
37394
|
-
// Clean up cached snapshot
|
|
37443
|
+
// Clean up cached snapshot and pending descriptions
|
|
37395
37444
|
this.snapshotCache.delete(tracked.globalId);
|
|
37445
|
+
this.pendingDescriptions.delete(tracked.globalId);
|
|
37396
37446
|
this.alertManager.checkAndAlert('lost_tracking', tracked, {
|
|
37397
37447
|
objectClass: tracked.className,
|
|
37398
37448
|
objectLabel: tracked.label,
|