"""Vector storage and retrieval with sqlite-vec.""" import sqlite3 import numpy as np import sqlite_vec def embedding_store_sqlvec(db_path: str, table: str, ids: list, embeddings: list, dim: int = 384) -> int: """Inserta embeddings en una tabla sqlite-vec. Crea la tabla virtual si no existe. Inserta en batches de 500. Args: db_path: Path al archivo SQLite. table: Nombre de la tabla virtual vec0. ids: Lista de IDs (int) para cada embedding. embeddings: Lista de arrays numpy float32. dim: Dimension de los embeddings (default 384 para e5-small). Returns: Numero de embeddings insertados. Raises: sqlite3.Error: Si hay error de escritura en la BD. """ db = sqlite3.connect(db_path) db.enable_load_extension(True) sqlite_vec.load(db) db.execute(f"CREATE VIRTUAL TABLE IF NOT EXISTS [{table}] USING vec0(embedding float[{dim}])") batch_size = 500 count = 0 for i in range(0, len(ids), batch_size): batch = [ (int(ids[j]), np.asarray(embeddings[j], dtype=np.float32).tobytes()) for j in range(i, min(i + batch_size, len(ids))) ] db.executemany(f"INSERT INTO [{table}](rowid, embedding) VALUES (?, ?)", batch) count += len(batch) db.commit() db.close() return count def embedding_search_sqlvec(db_path: str, table: str, query_embedding: list, k: int = 10) -> list: """Busca los k vecinos mas cercanos en una tabla sqlite-vec. Args: db_path: Path al archivo SQLite con la tabla vec0. table: Nombre de la tabla virtual. query_embedding: Array numpy float32 del query. k: Numero de resultados a retornar. Returns: Lista de dicts con 'rowid' y 'distance' ordenados por cercania. Raises: sqlite3.Error: Si la tabla no existe o hay error de lectura. """ db = sqlite3.connect(db_path) db.enable_load_extension(True) sqlite_vec.load(db) q_bytes = np.asarray(query_embedding, dtype=np.float32).tobytes() rows = db.execute( f"SELECT rowid, distance FROM [{table}] WHERE embedding MATCH ? ORDER BY distance LIMIT ?", (q_bytes, k), ).fetchall() db.close() return [{"rowid": r[0], "distance": r[1]} for r in rows]