llama-index-vector-stores-awsdocdb 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.1
2
+ Name: llama-index-vector-stores-awsdocdb
3
+ Version: 0.1.4
4
+ Summary: llama-index vector_stores awsdocdb integration
5
+ License: MIT
6
+ Author: Prajwal Saokar
7
+ Author-email: prajwal.saokar@gmail.com
8
+ Requires-Python: >=3.8.1,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Requires-Dist: llama-index-core (>=0.10.1,<0.11.0)
15
+ Requires-Dist: pymongo (>=4.6.1,<5.0.0)
16
+ Description-Content-Type: text/markdown
17
+
18
+ LlamaIndex Vector_Stores Integration: AWS Document DB
19
+
@@ -0,0 +1 @@
1
+ LlamaIndex Vector_Stores Integration: AWS Document DB
@@ -0,0 +1,3 @@
1
+ from llama_index.vector_stores.awsdocdb.base import AWSDocDbVectorStore
2
+
3
+ __all__ = ["AWSDocDbVectorStore"]
@@ -0,0 +1,305 @@
1
+ """AWS Document DB Vector store index.
2
+
3
+ An index that that is built on top of an existing vector store.
4
+
5
+ """
6
+ import logging
7
+ from enum import Enum
8
+ from typing import Any, Dict, List, Optional, cast
9
+ import numpy as np
10
+
11
+ from llama_index.core.schema import BaseNode, MetadataMode, TextNode
12
+ from llama_index.core.vector_stores.types import (
13
+ MetadataFilters,
14
+ VectorStore,
15
+ VectorStoreQuery,
16
+ VectorStoreQueryResult,
17
+ )
18
+ from llama_index.core.vector_stores.utils import (
19
+ legacy_metadata_dict_to_node,
20
+ metadata_dict_to_node,
21
+ node_to_metadata_dict,
22
+ )
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ def similarity(embedding1: List, embedding2: List, mode) -> float:
28
+ embedding1 = np.array(embedding1)
29
+ embedding2 = np.array(embedding2)
30
+ if mode == AWSDocDbVectorStoreSimilarityType.Euclidean:
31
+ return -float(np.linalg.norm(np.array(embedding1) - np.array(embedding2)))
32
+ elif mode == AWSDocDbVectorStoreSimilarityType.DotProduct:
33
+ return np.dot(embedding1, embedding2)
34
+ else:
35
+ product = np.dot(embedding1, embedding2)
36
+ norm = np.linalg.norm(embedding1) * np.linalg.norm(embedding2)
37
+ return product / norm
38
+
39
+
40
+ class AWSDocDbVectorStoreSimilarityType(Enum):
41
+ Euclidean = "euclidean"
42
+ DotProduct = "dotProduct"
43
+ Cosine = "cosine"
44
+
45
+
46
+ def _to_mongodb_filter(standard_filters: MetadataFilters) -> Dict:
47
+ """Convert from standard dataclass to filter dict."""
48
+ filters = {}
49
+ for filter in standard_filters.legacy_filters():
50
+ filters[filter.key] = filter.value
51
+ return filters
52
+
53
+
54
+ class DocDbIndex:
55
+ def __init__(self, _index_name, _embedding_key, _collection) -> None:
56
+ self._index_name = _index_name
57
+ self._embedding_key = _embedding_key
58
+ self._collection = _collection
59
+
60
+ def create_index(
61
+ self,
62
+ dimensions: int,
63
+ similarity: AWSDocDbVectorStoreSimilarityType = AWSDocDbVectorStoreSimilarityType.Cosine,
64
+ ):
65
+ create_index_commands = {
66
+ "createIndexes": self._collection.name,
67
+ "indexes": [
68
+ {
69
+ "name": self._index_name,
70
+ "key": {self._embedding_key: "vector"},
71
+ "vectorOptions": {
72
+ "type": "hnsw",
73
+ "similarity": similarity,
74
+ "dimensions": dimensions,
75
+ },
76
+ }
77
+ ],
78
+ }
79
+
80
+ # retrieve the database object
81
+ current_database = self._collection.database
82
+
83
+ # invoke the command from the database object
84
+ create_index_responses: dict[str, Any] = current_database.command(
85
+ create_index_commands
86
+ )
87
+
88
+ return create_index_responses
89
+
90
+ def index_exists(self) -> bool:
91
+ cursor = self._collection.list_indexes()
92
+ index_name = self._index_name
93
+
94
+ for res in cursor:
95
+ current_index_name = res.pop("name")
96
+ if current_index_name == index_name:
97
+ return True
98
+
99
+ return False
100
+
101
+ def delete_index(self) -> None:
102
+ if self.index_exists():
103
+ self._collection.drop_index(self._index_name)
104
+ # Raises OperationFailure on an error (e.g. trying to drop
105
+ # an index that does not exist)
106
+
107
+
108
+ class AWSDocDbVectorStore(VectorStore):
109
+ """AWS DocumentDB Vector Store.
110
+
111
+ To use, you should have both:
112
+ - the ``pymongo`` python package installed
113
+ - a connection string associated with a DocumentDB Instance
114
+
115
+ Please refer to the official Vector Search documentation for more details:
116
+ https://docs.aws.amazon.com/documentdb/latest/developerguide/vector-search.html
117
+
118
+ """
119
+
120
+ stores_text: bool = True
121
+ flat_metadata: bool = True
122
+
123
+ def __init__(
124
+ self,
125
+ docdb_client: Optional[Any] = None,
126
+ db_name: str = "default_db",
127
+ index_name: str = "default_index",
128
+ collection_name: str = "default_collection",
129
+ id_key: str = "id",
130
+ embedding_key: str = "embedding",
131
+ text_key: str = "text",
132
+ metadata_key: str = "metadata",
133
+ insert_kwargs: Optional[Dict] = None,
134
+ similarity_score="cosine",
135
+ **kwargs: Any,
136
+ ) -> None:
137
+ """Initialize the vector store.
138
+
139
+ Args:
140
+ docdb_client: A DocumentDB client.
141
+ db_name: A DocumentDB database name.
142
+ collection_name: A DocumentDB collection name.
143
+ id_key: The data field to use as the id.
144
+ embedding_key: A DocumentDB field that will contain
145
+ the embedding for each document.
146
+ text_key: A DocumentDB field that will contain the text for each document.
147
+ metadata_key: A DocumentDB field that will contain
148
+ the metadata for each document.
149
+ insert_kwargs: The kwargs used during `insert`.
150
+ """
151
+ import_err_msg = "`pymongo` package not found, please run `pip install pymongo`"
152
+ try:
153
+ from pymongo import MongoClient
154
+ except ImportError:
155
+ raise ImportError(import_err_msg)
156
+
157
+ if docdb_client is not None:
158
+ self._docdb_client = cast(MongoClient, docdb_client)
159
+ else:
160
+ raise ValueError("Must specify connection string to DocumentDB instance ")
161
+ self._similarity_score = similarity_score
162
+ self._collection = self._docdb_client[db_name][collection_name]
163
+ self._embedding_key = embedding_key
164
+ self._id_key = id_key
165
+ self._text_key = text_key
166
+ self._metadata_key = metadata_key
167
+ self._insert_kwargs = insert_kwargs or {}
168
+ self._index_crud = DocDbIndex(index_name, self._embedding_key, self._collection)
169
+
170
+ def add(
171
+ self,
172
+ nodes: List[BaseNode],
173
+ **add_kwargs: Any,
174
+ ) -> List[str]:
175
+ """Add nodes to index.
176
+
177
+ Args:
178
+ nodes: List[BaseNode]: list of nodes with embeddings
179
+
180
+ Returns:
181
+ A List of ids for successfully added nodes.
182
+
183
+ """
184
+ ids = []
185
+ data_to_insert = []
186
+ for node in nodes:
187
+ metadata = node_to_metadata_dict(
188
+ node, remove_text=True, flat_metadata=self.flat_metadata
189
+ )
190
+
191
+ entry = {
192
+ self._id_key: node.node_id,
193
+ self._embedding_key: node.get_embedding(),
194
+ self._text_key: node.get_content(metadata_mode=MetadataMode.NONE) or "",
195
+ self._metadata_key: metadata,
196
+ }
197
+ data_to_insert.append(entry)
198
+ ids.append(node.node_id)
199
+ logger.debug("Inserting data into DocumentDB: %s", data_to_insert)
200
+ insert_result = self._collection.insert_many(
201
+ data_to_insert, **self._insert_kwargs
202
+ )
203
+ logger.debug("Result of insert: %s", insert_result)
204
+ return ids
205
+
206
+ def delete(self, ref_doc_id: str, **delete_kwargs: Any) -> None:
207
+ """
208
+ Delete nodes using by id.
209
+
210
+ Args:
211
+ ref_doc_id (str): The doc_id of the document to delete.
212
+
213
+ """
214
+ if ref_doc_id is None:
215
+ raise ValueError("No document id provided to delete.")
216
+ self._collection.delete_one({self._metadata_key + ".ref_doc_id": ref_doc_id})
217
+
218
+ @property
219
+ def client(self) -> Any:
220
+ """Return DocDB client."""
221
+ return self._docdb_client
222
+
223
+ def _query(
224
+ self, query: VectorStoreQuery, projection: Optional[Dict[str, int]] = None
225
+ ) -> VectorStoreQueryResult:
226
+ params: Dict[str, Any] = {
227
+ "vector": query.query_embedding,
228
+ "path": self._embedding_key,
229
+ "similarity": self._similarity_score,
230
+ "k": query.similarity_top_k,
231
+ }
232
+ if query.filters:
233
+ params["filter"] = _to_mongodb_filter(query.filters)
234
+
235
+ if projection is None:
236
+ pipeline = [{"$search": {"vectorSearch": params}}]
237
+ else:
238
+ pipeline = [{"$search": {"vectorSearch": params}}, {"$project": projection}]
239
+ logger.debug("Running query pipeline: %s", pipeline)
240
+ cursor = self._collection.aggregate(pipeline) # type: ignore
241
+ top_k_nodes = []
242
+ top_k_ids = []
243
+ top_k_scores = []
244
+ for res in cursor:
245
+ text = res.pop(self._text_key)
246
+ vector = res.pop(self._embedding_key)
247
+ id = res.pop(self._id_key)
248
+ metadata_dict = res.pop(self._metadata_key)
249
+ score = similarity(query.query_embedding, vector, self._similarity_score)
250
+
251
+ try:
252
+ node = metadata_dict_to_node(metadata_dict)
253
+ node.set_content(text)
254
+ except Exception:
255
+ # NOTE: deprecated legacy logic for backward compatibility
256
+ metadata, node_info, relationships = legacy_metadata_dict_to_node(
257
+ metadata_dict
258
+ )
259
+
260
+ node = TextNode(
261
+ text=text,
262
+ id_=id,
263
+ metadata=metadata,
264
+ start_char_idx=node_info.get("start", None),
265
+ end_char_idx=node_info.get("end", None),
266
+ relationships=relationships,
267
+ )
268
+
269
+ top_k_ids.append(id)
270
+ top_k_nodes.append(node)
271
+ top_k_scores.append(score)
272
+ result = VectorStoreQueryResult(
273
+ nodes=top_k_nodes, similarities=top_k_scores, ids=top_k_ids
274
+ )
275
+ logger.debug("Result of query: %s", result)
276
+ return result
277
+
278
+ def query(
279
+ self,
280
+ query: VectorStoreQuery,
281
+ projection: Optional[Dict[str, int]] = None,
282
+ **kwargs: Any,
283
+ ) -> VectorStoreQueryResult:
284
+ """Query index for top k most similar nodes.
285
+
286
+ Args:
287
+ query: a VectorStoreQuery object.
288
+ projection: a dictionary specifying which fields to return after the search
289
+
290
+ Returns:
291
+ A VectorStoreQueryResult containing the results of the query.
292
+ """
293
+ return self._query(query, projection=projection)
294
+
295
+ def create_index(self, dimensions, similarity_score=None):
296
+ score = self._similarity_score
297
+ if similarity_score is not None:
298
+ score = similarity
299
+ return self._index_crud.create_index(dimensions, score)
300
+
301
+ def delete_index(self):
302
+ return self._index_crud.delete_index()
303
+
304
+ def __del__(self) -> None:
305
+ self.docdb_client.close()
@@ -0,0 +1,63 @@
1
+ [build-system]
2
+ build-backend = "poetry.core.masonry.api"
3
+ requires = ["poetry-core"]
4
+
5
+ [tool.codespell]
6
+ check-filenames = true
7
+ check-hidden = true
8
+ skip = "*.csv,*.html,*.json,*.jsonl,*.pdf,*.txt,*.ipynb"
9
+
10
+ [tool.llamahub]
11
+ contains_example = false
12
+ import_path = "llama_index.vector_stores.awsdocdb"
13
+
14
+ [tool.llamahub.class_authors]
15
+ AWSDocDbVectorStore = "llama-index"
16
+
17
+ [tool.mypy]
18
+ disallow_untyped_defs = true
19
+ exclude = ["_static", "build", "examples", "notebooks", "venv"]
20
+ ignore_missing_imports = true
21
+ python_version = "3.8"
22
+
23
+ [tool.poetry]
24
+ authors = ["Prajwal Saokar <prajwal.saokar@gmail.com>", "Vidur Modgil", "vmod2005@gmail.com"]
25
+ description = "llama-index vector_stores awsdocdb integration"
26
+ exclude = ["**/BUILD"]
27
+ license = "MIT"
28
+ name = "llama-index-vector-stores-awsdocdb"
29
+ readme = "README.md"
30
+ version = "0.1.4"
31
+
32
+ [tool.poetry.dependencies]
33
+ python = ">=3.8.1,<4.0"
34
+ llama-index-core = "^0.10.1"
35
+ pymongo = "^4.6.1"
36
+
37
+ [tool.poetry.group.dev.dependencies]
38
+ ipython = "8.10.0"
39
+ jupyter = "^1.0.0"
40
+ mypy = "0.991"
41
+ pre-commit = "3.2.0"
42
+ pylint = "2.15.10"
43
+ pytest = "7.2.1"
44
+ pytest-mock = "3.11.1"
45
+ ruff = "0.0.292"
46
+ tree-sitter-languages = "^1.8.0"
47
+ types-Deprecated = ">=0.1.0"
48
+ types-PyYAML = "^6.0.12.12"
49
+ types-protobuf = "^4.24.0.4"
50
+ types-redis = "4.5.5.0"
51
+ types-requests = "2.28.11.8"
52
+ types-setuptools = "67.1.0.0"
53
+
54
+ [tool.poetry.group.dev.dependencies.black]
55
+ extras = ["jupyter"]
56
+ version = "<=23.9.1,>=23.7.0"
57
+
58
+ [tool.poetry.group.dev.dependencies.codespell]
59
+ extras = ["toml"]
60
+ version = ">=v2.2.6"
61
+
62
+ [[tool.poetry.packages]]
63
+ include = "llama_index/"