Spaces:
Sleeping
Sleeping
| import pickle | |
| from pathlib import Path | |
| import faiss | |
| import numpy as np | |
| from PIL import Image | |
| class VectorStore: | |
| """Read/write the FAISS index and metadata, serve nearest-neighbour results.""" | |
| INDEX_PATH = Path("data/index.faiss") | |
| META_PATH = Path("data/metadata.pkl") | |
| def __init__(self): | |
| self._index = None | |
| self._metadata = [] | |
| self._try_load_from_disk() | |
| def is_index_loaded(self): | |
| return self._index is not None and self._index.ntotal > 0 | |
| def add(self, vectors, metadata): | |
| if self._index is None: | |
| dim = vectors.shape[1] | |
| self._index = faiss.IndexFlatIP(dim) | |
| self._index.add(vectors.astype("float32")) | |
| self._metadata.extend(metadata) | |
| self._save_to_disk() | |
| def search(self, query_vec, k=6): | |
| if not self.is_index_loaded(): | |
| return [] | |
| q = query_vec.astype("float32").reshape(1, -1) | |
| scores, indices = self._index.search(q, k) | |
| results = [] | |
| for score, idx in zip(scores[0], indices[0]): | |
| if idx == -1: | |
| continue | |
| item = dict(self._metadata[idx]) | |
| item["score"] = float(score) | |
| if "image" not in item and "image_url" in item: | |
| item["image"] = self._load_image(item["image_url"]) | |
| results.append(item) | |
| return results | |
| def _try_load_from_disk(self): | |
| if self.INDEX_PATH.exists() and self.META_PATH.exists(): | |
| self._index = faiss.read_index(str(self.INDEX_PATH)) | |
| with open(self.META_PATH, "rb") as f: | |
| self._metadata = pickle.load(f) | |
| def _save_to_disk(self): | |
| self.INDEX_PATH.parent.mkdir(parents=True, exist_ok=True) | |
| faiss.write_index(self._index, str(self.INDEX_PATH)) | |
| with open(self.META_PATH, "wb") as f: | |
| pickle.dump(self._metadata, f) | |
| def _load_image(url): | |
| try: | |
| import requests | |
| from io import BytesIO | |
| resp = requests.get(url, timeout=5) | |
| return Image.open(BytesIO(resp.content)).convert("RGB") | |
| except Exception: | |
| return None |