ayush2917 commited on
Commit
eebdfa4
·
verified ·
1 Parent(s): a6ad404

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -263
app.py CHANGED
@@ -7,7 +7,7 @@ from typing import Dict, List, Tuple, Optional
7
 
8
  # Configure logging
9
  logging.basicConfig(
10
- level=logging.DEBUG,
11
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
12
  )
13
  logger = logging.getLogger(__name__)
@@ -15,297 +15,95 @@ logger = logging.getLogger(__name__)
15
  app = Flask(__name__, static_folder='static')
16
  CORS(app)
17
 
18
- # Configuration
19
- MODEL_API_URL = "https://api-inference.huggingface.co/models/HuggingFaceH4/zephyr-7b-beta"
20
- API_KEY = os.getenv("HF_API_KEY")
21
  FALLBACK_ENABLED = True
22
- MODEL_TIMEOUT = 15 # seconds
23
- MODEL_MAX_TOKENS = 300
24
-
25
- # Type aliases
26
- QuickReply = Dict[str, str]
27
- CategoryResponses = Dict[str, Dict[str, Dict[str, str]]]
28
 
29
  class RupeiaChatbot:
30
- """Main chatbot class handling both model and fallback responses"""
31
-
32
  def __init__(self):
33
- self.fallback_responses = self._load_fallback_responses()
34
- self.category_quick_replies = self._load_category_quick_replies()
35
-
36
- @staticmethod
37
- def _load_fallback_responses() -> CategoryResponses:
38
- """Load all predefined fallback responses"""
39
  return {
40
- "investment": {
41
- "sip_start_detailed": {
42
- "response": "To start a new SIP with Rupeia, log in to your account, navigate to 'Investments' > 'SIP', select a fund like DSP ELSS Tax Saver Fund, and set your investment amount and frequency. The minimum investment amount is ₹500 per month.",
43
- "quick_replies": [
44
- {"title": "SIP vs Lumpsum", "value": "What's the difference between SIP and Lumpsum?"},
45
- {"title": "Pause SIP", "value": "Can I pause my SIP for a few months?"}
46
- ]
47
- },
48
- "sip_lumpsum_diff_detailed": {
49
- "response": "SIP involves regular, fixed investments (e.g., monthly), ideal for disciplined investing, while Lumpsum is a one-time investment, suited for surplus funds.",
50
- "quick_replies": [
51
- {"title": "Start SIP", "value": "How do I start an SIP with Rupeia?"},
52
- {"title": "Wealth+ Features", "value": "What's Wealth+ and how do I get it?"}
53
- ]
54
- }
55
- },
56
- "goals": {
57
- "create_goal_detailed": {
58
- "response": "To create a financial goal like saving for a wedding, go to 'Goals' > 'Create Goal', select the goal type, enter your budget and duration, and Rupeia will suggest an investment plan.",
59
- "quick_replies": [
60
- {"title": "Change Goal Budget", "value": "Can I change my goal budget later?"},
61
- {"title": "Track Goals", "value": "What goals can I track on Rupeia?"}
62
- ]
63
- }
64
  },
65
- "benefits": {
66
- "monitoring_24_7_detailed": {
67
- "response": "Rupeia's 24/7 monitoring uses AI to track market trends and adjust your portfolio for steady growth.",
68
- "quick_replies": [
69
- {"title": "Personalized Suggestions", "value": "How are investment suggestions personalized?"},
70
- {"title": "Loss Prevention", "value": "How does Loss Prevention work?"}
71
- ]
72
- }
73
- },
74
- "gadgets": {
75
- "save_ipad_detailed": {
76
- "response": "To save for an iPad, create a goal in 'Goals' > 'Gadgets', set your budget and timeline, and link it to an SIP.",
77
- "quick_replies": [
78
- {"title": "Samsung Tablets", "value": "Which Samsung tablets fit my budget?"},
79
- {"title": "Compare Phones", "value": "How do I compare Apple and Samsung phones?"}
80
- ]
81
- }
82
- },
83
- "news_blogs": {
84
- "find_news_detailed": {
85
- "response": "Find financial news in 'News & Blogs' > 'Financial News'. You can bookmark articles to save them.",
86
- "quick_replies": [
87
- {"title": "Save Blogs", "value": "Can I save blogs?"},
88
- {"title": "Gadget Blogs", "value": "How do I find blogs on gadgets?"}
89
- ]
90
- }
91
- }
92
- }
93
-
94
- @staticmethod
95
- def _load_category_quick_replies() -> Dict[str, List[QuickReply]]:
96
- """Load quick replies organized by category"""
97
- return {
98
- "investment": [
99
- {"title": "Start SIP", "value": "How do I start an SIP with Rupeia?"},
100
- {"title": "SIP vs Lumpsum", "value": "What's the difference between SIP and Lumpsum?"},
101
- {"title": "Wealth+ Features", "value": "What's Wealth+ and how do I get it?"}
102
- ],
103
- "goals": [
104
- {"title": "Set Car Goal", "value": "How do I set a goal to save for a car?"},
105
- {"title": "Change Goal Budget", "value": "Can I change my goal budget later?"}
106
- ],
107
- "benefits": [
108
- {"title": "24/7 Monitoring", "value": "What's 24/7 monitoring on my portfolio?"},
109
- {"title": "Personalized Suggestions", "value": "How are investment suggestions personalized?"}
110
- ],
111
- "gadgets": [
112
- {"title": "Save for iPad", "value": "Can I save for an iPad with SIP?"},
113
- {"title": "Compare Phones", "value": "How do I compare Apple and Samsung phones?"}
114
- ],
115
- "news_blogs": [
116
- {"title": "Latest News", "value": "Where's the latest financial news?"},
117
- {"title": "Save Blogs", "value": "Can I save blogs?"}
118
- ]
119
  }
120
-
121
- def match_category(self, message: str) -> Optional[str]:
122
- """Determine which category the user's message belongs to"""
123
- message = message.lower()
124
- category_keywords = {
125
- "investment": ["sip", "lumpsum", "portfolio", "wealth+"],
126
- "goals": ["goal", "budget", "save for"],
127
- "benefits": ["monitoring", "personalized", "loss prevention"],
128
- "gadgets": ["ipad", "phone", "laptop", "gadget"],
129
- "news_blogs": ["news", "blog", "article"]
130
- }
131
-
132
- for category, keywords in category_keywords.items():
133
- if any(keyword in message for keyword in keywords):
134
- return category
135
- return None
136
-
137
- def get_fallback_response(self, message: str) -> Tuple[str, List[QuickReply]]:
138
- """Get the most appropriate predefined response"""
139
  message = message.lower()
140
-
141
- # Try exact matches first
142
- for category, items in self.fallback_responses.items():
143
- for key, data in items.items():
144
- keywords = key.split('_') + [category]
145
- if any(keyword in message for keyword in keywords):
146
- return data["response"], data["quick_replies"]
147
-
148
- # Then try category-based quick replies
149
- category = self.match_category(message)
150
- if category:
151
- return (
152
- f"I can help with {category} related questions. Please choose an option:",
153
- self.category_quick_replies.get(category, [])
154
- )
155
-
156
- # Final fallback
157
- return (
158
- "I'm here to help with your financial questions. Could you be more specific?",
159
- [
160
- {"title": "Investments", "value": "Tell me about investment options"},
161
- {"title": "Savings Goals", "value": "How to set savings goals?"}
162
- ]
163
- )
164
-
165
- def generate_model_response(self, message: str) -> Optional[str]:
166
- """Generate response using Hugging Face model"""
167
- if not API_KEY:
168
- logger.warning("No Hugging Face API key configured")
169
  return None
170
 
171
- prompt = f"""<|system|>
172
- You are Rupeia, a helpful AI financial assistant for a platform called Rupeia.
173
- Provide concise, accurate responses to financial questions.
174
- Always respond in a professional but friendly tone.
175
- If asked about non-financial topics, politely redirect to financial matters.
176
- </s>
177
- <|user|>
178
- {message}
179
- </s>
180
- <|assistant|>
181
- """
182
-
183
  try:
184
- headers = {
185
- "Authorization": f"Bearer {API_KEY}",
186
- "Content-Type": "application/json"
187
- }
188
- payload = {
189
- "inputs": prompt,
190
- "parameters": {
191
- "max_new_tokens": MODEL_MAX_TOKENS,
192
- "temperature": 0.7,
193
- "top_p": 0.9,
194
- "do_sample": True,
195
- "repetition_penalty": 1.1
196
- }
197
- }
198
-
199
  response = requests.post(
200
- MODEL_API_URL,
201
  headers=headers,
202
  json=payload,
203
  timeout=MODEL_TIMEOUT
204
  )
205
-
206
- # Handle model loading case
207
- if response.status_code == 503:
208
- eta = response.json().get('estimated_time', 30)
209
- logger.warning(f"Model is loading, estimated time: {eta} seconds")
210
- return None
211
-
212
  response.raise_for_status()
213
-
214
- # Clean up the response
215
- generated_text = response.json()[0]['generated_text']
216
- clean_response = generated_text.replace(prompt, "").strip()
217
-
218
- # Ensure response ends with proper punctuation
219
- if clean_response and clean_response[-1] not in {'.', '!', '?'}:
220
- clean_response += '.'
221
-
222
- return clean_response
223
-
224
- except requests.exceptions.RequestException as e:
225
- logger.error(f"Model API request failed: {str(e)}")
226
- return None
227
- except (KeyError, IndexError, TypeError) as e:
228
- logger.error(f"Failed to parse model response: {str(e)}")
229
  return None
230
 
231
- @app.route('/api/chat', methods=['POST'])
232
- def handle_chat():
233
- """Main chat endpoint"""
234
  try:
235
  data = request.get_json()
236
- if not data or 'message' not in data:
237
- raise ValueError("Invalid request format")
238
-
239
- message = data['message'].strip()
240
- if not message:
241
- raise ValueError("Empty message received")
242
-
243
- conversation_id = data.get('conversation_id', '')
244
- logger.info(f"Processing message: '{message}' (conversation: {conversation_id})")
245
 
246
  chatbot = RupeiaChatbot()
 
247
 
248
- # Try to get model response first
249
- model_response = chatbot.generate_model_response(message)
250
-
251
- # Determine response source
252
  if model_response:
253
- response_text = model_response
254
- quick_replies = [] # Model responses typically don't need quick replies
255
- source = "model"
256
- logger.debug("Using model-generated response")
257
- elif FALLBACK_ENABLED:
258
- response_text, quick_replies = chatbot.get_fallback_response(message)
259
- source = "fallback"
260
- logger.debug("Using fallback response")
261
- else:
262
- raise RuntimeError("No response available")
263
-
264
- return jsonify({
265
- 'text': response_text,
266
- 'quickReplies': quick_replies,
267
- 'conversation_id': conversation_id,
268
- 'source': source
269
- })
270
 
271
- except ValueError as e:
272
- logger.warning(f"Invalid request: {str(e)}")
273
- return jsonify({
274
- 'text': "Please provide a valid message.",
275
- 'quickReplies': [],
276
- 'conversation_id': data.get('conversation_id', ''),
277
- 'source': 'error'
278
- }), 400
279
  except Exception as e:
280
- logger.error(f"Error processing chat request: {str(e)}")
281
- return jsonify({
282
- 'text': "Sorry, I'm having trouble responding. Please try again later.",
283
- 'quickReplies': [],
284
- 'conversation_id': data.get('conversation_id', ''),
285
- 'source': 'error'
286
- }), 500
287
 
288
  @app.route('/')
289
- def serve_index():
290
- """Serve the main index page"""
291
  return send_from_directory('templates', 'index.html')
292
 
293
- @app.route('/static/<path:path>')
294
- def serve_static(path):
295
- """Serve static files"""
296
- return send_from_directory('static', path)
297
-
298
  if __name__ == '__main__':
299
- port = int(os.getenv("PORT", 5000))
300
- debug_mode = os.getenv("DEBUG", "false").lower() == "true"
301
-
302
- logger.info(f"Starting Rupeia Chatbot on port {port}")
303
- logger.info(f"Model API: {'enabled' if API_KEY else 'disabled'}")
304
- logger.info(f"Fallback responses: {'enabled' if FALLBACK_ENABLED else 'disabled'}")
305
-
306
- app.run(
307
- host='0.0.0.0',
308
- port=port,
309
- debug=debug_mode,
310
- use_reloader=False
311
- )
 
7
 
8
  # Configure logging
9
  logging.basicConfig(
10
+ level=logging.INFO,
11
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
12
  )
13
  logger = logging.getLogger(__name__)
 
15
  app = Flask(__name__, static_folder='static')
16
  CORS(app)
17
 
18
+ # Hugging Face Space Configuration
19
+ HF_API_URL = os.getenv("HF_API_URL", "https://api-inference.huggingface.co/models/HuggingFaceH4/zephyr-7b-beta")
20
+ HF_TOKEN = os.getenv("HF_TOKEN") # Will use Spaces secrets
21
  FALLBACK_ENABLED = True
22
+ MODEL_TIMEOUT = 10 # seconds
 
 
 
 
 
23
 
24
  class RupeiaChatbot:
 
 
25
  def __init__(self):
26
+ self.fallback_responses = self._load_responses()
27
+ self.quick_replies = self._load_quick_replies()
28
+
29
+ def _load_responses(self) -> Dict[str, Dict[str, str]]:
30
+ """Load all predefined responses"""
 
31
  return {
32
+ "sip": {
33
+ "response": "To start an SIP with Rupeia: 1) Login 2) Go to Investments 3) Select SIP option 4) Choose fund and amount. Minimum ₹500/month.",
34
+ "quick_replies": ["sip vs lumpsum", "pause sip"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  },
36
+ # Add other responses similarly
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
+
39
+ def get_fallback(self, message: str) -> Tuple[str, List[str]]:
40
+ """Get fallback response"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  message = message.lower()
42
+ for intent, data in self.fallback_responses.items():
43
+ if intent in message:
44
+ return data["response"], data["quick_replies"]
45
+ return "I can help with investments, goals, and more. Please ask clearly.", ["start sip", "set goal"]
46
+
47
+ def query_model(self, message: str) -> Optional[str]:
48
+ """Query HF model with error handling"""
49
+ if not HF_TOKEN:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  return None
51
 
52
+ headers = {"Authorization": f"Bearer {HF_TOKEN}"}
53
+ payload = {
54
+ "inputs": f"""<|system|>
55
+ You are Rupeia, a financial assistant. Respond concisely to:
56
+ {message}</s><|assistant|>""",
57
+ "parameters": {"max_new_tokens": 250}
58
+ }
59
+
 
 
 
 
60
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  response = requests.post(
62
+ HF_API_URL,
63
  headers=headers,
64
  json=payload,
65
  timeout=MODEL_TIMEOUT
66
  )
 
 
 
 
 
 
 
67
  response.raise_for_status()
68
+ return response.json()[0]['generated_text']
69
+ except Exception as e:
70
+ logger.error(f"Model error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  return None
72
 
73
+ @app.route('/chat', methods=['POST'])
74
+ def chat():
 
75
  try:
76
  data = request.get_json()
77
+ message = data.get('message', '').strip()
 
 
 
 
 
 
 
 
78
 
79
  chatbot = RupeiaChatbot()
80
+ model_response = chatbot.query_model(message)
81
 
 
 
 
 
82
  if model_response:
83
+ return jsonify({
84
+ 'text': model_response,
85
+ 'source': 'model'
86
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
+ if FALLBACK_ENABLED:
89
+ text, replies = chatbot.get_fallback(message)
90
+ return jsonify({
91
+ 'text': text,
92
+ 'quick_replies': replies,
93
+ 'source': 'fallback'
94
+ })
95
+
96
  except Exception as e:
97
+ logger.error(f"Chat error: {str(e)}")
98
+
99
+ return jsonify({
100
+ 'text': "Service unavailable. Please try later.",
101
+ 'source': 'error'
102
+ }), 503
 
103
 
104
  @app.route('/')
105
+ def home():
 
106
  return send_from_directory('templates', 'index.html')
107
 
 
 
 
 
 
108
  if __name__ == '__main__':
109
+ app.run(debug=False, host='0.0.0.0', port=8080)