feat(fc2b): add tag_and_embed + backfill Celery tasks
tag_and_embed: Camie + SigLIP on one image (video → 10-frame sample, max-pool tags, mean-pool embeddings), stores predictions/embedding with model versions, then enqueues per-image allowlist apply. backfill: keyset-paginated discovery of images missing predictions/embeddings for the current model versions (restart-safe). apply_allowlist_tags stub included so .delay() resolves between commits (filled in Task 9). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""tag_and_embed / backfill task tests. Models aren't in CI, so we test
|
||||
the pure helpers (_maxpool_predictions, _is_video) as unit tests, and the
|
||||
DB-touching backfill query as an integration test with monkeypatched
|
||||
inference.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from backend.app.services.ml.tagger import TagPrediction
|
||||
from backend.app.tasks.ml import _is_video, _maxpool_predictions
|
||||
|
||||
|
||||
def test_is_video():
|
||||
assert _is_video(Path("a.mp4")) is True
|
||||
assert _is_video(Path("a.MKV")) is True
|
||||
assert _is_video(Path("a.jpg")) is False
|
||||
|
||||
|
||||
def test_maxpool_predictions():
|
||||
f1 = {"smile": TagPrediction("smile", "general", 0.6)}
|
||||
f2 = {
|
||||
"smile": TagPrediction("smile", "general", 0.9),
|
||||
"sword": TagPrediction("sword", "general", 0.7),
|
||||
}
|
||||
merged = _maxpool_predictions([f1, f2])
|
||||
assert merged["smile"]["confidence"] == 0.9
|
||||
assert merged["sword"]["confidence"] == 0.7
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.asyncio
|
||||
async def test_backfill_enqueues_missing(db, monkeypatch):
|
||||
from backend.app.models import ImageRecord
|
||||
from backend.app.tasks import ml as ml_tasks
|
||||
|
||||
calls = []
|
||||
monkeypatch.setattr(
|
||||
ml_tasks.tag_and_embed, "delay", lambda image_id: calls.append(image_id)
|
||||
)
|
||||
|
||||
img = ImageRecord(
|
||||
path="/images/n.jpg", sha256="n" * 64, size_bytes=1,
|
||||
mime="image/jpeg", width=1, height=1,
|
||||
origin="imported_filesystem", integrity_status="unknown",
|
||||
tagger_predictions=None, siglip_embedding=None,
|
||||
)
|
||||
db.add(img)
|
||||
await db.commit()
|
||||
|
||||
count = ml_tasks.backfill()
|
||||
assert count >= 1
|
||||
assert img.id in calls
|
||||
Reference in New Issue
Block a user