Ir para o conteúdo

multi_model_store

module wittgenstein_pgvector_qsql.multi_model_store

MultiModelQSQLStore — one row per Q→SQL pair, one vector COLUMN per model.

Purpose-built for comparing embedding providers/models side by side without touching the production langchain_pg_embedding table (which LangChain's PGVector manages as ONE shared embedding column across every collection, and which in this platform's actual databases already carries a real HNSW index — HNSW requires a uniform dimension across every indexed row, so a different-dimension model cannot share that column/index no matter what collection_name it's given).

Each model gets its own embedding_<model_slug> column, sized to that model's exact dimension, with its own dedicated HNSW index — so comparing a 768-dim local model against the production 1536-dim OpenAI model never requires relaxing or reindexing anything already in use. Adding a new model is ALTER TABLE ... ADD COLUMN IF NOT EXISTS + CREATE INDEX IF NOT EXISTS, never a destructive migration.

Promoting a winning model to production (if its dimension differs from what's already live in langchain_pg_embedding) is a separate, deliberate step this store does not perform — see training/reindex_embeddings.py's module docstring.

Classes

wittgenstein_pgvector_qsql.multi_model_store.MultiModelQSQLStore

class MultiModelQSQLStore(pg_dsn: str)

Q→SQL pair store with one embedding column per model.

Schema (auto-created, additive only)

qsql_pair_embeddings( id BIGSERIAL PRIMARY KEY, question TEXT NOT NULL, sql TEXT NOT NULL, question_hash TEXT UNIQUE NOT NULL, embedding_ VECTOR(dim_1), embedding_ VECTOR(dim_2), ... )

Methods

  • ensure_model_column — Add embedding_<model_slug> (+ its HNSW index) if not present.

  • upsert_pair — Insert the pair if new (by question_hash); return its id either way.

  • set_embedding

  • search — Return the k most similar (question, sql, similarity) for model_slug's column.

  • pairs_missing_embedding — Return (id, question) of every pair whose model_slug column is NULL.

wittgenstein_pgvector_qsql.multi_model_store.MultiModelQSQLStore.ensure_model_column

method MultiModelQSQLStore.ensure_model_column(model_slug: str, dimension: int) → None

Add embedding_<model_slug> (+ its HNSW index) if not present.

Safe to call every run — IF NOT EXISTS throughout, never drops or resizes an existing column (a dimension mismatch on a pre-existing column raises rather than silently reinterpreting data).

Raises

  • RuntimeError

wittgenstein_pgvector_qsql.multi_model_store.MultiModelQSQLStore.upsert_pair

method MultiModelQSQLStore.upsert_pair(question: str, sql: str, question_hash: str) → int

Insert the pair if new (by question_hash); return its id either way.

wittgenstein_pgvector_qsql.multi_model_store.MultiModelQSQLStore.set_embedding

method MultiModelQSQLStore.set_embedding(model_slug: str, pair_id: int, embedding: list[float]) → None

wittgenstein_pgvector_qsql.multi_model_store.MultiModelQSQLStore.search

method MultiModelQSQLStore.search(model_slug: str, query_embedding: list[float], k: int = 3) → list[dict]

Return the k most similar (question, sql, similarity) for model_slug's column.

similarity is 1 − cosine distance (the <=> operator), so higher is closer, matching what LangChain's similarity_search_with_score consumers expect after the same conversion.

wittgenstein_pgvector_qsql.multi_model_store.MultiModelQSQLStore.pairs_missing_embedding

method MultiModelQSQLStore.pairs_missing_embedding(model_slug: str) → list[tuple[int, str]]

Return (id, question) of every pair whose model_slug column is NULL.

This is what makes reindexing-on-model-change safe to run on every deploy: pairs already embedded for this model are skipped, new pairs (or a brand-new model column) get backfilled.