From a8b6ea50052dcec3e0632d4f33b4686d7bc113db Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 23 Mar 2026 10:22:52 -0600 Subject: [PATCH] Various Fixes (#22594) * Fix handling of preview * Fix schema cleaning * Cleanup --- frigate/api/media.py | 12 ++++++------ frigate/api/preview.py | 6 +++--- frigate/data_processing/post/review_descriptions.py | 6 +++--- frigate/genai/ollama.py | 12 +++++++++--- frigate/output/preview.py | 4 ++-- frigate/record/export.py | 12 ++++++------ 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/frigate/api/media.py b/frigate/api/media.py index 28585bdd5..37c9bb3d0 100644 --- a/frigate/api/media.py +++ b/frigate/api/media.py @@ -1337,9 +1337,9 @@ def preview_gif( status_code=404, ) - file_start = f"preview_{camera_name}" - start_file = f"{file_start}-{start_ts}.{PREVIEW_FRAME_TYPE}" - end_file = f"{file_start}-{end_ts}.{PREVIEW_FRAME_TYPE}" + file_start = f"preview_{camera_name}-" + start_file = f"{file_start}{start_ts}.{PREVIEW_FRAME_TYPE}" + end_file = f"{file_start}{end_ts}.{PREVIEW_FRAME_TYPE}" selected_previews = [] for file in sorted(os.listdir(preview_dir)): @@ -1519,9 +1519,9 @@ def preview_mp4( status_code=404, ) - file_start = f"preview_{camera_name}" - start_file = f"{file_start}-{start_ts}.{PREVIEW_FRAME_TYPE}" - end_file = f"{file_start}-{end_ts}.{PREVIEW_FRAME_TYPE}" + file_start = f"preview_{camera_name}-" + start_file = f"{file_start}{start_ts}.{PREVIEW_FRAME_TYPE}" + end_file = f"{file_start}{end_ts}.{PREVIEW_FRAME_TYPE}" selected_previews = [] for file in sorted(os.listdir(preview_dir)): diff --git a/frigate/api/preview.py b/frigate/api/preview.py index a8fef2044..a5e30764d 100644 --- a/frigate/api/preview.py +++ b/frigate/api/preview.py @@ -145,9 +145,9 @@ def preview_hour( def get_preview_frames_from_cache(camera_name: str, start_ts: float, end_ts: float): """Get list of cached preview frames""" preview_dir = os.path.join(CACHE_DIR, "preview_frames") - file_start = f"preview_{camera_name}" - start_file = f"{file_start}-{start_ts}.{PREVIEW_FRAME_TYPE}" - end_file = f"{file_start}-{end_ts}.{PREVIEW_FRAME_TYPE}" + file_start = f"preview_{camera_name}-" + start_file = f"{file_start}{start_ts}.{PREVIEW_FRAME_TYPE}" + end_file = f"{file_start}{end_ts}.{PREVIEW_FRAME_TYPE}" selected_previews = [] for file in sorted(os.listdir(preview_dir)): diff --git a/frigate/data_processing/post/review_descriptions.py b/frigate/data_processing/post/review_descriptions.py index d065ed451..57bf0f7d1 100644 --- a/frigate/data_processing/post/review_descriptions.py +++ b/frigate/data_processing/post/review_descriptions.py @@ -324,9 +324,9 @@ class ReviewDescriptionProcessor(PostProcessorApi): end_time: float, ) -> list[str]: preview_dir = os.path.join(CACHE_DIR, "preview_frames") - file_start = f"preview_{camera}" - start_file = f"{file_start}-{start_time}.webp" - end_file = f"{file_start}-{end_time}.webp" + file_start = f"preview_{camera}-" + start_file = f"{file_start}{start_time}.webp" + end_file = f"{file_start}{end_time}.webp" all_frames = [] for file in sorted(os.listdir(preview_dir)): diff --git a/frigate/genai/ollama.py b/frigate/genai/ollama.py index 74c5b7a89..0bfb95000 100644 --- a/frigate/genai/ollama.py +++ b/frigate/genai/ollama.py @@ -54,12 +54,16 @@ class OllamaClient(GenAIClient): return None @staticmethod - def _clean_schema_for_ollama(schema: dict) -> dict: + def _clean_schema_for_ollama(schema: dict, *, _is_properties: bool = False) -> dict: """Strip Pydantic metadata from a JSON schema for Ollama compatibility. Ollama's grammar-based constrained generation works best with minimal schemas. Pydantic adds title/description/constraint fields that can cause the grammar generator to silently skip required fields. + + Keys inside a ``properties`` dict are actual field names and must never + be stripped, even if they collide with a metadata key name (e.g. a + model field called ``title``). """ STRIP_KEYS = { "title", @@ -71,10 +75,12 @@ class OllamaClient(GenAIClient): } result = {} for key, value in schema.items(): - if key in STRIP_KEYS: + if not _is_properties and key in STRIP_KEYS: continue if isinstance(value, dict): - result[key] = OllamaClient._clean_schema_for_ollama(value) + result[key] = OllamaClient._clean_schema_for_ollama( + value, _is_properties=(key == "properties") + ) elif isinstance(value, list): result[key] = [ OllamaClient._clean_schema_for_ollama(item) diff --git a/frigate/output/preview.py b/frigate/output/preview.py index b66c1298a..2c439038a 100644 --- a/frigate/output/preview.py +++ b/frigate/output/preview.py @@ -266,8 +266,8 @@ class PreviewRecorder: .timestamp() ) - file_start = f"preview_{config.name}" - start_file = f"{file_start}-{start_ts}.webp" + file_start = f"preview_{config.name}-" + start_file = f"{file_start}{start_ts}.webp" for file in sorted(os.listdir(os.path.join(CACHE_DIR, FOLDER_PREVIEW_FRAMES))): if not file.startswith(file_start): diff --git a/frigate/record/export.py b/frigate/record/export.py index c1c478ef4..59a19c49c 100644 --- a/frigate/record/export.py +++ b/frigate/record/export.py @@ -156,9 +156,9 @@ class RecordingExporter(threading.Thread): else: # need to generate from existing images preview_dir = os.path.join(CACHE_DIR, "preview_frames") - file_start = f"preview_{self.camera}" - start_file = f"{file_start}-{self.start_time}.{PREVIEW_FRAME_TYPE}" - end_file = f"{file_start}-{self.end_time}.{PREVIEW_FRAME_TYPE}" + file_start = f"preview_{self.camera}-" + start_file = f"{file_start}{self.start_time}.{PREVIEW_FRAME_TYPE}" + end_file = f"{file_start}{self.end_time}.{PREVIEW_FRAME_TYPE}" selected_preview = None for file in sorted(os.listdir(preview_dir)): @@ -264,9 +264,9 @@ class RecordingExporter(threading.Thread): if is_current_hour(self.start_time): # get list of current preview frames preview_dir = os.path.join(CACHE_DIR, "preview_frames") - file_start = f"preview_{self.camera}" - start_file = f"{file_start}-{self.start_time}.{PREVIEW_FRAME_TYPE}" - end_file = f"{file_start}-{self.end_time}.{PREVIEW_FRAME_TYPE}" + file_start = f"preview_{self.camera}-" + start_file = f"{file_start}{self.start_time}.{PREVIEW_FRAME_TYPE}" + end_file = f"{file_start}{self.end_time}.{PREVIEW_FRAME_TYPE}" for file in sorted(os.listdir(preview_dir)): if not file.startswith(file_start):