Wire the full embedding flow: - Rewrite Embedding model to use pgvector Vector(512) with HNSW index - Add embed_photo, vision_fanout, backfill_vision Celery tasks on dedicated `vision` queue - Hook vision_fanout into generate_thumbnails completion - Add POST /api/v1/photos/search with hybrid RRF ranking (semantic-only for now; FTS leg added in PR5) - Stub ocr_photo, detect_objects, extract_faces tasks for later PRs Migration 0003 drops/recreates the embeddings table (was never populated). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
20 lines
700 B
Python
20 lines
700 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.
|
|
"""
|
|
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. 'openclip_vitb32'
|
|
vector = Column(Vector(512)) # OpenCLIP ViT-B/32 → 512-d
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|