""" Pre-compute query embeddings and cache them to disk. The ef_sweep script (and any future queries-only experiment) reads from this cache to skip the embedding step. The cache key includes the embedding model name, so changing EMBEDDING_MODEL invalidates this cache automatically and forces a recompute. Usage: uv run python scripts/cache_query_vectors.py uv run python scripts/cache_query_vectors.py --recompute """ from __future__ import annotations import argparse import os import pickle import sys import time sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src")) import numpy as np from sentence_transformers import SentenceTransformer from codesearch.config import EMBEDDING_MODEL from codesearch.data import load_codesearch CACHE_DIR = ".cache" def cache_path(model_name: str) -> str: safe = model_name.replace("/", "_") return os.path.join(CACHE_DIR, f"query_vectors_{safe}.pkl") def main() -> None: parser = argparse.ArgumentParser( description="Cache query embeddings so the ef sweep can skip the encode step." ) parser.add_argument( "--recompute", action="store_true", help="Re-embed even if the cache already exists.", ) args = parser.parse_args() path = cache_path(EMBEDDING_MODEL) if os.path.exists(path) and not args.recompute: size_mb = os.path.getsize(path) / 1024 / 1024 print(f"[skip] Cache already exists at {path} ({size_mb:.1f} MB).") print(" Pass --recompute to rebuild.") return os.makedirs(CACHE_DIR, exist_ok=True) print("[1/3] Loading eval queries (test split only)...") _, queries = load_codesearch(n=-1, queries_only=True) print(f"[2/3] Loading embedding model: {EMBEDDING_MODEL}") model = SentenceTransformer(EMBEDDING_MODEL) print(f"[3/3] Embedding {len(queries):,} queries...") t0 = time.perf_counter() vectors = model.encode( [q["query"] for q in queries], normalize_embeddings=True, show_progress_bar=True, batch_size=256, ) elapsed = time.perf_counter() - t0 print(f" Done in {elapsed:.1f}s ({elapsed / len(queries) * 1000:.2f} ms/query)") print(f"Writing cache to {path}...") with open(path, "wb") as f: pickle.dump( { "model": EMBEDDING_MODEL, "queries": queries, "vectors": np.asarray(vectors, dtype=np.float32), }, f, protocol=pickle.HIGHEST_PROTOCOL, ) size_mb = os.path.getsize(path) / 1024 / 1024 print(f"Cached {len(queries):,} query vectors ({size_mb:.1f} MB).") if __name__ == "__main__": main()