Spaces:
Sleeping
Sleeping
| import logging | |
| import hashlib | |
| from langchain_community.vectorstores import Chroma | |
| from langchain_openai import OpenAIEmbeddings | |
| from src.data.chroma_config import COLLECTION_NAME, PERSIST_DIRECTORY, ensure_persist_dir | |
| from src.core.settings import get_settings | |
| logger = logging.getLogger(__name__) | |
| class SemanticCache: | |
| def __init__( | |
| self, | |
| persist_directory: str = PERSIST_DIRECTORY, | |
| collection_name: str = COLLECTION_NAME, | |
| ): | |
| """ | |
| Initializes the Semantic Caching layer utilizing ChromaDB. | |
| """ | |
| self.persist_directory = persist_directory | |
| settings = get_settings() | |
| self.embeddings = OpenAIEmbeddings(model=settings.models.embedding_model) | |
| ensure_persist_dir(self.persist_directory) | |
| self.vector_store = Chroma( | |
| collection_name=collection_name, | |
| embedding_function=self.embeddings, | |
| persist_directory=self.persist_directory, | |
| ) | |
| def _doc_id(self, normalized_query: str) -> str: | |
| h = hashlib.sha256() | |
| h.update(normalized_query.encode("utf-8")) | |
| return h.hexdigest() | |
| def check_cache(self, query: str, threshold: float = 0.70): | |
| """ | |
| Check the vector store for a semantically similar query. | |
| Returns the cached response if a match is found above the similarity threshold. | |
| """ | |
| # Normalize the query to prevent case/whitespace from skewing the embeddings | |
| query_normalized = query.lower().strip() | |
| where = {"namespace": "semantic_cache"} | |
| try: | |
| results = self.vector_store.similarity_search_with_relevance_scores( | |
| query_normalized, k=1, filter=where | |
| ) | |
| except TypeError: | |
| # Older wrappers use `where` not `filter`. | |
| results = self.vector_store.similarity_search_with_relevance_scores( | |
| query_normalized, k=1, where=where | |
| ) # type: ignore[call-arg] | |
| except Exception: | |
| logger.exception("SemanticCache: check_cache failed") | |
| return None | |
| if results: | |
| doc, score = results[0] | |
| if score >= threshold: | |
| logger.info(f"Semantic Match Score: {score:.3f} greater than threshold {threshold:.3f}") | |
| meta = getattr(doc, "metadata", {}) or {} | |
| return meta.get("response") | |
| return None | |
| def save_to_cache(self, query: str, response: str): | |
| """ | |
| Save the given query and response combo to the semantic cache. | |
| """ | |
| query_normalized = query.lower().strip() | |
| doc_id = self._doc_id(query_normalized) | |
| # Best-effort de-dupe: keep the first cached answer for a given normalized query. | |
| try: | |
| existing = set(self.vector_store.get(ids=[doc_id]).get("ids", [])) | |
| except Exception: | |
| existing = set() | |
| if doc_id in existing: | |
| return | |
| self.vector_store.add_texts( | |
| texts=[query_normalized], | |
| metadatas=[{"namespace": "semantic_cache", "response": response}], | |
| ids=[doc_id], | |
| ) | |