thecoderhere commited on
Commit
5221213
·
verified ·
1 Parent(s): aa889a9

Upload 23 files

Browse files
app/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask
2
+
3
+ print("Initializing Flask application...")
4
+
5
+ app = Flask(__name__)
6
+ app.config['SECRET_KEY'] = 'your-secret-key-here'
7
+
8
+ print("Loading routes...")
9
+ from app import routes
10
+
11
+ print("Application initialized successfully!")
app/__pycache__/__init__.cpython-313.pyc ADDED
Binary file (523 Bytes). View file
 
app/__pycache__/bot.cpython-313.pyc ADDED
Binary file (25.2 kB). View file
 
app/__pycache__/data_manager.cpython-313.pyc ADDED
Binary file (2.42 kB). View file
 
app/__pycache__/processor.cpython-313.pyc ADDED
Binary file (3.26 kB). View file
 
app/__pycache__/retriever.cpython-313.pyc ADDED
Binary file (1.9 kB). View file
 
app/__pycache__/routes.cpython-313.pyc ADDED
Binary file (1.13 kB). View file
 
app/__pycache__/run_scraper.cpython-313.pyc ADDED
Binary file (1.24 kB). View file
 
app/__pycache__/scraper.cpython-313.pyc ADDED
Binary file (20.9 kB). View file
 
app/bot.py ADDED
@@ -0,0 +1,626 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/bot.py
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+ import os
6
+ import re
7
+ import unicodedata
8
+ import warnings
9
+ from pathlib import Path
10
+ from typing import Any, List, Dict, Tuple
11
+ import json
12
+
13
+ import numpy as np
14
+ import pandas as pd
15
+ import torch
16
+ from sentence_transformers import SentenceTransformer, CrossEncoder
17
+ from sklearn.metrics.pairwise import cosine_similarity
18
+ from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM
19
+ import nltk
20
+
21
+ # Download required NLTK data
22
+ try:
23
+ nltk.download('punkt', quiet=True)
24
+ nltk.download('stopwords', quiet=True)
25
+ except:
26
+ pass
27
+
28
+ warnings.filterwarnings("ignore")
29
+
30
+
31
+ class RequirementError(RuntimeError):
32
+ pass
33
+
34
+
35
+ class JupiterFAQBot:
36
+ # ------------------------------------------------------------------ #
37
+ # Free Models Configuration
38
+ # ------------------------------------------------------------------ #
39
+ MODELS = {
40
+ "bi": "sentence-transformers/all-MiniLM-L6-v2", # Fast semantic search
41
+ "cross": "cross-encoder/ms-marco-MiniLM-L-6-v2", # Reranking
42
+ "qa": "deepset/roberta-base-squad2", # Better QA model
43
+ "summarizer": "facebook/bart-large-cnn", # Better summarization
44
+ }
45
+
46
+ # Retrieval parameters
47
+ TOP_K = 15 # More candidates for better coverage
48
+ HIGH_SIM = 0.85 # High confidence threshold
49
+ CROSS_OK = 0.50 # Cross-encoder threshold
50
+ MIN_SIM = 0.40 # Minimum similarity to consider
51
+
52
+ # Paths
53
+ EMB_CACHE = Path("data/faq_embeddings.npy")
54
+ FAQ_PATH = Path("data/faqs.csv")
55
+
56
+ # Response templates for better UX
57
+ CONFIDENCE_LEVELS = {
58
+ "high": "This information matches your query based on our FAQs:\n\n",
59
+ "medium": "This appears to be relevant to your question:\n\n",
60
+ "low": "This may be related to your query and could be helpful:\n\n",
61
+ "none": (
62
+ "We couldn't find a direct match for your question. "
63
+ "However, we can assist with topics such as:\n"
64
+ "• Account opening and KYC\n"
65
+ "• Payments and UPI\n"
66
+ "• Rewards and cashback\n"
67
+ "• Credit cards and loans\n"
68
+ "• Investments and savings\n\n"
69
+ "Please try rephrasing your question or selecting a topic above."
70
+ )
71
+ }
72
+
73
+ # ------------------------------------------------------------------ #
74
+ def __init__(self, csv_path: str = None) -> None:
75
+ logging.basicConfig(format="%(levelname)s | %(message)s", level=logging.INFO)
76
+
77
+ # Use provided path or default
78
+ self.csv_path = csv_path or str(self.FAQ_PATH)
79
+
80
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
81
+ self.pipe_dev = 0 if self.device.type == "cuda" else -1
82
+
83
+ self._load_data(self.csv_path)
84
+ self._setup_models()
85
+ self._setup_embeddings()
86
+
87
+ logging.info("Jupiter FAQ Bot ready ✔")
88
+
89
+ # ------------------------ Text Processing ------------------------- #
90
+ @staticmethod
91
+ def _clean(text: str) -> str:
92
+ """Clean and normalize text"""
93
+ if pd.isna(text):
94
+ return ""
95
+ text = str(text)
96
+ text = unicodedata.normalize("NFC", text)
97
+ # Remove extra whitespace but keep sentence structure
98
+ text = re.sub(r'\s+', ' ', text)
99
+ # Keep bullet points and formatting
100
+ text = re.sub(r'•\s*', '\n• ', text)
101
+ return text.strip()
102
+
103
+ @staticmethod
104
+ def _preprocess_query(query: str) -> str:
105
+ """Preprocess user query for better matching"""
106
+ # Expand common abbreviations
107
+ abbreviations = {
108
+ 'kyc': 'know your customer verification',
109
+ 'upi': 'unified payments interface',
110
+ 'fd': 'fixed deposit',
111
+ 'sip': 'systematic investment plan',
112
+ 'neft': 'national electronic funds transfer',
113
+ 'rtgs': 'real time gross settlement',
114
+ 'imps': 'immediate payment service',
115
+ 'emi': 'equated monthly installment',
116
+ 'apr': 'annual percentage rate',
117
+ 'atm': 'automated teller machine',
118
+ 'pin': 'personal identification number',
119
+ }
120
+
121
+ query_lower = query.lower()
122
+ for abbr, full in abbreviations.items():
123
+ if abbr in query_lower.split():
124
+ query_lower = query_lower.replace(abbr, full)
125
+
126
+ return query_lower
127
+
128
+ # ------------------------ Initialization -------------------------- #
129
+ def _load_data(self, path: str):
130
+ """Load and preprocess FAQ data"""
131
+ if not Path(path).exists():
132
+ raise RequirementError(f"CSV not found: {path}")
133
+
134
+ df = pd.read_csv(path)
135
+
136
+ # Clean all text fields
137
+ df["question"] = df["question"].apply(self._clean)
138
+ df["answer"] = df["answer"].apply(self._clean)
139
+ df["category"] = df["category"].fillna("General")
140
+
141
+ # Create searchable text combining question and category
142
+ df["searchable"] = df["question"].str.lower() + " " + df["category"].str.lower()
143
+
144
+ # Remove duplicates
145
+ df = df.drop_duplicates(subset=["question"]).reset_index(drop=True)
146
+
147
+ self.faq = df
148
+ logging.info(f"Loaded {len(self.faq)} FAQ entries from {len(df['category'].unique())} categories")
149
+
150
+ def _setup_models(self):
151
+ """Initialize all models"""
152
+ logging.info("Loading models...")
153
+
154
+ # Sentence transformer for embeddings
155
+ self.bi = SentenceTransformer(self.MODELS["bi"], device=self.device)
156
+
157
+ # Cross-encoder for reranking
158
+ self.cross = CrossEncoder(self.MODELS["cross"], device=self.device)
159
+
160
+ # QA model
161
+ self.qa = pipeline(
162
+ "question-answering",
163
+ model=self.MODELS["qa"],
164
+ device=self.pipe_dev,
165
+ handle_impossible_answer=True
166
+ )
167
+
168
+ # Summarization model - using BART for better quality
169
+ self.summarizer = pipeline(
170
+ "summarization",
171
+ model=self.MODELS["summarizer"],
172
+ device=self.pipe_dev,
173
+ max_length=150,
174
+ min_length=50
175
+ )
176
+
177
+ logging.info("All models loaded successfully")
178
+
179
+ def _setup_embeddings(self):
180
+ """Create or load embeddings"""
181
+ questions = self.faq["searchable"].tolist()
182
+
183
+ if self.EMB_CACHE.exists():
184
+ emb = np.load(self.EMB_CACHE)
185
+ if len(emb) != len(questions):
186
+ logging.info("Regenerating embeddings due to data change...")
187
+ emb = self.bi.encode(questions, show_progress_bar=True, convert_to_tensor=False)
188
+ np.save(self.EMB_CACHE, emb)
189
+ else:
190
+ logging.info("Creating embeddings for the first time...")
191
+ emb = self.bi.encode(questions, show_progress_bar=True, convert_to_tensor=False)
192
+ self.EMB_CACHE.parent.mkdir(parents=True, exist_ok=True)
193
+ np.save(self.EMB_CACHE, emb)
194
+
195
+ self.embeddings = emb
196
+
197
+ # ------------------------- Retrieval ------------------------------ #
198
+ def _retrieve_candidates(self, query: str, top_k: int = None) -> List[Dict]:
199
+ """Retrieve top candidates using semantic search"""
200
+ if top_k is None:
201
+ top_k = self.TOP_K
202
+
203
+ # Preprocess query
204
+ processed_query = self._preprocess_query(query)
205
+
206
+ # Encode query
207
+ query_emb = self.bi.encode([processed_query])
208
+
209
+ # Calculate similarities
210
+ similarities = cosine_similarity(query_emb, self.embeddings)[0]
211
+
212
+ # Get top indices
213
+ top_indices = similarities.argsort()[-top_k:][::-1]
214
+
215
+ # Filter by minimum similarity
216
+ candidates = []
217
+ for idx in top_indices:
218
+ if similarities[idx] >= self.MIN_SIM:
219
+ candidates.append({
220
+ "idx": int(idx),
221
+ "question": self.faq.iloc[idx]["question"],
222
+ "answer": self.faq.iloc[idx]["answer"],
223
+ "category": self.faq.iloc[idx]["category"],
224
+ "similarity": float(similarities[idx])
225
+ })
226
+
227
+ return candidates
228
+
229
+ def _rerank_candidates(self, query: str, candidates: List[Dict]) -> List[Dict]:
230
+ """Rerank candidates using cross-encoder"""
231
+ if not candidates:
232
+ return []
233
+
234
+ # Prepare pairs for cross-encoder
235
+ pairs = [[query, c["question"]] for c in candidates]
236
+
237
+ # Get cross-encoder scores
238
+ scores = self.cross.predict(pairs, convert_to_numpy=True)
239
+
240
+ # Add scores to candidates
241
+ for c, score in zip(candidates, scores):
242
+ c["cross_score"] = float(score)
243
+
244
+ # Filter and sort by cross-encoder score
245
+ reranked = [c for c in candidates if c["cross_score"] >= self.CROSS_OK]
246
+ reranked.sort(key=lambda x: x["cross_score"], reverse=True)
247
+
248
+ return reranked
249
+
250
+ def _extract_answer(self, query: str, context: str) -> Dict[str, Any]:
251
+ """Extract specific answer using QA model"""
252
+ try:
253
+ result = self.qa(question=query, context=context)
254
+ return {
255
+ "answer": result["answer"],
256
+ "score": result["score"],
257
+ "start": result.get("start", 0),
258
+ "end": result.get("end", len(result["answer"]))
259
+ }
260
+ except Exception as e:
261
+ logging.warning(f"QA extraction failed: {e}")
262
+ return {"answer": context, "score": 0.5}
263
+
264
+ def _create_friendly_response(self, answers: List[str], confidence: str = "medium") -> str:
265
+ """Create a user-friendly response from multiple answers"""
266
+ if not answers:
267
+ return self.CONFIDENCE_LEVELS["none"]
268
+
269
+ # Remove duplicates while preserving order
270
+ unique_answers = []
271
+ seen = set()
272
+ for ans in answers:
273
+ normalized = ans.lower().strip()
274
+ if normalized not in seen:
275
+ seen.add(normalized)
276
+ unique_answers.append(ans)
277
+
278
+ if len(unique_answers) == 1:
279
+ # Single answer - return as is with confidence prefix
280
+ return self.CONFIDENCE_LEVELS[confidence] + unique_answers[0]
281
+
282
+ # Multiple answers - need to summarize
283
+ combined_text = " ".join(unique_answers)
284
+
285
+ # If text is short enough, format it nicely
286
+ if len(combined_text) < 300:
287
+ response = self.CONFIDENCE_LEVELS[confidence]
288
+ for i, answer in enumerate(unique_answers):
289
+ if "•" in answer:
290
+ # Already has bullets
291
+ response += answer + "\n\n"
292
+ else:
293
+ # Add as paragraph
294
+ response += answer + "\n\n"
295
+ return response.strip()
296
+
297
+ # Long text - summarize it
298
+ try:
299
+ # Prepare text for summarization
300
+ summary_input = f"Summarize the following information about Jupiter banking services: {combined_text}"
301
+
302
+ # Generate summary
303
+ summary = self.summarizer(summary_input, max_length=150, min_length=50, do_sample=False)
304
+ summarized_text = summary[0]['summary_text']
305
+
306
+ # Make it more conversational
307
+ response = self.CONFIDENCE_LEVELS[confidence]
308
+ response += self._make_conversational(summarized_text)
309
+
310
+ return response
311
+
312
+ except Exception as e:
313
+ logging.warning(f"Summarization failed: {e}")
314
+ # Fallback to formatted response
315
+ return self._format_multiple_answers(unique_answers, confidence)
316
+
317
+ def _make_conversational(self, text: str) -> str:
318
+ """Make response more conversational and friendly"""
319
+ # Add appropriate punctuation if missing
320
+ if text and text[-1] not in '.!?':
321
+ text += '.'
322
+
323
+ # Replace robotic phrases
324
+ replacements = {
325
+ "The user": "You",
326
+ "the user": "you",
327
+ "It is": "It's",
328
+ "You will": "You'll",
329
+ "You can not": "You can't",
330
+ "Do not": "Don't",
331
+ }
332
+
333
+ for old, new in replacements.items():
334
+ text = text.replace(old, new)
335
+
336
+ return text
337
+
338
+ def _format_multiple_answers(self, answers: List[str], confidence: str) -> str:
339
+ """Format multiple answers nicely"""
340
+ response = self.CONFIDENCE_LEVELS[confidence]
341
+
342
+ if len(answers) <= 3:
343
+ # Few answers - show all
344
+ for answer in answers:
345
+ if "•" in answer:
346
+ response += answer + "\n\n"
347
+ else:
348
+ response += f"• {answer}\n\n"
349
+ else:
350
+ # Many answers - group by category
351
+ response += "Here are the key points:\n\n"
352
+ for i, answer in enumerate(answers[:5]): # Limit to 5
353
+ response += f"{i+1}. {answer}\n\n"
354
+
355
+ return response.strip()
356
+
357
+ # ------------------------- Main API ------------------------------- #
358
+ def generate_response(self, query: str) -> str:
359
+ """Generate response for user query"""
360
+ query = self._clean(query)
361
+
362
+ # Step 1: Retrieve candidates
363
+ candidates = self._retrieve_candidates(query)
364
+
365
+ if not candidates:
366
+ return self.CONFIDENCE_LEVELS["none"]
367
+
368
+ # Step 2: Check for high similarity match
369
+ if candidates[0]["similarity"] >= self.HIGH_SIM:
370
+ return self.CONFIDENCE_LEVELS["high"] + candidates[0]["answer"]
371
+
372
+ # Step 3: Rerank candidates
373
+ reranked = self._rerank_candidates(query, candidates)
374
+
375
+ if not reranked:
376
+ # Use original candidates with lower confidence
377
+ reranked = candidates[:3]
378
+ confidence = "low"
379
+ else:
380
+ confidence = "high" if reranked[0]["cross_score"] > 0.8 else "medium"
381
+
382
+ # Step 4: Extract relevant answers
383
+ relevant_answers = []
384
+
385
+ for candidate in reranked[:5]: # Top 5 reranked
386
+ # Try QA extraction for more specific answer
387
+ qa_result = self._extract_answer(query, candidate["answer"])
388
+
389
+ if qa_result["score"] > 0.3:
390
+ # Good QA match
391
+ relevant_answers.append(qa_result["answer"])
392
+ else:
393
+ # Use full answer if QA didn't find specific part
394
+ relevant_answers.append(candidate["answer"])
395
+
396
+ # Step 5: Create final response
397
+ final_response = self._create_friendly_response(relevant_answers, confidence)
398
+
399
+ return final_response
400
+
401
+ def suggest_related_queries(self, query: str) -> List[str]:
402
+ """Suggest related queries based on similar questions"""
403
+ candidates = self._retrieve_candidates(query, top_k=10)
404
+
405
+ related = []
406
+ seen = set()
407
+
408
+ for candidate in candidates:
409
+ if candidate["similarity"] >= 0.5 and candidate["similarity"] < 0.9:
410
+ # Clean question for display
411
+ clean_q = candidate["question"].strip()
412
+ if clean_q.lower() not in seen and clean_q.lower() != query.lower():
413
+ seen.add(clean_q.lower())
414
+ related.append(clean_q)
415
+
416
+ # Return top 5 related queries
417
+ return related[:5]
418
+
419
+ def get_categories(self) -> List[str]:
420
+ """Get all available FAQ categories"""
421
+ return sorted(self.faq["category"].unique().tolist())
422
+
423
+ def get_faqs_by_category(self, category: str) -> List[Dict[str, str]]:
424
+ """Get all FAQs for a specific category"""
425
+ cat_faqs = self.faq[self.faq["category"].str.lower() == category.lower()]
426
+
427
+ return [
428
+ {
429
+ "question": row["question"],
430
+ "answer": row["answer"]
431
+ }
432
+ for _, row in cat_faqs.iterrows()
433
+ ]
434
+
435
+ def search_faqs(self, keyword: str) -> List[Dict[str, str]]:
436
+ """Simple keyword search in FAQs"""
437
+ keyword_lower = keyword.lower()
438
+
439
+ matches = []
440
+ for _, row in self.faq.iterrows():
441
+ if (keyword_lower in row["question"].lower() or
442
+ keyword_lower in row["answer"].lower()):
443
+ matches.append({
444
+ "question": row["question"],
445
+ "answer": row["answer"],
446
+ "category": row["category"]
447
+ })
448
+
449
+ return matches[:10] # Limit to 10 results
450
+
451
+
452
+ # Evaluation module
453
+ class BotEvaluator:
454
+ """Evaluate bot performance"""
455
+
456
+ def __init__(self, bot: JupiterFAQBot):
457
+ self.bot = bot
458
+
459
+ def create_test_queries(self) -> List[Dict[str, str]]:
460
+ """Create test queries based on FAQ categories"""
461
+ test_queries = [
462
+ # Account queries
463
+ {"query": "How do I open an account?", "expected_category": "Account"},
464
+ {"query": "What is Jupiter savings account?", "expected_category": "Account"},
465
+
466
+ # Payment queries
467
+ {"query": "How to make UPI payment?", "expected_category": "Payments"},
468
+ {"query": "What is the daily transaction limit?", "expected_category": "Payments"},
469
+
470
+ # Rewards queries
471
+ {"query": "How do I earn cashback?", "expected_category": "Rewards"},
472
+ {"query": "What are Jewels?", "expected_category": "Rewards"},
473
+
474
+ # Investment queries
475
+ {"query": "Can I invest in mutual funds?", "expected_category": "Investments"},
476
+ {"query": "What is Magic Spends?", "expected_category": "Magic Spends"},
477
+
478
+ # Loan queries
479
+ {"query": "How to apply for personal loan?", "expected_category": "Jupiter Loans"},
480
+ {"query": "What is the interest rate?", "expected_category": "Jupiter Loans"},
481
+
482
+ # Card queries
483
+ {"query": "How to get credit card?", "expected_category": "Edge+ Credit Card"},
484
+ {"query": "Is there any annual fee?", "expected_category": "Edge+ Credit Card"},
485
+ ]
486
+
487
+ return test_queries
488
+
489
+ def evaluate_retrieval_accuracy(self) -> Dict[str, float]:
490
+ """Evaluate how well the bot retrieves relevant information"""
491
+ test_queries = self.create_test_queries()
492
+
493
+ correct = 0
494
+ total = len(test_queries)
495
+
496
+ results = []
497
+
498
+ for test in test_queries:
499
+ response = self.bot.generate_response(test["query"])
500
+
501
+ # Check if response mentions expected category content
502
+ is_correct = test["expected_category"].lower() in response.lower()
503
+
504
+ if is_correct:
505
+ correct += 1
506
+
507
+ results.append({
508
+ "query": test["query"],
509
+ "expected_category": test["expected_category"],
510
+ "response": response[:200] + "..." if len(response) > 200 else response,
511
+ "correct": is_correct
512
+ })
513
+
514
+ accuracy = correct / total if total > 0 else 0
515
+
516
+ return {
517
+ "accuracy": accuracy,
518
+ "correct": correct,
519
+ "total": total,
520
+ "results": results
521
+ }
522
+
523
+ def evaluate_response_quality(self) -> Dict[str, Any]:
524
+ """Evaluate the quality of responses"""
525
+ test_queries = [
526
+ "What is Jupiter?",
527
+ "How do I earn rewards?",
528
+ "Tell me about credit cards",
529
+ "Can I get a loan?",
530
+ "How to invest money?"
531
+ ]
532
+
533
+ quality_metrics = []
534
+
535
+ for query in test_queries:
536
+ response = self.bot.generate_response(query)
537
+
538
+ # Check quality indicators
539
+ has_greeting = any(phrase in response for phrase in ["Based on", "Here's", "I found"])
540
+ has_structure = "\n" in response or "•" in response
541
+ appropriate_length = 50 < len(response) < 500
542
+
543
+ quality_score = sum([has_greeting, has_structure, appropriate_length]) / 3
544
+
545
+ quality_metrics.append({
546
+ "query": query,
547
+ "response_length": len(response),
548
+ "has_greeting": has_greeting,
549
+ "has_structure": has_structure,
550
+ "appropriate_length": appropriate_length,
551
+ "quality_score": quality_score
552
+ })
553
+
554
+ avg_quality = sum(m["quality_score"] for m in quality_metrics) / len(quality_metrics)
555
+
556
+ return {
557
+ "average_quality_score": avg_quality,
558
+ "metrics": quality_metrics
559
+ }
560
+
561
+
562
+ # Utility functions for data preparation
563
+ def prepare_faq_data(csv_path: str = "data/faqs.csv") -> pd.DataFrame:
564
+ """Prepare and validate FAQ data"""
565
+ df = pd.read_csv(csv_path)
566
+
567
+ # Ensure required columns exist
568
+ required_cols = ["question", "answer", "category"]
569
+ if not all(col in df.columns for col in required_cols):
570
+ raise ValueError(f"CSV must contain columns: {required_cols}")
571
+
572
+ # Basic stats
573
+ print(f"Total FAQs: {len(df)}")
574
+ print(f"Categories: {df['category'].nunique()}")
575
+ print(f"\nCategory distribution:")
576
+ print(df['category'].value_counts())
577
+
578
+ return df
579
+
580
+
581
+ # Main execution example
582
+ if __name__ == "__main__":
583
+ # Initialize bot
584
+ bot = JupiterFAQBot()
585
+
586
+ # Test some queries
587
+ test_queries = [
588
+ "How do I open a savings account?",
589
+ "What are the cashback rates?",
590
+ "Can I get a personal loan?",
591
+ "How to use UPI?",
592
+ "Tell me about investments"
593
+ ]
594
+
595
+ print("\n" + "="*50)
596
+ print("Testing Jupiter FAQ Bot")
597
+ print("="*50 + "\n")
598
+
599
+ for query in test_queries:
600
+ print(f"Q: {query}")
601
+ response = bot.generate_response(query)
602
+ print(f"A: {response}\n")
603
+
604
+ # Show related queries
605
+ related = bot.suggest_related_queries(query)
606
+ if related:
607
+ print("Related questions:")
608
+ for r in related[:3]:
609
+ print(f" - {r}")
610
+ print("\n" + "-"*50 + "\n")
611
+
612
+ # Run evaluation
613
+ print("\n" + "="*50)
614
+ print("Running Evaluation")
615
+ print("="*50 + "\n")
616
+
617
+ evaluator = BotEvaluator(bot)
618
+
619
+ # Retrieval accuracy
620
+ accuracy_results = evaluator.evaluate_retrieval_accuracy()
621
+ print(f"Retrieval Accuracy: {accuracy_results['accuracy']:.2%}")
622
+ print(f"Correct: {accuracy_results['correct']}/{accuracy_results['total']}")
623
+
624
+ # Response quality
625
+ quality_results = evaluator.evaluate_response_quality()
626
+ print(f"\nAverage Response Quality: {quality_results['average_quality_score']:.2%}")
app/routes.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import render_template, request, jsonify
2
+ from app import app
3
+ from app.bot import JupiterFAQBot
4
+
5
+ bot = JupiterFAQBot()
6
+
7
+ @app.route('/')
8
+ def index():
9
+ return render_template('index.html')
10
+
11
+ @app.route('/chat', methods=['POST'])
12
+ def chat():
13
+ data = request.json
14
+ question = data.get('question', '')
15
+
16
+ response = bot.generate_response(question)
17
+ suggestions = bot.suggest_related_queries(question)
18
+
19
+ return jsonify({
20
+ 'response': response,
21
+ 'suggestions': suggestions
22
+ })
app/run_scraper.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # run_scraper.py
2
+ import sys
3
+ import logging
4
+ from app.scraper import FAQUpdater
5
+
6
+ def main():
7
+ """Run the FAQ scraping process"""
8
+
9
+ # Setup logging
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
13
+ )
14
+
15
+ # Check if force update is requested
16
+ force_update = '--force' in sys.argv
17
+
18
+ # Run updater
19
+ updater = FAQUpdater()
20
+ df = updater.check_and_update(force_update=force_update)
21
+
22
+ # Display stats
23
+ stats = updater.get_scraping_stats(df)
24
+ print(f"\nScraping Statistics:")
25
+ print(f"Total FAQs: {stats['total_faqs']}")
26
+ print(f"Categories: {stats['categories']}")
27
+ print(f"Category Distribution: {stats['category_distribution']}")
28
+
29
+ if __name__ == "__main__":
30
+ main()
app/scraper.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/scraper.py (Enhanced version)
2
+ import requests
3
+ from bs4 import BeautifulSoup
4
+ import pandas as pd
5
+ import time
6
+ import re
7
+ import json
8
+ from typing import List, Dict, Optional
9
+ from pathlib import Path
10
+ import logging
11
+ from selenium import webdriver
12
+ from selenium.webdriver.common.by import By
13
+ from selenium.webdriver.support.ui import WebDriverWait
14
+ from selenium.webdriver.support import expected_conditions as EC
15
+ from selenium.webdriver.chrome.options import Options
16
+ from selenium.common.exceptions import TimeoutException
17
+
18
+ logging.basicConfig(level=logging.INFO)
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class JupiterFAQScraper:
23
+ """Enhanced scraper for Jupiter Money website"""
24
+
25
+ def __init__(self):
26
+ self.base_url = "https://jupiter.money"
27
+ self.target_urls = [
28
+ "https://jupiter.money/savings-account/",
29
+ "https://jupiter.money/pro-salary-account/",
30
+ # ... other URLs
31
+ ]
32
+
33
+ self.session = requests.Session()
34
+ self.session.headers.update({
35
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
36
+ })
37
+
38
+ def test_scraping(self) -> Dict[str, bool]:
39
+ """Test if scraping actually works"""
40
+ results = {}
41
+
42
+ # Test 1: Can we access the website?
43
+ try:
44
+ response = self.session.get(self.base_url, timeout=10)
45
+ results['website_accessible'] = response.status_code == 200
46
+ logger.info(f"Website accessible: {results['website_accessible']}")
47
+ except:
48
+ results['website_accessible'] = False
49
+ logger.error("Cannot access Jupiter website")
50
+
51
+ # Test 2: Can we find FAQ content?
52
+ if results['website_accessible']:
53
+ try:
54
+ soup = BeautifulSoup(response.content, 'html.parser')
55
+ # Look for common FAQ indicators
56
+ faq_indicators = ['faq', 'help', 'support', 'question', 'answer']
57
+ content = soup.get_text().lower()
58
+ results['faq_content_found'] = any(indicator in content for indicator in faq_indicators)
59
+ logger.info(f"FAQ content found: {results['faq_content_found']}")
60
+ except:
61
+ results['faq_content_found'] = False
62
+
63
+ return results
64
+
65
+ def scrape_with_fallback(self) -> pd.DataFrame:
66
+ """Try multiple scraping methods with fallback"""
67
+ all_faqs = []
68
+
69
+ # Method 1: Try actual scraping
70
+ logger.info("Attempting to scrape Jupiter website...")
71
+ test_results = self.test_scraping()
72
+
73
+ if test_results.get('website_accessible'):
74
+ # Try basic scraping
75
+ for url in self.target_urls[:3]: # Test with first 3 URLs
76
+ try:
77
+ faqs = self.scrape_page_safe(url)
78
+ all_faqs.extend(faqs)
79
+ if faqs:
80
+ logger.info(f"Successfully scraped {len(faqs)} FAQs from {url}")
81
+ except Exception as e:
82
+ logger.warning(f"Failed to scrape {url}: {e}")
83
+
84
+ # Method 2: If scraping fails or gets too little data, use fallback
85
+ if len(all_faqs) < 10:
86
+ logger.warning("Actual scraping yielded insufficient data. Using fallback FAQ data...")
87
+ all_faqs = self.get_fallback_faqs()
88
+
89
+ # Create DataFrame
90
+ df = pd.DataFrame(all_faqs)
91
+ if not df.empty:
92
+ df = df.drop_duplicates(subset=['question'])
93
+
94
+ return df
95
+
96
+ def scrape_page_safe(self, url: str) -> List[Dict]:
97
+ """Safely scrape a page with error handling"""
98
+ faqs = []
99
+
100
+ try:
101
+ response = self.session.get(url, timeout=10)
102
+ if response.status_code != 200:
103
+ logger.warning(f"Got status code {response.status_code} for {url}")
104
+ return faqs
105
+
106
+ soup = BeautifulSoup(response.content, 'html.parser')
107
+
108
+ # Strategy 1: Look for structured data
109
+ scripts = soup.find_all('script', type='application/ld+json')
110
+ for script in scripts:
111
+ try:
112
+ data = json.loads(script.string)
113
+ if '@type' in data and 'FAQ' in str(data.get('@type')):
114
+ # Extract FAQ structured data
115
+ faqs.extend(self.extract_structured_faqs(data))
116
+ except:
117
+ continue
118
+
119
+ # Strategy 2: Look for FAQ sections
120
+ faq_sections = soup.find_all(['div', 'section'],
121
+ class_=re.compile(r'faq|question|help|support', re.I))
122
+
123
+ for section in faq_sections[:5]: # Limit to prevent too many
124
+ faqs.extend(self.extract_section_faqs(section, url))
125
+
126
+ except Exception as e:
127
+ logger.error(f"Error scraping {url}: {e}")
128
+
129
+ return faqs
130
+
131
+ def extract_structured_faqs(self, data: dict) -> List[Dict]:
132
+ """Extract FAQs from structured data"""
133
+ faqs = []
134
+
135
+ if isinstance(data, dict):
136
+ if data.get('@type') == 'FAQPage':
137
+ for item in data.get('mainEntity', []):
138
+ if item.get('@type') == 'Question':
139
+ faqs.append({
140
+ 'question': self._clean_text(item.get('name', '')),
141
+ 'answer': self._clean_text(
142
+ item.get('acceptedAnswer', {}).get('text', '')
143
+ ),
144
+ 'category': 'General'
145
+ })
146
+
147
+ return faqs
148
+
149
+ def extract_section_faqs(self, section, url: str) -> List[Dict]:
150
+ """Extract FAQs from a page section"""
151
+ faqs = []
152
+ category = self._get_category_from_url(url)
153
+
154
+ # Look for Q&A pairs
155
+ questions = section.find_all(['h2', 'h3', 'h4', 'dt', 'div'],
156
+ class_=re.compile(r'question|title|header', re.I))
157
+
158
+ for q in questions[:10]: # Limit to prevent too many
159
+ # Try to find corresponding answer
160
+ answer = None
161
+
162
+ # Check next sibling
163
+ next_elem = q.find_next_sibling()
164
+ if next_elem and next_elem.name in ['p', 'div', 'dd']:
165
+ answer = next_elem
166
+
167
+ # Check parent's next sibling
168
+ if not answer:
169
+ parent = q.parent
170
+ if parent:
171
+ next_elem = parent.find_next_sibling()
172
+ if next_elem:
173
+ answer = next_elem.find(['p', 'div'])
174
+
175
+ if answer:
176
+ faqs.append({
177
+ 'question': self._clean_text(q.get_text()),
178
+ 'answer': self._clean_text(answer.get_text()),
179
+ 'category': category
180
+ })
181
+
182
+ return faqs
183
+
184
+ def get_fallback_faqs(self) -> List[Dict]:
185
+ """Return comprehensive fallback FAQs based on Jupiter's services"""
186
+ # This is the fallback data that will be used if scraping fails
187
+ # Based on the FAQs you provided earlier
188
+ return [
189
+ # Account
190
+ {
191
+ 'question': 'What is the Jupiter All-in-1 Savings Account?',
192
+ 'answer': 'The All-in-1 Savings Account on Jupiter powered by Federal Bank helps you manage your money better with faster payments, smart saving tools, and investment insights—all in one place.',
193
+ 'category': 'Account'
194
+ },
195
+ {
196
+ 'question': 'How do I open a Jupiter Savings Account?',
197
+ 'answer': 'You can open your Jupiter digital account by following a few simple steps: 1. Install the Jupiter app 2. Tap "Open an all-in-1 Savings Account" while selecting a Jupiter experience 3. Complete your video KYC',
198
+ 'category': 'Account'
199
+ },
200
+ # Add more FAQs here from your data...
201
+ # (Include all the FAQs you provided)
202
+ ]
203
+
204
+ def _clean_text(self, text: str) -> str:
205
+ """Clean text"""
206
+ if not text:
207
+ return ""
208
+ text = ' '.join(text.split())
209
+ text = re.sub(r'<[^>]+>', '', text)
210
+ return text.strip()
211
+
212
+ def _get_category_from_url(self, url: str) -> str:
213
+ """Get category from URL"""
214
+ url_lower = url.lower()
215
+ if 'account' in url_lower:
216
+ return 'Account'
217
+ elif 'payment' in url_lower or 'upi' in url_lower:
218
+ return 'Payments'
219
+ elif 'card' in url_lower:
220
+ return 'Cards'
221
+ elif 'loan' in url_lower:
222
+ return 'Loans'
223
+ elif 'invest' in url_lower or 'mutual' in url_lower:
224
+ return 'Investments'
225
+ return 'General'
226
+
227
+ def run_complete_scraping(self) -> pd.DataFrame:
228
+ """Main method to run scraping with all fallbacks"""
229
+ logger.info("Starting Jupiter FAQ scraping process...")
230
+
231
+ # Try scraping with fallback
232
+ df = self.scrape_with_fallback()
233
+
234
+ if df.empty:
235
+ logger.error("No FAQ data could be obtained!")
236
+ else:
237
+ logger.info(f"Total FAQs collected: {len(df)}")
238
+
239
+ # Save to CSV
240
+ self.save_to_csv(df)
241
+
242
+ return df
243
+
244
+ def save_to_csv(self, df: pd.DataFrame, filename: str = "data/faqs.csv"):
245
+ """Save FAQs to CSV"""
246
+ import os
247
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
248
+
249
+ if not df.empty:
250
+ df = df[['question', 'answer', 'category']]
251
+ df.to_csv(filename, index=False)
252
+ logger.info(f"Saved {len(df)} FAQs to {filename}")
253
+
254
+
255
+ class FAQUpdater:
256
+ """Manages FAQ updates with reliability checks"""
257
+
258
+ def __init__(self):
259
+ self.scraper = JupiterFAQScraper()
260
+ self.faq_file = "data/faqs.csv"
261
+
262
+ def check_and_update(self, force_update: bool = False) -> pd.DataFrame:
263
+ """Check and update FAQs with verification"""
264
+ import os
265
+ from datetime import datetime, timedelta
266
+
267
+ # First, check if we have existing FAQ data
268
+ if os.path.exists(self.faq_file) and not force_update:
269
+ # Load existing data
270
+ existing_df = pd.read_csv(self.faq_file)
271
+
272
+ # Check file age
273
+ file_time = datetime.fromtimestamp(os.path.getmtime(self.faq_file))
274
+ if datetime.now() - file_time < timedelta(days=7):
275
+ logger.info(f"FAQ data is recent (updated {file_time.strftime('%Y-%m-%d')})")
276
+ return existing_df
277
+
278
+ # Try to update
279
+ logger.info("Attempting to update FAQ data...")
280
+
281
+ # Test if scraping works
282
+ test_results = self.scraper.test_scraping()
283
+
284
+ if not test_results.get('website_accessible'):
285
+ logger.warning("Cannot access Jupiter website. Using existing/fallback data.")
286
+ if os.path.exists(self.faq_file):
287
+ return pd.read_csv(self.faq_file)
288
+ else:
289
+ # Use fallback data
290
+ fallback_faqs = self.scraper.get_fallback_faqs()
291
+ df = pd.DataFrame(fallback_faqs)
292
+ self.scraper.save_to_csv(df)
293
+ return df
294
+
295
+ # Try scraping
296
+ new_df = self.scraper.run_complete_scraping()
297
+
298
+ # Verify the scraped data
299
+ if self.verify_scraped_data(new_df):
300
+ # Backup old file if exists
301
+ if os.path.exists(self.faq_file):
302
+ backup_name = f"data/faqs_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
303
+ os.rename(self.faq_file, backup_name)
304
+ logger.info(f"Backed up old FAQs to {backup_name}")
305
+
306
+ return new_df
307
+ else:
308
+ logger.warning("Scraped data failed verification. Using existing/fallback data.")
309
+ if os.path.exists(self.faq_file):
310
+ return pd.read_csv(self.faq_file)
311
+ else:
312
+ # Use comprehensive fallback
313
+ return self.create_comprehensive_fallback()
314
+
315
+ def verify_scraped_data(self, df: pd.DataFrame) -> bool:
316
+ """Verify if scraped data is valid"""
317
+ if df.empty:
318
+ return False
319
+
320
+ # Check minimum requirements
321
+ if len(df) < 20: # Expecting at least 20 FAQs
322
+ logger.warning(f"Only {len(df)} FAQs scraped, seems too low")
323
+ return False
324
+
325
+ # Check if we have multiple categories
326
+ if df['category'].nunique() < 3:
327
+ logger.warning("Not enough FAQ categories")
328
+ return False
329
+
330
+ # Check answer quality
331
+ avg_answer_length = df['answer'].str.len().mean()
332
+ if avg_answer_length < 50:
333
+ logger.warning("Answers seem too short")
334
+ return False
335
+
336
+ return True
337
+
338
+ def create_comprehensive_fallback(self) -> pd.DataFrame:
339
+ """Create comprehensive fallback FAQ data"""
340
+ # This includes ALL the FAQs you provided
341
+ fallback_data = [
342
+ # Account FAQs
343
+ {"question": "What is the Jupiter All-in-1 Savings Account?", "answer": "The All-in-1 Savings Account on Jupiter powered by Federal Bank helps you manage your money better with faster payments, smart saving tools, and investment insights—all in one place.", "category": "Account"},
344
+ {"question": "How do I open a Jupiter Savings Account?", "answer": "You can open your Jupiter digital account by following a few simple steps: 1. Install the Jupiter app 2. Tap 'Open an all-in-1 Savings Account' while selecting a Jupiter experience 3. Complete your video KYC", "category": "Account"},
345
+ {"question": "Do I earn Jewels for making payments?", "answer": "Yes! You earn up to 1% cashback as Jewels on: • UPI payments • Debit Card spends (online & offline) • Investments in Digital Gold", "category": "Rewards"},
346
+ {"question": "Can I use my Jupiter Debit Card outside India?", "answer": "Absolutely. You can spend in over 120 countries with 0% forex fee on international transactions using your Jupiter Debit Card.", "category": "Card"},
347
+ {"question": "Do I earn Jewels on International payments?", "answer": "Yes, you also earn up to 1% cashback on online and offline international spends.", "category": "Rewards"},
348
+ {"question": "What payment modes are available with the Jupiter account?", "answer": "You can make superfast payments with UPI, IMPS, and debit card—whether it's for recharges, bills, or merchant transactions.", "category": "Payments"},
349
+ {"question": "Can I invest using my Jupiter account?", "answer": "Yes! You can invest in Mutual Funds and Digital Gold with up to 1.5% extra returns on curated mutual fund plans.", "category": "Investments"},
350
+ {"question": "What additional benefits do I get with the Savings Account?", "answer": "You earn up to 1% cashback as Jewels on: • Free cheque book • Free IMPS transfers • ATM withdrawals", "category": "Account"},
351
+ # Include ALL other FAQs from your data here...
352
+ ]
353
+
354
+ df = pd.DataFrame(fallback_data)
355
+ self.scraper.save_to_csv(df)
356
+ return df
357
+
358
+ def get_scraping_stats(self, df: pd.DataFrame) -> Dict:
359
+ """Get statistics about FAQ data"""
360
+ return {
361
+ 'total_faqs': len(df),
362
+ 'categories': df['category'].nunique(),
363
+ 'category_distribution': df['category'].value_counts().to_dict(),
364
+ 'avg_question_length': df['question'].str.len().mean(),
365
+ 'avg_answer_length': df['answer'].str.len().mean(),
366
+ 'data_source': 'scraped' if len(df) > 50 else 'fallback'
367
+ }
368
+
369
+
370
+ # Create a simple test script
371
+ def test_scraper():
372
+ """Test if the scraper can actually get data"""
373
+ print("Testing Jupiter FAQ Scraper...")
374
+ print("-" * 50)
375
+
376
+ scraper = JupiterFAQScraper()
377
+
378
+ # Test 1: Website accessibility
379
+ test_results = scraper.test_scraping()
380
+ print(f"Website accessible: {test_results.get('website_accessible', False)}")
381
+ print(f"FAQ content found: {test_results.get('faq_content_found', False)}")
382
+
383
+ # Test 2: Try scraping one page
384
+ print("\nTesting page scraping...")
385
+ test_url = "https://jupiter.money/savings-account/"
386
+ faqs = scraper.scrape_page_safe(test_url)
387
+ print(f"FAQs found on {test_url}: {len(faqs)}")
388
+
389
+ if faqs:
390
+ print("\nSample FAQ:")
391
+ print(f"Q: {faqs[0]['question'][:100]}...")
392
+ print(f"A: {faqs[0]['answer'][:100]}...")
393
+
394
+ # Test 3: Full scraping
395
+ print("\nRunning full scraping process...")
396
+ df = scraper.run_complete_scraping()
397
+ print(f"Total FAQs collected: {len(df)}")
398
+
399
+ if not df.empty:
400
+ print(f"Categories: {df['category'].unique()}")
401
+ print(f"Data saved to: data/faqs.csv")
402
+
403
+ return df
404
+
405
+
406
+ if __name__ == "__main__":
407
+ # Run the test
408
+ test_scraper()
app/setup_scraping.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # setup_scraping.py
2
+ import subprocess
3
+ import sys
4
+
5
+ def setup_selenium_driver():
6
+ """Install Chrome driver for Selenium"""
7
+ try:
8
+ from webdriver_manager.chrome import ChromeDriverManager
9
+ from selenium import webdriver
10
+ from selenium.webdriver.chrome.service import Service
11
+
12
+ # This will download the driver if needed
13
+ service = Service(ChromeDriverManager().install())
14
+ print("Chrome driver installed successfully!")
15
+ except Exception as e:
16
+ print(f"Error setting up Chrome driver: {e}")
17
+ print("You may need to install Chrome/Chromium browser")
18
+
19
+ def main():
20
+ print("Setting up scraping environment...")
21
+
22
+ # Install requirements
23
+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'selenium', 'webdriver-manager'])
24
+
25
+ # Setup driver
26
+ setup_selenium_driver()
27
+
28
+ print("Setup complete! You can now run the scraper.")
29
+
30
+ if __name__ == "__main__":
31
+ main()
app/static/css/style.css ADDED
@@ -0,0 +1,571 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* app/static/css/style.css */
2
+
3
+ :root {
4
+ --primary-color: #2962ff;
5
+ --primary-light: #768fff;
6
+ --primary-dark: #0039cb;
7
+ --secondary-color: #fd7e14;
8
+ --background-color: #f5f7fb;
9
+ --chat-bg: #ffffff;
10
+ --text-primary: #2c3e50;
11
+ --text-secondary: #34495e;
12
+ --shadow-color: rgba(0, 0, 0, 0.1);
13
+ --gradient-1: linear-gradient(135deg, #6B73FF 0%, #000DFF 100%);
14
+ --gradient-2: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
15
+ --gradient-3: linear-gradient(45deg, #2962ff, #768fff);
16
+ --error-color: #dc3545;
17
+ --success-color: #28a745;
18
+ }
19
+
20
+ * {
21
+ margin: 0;
22
+ padding: 0;
23
+ box-sizing: border-box;
24
+ font-family: 'Inter', sans-serif;
25
+ }
26
+
27
+ body {
28
+ background-color: var(--background-color);
29
+ height: 100vh;
30
+ overflow: hidden;
31
+ }
32
+
33
+ /* Main Container */
34
+ .chat-container {
35
+ height: 100vh;
36
+ display: flex;
37
+ flex-direction: column;
38
+ }
39
+
40
+ /* Header Styling */
41
+ .chat-header {
42
+ background: var(--gradient-1);
43
+ padding: 15px 20px;
44
+ color: white;
45
+ box-shadow: 0 2px 10px var(--shadow-color);
46
+ z-index: 1000;
47
+ }
48
+
49
+ .header-content {
50
+ display: flex;
51
+ align-items: center;
52
+ gap: 12px;
53
+ }
54
+
55
+ .logo {
56
+ width: 32px;
57
+ height: 32px;
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ }
62
+
63
+ .header-content h1 {
64
+ font-size: 1.2rem;
65
+ font-weight: 600;
66
+ }
67
+
68
+ /* Main Content Layout */
69
+ .main-content {
70
+ display: flex;
71
+ height: calc(100vh - 60px);
72
+ overflow: hidden;
73
+ }
74
+
75
+ /* Categories Sidebar */
76
+ .categories-sidebar {
77
+ width: 300px;
78
+ background: white;
79
+ padding: 20px;
80
+ overflow-y: auto;
81
+ border-right: 1px solid rgba(0,0,0,0.1);
82
+ }
83
+
84
+ .categories-sidebar h2 {
85
+ font-size: 1.1rem;
86
+ color: var(--text-primary);
87
+ margin-bottom: 20px;
88
+ padding-bottom: 10px;
89
+ border-bottom: 2px solid var(--primary-light);
90
+ }
91
+
92
+ .categories-accordion {
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: 12px;
96
+ }
97
+
98
+ .category-item {
99
+ background: white;
100
+ border-radius: 12px;
101
+ overflow: hidden;
102
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
103
+ transition: all 0.3s ease;
104
+ }
105
+
106
+ .category-header {
107
+ padding: 15px;
108
+ display: flex;
109
+ justify-content: space-between;
110
+ align-items: center;
111
+ cursor: pointer;
112
+ background: linear-gradient(145deg, #ffffff, #f8f9fa);
113
+ transition: all 0.3s ease;
114
+ }
115
+
116
+ .category-header:hover {
117
+ background: linear-gradient(145deg, #f8f9fa, #e9ecef);
118
+ }
119
+
120
+ .category-header span {
121
+ font-weight: 500;
122
+ color: var(--text-primary);
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 8px;
126
+ }
127
+
128
+ .arrow-icon {
129
+ width: 20px;
130
+ height: 20px;
131
+ fill: var(--text-primary);
132
+ transition: transform 0.3s ease;
133
+ }
134
+
135
+ .category-item.active .arrow-icon {
136
+ transform: rotate(180deg);
137
+ }
138
+
139
+ .category-content {
140
+ display: none;
141
+ padding: 12px;
142
+ background: var(--background-color);
143
+ }
144
+
145
+ .category-item.active .category-content {
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: 8px;
149
+ }
150
+
151
+ .category-content button {
152
+ background: white;
153
+ border: none;
154
+ padding: 12px 16px;
155
+ text-align: left;
156
+ border-radius: 8px;
157
+ cursor: pointer;
158
+ transition: all 0.3s ease;
159
+ color: var(--text-primary);
160
+ font-size: 0.9rem;
161
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
162
+ position: relative;
163
+ overflow: hidden;
164
+ z-index: 0;
165
+ }
166
+
167
+ .category-content button::before {
168
+ content: '';
169
+ position: absolute;
170
+ left: 0;
171
+ top: 0;
172
+ height: 100%;
173
+ width: 100%;
174
+ background: var(--primary-light); /* subtle hover background */
175
+ opacity: 0;
176
+ transition: opacity 0.3s ease;
177
+ z-index: 0;
178
+ border-radius: 8px;
179
+ }
180
+
181
+ .category-content button span {
182
+ position: relative;
183
+ z-index: 1;
184
+ transition: color 0.3s ease;
185
+ color: var(--text-primary);
186
+ }
187
+
188
+ .category-content button:hover::before {
189
+ opacity: 1;
190
+ }
191
+
192
+ .category-content button:hover span {
193
+ color: white;
194
+ }
195
+
196
+
197
+ /* Chat Section */
198
+ .chat-section {
199
+ flex: 1;
200
+ display: flex;
201
+ flex-direction: column;
202
+ background: var(--background-color);
203
+ }
204
+
205
+ .chat-messages {
206
+ flex: 1;
207
+ padding: 20px;
208
+ overflow-y: auto;
209
+ display: flex;
210
+ flex-direction: column;
211
+ gap: 15px;
212
+ }
213
+
214
+ /* Message Styling */
215
+ .message {
216
+ max-width: 80%;
217
+ animation: fadeIn 0.3s ease;
218
+ }
219
+
220
+ .message.user {
221
+ margin-left: auto;
222
+ }
223
+
224
+ .message-content {
225
+ padding: 15px;
226
+ border-radius: 12px;
227
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
228
+ position: relative;
229
+ }
230
+
231
+ .message.system .message-content {
232
+ background: white;
233
+ color: var(--text-primary);
234
+ text-shadow: 0 1px 1px rgba(0,0,0,0.03);
235
+ border-radius: 12px 12px 12px 0;
236
+ }
237
+
238
+ .message.user .message-content {
239
+ background: var(--gradient-2);
240
+ color: white;
241
+ border-radius: 12px 12px 0 12px;
242
+ }
243
+
244
+ /* Welcome Message */
245
+ .welcome-message {
246
+ padding: 15px;
247
+ background: white;
248
+ color: var(--text-primary);
249
+ border-radius: 12px;
250
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
251
+ }
252
+
253
+ .welcome-message h3 {
254
+ color: var(--primary-color);
255
+ margin-bottom: 10px;
256
+ font-size: 1.2rem;
257
+ }
258
+
259
+ /* Suggestions Styling */
260
+ .suggestions {
261
+ margin: 15px 0;
262
+ padding: 15px;
263
+ background: white;
264
+ color: var(--text-primary);
265
+ border-radius: 12px;
266
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
267
+ }
268
+
269
+ .suggestions p {
270
+ color: var(--text-secondary);
271
+ font-size: 0.9rem;
272
+ margin-bottom: 12px;
273
+ }
274
+
275
+ .suggestion-buttons {
276
+ display: flex;
277
+ flex-wrap: wrap;
278
+ gap: 8px;
279
+ }
280
+
281
+ .suggestion-btn {
282
+ background: var(--background-color);
283
+ border: none;
284
+ padding: 8px 16px;
285
+ border-radius: 20px;
286
+ cursor: pointer;
287
+ transition: all 0.3s ease;
288
+ font-size: 0.9rem;
289
+ color: var(--primary-color);
290
+ position: relative;
291
+ overflow: hidden;
292
+ }
293
+
294
+ .suggestion-btn::before {
295
+ content: '';
296
+ position: absolute;
297
+ top: 0;
298
+ left: 0;
299
+ width: 100%;
300
+ height: 100%;
301
+ background: var(--gradient-3);
302
+ opacity: 0;
303
+ transition: opacity 0.3s ease;
304
+ }
305
+
306
+ .suggestion-btn span {
307
+ position: relative;
308
+ z-index: 1;
309
+ }
310
+
311
+ .suggestion-btn:hover {
312
+ color: white;
313
+ transform: translateY(-2px);
314
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
315
+ }
316
+
317
+ .suggestion-btn:hover::before {
318
+ opacity: 1;
319
+ }
320
+
321
+ .chat-input-container {
322
+ background-color: #121212; /* very dark gray (softer than pure black) */
323
+ border-top: 1px solid #222222; /* dark border */
324
+ color: white;
325
+ }
326
+
327
+ .chat-input-form {
328
+ display: flex;
329
+ gap: 12px;
330
+ background: #1e1e1e; /* dark gray background */
331
+ padding: 5px;
332
+ border-radius: 30px;
333
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.7); /* stronger dark shadow */
334
+ }
335
+
336
+ .chat-input-form input {
337
+ flex: 1;
338
+ padding: 12px 20px; /* keep original padding */
339
+ border: 2px solid #444444; /* dark border */
340
+ border-radius: 25px;
341
+ font-size: 0.95rem;
342
+ background-color: #121212; /* dark input background */
343
+ color: white;
344
+ transition: all 0.3s ease;
345
+ }
346
+
347
+ .chat-input-form input::placeholder {
348
+ color: #888888; /* lighter placeholder */
349
+ }
350
+
351
+ .chat-input-form input:focus {
352
+ border-color: var(--primary-color);
353
+ outline: none;
354
+ box-shadow: 0 0 0 3px rgba(41, 98, 255, 0.5); /* stronger focus glow */
355
+ }
356
+
357
+ .chat-input-form button {
358
+ background: var(--gradient-1);
359
+ color: white;
360
+ border: none;
361
+ width: 46px;
362
+ height: 46px;
363
+ border-radius: 50%;
364
+ cursor: pointer;
365
+ display: flex;
366
+ align-items: center;
367
+ justify-content: center;
368
+ transition: all 0.3s ease;
369
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
370
+ }
371
+
372
+ .chat-input-form button:hover {
373
+ transform: scale(1.05);
374
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
375
+ }
376
+
377
+ /* Loading Animation */
378
+ .typing-indicator {
379
+ display: flex;
380
+ gap: 8px;
381
+ padding: 10px;
382
+ }
383
+
384
+ .typing-indicator span {
385
+ width: 8px;
386
+ height: 8px;
387
+ background: var(--primary-light);
388
+ border-radius: 50%;
389
+ animation: bounce 1s infinite;
390
+ }
391
+
392
+ .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
393
+ .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
394
+
395
+ @keyframes bounce {
396
+ 0%, 100% { transform: translateY(0); }
397
+ 50% { transform: translateY(-8px); }
398
+ }
399
+
400
+ /* List Styling */
401
+ .response-list {
402
+ margin-top: 10px;
403
+ list-style: none;
404
+ }
405
+
406
+ .response-list li {
407
+ margin: 8px 0;
408
+ padding: 10px 15px;
409
+ background: rgba(0,0,0,0.03);
410
+ border-radius: 8px;
411
+ position: relative;
412
+ padding-left: 25px;
413
+ }
414
+
415
+ .response-list li::before {
416
+ content: '•';
417
+ position: absolute;
418
+ left: 10px;
419
+ color: var(--primary-color);
420
+ }
421
+
422
+ /* Scrollbar Styling */
423
+ ::-webkit-scrollbar {
424
+ width: 6px;
425
+ }
426
+
427
+ ::-webkit-scrollbar-track {
428
+ background: transparent;
429
+ }
430
+
431
+ ::-webkit-scrollbar-thumb {
432
+ background: rgba(0,0,0,0.2);
433
+ border-radius: 3px;
434
+ }
435
+
436
+ ::-webkit-scrollbar-thumb:hover {
437
+ background: rgba(0,0,0,0.3);
438
+ }
439
+
440
+ /* Animations */
441
+ @keyframes fadeIn {
442
+ from {
443
+ opacity: 0;
444
+ transform: translateY(10px);
445
+ }
446
+ to {
447
+ opacity: 1;
448
+ transform: translateY(0);
449
+ }
450
+ }
451
+
452
+ /* Error State */
453
+ .error {
454
+ color: var(--error-color);
455
+ padding: 10px;
456
+ border-radius: 8px;
457
+ background: rgba(220, 53, 69, 0.1);
458
+ }
459
+
460
+ /* Responsive Design */
461
+ @media (max-width: 768px) {
462
+ .main-content {
463
+ flex-direction: column;
464
+ }
465
+
466
+ .categories-sidebar {
467
+ width: 100%;
468
+ max-height: 40vh;
469
+ }
470
+
471
+ .chat-section {
472
+ height: 60vh;
473
+ }
474
+
475
+ .message {
476
+ max-width: 90%;
477
+ }
478
+
479
+ .chat-input-form input {
480
+ padding: 10px 15px;
481
+ }
482
+
483
+ .chat-input-form button {
484
+ width: 40px;
485
+ height: 40px;
486
+ }
487
+
488
+ .suggestion-buttons {
489
+ flex-direction: column;
490
+ }
491
+
492
+ .suggestion-btn {
493
+ width: 100%;
494
+ }
495
+ }
496
+
497
+ /* Dark Mode Support */
498
+ @media (prefers-color-scheme: dark) {
499
+ :root {
500
+ --background-color: #1a1a1a;
501
+ --chat-bg: #2d2d2d;
502
+ --text-primary: #ffffff;
503
+ --text-secondary: #cccccc;
504
+ }
505
+
506
+ .categories-sidebar,
507
+ .category-content button,
508
+ .message.system .message-content,
509
+ .suggestions,
510
+ .welcome-message {
511
+ background: #2d2d2d;
512
+ color: var(--text-primary);
513
+ }
514
+
515
+ .category-header {
516
+ background: #2b2b2b;
517
+ }
518
+
519
+ .chat-input-form input {
520
+ background: #2d2d2d;
521
+ border-color: #3d3d3d;
522
+ color: white;
523
+ }
524
+
525
+ /* CATEGORY SECTION COLOR UPDATES */
526
+ .category-item {
527
+ background: #232323; /* slightly darker for distinction */
528
+ box-shadow: 0 2px 8px rgba(255, 255, 255, 0.05);
529
+ }
530
+
531
+ .category-header {
532
+ background: linear-gradient(145deg, #2b2b2b, #222222);
533
+ color: var(--text-primary);
534
+ }
535
+
536
+ .category-header:hover {
537
+ background: linear-gradient(145deg, #222222, #2b2b2b);
538
+ }
539
+
540
+ .category-header span {
541
+ color: var(--text-primary);
542
+ }
543
+
544
+ .arrow-icon {
545
+ fill: var(--text-secondary);
546
+ }
547
+
548
+ .category-content {
549
+ background: #1f1f1f;
550
+ }
551
+
552
+ .category-content button {
553
+ background: #2d2d2d;
554
+ color: var(--text-primary);
555
+ box-shadow: 0 2px 4px rgba(255, 255, 255, 0.03);
556
+ }
557
+
558
+ .category-content button::before {
559
+ background: rgba(118, 143, 255, 0.15); /* subtle blue hover */
560
+ }
561
+
562
+ .category-content button:hover span {
563
+ color: white;
564
+ }
565
+
566
+ .logo {
567
+ color: #ff4081;
568
+ fill: #ff4081;
569
+ }
570
+ }
571
+
app/static/img/jupiter-logo.png ADDED
app/templates/base.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Jupiter FAQ Bot</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
8
+ <!-- Add Google Font -->
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
10
+ </head>
11
+ <body>
12
+ {% block content %}{% endblock %}
13
+ </body>
14
+ </html>
app/templates/index.html ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <div class="chat-container">
4
+ <!-- Header -->
5
+ <div class="chat-header">
6
+ <div class="header-content">
7
+ <div class="logo">
8
+ <svg width="32" height="32" viewBox="0 0 40 40">
9
+ <circle cx="20" cy="20" r="18" fill="#ff7f50"/>
10
+ <text x="15" y="28" fill="white" font-size="20">J</text>
11
+ </svg>
12
+ </div>
13
+ <h1>Jupiter AI Assistant</h1>
14
+ </div>
15
+ </div>
16
+
17
+ <!-- Main Content -->
18
+ <div class="main-content">
19
+ <!-- Updated Categories Sidebar with Questions from Provided Data Only -->
20
+ <div class="categories-sidebar">
21
+ <h2>📚 Quick Topics</h2>
22
+ <div class="categories-accordion">
23
+ <!-- Account & KYC -->
24
+ <div class="category-item">
25
+ <div class="category-header" onclick="toggleCategory(this)">
26
+ <span>🏦 Account & KYC</span>
27
+ <svg class="arrow-icon" viewBox="0 0 24 24">
28
+ <path d="M7 10l5 5 5-5z"/>
29
+ </svg>
30
+ </div>
31
+ <div class="category-content">
32
+ <button onclick="askQuestion('What is the Jupiter All-in-1 Savings Account?')">All-in-1 Savings Account</button>
33
+ <button onclick="askQuestion('How do I open a Jupiter Savings Account?')">Open Jupiter Account</button>
34
+ <button onclick="askQuestion('What additional benefits do I get with the Savings Account?')">Savings Account Benefits</button>
35
+ <button onclick="askQuestion('How do I open a Salary account?')">Open Salary Account</button>
36
+ <button onclick="askQuestion('Which documents are required for opening a Salary account?')">Documents for Salary Account</button>
37
+ <button onclick="askQuestion('What is the initial deposit when you open a Salary Account?')">Initial Deposit Required</button>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Payments & UPI -->
42
+ <div class="category-item">
43
+ <div class="category-header" onclick="toggleCategory(this)">
44
+ <span>💸 Payments & UPI</span>
45
+ <svg class="arrow-icon" viewBox="0 0 24 24">
46
+ <path d="M7 10l5 5 5-5z"/>
47
+ </svg>
48
+ </div>
49
+ <div class="category-content">
50
+ <button onclick="askQuestion('What payment methods does Jupiter support?')">Payment Methods</button>
51
+ <button onclick="askQuestion('Is there a transaction limit for UPI payments?')">UPI Transaction Limits</button>
52
+ <button onclick="askQuestion('Are there any charges for UPI transactions?')">UPI Transaction Charges</button>
53
+ <button onclick="askQuestion('What is UPI Lite and how does it work?')">UPI Lite Feature</button>
54
+ <button onclick="askQuestion('Can I send money to contacts using other UPI apps?')">Send to Other UPI Apps</button>
55
+ <button onclick="askQuestion('What types of bills can I pay on Jupiter?')">Bill Payment Types</button>
56
+ </div>
57
+ </div>
58
+
59
+ <!-- Cards -->
60
+ <div class="category-item">
61
+ <div class="category-header" onclick="toggleCategory(this)">
62
+ <span>💳 Cards</span>
63
+ <svg class="arrow-icon" viewBox="0 0 24 24">
64
+ <path d="M7 10l5 5 5-5z"/>
65
+ </svg>
66
+ </div>
67
+ <div class="category-content">
68
+ <button onclick="askQuestion('How can I apply for the Edge+ CSB Bank RuPay Credit Card?')">Apply for Edge+ Card</button>
69
+ <button onclick="askQuestion('What is the maximum cashback I can earn for each category?')">Maximum Cashback Limits</button>
70
+ <button onclick="askQuestion('Are there any fees for the Edge+ card?')">Edge+ Card Fees</button>
71
+ <button onclick="askQuestion('Can I use my Jupiter Debit Card outside India?')">International Debit Card Usage</button>
72
+ <button onclick="askQuestion('How to activate Edge Federal Bank Visa Credit Card?')">Activate Visa Card</button>
73
+ <button onclick="askQuestion('Is Edge Federal Bank Visa Credit Card lifetime free?')">Lifetime Free Card</button>
74
+ </div>
75
+ </div>
76
+
77
+ <!-- Rewards & Jewels -->
78
+ <div class="category-item">
79
+ <div class="category-header" onclick="toggleCategory(this)">
80
+ <span>💎 Rewards & Jewels</span>
81
+ <svg class="arrow-icon" viewBox="0 0 24 24">
82
+ <path d="M7 10l5 5 5-5z"/>
83
+ </svg>
84
+ </div>
85
+ <div class="category-content">
86
+ <button onclick="askQuestion('Do I earn Jewels for making payments?')">Earn Jewels on Payments</button>
87
+ <button onclick="askQuestion('How do I earn Jewels on Jupiter?')">How to Earn Jewels</button>
88
+ <button onclick="askQuestion('What can I redeem Jewels for?')">Redeem Jewels Options</button>
89
+ <button onclick="askQuestion('How fast can I redeem Jewels on Jupiter?')">Instant Jewels Redemption</button>
90
+ <button onclick="askQuestion('Do the earned Jewels expire?')">Jewels Expiry Policy</button>
91
+ <button onclick="askQuestion('Do I earn Jewels on International payments?')">International Payment Rewards</button>
92
+ </div>
93
+ </div>
94
+
95
+ <!-- Investments -->
96
+ <div class="category-item">
97
+ <div class="category-header" onclick="toggleCategory(this)">
98
+ <span>📈 Investments</span>
99
+ <svg class="arrow-icon" viewBox="0 0 24 24">
100
+ <path d="M7 10l5 5 5-5z"/>
101
+ </svg>
102
+ </div>
103
+ <div class="category-content">
104
+ <button onclick="askQuestion('What investment options are available on Jupiter?')">Investment Options</button>
105
+ <button onclick="askQuestion('What is Magic Spends and how does it work?')">Magic Spends Feature</button>
106
+ <button onclick="askQuestion('Can I invest using my Jupiter account?')">Invest with Jupiter</button>
107
+ <button onclick="askQuestion('What is Digital Gold on Jupiter?')">Digital Gold Investment</button>
108
+ <button onclick="askQuestion('Can I start investing in Gold with just ₹10?')">₹10 Gold Investment</button>
109
+ <button onclick="askQuestion('How do No-Penalty SIPs work on Jupiter?')">No-Penalty SIPs</button>
110
+ </div>
111
+ </div>
112
+
113
+ <!-- Loans -->
114
+ <div class="category-item">
115
+ <div class="category-header" onclick="toggleCategory(this)">
116
+ <span>💰 Loans</span>
117
+ <svg class="arrow-icon" viewBox="0 0 24 24">
118
+ <path d="M7 10l5 5 5-5z"/>
119
+ </svg>
120
+ </div>
121
+ <div class="category-content">
122
+ <button onclick="askQuestion('What types of loans can I get on Jupiter?')">Available Loan Types</button>
123
+ <button onclick="askQuestion('What are the interest rates applicable on loans with Jupiter?')">Loan Interest Rates</button>
124
+ <button onclick="askQuestion('What is a loan against mutual funds?')">Loan Against Mutual Funds</button>
125
+ <button onclick="askQuestion('Who can apply for a Personal Loan on Jupiter?')">Personal Loan Eligibility</button>
126
+ <button onclick="askQuestion('What is Mini-Loan?')">About Mini-Loan</button>
127
+ <button onclick="askQuestion('Will I get reminders for my EMI payments?')">EMI Payment Reminders</button>
128
+ </div>
129
+ </div>
130
+
131
+ <!-- Money Management -->
132
+ <div class="category-item">
133
+ <div class="category-header" onclick="toggleCategory(this)">
134
+ <span>📊 Money Management</span>
135
+ <svg class="arrow-icon" viewBox="0 0 24 24">
136
+ <path d="M7 10l5 5 5-5z"/>
137
+ </svg>
138
+ </div>
139
+ <div class="category-content">
140
+ <button onclick="askQuestion('What can I do in the Money tab on Jupiter?')">Money Tab Features</button>
141
+ <button onclick="askQuestion('How does Jupiter categorize my spending?')">Spending Categories</button>
142
+ <button onclick="askQuestion('Can I set Budgets in the Money tab?')">Set Monthly Budgets</button>
143
+ <button onclick="askQuestion('What is Account Aggregator and how does it work?')">Account Aggregator</button>
144
+ <button onclick="askQuestion('What is Net Worth in Jupiter and how is it calculated?')">Net Worth Tracker</button>
145
+ <button onclick="askQuestion('Can I track my credit card expenses in the Money tab?')">Track Credit Card Expenses</button>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </div>
150
+
151
+ <!-- Chat Section -->
152
+ <div class="chat-section">
153
+ <div class="chat-messages" id="chat-messages">
154
+ <div class="message system">
155
+ <div class="message-content">
156
+ <div class="welcome-message">
157
+ <h3>👋 Welcome to Jupiter!</h3>
158
+ <p>I'm here to help you with any questions about Jupiter's services. Choose a category from the sidebar or ask me anything!</p>
159
+ </div>
160
+ </div>
161
+ </div>
162
+ </div>
163
+
164
+ <div class="chat-input-container">
165
+ <form class="chat-input-form" onsubmit="sendMessage(event)">
166
+ <input type="text" id="user-input" placeholder="Ask me anything about Jupiter..." required>
167
+ <button type="submit">
168
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
169
+ <path d="M22 2L11 13"></path>
170
+ <path d="M22 2L15 22L11 13L2 9L22 2Z"></path>
171
+ </svg>
172
+ </button>
173
+ </form>
174
+ </div>
175
+ </div>
176
+ </div>
177
+ </div>
178
+
179
+ <script>
180
+ function toggleCategory(header) {
181
+ const categoryItem = header.parentElement;
182
+ const wasActive = categoryItem.classList.contains('active');
183
+
184
+ // Close all categories
185
+ document.querySelectorAll('.category-item').forEach(item => {
186
+ item.classList.remove('active');
187
+ });
188
+
189
+ // Open clicked category if it wasn't active
190
+ if (!wasActive) {
191
+ categoryItem.classList.add('active');
192
+ }
193
+ }
194
+
195
+ function formatMessage(text) {
196
+ if (text.includes("•")) {
197
+ const parts = text.split("•");
198
+ let formattedText = parts[0];
199
+ if (parts.length > 1) {
200
+ formattedText += '<ul class="response-list">';
201
+ for (let i = 1; i < parts.length; i++) {
202
+ if (parts[i].trim()) {
203
+ formattedText += `<li>${parts[i].trim()}</li>`;
204
+ }
205
+ }
206
+ formattedText += '</ul>';
207
+ }
208
+ return formattedText;
209
+ }
210
+ return text;
211
+ }
212
+
213
+ function addSuggestions(suggestions) {
214
+ if (!suggestions || suggestions.length === 0) return;
215
+
216
+ const suggestionsDiv = document.createElement('div');
217
+ suggestionsDiv.className = 'suggestions';
218
+ suggestionsDiv.innerHTML = '<p>You might also want to know:</p>';
219
+
220
+ const buttonsContainer = document.createElement('div');
221
+ buttonsContainer.className = 'suggestion-buttons';
222
+
223
+ suggestions.forEach(suggestion => {
224
+ const btn = document.createElement('button');
225
+ btn.className = 'suggestion-btn';
226
+ btn.innerHTML = `<span>${suggestion}</span>`;
227
+ btn.onclick = () => askQuestion(suggestion);
228
+ buttonsContainer.appendChild(btn);
229
+ });
230
+
231
+ suggestionsDiv.appendChild(buttonsContainer);
232
+ document.getElementById('chat-messages').appendChild(suggestionsDiv);
233
+ }
234
+
235
+ function askQuestion(question) {
236
+ document.getElementById('user-input').value = question;
237
+ document.querySelector('.chat-input-form').dispatchEvent(new Event('submit'));
238
+ }
239
+
240
+ function sendMessage(event) {
241
+ event.preventDefault();
242
+
243
+ const input = document.getElementById('user-input');
244
+ const messages = document.getElementById('chat-messages');
245
+
246
+ // Add user message
247
+ const userMessage = document.createElement('div');
248
+ userMessage.className = 'message user';
249
+ userMessage.innerHTML = `<div class="message-content">${input.value}</div>`;
250
+ messages.appendChild(userMessage);
251
+
252
+ // Add loading message
253
+ const loadingMessage = document.createElement('div');
254
+ loadingMessage.className = 'message system loading';
255
+ loadingMessage.innerHTML = `
256
+ <div class="message-content">
257
+ <div class="typing-indicator">
258
+ <span></span><span></span><span></span>
259
+ </div>
260
+ </div>
261
+ `;
262
+ messages.appendChild(loadingMessage);
263
+
264
+ // Scroll to bottom
265
+ messages.scrollTop = messages.scrollHeight;
266
+
267
+ // Send request to server
268
+ fetch('/chat', {
269
+ method: 'POST',
270
+ headers: {
271
+ 'Content-Type': 'application/json',
272
+ },
273
+ body: JSON.stringify({
274
+ question: input.value
275
+ })
276
+ })
277
+ .then(response => response.json())
278
+ .then(data => {
279
+ // Remove loading message
280
+ messages.removeChild(loadingMessage);
281
+
282
+ // Add bot response
283
+ const botMessage = document.createElement('div');
284
+ botMessage.className = 'message system';
285
+ botMessage.innerHTML = `<div class="message-content">${formatMessage(data.response)}</div>`;
286
+ messages.appendChild(botMessage);
287
+
288
+ // Add suggestions if any
289
+ if (data.suggestions) {
290
+ addSuggestions(data.suggestions);
291
+ }
292
+
293
+ // Clear input and scroll
294
+ input.value = '';
295
+ messages.scrollTop = messages.scrollHeight;
296
+ })
297
+ .catch(error => {
298
+ console.error('Error:', error);
299
+ loadingMessage.innerHTML = '<div class="message-content error">Sorry, something went wrong. Please try again.</div>';
300
+ });
301
+ }
302
+ </script>
303
+ {% endblock %}
config.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ class Config:
2
+ SECRET_KEY = 'your-secret-key-here'
3
+ DEBUG = True
data/faq_embeddings.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:667151a932f014e87ec59cfe71b461c678b271ad3924c8a4f32514e64264f7e5
3
+ size 227456
data/faqs.csv ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "question","answer","category"
2
+ "What is the Jupiter All-in-1 Savings Account?","The All-in-1 Savings Account on Jupiter powered by Federal Bank helps you manage your money better with faster payments, smart saving tools, and investment insights—all in one place.",Account
3
+ "How do I open a Jupiter Savings Account?","You can open your Jupiter digital account by following a few simple steps: 1. Install the Jupiter app 2. Tap 'Open an all-in-1 Savings Account' while selecting a Jupiter experience 3. Complete your video KYC",Account
4
+ "Do I earn Jewels for making payments?","Yes! You earn up to 1% cashback as Jewels on: • UPI payments • Debit Card spends (online & offline) • Investments in Digital Gold",Rewards
5
+ "Can I use my Jupiter Debit Card outside India?","Absolutely. You can spend in over 120 countries with 0% forex fee on international transactions using your Jupiter Debit Card.",Card
6
+ "Do I earn Jewels on International payments?","Yes, you also earn up to 1% cashback on online and offline international spends.",Rewards
7
+ "What payment modes are available with the Jupiter account?","You can make superfast payments with UPI, IMPS, and debit card—whether it's for recharges, bills, or merchant transactions.",Payments
8
+ "Can I invest using my Jupiter account?","Yes! You can invest in Mutual Funds and Digital Gold with up to 1.5% extra returns on curated mutual fund plans.",Investments
9
+ "What additional benefits do I get with the Savings Account?","You earn up to 1% cashback as Jewels on: • Free cheque book • Free IMPS transfers • ATM withdrawals",Account
10
+ "How is this Salary account different then?","Our Salary Account can also be opened by an employee and does not necessarily need an employer to open one for you.",Salary Account
11
+ "Why should I switch to Salary account?","It's your Salary Account with extra benefits. Get the most out of your salary. Salary account is an upgraded version of the Basic account. Receive your monthly salary in your new Salary Account to earn maximum rewards on Jupiter, free credit & debit card, Mini Loans starting from 0% interest.",Salary Account
12
+ "How do I open a Salary account?","To open a Salary account, follow these easy steps: 1. Download the Jupiter app and create your account 2. Complete your video verification / full KYC 3. Submit your company's name and verify your work email address 4. Share your new account details with HR and make sure to get your next salary in your new Salary Account within 60 days. That's it! Once your salary is credited, you'll automatically be upgraded to a Salary account.",Salary Account
13
+ "I already have a Savings Account on the Jupiter app. How do I upgrade to a Salary account?","To upgrade to a Salary account, please go to 'Settings' on the Jupiter app and follow 3 simple steps. Currently open to select few.",Salary Account
14
+ "Who all can open a Salary account on Jupiter?","To open a Salary account, please ensure that your in-hand salary is more ₹20,000. However, this is negotiable for teams. Please connect with us to learn more about the Salary account.",Salary Account
15
+ "How much time does it take to open a Salary account?","It takes only 3 minutes to complete all the steps to open a Salary account.",Salary Account
16
+ "Can I change my already existing salary account? How?","Create a new account on the app, submit your new bank account details with your HR to receive further salaries in that account and turn yourself into a Salary Account user",Salary Account
17
+ "Are there any charges for opening a Salary account?","Opening a Salary Account on Jupiter is completely free. There are also no charges for UPI, NEFT, and RTGS transactions.",Salary Account
18
+ "What is Mini-Loan?","By opening a Salary account, you can instantly withdraw your partial salary before salary day with no interest charges.",Salary Account
19
+ "I'm a non-salaried individual. How do I upgrade to Salary account?","Keep an eye out – we'll bring the benefits of Salary account to freelancers & other non-salaried individuals soon.",Salary Account
20
+ "Which documents are required for opening a Salary account?","Your Aadhaar card linked with your phone number and PAN card are required to open a Salary account.",Salary Account
21
+ "How much does the Corporate Salary Account cost?","It's completely free.",Corporate Salary Account
22
+ "What if my company already has salary accounts for employees with another bank?","If you have an existing account with another bank, you can continue using it for business banking. You can still use Jupiter Corporate Salary Account for your employees with discounted medical insurance, 0% interest loans, 0 forex fees, and more. Ultimately, you get the best of both worlds.",Corporate Salary Account
23
+ "Is my employee's money safe?","It is 100% safe. Jupiter Corporate Salary Account is hosted in an RBI-licensed bank (Federal Bank) and the money in the account is insured up to ₹5,00,000.",Corporate Salary Account
24
+ "Does my company need a minimum number of employees?","We support companies of all sizes. From 2-person businesses to organizations that have over 1000 employees.",Corporate Salary Account
25
+ "What is the benefit for my company?","Jupiter Corporate Salary Account helps you save up to ₹50,00,000 yearly by cutting down on HRMS costs, payroll software costs and health insurance costs. You can streamline payout processes with our bulk salary payout portal. Your employees can also get loans at 0% interest, and invest in Mutual Funds and Digital Gold on Jupiter.",Corporate Salary Account
26
+ "Which documents are needed for opening a Salary Account?","Your employees need to have the following to open a Jupiter Corporate Salary Account: Physical PAN card, A smart phone, Mobile number linked to Aadhar card, PAN & Aadhaar should be linked.",Corporate Salary Account
27
+ "What is the initial deposit when you open a Salary Account?","No initial deposit is required to open a Salary Account.",Corporate Salary Account
28
+ "Do we need to shift our Current Account to Jupiter?","Absolutely not, your existing relationship with banker is mostly for Business Banking. You shall continue enjoying benefits provided by your existing banker (for business banking) + enjoy additional benefits from Jupiter against your employee's bank account. You get to enjoy the best of both worlds.",Corporate Salary Account
29
+ "Do employees need to maintain any minimum balance?","While users are required to maintain ₹5,000 as the average monthly balance, charges up to ₹200 for not maintaining of balance. However for Salary Account users, the account is a zero balance account on consistent salary credits.",Corporate Salary Account
30
+ "Is it a Federal bank account or Jupiter account?","A Federal Bank account is created for each employee. They can use the Jupiter app to pay, invest or save money without any restrictions.",Corporate Salary Account
31
+ "Can the employee continue with the Jupiter salary account even if he leaves the present company?","Your employees can continue using the same Salary Account with their new employer and keep enjoying our benefits.",Corporate Salary Account
32
+ "If any employee has an active loan with their current bank, can they still use the Jupiter Corporate Salary Account?","They can continue paying their loan with their existing bank. Jupiter's Autopay feature lets them auto-transfer any amount from their Jupiter account to their existing bank, ensuring their loan payments continue seamlessly.",Corporate Salary Account
33
+ "What payment methods does Jupiter support?","You can make payments via UPI, your linked bank account, and debit cards. You can also link any bank's RuPay Credit Card to Jupiter UPI and make payments.",Payments
34
+ "Is it safe to make payments through Jupiter?","Yes. Jupiter uses industry-standard encryption and is compliant with RBI regulations to ensure secure and private transactions.",Payments
35
+ "Can I pay my bills using Jupiter?","Yes, you can pay utility bills like electricity, gas, DTH, water, and mobile recharges directly from the app.",Payments
36
+ "Can I link multiple bank accounts to Jupiter?","Yes, you can link more than one bank account and switch between them when making UPI payments.",Payments
37
+ "Can I track my payments on Jupiter?","Yes, you can track all your payments, see your top spending categories, and get actionable insights to better manage your money.",Payments
38
+ "How do I earn cashback or rewards on payments?","If you have a PRO Savings Account, you earn 1% cashback on all Debit Card payments and UPI payments from 7 top brands. If you have linked any RuPay Credit Card to Jupiter UPI, you earn up to 2% cashback on every payment. Cashback is credited in the form of Jewels. 5 Jewels = ₹1",Payments
39
+ "Are there any charges for UPI transactions?","No, UPI payments are free of charge on Jupiter.",Payments
40
+ "Is there a transaction limit for UPI payments?","Yes, the daily UPI limit is set by your bank and NPCI, which is generally ₹1 Lakh per day.",Payments
41
+ "What types of bills can I pay on Jupiter?","You can pay your credit card bills, electricity, DTH, broadband, water, gas, mobile prepaid, and more—all in one place.",Bill Payments
42
+ "What payment modes can I use to pay bills on Jupiter?","You can pay bills through bank transfers linked to your Federal Bank Savings Account, UPI, or your RuPay Credit Cards.",Bill Payments
43
+ "Do I get rewards for bill payments?","Yes! You earn Jewels for every eligible bill and recharge made with Jupiter.",Bill Payments
44
+ "What are Jewels and how do they work?","Jewels are credits you earn when you make eligible payments with Jupiter. You can redeem them in cash for instant discounts on future bill payments.\n₹1= 5 Points*\n*As per the rewards policy",Bill Payments
45
+ "How do I activate Autopay?","To set up Autopay for bills & recharges, use your Jupiter UPI ID as a default payment method on the respective merchant app or website.",Bill Payments
46
+ "Do I need to upload any documents or enter bill details manually each time?","No paperwork needed! Just link your billing merchant once, and your dues automatically in the future.",Bill Payments
47
+ "Can I track my past payments?","Yes, your entire payment history is available under the 'Bills & Recharges' section in the app.",Bill Payments
48
+ "What type of payments can I make with Jupiter UPI?","You can send money, pay contacts, scan QR codes at stores, and shop online—securely and instantly.",UPI Payments
49
+ "Do I need to open a Jupiter Savings Account to use UPI?","No, you can use Jupiter UPI with your existing bank account by linking it on the Jupiter app.",UPI Payments
50
+ "What rewards do I get with my Edge CSB Bank RuPay Credit Card?","You can get 2% cashback on UPI & credit card spends with the Edge CSB Bank RuPay Credit Card.",UPI Payments
51
+ "Can I send money to contacts using other UPI apps?","Absolutely. Jupiter lets you send money to anyone—regardless of the UPI app they use.",UPI Payments
52
+ "What is UPI Lite and how does it work?","UPI Lite lets you make small-value payments without entering your UPI PIN (Up to ₹5,000). It's perfect for quick spends like groceries or tea.",UPI Payments
53
+ "Is there a limit on UPI transactions?","To ensure safety of your transactions, NPCI has set the maximum daily UPI payments limit to ₹1 Lakh.",UPI Payments
54
+ "What is Magic Spends and how does it work?","Magic Spends is an exclusive feature on Jupiter to automatically invest every time you spend. You can select any amount, set a monthly limit, and invest in either Mutual Funds or Digital Gold with every UPI, Debit Card, and Credit Card payment.",Magic Spends
55
+ "Where can I invest with Magic Spends?","You can invest in a Mutual Fund of your choice or 24K Digital Gold every time you spend.",Magic Spends
56
+ "Can I customize how much money I auto-invest every time I spend?","Yes, you can auto-invest ₹10 or more in Digital Gold or ₹100 or more in Mutual Funds every time you spend with Magic Spends.",Magic Spends
57
+ "Can I track my invested amount with Magic Spends?","Yes, you can track the value of your invested amount in real-time for both Mutual Funds and Digital Gold.",Magic Spends
58
+ "What happens if I don't have enough balance in my account?","If you don't have enough balance while spending, Magic Spends will be skipped. You can continue auto-investing after you add money to your account.",Magic Spends
59
+ "Can I pause Magic Spends or withdraw my invested amount?","Yes, you can pause Magic Spends anytime through the app. You can also withdraw the money you've invested and it will be credited instantly to your account.",Magic Spends
60
+ "Are there any fees or charges for Magic Spends?","No, Magic Spends is completely free to set up and Jupiter does not charge any commissions.",Magic Spends
61
+ "How can I apply for the Edge+ CSB Bank RuPay Credit Card?","You can check your eligibility and credit limit for the credit card instantly. To apply for the card, download the Jupiter app and complete your application anytime. It's 100% digital with no paperwork!",Edge+ Credit Card
62
+ "What's the maximum cashback I can earn with Edge+?","There is no maximum limit of cashback which can be earned with Edge+, there are limits on accelerated cashback for categories.",Edge+ Credit Card
63
+ "What is the maximum cashback I can earn for each category?","The maximum cashback applicable under each category is: 10% cashback - ₹1,500 per billing cycle (merchant limit of ₹500); 5% cashback - ₹1,000 per billing cycle; 1% cashback - No Limit",Edge+ Credit Card
64
+ "How is cashback credited?","The cashback amount is credited to your account instantly in the form of Jewels. 5 Jewels = ₹1 and you can redeem Jewels as cash, gift cards, Digital Gold, and for bill payments.",Edge+ Credit Card
65
+ "Which brands does Edge+ offer cashback on?","10% cashback on shopping: Amazon, Flipkart, Myntra, Ajio, Zara, Nykaa, Croma, Reliance Trends, Tata Cliq, Reliance Digital; 5% cashback on travel: MakeMyTrip, EaseMyTrip, Yatra, Cleartrip; 1% cashback: On all other offline & online eligible spends. Brand names are indicative and subject to change.",Edge+ Credit Card
66
+ "Are there any fees for the Edge+ card?","There is only a one-time joining fee of ₹499+GST for the card. You also get a Fraud Protection Plan by OneAssist which includes an Amazon Prime Membership worth ₹1499. The card is lifetime free with no annual fee.",Edge+ Credit Card
67
+ "How can I start using UPI on my RuPay Credit Card?","Install the Jupiter app and apply for the Edge CSB Bank RuPay Credit Card. Fill in the necessary details. Once approved, you can start using the card to make UPI payments on your RuPay Credit Card.",UPI on RuPay Credit Card
68
+ "How to use the Edge CSB Bank RuPay Credit Card for UPI payments?","You can enable online transactions on your Edge RuPay Credit Card to use it on any website or app.",UPI on RuPay Credit Card
69
+ "Can I send money to friends using a Credit Card on UPI?","No, you can only use the Edge CSB Bank RuPay Credit Card to pay online and offline merchants.",UPI on RuPay Credit Card
70
+ "Who can apply for a RuPay Credit Card?","Anyone between 23 and 60 years and with a good credit score can apply for the Edge CSB Bank Credit Card.",UPI on RuPay Credit Card
71
+ "Is it safe to use my RuPay Credit Card on UPI?","It is absolutely safe to use your RuPay Credit Card on UPI.",UPI on RuPay Credit Card
72
+ "Can I withdraw money from UPI Credit Cards?","Currently, ATM withdrawal is not enabled on your Edge RuPay Credit Card.",UPI on RuPay Credit Card
73
+ "How can I use my RuPay Card for online payments?","Once you enable online transactions on your Edge RuPay Credit Card, you can use it on any website or app to pay.",UPI on RuPay Credit Card
74
+ "How can I pay a retail merchant offline with UPI on Credit Card?","Edge RuPay Credit Card allows you to make UPI payments with a Credit Card. You can scan any QR code and pay with your credit card.",UPI on RuPay Credit Card
75
+ "How can I link my Edge CSB Bank Credit Card to UPI?","Once you have activated your credit card on Jupiter, you will be able to use it with Jupiter UPI automatically.",UPI on RuPay Credit Card
76
+ "Will I earn rewards on UPI Spends using the Edge CSB Bank RuPay Credit Card?","Yes, you can earn assured 2% cashback on UPI transactions.",UPI on RuPay Credit Card
77
+ "How do I generate UPI PIN for my Edge CSB Bank RuPay Credit Card?","You can easily set your Edge UPI Credit Card PIN by going to 'Credit Card' tab on home screen → Choose 'View Card' → Tap 'Settings' gear on top → Tap 'Reset PIN'. Then change your PIN",UPI on RuPay Credit Card
78
+ "What is the eligibility criteria for getting the Edge CSB Bank RuPay Credit Card?","Currently users with a good credit score, and who are older than 21 years can be eligible for the Edge RuPay Credit Card.",UPI on RuPay Credit Card
79
+ "How can I apply for a Edge Federal Bank Visa Credit Card?","Edge Federal Bank Visa Credit card is available exclusively on the Jupiter app. Download the Jupiter app and apply for a Edge Federal Bank Visa Credit Card.",Edge Federal Bank Visa Credit Card
80
+ "How can I check my Edge Federal Bank Visa Credit Card Outstanding Balance?","On the Jupiter app, go to the 'Credit Cards' tab, here, you will be able to see the current outstanding and available limit.",Edge Federal Bank Visa Credit Card
81
+ "Can I use my Edge Federal Bank Visa Credit Card for international payments?","Yes, you can do international transactions with your Edge Federal Bank Visa Credit Card.",Edge Federal Bank Visa Credit Card
82
+ "How to activate Edge Federal Bank Visa Credit Card?","Activate your card by scanning the QR code on the card kit",Edge Federal Bank Visa Credit Card
83
+ "Who is eligible for Edge Federal Bank Visa Credit Card?","Anyone older than 21 years and with a good credit score can apply for the Credit Card.",Edge Federal Bank Visa Credit Card
84
+ "What are the fees and charges on the Edge Federal Bank Visa Credit Card?","Edge Federal Bank Visa Credit Card has zero joining fee and zero service charges for transactions. Read MITC at https://jupiter.money/edge-card-mitc/ for more information.",Edge Federal Bank Visa Credit Card
85
+ "Can I withdraw money using the Edge Federal Bank Visa Credit Card?","Currently, ATM withdrawals are not enabled on your Edge Federal Bank Visa Credit Card.",Edge Federal Bank Visa Credit Card
86
+ "Is Edge Federal Bank Visa Credit Card lifetime free?","Edge Federal Bank Visa Credit Card's annual fee of ₹999 is waived off on eligible spends of ₹1.2 lacs in the preceding year",Edge Federal Bank Visa Credit Card
87
+ "What is the credit limit of Edge Federal Bank Visa Credit Card?","Your limit is calculated based on your credit score",Edge Federal Bank Visa Credit Card
88
+ "How do I earn Jewels on Jupiter?","You earn Jewels when you spend using Jupiter—whether it's with UPI, your debit card, or linked credit cards. 5 Jewels = 1 Rupee",Jupiter Jewels Rewards
89
+ "What can I redeem Jewels for?","You can choose from the following loan options: Digital Gold, Cash credits, Gift vouchers, Bill payments, Credit card discounts",Jupiter Jewels Rewards
90
+ "How fast can I redeem Jewels on Jupiter?","Yes, you can redeem the Jewels instantly. You can convert them to cash, gold, vouchers or use them to get discounts on bill payments.",Jupiter Jewels Rewards
91
+ "Do I get extra rewards with linked accounts or cards?","Yes! For example: PRO Savings Account users get 1% cashback; Edge CSB Bank RuPay Card users get 2% cashback; Linking any RuPay credit card earns you up to 2% cashback on UPI and spends",Jupiter Jewels Rewards
92
+ "Do the earned Jewels expire?","Your Jewels are safe and redeemable directly on the Jupiter app. Expiry terms, if any, are clearly shown under your rewards dashboard.",Jupiter Jewels Rewards
93
+ "What types of loans can I get on Jupiter?","You can choose from the following loan options: Personal Loan - Up to ₹5,00,000; Mini Loan - Up to ₹1,00,000; Loan against Mutual Funds - Up to ₹50,00,000",Jupiter Loans
94
+ "Who can apply for a Personal Loan on Jupiter?","To apply for a Personal Loan, you must: Be an Indian resident; Be 21 to 60 years old; Be either salaried, business owner, or self-employed",Jupiter Loans
95
+ "What are the interest rates applicable on loans with Jupiter?","Personal Loans start at 1.33% per month; Mini Loans have 0% interest If repaid in 45 days; Loans against Mutual Funds start at 10.5% per annum",Jupiter Loans
96
+ "Is the loan application process fully digital?","Yes, the entire process, from checking eligibility to receiving funds, is 100% digital on the Jupiter app.",Jupiter Loans
97
+ "How do I apply for a loan on Jupiter?","Steps to apply: 1. Check your eligibility 2. Select your preferred loan option 3. Confirm the loan details 4. Get money instantly in your account",Jupiter Loans
98
+ "Will I get reminders for my EMI payments?","Yes, Jupiter sends timely EMI reminders so you never miss a due date.",Jupiter Loans
99
+ "Is there any support if I face issues?","Yes, you get 24x7 support from our team via the Jupiter app.",Jupiter Loans
100
+ "What can I use my loan for?","The loans are flexible and can be used for any purpose, including: Laptop or phone purchase; Medical expenses; Travel, wedding, or home renovation; Debt consolidation or emergencies",Jupiter Loans
101
+ "What is a loan against mutual funds?","A Loan Against Mutual Funds lets you borrow money by pledging your Mutual Funds. On Jupiter, you can get a Loan Against Mutual Funds of up to ₹2 Crore",Loan Against Mutual Funds
102
+ "How much money can I borrow?","Your loan limit will depend on the amount and type of Mutual Funds in your portfolio",Loan Against Mutual Funds
103
+ "Can I choose any date for my monthly loan repayment?","You can repay the principal amount anytime within 36 months and the interest in 12 EMIs",Loan Against Mutual Funds
104
+ "Do I need to pay interest on the whole amount I am eligible for?","No, you pay monthly interest only on the withdrawn amount out of the total credit limit given to you",Loan Against Mutual Funds
105
+ "What is lien marking/pledging of Mutual Funds?","When you take a Loan Against Mutual Funds, your funds will be lien marked. This means, the lender has the right to hold your units and sell them in case the loan isn't repaid. However, your funds will continue earning returns and will be redeemable once the loan is fully repaid",Loan Against Mutual Funds
106
+ "Which mutual fund schemes are accepted or can be pledged to take loans?","Your loan limit can be up to 75% of the Net Asset Value of your Debt Mutual Funds, and 45% of the NAV of Equity Mutual Funds. For example, if you have invested ₹3 Lakh in Debt Mutual Funds and ₹1 Lakh in Equity Mutual Funds, your available loan limit will be 75% of ₹3 Lakhs plus 45% of ₹1 Lakh equal to ₹2.70 Lakh",Loan Against Mutual Funds
107
+ "How do I check my limit?","You can check your limit on the Jupiter app",Loan Against Mutual Funds
108
+ "Can I pledge any Mutual Fund?","Yes, you can pledge any eligible Mutual Fund. You can also choose the number of units you want to pledge",Loan Against Mutual Funds
109
+ "When can I redeem the Mutual Funds I have pledged?","Your Mutual Funds will be redeemable once your loan is fully repaid",Loan Against Mutual Funds
110
+ "What is the tenure of the loan against mutual funds?","You can repay the principal amount in up to 36 months and the interest in 12 EMIs",Loan Against Mutual Funds
111
+ "What is the credit limit and how is it calculated?","Your credit limit is calculated based on the value of your Mutual Funds portfolio",Loan Against Mutual Funds
112
+ "What investment options are available on Jupiter?","You can invest in the following: Mutual Funds, Digital Gold, Magic Spends, Fixed Deposits",Investments
113
+ "Do I need to open a Savings Account to Invest on Jupiter?","Yes, you need to open a Savings Account to invest in Mutual Funds, Magic Spends or Fixed Deposits. But, you can start investing in Digital Gold even without opening a Savings Account",Investments
114
+ "Is there a fee for investing in Mutual Funds on Jupiter?","No, Jupiter doesn't charge any commissions or extra fees on Mutual Funds",Investments
115
+ "How do No-Penalty SIPs work on Jupiter ?","No-Penalty SIPs let you skip SIPs without having to pay any extra charges",Investments
116
+ "What is 'Magic Spends' and how does it work?","Magic Spends lets you auto-invest small amounts like ₹10 or more every time you spend, turning your daily spending into effortless investing",Investments
117
+ "Can I withdraw from Fixed Deposits before maturity?","Yes, FlexiFD lets you withdraw money in parts without having to break your full FD",Investments
118
+ "Is my money safe when I invest on Jupiter?","Yes. Jupiter partners with trusted AMCs, RBI-regulated entities, and SEBI-compliant platforms to ensure your investments are 100% safe",Investments
119
+ "Am I eligible to invest on Jupiter?","We're currently open to folks who have Mutual Fund investment accounts already. If you've invested in Mutual Funds in India, or have signed up to invest on another platform before, you're eligible! Go ahead and sign up on the Jupiter app to get started. In a few weeks, everyone else will also be able to invest on Jupiter — hang in there, and watch this space for updates",Investments
120
+ "How do I pay less and earn more using the Jupiter App?","On Jupiter, you pay less and earn more on your investments because: 1. Jupiter doesn't charge any hidden commissions of 1.5% over and above your investment amount like other regular funds. 2. Jupiter doesn't charge Penalties of up to Rs 750 that other banks charge in case you miss a SIP because of insufficient balance in your account",Investments
121
+ "What's the difference between a No-Penalty SIP and a Normal SIP?","In case you miss a SIP because of insufficient balance in your account, your bank charges you ₹250–₹750 as Penalty for that missed SIP. Jupiter just smartly skips your SIP! So, there are no Penalty charges",Investments
122
+ "What all Mutual Funds does Jupiter have?","Jupiter has 1000+ Mutual Funds from 39 major AMCs across India. Jupiter only provides growth Direct Mutual Funds which are the best commission-free funds in India",Investments
123
+ "What are Mutual Funds?","Mutual fund is an investment vehicle that brings together money from many people like you and invests it in stocks, bonds or other assets. Example: ICICI Prudential Technology Fund invests your money, majorly in high growth technology companies like Infosys, Wipro, HCL Technologies, etc.",Investments
124
+ "Why should I invest with Jupiter Money?","Pay less, earn more returns: 1. No hidden commissions of 1.5% like regular funds 2. No penalties up to Rs 750 for missed SIPs. Easy investing: - No-Penalty SIPs auto-skip if funds are low - One-click SIPs and cancellations - No paperwork",Investments
125
+ "What are Direct Mutual Funds which Jupiter provides?","Direct Mutual Funds are Mutual Funds with Zero Commissions or Brokerage charges. They offer up to 1.5% extra returns compared to other Mutual Funds",Investments
126
+ "Why should I invest in Mutual Funds?","You can invest in Mutual funds as: 1. You can save up to Rs 46,800 in taxes every year. 2. Your invested money compounds and generates more returns. 3. You build financial discipline.",Investments
127
+ "Are Mutual Funds regulated?","Mutual Fund companies are regulated by Securities and Exchange Board of India (SEBI). SEBI falls under the control of the Ministry of Finance of India.",Investments
128
+ "I don't know what Mutual Funds to invest in. What do I do?","We've created Mutual Fund collections to help you: - High Growth - Tax Saving Funds - Better than FD - Low cost index funds - SIP with 500. These are matched to your investment goals.",Investments
129
+ "What is the smallest amount I can invest in Mutual Funds?","You can start investing in Mutual Funds with as little as Rs. 500. We've put together a Collection containing all Mutual Funds where you can start investing with Rs 500.",Investments
130
+ "Is investing in Mutual Funds difficult?","Investing in Mutual Funds is simple on Jupiter: - Pick a fund or collection - Choose SIP or One Time investment - Swipe to pay. That's it!",Investments
131
+ "How long does it take to get started with Mutual Funds on Jupiter?","If you're a verified user on the Jupiter app, you can make your investment in under 5 minutes",Investments
132
+ "I've never been charged a Penalty before. Why should I choose No-Penalty SIPs?","Even if you're disciplined, No-Penalty SIP gives you flexibility. If you have an emergency or want to use your money elsewhere, just skip your SIP — no charges, no questions asked.",Investments
133
+ "How is Jupiter not charging the Rs. 250-750 Penalty? Is there any catch or fine-print?","Banks charge this penalty when your SIP mandate fails due to insufficient balance. Since you bank and invest on Jupiter, we detect low balance and pause your SIP, avoiding penalty fees completely.",Investments
134
+ "Do you mean I don't get same day's NAV elsewhere?","Most platforms take a few working days to allocate units after order placement, and the NAV can change. On Jupiter, if you invest before 2 PM on working days, you get that day's NAV.",Investments
135
+ "What is Digital Gold on Jupiter?","Digital Gold on Jupiter allows you to buy and sell 24K gold online at live market prices, starting from as low as ₹10. It's 100% digital and secure with no making charges.",Digital Gold
136
+ "How is my Digital Gold stored?","Every gram of Digital Gold you purchase is stored as 24K physical Gold in lifetime-free vaults by our trusted partners.",Digital Gold
137
+ "Can I start investing in Gold with just ₹10?","Yes, you can purchase 24K Digital Gold with just ₹10 or more. You can either make a one-time investment or invest regularly with daily, weekly, and monthly SIPs.",Digital Gold
138
+ "Can I sell my Digital Gold anytime?","Yes, you can instantly sell your Digital Gold at live market prices from the Jupiter app.",Digital Gold
139
+ "Can I get physical delivery of the gold?","Currently, Jupiter offers only digital storage of gold. Physical delivery is not available.",Digital Gold
140
+ "How is the price of Digital Gold determined?","Prices are updated live and reflect the current market rate of 24K gold with no making charges.",Digital Gold
141
+ "Why should I invest in Digital Gold?","Digital Gold lets you invest in 24K pure gold without worrying about storage or safety. Unlike traditional options, you can start small, track your gold value in real time, and sell it anytime you want. It's a smart way to grow your savings and with Jupiter, the process is completely digital.",Digital Gold
142
+ "Can I set up automatic investments in Digital Gold?","Yes, you can set up a daily, weekly, or monthly SIP to auto-invest in Digital Gold. You can also set up Magic Spends, a feature exclusively on Jupiter, to auto-invest in Gold every time you spend.",Digital Gold
143
+ "What is Magic Spends and how does it work?","Magic Spends is an exclusive feature on Jupiter to automatically invest every time you spend. You can select any amount, set a monthly limit, and invest in either Mutual Funds or Digital Gold with every UPI, Debit Card, and Credit Card payment.",Magic Spends
144
+ "Where can I invest with Magic Spends?","You can invest in a Mutual Fund of your choice or 24K Digital Gold every time you spend.",Magic Spends
145
+ "Can I customize how much money I auto-invest every time I spend?","Yes, you can auto-invest ₹10 or more in Digital Gold or ₹100 or more in Mutual Funds every time you spend with Magic Spends.",Magic Spends
146
+ "Can I track my invested amount with Magic Spends?","Yes, you can track the value of your invested amount in real-time for both Mutual Funds and Digital Gold.",Magic Spends
147
+ "What happens if I don't have enough balance in my account?","If you don't have enough balance while spending, Magic Spends will be skipped. You can continue auto-investing after you add money to your account.",Magic Spends
148
+ "Can I pause Magic Spends or withdraw my invested amount?","Yes, you can pause Magic Spends anytime through the app. You can also withdraw the money you've invested and it will be credited instantly to your account.",Magic Spends
149
+ "Are there any fees or charges for Magic Spends?","No, Magic Spends is completely free to set up and Jupiter does not charge any commissions.",Magic Spends
150
+ "What can I do in the Money tab on Jupiter?","You can track your income, expenses, top categories, upcoming bills, and get smart insights on where your money goes — all in one place.",Money Tab
151
+ "How does Jupiter categorize my spending?","Jupiter auto-categorizes your transactions (like groceries, food, shopping, etc.) using smart tags so you can understand your spending patterns at a glance.",Money Tab
152
+ "Can I track my credit card expenses in the Money tab?","Yes, you can link your credit cards and view all your spends, dues, and statements in one place, even if they're from other banks.",Money Tab
153
+ "What is Account Aggregator and how does it work?","Account Aggregator (AA) is a secure framework regulated by the RBI that lets you link and track balances and payments from all your bank accounts. Rest assured, you stay in control and choose what to share and for how long.",Money Tab
154
+ "Is it safe to link my accounts through Account Aggregator?","Yes, it's completely safe. Jupiter only accesses your data with your consent via licensed Account Aggregators like Finvu, and cannot access it without your permission.",Money Tab
155
+ "Can I set Budgets in the Money tab?","Yes, you can set a monthly Budgets to control overspending and manage your money better every month. Jupiter lets you know when you're about to cross your monthly Budget so you can plan your expenses better!",Money Tab
156
+ "What is Net Worth in Jupiter and how is it calculated?","Your Net Worth on Jupiter is the difference between what you own and what you owe. It includes all your linked bank balances, investments (like FDs or mutual funds), and credit card dues or loans. When you link external accounts using Account Aggregator, we give you a real-time, 360° view of your true net worth in one simple number.",Money Tab
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Core dependencies
2
+ flask
3
+ pandas
4
+ numpy
5
+ torch
6
+ sentence-transformers
7
+ transformers
8
+ scikit-learn
9
+
10
+ # Scraping dependencies
11
+ beautifulsoup4
12
+ requests
13
+ selenium
14
+ webdriver-manager
15
+
16
+ # NLP and utilities
17
+ nltk
18
+ python-dotenv
19
+
20
+ # Optional for notebook demo
21
+ jupyter
22
+ matplotlib
23
+ seaborn
run.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from app import app
2
+
3
+ if __name__ == '__main__':
4
+ print("Starting Flask application...")
5
+ app.run(debug=True, port=5000)