diff --git a/frigate/genai/__init__.py b/frigate/genai/__init__.py index 3aca4a8fb4..4466e2a652 100644 --- a/frigate/genai/__init__.py +++ b/frigate/genai/__init__.py @@ -23,6 +23,7 @@ from frigate.genai.prompts import ( build_review_summary_prompt, ) from frigate.models import Event +from frigate.util.builtin import has_non_finite_number logger = logging.getLogger(__name__) @@ -164,6 +165,15 @@ class GenAIClient: except json.JSONDecodeError as je: logger.error("Failed to parse review description JSON: %s", je) return None + + # model_construct skips validation, so non-finite numbers that + # the validated path would have rejected have to be caught here + if has_non_finite_number(raw): + logger.error( + "Discarding review description containing non-finite numbers." + ) + return None + # observations and confidence are required on the model; fill an empty default # if the response omitted it so attribute access stays safe. raw.setdefault("observations", []) diff --git a/frigate/util/builtin.py b/frigate/util/builtin.py index 35dba2f3a7..c1a6e787cb 100644 --- a/frigate/util/builtin.py +++ b/frigate/util/builtin.py @@ -472,6 +472,18 @@ def sanitize_float(value): return value +def has_non_finite_number(value: Any) -> bool: + """Return True if any number in a parsed JSON value is NaN or infinite.""" + if isinstance(value, float): + return not math.isfinite(value) + if isinstance(value, dict): + return any(has_non_finite_number(v) for v in value.values()) + if isinstance(value, list): + return any(has_non_finite_number(v) for v in value) + + return False + + def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float: return 1 - cosine_distance(a, b)