@blueharford/scrypted-spatial-awareness 0.6.24 → 0.6.25

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/plugin.zip CHANGED
Binary file
@@ -37351,14 +37351,23 @@ class TrackingEngine {
37351
37351
  recordLlmCall() {
37352
37352
  this.lastLlmCallTime = Date.now();
37353
37353
  }
37354
+ /** Check and record LLM call - returns false if rate limited */
37355
+ tryLlmCall() {
37356
+ if (!this.isLlmCallAllowed()) {
37357
+ this.console.log('[LLM] Rate limited, skipping LLM call');
37358
+ return false;
37359
+ }
37360
+ this.recordLlmCall();
37361
+ return true;
37362
+ }
37354
37363
  /** Get spatial reasoning result for movement (uses RAG + LLM) with debouncing and fallback */
37355
37364
  async getSpatialDescription(tracked, fromCameraId, toCameraId, transitTime, currentCameraId) {
37356
37365
  const fallbackEnabled = this.config.llmFallbackEnabled ?? true;
37357
37366
  const fallbackTimeout = this.config.llmFallbackTimeout ?? 3000;
37358
37367
  try {
37359
37368
  // Check rate limiting - if not allowed, return null to use basic description
37360
- if (!this.isLlmCallAllowed()) {
37361
- this.console.log('LLM rate-limited, using basic notification');
37369
+ if (!this.tryLlmCall()) {
37370
+ this.console.log('[Movement] LLM rate-limited, using basic notification');
37362
37371
  return null;
37363
37372
  }
37364
37373
  // Get snapshot from camera for LLM analysis (if LLM is enabled)
@@ -37369,8 +37378,6 @@ class TrackingEngine {
37369
37378
  mediaObject = await camera.takePicture();
37370
37379
  }
37371
37380
  }
37372
- // Record that we're making an LLM call
37373
- this.recordLlmCall();
37374
37381
  // Use spatial reasoning engine for rich context-aware description
37375
37382
  // Apply timeout if fallback is enabled
37376
37383
  let result;
@@ -37524,17 +37531,25 @@ class TrackingEngine {
37524
37531
  this.console.log(`[Entry Alert] Prefetch result: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
37525
37532
  }
37526
37533
  catch (e) {
37527
- this.console.warn(`[Entry Alert] Prefetch failed, generating fallback: ${e}`);
37534
+ this.console.warn(`[Entry Alert] Prefetch failed, using basic description: ${e}`);
37535
+ // Don't make another LLM call - use basic description (no mediaObject = no LLM)
37528
37536
  spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId);
37529
37537
  }
37530
37538
  this.pendingDescriptions.delete(globalId);
37531
37539
  }
37532
37540
  else {
37533
- // Fallback: generate description now (slower path)
37534
- this.console.log(`[Entry Alert] No prefetch available, generating now`);
37535
- const mediaObject = this.snapshotCache.get(globalId);
37536
- spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId, mediaObject);
37537
- this.console.log(`[Entry Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
37541
+ // No prefetch available - only call LLM if rate limit allows
37542
+ if (this.tryLlmCall()) {
37543
+ this.console.log(`[Entry Alert] No prefetch, generating with LLM`);
37544
+ const mediaObject = this.snapshotCache.get(globalId);
37545
+ spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId, mediaObject);
37546
+ this.console.log(`[Entry Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
37547
+ }
37548
+ else {
37549
+ // Rate limited - use basic description (no LLM)
37550
+ this.console.log(`[Entry Alert] Rate limited, using basic description`);
37551
+ spatialResult = await this.spatialReasoning.generateEntryDescription(tracked, sighting.cameraId);
37552
+ }
37538
37553
  }
37539
37554
  // Always use movement alert type for smart notifications with LLM descriptions
37540
37555
  // The property_entry/property_exit types are legacy and disabled by default
@@ -37561,9 +37576,9 @@ class TrackingEngine {
37561
37576
  if (mediaObject) {
37562
37577
  this.snapshotCache.set(globalId, mediaObject);
37563
37578
  this.console.log(`[Snapshot] Cached snapshot for ${globalId.slice(0, 8)} from ${cameraId}`);
37564
- // Start LLM analysis immediately in parallel (don't await)
37579
+ // Start LLM analysis immediately in parallel (don't await) - but respect rate limits
37565
37580
  const tracked = this.state.getObject(globalId);
37566
- if (tracked && this.config.useLlmDescriptions) {
37581
+ if (tracked && this.config.useLlmDescriptions && this.tryLlmCall()) {
37567
37582
  this.console.log(`[LLM Prefetch] Starting ${eventType} analysis for ${globalId.slice(0, 8)}`);
37568
37583
  const descriptionPromise = eventType === 'exit'
37569
37584
  ? this.spatialReasoning.generateExitDescription(tracked, cameraId, mediaObject)
@@ -37576,6 +37591,9 @@ class TrackingEngine {
37576
37591
  this.console.warn(`[LLM Prefetch] Failed for ${globalId.slice(0, 8)}: ${e}`);
37577
37592
  });
37578
37593
  }
37594
+ else if (tracked && this.config.useLlmDescriptions) {
37595
+ this.console.log(`[LLM Prefetch] Skipped for ${globalId.slice(0, 8)} - rate limited`);
37596
+ }
37579
37597
  }
37580
37598
  }
37581
37599
  }
@@ -37644,17 +37662,25 @@ class TrackingEngine {
37644
37662
  this.console.log(`[Exit Alert] Prefetch result: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
37645
37663
  }
37646
37664
  catch (e) {
37647
- this.console.warn(`[Exit Alert] Prefetch failed, generating fallback: ${e}`);
37665
+ this.console.warn(`[Exit Alert] Prefetch failed, using basic description: ${e}`);
37666
+ // Don't make another LLM call - use basic description
37648
37667
  spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId);
37649
37668
  }
37650
37669
  this.pendingDescriptions.delete(tracked.globalId);
37651
37670
  }
37652
37671
  else {
37653
- // Fallback: generate description now (slower path)
37654
- this.console.log(`[Exit Alert] No prefetch available, generating now`);
37655
- const mediaObject = this.snapshotCache.get(tracked.globalId);
37656
- spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId, mediaObject);
37657
- this.console.log(`[Exit Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
37672
+ // No prefetch available - only call LLM if rate limit allows
37673
+ if (this.tryLlmCall()) {
37674
+ this.console.log(`[Exit Alert] No prefetch, generating with LLM`);
37675
+ const mediaObject = this.snapshotCache.get(tracked.globalId);
37676
+ spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId, mediaObject);
37677
+ this.console.log(`[Exit Alert] Got description: "${spatialResult.description.substring(0, 60)}...", usedLlm=${spatialResult.usedLlm}`);
37678
+ }
37679
+ else {
37680
+ // Rate limited - use basic description (no LLM)
37681
+ this.console.log(`[Exit Alert] Rate limited, using basic description`);
37682
+ spatialResult = await this.spatialReasoning.generateExitDescription(current, sighting.cameraId);
37683
+ }
37658
37684
  }
37659
37685
  // Use movement alert for exit too - smart notifications with LLM descriptions
37660
37686
  await this.alertManager.checkAndAlert('movement', current, {
@@ -39197,8 +39223,8 @@ class SpatialAwarenessPlugin extends sdk_1.ScryptedDeviceBase {
39197
39223
  llmDebounceInterval: {
39198
39224
  title: 'LLM Rate Limit (seconds)',
39199
39225
  type: 'number',
39200
- defaultValue: 10,
39201
- description: 'Minimum time between LLM calls to prevent API overload (0 = no limit)',
39226
+ defaultValue: 30,
39227
+ description: 'Minimum time between LLM calls to prevent API rate limiting. Increase if you get rate limit errors. (0 = no limit)',
39202
39228
  group: 'AI & Spatial Reasoning',
39203
39229
  },
39204
39230
  llmFallbackEnabled: {