@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/out/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -141,6 +141,8 @@ export class TrackingEngine {
|
|
|
141
141
|
// ==================== Snapshot Cache ====================
|
|
142
142
|
/** Cached snapshots for tracked objects (for faster notifications) */
|
|
143
143
|
private snapshotCache: Map<GlobalTrackingId, MediaObject> = new Map();
|
|
144
|
+
/** Pending LLM description promises (started when snapshot is captured) */
|
|
145
|
+
private pendingDescriptions: Map<GlobalTrackingId, Promise<SpatialReasoningResult>> = new Map();
|
|
144
146
|
|
|
145
147
|
constructor(
|
|
146
148
|
topology: CameraTopology,
|
|
@@ -555,18 +557,27 @@ export class TrackingEngine {
|
|
|
555
557
|
// Check if we've already alerted for this object
|
|
556
558
|
if (this.isInAlertCooldown(globalId)) return;
|
|
557
559
|
|
|
558
|
-
// Use
|
|
559
|
-
let
|
|
560
|
-
this.
|
|
560
|
+
// Use prefetched LLM result if available (started when snapshot was captured)
|
|
561
|
+
let spatialResult: SpatialReasoningResult;
|
|
562
|
+
const pendingDescription = this.pendingDescriptions.get(globalId);
|
|
561
563
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
564
|
+
if (pendingDescription) {
|
|
565
|
+
this.console.log(`[Entry Alert] Using prefetched LLM result for ${globalId.slice(0, 8)}`);
|
|
566
|
+
try {
|
|
567
|
+
spatialResult = await pendingDescription;
|
|
568
|
+
this.console.log(`[Entry Alert] Prefetch result: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
569
|
+
} catch (e) {
|
|
570
|
+
this.console.warn(`[Entry Alert] Prefetch failed, generating fallback: ${e}`);
|
|
571
|
+
spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId);
|
|
572
|
+
}
|
|
573
|
+
this.pendingDescriptions.delete(globalId);
|
|
574
|
+
} else {
|
|
575
|
+
// Fallback: generate description now (slower path)
|
|
576
|
+
this.console.log(`[Entry Alert] No prefetch available, generating now`);
|
|
577
|
+
const mediaObject = this.snapshotCache.get(globalId);
|
|
578
|
+
spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId, mediaObject);
|
|
579
|
+
this.console.log(`[Entry Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
580
|
+
}
|
|
570
581
|
|
|
571
582
|
if (isEntryPoint) {
|
|
572
583
|
// Entry point - generate property entry alert
|
|
@@ -599,8 +610,12 @@ export class TrackingEngine {
|
|
|
599
610
|
}, this.config.loiteringThreshold);
|
|
600
611
|
}
|
|
601
612
|
|
|
602
|
-
/** Capture and cache a snapshot for a tracked object */
|
|
603
|
-
private async captureAndCacheSnapshot(
|
|
613
|
+
/** Capture and cache a snapshot for a tracked object, and start LLM analysis immediately */
|
|
614
|
+
private async captureAndCacheSnapshot(
|
|
615
|
+
globalId: GlobalTrackingId,
|
|
616
|
+
cameraId: string,
|
|
617
|
+
eventType: 'entry' | 'exit' | 'movement' = 'entry'
|
|
618
|
+
): Promise<void> {
|
|
604
619
|
try {
|
|
605
620
|
const camera = systemManager.getDeviceById<Camera>(cameraId);
|
|
606
621
|
if (camera?.interfaces?.includes(ScryptedInterface.Camera)) {
|
|
@@ -608,6 +623,24 @@ export class TrackingEngine {
|
|
|
608
623
|
if (mediaObject) {
|
|
609
624
|
this.snapshotCache.set(globalId, mediaObject);
|
|
610
625
|
this.console.log(`[Snapshot] Cached snapshot for ${globalId.slice(0, 8)} from ${cameraId}`);
|
|
626
|
+
|
|
627
|
+
// Start LLM analysis immediately in parallel (don't await)
|
|
628
|
+
const tracked = this.state.getObject(globalId);
|
|
629
|
+
if (tracked && this.config.useLlmDescriptions) {
|
|
630
|
+
this.console.log(`[LLM Prefetch] Starting ${eventType} analysis for ${globalId.slice(0, 8)}`);
|
|
631
|
+
const descriptionPromise = eventType === 'exit'
|
|
632
|
+
? this.spatialReasoning.generateExitDescription(tracked, cameraId, mediaObject)
|
|
633
|
+
: this.spatialReasoning.generateEntryDescription(tracked, cameraId, mediaObject);
|
|
634
|
+
|
|
635
|
+
this.pendingDescriptions.set(globalId, descriptionPromise);
|
|
636
|
+
|
|
637
|
+
// Log when complete (for debugging)
|
|
638
|
+
descriptionPromise.then(result => {
|
|
639
|
+
this.console.log(`[LLM Prefetch] ${eventType} analysis ready for ${globalId.slice(0, 8)}: "${result.description.substring(0, 40)}..."`);
|
|
640
|
+
}).catch(e => {
|
|
641
|
+
this.console.warn(`[LLM Prefetch] Failed for ${globalId.slice(0, 8)}: ${e}`);
|
|
642
|
+
});
|
|
643
|
+
}
|
|
611
644
|
}
|
|
612
645
|
}
|
|
613
646
|
} catch (e) {
|
|
@@ -663,8 +696,9 @@ export class TrackingEngine {
|
|
|
663
696
|
this.state.markPending(tracked.globalId);
|
|
664
697
|
|
|
665
698
|
// Capture a fresh snapshot now while object is still visible (before they leave)
|
|
699
|
+
// Also starts LLM analysis immediately in parallel
|
|
666
700
|
if (this.config.useLlmDescriptions) {
|
|
667
|
-
this.captureAndCacheSnapshot(tracked.globalId, sighting.cameraId).catch(e => {
|
|
701
|
+
this.captureAndCacheSnapshot(tracked.globalId, sighting.cameraId, 'exit').catch(e => {
|
|
668
702
|
this.console.warn(`[Exit Snapshot] Failed to update snapshot: ${e}`);
|
|
669
703
|
});
|
|
670
704
|
}
|
|
@@ -675,21 +709,27 @@ export class TrackingEngine {
|
|
|
675
709
|
if (current && current.state === 'pending') {
|
|
676
710
|
this.state.markExited(tracked.globalId, sighting.cameraId, sighting.cameraName);
|
|
677
711
|
|
|
678
|
-
// Use
|
|
679
|
-
let
|
|
680
|
-
this.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
712
|
+
// Use prefetched LLM result if available (started when exit was first detected)
|
|
713
|
+
let spatialResult: SpatialReasoningResult;
|
|
714
|
+
const pendingDescription = this.pendingDescriptions.get(tracked.globalId);
|
|
715
|
+
|
|
716
|
+
if (pendingDescription) {
|
|
717
|
+
this.console.log(`[Exit Alert] Using prefetched LLM result for ${tracked.globalId.slice(0, 8)}`);
|
|
718
|
+
try {
|
|
719
|
+
spatialResult = await pendingDescription;
|
|
720
|
+
this.console.log(`[Exit Alert] Prefetch result: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
721
|
+
} catch (e) {
|
|
722
|
+
this.console.warn(`[Exit Alert] Prefetch failed, generating fallback: ${e}`);
|
|
723
|
+
spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId);
|
|
724
|
+
}
|
|
725
|
+
this.pendingDescriptions.delete(tracked.globalId);
|
|
726
|
+
} else {
|
|
727
|
+
// Fallback: generate description now (slower path)
|
|
728
|
+
this.console.log(`[Exit Alert] No prefetch available, generating now`);
|
|
729
|
+
const mediaObject = this.snapshotCache.get(tracked.globalId);
|
|
730
|
+
spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId, mediaObject);
|
|
731
|
+
this.console.log(`[Exit Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
|
|
732
|
+
}
|
|
693
733
|
|
|
694
734
|
await this.alertManager.checkAndAlert('property_exit', current, {
|
|
695
735
|
cameraId: sighting.cameraId,
|
|
@@ -700,8 +740,9 @@ export class TrackingEngine {
|
|
|
700
740
|
usedLlm: spatialResult.usedLlm,
|
|
701
741
|
});
|
|
702
742
|
|
|
703
|
-
// Clean up cached snapshot after exit alert
|
|
743
|
+
// Clean up cached snapshot and pending descriptions after exit alert
|
|
704
744
|
this.snapshotCache.delete(tracked.globalId);
|
|
745
|
+
this.pendingDescriptions.delete(tracked.globalId);
|
|
705
746
|
}
|
|
706
747
|
this.pendingTimers.delete(tracked.globalId);
|
|
707
748
|
}, this.config.correlationWindow);
|
|
@@ -724,8 +765,9 @@ export class TrackingEngine {
|
|
|
724
765
|
`(not seen for ${Math.round(timeSinceSeen / 1000)}s)`
|
|
725
766
|
);
|
|
726
767
|
|
|
727
|
-
// Clean up cached snapshot
|
|
768
|
+
// Clean up cached snapshot and pending descriptions
|
|
728
769
|
this.snapshotCache.delete(tracked.globalId);
|
|
770
|
+
this.pendingDescriptions.delete(tracked.globalId);
|
|
729
771
|
|
|
730
772
|
this.alertManager.checkAndAlert('lost_tracking', tracked, {
|
|
731
773
|
objectClass: tracked.className,
|