- detect_objects, classify_content, recluster_faces now look up the photo's user_id and set it on created Tag rows — fixes tags being invisible to the owning user due to NULL user_id - Initial admin setup creates source root at the mount root (/photos) instead of a subdirectory, since the admin owns the entire library - Revert to OpenCLIP ViT-B/32 (512-d) as default embedder — SigLIP requires transformers version alignment not yet available in the Docker image. SigLIP2 code remains for future enablement. - Add transformers to requirements for future SigLIP support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
25 lines
951 B
Python
25 lines
951 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(512)) # OpenCLIP ViT-B/32 → 512-d
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|