Skip to content

Phase 1: classifier embedding → photo_embeddings table #646

Description

@jss367

Context

First implementation phase of the storage-philosophy roadmap (#645). Generalizes the per-model caching pattern PR #636 established for detections/predictions to the classifier embedding slot.

Problem

photos.embedding is currently a single BLOB slot tagged by photos.embedding_model. Re-running a different classifier overwrites the previous embedding. After PR #636 made classifier_runs a workspace-shared skip set, switching models in workspace B silently destroys workspace A's embedding even though A's predictions remain cached — A then renders with B's embedding under the hood.

What to build

New table:

CREATE TABLE photo_embeddings (
    photo_id    INTEGER NOT NULL REFERENCES photos(id) ON DELETE CASCADE,
    model       TEXT NOT NULL,              -- e.g. "bioclip-2"
    variant     TEXT NOT NULL DEFAULT '',
    embedding   BLOB NOT NULL,
    created_at  TEXT NOT NULL DEFAULT (datetime('now')),
    PRIMARY KEY (photo_id, model, variant)
);

Migration

Backfill from existing photos.embedding + embedding_model (where embedding IS NOT NULL), then drop both columns.

Readers to update

  • vireo/classify_job.py:store_photo_embeddingupsert_photo_embedding(pid, model, embedding)
  • vireo/db.py:get_photo_embedding(photo_id) → take optional model param
  • vireo/db.py:get_embeddings_by_model(model) — already filters by model name, just point at new table
  • /api/species/<n>/clusters (vireo/app.py)
  • /api/photos/<id>/similar (vireo/app.py)

Tests

Mirror PR #636 pattern: db-level tests for the upsert/get helpers + an integration test that classifies with two different models and asserts both embeddings survive.

Reference

Full design at docs/plans/2026-04-24-storage-philosophy-design.md (Phase 1 section).

Tracker: #645

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions