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
-
MultiModelQSQLStore — Q→SQL pair store with one embedding column per model.
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_
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. -
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_slugcolumn 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.