semantic-kernel 0.2.4.dev0__py3-none-any.whl → 0.2.5.dev0__py3-none-any.whl
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.
- semantic_kernel/{ai → connectors/ai}/chat_completion_client_base.py +1 -1
- semantic_kernel/connectors/ai/hugging_face/__init__.py +10 -0
- semantic_kernel/connectors/ai/hugging_face/services/hf_text_completion.py +95 -0
- semantic_kernel/connectors/ai/hugging_face/services/hf_text_embedding.py +63 -0
- semantic_kernel/connectors/ai/open_ai/__init__.py +29 -0
- semantic_kernel/{ai → connectors/ai}/open_ai/services/azure_chat_completion.py +2 -2
- semantic_kernel/{ai → connectors/ai}/open_ai/services/azure_text_completion.py +2 -2
- semantic_kernel/{ai → connectors/ai}/open_ai/services/azure_text_embedding.py +2 -2
- semantic_kernel/{ai → connectors/ai}/open_ai/services/open_ai_chat_completion.py +36 -4
- semantic_kernel/{ai → connectors/ai}/open_ai/services/open_ai_text_completion.py +10 -6
- semantic_kernel/{ai → connectors/ai}/open_ai/services/open_ai_text_embedding.py +2 -2
- semantic_kernel/{ai → connectors/ai}/text_completion_client_base.py +4 -2
- semantic_kernel/core_skills/time_skill.py +11 -1
- semantic_kernel/kernel.py +23 -17
- semantic_kernel/kernel_config.py +118 -109
- semantic_kernel/kernel_exception.py +4 -4
- semantic_kernel/kernel_extensions/import_skills.py +21 -21
- semantic_kernel/kernel_extensions/memory_configuration.py +10 -12
- semantic_kernel/memory/memory_query_result.py +36 -6
- semantic_kernel/memory/memory_record.py +52 -11
- semantic_kernel/memory/memory_store_base.py +74 -5
- semantic_kernel/memory/semantic_text_memory.py +92 -10
- semantic_kernel/memory/semantic_text_memory_base.py +1 -2
- semantic_kernel/memory/volatile_memory_store.py +251 -25
- semantic_kernel/orchestration/sk_function.py +32 -30
- semantic_kernel/orchestration/sk_function_base.py +11 -9
- semantic_kernel/semantic_functions/prompt_template_config.py +2 -2
- semantic_kernel/text/__init__.py +17 -0
- semantic_kernel/text/function_extension.py +23 -0
- semantic_kernel/text/text_chunker.py +250 -0
- {semantic_kernel-0.2.4.dev0.dist-info → semantic_kernel-0.2.5.dev0.dist-info}/METADATA +9 -6
- {semantic_kernel-0.2.4.dev0.dist-info → semantic_kernel-0.2.5.dev0.dist-info}/RECORD +37 -35
- semantic_kernel/ai/embeddings/embedding_index_base.py +0 -20
- semantic_kernel/ai/open_ai/__init__.py +0 -27
- semantic_kernel/memory/storage/data_entry.py +0 -36
- semantic_kernel/memory/storage/data_store_base.py +0 -36
- semantic_kernel/memory/storage/volatile_data_store.py +0 -62
- /semantic_kernel/{ai → connectors/ai}/ai_exception.py +0 -0
- /semantic_kernel/{ai → connectors/ai}/chat_request_settings.py +0 -0
- /semantic_kernel/{ai → connectors/ai}/complete_request_settings.py +0 -0
- /semantic_kernel/{ai → connectors/ai}/embeddings/embedding_generator_base.py +0 -0
- {semantic_kernel-0.2.4.dev0.dist-info → semantic_kernel-0.2.5.dev0.dist-info}/WHEEL +0 -0
|
@@ -1,10 +1,79 @@
|
|
|
1
1
|
# Copyright (c) Microsoft. All rights reserved.
|
|
2
2
|
|
|
3
|
-
from abc import
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from typing import List, Tuple
|
|
4
5
|
|
|
5
|
-
from
|
|
6
|
-
from semantic_kernel.memory.storage.data_store_base import DataStoreBase
|
|
6
|
+
from numpy import ndarray
|
|
7
7
|
|
|
8
|
+
from semantic_kernel.memory.memory_record import MemoryRecord
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
class MemoryStoreBase:
|
|
12
|
+
@abstractmethod
|
|
13
|
+
async def create_collection_async(self, collection_name: SystemError) -> None:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
async def get_collections_async(
|
|
18
|
+
self,
|
|
19
|
+
) -> List[str]:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
async def delete_collection_async(self, collection_name: str) -> None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
async def does_collection_exist_async(self, collection_name: str) -> bool:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
async def upsert_async(self, collection_name: str, record: MemoryRecord) -> str:
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
async def upsert_batch_async(
|
|
36
|
+
self, collection_name: str, records: List[MemoryRecord]
|
|
37
|
+
) -> List[str]:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
async def get_async(
|
|
42
|
+
self, collection_name: str, key: str, with_embedding: bool
|
|
43
|
+
) -> MemoryRecord:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
async def get_batch_async(
|
|
48
|
+
self, collection_name: str, keys: List[str], with_embeddings: bool
|
|
49
|
+
) -> List[MemoryRecord]:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
async def remove_async(self, collection_name: str, key: str) -> None:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
async def remove_batch_async(self, collection_name: str, keys: List[str]) -> None:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
async def get_nearest_matches_async(
|
|
62
|
+
self,
|
|
63
|
+
collection_name: str,
|
|
64
|
+
embedding: ndarray,
|
|
65
|
+
limit: int,
|
|
66
|
+
min_relevance_score: float,
|
|
67
|
+
with_embeddings: bool,
|
|
68
|
+
) -> List[Tuple[MemoryRecord, float]]:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
async def get_nearest_match_async(
|
|
73
|
+
self,
|
|
74
|
+
collection_name: str,
|
|
75
|
+
embedding: ndarray,
|
|
76
|
+
min_relevance_score: float,
|
|
77
|
+
with_embedding: bool,
|
|
78
|
+
) -> Tuple[MemoryRecord, float]:
|
|
79
|
+
pass
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import List, Optional
|
|
4
4
|
|
|
5
|
-
from semantic_kernel.ai.embeddings.embedding_generator_base import (
|
|
5
|
+
from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import (
|
|
6
6
|
EmbeddingGeneratorBase,
|
|
7
7
|
)
|
|
8
8
|
from semantic_kernel.memory.memory_query_result import MemoryQueryResult
|
|
@@ -18,6 +18,16 @@ class SemanticTextMemory(SemanticTextMemoryBase):
|
|
|
18
18
|
def __init__(
|
|
19
19
|
self, storage: MemoryStoreBase, embeddings_generator: EmbeddingGeneratorBase
|
|
20
20
|
) -> None:
|
|
21
|
+
"""Initialize a new instance of SemanticTextMemory.
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
24
|
+
storage {MemoryStoreBase} -- The MemoryStoreBase to use for storage.
|
|
25
|
+
embeddings_generator {EmbeddingGeneratorBase} -- The EmbeddingGeneratorBase
|
|
26
|
+
to use for generating embeddings.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
None -- None.
|
|
30
|
+
"""
|
|
21
31
|
self._storage = storage
|
|
22
32
|
self._embeddings_generator = embeddings_generator
|
|
23
33
|
|
|
@@ -28,10 +38,29 @@ class SemanticTextMemory(SemanticTextMemoryBase):
|
|
|
28
38
|
id: str,
|
|
29
39
|
description: Optional[str] = None,
|
|
30
40
|
) -> None:
|
|
41
|
+
"""Save information to the memory (calls the memory store's upsert method).
|
|
42
|
+
|
|
43
|
+
Arguments:
|
|
44
|
+
collection {str} -- The collection to save the information to.
|
|
45
|
+
text {str} -- The text to save.
|
|
46
|
+
id {str} -- The id of the information.
|
|
47
|
+
description {Optional[str]} -- The description of the information.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
None -- None.
|
|
51
|
+
"""
|
|
52
|
+
# TODO: not the best place to create collection, but will address this behavior together with .NET SK
|
|
53
|
+
if not await self._storage.does_collection_exist_async(
|
|
54
|
+
collection_name=collection
|
|
55
|
+
):
|
|
56
|
+
await self._storage.create_collection_async(collection_name=collection)
|
|
57
|
+
|
|
31
58
|
embedding = await self._embeddings_generator.generate_embeddings_async([text])
|
|
32
|
-
data = MemoryRecord.local_record(
|
|
59
|
+
data = MemoryRecord.local_record(
|
|
60
|
+
id=id, text=text, description=description, embedding=embedding
|
|
61
|
+
)
|
|
33
62
|
|
|
34
|
-
await self._storage.
|
|
63
|
+
await self._storage.upsert_async(collection_name=collection, record=data)
|
|
35
64
|
|
|
36
65
|
async def save_reference_async(
|
|
37
66
|
self,
|
|
@@ -41,35 +70,88 @@ class SemanticTextMemory(SemanticTextMemoryBase):
|
|
|
41
70
|
external_source_name: str,
|
|
42
71
|
description: Optional[str] = None,
|
|
43
72
|
) -> None:
|
|
73
|
+
"""Save a reference to the memory (calls the memory store's upsert method).
|
|
74
|
+
|
|
75
|
+
Arguments:
|
|
76
|
+
collection {str} -- The collection to save the reference to.
|
|
77
|
+
text {str} -- The text to save.
|
|
78
|
+
external_id {str} -- The external id of the reference.
|
|
79
|
+
external_source_name {str} -- The external source name of the reference.
|
|
80
|
+
description {Optional[str]} -- The description of the reference.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
None -- None.
|
|
84
|
+
"""
|
|
85
|
+
# TODO: not the best place to create collection, but will address this behavior together with .NET SK
|
|
86
|
+
if not await self._storage.does_collection_exist_async(
|
|
87
|
+
collection_name=collection
|
|
88
|
+
):
|
|
89
|
+
await self._storage.create_collection_async(collection_name=collection)
|
|
90
|
+
|
|
44
91
|
embedding = await self._embeddings_generator.generate_embeddings_async([text])
|
|
45
92
|
data = MemoryRecord.reference_record(
|
|
46
|
-
external_id,
|
|
93
|
+
id=external_id,
|
|
94
|
+
source_name=external_source_name,
|
|
95
|
+
description=description,
|
|
96
|
+
embedding=embedding,
|
|
47
97
|
)
|
|
48
98
|
|
|
49
|
-
await self._storage.
|
|
99
|
+
await self._storage.upsert_async(collection_name=collection, record=data)
|
|
50
100
|
|
|
51
101
|
async def get_async(
|
|
52
102
|
self,
|
|
53
103
|
collection: str,
|
|
54
|
-
|
|
104
|
+
key: str,
|
|
55
105
|
) -> Optional[MemoryQueryResult]:
|
|
56
|
-
|
|
106
|
+
"""Get information from the memory (calls the memory store's get method).
|
|
107
|
+
|
|
108
|
+
Arguments:
|
|
109
|
+
collection {str} -- The collection to get the information from.
|
|
110
|
+
key {str} -- The key of the information.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Optional[MemoryQueryResult] -- The MemoryQueryResult if found, None otherwise.
|
|
114
|
+
"""
|
|
115
|
+
record = await self._storage.get_async(collection_name=collection, key=key)
|
|
116
|
+
return MemoryQueryResult.from_memory_record(record) if record else None
|
|
57
117
|
|
|
58
118
|
async def search_async(
|
|
59
119
|
self,
|
|
60
120
|
collection: str,
|
|
61
121
|
query: str,
|
|
62
122
|
limit: int = 1,
|
|
63
|
-
min_relevance_score: float = 0.
|
|
123
|
+
min_relevance_score: float = 0.0,
|
|
124
|
+
with_embeddings: bool = False,
|
|
64
125
|
) -> List[MemoryQueryResult]:
|
|
126
|
+
"""Search the memory (calls the memory store's get_nearest_matches method).
|
|
127
|
+
|
|
128
|
+
Arguments:
|
|
129
|
+
collection {str} -- The collection to search in.
|
|
130
|
+
query {str} -- The query to search for.
|
|
131
|
+
limit {int} -- The maximum number of results to return. (default: {1})
|
|
132
|
+
min_relevance_score {float} -- The minimum relevance score to return. (default: {0.0})
|
|
133
|
+
with_embeddings {bool} -- Whether to return the embeddings of the results. (default: {False})
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List[MemoryQueryResult] -- The list of MemoryQueryResult found.
|
|
137
|
+
"""
|
|
65
138
|
query_embedding = await self._embeddings_generator.generate_embeddings_async(
|
|
66
139
|
[query]
|
|
67
140
|
)
|
|
68
141
|
results = await self._storage.get_nearest_matches_async(
|
|
69
|
-
collection,
|
|
142
|
+
collection_name=collection,
|
|
143
|
+
embedding=query_embedding,
|
|
144
|
+
limit=limit,
|
|
145
|
+
min_relevance_score=min_relevance_score,
|
|
146
|
+
with_embeddings=with_embeddings,
|
|
70
147
|
)
|
|
71
148
|
|
|
72
149
|
return [MemoryQueryResult.from_memory_record(r[0], r[1]) for r in results]
|
|
73
150
|
|
|
74
151
|
async def get_collections_async(self) -> List[str]:
|
|
75
|
-
|
|
152
|
+
"""Get the list of collections in the memory (calls the memory store's get_collections method).
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
List[str] -- The list of all the memory collection names.
|
|
156
|
+
"""
|
|
157
|
+
return await self._storage.get_collections_async()
|
|
@@ -26,7 +26,6 @@ class SemanticTextMemoryBase(ABC):
|
|
|
26
26
|
external_id: str,
|
|
27
27
|
external_source_name: str,
|
|
28
28
|
description: Optional[str] = None,
|
|
29
|
-
# TODO: ctoken?
|
|
30
29
|
) -> None:
|
|
31
30
|
pass
|
|
32
31
|
|
|
@@ -34,7 +33,7 @@ class SemanticTextMemoryBase(ABC):
|
|
|
34
33
|
async def get_async(
|
|
35
34
|
self,
|
|
36
35
|
collection: str,
|
|
37
|
-
query: str,
|
|
36
|
+
query: str,
|
|
38
37
|
) -> Optional[MemoryQueryResult]:
|
|
39
38
|
pass
|
|
40
39
|
|
|
@@ -1,37 +1,259 @@
|
|
|
1
1
|
# Copyright (c) Microsoft. All rights reserved.
|
|
2
2
|
|
|
3
|
+
from copy import deepcopy
|
|
3
4
|
from logging import Logger
|
|
4
|
-
from typing import List, Optional, Tuple
|
|
5
|
+
from typing import Dict, List, Optional, Tuple
|
|
5
6
|
|
|
6
7
|
from numpy import array, linalg, ndarray
|
|
7
8
|
|
|
8
9
|
from semantic_kernel.memory.memory_record import MemoryRecord
|
|
9
10
|
from semantic_kernel.memory.memory_store_base import MemoryStoreBase
|
|
10
|
-
from semantic_kernel.memory.storage.volatile_data_store import VolatileDataStore
|
|
11
11
|
from semantic_kernel.utils.null_logger import NullLogger
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class VolatileMemoryStore(
|
|
14
|
+
class VolatileMemoryStore(MemoryStoreBase):
|
|
15
|
+
_store: Dict[str, Dict[str, MemoryRecord]]
|
|
16
|
+
_logger: Logger
|
|
17
|
+
|
|
15
18
|
def __init__(self, logger: Optional[Logger] = None) -> None:
|
|
16
|
-
|
|
19
|
+
self._store = {}
|
|
17
20
|
self._logger = logger or NullLogger()
|
|
21
|
+
"""Initializes a new instance of the VolatileMemoryStore class.
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
24
|
+
logger {Optional[Logger]} -- The logger to use. (default: {None})
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
async def create_collection_async(self, collection_name: str) -> None:
|
|
28
|
+
"""Creates a new collection if it does not exist.
|
|
29
|
+
|
|
30
|
+
Arguments:
|
|
31
|
+
collection_name {str} -- The name of the collection to create.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
None
|
|
35
|
+
"""
|
|
36
|
+
if collection_name in self._store:
|
|
37
|
+
pass
|
|
38
|
+
else:
|
|
39
|
+
self._store[collection_name] = {}
|
|
40
|
+
|
|
41
|
+
async def get_collections_async(
|
|
42
|
+
self,
|
|
43
|
+
) -> List[str]:
|
|
44
|
+
"""Gets the list of collections.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
List[str] -- The list of collections.
|
|
48
|
+
"""
|
|
49
|
+
return list(self._store.keys())
|
|
50
|
+
|
|
51
|
+
async def delete_collection_async(self, collection_name: str) -> None:
|
|
52
|
+
"""Deletes a collection.
|
|
53
|
+
|
|
54
|
+
Arguments:
|
|
55
|
+
collection_name {str} -- The name of the collection to delete.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
None
|
|
59
|
+
"""
|
|
60
|
+
if collection_name in self._store:
|
|
61
|
+
del self._store[collection_name]
|
|
62
|
+
|
|
63
|
+
async def does_collection_exist_async(self, collection_name: str) -> bool:
|
|
64
|
+
"""Checks if a collection exists.
|
|
65
|
+
|
|
66
|
+
Arguments:
|
|
67
|
+
collection_name {str} -- The name of the collection to check.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
bool -- True if the collection exists; otherwise, False.
|
|
71
|
+
"""
|
|
72
|
+
return collection_name in self._store
|
|
73
|
+
|
|
74
|
+
async def upsert_async(self, collection_name: str, record: MemoryRecord) -> str:
|
|
75
|
+
"""Upserts a record.
|
|
76
|
+
|
|
77
|
+
Arguments:
|
|
78
|
+
collection_name {str} -- The name of the collection to upsert the record into.
|
|
79
|
+
record {MemoryRecord} -- The record to upsert.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
str -- The unqiue database key of the record.
|
|
83
|
+
"""
|
|
84
|
+
if collection_name not in self._store:
|
|
85
|
+
raise Exception(f"Collection '{collection_name}' does not exist")
|
|
86
|
+
|
|
87
|
+
record._key = record._id
|
|
88
|
+
self._store[collection_name][record._key] = record
|
|
89
|
+
return record._key
|
|
90
|
+
|
|
91
|
+
async def upsert_batch_async(
|
|
92
|
+
self, collection_name: str, records: List[MemoryRecord]
|
|
93
|
+
) -> List[str]:
|
|
94
|
+
"""Upserts a batch of records.
|
|
95
|
+
|
|
96
|
+
Arguments:
|
|
97
|
+
collection_name {str} -- The name of the collection to upsert the records into.
|
|
98
|
+
records {List[MemoryRecord]} -- The records to upsert.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List[str] -- The unqiue database keys of the records.
|
|
102
|
+
"""
|
|
103
|
+
if collection_name not in self._store:
|
|
104
|
+
raise Exception(f"Collection '{collection_name}' does not exist")
|
|
105
|
+
|
|
106
|
+
for record in records:
|
|
107
|
+
record._key = record._id
|
|
108
|
+
self._store[collection_name][record._key] = record
|
|
109
|
+
return [record._key for record in records]
|
|
110
|
+
|
|
111
|
+
async def get_async(
|
|
112
|
+
self, collection_name: str, key: str, with_embedding: bool = False
|
|
113
|
+
) -> MemoryRecord:
|
|
114
|
+
"""Gets a record.
|
|
115
|
+
|
|
116
|
+
Arguments:
|
|
117
|
+
collection_name {str} -- The name of the collection to get the record from.
|
|
118
|
+
key {str} -- The unique database key of the record.
|
|
119
|
+
with_embedding {bool} -- Whether to include the embedding in the result. (default: {False})
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
MemoryRecord -- The record.
|
|
123
|
+
"""
|
|
124
|
+
if collection_name not in self._store:
|
|
125
|
+
raise Exception(f"Collection '{collection_name}' does not exist")
|
|
126
|
+
|
|
127
|
+
if key not in self._store[collection_name]:
|
|
128
|
+
raise Exception(f"Key '{key}' not found in collection '{collection_name}'")
|
|
129
|
+
|
|
130
|
+
result = self._store[collection_name][key]
|
|
131
|
+
|
|
132
|
+
if not with_embedding:
|
|
133
|
+
# create copy of results without embeddings
|
|
134
|
+
result = deepcopy(result)
|
|
135
|
+
result._embedding = None
|
|
136
|
+
return result
|
|
137
|
+
|
|
138
|
+
async def get_batch_async(
|
|
139
|
+
self, collection_name: str, keys: List[str], with_embeddings: bool = False
|
|
140
|
+
) -> List[MemoryRecord]:
|
|
141
|
+
"""Gets a batch of records.
|
|
142
|
+
|
|
143
|
+
Arguments:
|
|
144
|
+
collection_name {str} -- The name of the collection to get the records from.
|
|
145
|
+
keys {List[str]} -- The unique database keys of the records.
|
|
146
|
+
with_embeddings {bool} -- Whether to include the embeddings in the results. (default: {False})
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
List[MemoryRecord] -- The records.
|
|
150
|
+
"""
|
|
151
|
+
if collection_name not in self._store:
|
|
152
|
+
raise Exception(f"Collection '{collection_name}' does not exist")
|
|
153
|
+
|
|
154
|
+
results = [
|
|
155
|
+
self._store[collection_name][key]
|
|
156
|
+
for key in keys
|
|
157
|
+
if key in self._store[collection_name]
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
if not with_embeddings:
|
|
161
|
+
# create copy of results without embeddings
|
|
162
|
+
for result in results:
|
|
163
|
+
result = deepcopy(result)
|
|
164
|
+
result._embedding = None
|
|
165
|
+
return results
|
|
166
|
+
|
|
167
|
+
async def remove_async(self, collection_name: str, key: str) -> None:
|
|
168
|
+
"""Removes a record.
|
|
169
|
+
|
|
170
|
+
Arguments:
|
|
171
|
+
collection_name {str} -- The name of the collection to remove the record from.
|
|
172
|
+
key {str} -- The unique database key of the record to remove.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
None
|
|
176
|
+
"""
|
|
177
|
+
if collection_name not in self._store:
|
|
178
|
+
raise Exception(f"Collection '{collection_name}' does not exist")
|
|
179
|
+
|
|
180
|
+
if key not in self._store[collection_name]:
|
|
181
|
+
raise Exception(f"Key '{key}' not found in collection '{collection_name}'")
|
|
182
|
+
|
|
183
|
+
del self._store[collection_name][key]
|
|
184
|
+
|
|
185
|
+
async def remove_batch_async(self, collection_name: str, keys: List[str]) -> None:
|
|
186
|
+
"""Removes a batch of records.
|
|
187
|
+
|
|
188
|
+
Arguments:
|
|
189
|
+
collection_name {str} -- The name of the collection to remove the records from.
|
|
190
|
+
keys {List[str]} -- The unique database keys of the records to remove.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
None
|
|
194
|
+
"""
|
|
195
|
+
if collection_name not in self._store:
|
|
196
|
+
raise Exception(f"Collection '{collection_name}' does not exist")
|
|
197
|
+
|
|
198
|
+
for key in keys:
|
|
199
|
+
if key in self._store[collection_name]:
|
|
200
|
+
del self._store[collection_name][key]
|
|
201
|
+
|
|
202
|
+
async def get_nearest_match_async(
|
|
203
|
+
self,
|
|
204
|
+
collection_name: str,
|
|
205
|
+
embedding: ndarray,
|
|
206
|
+
min_relevance_score: float = 0.0,
|
|
207
|
+
with_embedding: bool = False,
|
|
208
|
+
) -> Tuple[MemoryRecord, float]:
|
|
209
|
+
"""Gets the nearest match to an embedding using cosine similarity.
|
|
210
|
+
|
|
211
|
+
Arguments:
|
|
212
|
+
collection_name {str} -- The name of the collection to get the nearest match from.
|
|
213
|
+
embedding {ndarray} -- The embedding to find the nearest match to.
|
|
214
|
+
min_relevance_score {float} -- The minimum relevance score of the match. (default: {0.0})
|
|
215
|
+
with_embedding {bool} -- Whether to include the embedding in the result. (default: {False})
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Tuple[MemoryRecord, float] -- The record and the relevance score.
|
|
219
|
+
"""
|
|
220
|
+
return self.get_nearest_matches_async(
|
|
221
|
+
collection_name=collection_name,
|
|
222
|
+
embedding=embedding,
|
|
223
|
+
limit=1,
|
|
224
|
+
min_relevance_score=min_relevance_score,
|
|
225
|
+
with_embeddings=with_embedding,
|
|
226
|
+
)
|
|
18
227
|
|
|
19
228
|
async def get_nearest_matches_async(
|
|
20
229
|
self,
|
|
21
|
-
|
|
230
|
+
collection_name: str,
|
|
22
231
|
embedding: ndarray,
|
|
23
|
-
limit: int
|
|
24
|
-
min_relevance_score: float = 0.
|
|
232
|
+
limit: int,
|
|
233
|
+
min_relevance_score: float = 0.0,
|
|
234
|
+
with_embeddings: bool = False,
|
|
25
235
|
) -> List[Tuple[MemoryRecord, float]]:
|
|
26
|
-
|
|
236
|
+
"""Gets the nearest matches to an embedding using cosine similarity.
|
|
237
|
+
|
|
238
|
+
Arguments:
|
|
239
|
+
collection_name {str} -- The name of the collection to get the nearest matches from.
|
|
240
|
+
embedding {ndarray} -- The embedding to find the nearest matches to.
|
|
241
|
+
limit {int} -- The maximum number of matches to return.
|
|
242
|
+
min_relevance_score {float} -- The minimum relevance score of the matches. (default: {0.0})
|
|
243
|
+
with_embeddings {bool} -- Whether to include the embeddings in the results. (default: {False})
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
List[Tuple[MemoryRecord, float]] -- The records and their relevance scores.
|
|
247
|
+
"""
|
|
248
|
+
if collection_name not in self._store:
|
|
27
249
|
return []
|
|
28
250
|
|
|
29
|
-
|
|
251
|
+
# Get all the records in the collection
|
|
252
|
+
memory_records = list(self._store[collection_name].values())
|
|
253
|
+
|
|
30
254
|
# Convert the collection of embeddings into a numpy array (stacked)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
embedding_array = embedding_array.reshape(embedding_array.shape[0], -1)
|
|
255
|
+
embeddings = array([x._embedding for x in memory_records], dtype=float)
|
|
256
|
+
embeddings = embeddings.reshape(embeddings.shape[0], -1)
|
|
35
257
|
|
|
36
258
|
# If the query embedding has shape (1, embedding_size),
|
|
37
259
|
# reshape it to (embedding_size,)
|
|
@@ -42,11 +264,11 @@ class VolatileMemoryStore(VolatileDataStore, MemoryStoreBase):
|
|
|
42
264
|
|
|
43
265
|
# Use numpy to get the cosine similarity between the query
|
|
44
266
|
# embedding and all the embeddings in the collection
|
|
45
|
-
similarity_scores = self.compute_similarity_scores(embedding,
|
|
267
|
+
similarity_scores = self.compute_similarity_scores(embedding, embeddings)
|
|
46
268
|
|
|
47
269
|
# Then, sort the results by the similarity score
|
|
48
270
|
sorted_results = sorted(
|
|
49
|
-
zip(
|
|
271
|
+
zip(memory_records, similarity_scores),
|
|
50
272
|
key=lambda x: x[1],
|
|
51
273
|
reverse=True,
|
|
52
274
|
)
|
|
@@ -56,22 +278,26 @@ class VolatileMemoryStore(VolatileDataStore, MemoryStoreBase):
|
|
|
56
278
|
|
|
57
279
|
# Then, take the top N results
|
|
58
280
|
top_results = filtered_results[:limit]
|
|
281
|
+
|
|
282
|
+
if not with_embeddings:
|
|
283
|
+
# create copy of results without embeddings
|
|
284
|
+
for result in top_results:
|
|
285
|
+
result = deepcopy(result)
|
|
286
|
+
result[0]._embedding = None
|
|
59
287
|
return top_results
|
|
60
288
|
|
|
61
289
|
def compute_similarity_scores(
|
|
62
290
|
self, embedding: ndarray, embedding_array: ndarray
|
|
63
291
|
) -> ndarray:
|
|
64
|
-
"""
|
|
65
|
-
query embedding and all the embeddings in the collection.
|
|
66
|
-
Ignore the corresponding operation if zero vectors
|
|
67
|
-
are involved (in query embedding or the embedding collection)
|
|
68
|
-
|
|
69
|
-
:param embedding: The query embedding.
|
|
70
|
-
:param embedding_array: The collection of embeddings.
|
|
71
|
-
:return: similarity_scores: The similarity scores between the query embedding
|
|
72
|
-
and all the embeddings in the collection.
|
|
73
|
-
"""
|
|
292
|
+
"""Computes the cosine similarity scores between a query embedding and a group of embeddings.
|
|
74
293
|
|
|
294
|
+
Arguments:
|
|
295
|
+
embedding {ndarray} -- The query embedding.
|
|
296
|
+
embedding_array {ndarray} -- The group of embeddings.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
ndarray -- The cosine similarity scores.
|
|
300
|
+
"""
|
|
75
301
|
query_norm = linalg.norm(embedding)
|
|
76
302
|
collection_norm = linalg.norm(embedding_array, axis=1)
|
|
77
303
|
|