Files
frigate-notify/tests/test_recognition.py
T
pluto 2df137ee2a feat: add recognition engine with CLIP matching and Ollama vision LLM
- OllamaClient: analyze_image, compare_images, is_available
- Recognition pipeline: CLIP embedding -> cosine similarity -> threshold
  routing (known >0.85, unknown <0.60, ambiguous -> Ollama fallback)
- Vision dispatcher: get_description() supports haiku/ollama/both backends
- 9 embedding serialization tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:17:49 -06:00

83 lines
3.0 KiB
Python

"""Tests for the recognition module — embedding serialization helpers."""
import numpy as np
import pytest
from recognition import bytes_to_embedding, embedding_to_bytes
class TestEmbeddingToBytes:
"""Tests for embedding_to_bytes()."""
def test_roundtrip_preserves_values(self):
"""Packing and unpacking should return the same values."""
original = [0.1, 0.2, 0.3, -0.5, 1.0, 0.0]
packed = embedding_to_bytes(original)
recovered = bytes_to_embedding(packed)
np.testing.assert_allclose(recovered, original, rtol=1e-6)
def test_correct_byte_length_768(self):
"""A 768-dim embedding should produce 768 * 4 = 3072 bytes."""
embedding = [0.0] * 768
packed = embedding_to_bytes(embedding)
assert len(packed) == 768 * 4
def test_correct_byte_length_arbitrary(self):
"""Byte length should always be len(embedding) * 4."""
for dim in [1, 10, 512, 1024]:
embedding = [1.0] * dim
packed = embedding_to_bytes(embedding)
assert len(packed) == dim * 4
def test_empty_embedding(self):
"""An empty embedding should produce 0 bytes."""
packed = embedding_to_bytes([])
assert len(packed) == 0
recovered = bytes_to_embedding(packed)
assert len(recovered) == 0
class TestBytesToEmbedding:
"""Tests for bytes_to_embedding()."""
def test_correct_shape(self):
"""Should return a 1-D array with the correct number of elements."""
embedding = [0.5] * 768
packed = embedding_to_bytes(embedding)
recovered = bytes_to_embedding(packed)
assert recovered.shape == (768,)
def test_correct_dtype(self):
"""Should return float32 numpy array."""
embedding = [0.1, 0.2, 0.3]
packed = embedding_to_bytes(embedding)
recovered = bytes_to_embedding(packed)
assert recovered.dtype == np.float32
def test_negative_values(self):
"""Negative values should survive roundtrip."""
original = [-1.0, -0.5, 0.0, 0.5, 1.0]
packed = embedding_to_bytes(original)
recovered = bytes_to_embedding(packed)
np.testing.assert_allclose(recovered, original, rtol=1e-6)
def test_large_values(self):
"""Large float values should survive roundtrip within float32 precision."""
original = [1e10, -1e10, 3.14159, 2.71828]
packed = embedding_to_bytes(original)
recovered = bytes_to_embedding(packed)
np.testing.assert_allclose(recovered, original, rtol=1e-5)
def test_realistic_clip_embedding(self):
"""Simulate a realistic CLIP embedding (768-dim, normalized)."""
rng = np.random.default_rng(42)
vec = rng.standard_normal(768).astype(np.float32)
vec = vec / np.linalg.norm(vec) # normalize
packed = embedding_to_bytes(vec.tolist())
recovered = bytes_to_embedding(packed)
assert recovered.shape == (768,)
assert recovered.dtype == np.float32
np.testing.assert_allclose(recovered, vec, rtol=1e-6)