Skip to content

Commit 48df7db

Browse files
badbreadclaude
andcommitted
Skip escalation on AI false positives (all-unknown description)
When Gemini analyzes snapshots and returns all "unknown" fields (suspect_count, description, location), the person likely doesn't exist — Frigate false positive. Stage 1 warning still plays but the full dispatch call is skipped. MQTT error published as "false_positive" type for Home Assistant tracking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7a37689 commit 48df7db

1 file changed

Lines changed: 47 additions & 11 deletions

File tree

voxwatch/voxwatch_service.py

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -947,17 +947,53 @@ async def _handle_detection(self, event_data: dict) -> None:
947947
ai_description: str | None = await ai_prep_task
948948
_escalation_description = ai_description
949949

950-
s_esc_description, s_esc_push_ok, s_esc_message = await self._run_escalation(
951-
event_id, camera_name, camera_stream, mode_name, ai_description,
952-
_active_mode_obj.voice,
953-
)
954-
# Use the more detailed description if the escalation stage got one
955-
if s_esc_description:
956-
_escalation_description = s_esc_description
957-
_escalation_message = s_esc_message
958-
_escalation_audio_success = s_esc_push_ok
959-
if s_esc_push_ok:
960-
record_audio_push(self._camera_stats, camera_name, s_esc_push_ok)
950+
# AI validation: if the AI couldn't identify anyone (all fields
951+
# are "unknown"), this is likely a Frigate false positive. Skip
952+
# the escalation to avoid playing a dispatch call for nobody.
953+
_skip_escalation = False
954+
if ai_description and '"unknown"' in ai_description:
955+
import json as _json
956+
try:
957+
_cleaned = ai_description.strip()
958+
if _cleaned.startswith("```"):
959+
_lines = _cleaned.splitlines()
960+
_inner = _lines[1:]
961+
if _inner and _inner[-1].strip().startswith("```"):
962+
_inner = _inner[:-1]
963+
_cleaned = "\n".join(_inner).strip()
964+
_parsed = _json.loads(_cleaned)
965+
if isinstance(_parsed, dict):
966+
_desc = _parsed.get("description", "").strip().lower()
967+
_loc = _parsed.get("location", "").strip().lower()
968+
if _desc in ("unknown", "") and _loc in ("unknown", ""):
969+
logger.info(
970+
"Escalation: AI returned all-unknown description — "
971+
"likely false positive from Frigate. Skipping escalation."
972+
)
973+
_skip_escalation = True
974+
_escalation_ran = False
975+
if self._publisher:
976+
self._publisher.publish_error(
977+
camera=camera_name,
978+
error_type="false_positive",
979+
error_message="AI could not identify anyone in snapshots — likely Frigate false positive",
980+
fallback_used=False,
981+
)
982+
except Exception:
983+
pass # JSON parse failed — proceed with escalation anyway
984+
985+
if not _skip_escalation:
986+
s_esc_description, s_esc_push_ok, s_esc_message = await self._run_escalation(
987+
event_id, camera_name, camera_stream, mode_name, ai_description,
988+
_active_mode_obj.voice,
989+
)
990+
# Use the more detailed description if the escalation stage got one
991+
if s_esc_description:
992+
_escalation_description = s_esc_description
993+
_escalation_message = s_esc_message
994+
_escalation_audio_success = s_esc_push_ok
995+
if s_esc_push_ok:
996+
record_audio_push(self._camera_stats, camera_name, s_esc_push_ok)
961997

962998
# ── MQTT: publish stage 2 ──────────────────────────────────────
963999
if self._publisher and _escalation_ran:

0 commit comments

Comments
 (0)