from datetime import datetime, timezone from pgvector.sqlalchemy import Vector from sqlalchemy import DateTime, ForeignKey, Integer from sqlalchemy.orm import Mapped, mapped_column from scribe.models import Base # bge-small-en-v1.5 produces 384-dim unit-normalized vectors. The column is a # native pgvector `vector(384)` (see migration 0067) so similarity search runs # as an indexed `ORDER BY embedding <=> :q LIMIT k` in Postgres rather than a # full-table Python cosine scan. EMBEDDING_DIM = 384 class NoteEmbedding(Base): """Stores the embedding vector for a note, used for semantic search.""" __tablename__ = "note_embeddings" note_id: Mapped[int] = mapped_column( Integer, ForeignKey("notes.id", ondelete="CASCADE"), primary_key=True, ) user_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True) embedding: Mapped[list] = mapped_column(Vector(EMBEDDING_DIM), nullable=False) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), )