refactor: strip AI pipeline to binary photo/other classifier
Drops face recognition, OCR, object detection, and semantic embeddings. The sole remaining vision task is a CLIP-based binary classifier (photography vs other); photos in "other" get needs_review=true so screenshots, documents, memes and scans can be triaged from a new filter pill in the UI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,7 +38,6 @@ from app.models import ( # noqa: E402, F401
|
||||
Tag,
|
||||
Heap,
|
||||
HeapPhoto,
|
||||
Embedding,
|
||||
)
|
||||
|
||||
config = context.config
|
||||
|
||||
65
backend/alembic/versions/0012_strip_ai_pipeline.py
Normal file
65
backend/alembic/versions/0012_strip_ai_pipeline.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Strip AI pipeline to binary classifier only
|
||||
|
||||
Revision ID: 0012_strip_ai
|
||||
Revises: 0011_sharing
|
||||
Create Date: 2026-04-14
|
||||
|
||||
Removes face recognition, OCR, object detection, and semantic embeddings.
|
||||
The remaining AI is a single binary 'photography' vs 'other' classifier
|
||||
whose output feeds Tag(kind='content_type') and a new Photo.needs_review
|
||||
flag.
|
||||
|
||||
Drops: embeddings, face_embeddings, ocr_text tables.
|
||||
Drops: photo_tags rows produced by 'vision:yolov8n' and 'vision:sface'.
|
||||
Drops: tags with kind IN ('object','scene','face_cluster').
|
||||
Drops: tags.representative_photo_id column.
|
||||
Adds: photos.needs_review (bool, default false) + partial index.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0012_strip_ai"
|
||||
down_revision: Union[str, None] = "0011_sharing"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Drop dropped-AI tables. CASCADE clears any lingering FKs/indices.
|
||||
op.execute("DROP TABLE IF EXISTS embeddings CASCADE")
|
||||
op.execute("DROP TABLE IF EXISTS face_embeddings CASCADE")
|
||||
op.execute("DROP TABLE IF EXISTS ocr_text CASCADE")
|
||||
|
||||
# Clear ML-produced photo_tags rows and their parent tags.
|
||||
op.execute(
|
||||
"DELETE FROM photo_tags WHERE source IN ('vision:yolov8n','vision:sface')"
|
||||
)
|
||||
op.execute(
|
||||
"DELETE FROM tags WHERE kind IN ('object','scene','face_cluster')"
|
||||
)
|
||||
|
||||
# Drop the face-cluster representative column.
|
||||
op.execute("ALTER TABLE tags DROP COLUMN IF EXISTS representative_photo_id")
|
||||
|
||||
# Add the needs_review flag.
|
||||
op.execute(
|
||||
"ALTER TABLE photos ADD COLUMN IF NOT EXISTS needs_review "
|
||||
"BOOLEAN NOT NULL DEFAULT false"
|
||||
)
|
||||
op.execute(
|
||||
"CREATE INDEX IF NOT EXISTS ix_photos_needs_review "
|
||||
"ON photos(needs_review) WHERE needs_review"
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Data is not recoverable on downgrade — only the schema stubs are
|
||||
# put back so a future reinstall of the old pipeline can re-populate.
|
||||
op.execute("DROP INDEX IF EXISTS ix_photos_needs_review")
|
||||
op.execute("ALTER TABLE photos DROP COLUMN IF EXISTS needs_review")
|
||||
|
||||
op.execute(
|
||||
"ALTER TABLE tags ADD COLUMN IF NOT EXISTS representative_photo_id "
|
||||
"VARCHAR REFERENCES photos(id) ON DELETE SET NULL"
|
||||
)
|
||||
35
backend/alembic/versions/0013_drop_old_content_types.py
Normal file
35
backend/alembic/versions/0013_drop_old_content_types.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""Drop legacy content_type tags from the 6-category classifier
|
||||
|
||||
Revision ID: 0013_drop_old_ct
|
||||
Revises: 0012_strip_ai
|
||||
Create Date: 2026-04-14
|
||||
|
||||
The previous classifier wrote Tag(kind='content_type', name IN
|
||||
('photograph','screenshot','document','receipt','meme','artwork')).
|
||||
The new binary classifier writes names ('photography','other'). Both
|
||||
coexisted after the cutover so users saw duplicate groupings like
|
||||
'photography' alongside 'photograph'. Drop the old names — photo_tags
|
||||
rows cascade-delete via the FK.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0013_drop_old_ct"
|
||||
down_revision: Union[str, None] = "0012_strip_ai"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
LEGACY_NAMES = ('photograph', 'screenshot', 'document', 'receipt', 'meme', 'artwork')
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute(
|
||||
"DELETE FROM tags WHERE kind = 'content_type' "
|
||||
f"AND name IN {LEGACY_NAMES}"
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
pass
|
||||
Reference in New Issue
Block a user