aniketsirsikar commited on
Commit
832eee4
·
verified ·
1 Parent(s): 0f973fd

Upload 2 files

Browse files
Files changed (2) hide show
  1. rag_pipeline.py +281 -0
  2. requirements.txt +6 -0
rag_pipeline.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ RAG Pipeline for Hindi Banking Voice Assistant
3
+ Uses: Whisper (STT) + Indic-Gemma (LLM) + Indic-Parler-TTS (TTS)
4
+ All retrieval in English, all responses in Hindi.
5
+ """
6
+
7
+ import re
8
+ import numpy as np
9
+ from typing import Optional
10
+ import torch
11
+ from knowledge_base import get_document_texts, KNOWLEDGE_BASE
12
+
13
+
14
+ # ─────────────────────────────────────────────
15
+ # FINANCE JARGON NORMALIZATION MAP
16
+ # Hindi/Hinglish → English canonical terms
17
+ # ─────────────────────────────────────────────
18
+ JARGON_MAP = {
19
+ # EMI variants
20
+ "kist": "EMI equated monthly installment",
21
+ "maahik kist": "monthly installment EMI",
22
+ "maasik bhugtan": "monthly payment EMI",
23
+ "kisten": "installments EMI",
24
+ "maahik bhugtan": "monthly installment EMI",
25
+
26
+ # Loan
27
+ "karz": "loan",
28
+ "udhaar": "loan credit",
29
+ "rin": "loan",
30
+ "loan lena": "apply for loan",
31
+ "loan milega": "loan eligibility",
32
+ "paise chahiye": "need money loan",
33
+ "paisa": "money funds",
34
+ "raqam": "amount loan",
35
+
36
+ # Interest
37
+ "byaj": "interest rate",
38
+ "sudh": "interest",
39
+ "byaj dar": "interest rate",
40
+ "faixed byaj": "fixed interest rate",
41
+ "badlav wala byaj": "floating interest rate",
42
+
43
+ # Bank account
44
+ "khata": "bank account",
45
+ "bachat khata": "savings account",
46
+ "khata kholna": "open bank account",
47
+ "bank mein khata": "bank account",
48
+
49
+ # Collateral/Guarantee
50
+ "zamanat": "collateral guarantee security",
51
+ "zamanatdar": "guarantor",
52
+ "girwi": "mortgage pledge",
53
+ "girvi rakhna": "pledge collateral",
54
+
55
+ # Documents
56
+ "kaagaz": "documents",
57
+ "dastavej": "documents",
58
+ "pehchaan patra": "identity proof",
59
+ "niwas praman": "address proof",
60
+ "aay praman": "income proof",
61
+
62
+ # Credit/CIBIL
63
+ "saakh": "credit score CIBIL",
64
+ "credit score kya hai": "what is credit score CIBIL",
65
+ "score": "CIBIL credit score",
66
+
67
+ # Principal/Tenure
68
+ "mool rashi": "principal amount",
69
+ "avadhi": "loan tenure duration",
70
+ "muddat": "loan tenure period",
71
+ "kitne saal": "how many years tenure",
72
+ "kitne mahine": "how many months tenure",
73
+
74
+ # Repayment
75
+ "wapasi": "repayment",
76
+ "bhugtan": "payment repayment",
77
+ "chukana": "repay loan",
78
+ "ada karna": "pay repay",
79
+
80
+ # Government schemes
81
+ "sarkar ki yojana": "government scheme",
82
+ "yojana": "scheme",
83
+ "sarkari loan": "government loan scheme",
84
+ "subsidy": "subsidy government benefit",
85
+ "anudan": "grant subsidy",
86
+
87
+ # Specific schemes
88
+ "mudra": "mudra loan PMMY",
89
+ "kisaan": "farmer kisan",
90
+ "kisan": "farmer kisan credit card",
91
+ "jan dhan": "PMJDY jan dhan account",
92
+ "bima": "insurance",
93
+ "jeevan bima": "life insurance PMJJBY",
94
+ "suraksha bima": "accident insurance PMSBY",
95
+ "pension": "pension APY Atal Pension Yojana",
96
+ "gramin bank": "rural bank RRB",
97
+ "shg": "self help group SHG women loan",
98
+ "samuh": "self help group SHG",
99
+ "mahila samuh": "women self help group SHG microfinance",
100
+
101
+ # Defaults/issues
102
+ "default": "loan default NPA",
103
+ "band ho gaya": "account closed loan default",
104
+ "paise nahin de paya": "unable to repay loan default",
105
+ "chhoot": "waiver loan waiver",
106
+
107
+ # Property
108
+ "ghar lena": "home purchase home loan",
109
+ "makan": "house home property",
110
+ "zameen": "land property",
111
+ "ghar banana": "home construction loan",
112
+ "flat": "apartment home loan",
113
+
114
+ # Grievance
115
+ "shikayat": "complaint grievance",
116
+ "problem": "complaint issue grievance",
117
+ "dhoka": "fraud complaint",
118
+ "pareshan": "problem issue complaint",
119
+ }
120
+
121
+
122
+ def normalize_jargon(text: str) -> str:
123
+ """
124
+ Replace Hindi/Hinglish finance jargon with standardized English terms
125
+ to improve retrieval quality.
126
+ """
127
+ text_lower = text.lower()
128
+ for hindi_term, english_term in JARGON_MAP.items():
129
+ if hindi_term in text_lower:
130
+ text_lower = text_lower.replace(hindi_term, english_term)
131
+ return text_lower
132
+
133
+
134
+ def translate_to_retrieval_query(hindi_text: str) -> str:
135
+ """
136
+ Convert normalized Hindi/Hinglish text to an English retrieval query.
137
+ This uses rule-based translation for common patterns.
138
+ For production: could use a translation model.
139
+ """
140
+ normalized = normalize_jargon(hindi_text)
141
+
142
+ # Extract key English terms that emerged from normalization
143
+ # plus keep any already-English words in the original
144
+ english_words = []
145
+
146
+ # Words already in English or normalized
147
+ for word in normalized.split():
148
+ if any(c.isalpha() for c in word):
149
+ english_words.append(word)
150
+
151
+ return " ".join(english_words[:20]) # cap at 20 words for retrieval
152
+
153
+
154
+ # ─────────────────────────────────────────────
155
+ # SIMPLE BM25-STYLE KEYWORD RETRIEVAL
156
+ # (No heavy dependencies — pure Python TF-IDF-like scoring)
157
+ # ─────────────────────────────────────────────
158
+ class SimpleRetriever:
159
+ def __init__(self):
160
+ self.documents = []
161
+ self.doc_ids = []
162
+ self.doc_words = []
163
+ self._build_index()
164
+
165
+ def _build_index(self):
166
+ """Build keyword index over knowledge base."""
167
+ for doc_id, text in get_document_texts():
168
+ self.doc_ids.append(doc_id)
169
+ self.documents.append(text.lower())
170
+ words = set(re.findall(r'\b\w+\b', text.lower()))
171
+ self.doc_words.append(words)
172
+
173
+ # Also index tags for better matching
174
+ for i, doc in enumerate(KNOWLEDGE_BASE):
175
+ tags_text = " ".join(doc.get("tags", []))
176
+ self.documents[i] += " " + tags_text.lower()
177
+ self.doc_words[i].update(re.findall(r'\b\w+\b', tags_text.lower()))
178
+
179
+ def retrieve(self, query: str, top_k: int = 3) -> list[dict]:
180
+ """Retrieve top_k most relevant documents for query."""
181
+ query_words = set(re.findall(r'\b\w+\b', query.lower()))
182
+
183
+ if not query_words:
184
+ return []
185
+
186
+ scores = []
187
+ for i, doc_words in enumerate(self.doc_words):
188
+ # Jaccard-like overlap + length normalization
189
+ overlap = len(query_words & doc_words)
190
+ score = overlap / (len(query_words) + 0.5)
191
+
192
+ # Bonus for exact phrase matches
193
+ for qw in query_words:
194
+ if len(qw) > 4 and qw in self.documents[i]:
195
+ score += 0.3
196
+
197
+ scores.append((score, i))
198
+
199
+ scores.sort(reverse=True)
200
+ top_indices = [idx for score, idx in scores[:top_k] if score > 0]
201
+
202
+ results = []
203
+ for idx in top_indices:
204
+ doc = KNOWLEDGE_BASE[idx]
205
+ results.append({
206
+ "id": doc["id"],
207
+ "title": doc["title"],
208
+ "content": doc["content"],
209
+ "category": doc["category"],
210
+ "score": scores[idx][0] if idx < len(scores) else 0,
211
+ })
212
+
213
+ return results
214
+
215
+
216
+ # Singleton retriever
217
+ _retriever = None
218
+
219
+ def get_retriever() -> SimpleRetriever:
220
+ global _retriever
221
+ if _retriever is None:
222
+ _retriever = SimpleRetriever()
223
+ return _retriever
224
+
225
+
226
+ def build_rag_prompt(user_question: str, retrieved_docs: list[dict]) -> str:
227
+ """
228
+ Build the prompt for the Indic-Gemma LLM.
229
+ System prompt enforces Hindi, grounded answers, brevity.
230
+ """
231
+ context_parts = []
232
+ for i, doc in enumerate(retrieved_docs, 1):
233
+ context_parts.append(f"[{i}] {doc['title']}\n{doc['content'][:600]}")
234
+
235
+ context = "\n\n".join(context_parts) if context_parts else "कोई प्रासंगिक जानकारी नहीं मिली।"
236
+
237
+ prompt = f"""<|system|>
238
+ आप एक सहायक बैंकिंग सहायक हैं जो भारतीय बैंकिंग, लोन, और सरकारी योजनाओं के बारे में सरल हिंदी में जानकारी देते हैं।
239
+
240
+ नियम:
241
+ 1. केवल नीचे दी गई जानकारी के आधार पर उत्तर दें। अनुमान न लगाएं।
242
+ 2. उत्तर छोटा, सरल और बोलने योग्य हो — 3-4 वाक्यों में।
243
+ 3. यदि जानकारी उपलब्ध नहीं है, तो कहें: "यह जानकारी मेरे पास नहीं है। कृपया अपने बैंक से संपर्क करें।"
244
+ 4. अंत में केवल एक जरूरी follow-up प्रश्न पूछें (यदि आवश्यक हो)।
245
+ 5. हमेशा हिंदी में उत्तर दें।
246
+
247
+ संदर्भ जानकारी:
248
+ {context}
249
+ <|end|>
250
+ <|user|>
251
+ {user_question}
252
+ <|end|>
253
+ <|assistant|>"""
254
+
255
+ return prompt
256
+
257
+
258
+ def format_response_for_tts(text: str) -> str:
259
+ """
260
+ Clean LLM output for TTS: remove special chars, ensure clean Hindi.
261
+ """
262
+ # Remove markdown
263
+ text = re.sub(r'\*+', '', text)
264
+ text = re.sub(r'#+\s*', '', text)
265
+ text = re.sub(r'\[[\d]+\]', '', text) # remove citation markers
266
+ text = re.sub(r'\n+', ' ', text)
267
+ text = re.sub(r'\s+', ' ', text)
268
+ text = text.strip()
269
+ return text
270
+
271
+
272
+ def get_tts_description(text: str) -> str:
273
+ """
274
+ Parler-TTS requires a description of the speaker/style.
275
+ Returns appropriate description for Hindi banking assistant.
276
+ """
277
+ return (
278
+ "A calm, clear female voice speaking in Hindi. "
279
+ "The speech is measured and helpful, like a bank customer service representative. "
280
+ "Very clear pronunciation, moderate pace, friendly tone."
281
+ )
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # CPU Basic safe — all models via HF Inference API, nothing loads locally
2
+ gradio>=4.44.0
3
+ requests>=2.31.0
4
+ soundfile>=0.12.1
5
+ numpy>=1.24.0
6
+ # NO torch, NO transformers, NO parler-tts — all inference is API-based