import Heading from "@/components/ui/heading"; import { FrigateConfig, SearchModelSize } from "@/types/frigateConfig"; import useSWR from "swr"; import axios from "axios"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { useCallback, useContext, useEffect, useState } from "react"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { Toaster } from "@/components/ui/sonner"; import { toast } from "sonner"; import { Separator } from "@/components/ui/separator"; import { Link } from "react-router-dom"; import { LuExternalLink } from "react-icons/lu"; import { StatusBarMessagesContext } from "@/context/statusbar-provider"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, } from "@/components/ui/select"; type ClassificationSettings = { search: { enabled?: boolean; reindex?: boolean; model_size?: SearchModelSize; }; face: { enabled?: boolean; }; lpr: { enabled?: boolean; }; }; type ClassificationSettingsViewProps = { setUnsavedChanges: React.Dispatch>; }; export default function ClassificationSettingsView({ setUnsavedChanges, }: ClassificationSettingsViewProps) { const { data: config, mutate: updateConfig } = useSWR("config"); const [changedValue, setChangedValue] = useState(false); const [isLoading, setIsLoading] = useState(false); const { addMessage, removeMessage } = useContext(StatusBarMessagesContext)!; const [classificationSettings, setClassificationSettings] = useState({ search: { enabled: undefined, reindex: undefined, model_size: undefined, }, face: { enabled: undefined, }, lpr: { enabled: undefined, }, }); const [origSearchSettings, setOrigSearchSettings] = useState({ search: { enabled: undefined, reindex: undefined, model_size: undefined, }, face: { enabled: undefined, }, lpr: { enabled: undefined, }, }); useEffect(() => { if (config) { if (classificationSettings?.search.enabled == undefined) { setClassificationSettings({ search: { enabled: config.semantic_search.enabled, reindex: config.semantic_search.reindex, model_size: config.semantic_search.model_size, }, face: { enabled: config.face_recognition.enabled, }, lpr: { enabled: config.lpr.enabled, }, }); } setOrigSearchSettings({ search: { enabled: config.semantic_search.enabled, reindex: config.semantic_search.reindex, model_size: config.semantic_search.model_size, }, face: { enabled: config.face_recognition.enabled, }, lpr: { enabled: config.lpr.enabled, }, }); } // we know that these deps are correct // eslint-disable-next-line react-hooks/exhaustive-deps }, [config]); const handleClassificationConfigChange = ( newConfig: Partial, ) => { setClassificationSettings((prevConfig) => ({ search: { ...prevConfig.search, ...newConfig.search, }, face: { ...prevConfig.face, ...newConfig.face }, lpr: { ...prevConfig.lpr, ...newConfig.lpr }, })); setUnsavedChanges(true); setChangedValue(true); }; const saveToConfig = useCallback(async () => { setIsLoading(true); axios .put( `config/set?semantic_search.enabled=${classificationSettings.search.enabled ? "True" : "False"}&semantic_search.reindex=${classificationSettings.search.reindex ? "True" : "False"}&semantic_search.model_size=${classificationSettings.search.model_size}`, { requires_restart: 0, }, ) .then((res) => { if (res.status === 200) { toast.success("Classification settings have been saved.", { position: "top-center", }); setChangedValue(false); updateConfig(); } else { toast.error(`Failed to save config changes: ${res.statusText}`, { position: "top-center", }); } }) .catch((error) => { const errorMessage = error.response?.data?.message || error.response?.data?.detail || "Unknown error"; toast.error(`Failed to save config changes: ${errorMessage}`, { position: "top-center", }); }) .finally(() => { setIsLoading(false); }); }, [updateConfig, classificationSettings.search]); const onCancel = useCallback(() => { setClassificationSettings(origSearchSettings); setChangedValue(false); removeMessage("search_settings", "search_settings"); }, [origSearchSettings, removeMessage]); useEffect(() => { if (changedValue) { addMessage( "search_settings", `Unsaved Classification settings changes`, undefined, "search_settings", ); } else { removeMessage("search_settings", "search_settings"); } // we know that these deps are correct // eslint-disable-next-line react-hooks/exhaustive-deps }, [changedValue]); useEffect(() => { document.title = "Classification Settings - Frigate"; }, []); if (!config) { return ; } return (
Classification Settings Semantic Search

Semantic Search in Frigate allows you to find tracked objects within your review items using either the image itself, a user-defined text description, or an automatically generated one.

Read the Documentation
{ handleClassificationConfigChange({ search: { enabled: isChecked }, }); }} />
{ handleClassificationConfigChange({ search: { reindex: isChecked }, }); }} />
Re-indexing will reprocess all thumbnails and descriptions (if enabled) and apply the embeddings on each startup.{" "} Don't forget to disable the option after restarting!
Model Size

The size of the model used for Semantic Search embeddings.

  • Using small employs a quantized version of the model that uses less RAM and runs faster on CPU with a very negligible difference in embedding quality.
  • Using large employs the full Jina model and will automatically run on the GPU if applicable.
Face Recognition

Face recognition allows people to be assigned names and when their face is recognized Frigate will assign the person's name as a sub label. This information is included in the UI, filters, as well as in notifications.

Read the Documentation
{ handleClassificationConfigChange({ face: { enabled: isChecked }, }); }} />
License Plate Recognition

Frigate can recognize license plates on vehicles and automatically add the detected characters to the recognized_license_plate field or a known name as a sub_label to objects that are of type car. A common use case may be to read the license plates of cars pulling into a driveway or cars passing by on a street.

Read the Documentation
{ handleClassificationConfigChange({ lpr: { enabled: isChecked }, }); }} />
); }