Files
mule-image/backend/alembic/versions/0005_face_embeddings.py
dtoro ad007e4cd4 feat: add face detection, recognition, and clustering
- Create face_embeddings table with pgvector Vector(128) + HNSW index
- Implement extract_faces task (YuNet detection + SFace recognition)
- Implement recluster_faces task (DBSCAN clustering → Tag(kind=face_cluster))
- Clusters are named "Person N" and get representative_photo_id
- cluster_id FK → tags.id, SET NULL on delete for merge/rename support

Migration 0005 creates the face_embeddings table.

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

42 lines
1.3 KiB
Python

"""face_embeddings table
Revision ID: 0005_face_embeddings
Revises: 0004_ocr_fts
Create Date: 2026-04-10
Create face_embeddings table with pgvector Vector(128) for SFace
recognition embeddings and HNSW index.
"""
from typing import Sequence, Union
from alembic import op
revision: str = "0005_face_embeddings"
down_revision: Union[str, None] = "0004_ocr_fts"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute("""
CREATE TABLE IF NOT EXISTS face_embeddings (
id VARCHAR PRIMARY KEY,
photo_id VARCHAR NOT NULL REFERENCES photos(id) ON DELETE CASCADE,
bbox JSONB,
vector vector(128),
cluster_id VARCHAR REFERENCES tags(id) ON DELETE SET NULL,
quality FLOAT,
created_at TIMESTAMPTZ DEFAULT now()
)
""")
op.execute("CREATE INDEX IF NOT EXISTS ix_face_embeddings_photo_id ON face_embeddings(photo_id)")
op.execute("CREATE INDEX IF NOT EXISTS ix_face_embeddings_cluster_id ON face_embeddings(cluster_id)")
op.execute("""
CREATE INDEX IF NOT EXISTS ix_face_embeddings_vector_hnsw
ON face_embeddings USING hnsw (vector vector_cosine_ops)
""")
def downgrade() -> None:
op.execute("DROP TABLE IF EXISTS face_embeddings")