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>
84 lines
2.2 KiB
Python
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")
|