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>
25 lines
950 B
Python
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())
|