File size: 3,156 Bytes
cd6f412
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import logging
import chromadb
# from langchain_community.vectorstores import Chroma
from langchain_chroma import Chroma
from langchain_core.embeddings import Embeddings
# from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_huggingface import HuggingFaceEmbeddings

from pathlib import Path

from typing import List
from langchain_core.documents import Document

from ..config import settings

logger = logging.getLogger(__name__)

# Use a local, open-source embedding model for cost-effectiveness and privacy.
EMBEDDING_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
# Define the path for the persistent ChromaDB store
CHROMA_PATH = "data/vector_store"

class VectorStoreService:
    def __init__(self):
        """Initializes the VectorStoreService."""
        self.client = chromadb.PersistentClient(path=CHROMA_PATH)
        self.embedding_function = self._get_embedding_function()
        self.collection_name = "insucompass_kb"
        
        # Get or create the collection
        self.collection = self.client.get_or_create_collection(
            name=self.collection_name,
            embedding_function=None # LangChain's wrapper handles this
        )
        
        self.langchain_chroma = Chroma(
            client=self.client,
            collection_name=self.collection_name,
            embedding_function=self.embedding_function,
        )
        logger.info(f"ChromaDB service initialized. Collection '{self.collection_name}' at {CHROMA_PATH}")

    def _get_embedding_function(self) -> Embeddings:
        """Initializes and returns the embedding model."""
        logger.info(f"Loading embedding model: {EMBEDDING_MODEL_NAME}")
        # Specify 'mps' for Apple Silicon, 'cuda' for NVIDIA, or 'cpu'
        model_kwargs = {'device': 'cpu'} 
        encode_kwargs = {'normalize_embeddings': False}
        return HuggingFaceEmbeddings(
            model_name=EMBEDDING_MODEL_NAME,
            model_kwargs=model_kwargs,
            encode_kwargs=encode_kwargs
        )

    def add_documents(self, documents: List[Document]) -> List[str]:
        """
        Adds a list of documents to the Chroma vector store.

        Args:
            documents: A list of LangChain Document objects.

        Returns:
            A list of vector IDs for the added documents.
        """
        if not documents:
            logger.warning("No documents provided to add to the vector store.")
            return []
            
        logger.info(f"Adding {len(documents)} documents to the vector store...")
        try:
            vector_ids = self.langchain_chroma.add_documents(documents)
            logger.info(f"Successfully added {len(documents)} documents.")
            return vector_ids
        except Exception as e:
            logger.error(f"Failed to add documents to vector store: {e}")
            raise

    def get_retriever(self, search_kwargs={'k': 5}):
        """Returns a LangChain retriever for the vector store."""
        return self.langchain_chroma.as_retriever(search_type="mmr", search_kwargs=search_kwargs)

# Singleton instance
vector_store_service = VectorStoreService()