Datavision / backend /vector /store_faiss.py
DataVision CI/CD Bot
release: clean production build for HuggingFace Space
09801ca
Raw
History Blame Contribute Delete
4.56 kB
# FAISS vector store module
import faiss
import pickle
import numpy as np
from config.settings import Settings
from core.llm import embed_text
class FaissStore:
def __init__(self):
self.index = faiss.IndexFlatL2(Settings.EMBED_DIM)
self.meta = []
def add(self, emb, meta):
self.index.add(np.array(emb, dtype="float32"))
self.meta.extend(meta)
def clear(self):
"""Clear all vectors and metadata - for retraining"""
self.index = faiss.IndexFlatL2(Settings.EMBED_DIM)
self.meta = []
print("๐Ÿ—‘๏ธ FAISS store cleared for fresh retraining")
def save(self, user_id: str = "user_001"):
"""Save FAISS index to per-user directory"""
from pathlib import Path
# Use per-user directory
if user_id:
user_faiss_dir = Settings.STORAGE / "users" / user_id / "faiss"
user_faiss_dir.mkdir(parents=True, exist_ok=True)
else:
user_faiss_dir = Settings.FAISS_DIR
user_faiss_dir.mkdir(parents=True, exist_ok=True)
idx_path = user_faiss_dir / "index.faiss"
meta_path = user_faiss_dir / "meta.pkl"
print(f"๐Ÿ’พ Saving FAISS to: {idx_path}")
faiss.write_index(self.index, str(idx_path))
with open(meta_path, "wb") as f:
pickle.dump(self.meta, f)
print(f"โœ… FAISS saved: {self.index.ntotal} vectors, {len(self.meta)} metadata entries")
@staticmethod
def load_or_create(user_id: str = "user_001", fresh: bool = False):
"""
Load or create FAISS store for specific user.
Args:
user_id: User identifier
fresh: If True, create fresh store ignoring existing data (for retraining)
"""
from pathlib import Path
store = FaissStore()
# If fresh=True, return empty store for clean retraining
if fresh:
print(f"๐Ÿ†• Creating fresh FAISS store for user {user_id}")
return store
# Use per-user directory
if user_id:
user_faiss_dir = Settings.STORAGE / "users" / user_id / "faiss"
idx = user_faiss_dir / "index.faiss"
meta = user_faiss_dir / "meta.pkl"
else:
idx = Settings.FAISS_DIR / "index.faiss"
meta = Settings.FAISS_DIR / "meta.pkl"
print(f"๐Ÿ“‚ Loading FAISS from: {idx}")
if idx.exists():
store.index = faiss.read_index(str(idx))
print(f"โœ… FAISS loaded: {store.index.ntotal} vectors")
else:
print(f"โš ๏ธ FAISS index not found at {idx}, creating new")
if meta.exists():
store.meta = pickle.load(open(meta, "rb"))
print(f"โœ… Metadata loaded: {len(store.meta)} entries")
else:
print(f"โš ๏ธ Metadata not found at {meta}")
return store
@staticmethod
def delete_index(user_id: str = "user_001"):
"""Delete FAISS index files for user - for clean retraining"""
from pathlib import Path
import shutil
if user_id:
user_faiss_dir = Settings.STORAGE / "users" / user_id / "faiss"
else:
user_faiss_dir = Settings.FAISS_DIR
if user_faiss_dir.exists():
shutil.rmtree(user_faiss_dir)
user_faiss_dir.mkdir(parents=True, exist_ok=True)
print(f"๐Ÿ—‘๏ธ Deleted FAISS index for user {user_id}")
def search(self, query, k=5):
"""Search with automatic query embedding"""
if self.index.ntotal == 0:
print("โš ๏ธ FAISS index is empty - no vectors to search")
return []
# Embed query text if it's a string
if isinstance(query, str):
query_vector = embed_text(query)
if query_vector is None:
print("โš ๏ธ Failed to embed query")
return []
else:
query_vector = query
query_vector = np.array([query_vector], dtype="float32")
# Limit k to available vectors
actual_k = min(k, self.index.ntotal)
_, ids = self.index.search(query_vector, actual_k)
results = []
for i in ids[0]:
if 0 <= i < len(self.meta):
meta = self.meta[i]
results.append({
"text": meta.get("text", ""),
"metadata": meta
})
return results