Files
resolutionflow/backend/alembic/versions/a7c9e3b1f402_add_ai_session_embeddings_table.py
chihlasm e356103408 feat(search): add semantic similar session matching via Voyage AI embeddings
Adds vector-based similar session discovery using the existing Voyage AI
embedding infrastructure and pgvector cosine similarity search.

- New AISessionEmbedding model with vector(1024) column
- session_embedding_service: generate + upsert embeddings, find similar sessions
- Embeddings generated on session create (from problem_summary/domain) and
  updated on resolve (adds resolution_summary)
- GET /ai-sessions/{id}/similar endpoint returns top-N similar sessions
- Migration a7c9e3b1f402 creates ai_session_embeddings table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 03:48:09 +00:00

84 lines
2.2 KiB
Python

"""add ai_session_embeddings table for similar-session matching
Revision ID: a7c9e3b1f402
Revises: dbf67047d4c8
Create Date: 2026-03-20
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = "a7c9e3b1f402"
down_revision: Union[str, None] = "dbf67047d4c8"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# pgvector extension should already exist from migration 042
op.execute("CREATE EXTENSION IF NOT EXISTS vector")
op.create_table(
"ai_session_embeddings",
sa.Column(
"id",
postgresql.UUID(as_uuid=True),
primary_key=True,
server_default=sa.text("gen_random_uuid()"),
),
sa.Column(
"session_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("ai_sessions.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column(
"account_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("accounts.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("chunk_text", sa.Text(), nullable=False),
sa.Column(
"embedding_model",
sa.String(50),
nullable=False,
server_default="voyage-3.5",
),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
),
)
# Add vector column via raw SQL (pgvector type not in SA dialect)
op.execute(
"ALTER TABLE ai_session_embeddings ADD COLUMN embedding vector(1024)"
)
op.create_index(
"ix_ai_session_embeddings_session_id",
"ai_session_embeddings",
["session_id"],
unique=True,
)
op.create_index(
"ix_ai_session_embeddings_account_id",
"ai_session_embeddings",
["account_id"],
)
def downgrade() -> None:
op.drop_table("ai_session_embeddings")