@blueharford/scrypted-spatial-awareness 0.6.15 → 0.6.16
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 +90 -2
- package/out/main.nodejs.js.map +1 -1
- package/out/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/core/topology-discovery.ts +96 -2
package/out/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -390,7 +390,7 @@ export class TopologyDiscoveryEngine {
|
|
|
390
390
|
],
|
|
391
391
|
},
|
|
392
392
|
],
|
|
393
|
-
max_tokens:
|
|
393
|
+
max_tokens: 4000, // Increased for detailed scene analysis
|
|
394
394
|
temperature: 0.3,
|
|
395
395
|
});
|
|
396
396
|
|
|
@@ -403,7 +403,8 @@ export class TopologyDiscoveryEngine {
|
|
|
403
403
|
jsonStr = jsonStr.replace(/```json?\n?/g, '').replace(/```$/g, '').trim();
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
|
|
406
|
+
// Try to recover truncated JSON
|
|
407
|
+
const parsed = this.parseJsonWithRecovery(jsonStr, cameraName);
|
|
407
408
|
|
|
408
409
|
// Map parsed data to our types
|
|
409
410
|
if (Array.isArray(parsed.landmarks)) {
|
|
@@ -544,6 +545,99 @@ export class TopologyDiscoveryEngine {
|
|
|
544
545
|
return 'medium'; // Default to medium if not specified
|
|
545
546
|
}
|
|
546
547
|
|
|
548
|
+
/** Try to parse JSON with recovery for truncated responses */
|
|
549
|
+
private parseJsonWithRecovery(jsonStr: string, context: string): any {
|
|
550
|
+
// First, try direct parse
|
|
551
|
+
try {
|
|
552
|
+
return JSON.parse(jsonStr);
|
|
553
|
+
} catch (e) {
|
|
554
|
+
// Log the raw response for debugging (first 500 chars)
|
|
555
|
+
this.console.log(`[Discovery] Raw LLM response for ${context} (first 500 chars): ${jsonStr.substring(0, 500)}...`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Try to recover truncated JSON by finding complete sections
|
|
559
|
+
try {
|
|
560
|
+
// Find where valid JSON might end (look for last complete object/array)
|
|
561
|
+
let recoveredJson = jsonStr;
|
|
562
|
+
|
|
563
|
+
// Try to close unclosed strings
|
|
564
|
+
const lastQuote = recoveredJson.lastIndexOf('"');
|
|
565
|
+
const lastColon = recoveredJson.lastIndexOf(':');
|
|
566
|
+
if (lastQuote > lastColon) {
|
|
567
|
+
// We might be in the middle of a string value
|
|
568
|
+
const beforeQuote = recoveredJson.substring(0, lastQuote);
|
|
569
|
+
const afterLastCompleteEntry = beforeQuote.lastIndexOf('},');
|
|
570
|
+
if (afterLastCompleteEntry > 0) {
|
|
571
|
+
recoveredJson = beforeQuote.substring(0, afterLastCompleteEntry + 1);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Close any unclosed arrays/objects
|
|
576
|
+
let openBraces = (recoveredJson.match(/{/g) || []).length;
|
|
577
|
+
let closeBraces = (recoveredJson.match(/}/g) || []).length;
|
|
578
|
+
let openBrackets = (recoveredJson.match(/\[/g) || []).length;
|
|
579
|
+
let closeBrackets = (recoveredJson.match(/\]/g) || []).length;
|
|
580
|
+
|
|
581
|
+
// Add missing closing brackets/braces
|
|
582
|
+
while (closeBrackets < openBrackets) {
|
|
583
|
+
recoveredJson += ']';
|
|
584
|
+
closeBrackets++;
|
|
585
|
+
}
|
|
586
|
+
while (closeBraces < openBraces) {
|
|
587
|
+
recoveredJson += '}';
|
|
588
|
+
closeBraces++;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const recovered = JSON.parse(recoveredJson);
|
|
592
|
+
this.console.log(`[Discovery] Recovered truncated JSON for ${context}`);
|
|
593
|
+
return recovered;
|
|
594
|
+
} catch (recoveryError) {
|
|
595
|
+
// Last resort: try to extract just landmarks array
|
|
596
|
+
try {
|
|
597
|
+
const landmarksMatch = jsonStr.match(/"landmarks"\s*:\s*\[([\s\S]*?)(?:\]|$)/);
|
|
598
|
+
const zonesMatch = jsonStr.match(/"zones"\s*:\s*\[([\s\S]*?)(?:\]|$)/);
|
|
599
|
+
|
|
600
|
+
const result: any = { landmarks: [], zones: [], edges: {}, orientation: 'unknown' };
|
|
601
|
+
|
|
602
|
+
if (landmarksMatch) {
|
|
603
|
+
// Try to parse individual landmark objects
|
|
604
|
+
const landmarksStr = landmarksMatch[1];
|
|
605
|
+
const landmarkObjects = landmarksStr.match(/\{[^{}]*\}/g) || [];
|
|
606
|
+
result.landmarks = landmarkObjects.map((obj: string) => {
|
|
607
|
+
try {
|
|
608
|
+
return JSON.parse(obj);
|
|
609
|
+
} catch {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
}).filter(Boolean);
|
|
613
|
+
this.console.log(`[Discovery] Extracted ${result.landmarks.length} landmarks from partial response for ${context}`);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (zonesMatch) {
|
|
617
|
+
const zonesStr = zonesMatch[1];
|
|
618
|
+
const zoneObjects = zonesStr.match(/\{[^{}]*\}/g) || [];
|
|
619
|
+
result.zones = zoneObjects.map((obj: string) => {
|
|
620
|
+
try {
|
|
621
|
+
return JSON.parse(obj);
|
|
622
|
+
} catch {
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
}).filter(Boolean);
|
|
626
|
+
this.console.log(`[Discovery] Extracted ${result.zones.length} zones from partial response for ${context}`);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (result.landmarks.length > 0 || result.zones.length > 0) {
|
|
630
|
+
return result;
|
|
631
|
+
}
|
|
632
|
+
} catch (extractError) {
|
|
633
|
+
// Give up
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
this.console.warn(`[Discovery] Could not recover JSON for ${context}`);
|
|
637
|
+
throw new Error(`Failed to parse LLM response: truncated or malformed JSON`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
547
641
|
/** Resolve a camera reference (name or deviceId) to its deviceId */
|
|
548
642
|
private resolveCameraRef(ref: string): string | null {
|
|
549
643
|
if (!this.topology?.cameras || !ref) return null;
|