File size: 4,425 Bytes
b29e221
 
f136964
b29e221
 
 
 
f136964
b29e221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f136964
 
 
 
 
b29e221
 
 
 
 
 
 
 
 
 
 
f136964
b29e221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f136964
b29e221
f136964
 
 
 
 
 
 
 
 
 
 
b29e221
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f136964
b29e221
 
 
 
 
 
 
f136964
b29e221
 
 
 
 
 
 
 
 
 
f136964
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import os
import json
from PyPDF2 import PdfReader  # FIXED: Correct case (was "PyPDF2")
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_chroma import Chroma
from langchain_groq import ChatGroq
from langchain.chains.retrieval_qa.base import RetrievalQA  # FIXED: Updated import path

# Setup working directory
working_dir = os.path.dirname(os.path.abspath(__file__))

os.environ["GROQ_API_KEY"] = os.getenv('GROQ_API_KEY')

# Load embedding model
embedding = HuggingFaceEmbeddings()

# Initialize LLM from Groq
llm = ChatGroq(
    model="deepseek-r1-distill-llama-70b",
    temperature=0
)


def extract_text_from_pdf(file_path):
    """
    Extract text content from a PDF file using PyPDF2.
    """
    try:
        reader = PdfReader(file_path)
        text = ""
        for page in reader.pages:
            extracted = page.extract_text()
            if extracted:
                text += extracted + "\n"
        if not text.strip():
            raise ValueError(f"โš ๏ธ No text extracted from '{os.path.basename(file_path)}'. The file might be empty or image-based.")
        return text
    except Exception as e:
        raise RuntimeError(f"โš ๏ธ Error extracting text from PDF: {e}")


def process_document_to_chroma_db(directory_path):
    """
    Process all PDF documents in the given directory, split their text, 
    and store embeddings in a persistent ChromaDB.
    """
    try:
        all_texts = []
        # Iterate through all PDF files in the directory
        for file_name in os.listdir(directory_path):
            if file_name.endswith(".pdf"):
                file_path = os.path.join(directory_path, file_name)
                print(f"๐Ÿ“‚ Processing document: {file_name}")

                # Extract text from the PDF
                text = extract_text_from_pdf(file_path)

                # Split text into chunks
                print("๐Ÿ”„ Splitting document into smaller chunks...")
                text_splitter = RecursiveCharacterTextSplitter(
                    chunk_size=2000,
                    chunk_overlap=200
                )
                texts = text_splitter.split_text(text)
                all_texts.extend(texts)

        if not all_texts:
            raise ValueError("โš ๏ธ No PDF files with extractable text found in the directory.")

        # Create a persistent ChromaDB instance with all texts
        print("๐Ÿ’พ Storing embeddings in ChromaDB...")
        vectordb = Chroma.from_texts(
            texts=all_texts,
            embedding=embedding,
            persist_directory=os.path.join(working_dir, "doc_vectorstore")
        )
        # No need to call persist() explicitly with Chroma 0.4+

        print("โœ… All documents successfully processed and stored in ChromaDB.")
        return "โœ… Documents successfully processed and stored in ChromaDB."

    except Exception as e:
        raise RuntimeError(f"โš ๏ธ Error processing documents: {e}")


def answer_question(user_question):
    """
    Retrieve and generate an answer for the given user question 
    based on the stored document embeddings.
    """
    try:
        # Load the persistent vector database
        vectordb_path = os.path.join(working_dir, "doc_vectorstore")
        if not os.path.exists(vectordb_path):
            raise FileNotFoundError("โš ๏ธ ChromaDB vector store not found. Please process a document first.")

        print("๐Ÿ“‚ Loading vector database...")
        vectordb = Chroma(
            persist_directory=vectordb_path,
            embedding_function=embedding
        )

        # Create a retriever from the vector database
        retriever = vectordb.as_retriever(search_kwargs={"k": 3})

        # Create a QA chain with DeepSeek-R1
        print("๐Ÿค– Initializing Retrieval QA chain...")
        qa_chain = RetrievalQA.from_chain_type(
            llm=llm,
            chain_type="stuff",
            retriever=retriever,
            return_source_documents=True  # Optional: helps debugging
        )

        # Invoke the QA chain with the user question
        print("๐Ÿ’ฌ Generating answer...")
        response = qa_chain.invoke({"query": user_question})
        answer = response.get("result", "โš ๏ธ No response generated.")

        return answer

    except Exception as e:
        raise RuntimeError(f"โš ๏ธ Error generating response: {e}")