Improve System Health pane (#24188)

* build out system health pane

* tweaks

* fixes

* fix notice link so it opens the correct camera

* tweak language
This commit is contained in:
Josh Hawkins
2026-09-12 07:30:04 -06:00
committed by Nicolas Mowen
parent 6d33b31bc6
commit 70ce193e09
57 changed files with 3965 additions and 464 deletions
+17
View File
@@ -60,6 +60,8 @@ from frigate.data_processing.real_time.license_plate import (
)
from frigate.data_processing.types import DataProcessorMetrics, PostProcessDataEnum
from frigate.db.sqlitevecq import SqliteVecQueueDatabase
from frigate.detectors.detection_runners import snapshot_loaded_devices
from frigate.embeddings.types import fold_runtime_devices
from frigate.events.types import (
EventStateEnum,
EventTypeEnum,
@@ -101,6 +103,7 @@ class EmbeddingMaintainer(threading.Thread):
super().__init__(name="embeddings_maintainer")
self.config = config
self.metrics = metrics
self._published_devices: dict[str, str] = {}
self.embeddings = None
self.config_updater = CameraConfigUpdateSubscriber(
self.config,
@@ -337,6 +340,7 @@ class EmbeddingMaintainer(threading.Thread):
self._expire_dedicated_lpr()
self._process_finalized()
self._process_event_metadata()
self._publish_runtime_devices()
# Shutdown deferred processors
for processor in self.realtime_processors:
@@ -738,6 +742,19 @@ class EmbeddingMaintainer(threading.Thread):
if isinstance(processor, ReviewDescriptionProcessor):
processor.process_data(review_updates, PostProcessDataEnum.review)
def _publish_runtime_devices(self) -> None:
"""Push each enrichment's loaded device to the shared metrics dict.
Runs every loop iteration, so only changed entries cross the manager
boundary.
"""
for enrichment, device in fold_runtime_devices(
snapshot_loaded_devices()
).items():
if self._published_devices.get(enrichment) != device:
self.metrics.runtime_devices[enrichment] = device
self._published_devices[enrichment] = device
def _process_event_metadata(self):
# Check for regenerate description requests
(topic, payload) = self.event_metadata_subscriber.check_for_update()
+13 -5
View File
@@ -6,7 +6,10 @@ import os
import numpy as np
from frigate.const import MODEL_CACHE_DIR
from frigate.detectors.detection_runners import get_optimized_runner
from frigate.detectors.detection_runners import (
get_optimized_runner,
record_loaded_device,
)
from frigate.embeddings.types import EnrichmentModelTypeEnum
from frigate.log import suppress_stderr_during
from frigate.util.downloader import ModelDownloader
@@ -62,13 +65,18 @@ class FaceNetEmbedding(BaseEmbedding):
if self.downloader:
self.downloader.wait_for_download()
model_path = os.path.join(MODEL_CACHE_DIR, "facedet/facenet.tflite")
# Suppress TFLite delegate creation messages that bypass Python logging
with suppress_stderr_during("tflite_interpreter_init"):
self.runner = Interpreter(
model_path=os.path.join(MODEL_CACHE_DIR, "facedet/facenet.tflite"),
num_threads=2,
)
self.runner = Interpreter(model_path=model_path, num_threads=2)
self.runner.allocate_tensors()
# tflite never goes through get_optimized_runner, so the small face
# model would otherwise never report a device
record_loaded_device(
model_path, EnrichmentModelTypeEnum.facenet.value, "CPU"
)
self.tensor_input_details = self.runner.get_input_details()
self.tensor_output_details = self.runner.get_output_details()
+34
View File
@@ -13,3 +13,37 @@ class EnrichmentModelTypeEnum(str, Enum):
jina_v2 = "jina_v2"
paddleocr = "paddleocr"
yolov9_license_plate = "yolov9_license_plate"
# which enrichment each model type belongs to; the single place this lives
ENRICHMENT_FOR_MODEL_TYPE: dict[str, str] = {
EnrichmentModelTypeEnum.arcface.value: "face_recognition",
EnrichmentModelTypeEnum.facenet.value: "face_recognition",
EnrichmentModelTypeEnum.jina_v1.value: "semantic_search",
EnrichmentModelTypeEnum.jina_v2.value: "semantic_search",
EnrichmentModelTypeEnum.paddleocr.value: "lpr",
EnrichmentModelTypeEnum.yolov9_license_plate.value: "lpr",
}
def fold_runtime_devices(loaded: dict[str, tuple[str, str]]) -> dict[str, str]:
"""One device per enrichment from the per model registry.
A non CPU device wins so a model pinned to the CPU on purpose (the Jina V1
text model) does not hide the accelerator its sibling loaded on, while an
enrichment whose models all fell back to the CPU still reports CPU.
"""
folded: dict[str, str] = {}
for model_type, device in loaded.values():
enrichment = ENRICHMENT_FOR_MODEL_TYPE.get(model_type)
if enrichment is None:
continue
current = folded.get(enrichment)
if current is None or ("CPU" in current and "CPU" not in device):
folded[enrichment] = device
return folded