-
Notifications
You must be signed in to change notification settings - Fork 15.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cosmosdbnosql: Added Cosmos DB NoSQL Semantic Cache Integration with tests and jupyter notebook #24424
cosmosdbnosql: Added Cosmos DB NoSQL Semantic Cache Integration with tests and jupyter notebook #24424
Changes from 60 commits
80936bf
01b7bd1
e891cd9
676d762
7f00b8a
66289ed
bb55a4b
2184d8e
d9f684b
4701fb7
d23bcd6
043f2bf
4f58256
cd84f02
aa1e846
636415b
cd744f0
059be6a
c625e7d
31b7c1b
98d9d7e
2c4d2d3
04ac7ae
2c4f281
0caa7cd
9766154
8891976
eafbcb8
dbe1504
9ffa53f
eb1f8bf
3599624
f18a5d3
f2f1ff5
1e818da
0441131
810dd00
c3d0917
a748a58
81887e5
860b0c0
3378365
fe08580
cb02b1a
bc8ee2d
a5eebd9
5f2c91f
cce4959
0441fa7
d003423
05fd438
9e7838c
f4250ac
c13660c
3f23135
7fc0f59
224393c
f87f18d
cbd6f2b
1ce7bbf
9c3bdcb
1c1a1e8
6db72c4
41474c4
ae256f5
a4b7d1a
3cc98f8
d237cd5
60cf24f
e175d81
92ed0ca
a7a4544
29044fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,7 +80,10 @@ | |
from langchain_community.utilities.astradb import ( | ||
_AstraDBCollectionEnvironment, | ||
) | ||
from langchain_community.vectorstores import AzureCosmosDBVectorSearch | ||
from langchain_community.vectorstores import ( | ||
AzureCosmosDBNoSqlVectorSearch, | ||
AzureCosmosDBVectorSearch, | ||
) | ||
from langchain_community.vectorstores import ( | ||
OpenSearchVectorSearch as OpenSearchVectorStore, | ||
) | ||
|
@@ -92,6 +95,7 @@ | |
if TYPE_CHECKING: | ||
import momento | ||
from astrapy.db import AstraDB, AsyncAstraDB | ||
from azure.cosmos.cosmos_client import CosmosClient | ||
from cassandra.cluster import Session as CassandraSession | ||
|
||
|
||
|
@@ -2102,7 +2106,7 @@ def __init__( | |
ef_construction: int = 64, | ||
ef_search: int = 40, | ||
score_threshold: Optional[float] = None, | ||
application_name: str = "LANGCHAIN_CACHING_PYTHON", | ||
application_name: str = "LangChain-CDBNoSQL-SemanticCache-Python", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just a name change, this should not break any workflows. This user agent is being used for tracking purposes only. We want to have it in this pattern, as Azure CosmosDB is integrated in various AI frameworks (microsoft internal and external), and we want to create dashboards to track the usage using these user agents. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but if someone is using the default It's OK to make such a change if important, but users are already free to specify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we have seen that a lot of users are not specifying the application_name, and we can only see default value in our telemetry. So, that's the reason for this update as we can keep the application_name consistent across all our integrations. |
||
): | ||
""" | ||
Args: | ||
|
@@ -2267,14 +2271,115 @@ def clear(self, **kwargs: Any) -> None: | |
index_name = self._index_name(kwargs["llm_string"]) | ||
if index_name in self._cache_dict: | ||
self._cache_dict[index_name].get_collection().delete_many({}) | ||
# self._cache_dict[index_name].clear_collection() | ||
|
||
@staticmethod | ||
def _validate_enum_value(value: Any, enum_type: Type[Enum]) -> None: | ||
if not isinstance(value, enum_type): | ||
raise ValueError(f"Invalid enum value: {value}. Expected {enum_type}.") | ||
|
||
|
||
class AzureCosmosDBNoSqlSemanticCache(BaseCache): | ||
"""Cache that uses Cosmos DB NoSQL backend""" | ||
|
||
def __init__( | ||
self, | ||
embedding: Embeddings, | ||
cosmos_client: CosmosClient, | ||
database_name: str = "CosmosNoSqlCacheDB", | ||
container_name: str = "CosmosNoSqlCacheContainer", | ||
*, | ||
vector_embedding_policy: Dict[str, Any], | ||
indexing_policy: Dict[str, Any], | ||
cosmos_container_properties: Dict[str, Any], | ||
cosmos_database_properties: Dict[str, Any], | ||
): | ||
self.cosmos_client = cosmos_client | ||
self.database_name = database_name | ||
self.container_name = container_name | ||
self.embedding = embedding | ||
self.vector_embedding_policy = vector_embedding_policy | ||
self.indexing_policy = indexing_policy | ||
self.cosmos_container_properties = cosmos_container_properties | ||
self.cosmos_database_properties = cosmos_database_properties | ||
self._cache_dict: Dict[str, AzureCosmosDBNoSqlVectorSearch] = {} | ||
|
||
def _cache_name(self, llm_string: str) -> str: | ||
hashed_index = _hash(llm_string) | ||
return f"cache:{hashed_index}" | ||
|
||
def _get_llm_cache(self, llm_string: str) -> AzureCosmosDBNoSqlVectorSearch: | ||
cache_name = self._cache_name(llm_string) | ||
|
||
# return vectorstore client for the specific llm string | ||
if cache_name in self._cache_dict: | ||
return self._cache_dict[cache_name] | ||
|
||
# create new vectorstore client to create the cache | ||
if self.cosmos_client: | ||
self._cache_dict[cache_name] = AzureCosmosDBNoSqlVectorSearch( | ||
cosmos_client=self.cosmos_client, | ||
embedding=self.embedding, | ||
vector_embedding_policy=self.vector_embedding_policy, | ||
indexing_policy=self.indexing_policy, | ||
cosmos_container_properties=self.cosmos_container_properties, | ||
cosmos_database_properties=self.cosmos_database_properties, | ||
database_name=self.database_name, | ||
container_name=self.container_name, | ||
) | ||
|
||
return self._cache_dict[cache_name] | ||
|
||
def lookup(self, prompt: str, llm_string: str) -> Optional[RETURN_VAL_TYPE]: | ||
"""Look up based on prompt.""" | ||
llm_cache = self._get_llm_cache(llm_string) | ||
generations: List = [] | ||
# Read from a Hash | ||
results = llm_cache.similarity_search( | ||
query=prompt, | ||
k=1, | ||
) | ||
if results: | ||
for document in results: | ||
try: | ||
generations.extend(loads(document.metadata["return_val"])) | ||
except Exception: | ||
logger.warning( | ||
"Retrieving a cache value that could not be deserialized " | ||
"properly. This is likely due to the cache being in an " | ||
"older format. Please recreate your cache to avoid this " | ||
"error." | ||
) | ||
|
||
generations.extend( | ||
_load_generations_from_json(document.metadata["return_val"]) | ||
) | ||
return generations if generations else None | ||
|
||
def update(self, prompt: str, llm_string: str, return_val: RETURN_VAL_TYPE) -> None: | ||
"""Update cache based on prompt and llm_string.""" | ||
for gen in return_val: | ||
if not isinstance(gen, Generation): | ||
raise ValueError( | ||
"CosmosDBNoSqlSemanticCache only supports caching of " | ||
f"normal LLM generations, got {type(gen)}" | ||
) | ||
llm_cache = self._get_llm_cache(llm_string) | ||
metadata = { | ||
"llm_string": llm_string, | ||
"prompt": prompt, | ||
"return_val": dumps([g for g in return_val]), | ||
} | ||
llm_cache.add_texts(texts=[prompt], metadatas=[metadata]) | ||
|
||
def clear(self, **kwargs: Any) -> None: | ||
"""Clear semantic cache for a given llm_string.""" | ||
cache_name = self._cache_name(llm_string=kwargs["llm-string"]) | ||
if cache_name in self._cache_dict: | ||
container = self._cache_dict["cache_name"].get_container() | ||
for item in container.read_all_items(): | ||
container.delete_item(item) | ||
|
||
|
||
class OpenSearchSemanticCache(BaseCache): | ||
"""Cache that uses OpenSearch vector store backend""" | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit) Could be worth running these cells to demonstrate the speedup.