File size: 8,774 Bytes
068d763
 
 
 
 
 
 
 
e0cae50
 
1081bff
e0cae50
068d763
 
 
 
 
 
 
 
 
 
 
1081bff
068d763
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e0cae50
465fd81
e0cae50
 
 
 
 
 
068d763
e0cae50
068d763
e0cae50
 
63f73e0
068d763
e0cae50
 
 
62c9793
e0cae50
62c9793
e0cae50
 
 
 
 
62c9793
 
e0cae50
62c9793
e0cae50
 
 
068d763
e0cae50
068d763
 
62c9793
 
 
 
 
49f5dbd
 
 
 
 
 
 
 
 
 
62c9793
 
49f5dbd
e0cae50
62c9793
 
e0cae50
97d9b12
 
e0cae50
97d9b12
 
 
 
 
 
 
068d763
e0cae50
8989096
 
 
 
 
 
 
 
 
 
 
 
 
97d9b12
8989096
 
 
 
97d9b12
8989096
 
 
 
 
 
 
 
 
 
 
 
97d9b12
e0cae50
068d763
 
 
8989096
 
 
 
 
 
 
e0cae50
97d9b12
068d763
8989096
068d763
 
8989096
068d763
 
 
 
97d9b12
068d763
97d9b12
 
 
 
 
8989096
 
97d9b12
8989096
97d9b12
8989096
 
 
 
 
 
 
 
97d9b12
 
 
 
068d763
e0cae50
 
 
068d763
 
 
 
 
 
 
 
 
 
 
 
97d9b12
068d763
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
from transformers import AutoTokenizer, AutoModelForCausalLM
from langchain_core.output_parsers import BaseOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough, RunnableMap
from langchain.schema import Generation
import torch

# Load your tokenizer and model
tokenizer = AutoTokenizer.from_pretrained("NLPGenius/GovGPT-llama3")
model = AutoModelForCausalLM.from_pretrained("NLPGenius/GovGPT-llama3", 
                                             #torch_dtype=torch.bfloat16, 
                                             device_map="auto",)


import transformers
from transformers import pipeline
llm_chain = transformers.pipeline(
    model=model, tokenizer=tokenizer,
    return_full_text=True,  # langchain expects the full text
    task='text-generation',
    # we pass model parameters here too
    temperature=0.3,  # 'randomness' of outputs, 0.0 is the min and 1.0 the max
    do_sample=True,
    max_length=2000,  # max number of tokens to generate in the output
    truncation=True,
    repetition_penalty=1.1  # without this output begins repeating
)

from langchain.llms import HuggingFacePipeline

llm = HuggingFacePipeline(pipeline=llm_chain)

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS

# Define the chunking and embedding strategy
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
embedding_model = HuggingFaceEmbeddings()  # Adjust if you have a specific embedding model

import os
import rarfile
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# Function to create FAISS index
def create_faiss_index(docs):
    texts = text_splitter.split_documents(docs)  # Ensure `text_splitter` is defined in your script
    vector_store = FAISS.from_documents(texts, embedding_model)
    return vector_store

# Function to fetch and process PDF files
def fetch_and_process_pdfs(pdf_paths):
    all_documents = []
    for pdf_file in pdf_paths:
        loader = PyPDFLoader(pdf_file)
        documents = loader.load()
        all_documents.extend(documents)
    return all_documents

# Function to create RAG pipeline with provided PDF paths
def create_rag_pipeline(pdf_paths):
    # Fetch and process PDF files
    documents = fetch_and_process_pdfs(pdf_paths)

    # Create vector store
    vector_store = create_faiss_index(documents)
    retriever = vector_store.as_retriever()

    return retriever

# Specify the PDF paths
pdfs_paths = [
    "Esta_Code_2024_.pdf", 
    "KP_Policy_Agriculture1.pdf", 
    "THE_KHYBER_PAKHTUNKHWA_SALES_TAX_ON_SERVICES_ACT_2022.pdf", 
    "The-Khyber-Pakhtunkhwa-Police-Amendment-Act-2024.pdf",
    "Home_department_ARMS_ACT_2013.pdf",
    "Khyber Pakhtunkhwa Civil Servants Pension Rules, 2021.pdf",
    "Lease-Policy-2015.pdf",
    "Planning & Development ADP Policy 2020-21.pdf",
    "The Khyber Pakhtunkhwa Contributory Provident Fund Rules, 2022 .pdf",
    "THE_KHYBER_PAKHTUNKHWA_EMERGENCY_RESCUE_SERVICE_ACT_2012.pdf",
    "The_Khyber_Pakhtunkhwa_Water_Act_2020_Act_No_XXV_of_2020.pdf",
    "the_Khyber_Pakhtunkhwa_Zakat_and_Ushr_Act,_2011.pdf",
    "The-Khyber-Pakhtunkhwa-Provincial-Assembly-Secretariat-Employees-Terms-and-Conditions-of-Service-Bill-2024-1.pdf"
]


# Usage example
retriever = create_rag_pipeline(pdfs_paths)


from langchain.schema.runnable import RunnablePassthrough, RunnableLambda
import re

# # Define a custom output parser to extract only the answer
# class AnswerOutputParser(BaseOutputParser):
#     def parse(self, text: str) -> str:
#         # Extract everything after "Answer:"
#         if "Answer:" in text:
#             return text.split("Answer:")[-1].strip()
#         return "I don't know."

# Function to perform model inference
import re
from fuzzywuzzy import fuzz  # For relevance matching

def is_context_relevant(context, question, threshold=40):
    """
    Checks if the retrieved context is relevant to the question using fuzzy matching.
    Returns True if relevant, False otherwise.
    """
    context_snippet = " ".join(context.split()[:100])  # Use only first 100 words for efficiency
    relevance_score = fuzz.partial_ratio(context_snippet.lower(), question.lower())

    return relevance_score >= threshold  # Only accept if score is above threshold

def model_inference(retriever, question, llm):
    # Validate question: Reject if too short or empty
    if not question.strip() or len(question.strip()) < 5:
        return "Answer:\n        The question is invalid or lacks sufficient detail."

    # Retrieve relevant documents
    retrieved_docs = retriever.invoke(question)

    # If no relevant documents are found, reject the query
    if not retrieved_docs:
        return "Answer:\n        The answer is not found in the context provided.\nReferences: No references found."

    # Extract context
    context = "\n\n".join([doc.page_content for doc in retrieved_docs]).strip()

    # **New: Check if the retrieved context is relevant to the question**
    if not is_context_relevant(context, question):
        return "Answer:\n        The answer is not found in the context provided.\nReferences: No references found."

    # Define prompt template
    prompt = PromptTemplate(
        input_variables=["context", "question"],
        template="""\
        You are a highly accurate and reliable assistant. Follow these strict rules:

        1. **Use Only the Provided Context** – Base your answer strictly on the given context. Do not infer or generate extra details.
        2. **Reject Invalid or Unrelated Questions** – If the question does not relate to the context, respond with: "The answer is not found in the context provided."
        3. **Ensure Context Relevance** – Answer only if the exact information exists in the context.
        4. **No Hallucination** – Do not assume, summarize, or infer beyond what is explicitly stated.
        5. **Concise and Relevant Answers** – Avoid redundancy. Provide only the most accurate response.

        Context:
        {context}

        Query:
        {question}

        Answer:
        """
    )

    # Define processing chain
    rag_chain = (
        RunnablePassthrough.assign(context=lambda _: context, question=lambda _: question)
        | prompt
        | llm
    )

    # Invoke the chain and get the response
    response = rag_chain.invoke({}).strip()

    # Extract only the final "Answer:" section
    match = re.findall(r"Answer:\s*(.*)", response, re.DOTALL)
    final_answer = "Answer:\n        " + match[-1].strip() if match else "Answer:\n        The answer is not found in the context provided."

    # Extract only the first relevant reference
    references = (
        f"Source: {retrieved_docs[0].metadata['source']}, Page: {retrieved_docs[0].metadata.get('page_label', 'Unknown')}"
        if retrieved_docs else "No references found."
    )

    return f"{final_answer}\nReferences: {references}"

# # Example usage:
# formatted_output = model_inference(retriever, "What is the main purpose of the Khyber Pakhtunkhwa Arms Act, 2013?", llm)

#response = model_inference(retriever, "Why was a new agriculture policy needed in Khyber Pakhtunkhwa?")



from flask import Flask, request, jsonify
from flask_cors import CORS

# Initialize Flask app
app = Flask(__name__)
CORS(app, supports_credentials=True, allow_headers=["Content-Type"])

# Mock chatbot function (replace with your actual pipeline)
def chatbot_response(query):
    try:
        # Call your actual RAG pipeline here
        response = model_inference(retriever, query, llm)
        return response
    except Exception as e:
        return f"Error: {str(e)}"

# Route for chatbot responses
@app.route('/', methods=['GET','POST'])
def chat():
    
    # Try to get JSON data
    if request.content_type == 'application/json':
        data = request.get_json(silent=True)
        user_query = data.get("query", "").strip() if data else ""
    else:
        # Fallback to form-data
        user_query = request.form.get("query", "").strip()
    
    if not user_query:
        return jsonify({"error": "Please provide a valid query."}), 400

    # Get chatbot response
    bot_reply = chatbot_response(user_query)
    return jsonify({"response": bot_reply})

# Run the Flask app
if __name__ == '__main__':
    app.run()