saadawaissheikh commited on
Commit
8f0f770
·
verified ·
1 Parent(s): 1e2e930

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +113 -80
app.py CHANGED
@@ -1,98 +1,115 @@
1
  import os
2
- import gradio as gr
3
- import pdfplumber
4
  import re
5
-
 
6
  from langchain.docstore.document import Document
7
- from langchain_community.vectorstores import FAISS # Fixed deprecation warning
8
  from langchain.embeddings.base import Embeddings
9
  from sklearn.feature_extraction.text import TfidfVectorizer
10
- from langchain.chains import RetrievalQA
11
  from langchain.prompts import PromptTemplate
 
12
  from langchain_openai import ChatOpenAI
13
  from transformers import pipeline
14
 
15
-
16
- # Set OpenRouter API env vars (used by ChatOpenAI)
17
- os.environ["OPENAI_API_KEY"] = os.environ.get("OPENROUTER_API_KEY")
 
18
  os.environ["OPENAI_API_BASE"] = "https://openrouter.ai/api/v1"
19
- os.environ["OPENAI_API_HEADERS"] = '{"HTTP-Referer":"https://huggingface.co", "X-Title":"PDF-RAG"}'
20
-
21
- # Global variables
22
- qa_chain = None
23
- translator_en2ur = None
24
- translator_ur2en = None
25
-
26
- # -------------------- PDF Extraction --------------------
27
- def extract_clean_sections(file_path):
28
  with pdfplumber.open(file_path) as pdf:
29
- full_text = ""
30
  for page in pdf.pages:
31
- text = page.extract_text()
32
- if text:
33
- text = re.sub(r'Systems Campus.*?Lahore', '', text)
34
- text = re.sub(r'E-mail:.*?systemsltd\.com', '', text)
35
- full_text += text + "\n"
36
 
 
37
  pattern = r"(?<=\n)([A-Z][^\n]{3,50}):"
38
- parts = re.split(pattern, full_text)
39
 
40
  docs = []
41
  for i in range(1, len(parts), 2):
42
- title = parts[i].strip()
43
  content = parts[i + 1].strip()
44
  if len(content) > 20:
45
- docs.append(Document(page_content=f"{title}:\n{content}", metadata={"section": title}))
 
 
46
  return docs
47
 
48
- # -------------------- TF-IDF Embedder --------------------
 
 
49
  class TfidfEmbedding(Embeddings):
50
  def __init__(self):
51
- self.vectorizer = TfidfVectorizer()
52
 
53
  def fit(self, texts):
54
- self.vectorizer.fit(texts)
55
 
56
  def embed_documents(self, texts):
57
- return self.vectorizer.transform(texts).toarray()
58
 
59
  def embed_query(self, text):
60
- return self.vectorizer.transform([text]).toarray()[0]
61
 
62
- # -------------------- Custom Prompt --------------------
63
- TEMPLATE = """
64
- You are a strict healthcare policy checker for Systems Ltd.
 
 
65
  Always begin your answer clearly:
66
  - Say "Yes, ..." if the claim is valid
67
  - Say "No, ..." if the claim is not valid
68
- - Say "Partially, ..." if it's conditionally allowed
69
- Use the following policy information to support your answer.
70
  {context}
 
71
  Question: {question}
 
72
  Answer:
73
  """
74
- custom_prompt = PromptTemplate(template=TEMPLATE, input_variables=["context", "question"])
 
 
 
 
 
 
 
75
 
76
- # -------------------- Policy Initialization --------------------
77
- def initialize_policy():
78
- global qa_chain, translator_en2ur, translator_ur2en
 
 
79
 
80
- docs = extract_clean_sections("healthcare_policy.pdf")
81
- texts = [doc.page_content for doc in docs]
82
 
83
- embedder = TfidfEmbedding()
84
- embedder.fit(texts)
85
 
86
- vectordb = FAISS.from_texts(texts, embedder)
87
- retriever = vectordb.as_retriever()
88
 
89
  llm = ChatOpenAI(
90
- model="tngtech/deepseek-r1t2-chimera:free",
91
  base_url="https://openrouter.ai/api/v1",
92
  api_key=os.getenv("OPENAI_API_KEY"),
93
  default_headers={
94
  "HTTP-Referer": "https://huggingface.co",
95
- "X-Title": "PDF-RAG"
96
  },
97
  temperature=0.0
98
  )
@@ -100,53 +117,69 @@ def initialize_policy():
100
  qa_chain = RetrievalQA.from_chain_type(
101
  llm=llm,
102
  chain_type="stuff",
103
- retriever=retriever,
104
  return_source_documents=False,
105
- chain_type_kwargs={"prompt": custom_prompt}
106
  )
107
 
108
- # Load translation models
109
- translator_en2ur = pipeline("translation", model="Helsinki-NLP/opus-mt-en-ur")
110
- translator_ur2en = pipeline("translation", model="Helsinki-NLP/opus-mt-ur-en")
111
 
 
 
 
 
 
112
 
113
- # -------------------- QA with Bilingual Support --------------------
114
- def ask_policy_question(question, language):
 
 
 
 
 
115
  if qa_chain is None:
116
- return "The policy is still loading. Please wait."
 
117
  try:
118
- if language == "Urdu":
119
- question_en = translator_ur2en(question)[0]['translation_text']
120
- answer_en = qa_chain.run(question_en)
121
- answer_ur = translator_en2ur(answer_en)[0]['translation_text']
122
- return answer_ur
123
- else:
124
- return qa_chain.run(question)
125
  except Exception as e:
126
- return f"Error: {str(e)}"
127
 
128
- # -------------------- Gradio Interface --------------------
129
- status_text = "Loading..."
 
 
130
 
131
  with gr.Blocks() as demo:
132
- gr.Markdown("## 📋 SL HealthCare Claim Checker (Bilingual: English / اردو)")
133
 
134
- status_box = gr.Textbox(label="Status", value=status_text, interactive=False)
135
 
136
  with gr.Row():
137
- language = gr.Radio(choices=["English", "Urdu"], label="Select Language / زبان منتخب کریں", value="English")
138
- question = gr.Textbox(label="Enter your claim question / اپنا سوال درج کریں")
139
- ask_btn = gr.Button("Ask / پوچھیں")
 
 
 
140
 
141
- answer = gr.Textbox(label="Answer / جواب", lines=6)
142
- ask_btn.click(fn=ask_policy_question, inputs=[question, language], outputs=answer)
143
 
144
- def startup():
145
- global status_text
146
- initialize_policy()
147
- status_text = "Policy loaded. You may now ask questions."
148
- return status_text
149
 
150
- demo.load(fn=startup, outputs=status_box)
151
 
152
- demo.launch()
 
 
 
1
  import os
 
 
2
  import re
3
+ import pdfplumber
4
+ import gradio as gr
5
  from langchain.docstore.document import Document
6
+ from langchain_community.vectorstores import FAISS # new import path
7
  from langchain.embeddings.base import Embeddings
8
  from sklearn.feature_extraction.text import TfidfVectorizer
 
9
  from langchain.prompts import PromptTemplate
10
+ from langchain.chains import RetrievalQA
11
  from langchain_openai import ChatOpenAI
12
  from transformers import pipeline
13
 
14
+ # ------------------------------------------------------------------
15
+ # 🔧 1. OpenRouter configuration (acts as an OpenAI‑compatible host)
16
+ # ------------------------------------------------------------------
17
+ os.environ["OPENAI_API_KEY"] = os.getenv("OPENROUTER_API_KEY") # use the same key
18
  os.environ["OPENAI_API_BASE"] = "https://openrouter.ai/api/v1"
19
+ os.environ["OPENAI_API_HEADERS"] = (
20
+ '{"HTTP-Referer":"https://huggingface.co", "X-Title":"PDF‑RAG"}'
21
+ )
22
+
23
+ # ------------------------------------------------------------------
24
+ # 📄 2. PDF → clean sections
25
+ # ------------------------------------------------------------------
26
+ def extract_clean_sections(file_path: str):
 
27
  with pdfplumber.open(file_path) as pdf:
28
+ full = ""
29
  for page in pdf.pages:
30
+ text = page.extract_text() or ""
31
+ # strip repeating footer/header noise
32
+ text = re.sub(r"Systems Campus.*?Lahore", "", text, flags=re.I)
33
+ text = re.sub(r"E-mail:.*?systemsltd\.com", "", text, flags=re.I)
34
+ full += text + "\n"
35
 
36
+ # split on headings in ALL‑CAPS followed by colon
37
  pattern = r"(?<=\n)([A-Z][^\n]{3,50}):"
38
+ parts = re.split(pattern, full)
39
 
40
  docs = []
41
  for i in range(1, len(parts), 2):
42
+ title = parts[i].strip()
43
  content = parts[i + 1].strip()
44
  if len(content) > 20:
45
+ docs.append(
46
+ Document(page_content=f"{title}:\n{content}", metadata={"section": title})
47
+ )
48
  return docs
49
 
50
+ # ------------------------------------------------------------------
51
+ # 🔍 3. Lightweight TF‑IDF embeddings (LangChain interface)
52
+ # ------------------------------------------------------------------
53
  class TfidfEmbedding(Embeddings):
54
  def __init__(self):
55
+ self.vec = TfidfVectorizer()
56
 
57
  def fit(self, texts):
58
+ self.vec.fit(texts)
59
 
60
  def embed_documents(self, texts):
61
+ return self.vec.transform(texts).toarray()
62
 
63
  def embed_query(self, text):
64
+ return self.vec.transform([text]).toarray()[0]
65
 
66
+ # ------------------------------------------------------------------
67
+ # 📝 4. Strict answer prompt
68
+ # ------------------------------------------------------------------
69
+ PROMPT_TMPL = """
70
+ You are a strict healthcare policy checker for Systems Ltd.
71
  Always begin your answer clearly:
72
  - Say "Yes, ..." if the claim is valid
73
  - Say "No, ..." if the claim is not valid
74
+ - Say "Partially, ..." if it is conditionally allowed
75
+ Use ONLY the following policy information to justify your answer.
76
  {context}
77
+
78
  Question: {question}
79
+
80
  Answer:
81
  """
82
+ STRICT_PROMPT = PromptTemplate(
83
+ template=PROMPT_TMPL, input_variables=["context", "question"]
84
+ )
85
+
86
+ # globals
87
+ qa_chain = None
88
+ tr_en2ur = None
89
+ tr_ur2en = None
90
 
91
+ # ------------------------------------------------------------------
92
+ # 🚀 5. Initialise on startup (build index + load translators)
93
+ # ------------------------------------------------------------------
94
+ def initialise():
95
+ global qa_chain, tr_en2ur, tr_ur2en
96
 
97
+ docs = extract_clean_sections("healthcare_policy.pdf")
98
+ texts = [d.page_content for d in docs]
99
 
100
+ emb = TfidfEmbedding()
101
+ emb.fit(texts)
102
 
103
+ vectdb = FAISS.from_texts(texts, emb)
104
+ retr = vectdb.as_retriever()
105
 
106
  llm = ChatOpenAI(
107
+ model="tngtech/deepseek-r1t2-chimera:free", # any OpenRouter model OK
108
  base_url="https://openrouter.ai/api/v1",
109
  api_key=os.getenv("OPENAI_API_KEY"),
110
  default_headers={
111
  "HTTP-Referer": "https://huggingface.co",
112
+ "X-Title": "PDFRAG"
113
  },
114
  temperature=0.0
115
  )
 
117
  qa_chain = RetrievalQA.from_chain_type(
118
  llm=llm,
119
  chain_type="stuff",
120
+ retriever=retr,
121
  return_source_documents=False,
122
+ chain_type_kwargs={"prompt": STRICT_PROMPT},
123
  )
124
 
125
+ # translation pipelines (require `sentencepiece`)
126
+ tr_en2ur = pipeline("translation", model="Helsinki-NLP/opus-mt-en-ur")
127
+ tr_ur2en = pipeline("translation", model="Helsinki-NLP/opus-mt-ur-en")
128
 
129
+ # ------------------------------------------------------------------
130
+ # 🗣️ 6. Translation helpers
131
+ # ------------------------------------------------------------------
132
+ def urdu_to_english(text: str) -> str:
133
+ return tr_ur2en(text)[0]["translation_text"]
134
 
135
+ def english_to_urdu(text: str) -> str:
136
+ return tr_en2ur(text)[0]["translation_text"]
137
+
138
+ # ------------------------------------------------------------------
139
+ # 🤖 7. Main QA endpoint
140
+ # ------------------------------------------------------------------
141
+ def answer(question: str, lang: str):
142
  if qa_chain is None:
143
+ return "⏳ Policy is still loading, please wait..."
144
+
145
  try:
146
+ # 🔄 Urdu English → RAG
147
+ q_en = urdu_to_english(question) if lang == "Urdu" else question
148
+ a_en = qa_chain.run(q_en)
149
+
150
+ # 🔄 English → Urdu (if needed)
151
+ return english_to_urdu(a_en) if lang == "Urdu" else a_en
 
152
  except Exception as e:
153
+ return f"❌ Error: {e}"
154
 
155
+ # ------------------------------------------------------------------
156
+ # 🎛️ 8. Gradio UI
157
+ # ------------------------------------------------------------------
158
+ status = "⏳ Loading policy ..."
159
 
160
  with gr.Blocks() as demo:
161
+ gr.Markdown("## 📋 SL HealthCare Claim Checker — Bilingual (English / اردو)")
162
 
163
+ status_box = gr.Textbox(value=status, label="Status", interactive=False)
164
 
165
  with gr.Row():
166
+ lang_sel = gr.Radio(choices=["English", "Urdu"], value="English",
167
+ label="Language / زبان")
168
+ question = gr.Textbox(lines=2, label="Your question / اپنا سوال")
169
+ ask_btn = gr.Button("Ask / پوچھیں")
170
+
171
+ answer_box = gr.Textbox(lines=6, label="Answer / جواب", interactive=False)
172
 
173
+ ask_btn.click(answer, inputs=[question, lang_sel], outputs=answer_box)
 
174
 
175
+ def on_load():
176
+ global status
177
+ initialise()
178
+ status = "✅ Policy loaded — ask away!"
179
+ return status
180
 
181
+ demo.load(fn=on_load, outputs=status_box)
182
 
183
+ # local run
184
+ if __name__ == "__main__":
185
+ demo.launch()