import numpy as np try: import faiss from sentence_transformers import SentenceTransformer except Exception as e: print(f"VectorService: Failed to import dependencies: {e}") faiss = None SentenceTransformer = None class VectorService: def __init__(self): self.model = None self.index = None self.chunks = [] if SentenceTransformer: # Load model once. This might be slow on startup. print("Loading generic embedding model (all-MiniLM-L6-v2)...") try: self.model = SentenceTransformer("all-MiniLM-L6-v2") print("Embedding model loaded successfully.") except Exception as e: print(f"Failed to load embedding model: {e}") def create_index_from_results(self, results: list): """ Takes a list of search result dicts, creates embeddings, and builds a FAISS index. """ if not self.model or not faiss: print("VectorService: Dependencies missing or model not loaded.") return self.chunks = [] texts_to_embed = [] for res in results: # Combine Title and Content for a rich embedding context text = f"Title: {res.get('title', '')}\nContent: {res.get('content', '')}" self.chunks.append(res) # Keep reference to original object texts_to_embed.append(text) if not texts_to_embed: return try: embeddings = self.model.encode(texts_to_embed) dimension = embeddings.shape[1] self.index = faiss.IndexFlatL2(dimension) self.index.add(np.array(embeddings)) print(f"VectorService: Created FAISS index with {self.index.ntotal} vectors") except Exception as e: print(f"VectorService Error during indexing: {e}") def search_similar(self, query: str, k: int = 3): """ Searches the FAISS index for the most relevant chunks to the query. """ if not self.index or not self.model: return [] try: query_emb = self.model.encode([query]) distances, indices = self.index.search(query_emb, k) top_results = [] for idx in indices[0]: if idx < len(self.chunks) and idx >= 0: top_results.append(self.chunks[idx]) return top_results except Exception as e: print(f"VectorService Error during search: {e}") return [] vector_service = VectorService()