Adjust threat level interaction to be less strict

This commit is contained in:
Nicolas Mowen 2025-11-26 10:54:49 -07:00
parent 890ae6ae50
commit 376e5b5c25
6 changed files with 76 additions and 23 deletions

View File

@ -375,7 +375,19 @@ class WebPushClient(Communicator):
ended = state == "end" or state == "genai"
if state == "genai" and payload["after"]["data"]["metadata"]:
title = payload["after"]["data"]["metadata"]["title"]
base_title = payload["after"]["data"]["metadata"]["title"]
threat_level = payload["after"]["data"]["metadata"].get(
"potential_threat_level", 0
)
# Add prefix for threat levels 1 and 2
if threat_level == 1:
title = f"Needs Review: {base_title}"
elif threat_level == 2:
title = f"Security Concern: {base_title}"
else:
title = base_title
message = payload["after"]["data"]["metadata"]["scene"]
else:
title = f"{titlecase(', '.join(sorted_objects).replace('_', ' '))}{' was' if state == 'end' else ''} detected in {titlecase(', '.join(payload['after']['data']['zones']).replace('_', ' '))}"

View File

@ -205,14 +205,20 @@ Rules for the report:
- Group bullets under subheadings when multiple events fall into the same category (e.g., Vehicle Activity, Porch Activity, Unusual Behavior).
- Threat levels
- Always show (threat level: X) for each event.
- Always show the threat level for each event using these labels:
- Threat level 0: "Normal"
- Threat level 1: "Needs review"
- Threat level 2: "Security concern"
- Format as (threat level: Normal), (threat level: Needs review), or (threat level: Security concern).
- If multiple events at the same time share the same threat level, only state it once.
- Final assessment
- End with a Final Assessment section.
- If all events are threat level 1 with no escalation:
- If all events are threat level 0:
Final assessment: Only normal residential activity observed during this period.
- If threat level 2+ events are present, clearly summarize them as Potential concerns requiring review.
- If threat level 1 events are present:
Final assessment: Some activity requires review but no security concerns identified.
- If threat level 2 events are present, clearly summarize them as Security concerns requiring immediate attention.
- Conciseness
- Do not repeat benign clothing/appearance details unless they distinguish individuals.

View File

@ -54,6 +54,7 @@
"selected_other": "{{count}} selected",
"camera": "Camera",
"detected": "detected",
"suspiciousActivity": "Suspicious Activity",
"threateningActivity": "Threatening Activity"
"normalActivity": "Normal",
"needsReview": "Needs review",
"securityConcern": "Security concern"
}

View File

@ -1,7 +1,11 @@
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer";
import { cn } from "@/lib/utils";
import { ReviewSegment, ThreatLevel } from "@/types/review";
import {
ReviewSegment,
ThreatLevel,
THREAT_LEVEL_LABELS,
} from "@/types/review";
import { useEffect, useMemo, useState } from "react";
import { isDesktop } from "react-device-detect";
import { useTranslation } from "react-i18next";
@ -55,13 +59,22 @@ export function GenAISummaryDialog({
}
let concerns = "";
switch (aiAnalysis.potential_threat_level) {
case ThreatLevel.SUSPICIOUS:
concerns = `${t("suspiciousActivity", { ns: "views/events" })}\n`;
const threatLevel = aiAnalysis.potential_threat_level ?? 0;
if (threatLevel > 0) {
let label = "";
switch (threatLevel) {
case ThreatLevel.NEEDS_REVIEW:
label = t("needsReview", { ns: "views/events" });
break;
case ThreatLevel.DANGER:
concerns = `${t("threateningActivity", { ns: "views/events" })}\n`;
case ThreatLevel.SECURITY_CONCERN:
label = t("securityConcern", { ns: "views/events" });
break;
default:
label = THREAT_LEVEL_LABELS[threatLevel as ThreatLevel] || "Unknown";
}
concerns = `${label}\n`;
}
(aiAnalysis.other_concerns ?? []).forEach((c) => {

View File

@ -1,7 +1,11 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useApiHost } from "@/api";
import { isCurrentHour } from "@/utils/dateUtil";
import { ReviewSegment } from "@/types/review";
import {
ReviewSegment,
ThreatLevel,
THREAT_LEVEL_LABELS,
} from "@/types/review";
import { getIconForLabel } from "@/utils/iconUtil";
import TimeAgo from "../dynamic/TimeAgo";
import useSWR from "swr";
@ -44,7 +48,7 @@ export default function PreviewThumbnailPlayer({
onClick,
onTimeUpdate,
}: PreviewPlayerProps) {
const { t } = useTranslation(["components/player"]);
const { t } = useTranslation(["components/player", "views/events"]);
const apiHost = useApiHost();
const { data: config } = useSWR<FrigateConfig>("config");
const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();
@ -319,11 +323,21 @@ export default function PreviewThumbnailPlayer({
</TooltipTrigger>
</div>
<TooltipContent className="smart-capitalize">
{review.data.metadata.potential_threat_level == 1 ? (
<>{t("suspiciousActivity", { ns: "views/events" })}</>
) : (
<>{t("threateningActivity", { ns: "views/events" })}</>
)}
{(() => {
const threatLevel =
review.data.metadata.potential_threat_level ?? 0;
switch (threatLevel) {
case ThreatLevel.NEEDS_REVIEW:
return t("needsReview", { ns: "views/events" });
case ThreatLevel.SECURITY_CONCERN:
return t("securityConcern", { ns: "views/events" });
default:
return (
THREAT_LEVEL_LABELS[threatLevel as ThreatLevel] ||
"Unknown"
);
}
})()}
</TooltipContent>
</Tooltip>
)}

View File

@ -87,6 +87,13 @@ export type ZoomLevel = {
};
export enum ThreatLevel {
SUSPICIOUS = 1,
DANGER = 2,
NORMAL = 0,
NEEDS_REVIEW = 1,
SECURITY_CONCERN = 2,
}
export const THREAT_LEVEL_LABELS: Record<ThreatLevel, string> = {
[ThreatLevel.NORMAL]: "Normal",
[ThreatLevel.NEEDS_REVIEW]: "Needs review",
[ThreatLevel.SECURITY_CONCERN]: "Security concern",
};