siricy commited on
Commit
33546f7
·
verified ·
1 Parent(s): adb39da
Files changed (1) hide show
  1. app.py +517 -221
app.py CHANGED
@@ -1,22 +1,16 @@
1
  import streamlit as st
2
  import os
 
 
 
3
  import tempfile
4
 
5
  st.set_page_config(page_title="MyMoola", layout="wide")
6
 
7
- # Set environment variables for HuggingFace before importing transformers
8
- temp_dir = tempfile.mkdtemp()
9
- os.environ["TRANSFORMERS_CACHE"] = temp_dir
10
- os.environ["HF_HOME"] = temp_dir
11
- os.environ["HUGGINGFACE_HUB_CACHE"] = temp_dir
12
-
13
- from transformers import pipeline
14
-
15
- # ---------- Custom scrapbook-style CSS + Corner Images + Shimmer title + Sparkling page ----------
16
  page_bg = """
17
  <style>
18
  @import url('https://fonts.googleapis.com/css2?family=Baloo+2:wght@600&display=swap');
19
-
20
  /* Scrapbook background: dark, graph-paper vibe, and corner images */
21
  [data-testid="stAppViewContainer"] {
22
  background-color: #0e0e0e;
@@ -34,7 +28,6 @@ page_bg = """
34
  position: relative;
35
  overflow: hidden;
36
  }
37
-
38
  /* Sparkle container */
39
  .sparkle {
40
  pointer-events: none;
@@ -44,7 +37,6 @@ page_bg = """
44
  z-index: 9999;
45
  overflow: visible;
46
  }
47
-
48
  .sparkle-dot {
49
  position: absolute;
50
  width: 6px;
@@ -55,12 +47,10 @@ page_bg = """
55
  filter: drop-shadow(0 0 3px #fff);
56
  animation: sparkle-flicker 3s infinite ease-in-out;
57
  }
58
-
59
  @keyframes sparkle-flicker {
60
  0%, 100% {opacity: 0.6;}
61
  50% {opacity: 0.1;}
62
  }
63
-
64
  /* Animate multiple dots with different delays and positions */
65
  .sparkle-dot:nth-child(1) { top: 8%; left: 10%; animation-delay: 0s; }
66
  .sparkle-dot:nth-child(2) { top: 12%; left: 25%; animation-delay: 0.4s; }
@@ -72,7 +62,6 @@ page_bg = """
72
  .sparkle-dot:nth-child(8) { top: 33%; left: 15%; animation-delay: 2.8s; }
73
  .sparkle-dot:nth-child(9) { top: 36%; left: 50%; animation-delay: 3.2s; }
74
  .sparkle-dot:nth-child(10) { top: 40%; left: 65%; animation-delay: 3.6s; }
75
-
76
  /* Shimmer animation for heading */
77
  h1 {
78
  text-align: center;
@@ -106,7 +95,6 @@ h1::before {
106
  left: 125%;
107
  }
108
  }
109
-
110
  /* Chat bubbles */
111
  .chat-bubble-user {
112
  text-align: right;
@@ -132,7 +120,6 @@ h1::before {
132
  color: #111;
133
  box-shadow: 3px 3px 0px #222;
134
  }
135
-
136
  /* Quick buttons styling */
137
  .quick-button {
138
  background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
@@ -150,12 +137,10 @@ h1::before {
150
  text-decoration: none;
151
  display: inline-block;
152
  }
153
-
154
  .quick-button:hover {
155
  transform: translate(-1px, -1px);
156
  box-shadow: 4px 4px 0px #222;
157
  }
158
-
159
  /* Loading spinner */
160
  .loading-moola {
161
  text-align: center;
@@ -164,7 +149,6 @@ h1::before {
164
  font-size: 1.2rem;
165
  margin: 20px 0;
166
  }
167
-
168
  /* Status messages */
169
  .status-success {
170
  background: linear-gradient(135deg, #7bed9f, #70b3ff);
@@ -176,7 +160,6 @@ h1::before {
176
  font-weight: 600;
177
  box-shadow: 2px 2px 0px #222;
178
  }
179
-
180
  .status-info {
181
  background: linear-gradient(135deg, #70b3ff, #5a9cff);
182
  color: #111;
@@ -187,7 +170,6 @@ h1::before {
187
  font-weight: 600;
188
  box-shadow: 2px 2px 0px #222;
189
  }
190
-
191
  .status-warning {
192
  background: linear-gradient(135deg, #ffa726, #ffb74d);
193
  color: #111;
@@ -198,7 +180,6 @@ h1::before {
198
  font-weight: 600;
199
  box-shadow: 2px 2px 0px #222;
200
  }
201
-
202
  /* Chat container */
203
  .chat-container {
204
  max-height: 400px;
@@ -206,123 +187,359 @@ h1::before {
206
  padding: 10px;
207
  margin: 20px 0;
208
  }
209
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  </style>
211
  """
212
 
213
  st.markdown(page_bg, unsafe_allow_html=True)
214
 
215
- # ---------- AI Model Loading ----------
216
- @st.cache_resource
217
  def load_model():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  try:
219
- generator = pipeline(
220
- "text-generation",
221
- model="microsoft/DialoGPT-small",
222
- device_map="auto"
223
- )
224
- return generator
225
  except Exception as e:
226
- st.error(f"Model loading failed: {e}")
227
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
- # Predefined responses for common financial questions
230
- FINANCIAL_RESPONSES = {
231
- "50/30/20 rule": """🎯 **The 50/30/20 Rule**
 
232
 
233
- 💰 **50% for Needs**: Rent, utilities, groceries, minimum debt payments
234
- 🎉 **30% for Wants**: Fun stuff! Dining out, entertainment, hobbies
235
- 💎 **20% for Savings**: Emergency fund, retirement, investments
236
-
237
- This magical formula helps you enjoy today while building tomorrow! ✨""",
238
-
239
- "emergency fund": """🚨 **Emergency Fund Starter Guide**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
- 🎯 **Goal**: Start with $500, then build to 3-6 months expenses
242
- 🏦 **Account**: Separate high-yield savings (don't touch!)
243
- 🔄 **Automate**: $25-50 per paycheck adds up fast
244
- 🎁 **Windfalls**: Tax refunds → straight to emergency fund
245
- 🌱 **Start Small**: Even $5/week = $260/year!
246
-
247
- Remember: Something beats nothing every time! 💪""",
248
 
249
- "credit score": """📊 **Credit Score Guide**
 
250
 
251
- **800-850**: Excellent (VIP treatment everywhere!)
252
- **740-799**: Very Good (great rates, easy approvals)
253
- 💚 **670-739**: Good (decent rates, most loans approved)
254
- ⚠️ **580-669**: Fair (higher rates, work needed)
255
- 🔴 **300-579**: Poor (time to rebuild!)
256
-
257
- **Boost Tips**: Pay on time, low balances, keep old cards! 🚀""",
258
-
259
- "invest $1000": """💸 **Smart $1000 Investment Ideas**
260
 
261
- 🏦 **High-Yield Savings** (Safe): 4-5% return, perfect for emergencies
262
- 📈 **Index Funds** (Growth): VTI, VOO - diversified and low fees
263
- 🎯 **Target-Date Funds** (Easy): Auto-pilot investing magic
264
- 🌟 **Roth IRA** (Tax-Free): Future you will thank you!
265
- 🤖 **Robo-Advisor** (Hands-off): Let the bots handle it!
266
-
267
- Pro tip: Emergency fund first, then invest for 5+ years! 💎""",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
- "budget": """💰 **Budgeting Made Simple**
 
 
270
 
271
- 📝 **Track Everything**: Apps like Mint, YNAB, or good ol' spreadsheets
272
- 🎯 **Categories**: Needs, wants, savings (50/30/20 rule!)
273
- ⚡ **Automate**: Bills, savings - set it and forget it
274
- 🔍 **Review Monthly**: Spot patterns, adjust as needed
275
- 🎉 **Celebrate Wins**: Hit a goal? Treat yourself (within budget!)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
- Budgeting = giving your money a job description! 💼""",
 
 
 
 
 
278
 
279
- "debt": """⚡ **Debt Destruction Plan**
280
-
281
- 📊 **List Everything**: Write down all debts, rates, minimums
282
- 🔥 **Avalanche Method**: Pay minimums, attack highest rate first
283
- ❄️ **Snowball Method**: Pay minimums, attack smallest balance first
284
- 💳 **Stop Using Cards**: Cash/debit only during payoff
285
- 🎯 **Extra Income**: Side hustle? Straight to debt!
286
 
287
- You've got this! Every payment is progress! 💪"""
288
- }
 
 
 
 
 
 
 
 
 
 
 
 
289
 
290
- def get_financial_advice(question):
291
- """Get financial advice from AI or predefined responses"""
292
- question_lower = question.lower()
 
 
 
293
 
294
- # Check for predefined responses first
295
- for key, response in FINANCIAL_RESPONSES.items():
296
- if any(keyword in question_lower for keyword in key.split()):
297
  return response
298
 
299
- # Try to use the AI model if available
300
- generator = st.session_state.get('model_generator')
301
- if generator:
302
- try:
303
- prompt = f"As MyMoola, a friendly financial advisor, give helpful advice for: {question}"
304
- response = generator(prompt, max_length=150, num_return_sequences=1, temperature=0.7)
305
- generated_text = response[0]['generated_text'].replace(prompt, "").strip()
306
-
307
- # Add some personality if the response is too dry
308
- if len(generated_text) < 50:
309
- return f"💡 Great question! {generated_text} Remember, small steps lead to big wins! 🚀"
310
- return f"💡 {generated_text}"
311
- except:
312
- pass
313
 
314
- # Fallback response with personality
315
- return """🤔 That's a great financial question! While I'm still learning, here are some universal money tips:
316
-
317
- 💰 **Track your spending** - knowledge is power!
318
- 🎯 **Pay yourself first** - automate that savings
319
- **Tackle high-interest debt** - it's costing you big time
320
- 📈 **Start investing early** - time is your best friend
321
- 🛡️ **Build an emergency fund** - life happens!
322
-
323
- Want more specific advice? Try asking about budgeting, investing, or debt payoff! 💪"""
324
-
325
- # ---------- Sparkle dots ----------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  sparkle_html = """
327
  <div class="sparkle">
328
  <div class="sparkle-dot"></div>
@@ -339,125 +556,204 @@ sparkle_html = """
339
  """
340
  st.markdown(sparkle_html, unsafe_allow_html=True)
341
 
342
- # ---------- Model Loading Status ----------
343
- if 'model_loaded' not in st.session_state:
344
- st.session_state.model_loaded = False
345
- st.session_state.model_generator = None
346
-
347
- if not st.session_state.model_loaded:
348
- with st.spinner("🐷 Moola is getting ready to help you with finances..."):
349
- try:
350
- generator = load_model()
351
- if generator:
352
- st.session_state.model_generator = generator
353
- st.session_state.model_loaded = True
354
- st.markdown("<div class='status-success'>🎉 Moola is ready to make you financially awesome!</div>", unsafe_allow_html=True)
355
- else:
356
- st.markdown("<div class='status-info'>💭 Moola is using brain power mode (predefined responses)!</div>", unsafe_allow_html=True)
357
- st.session_state.model_loaded = True
358
- except Exception as e:
359
- st.markdown("<div class='status-warning'>⚡ Moola is running on backup wisdom!</div>", unsafe_allow_html=True)
360
- st.session_state.model_loaded = True
361
-
362
- # ---------- Title with shimmer effect ----------
363
- st.markdown("<h1>💰 MyMoola - Your Finance Buddy 🐷✨</h1>", unsafe_allow_html=True)
364
-
365
- # ---------- Quick Action Buttons ----------
366
- st.markdown("### 🚀 Quick Questions")
367
-
368
- col1, col2, col3 = st.columns(3)
369
-
370
- with col1:
371
- if st.button("💡 50/30/20 Rule", key="btn1"):
372
- if "messages" not in st.session_state:
373
- st.session_state.messages = []
374
- st.session_state.messages.append(("You", "What is the 50/30/20 rule?"))
375
- response = get_financial_advice("50/30/20 rule")
376
- st.session_state.messages.append(("Moola", response))
377
-
378
- if st.button("📊 Good Credit Score", key="btn4"):
379
- if "messages" not in st.session_state:
380
- st.session_state.messages = []
381
- st.session_state.messages.append(("You", "What's a good credit score?"))
382
- response = get_financial_advice("credit score")
383
- st.session_state.messages.append(("Moola", response))
384
-
385
  with col2:
386
- if st.button("🏦 Emergency Fund", key="btn2"):
387
- if "messages" not in st.session_state:
388
- st.session_state.messages = []
389
- st.session_state.messages.append(("You", "How do I start an emergency fund?"))
390
- response = get_financial_advice("emergency fund")
391
- st.session_state.messages.append(("Moola", response))
392
-
393
- if st.button("💰 Budgeting Tips", key="btn5"):
394
- if "messages" not in st.session_state:
395
- st.session_state.messages = []
396
- st.session_state.messages.append(("You", "How do I create a budget?"))
397
- response = get_financial_advice("budget")
398
- st.session_state.messages.append(("Moola", response))
399
-
400
- with col3:
401
- if st.button("📈 Invest $1000", key="btn3"):
402
- if "messages" not in st.session_state:
403
- st.session_state.messages = []
404
- st.session_state.messages.append(("You", "How should I invest $1000?"))
405
- response = get_financial_advice("invest $1000")
406
- st.session_state.messages.append(("Moola", response))
407
-
408
- if st.button("⚡ Pay Off Debt", key="btn6"):
409
- if "messages" not in st.session_state:
410
- st.session_state.messages = []
411
- st.session_state.messages.append(("You", "How do I pay off debt faster?"))
412
- response = get_financial_advice("debt")
413
- st.session_state.messages.append(("Moola", response))
414
-
415
- # ---------- Interactive chat interface ----------
416
- if "messages" not in st.session_state:
417
- st.session_state.messages = []
418
-
419
- def submit_message():
420
- user_text = st.session_state.input_text
421
- if user_text.strip():
422
- st.session_state.messages.append(("You", user_text))
423
-
424
- with st.spinner("🐷 Moola is thinking..."):
425
- bot_reply = get_financial_advice(user_text)
426
- st.session_state.messages.append(("Moola", bot_reply))
427
 
428
- st.session_state.input_text = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
 
430
- st.markdown("### 💬 Chat with Moola")
431
- st.text_input("Ask me anything about money! 💸", key="input_text", on_change=submit_message,
432
- placeholder="e.g., How do I save for a house?")
433
 
434
- # Display chat messages in a scrollable container
435
- if st.session_state.messages:
436
- st.markdown('<div class="chat-container">', unsafe_allow_html=True)
 
 
 
437
 
438
- # Show recent messages (last 10 to avoid clutter)
439
- recent_messages = st.session_state.messages[-10:] if len(st.session_state.messages) > 10 else st.session_state.messages
 
 
 
 
 
 
 
 
 
 
440
 
441
- for role, text in recent_messages:
442
- if role == "You":
443
- st.markdown(f"<div style='text-align: right;'><div class='chat-bubble-user'><b>You:</b> {text}</div></div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
  else:
445
- st.markdown(f"<div style='text-align: left;'><div class='chat-bubble-bot'><b>🐷 Moola:</b> {text}</div></div>", unsafe_allow_html=True)
446
 
447
  st.markdown('</div>', unsafe_allow_html=True)
448
-
449
- # Clear chat button
450
- if st.button("🗑️ Clear Chat History"):
451
- st.session_state.messages = []
452
- st.experimental_rerun()
453
 
454
- # ---------- Footer ----------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  st.markdown("---")
456
  st.markdown("""
457
  <div style='text-align: center; color: #ffe066; font-family: "Baloo 2", cursive; margin-top: 30px;'>
458
- <p>🌟 <b>MyMoola</b> - Making money management fun and friendly! 🌟</p>
459
  <p style='font-size: 0.8rem; color: #ccc;'>
460
- 💡 <i>Educational purposes only. Always consult financial advisors for personalized advice.</i>
461
  </p>
462
  </div>
463
  """, unsafe_allow_html=True)
 
1
  import streamlit as st
2
  import os
3
+ import shutil
4
+ from transformers import AutoTokenizer, AutoModelForCausalLM
5
+ from huggingface_hub import snapshot_download
6
  import tempfile
7
 
8
  st.set_page_config(page_title="MyMoola", layout="wide")
9
 
10
+ # Custom scrapbook-style CSS + Corner Images + Shimmer title + Sparkling page
 
 
 
 
 
 
 
 
11
  page_bg = """
12
  <style>
13
  @import url('https://fonts.googleapis.com/css2?family=Baloo+2:wght@600&display=swap');
 
14
  /* Scrapbook background: dark, graph-paper vibe, and corner images */
15
  [data-testid="stAppViewContainer"] {
16
  background-color: #0e0e0e;
 
28
  position: relative;
29
  overflow: hidden;
30
  }
 
31
  /* Sparkle container */
32
  .sparkle {
33
  pointer-events: none;
 
37
  z-index: 9999;
38
  overflow: visible;
39
  }
 
40
  .sparkle-dot {
41
  position: absolute;
42
  width: 6px;
 
47
  filter: drop-shadow(0 0 3px #fff);
48
  animation: sparkle-flicker 3s infinite ease-in-out;
49
  }
 
50
  @keyframes sparkle-flicker {
51
  0%, 100% {opacity: 0.6;}
52
  50% {opacity: 0.1;}
53
  }
 
54
  /* Animate multiple dots with different delays and positions */
55
  .sparkle-dot:nth-child(1) { top: 8%; left: 10%; animation-delay: 0s; }
56
  .sparkle-dot:nth-child(2) { top: 12%; left: 25%; animation-delay: 0.4s; }
 
62
  .sparkle-dot:nth-child(8) { top: 33%; left: 15%; animation-delay: 2.8s; }
63
  .sparkle-dot:nth-child(9) { top: 36%; left: 50%; animation-delay: 3.2s; }
64
  .sparkle-dot:nth-child(10) { top: 40%; left: 65%; animation-delay: 3.6s; }
 
65
  /* Shimmer animation for heading */
66
  h1 {
67
  text-align: center;
 
95
  left: 125%;
96
  }
97
  }
 
98
  /* Chat bubbles */
99
  .chat-bubble-user {
100
  text-align: right;
 
120
  color: #111;
121
  box-shadow: 3px 3px 0px #222;
122
  }
 
123
  /* Quick buttons styling */
124
  .quick-button {
125
  background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
 
137
  text-decoration: none;
138
  display: inline-block;
139
  }
 
140
  .quick-button:hover {
141
  transform: translate(-1px, -1px);
142
  box-shadow: 4px 4px 0px #222;
143
  }
 
144
  /* Loading spinner */
145
  .loading-moola {
146
  text-align: center;
 
149
  font-size: 1.2rem;
150
  margin: 20px 0;
151
  }
 
152
  /* Status messages */
153
  .status-success {
154
  background: linear-gradient(135deg, #7bed9f, #70b3ff);
 
160
  font-weight: 600;
161
  box-shadow: 2px 2px 0px #222;
162
  }
 
163
  .status-info {
164
  background: linear-gradient(135deg, #70b3ff, #5a9cff);
165
  color: #111;
 
170
  font-weight: 600;
171
  box-shadow: 2px 2px 0px #222;
172
  }
 
173
  .status-warning {
174
  background: linear-gradient(135deg, #ffa726, #ffb74d);
175
  color: #111;
 
180
  font-weight: 600;
181
  box-shadow: 2px 2px 0px #222;
182
  }
 
183
  /* Chat container */
184
  .chat-container {
185
  max-height: 400px;
 
187
  padding: 10px;
188
  margin: 20px 0;
189
  }
190
+ /* Calculator cards */
191
+ .calculator-card {
192
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
193
+ border-radius: 15px;
194
+ padding: 20px;
195
+ margin: 15px 0;
196
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
197
+ backdrop-filter: blur(10px);
198
+ border: 1px solid rgba(255, 255, 255, 0.1);
199
+ }
200
+ .result-card {
201
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
202
+ border-radius: 12px;
203
+ padding: 15px;
204
+ margin: 10px 0;
205
+ text-align: center;
206
+ box-shadow: 0 4px 15px rgba(79, 172, 254, 0.3);
207
+ color: #fff;
208
+ font-family: 'Baloo 2', cursive;
209
+ font-weight: 600;
210
+ }
211
+ .needs-card {
212
+ background: linear-gradient(135deg, #ff6b6b, #ff8e8e);
213
+ }
214
+ .wants-card {
215
+ background: linear-gradient(135deg, #4ecdc4, #44a08d);
216
+ }
217
+ .savings-card {
218
+ background: linear-gradient(135deg, #45b7d1, #96c93d);
219
+ }
220
+ .input-container {
221
+ background: rgba(255, 255, 255, 0.1);
222
+ border-radius: 10px;
223
+ padding: 15px;
224
+ margin: 10px 0;
225
+ backdrop-filter: blur(5px);
226
+ }
227
+ .calculator-title {
228
+ color: #ffe066;
229
+ font-family: 'Baloo 2', cursive;
230
+ font-size: 1.8rem;
231
+ text-align: center;
232
+ margin-bottom: 20px;
233
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
234
+ }
235
  </style>
236
  """
237
 
238
  st.markdown(page_bg, unsafe_allow_html=True)
239
 
240
+ # Load Hugging Face Model (with error handling)
241
+ @st.cache_resource(show_spinner=False)
242
  def load_model():
243
+ model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
244
+
245
+ # Try multiple cache directory strategies
246
+ cache_options = [
247
+ "./model_cache", # Original attempt
248
+ os.path.expanduser("~/.cache/huggingface/transformers"), # Default HF cache
249
+ tempfile.mkdtemp(), # Temporary directory
250
+ None # Let HF handle it automatically
251
+ ]
252
+
253
+ for cache_dir in cache_options:
254
+ try:
255
+ # Clear any existing lock files if using custom cache
256
+ if cache_dir and cache_dir.startswith("./"):
257
+ lock_pattern = os.path.join(cache_dir, "models--TinyLlama--TinyLlama-1.1B-Chat-v1.0", "*.lock")
258
+ import glob
259
+ for lock_file in glob.glob(lock_pattern):
260
+ try:
261
+ os.remove(lock_file)
262
+ st.info(f"Removed stale lock file: {lock_file}")
263
+ except:
264
+ pass
265
+
266
+ # Attempt to load the model
267
+ st.info(f"Trying cache directory: {cache_dir or 'default'}")
268
+
269
+ if cache_dir:
270
+ tokenizer = AutoTokenizer.from_pretrained(
271
+ model_name,
272
+ cache_dir=cache_dir,
273
+ local_files_only=False,
274
+ force_download=False
275
+ )
276
+ model = AutoModelForCausalLM.from_pretrained(
277
+ model_name,
278
+ cache_dir=cache_dir,
279
+ local_files_only=False,
280
+ force_download=False
281
+ )
282
+ else:
283
+ # Use default HF cache location
284
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
285
+ model = AutoModelForCausalLM.from_pretrained(model_name)
286
+
287
+ st.success(f"Model loaded successfully using cache: {cache_dir or 'default'}")
288
+ return tokenizer, model
289
+
290
+ except Exception as e:
291
+ st.warning(f"Failed with cache {cache_dir or 'default'}: {str(e)}")
292
+ continue
293
+
294
+ # If all cache options fail, try downloading without cache
295
  try:
296
+ st.info("Attempting direct download without cache...")
297
+ tokenizer = AutoTokenizer.from_pretrained(model_name, cache_dir=None)
298
+ model = AutoModelForCausalLM.from_pretrained(model_name, cache_dir=None)
299
+ return tokenizer, model
 
 
300
  except Exception as e:
301
+ st.error(f"All loading attempts failed. Error: {str(e)}")
302
+ st.error("Please check your internet connection and try again.")
303
+ st.stop()
304
+
305
+ # Alternative: Load smaller model if TinyLlama fails
306
+ @st.cache_resource(show_spinner=False)
307
+ def load_fallback_model():
308
+ """Load an even smaller model as fallback"""
309
+ try:
310
+ model_name = "microsoft/DialoGPT-small" # Smaller alternative
311
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
312
+ model = AutoModelForCausalLM.from_pretrained(model_name)
313
+ return tokenizer, model
314
+ except Exception as e:
315
+ st.error(f"Fallback model also failed: {str(e)}")
316
+ return None, None
317
 
318
+ # Generate Response
319
+ def generate_response(user_input, tokenizer, model):
320
+ if not tokenizer or not model:
321
+ return "Sorry, the model is not available. Please refresh and try again."
322
 
323
+ try:
324
+ # Create a more detailed and specific prompt for financial advice
325
+ financial_context = """You are MyMoola, an expert financial advisor. Provide accurate, helpful financial advice in bullet point format.
326
+
327
+ IMPORTANT FORMATTING RULES:
328
+ - Use bullet points for all key information
329
+ - Keep each bullet point short (1-2 lines max)
330
+ - Provide 5-8 bullet points total
331
+ - Include specific numbers/amounts when relevant
332
+
333
+ CONTENT GUIDELINES:
334
+ - If asked about SIP, explain Systematic Investment Plan with practical steps
335
+ - If asked about stocks, provide actionable stock market advice
336
+ - Focus on Indian financial context (Rupees, Indian products)
337
+ - Give practical, actionable advice
338
+ - Use simple, clear language
339
+
340
+ FORMAT EXAMPLE:
341
+ **Topic Name**
342
+ • Point 1 with specific detail
343
+ • Point 2 with actionable step
344
+ • Point 3 with example/number
345
+
346
+ Remember: Be concise, practical, and use bullet points always."""
347
+
348
+ prompt = f"<|system|>\n{financial_context}\n<|user|>\n{user_input}\n<|assistant|>\n"
349
+
350
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=1024)
351
+
352
+ with st.spinner("AI is thinking..."):
353
+ outputs = model.generate(
354
+ **inputs,
355
+ max_new_tokens=250, # Increased to avoid cutoff
356
+ do_sample=True,
357
+ temperature=0.7, # Slightly higher for more variety
358
+ top_p=0.9,
359
+ repetition_penalty=1.2, # Higher to reduce repetition
360
+ pad_token_id=tokenizer.eos_token_id,
361
+ eos_token_id=tokenizer.eos_token_id,
362
+ early_stopping=True
363
+ )
364
+
365
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True)
366
+
367
+ # Extract only the assistant's response
368
+ if "<|assistant|>" in response:
369
+ response = response.split("<|assistant|>")[-1].strip()
370
+
371
+ # Clean up the response further
372
+ response = clean_response(response, user_input)
373
+
374
+ return response
375
 
376
+ except Exception as e:
377
+ return f"Sorry, I encountered an error generating a response: {str(e)}"
 
 
 
 
 
378
 
379
+ def clean_response(response, user_input):
380
+ """Clean and improve the AI response"""
381
 
382
+ # Remove any remaining system prompts or artifacts
383
+ response = response.replace("<|system|>", "").replace("<|user|>", "").replace("<|assistant|>", "")
 
 
 
 
 
 
 
384
 
385
+ # Handle specific financial terms that might be confused
386
+ user_lower = user_input.lower()
387
+
388
+ if "sip" in user_lower and "chatbot" in response.lower():
389
+ # If the AI confused SIP with a chatbot, provide correct SIP definition
390
+ return """**SIP (Systematic Investment Plan)**
391
+
392
+ • **What it is**: Regular monthly investment in mutual funds (min Rs.500)
393
+ • **Key Benefit**: Rupee cost averaging - buy more units when price is low
394
+ • **How to start**: Choose fund → Set amount → Auto-debit monthly
395
+ • **Best duration**: 3+ years for optimal returns
396
+ • **Returns**: 10-12% annually (equity funds, long-term)
397
+ • **Tax benefit**: ELSS funds save tax under 80C
398
+ • **Perfect for**: Retirement, child education, wealth building
399
+ • **Pro tip**: Start with Rs.1000/month, increase 10% yearly"""
400
+
401
+ if "stock trading" in user_lower and len(response) < 50:
402
+ # If response is too short for stock trading, provide comprehensive answer
403
+ return """**Stock Trading Basics for Beginners:**
404
+
405
+ • **Start with Education**: Learn fundamental & technical analysis
406
+ • **Open Accounts**: Demat & Trading account with broker
407
+ • **Start Small**: Begin with Rs.10,000-Rs.50,000
408
+ • **Choose Blue-chip**: Start with TCS, Reliance, HDFC Bank
409
+ • **Set Rules**: Never invest borrowed money, diversify sectors
410
+ • **Stop-loss**: Set limits to minimize losses
411
+ • **Investment vs Trading**: Buy & hold for years (safer) vs frequent trading (riskier)
412
+ • **Beginner Tip**: Start with SIP in index funds before individual stocks"""
413
+
414
+ # Remove any references to "MyMoola app" or similar generic responses
415
+ problematic_phrases = [
416
+ "mymoola app", "logging in with", "chatbot designed", "available on the",
417
+ "app and can be accessed", "mobile number", "email address",
418
+ "personalized financial advice based on your", "helps you save money and invest wisely"
419
+ ]
420
+
421
+ if any(phrase in response.lower() for phrase in problematic_phrases):
422
+ return generate_fallback_response(user_input)
423
+
424
+ # Validate response relevance
425
+ if len(response.strip()) < 20:
426
+ return generate_fallback_response(user_input)
427
+
428
+ # If response doesn't seem to address the financial question, use fallback
429
+ financial_keywords = ["invest", "money", "fund", "saving", "budget", "loan", "financial", "rupee", "rs.", "%", "return"]
430
+ if not any(keyword in response.lower() for keyword in financial_keywords) and len(user_input) > 10:
431
+ return generate_fallback_response(user_input)
432
+
433
+ return response
434
 
435
+ def generate_fallback_response(user_input):
436
+ """Generate appropriate fallback responses for common financial questions"""
437
+ user_lower = user_input.lower()
438
 
439
+ fallback_responses = {
440
+ "sip": """**SIP (Systematic Investment Plan)**
441
+
442
+ **What it is**: Regular monthly investment in mutual funds (min Rs.500)
443
+ **Key Benefit**: Rupee cost averaging - buy more units when price is low
444
+ • **How to start**: Choose fund → Set amount → Auto-debit monthly
445
+ • **Best duration**: 3+ years for optimal returns
446
+ • **Returns**: 10-12% annually (equity funds, long-term)
447
+ • **Tax benefit**: ELSS funds save tax under 80C
448
+ • **Perfect for**: Retirement, child education, wealth building
449
+ • **Pro tip**: Start with Rs.1000/month, increase 10% yearly""",
450
+
451
+ "mutual fund": """**Mutual Funds**
452
+
453
+ • **What it is**: Pool money from many investors to buy stocks/bonds
454
+ • **Types**: Equity (stocks), Debt (bonds), Hybrid (mixed)
455
+ • **Minimum**: Start with Rs.500/month via SIP
456
+ • **Returns**: 10-12% annually (equity funds, long-term)
457
+ • **Benefits**: Professional management, instant diversification
458
+ • **Liquidity**: Redeem anytime (except ELSS - 3yr lock)
459
+ • **Best for beginners**: Large cap or index funds
460
+ • **Tax**: Long-term gains >Rs.1L taxed at 10%""",
461
+
462
+ "emergency fund": """**Emergency Fund** is your financial safety net:
463
 
464
+ **Amount**: 6-12 months of expenses
465
+ • **Where**: High-yield savings account or liquid funds
466
+ • **Use**: Medical emergencies, job loss, urgent repairs
467
+ • **Build**: Start with Rs.1000/month, automate it""",
468
+
469
+ "budget": """**Budgeting Made Simple**:
470
 
471
+ **50-30-20 Rule**: 50% needs, 30% wants, 20% savings
472
+ • **Track**: Use apps or simple Excel sheet
473
+ **Categories**: Housing, food, transport, entertainment
474
+ **Review**: Monthly check and adjust as needed""",
475
+
476
+ "credit score": """**Credit Score** ranges from 300-850:
 
477
 
478
+ **750+**: Excellent - Best loan rates
479
+ • **700-749**: Good - Most loans approved
480
+ • **650-699**: Fair - Higher interest rates
481
+ • **Below 650**: Poor - Limited options
482
+ • **Improve**: Pay on time, keep utilization <30%, don't close old cards""",
483
+
484
+ "insurance": """**Insurance Basics**:
485
+
486
+ • **Term Life**: High coverage, low premium (Rs.50L for Rs.1000/month)
487
+ • **Health**: Medical expenses coverage (Rs.5-10L minimum)
488
+ • **General**: Car, home, travel insurance
489
+ • **Rule**: Life insurance = 10-15x annual income""",
490
+
491
+ "tax saving": """**Tax Saving Options (80C)**:
492
 
493
+ **ELSS Mutual Funds**: Best returns + tax saving
494
+ **PPF**: 15-year lock-in, tax-free returns
495
+ **EPF**: Employer contribution matching
496
+ • **Home Loan**: Principal repayment saves tax
497
+ • **Limit**: Rs.1.5L per year under 80C"""
498
+ }
499
 
500
+ # Check for multiple keywords
501
+ for key, response in fallback_responses.items():
502
+ if key in user_lower:
503
  return response
504
 
505
+ # Check for related terms
506
+ if any(term in user_lower for term in ["invest", "investment", "return"]):
507
+ return """**Investment Options for Beginners**:
508
+
509
+ **Best for Beginners**: SIP in diversified mutual funds
510
+ **Safe Options**: PPF, NSC, Bank FDs
511
+ **Market Linked**: Mutual funds, stocks, ETFs
512
+ • **Real Estate**: REITs for small amounts
513
+ **Golden Rule**: Start early, invest regularly, stay patient!"""
 
 
 
 
 
514
 
515
+ if any(term in user_lower for term in ["loan", "emi", "debt"]):
516
+ return """**Loan & Debt Management**:
517
+
518
+ **Repayment Strategy**: Pay high-interest debts first
519
+ **EMI Rule**: Total EMIs shouldn't exceed 40% of income
520
+ **Home Loan**: Longest tenure, lowest rates
521
+ **Personal Loan**: Avoid unless emergency
522
+ **Tip**: Extra payments toward principal save huge interest!"""
523
+
524
+ return "I'd be happy to help with your financial question! Could you please be more specific about what you'd like to know? I can help with topics like investments, budgeting, savings, SIP, mutual funds, insurance, loans, and more."
525
+
526
+ # Clear Cache Function
527
+ def clear_model_cache():
528
+ """Clear the model cache to resolve permission issues"""
529
+ cache_paths = [
530
+ "./model_cache",
531
+ os.path.expanduser("~/.cache/huggingface/transformers")
532
+ ]
533
+
534
+ for cache_path in cache_paths:
535
+ if os.path.exists(cache_path):
536
+ try:
537
+ shutil.rmtree(cache_path)
538
+ st.success(f"Cleared cache: {cache_path}")
539
+ except Exception as e:
540
+ st.warning(f"Could not clear {cache_path}: {str(e)}")
541
+
542
+ # Sparkle dots
543
  sparkle_html = """
544
  <div class="sparkle">
545
  <div class="sparkle-dot"></div>
 
556
  """
557
  st.markdown(sparkle_html, unsafe_allow_html=True)
558
 
559
+ # Add cache management
560
+ col1, col2 = st.columns([3, 1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  with col2:
562
+ if st.button("Clear Cache"):
563
+ clear_model_cache()
564
+ st.cache_resource.clear()
565
+ st.experimental_rerun()
566
+
567
+ # Model loading with better error handling
568
+ try:
569
+ with st.spinner("Loading TinyLLaMA model (first time may take a minute)..."):
570
+ tokenizer, model = load_model()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
 
572
+ if tokenizer and model:
573
+ st.success("Model loaded and ready to chat!")
574
+ else:
575
+ st.warning("Primary model failed. Trying fallback...")
576
+ with st.spinner("Loading fallback model..."):
577
+ tokenizer, model = load_fallback_model()
578
+ if tokenizer and model:
579
+ st.info("Fallback model loaded successfully!")
580
+ except Exception as e:
581
+ st.error(f"Model loading failed: {str(e)}")
582
+ st.info("**Troubleshooting Tips:**")
583
+ st.info("1. Try clicking 'Clear Cache' button above")
584
+ st.info("2. Check your internet connection")
585
+ st.info("3. Wait a few minutes and refresh the page")
586
+ st.stop()
587
+
588
+ # Title with shimmer effect
589
+ st.markdown("<h1>MyMoola - Your Finance Buddy</h1>", unsafe_allow_html=True)
590
+
591
+ # 50/30/20 Calculator Function
592
+ def calculate_50_30_20(income, current_expenses=0):
593
+ """Calculate 50/30/20 budget breakdown"""
594
+ available_income = max(0, income - current_expenses)
595
+
596
+ needs = available_income * 0.50
597
+ wants = available_income * 0.30
598
+ savings = available_income * 0.20
599
+
600
+ return {
601
+ 'available_income': available_income,
602
+ 'needs': needs,
603
+ 'wants': wants,
604
+ 'savings': savings,
605
+ 'needs_percentage': 50,
606
+ 'wants_percentage': 30,
607
+ 'savings_percentage': 20
608
+ }
609
 
610
+ # Quick Calculator Section
611
+ st.markdown("### Quick Calculator")
 
612
 
613
+ # Add tabs for different calculators
614
+ tab1, tab2 = st.tabs(["50/30/20 Budget Calculator", "Ask AI Questions"])
615
+
616
+ with tab1:
617
+ st.markdown('<div class="calculator-card">', unsafe_allow_html=True)
618
+ st.markdown('<div class="calculator-title">50/30/20 Budget Calculator</div>', unsafe_allow_html=True)
619
 
620
+ col1, col2 = st.columns(2)
621
+
622
+ with col1:
623
+ st.markdown('<div class="input-container">', unsafe_allow_html=True)
624
+ monthly_income = st.number_input(
625
+ "Monthly Income (Rs.)",
626
+ min_value=0,
627
+ value=50000,
628
+ step=1000,
629
+ help="Enter your total monthly income"
630
+ )
631
+ st.markdown('</div>', unsafe_allow_html=True)
632
 
633
+ with col2:
634
+ st.markdown('<div class="input-container">', unsafe_allow_html=True)
635
+ current_expenses = st.number_input(
636
+ "Current Fixed Expenses (Rs.)",
637
+ min_value=0,
638
+ value=0,
639
+ step=1000,
640
+ help="Enter your current unavoidable expenses (if any)"
641
+ )
642
+ st.markdown('</div>', unsafe_allow_html=True)
643
+
644
+ if st.button("Calculate Budget Breakdown", type="primary"):
645
+ if monthly_income > 0:
646
+ result = calculate_50_30_20(monthly_income, current_expenses)
647
+
648
+ st.markdown("#### Your Budget Breakdown:")
649
+
650
+ # Display results in beautiful cards
651
+ col1, col2, col3 = st.columns(3)
652
+
653
+ with col1:
654
+ st.markdown(f'''
655
+ <div class="result-card needs-card">
656
+ <h3>50% NEEDS</h3>
657
+ <h2>Rs. {result['needs']:,.0f}</h2>
658
+ <p>Rent, utilities, groceries, minimum debt payments</p>
659
+ </div>
660
+ ''', unsafe_allow_html=True)
661
+
662
+ with col2:
663
+ st.markdown(f'''
664
+ <div class="result-card wants-card">
665
+ <h3>30% WANTS</h3>
666
+ <h2>Rs. {result['wants']:,.0f}</h2>
667
+ <p>Entertainment, dining out, hobbies, shopping</p>
668
+ </div>
669
+ ''', unsafe_allow_html=True)
670
+
671
+ with col3:
672
+ st.markdown(f'''
673
+ <div class="result-card savings-card">
674
+ <h3>20% SAVINGS</h3>
675
+ <h2>Rs. {result['savings']:,.0f}</h2>
676
+ <p>Emergency fund, investments, retirement</p>
677
+ </div>
678
+ ''', unsafe_allow_html=True)
679
+
680
+ # Additional insights
681
+ st.markdown("#### Smart Tips:")
682
+ tips_col1, tips_col2 = st.columns(2)
683
+
684
+ with tips_col1:
685
+ st.info(f"""
686
+ **Automate Your Savings**
687
+ Set up automatic transfer of Rs. {result['savings']:,.0f} to a separate savings account on payday.
688
+ """)
689
+
690
+ with tips_col2:
691
+ st.success(f"""
692
+ **Emergency Fund Goal**
693
+ Build 6 months of expenses: Rs. {result['needs'] * 6:,.0f} using your 20% savings.
694
+ """)
695
+
696
+ if result['available_income'] < monthly_income:
697
+ st.warning(f"""
698
+ **Note:** After fixed expenses of Rs. {current_expenses:,}, you have Rs. {result['available_income']:,} available for the 50/30/20 allocation.
699
+ """)
700
  else:
701
+ st.error("Please enter a valid monthly income amount.")
702
 
703
  st.markdown('</div>', unsafe_allow_html=True)
 
 
 
 
 
704
 
705
+ with tab2:
706
+ # Example questions
707
+ st.header("Try Example Questions")
708
+ cols = st.columns(2)
709
+ if cols[0].button("What is the 50/30/20 rule?"):
710
+ st.session_state.setdefault("chat_history", [])
711
+ bot_response = generate_response("What is the 50/30/20 rule?", tokenizer, model)
712
+ st.session_state.chat_history.append({"user": "What is the 50/30/20 rule?", "bot": bot_response})
713
+
714
+ if cols[1].button("How do I start an emergency fund?"):
715
+ st.session_state.setdefault("chat_history", [])
716
+ bot_response = generate_response("How do I start an emergency fund?", tokenizer, model)
717
+ st.session_state.chat_history.append({"user": "How do I start an emergency fund?", "bot": bot_response})
718
+
719
+ # Additional example questions
720
+ cols2 = st.columns(2)
721
+ if cols2[0].button("What's a good credit score?"):
722
+ st.session_state.setdefault("chat_history", [])
723
+ bot_response = generate_response("What's a good credit score?", tokenizer, model)
724
+ st.session_state.chat_history.append({"user": "What's a good credit score?", "bot": bot_response})
725
+
726
+ if cols2[1].button("How to invest Rs.1000?"):
727
+ st.session_state.setdefault("chat_history", [])
728
+ bot_response = generate_response("How should I invest Rs.1000?", tokenizer, model)
729
+ st.session_state.chat_history.append({"user": "How should I invest Rs.1000?", "bot": bot_response})
730
+
731
+ # User input
732
+ st.subheader("Ask Your Own Question")
733
+ user_input = st.text_input("Type your financial question here:", placeholder="e.g., How do I budget my salary?")
734
+ if user_input:
735
+ st.session_state.setdefault("chat_history", [])
736
+ bot_response = generate_response(user_input, tokenizer, model)
737
+ st.session_state.chat_history.append({"user": user_input, "bot": bot_response})
738
+
739
+ # Display chat history
740
+ if "chat_history" in st.session_state and st.session_state.chat_history:
741
+ st.header("Chat History")
742
+ st.markdown('<div class="chat-container">', unsafe_allow_html=True)
743
+
744
+ for i, chat in enumerate(reversed(st.session_state.chat_history[-5:])): # Show last 5 chats
745
+ st.markdown(f"<div style='text-align: right;'><div class='chat-bubble-user'><b>You:</b> {chat['user']}</div></div>", unsafe_allow_html=True)
746
+ st.markdown(f"<div style='text-align: left;'><div class='chat-bubble-bot'><b>MyMoola:</b> {chat['bot']}</div></div>", unsafe_allow_html=True)
747
+
748
+ st.markdown('</div>', unsafe_allow_html=True)
749
+
750
+ # Add footer
751
  st.markdown("---")
752
  st.markdown("""
753
  <div style='text-align: center; color: #ffe066; font-family: "Baloo 2", cursive; margin-top: 30px;'>
754
+ <p><b>MyMoola</b> - Making financial literacy fun and accessible!</p>
755
  <p style='font-size: 0.8rem; color: #ccc;'>
756
+ <i>Disclaimer: This is for educational purposes. Always consult with a financial advisor for personalized advice.</i>
757
  </p>
758
  </div>
759
  """, unsafe_allow_html=True)