mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-31 07:27:57 +00:00
Compare commits
6 Commits
bd08b7d0bb
...
1bea9aec99
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bea9aec99 | ||
|
|
374cd2bb2f | ||
|
|
8c511083ff | ||
|
|
686fdef665 | ||
|
|
5876695aa1 | ||
|
|
1452c61378 |
@ -378,7 +378,9 @@ class WebPushClient(Communicator):
|
||||
message = f"Detected on {titlecase(camera.replace('_', ' '))}"
|
||||
|
||||
if ended:
|
||||
print(f"sending a message with message {message}")
|
||||
logger.debug(
|
||||
f"Sending a notification with state {state} and message {message}"
|
||||
)
|
||||
|
||||
# if event is ongoing open to live view otherwise open to recordings view
|
||||
direct_url = f"/review?id={reviewId}" if ended else f"/#{camera}"
|
||||
|
||||
@ -41,31 +41,34 @@ class GenAIClient:
|
||||
"""Generate a description for the review item activity."""
|
||||
context_prompt = f"""
|
||||
Please analyze the image(s), which are in chronological order, strictly from the perspective of the {review_data["camera"].replace("_", " ")} security camera.
|
||||
Your task is to provide a **neutral, factual, and objective description** of the scene, while also including **reasonable, evidence-based inferences** about the likely context or activity — but do not make unfounded assumptions.
|
||||
|
||||
Your task is to provide a **neutral, factual, and objective description** of the scene, while also:
|
||||
- Clearly stating **what is happening** based on observable actions and movements.
|
||||
- Including **reasonable, evidence-based inferences** about the likely activity or context, but only if directly supported by visible details.
|
||||
|
||||
When forming your description:
|
||||
- Base all statements on visible details in the images.
|
||||
- You may deduce plausible intent or context only if supported by clear, observable evidence (e.g., someone carrying tools toward a car may indicate vehicle maintenance).
|
||||
- Avoid implying hostility, criminal intent, or other strong judgments unless the visual evidence is unambiguous.
|
||||
- Distinguish between what is certain (facts) and what appears likely (reasonable inference).
|
||||
- **Facts first**: Describe the physical setting, people, and objects exactly as seen.
|
||||
- **Then context**: Briefly note plausible purposes or activities (e.g., “appears to be delivering a package” if carrying a box to a door).
|
||||
- Clearly separate certain facts (“A person is holding a ladder”) from reasonable inferences (“likely performing maintenance”).
|
||||
- Do not speculate beyond what is visible, and do not imply hostility, criminal intent, or other strong judgments unless there is unambiguous visual evidence.
|
||||
|
||||
Here is some information we already know:
|
||||
- The activity occurred at {review_data["timestamp"].strftime("%I:%M %p")}
|
||||
- The following objects were detected: {review_data["objects"]}
|
||||
- The following recognized objects were detected: {review_data["recognized_objects"]}
|
||||
- The activity happened in the following zones: {review_data["zones"]}
|
||||
Here is information already known:
|
||||
- Activity occurred at {review_data["timestamp"].strftime("%I:%M %p")}
|
||||
- Detected objects: {review_data["objects"]}
|
||||
- Recognized objects: {review_data["recognized_objects"]}
|
||||
- Zones involved: {review_data["zones"]}
|
||||
|
||||
Your response **MUST** be a flat JSON object with the following fields:
|
||||
- `scene` (string): A comprehensive description of the setting and entities, including relevant context and plausible inferences if supported by visual evidence.
|
||||
- `confidence` (float): A float between 0 and 1 representing your overall confidence in this analysis.
|
||||
- `potential_threat_level` (integer, optional): 1–3 scale. Only include if a clear security concern is visible:
|
||||
- 1 = Unusual but not overtly threatening
|
||||
- 2 = Suspicious or potentially harmful
|
||||
- 3 = Clear and immediate threat
|
||||
Omit this field entirely if there is no observable security concern.
|
||||
Your response **MUST** be a flat JSON object with:
|
||||
- `scene` (string): A full description including setting, entities, actions, and any plausible supported inferences.
|
||||
- `confidence` (float): A number 0–1 for overall confidence in the analysis.
|
||||
- `potential_threat_level` (integer, optional): Include only if there is a clear, observable security concern:
|
||||
- 1 = Unusual but not overtly threatening
|
||||
- 2 = Suspicious or potentially harmful
|
||||
- 3 = Clear and immediate threat
|
||||
Omit this field if no concern is evident.
|
||||
|
||||
**IMPORTANT:**
|
||||
Values for each field must be plain strings or integers — no nested objects or explanatory text.
|
||||
- Values must be plain strings, floats, or integers — no nested objects, no extra commentary.
|
||||
"""
|
||||
response = self._send(context_prompt, thumbnails)
|
||||
|
||||
@ -76,8 +79,11 @@ class GenAIClient:
|
||||
|
||||
try:
|
||||
return ReviewMetadata.model_validate_json(clean_json)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
# rarely LLMs can fail to follow directions on output format
|
||||
logger.warning(
|
||||
f"Failed to parse review description as the response did not match expected format. {e}"
|
||||
)
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -11,7 +11,11 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import { useFormattedTimestamp } from "@/hooks/use-date-utils";
|
||||
import { getIconForLabel } from "@/utils/iconUtil";
|
||||
import { useApiHost } from "@/api";
|
||||
import { ReviewDetailPaneType, ReviewSegment } from "@/types/review";
|
||||
import {
|
||||
ReviewDetailPaneType,
|
||||
ReviewSegment,
|
||||
ThreatLevel,
|
||||
} from "@/types/review";
|
||||
import { Event } from "@/types/event";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
@ -71,6 +75,23 @@ export default function ReviewDetailDialog({
|
||||
|
||||
const aiAnalysis = useMemo(() => review?.data?.metadata, [review]);
|
||||
|
||||
const aiThreatLevel = useMemo(() => {
|
||||
if (!aiAnalysis?.potential_threat_level) {
|
||||
return "None";
|
||||
}
|
||||
|
||||
switch (aiAnalysis.potential_threat_level) {
|
||||
case ThreatLevel.UNUSUAL:
|
||||
return "Unusual Activity";
|
||||
case ThreatLevel.SUSPICIOUS:
|
||||
return "Suspicious Activity";
|
||||
case ThreatLevel.DANGER:
|
||||
return "Danger";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}, [aiAnalysis]);
|
||||
|
||||
const hasMismatch = useMemo(() => {
|
||||
if (!review || !events) {
|
||||
return false;
|
||||
@ -235,12 +256,19 @@ export default function ReviewDetailDialog({
|
||||
{pane == "overview" && (
|
||||
<div className="flex flex-col gap-5 md:mt-3">
|
||||
{aiAnalysis != undefined && (
|
||||
<div className="m-2 flex h-full w-[90%] flex-col gap-2 rounded-md bg-card p-2">
|
||||
<div
|
||||
className={cn(
|
||||
"m-2 flex h-full w-full flex-col gap-2 rounded-md bg-card p-2",
|
||||
isDesktop && "w-[90%]",
|
||||
)}
|
||||
>
|
||||
AI Analysis
|
||||
<div className="text-sm text-primary/40">Description</div>
|
||||
<div className="text-sm smart-capitalize">
|
||||
{aiAnalysis.scene}
|
||||
</div>
|
||||
<div className="text-sm">{aiAnalysis.scene}</div>
|
||||
<div className="text-sm text-primary/40">Score</div>
|
||||
<div className="text-sm">{aiAnalysis.confidence * 100}%</div>
|
||||
<div className="text-sm text-primary/40">Threat Level</div>
|
||||
<div className="text-sm">{aiThreatLevel}</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex w-full flex-row">
|
||||
|
||||
@ -20,6 +20,8 @@ export type ReviewData = {
|
||||
zones: string[];
|
||||
metadata?: {
|
||||
scene: string;
|
||||
confidence: number;
|
||||
potential_threat_level?: number;
|
||||
};
|
||||
};
|
||||
|
||||
@ -76,3 +78,9 @@ export type ConsolidatedSegmentData = {
|
||||
};
|
||||
|
||||
export type TimelineZoomDirection = "in" | "out" | null;
|
||||
|
||||
export enum ThreatLevel {
|
||||
UNUSUAL = 1,
|
||||
SUSPICIOUS = 2,
|
||||
DANGER = 3,
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import monacoEditorPlugin from "vite-plugin-monaco-editor";
|
||||
|
||||
const proxyHost = process.env.PROXY_HOST || "192.168.50.106:5002";
|
||||
const proxyHost = process.env.PROXY_HOST || "localhost:5000";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user