"""Pinned dense-index backends used by E01 and E05.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path import sqlite3 import time from typing import Sequence import faiss import numpy as np import sqlite_vec from .components import Candidate from .retrieval import DenseRetriever, _candidate @dataclass(frozen=True, slots=True) class VectorBackendStats: backend: str build_seconds: float index_disk_bytes: int parameters: dict[str, int | str] def _matrix(dense: DenseRetriever) -> np.ndarray: return np.asarray([list(item) for item in dense.vectors], dtype="float32") class FaissFlatRetriever: def __init__(self, dense: DenseRetriever): started = time.monotonic() self.dense = dense matrix = _matrix(dense) self.index = faiss.IndexFlatIP(dense.spec.vector_dimension) self.index.add(matrix) self.stats = VectorBackendStats( backend="faiss_index_flat_ip", build_seconds=time.monotonic() - started, index_disk_bytes=0, parameters={"dimension": dense.spec.vector_dimension, "metric": "inner_product"}, ) def retrieve(self, query: str, limit: int) -> Sequence[Candidate]: vector = np.asarray([list(self.dense.query_vector(query))], dtype="float32") scores, indices = self.index.search(vector, min(limit, len(self.dense.chunks))) return tuple( _candidate(self.dense.chunks[int(index)], "dense_faiss_flat", float(score)) for score, index in zip(scores[0], indices[0]) if index >= 0 ) class FaissHNSWRetriever: def __init__( self, dense: DenseRetriever, neighbors: int = 32, ef_construction: int = 80, ef_search: int = 64, ): started = time.monotonic() self.dense = dense matrix = _matrix(dense) self.index = faiss.IndexHNSWFlat( dense.spec.vector_dimension, neighbors, faiss.METRIC_INNER_PRODUCT, ) self.index.hnsw.efConstruction = ef_construction self.index.hnsw.efSearch = ef_search self.index.add(matrix) self.stats = VectorBackendStats( backend="faiss_hnsw_ip", build_seconds=time.monotonic() - started, index_disk_bytes=0, parameters={ "dimension": dense.spec.vector_dimension, "metric": "inner_product", "neighbors": neighbors, "ef_construction": ef_construction, "ef_search": ef_search, }, ) def retrieve(self, query: str, limit: int) -> Sequence[Candidate]: vector = np.asarray([list(self.dense.query_vector(query))], dtype="float32") scores, indices = self.index.search(vector, min(limit, len(self.dense.chunks))) return tuple( _candidate(self.dense.chunks[int(index)], "dense_faiss_hnsw", float(score)) for score, index in zip(scores[0], indices[0]) if index >= 0 ) class SQLiteVecRetriever: def __init__(self, dense: DenseRetriever, path: Path): started = time.monotonic() self.dense = dense self.path = path path.parent.mkdir(parents=True, exist_ok=True) self.connection = sqlite3.connect(path) self.connection.enable_load_extension(True) sqlite_vec.load(self.connection) self.connection.enable_load_extension(False) self.connection.execute("DROP TABLE IF EXISTS vec_items") self.connection.execute( f"CREATE VIRTUAL TABLE vec_items USING vec0(" f"embedding float[{dense.spec.vector_dimension}] distance_metric=cosine)" ) self.connection.executemany( "INSERT INTO vec_items(rowid, embedding) VALUES (?, ?)", ((index + 1, vector.tobytes()) for index, vector in enumerate(dense.vectors)), ) self.connection.commit() self.stats = VectorBackendStats( backend="sqlite_vec_exact", build_seconds=time.monotonic() - started, index_disk_bytes=path.stat().st_size, parameters={"dimension": dense.spec.vector_dimension, "metric": "cosine"}, ) def retrieve(self, query: str, limit: int) -> Sequence[Candidate]: vector = self.dense.query_vector(query) rows = self.connection.execute( "SELECT rowid, distance FROM vec_items WHERE embedding MATCH ? AND k = ? ORDER BY distance", (vector.tobytes(), min(limit, len(self.dense.chunks))), ).fetchall() return tuple( _candidate( self.dense.chunks[int(rowid) - 1], "dense_sqlite_vec", 1.0 - float(distance), ) for rowid, distance in rows ) def close(self) -> None: self.connection.close()