| 65 | |
| 66 | @dataclass |
| 67 | class NanoVectorDBStorage(BaseVectorStorage): |
| 68 | cosine_better_than_threshold: float = 0.2 |
| 69 | |
| 70 | def __post_init__(self): |
| 71 | self._client_file_name = os.path.join( |
| 72 | self.global_config["working_dir"], f"vdb_{self.namespace}.json" |
| 73 | ) |
| 74 | self._max_batch_size = self.global_config["embedding_batch_num"] |
| 75 | self._client = NanoVectorDB( |
| 76 | self.embedding_func.embedding_dim, storage_file=self._client_file_name |
| 77 | ) |
| 78 | self.cosine_better_than_threshold = self.global_config.get( |
| 79 | "cosine_better_than_threshold", self.cosine_better_than_threshold |
| 80 | ) |
| 81 | |
| 82 | async def upsert(self, data: dict[str, dict]): |
| 83 | logger.info(f"Inserting {len(data)} vectors to {self.namespace}") |
| 84 | if not len(data): |
| 85 | logger.warning("You insert an empty data to vector DB") |
| 86 | return [] |
| 87 | list_data = [ |
| 88 | { |
| 89 | "__id__": k, |
| 90 | **{k1: v1 for k1, v1 in v.items() if k1 in self.meta_fields}, |
| 91 | } |
| 92 | for k, v in data.items() |
| 93 | ] |
| 94 | contents = [v["content"] for v in data.values()] |
| 95 | batches = [ |
| 96 | contents[i : i + self._max_batch_size] |
| 97 | for i in range(0, len(contents), self._max_batch_size) |
| 98 | ] |
| 99 | embedding_tasks = [self.embedding_func(batch) for batch in batches] |
| 100 | embeddings_list = [] |
| 101 | for f in tqdm_async( |
| 102 | asyncio.as_completed(embedding_tasks), |
| 103 | total=len(embedding_tasks), |
| 104 | desc="Generating embeddings", |
| 105 | unit="batch", |
| 106 | ): |
| 107 | embeddings = await f |
| 108 | embeddings_list.append(embeddings) |
| 109 | embeddings = np.concatenate(embeddings_list) |
| 110 | for i, d in enumerate(list_data): |
| 111 | d["__vector__"] = embeddings[i] |
| 112 | results = self._client.upsert(datas=list_data) |
| 113 | return results |
| 114 | |
| 115 | async def query(self, query: str, top_k=5): |
| 116 | embedding = await self.embedding_func([query]) |
| 117 | embedding = embedding[0] |
| 118 | results = self._client.query( |
| 119 | query=embedding, |
| 120 | top_k=top_k, |
| 121 | better_than_threshold=self.cosine_better_than_threshold, |
| 122 | ) |
| 123 | results = [ |
| 124 | {**dp, "id": dp["__id__"], "distance": dp["__metrics__"]} for dp in results |
nothing calls this directly
no outgoing calls
no test coverage detected