Files
mule-image/backend/app/models/embeddings.py
dtoro 94c07b1d0d feat: upgrade to SigLIP2 ViT-B/16 for semantic search
Replace OpenCLIP ViT-B/32 (512-d, ~78% recall) with SigLIP2 ViT-B/16
(768-d, ~84% recall) as the default embedding model for significantly
better image-text retrieval quality.

- New SigLIP2Embedder class with 384px input and SigLIP normalization
- ONNX export pipeline for SigLIP2 visual + textual encoders
- Migration 0010: resize embeddings.vector from 512 to 768 dimensions
- Config-driven model selection: "siglip2_vitb16" (default) or
  "openclip_vitb32" (legacy) — both models can coexist
- Content classifier follows the configured embedder family
- Existing embeddings cleared on migration; vision backfill regenerates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:09:16 +02:00

25 lines
950 B
Python

"""
Embedding model — stores CLIP/SigLIP image embeddings via pgvector.
Composite PK (photo_id, model) allows re-embedding with newer models
without clobbering old vectors.
Vector dimension is 768 to support SigLIP2 ViT-B/16 (the default).
OpenCLIP ViT-B/32 (512-d) embeddings are zero-padded on insert so
both models coexist in the same column. The padding is invisible to
cosine similarity (zeros don't affect the angle).
"""
from sqlalchemy import Column, String, ForeignKey, DateTime, func
from pgvector.sqlalchemy import Vector
from app.database import Base
class Embedding(Base):
__tablename__ = 'embeddings'
photo_id = Column(String, ForeignKey('photos.id', ondelete='CASCADE'), primary_key=True)
model = Column(String(64), primary_key=True) # e.g. 'siglip2_vitb16'
vector = Column(Vector(768)) # SigLIP2 ViT-B/16 → 768-d
created_at = Column(DateTime(timezone=True), server_default=func.now())