Vineetiitg commited on
Commit
8123d0b
·
1 Parent(s): f4f923e

feat: implement hybrid document ingestion with Qdrant and FastEmbed

Browse files
Files changed (2) hide show
  1. app/engine/ingestion.py +46 -0
  2. app/engine/retriever.py +31 -0
app/engine/ingestion.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from langchain_community.document_loaders import DirectoryLoader, TextLoader
3
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
4
+ from langchain_community.embeddings import FastEmbedEmbeddings
5
+ from langchain_qdrant import FastEmbedSparse, QdrantVectorStore, RetrievalMode
6
+
7
+ from app.core.config import settings
8
+
9
+ def ingest_documents(data_dir: str = "data/docs"):
10
+ print(f"Loading documents from {data_dir}...")
11
+ if not os.path.exists(data_dir):
12
+ os.makedirs(data_dir)
13
+
14
+ loader = DirectoryLoader(data_dir, glob="**/*.txt", loader_cls=TextLoader)
15
+ documents = loader.load()
16
+
17
+ if not documents:
18
+ print("No documents found. Please place some text documents into data/docs first.")
19
+ return
20
+
21
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
22
+ chunks = text_splitter.split_documents(documents)
23
+ print(f"Split documents into {len(chunks)} chunks.")
24
+
25
+ dense_embeddings = FastEmbedEmbeddings(model_name=settings.DENSE_EMBEDDING_MODEL)
26
+ sparse_embeddings = FastEmbedSparse(model_name=settings.SPARSE_EMBEDDING_MODEL)
27
+
28
+ store_options = {
29
+ "url": settings.QDRANT_URL,
30
+ } if settings.QDRANT_URL else {
31
+ "path": settings.QDRANT_LOCATION,
32
+ }
33
+
34
+ QdrantVectorStore.from_documents(
35
+ chunks,
36
+ embedding=dense_embeddings,
37
+ sparse_embedding=sparse_embeddings,
38
+ collection_name=settings.COLLECTION_NAME,
39
+ retrieval_mode=RetrievalMode.HYBRID,
40
+ force_recreate=True,
41
+ **store_options,
42
+ )
43
+ print("Ingestion complete! Hybrid index is built.")
44
+
45
+ if __name__ == "__main__":
46
+ ingest_documents()
app/engine/retriever.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_community.embeddings import FastEmbedEmbeddings
2
+ from langchain_qdrant import FastEmbedSparse, QdrantVectorStore, RetrievalMode
3
+ from langchain_community.cross_encoders import HuggingFaceCrossEncoder
4
+ from langchain.retrievers.document_compressors import CrossEncoderReranker
5
+ from langchain.retrievers import ContextualCompressionRetriever
6
+ from qdrant_client import QdrantClient
7
+
8
+ from app.core.config import settings
9
+
10
+ def get_reranked_retriever():
11
+ if settings.QDRANT_URL:
12
+ client = QdrantClient(url=settings.QDRANT_URL)
13
+ else:
14
+ client = QdrantClient(path=settings.QDRANT_LOCATION)
15
+
16
+ dense_embeddings = FastEmbedEmbeddings(model_name=settings.DENSE_EMBEDDING_MODEL)
17
+ sparse_embeddings = FastEmbedSparse(model_name=settings.SPARSE_EMBEDDING_MODEL)
18
+
19
+ qdrant = QdrantVectorStore(
20
+ client=client,
21
+ collection_name=settings.COLLECTION_NAME,
22
+ embedding=dense_embeddings,
23
+ sparse_embedding=sparse_embeddings,
24
+ retrieval_mode=RetrievalMode.HYBRID,
25
+ )
26
+
27
+ base_retriever = qdrant.as_retriever(search_kwargs={"k": 15})
28
+ model = HuggingFaceCrossEncoder(model_name=settings.RERANKER_MODEL)
29
+ compressor = CrossEncoderReranker(model=model, top_n=3)
30
+
31
+ return ContextualCompressionRetriever(base_compressor=compressor, base_retriever=base_retriever)