From b12552571ecf77babf38570fbfe8a1f88c4f48b3 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Tue, 16 Dec 2025 21:45:18 -0600 Subject: [PATCH] ensure jina loading takes place in the main thread to prevent lazily importing tensorflow in another thread later reverts atexit changes in https://github.com/blakeblackshear/frigate/pull/21301 and fixes https://github.com/blakeblackshear/frigate/discussions/21306 --- .../real_time/custom_classification.py | 14 +++++------ frigate/embeddings/maintainer.py | 23 ------------------- frigate/embeddings/onnx/jina_v1_embedding.py | 3 +++ frigate/embeddings/onnx/jina_v2_embedding.py | 3 +++ 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/frigate/data_processing/real_time/custom_classification.py b/frigate/data_processing/real_time/custom_classification.py index dd011b48e..f50d5fd61 100644 --- a/frigate/data_processing/real_time/custom_classification.py +++ b/frigate/data_processing/real_time/custom_classification.py @@ -29,6 +29,11 @@ from frigate.util.object import box_overlaps, calculate_region from ..types import DataProcessorMetrics from .api import RealTimeProcessorApi +try: + from tflite_runtime.interpreter import Interpreter +except ModuleNotFoundError: + from tensorflow.lite.python.interpreter import Interpreter + logger = logging.getLogger(__name__) MAX_OBJECT_CLASSIFICATIONS = 16 @@ -47,7 +52,7 @@ class CustomStateClassificationProcessor(RealTimeProcessorApi): self.requestor = requestor self.model_dir = os.path.join(MODEL_CACHE_DIR, self.model_config.name) self.train_dir = os.path.join(CLIPS_DIR, self.model_config.name, "train") - self.interpreter: Any | None = None + self.interpreter: Interpreter | None = None self.tensor_input_details: dict[str, Any] | None = None self.tensor_output_details: dict[str, Any] | None = None self.labelmap: dict[int, str] = {} @@ -345,7 +350,7 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi): self.model_config = model_config self.model_dir = os.path.join(MODEL_CACHE_DIR, self.model_config.name) self.train_dir = os.path.join(CLIPS_DIR, self.model_config.name, "train") - self.interpreter: Any | None = None + self.interpreter: Interpreter | None = None self.sub_label_publisher = sub_label_publisher self.requestor = requestor self.tensor_input_details: dict[str, Any] | None = None @@ -368,11 +373,6 @@ class CustomObjectClassificationProcessor(RealTimeProcessorApi): @redirect_output_to_logger(logger, logging.DEBUG) def __build_detector(self) -> None: - try: - from tflite_runtime.interpreter import Interpreter - except ModuleNotFoundError: - from tensorflow.lite.python.interpreter import Interpreter - model_path = os.path.join(self.model_dir, "model.tflite") labelmap_path = os.path.join(self.model_dir, "labelmap.txt") diff --git a/frigate/embeddings/maintainer.py b/frigate/embeddings/maintainer.py index 33d09dcc3..78a251c42 100644 --- a/frigate/embeddings/maintainer.py +++ b/frigate/embeddings/maintainer.py @@ -146,29 +146,6 @@ class EmbeddingMaintainer(threading.Thread): self.detected_license_plates: dict[str, dict[str, Any]] = {} self.genai_client = get_genai_client(config) - # Pre-import TensorFlow/tflite on main thread to avoid atexit registration issues - # when importing from worker threads later (e.g., during dynamic config updates) - if ( - self.config.classification.bird.enabled - or len(self.config.classification.custom) > 0 - ): - try: - from tflite_runtime.interpreter import Interpreter # noqa: F401 - except ModuleNotFoundError: - try: - from tensorflow.lite.python.interpreter import ( # noqa: F401 - Interpreter, - ) - - logger.debug( - "Pre-imported TensorFlow Interpreter on main thread for classification models" - ) - except Exception as e: - logger.warning( - f"Failed to pre-import TensorFlow Interpreter: {e}. " - "Classification models may fail to load if added dynamically." - ) - # model runners to share between realtime and post processors if self.config.lpr.enabled: lpr_model_runner = LicensePlateModelRunner( diff --git a/frigate/embeddings/onnx/jina_v1_embedding.py b/frigate/embeddings/onnx/jina_v1_embedding.py index e64d8da39..519247f3c 100644 --- a/frigate/embeddings/onnx/jina_v1_embedding.py +++ b/frigate/embeddings/onnx/jina_v1_embedding.py @@ -186,6 +186,9 @@ class JinaV1ImageEmbedding(BaseEmbedding): download_func=self._download_model, ) self.downloader.ensure_model_files() + # Avoid lazy loading in worker threads: block until downloads complete + # and load the model on the main thread during initialization. + self._load_model_and_utils() else: self.downloader = None ModelDownloader.mark_files_state( diff --git a/frigate/embeddings/onnx/jina_v2_embedding.py b/frigate/embeddings/onnx/jina_v2_embedding.py index 44cc6c12b..fd4323f85 100644 --- a/frigate/embeddings/onnx/jina_v2_embedding.py +++ b/frigate/embeddings/onnx/jina_v2_embedding.py @@ -65,6 +65,9 @@ class JinaV2Embedding(BaseEmbedding): download_func=self._download_model, ) self.downloader.ensure_model_files() + # Avoid lazy loading in worker threads: block until downloads complete + # and load the model on the main thread during initialization. + self._load_model_and_utils() else: self.downloader = None ModelDownloader.mark_files_state(