File size: 5,044 Bytes
9a05fa5
 
 
ba8b895
 
 
 
9a05fa5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393c185
9a05fa5
393c185
9a05fa5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393c185
 
9a05fa5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ba8b895
 
 
 
 
 
 
 
 
 
9a05fa5
393c185
9a05fa5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a1dbb0
9a05fa5
5a1dbb0
 
9a05fa5
5a1dbb0
 
 
 
 
 
 
9a05fa5
 
 
 
 
 
 
 
 
 
 
 
 
 
5a1dbb0
 
9a05fa5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import gradio as gr
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# from langchain_chroma import Chroma
from langchain_community.vectorstores import FAISS


from langchain_groq import ChatGroq
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
import os
from dotenv import load_dotenv
from helper import SYSTEM_PROMPT
from langchain_google_genai import GoogleGenerativeAIEmbeddings
# from langchain.embeddings import HuggingFaceEmbeddings # open source free embedding
load_dotenv()


class PDFQAProcessor:

    SYSTEM_PROMPT = SYSTEM_PROMPT

    llm = ChatGroq(
        # model_name="deepseek-r1-distill-llama-70b",
        model_name="llama-3.3-70b-versatile",
        temperature=0.1,
        max_tokens=8000,
        api_key = os.getenv('GROQ_API_KEY')
    )

    # Setup RAG chain
    prompt = ChatPromptTemplate.from_messages([
        ("system", SYSTEM_PROMPT),
        ("human", "{input}"),
    ])
            
    question_answer_chain = create_stuff_documents_chain(llm, prompt)

    # EMBEDDING_MODEL = "intfloat/e5-large-v2"
    
    # embeddings = HuggingFaceEmbeddings(
    #     model_name=EMBEDDING_MODEL,
    #     model_kwargs={'device': 'cpu'},
    #     encode_kwargs={'normalize_embeddings': True}
    # )

    embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
    CHUNK_SIZE = 550
    CHUNK_OVERLAP = 80
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=CHUNK_SIZE,chunk_overlap = CHUNK_OVERLAP)
    # persist_directory="./chroma_db"


    def __init__(self):
        self.vectorstore = None
        self.retriever = None

    def process_pdfs(self, pdf_files):
        """Processing PDF files and creating vector store"""
        if not pdf_files:
            return "Please upload PDF files first!"
        
        try:
            # Load and split documents
            docs = []
            for pdf_file in pdf_files:
                loader = PyPDFLoader(pdf_file.name)
                docs.extend(loader.load())
            
            splits = self.text_splitter.split_documents(docs)

            # # Create vector store
            # self.vectorstore = Chroma.from_documents(
            #     documents=splits,
            #     embedding=self.embeddings,
            #     # persist_directory = self.persist_directory
            # )
            # Replace Chroma with:
            self.vectorstore = FAISS.from_documents(
                splits,
                self.embeddings
            )
            self.retriever = self.vectorstore.as_retriever(search_kwargs={"k": 18})
            return "PDFs processed successfully! Ask your questions now."
        
        except Exception as e:
            return f"Error processing PDFs: {str(e)}"

    def answer_question(self, question):
        """Handling question answering"""
        if not self.retriever:
            return "Please process PDFs first!", None
        
        try:
            # Initialize LLM
            rag_chain = create_retrieval_chain(self.retriever, self.question_answer_chain)
            
            response = rag_chain.invoke({"input": question})

            # final_response = response["answer"] + "\n\nSources\n\n"
            
            # for info in response["context"]:
            #     final_response += info.page_content + "\nSource of Info: " + info.metadata['source'] + "\nAt Page No: " + info.metadata['page_label']+"\n\n"

            final_response = response["answer"] + "\n\n### Sources\n\n"  # Changed to use markdown formatting
            for info in response["context"]:
                final_response += (
                    f"{info.page_content}<br>"  # Changed to use markdown bold formatting
                    f"Source of Info: {info.metadata['source']}<br>"
                    f"At Page No: {info.metadata['page_label']}<br><br>"
                    )
            return final_response
        except Exception as e:
            return f"Error answering question: {str(e)}", None

processor = PDFQAProcessor()

with gr.Blocks(title="PDF QA Assistant") as demo:
    with gr.Tab("Upload PDFs"):
        file_input = gr.Files(label="Upload PDFs", file_types=[".pdf"])
        process_btn = gr.Button("Process PDFs")
        status_output = gr.Textbox(label="Processing Status")
    
    with gr.Tab("Ask Questions"):
        question_input = gr.Textbox(label="Your Question")
        # answer_output = gr.Textbox(label="Answer", interactive=False)
        answer_output = gr.Markdown(label="Answer")
        ask_btn = gr.Button("Ask Question")

    process_btn.click(
        processor.process_pdfs,
        inputs=file_input,
        outputs=status_output
    )
    
    # QA workflow
    ask_btn.click(
        processor.answer_question,
        inputs=question_input,
        outputs=[answer_output]
    )

if __name__ == "__main__":
    demo.launch()