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.
- llama_index_vector_stores_awsdocdb-0.1.4/PKG-INFO +19 -0
- llama_index_vector_stores_awsdocdb-0.1.4/README.md +1 -0
- llama_index_vector_stores_awsdocdb-0.1.4/llama_index/vector_stores/awsdocdb/__init__.py +3 -0
- llama_index_vector_stores_awsdocdb-0.1.4/llama_index/vector_stores/awsdocdb/base.py +305 -0
- llama_index_vector_stores_awsdocdb-0.1.4/pyproject.toml +63 -0
|
@@ -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,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/"
|