TheSilentOne commited on
Commit
74e4c79
·
verified ·
1 Parent(s): 8ef71e8

Uploading necessary files to the Space

Browse files
Files changed (7) hide show
  1. app.py +523 -0
  2. demo_inputs.json +246 -0
  3. modal_app.py +412 -0
  4. requirements.txt +0 -0
  5. system_prompt.txt +167 -0
  6. utils.py +385 -0
  7. vllm_inference.py +69 -0
app.py ADDED
@@ -0,0 +1,523 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ import requests
4
+ import json
5
+ import plotly.graph_objects as go
6
+ import plotly.express as px
7
+ from fastapi import FastAPI
8
+ from typing import Dict, Any, Optional
9
+ import os
10
+ from datetime import datetime
11
+
12
+ # Custom CSS for MoneyMate branding
13
+ CUSTOM_CSS = """
14
+ .gradio-container {
15
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
16
+ font-family: 'Inter', sans-serif;
17
+ }
18
+
19
+ .main-header {
20
+ text-align: center;
21
+ color: white;
22
+ margin-bottom: 2rem;
23
+ }
24
+
25
+ .money-card {
26
+ background: rgba(255, 255, 255, 0.95);
27
+ border-radius: 15px;
28
+ padding: 1.5rem;
29
+ margin: 1rem 0;
30
+ box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
31
+ backdrop-filter: blur(4px);
32
+ border: 1px solid rgba(255, 255, 255, 0.18);
33
+ }
34
+
35
+ .modal-branding {
36
+ background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
37
+ -webkit-background-clip: text;
38
+ -webkit-text-fill-color: transparent;
39
+ font-weight: bold;
40
+ text-align: center;
41
+ margin-top: 1rem;
42
+ }
43
+
44
+ .advice-box {
45
+ background: #f8f9ff;
46
+ border-left: 4px solid #667eea;
47
+ padding: 1rem;
48
+ margin: 1rem 0;
49
+ border-radius: 8px;
50
+ }
51
+
52
+ .quick-action-btn {
53
+ background: linear-gradient(45deg, #667eea, #764ba2);
54
+ color: white;
55
+ border: none;
56
+ border-radius: 25px;
57
+ padding: 0.5rem 1rem;
58
+ margin: 0.25rem;
59
+ cursor: pointer;
60
+ transition: all 0.3s ease;
61
+ }
62
+
63
+ .quick-action-btn:hover {
64
+ transform: translateY(-2px);
65
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
66
+ }
67
+ """
68
+
69
+ # MCP Server configuration
70
+ class MCPServer:
71
+ def __init__(self):
72
+ self.tools = {
73
+ "salary_breakdown": self.salary_breakdown,
74
+ "investment_advice": self.investment_advice,
75
+ "expense_analysis": self.expense_analysis,
76
+ "savings_goal": self.savings_goal
77
+ }
78
+
79
+ def salary_breakdown(self, salary: float, expenses: Dict[str, float]) -> Dict[str, Any]:
80
+ """Break down salary using 50/30/20 rule with Indian context"""
81
+ needs = salary * 0.5 # 50% for needs
82
+ wants = salary * 0.3 # 30% for wants
83
+ savings = salary * 0.2 # 20% for savings/investments
84
+
85
+ return {
86
+ "breakdown": {
87
+ "needs": needs,
88
+ "wants": wants,
89
+ "savings": savings
90
+ },
91
+ "recommendations": self._get_indian_recommendations(salary)
92
+ }
93
+
94
+ def investment_advice(self, age: int, salary: float, risk_appetite: str) -> Dict[str, Any]:
95
+ """Provide investment advice for Indian market"""
96
+ equity_percentage = min(100 - age, 80) # Age-based equity allocation
97
+ debt_percentage = 100 - equity_percentage
98
+
99
+ return {
100
+ "allocation": {
101
+ "equity": equity_percentage,
102
+ "debt": debt_percentage
103
+ },
104
+ "instruments": self._get_indian_instruments(risk_appetite)
105
+ }
106
+
107
+ def expense_analysis(self, expenses: Dict[str, float]) -> Dict[str, Any]:
108
+ """Analyze expenses and provide optimization suggestions"""
109
+ total_expenses = sum(expenses.values())
110
+ analysis = {}
111
+
112
+ for category, amount in expenses.items():
113
+ percentage = (amount / total_expenses) * 100
114
+ analysis[category] = {
115
+ "amount": amount,
116
+ "percentage": percentage,
117
+ "status": self._categorize_expense(category, percentage)
118
+ }
119
+
120
+ return {"analysis": analysis, "suggestions": self._get_optimization_tips()}
121
+
122
+ def savings_goal(self, goal_amount: float, timeline_months: int, current_savings: float) -> Dict[str, Any]:
123
+ """Calculate monthly savings needed for a goal"""
124
+ remaining_amount = goal_amount - current_savings
125
+ monthly_required = remaining_amount / timeline_months if timeline_months > 0 else 0
126
+
127
+ return {
128
+ "monthly_required": monthly_required,
129
+ "total_goal": goal_amount,
130
+ "timeline": timeline_months,
131
+ "feasibility": "achievable" if monthly_required < 15000 else "challenging"
132
+ }
133
+
134
+ def _get_indian_recommendations(self, salary: float) -> list:
135
+ """Get India-specific financial recommendations"""
136
+ recommendations = [
137
+ "Build emergency fund of 6-12 months expenses",
138
+ "Start SIP in diversified equity mutual funds",
139
+ "Consider ELSS funds for tax saving under 80C",
140
+ "Open PPF account for long-term tax-free returns"
141
+ ]
142
+
143
+ if salary > 50000:
144
+ recommendations.append("Consider NPS for additional retirement planning")
145
+ if salary > 100000:
146
+ recommendations.append("Explore direct equity investment after gaining knowledge")
147
+
148
+ return recommendations
149
+
150
+ def _get_indian_instruments(self, risk_appetite: str) -> list:
151
+ """Get Indian investment instruments based on risk appetite"""
152
+ instruments = {
153
+ "conservative": ["PPF", "NSC", "FD", "Debt Mutual Funds"],
154
+ "moderate": ["Balanced Mutual Funds", "ELSS", "Gold ETF", "Corporate Bonds"],
155
+ "aggressive": ["Large Cap Funds", "Mid Cap Funds", "Small Cap Funds", "Direct Equity"]
156
+ }
157
+ return instruments.get(risk_appetite.lower(), instruments["moderate"])
158
+
159
+ def _categorize_expense(self, category: str, percentage: float) -> str:
160
+ """Categorize expense as optimal, high, or low"""
161
+ thresholds = {
162
+ "rent": (25, 35),
163
+ "food": (15, 25),
164
+ "transport": (10, 15),
165
+ "utilities": (5, 10),
166
+ "entertainment": (5, 15)
167
+ }
168
+
169
+ if category.lower() in thresholds:
170
+ low, high = thresholds[category.lower()]
171
+ if percentage < low:
172
+ return "low"
173
+ elif percentage > high:
174
+ return "high"
175
+ return "optimal"
176
+
177
+ def _get_optimization_tips(self) -> list:
178
+ """Get expense optimization tips"""
179
+ return [
180
+ "Use public transport or carpool to reduce transport costs",
181
+ "Cook at home more often to save on food expenses",
182
+ "Use energy-efficient appliances to reduce utility bills",
183
+ "Set a monthly entertainment budget and stick to it",
184
+ "Review and cancel unused subscriptions"
185
+ ]
186
+
187
+ # Initialize MCP Server
188
+ mcp_server = MCPServer()
189
+
190
+ # Modal backend URL (replace with your actual Modal deployment URL)
191
+ MODAL_BACKEND_URL = os.getenv("MODAL_BACKEND_URL", "https://kaustubhme0--moneymate-backend-fastapi-app.modal.run")
192
+
193
+ def call_modal_backend(user_input: str, context: Dict[str, Any] = None) -> str:
194
+ """Call Modal backend for AI-powered financial advice"""
195
+ try:
196
+ payload = {
197
+ "user_input": user_input,
198
+ "context": context or {}
199
+ }
200
+
201
+ response = requests.post(
202
+ f"{MODAL_BACKEND_URL}/financial_advice",
203
+ json=payload,
204
+ timeout=30
205
+ )
206
+
207
+ if response.status_code == 200:
208
+ return response.json().get("advice", "Unable to get advice at the moment.")
209
+ else:
210
+ return "Sorry, I'm having trouble connecting to the financial advisor. Please try again."
211
+
212
+ except requests.exceptions.RequestException as e:
213
+ return f"Connection error: {str(e)}. Please check your internet connection."
214
+
215
+ def create_salary_breakdown_chart(salary: float, needs: float, wants: float, savings: float):
216
+ """Create a pie chart for salary breakdown"""
217
+ labels = ['Needs (50%)', 'Wants (30%)', 'Savings (20%)']
218
+ values = [needs, wants, savings]
219
+ colors = ['#ff6b6b', '#4ecdc4', '#45b7d1']
220
+
221
+ fig = go.Figure(data=[go.Pie(
222
+ labels=labels,
223
+ values=values,
224
+ hole=0.4,
225
+ marker_colors=colors,
226
+ textinfo='label+percent',
227
+ textfont_size=12
228
+ )])
229
+
230
+ fig.update_layout(
231
+ title=f"Salary Breakdown for ₹{salary:,.0f}",
232
+ font=dict(size=14),
233
+ showlegend=True,
234
+ height=400
235
+ )
236
+
237
+ return fig
238
+
239
+ def create_expense_analysis_chart(expenses: Dict[str, float]):
240
+ """Create a bar chart for expense analysis"""
241
+ categories = list(expenses.keys())
242
+ amounts = list(expenses.values())
243
+
244
+ fig = go.Figure([go.Bar(
245
+ x=categories,
246
+ y=amounts,
247
+ marker_color='#667eea',
248
+ text=[f"₹{amount:,.0f}" for amount in amounts],
249
+ textposition='auto'
250
+ )])
251
+
252
+ fig.update_layout(
253
+ title="Monthly Expense Breakdown",
254
+ xaxis_title="Categories",
255
+ yaxis_title="Amount (₹)",
256
+ font=dict(size=12),
257
+ height=400
258
+ )
259
+
260
+ return fig
261
+
262
+ def process_financial_query(
263
+ salary: float,
264
+ rent: float,
265
+ food: float,
266
+ transport: float,
267
+ utilities: float,
268
+ entertainment: float,
269
+ other: float,
270
+ savings_goal: str,
271
+ user_question: str
272
+ ) -> tuple:
273
+ """Process user's financial query and return advice with visualizations"""
274
+
275
+ # Calculate totals
276
+ total_expenses = rent + food + transport + utilities + entertainment + other
277
+ remaining_salary = salary - total_expenses
278
+
279
+ # Create expense dictionary
280
+ expenses = {
281
+ "Rent": rent,
282
+ "Food": food,
283
+ "Transport": transport,
284
+ "Utilities": utilities,
285
+ "Entertainment": entertainment,
286
+ "Other": other
287
+ }
288
+
289
+ # Get salary breakdown using 50/30/20 rule
290
+ breakdown = mcp_server.salary_breakdown(salary, expenses)
291
+ needs = breakdown["breakdown"]["needs"]
292
+ wants = breakdown["breakdown"]["wants"]
293
+ savings = breakdown["breakdown"]["savings"]
294
+
295
+ # Create charts
296
+ salary_chart = create_salary_breakdown_chart(salary, needs, wants, savings)
297
+ expense_chart = create_expense_analysis_chart(expenses)
298
+
299
+ # Prepare context for Modal backend
300
+ context = {
301
+ "salary": salary,
302
+ "expenses": expenses,
303
+ "total_expenses": total_expenses,
304
+ "remaining_salary": remaining_salary,
305
+ "savings_goal": savings_goal,
306
+ "breakdown": breakdown
307
+ }
308
+
309
+ # Get AI advice
310
+ if user_question.strip():
311
+ advice = call_modal_backend(user_question, context)
312
+ else:
313
+ advice = call_modal_backend(f"Analyze my finances: Salary ₹{salary}, Total expenses ₹{total_expenses}", context)
314
+
315
+ # Create summary
316
+ summary = f"""
317
+ ## 💰 Financial Summary
318
+
319
+ **Monthly Salary:** ₹{salary:,.0f}
320
+ **Total Expenses:** ₹{total_expenses:,.0f}
321
+ **Remaining Amount:** ₹{remaining_salary:,.0f}
322
+
323
+ ### 📊 Recommended Allocation (50/30/20 Rule)
324
+ - **Needs (50%):** ₹{needs:,.0f}
325
+ - **Wants (30%):** ₹{wants:,.0f}
326
+ - **Savings (20%):** ₹{savings:,.0f}
327
+
328
+ ### 🎯 Status
329
+ {'✅ Good job! You have money left over.' if remaining_salary > 0 else '⚠️ You are overspending. Consider reducing expenses.'}
330
+ """
331
+
332
+ return salary_chart, expense_chart, summary, advice
333
+
334
+ def handle_quick_question(question: str, salary: float = 50000) -> str:
335
+ """Handle pre-defined quick questions"""
336
+ context = {"salary": salary}
337
+ return call_modal_backend(question, context)
338
+
339
+ # Create Gradio interface
340
+ def create_moneymate_app():
341
+ with gr.Blocks(css=CUSTOM_CSS, title="MoneyMate - Your Financial Assistant") as app:
342
+
343
+ # Header
344
+ gr.HTML("""
345
+ <div class="main-header">
346
+ <h1>💰 MoneyMate</h1>
347
+ <p>Your Personal Financial Assistant for Smart Money Management</p>
348
+ <div class="modal-branding">⚡ Powered by Modal Labs</div>
349
+ </div>
350
+ """)
351
+
352
+ with gr.Row():
353
+ with gr.Column(scale=1):
354
+ gr.HTML('<div class="money-card">')
355
+ gr.Markdown("### 💼 Your Financial Details")
356
+
357
+ salary_input = gr.Number(
358
+ label="Monthly Salary (₹)",
359
+ value=50000,
360
+ minimum=0,
361
+ step=1000
362
+ )
363
+
364
+ gr.Markdown("#### Monthly Expenses")
365
+ rent_input = gr.Number(label="Rent (₹)", value=15000, minimum=0)
366
+ food_input = gr.Number(label="Food (₹)", value=8000, minimum=0)
367
+ transport_input = gr.Number(label="Transport (₹)", value=3000, minimum=0)
368
+ utilities_input = gr.Number(label="Utilities (₹)", value=2000, minimum=0)
369
+ entertainment_input = gr.Number(label="Entertainment (₹)", value=4000, minimum=0)
370
+ other_input = gr.Number(label="Other Expenses (₹)", value=3000, minimum=0)
371
+
372
+ savings_goal_input = gr.Textbox(
373
+ label="Savings Goal",
374
+ placeholder="e.g., Emergency fund, Vacation, House down payment",
375
+ value="Emergency fund"
376
+ )
377
+
378
+ user_question_input = gr.Textbox(
379
+ label="Ask MoneyMate",
380
+ placeholder="e.g., How should I invest my savings? What's the best way to save for a house?",
381
+ lines=3
382
+ )
383
+
384
+ analyze_btn = gr.Button("Analyze My Finances 📊", variant="primary", size="large")
385
+ gr.HTML('</div>')
386
+
387
+ # Quick action buttons
388
+ gr.HTML('<div class="money-card">')
389
+ gr.Markdown("### 🚀 Quick Questions")
390
+
391
+ with gr.Row():
392
+ quick_btn1 = gr.Button("💡 Investment Tips", size="small")
393
+ quick_btn2 = gr.Button("🏠 Save for House", size="small")
394
+
395
+ with gr.Row():
396
+ quick_btn3 = gr.Button("✈️ Plan Vacation", size="small")
397
+ quick_btn4 = gr.Button("🚗 Buy a Car", size="small")
398
+
399
+ gr.HTML('</div>')
400
+
401
+ with gr.Column(scale=2):
402
+ gr.HTML('<div class="money-card">')
403
+
404
+ # Output components
405
+ with gr.Tab("📊 Salary Breakdown"):
406
+ salary_chart_output = gr.Plot()
407
+
408
+ with gr.Tab("💸 Expense Analysis"):
409
+ expense_chart_output = gr.Plot()
410
+
411
+ with gr.Tab("📋 Summary"):
412
+ summary_output = gr.Markdown()
413
+
414
+ with gr.Tab("🤖 AI Advice"):
415
+ advice_output = gr.Markdown(value="Click 'Analyze My Finances' to get personalized advice!")
416
+
417
+ gr.HTML('</div>')
418
+
419
+ # Event handlers
420
+ analyze_btn.click(
421
+ fn=process_financial_query,
422
+ inputs=[
423
+ salary_input, rent_input, food_input, transport_input,
424
+ utilities_input, entertainment_input, other_input,
425
+ savings_goal_input, user_question_input
426
+ ],
427
+ outputs=[salary_chart_output, expense_chart_output, summary_output, advice_output]
428
+ )
429
+
430
+ # Quick question handlers
431
+ quick_btn1.click(
432
+ fn=lambda s: handle_quick_question("What are the best investment options for a beginner in India?", s),
433
+ inputs=[salary_input],
434
+ outputs=[advice_output]
435
+ )
436
+
437
+ quick_btn2.click(
438
+ fn=lambda s: handle_quick_question("How should I save for buying a house in India?", s),
439
+ inputs=[salary_input],
440
+ outputs=[advice_output]
441
+ )
442
+
443
+ quick_btn3.click(
444
+ fn=lambda s: handle_quick_question("What's the best way to save for a vacation?", s),
445
+ inputs=[salary_input],
446
+ outputs=[advice_output]
447
+ )
448
+
449
+ quick_btn4.click(
450
+ fn=lambda s: handle_quick_question("How should I plan to buy a car with my salary?", s),
451
+ inputs=[salary_input],
452
+ outputs=[advice_output]
453
+ )
454
+
455
+ # Footer
456
+ gr.HTML("""
457
+ <div style="text-align: center; margin-top: 2rem; color: white;">
458
+ <p>Made with ❤️ for Agents & MCP Hackathon 2025</p>
459
+ <p>🏆 Track 1 — MCP Tool / Server</p>
460
+ </div>
461
+ """)
462
+
463
+ return app
464
+
465
+ # FastAPI wrapper for MCP compatibility
466
+ app_fastapi = FastAPI()
467
+
468
+ # Create and mount Gradio app
469
+ gradio_app = create_moneymate_app()
470
+
471
+ # MCP endpoints
472
+ @app_fastapi.post("/mcp/tools")
473
+ async def list_tools():
474
+ """List available MCP tools"""
475
+ return {
476
+ "tools": [
477
+ {
478
+ "name": "salary_breakdown",
479
+ "description": "Break down salary using 50/30/20 rule",
480
+ "inputSchema": {
481
+ "type": "object",
482
+ "properties": {
483
+ "salary": {"type": "number"},
484
+ "expenses": {"type": "object"}
485
+ }
486
+ }
487
+ },
488
+ {
489
+ "name": "investment_advice",
490
+ "description": "Get investment advice for Indian market",
491
+ "inputSchema": {
492
+ "type": "object",
493
+ "properties": {
494
+ "age": {"type": "integer"},
495
+ "salary": {"type": "number"},
496
+ "risk_appetite": {"type": "string"}
497
+ }
498
+ }
499
+ }
500
+ ]
501
+ }
502
+
503
+ @app_fastapi.post("/mcp/call_tool")
504
+ async def call_tool(request: dict):
505
+ """Call MCP tool"""
506
+ tool_name = request.get("name")
507
+ arguments = request.get("arguments", {})
508
+
509
+ if tool_name in mcp_server.tools:
510
+ result = mcp_server.tools[tool_name](**arguments)
511
+ return {"content": [{"type": "text", "text": json.dumps(result, indent=2)}]}
512
+ else:
513
+ return {"error": f"Tool {tool_name} not found"}
514
+
515
+ # Mount Gradio app
516
+ gr.mount_gradio_app(app_fastapi, gradio_app, path="/")
517
+
518
+ if __name__ == "__main__":
519
+ gradio_app.launch(
520
+ server_name="0.0.0.0",
521
+ server_port=7860,
522
+ share=True
523
+ )
demo_inputs.json ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "mcp_server_info": {
3
+ "name": "MoneyMate",
4
+ "version": "1.0.0",
5
+ "description": "AI-powered financial assistant for young Indian professionals",
6
+ "server_url": "https://kaustubhme0--moneymate-backend-fastapi-app.modal.run",
7
+ "gradio_app_url": "https://huggingface.co/spaces/[YOUR_USERNAME]/moneymate",
8
+ "mcp_compatibility": true
9
+ },
10
+ "sample_queries": [
11
+ {
12
+ "id": "salary_breakdown",
13
+ "category": "Salary Planning",
14
+ "user_input": "I earn ₹75,000 per month. How should I allocate my salary?",
15
+ "context": {
16
+ "salary": 75000,
17
+ "expenses": {
18
+ "rent": 25000,
19
+ "food": 8000,
20
+ "transport": 5000,
21
+ "utilities": 3000,
22
+ "entertainment": 7000
23
+ },
24
+ "total_expenses": 48000,
25
+ "remaining_salary": 27000,
26
+ "savings_goal": "Emergency fund and investments"
27
+ },
28
+ "expected_response_type": "financial_advice",
29
+ "description": "Basic salary allocation using 50/30/20 rule"
30
+ },
31
+ {
32
+ "id": "first_job_planning",
33
+ "category": "Career Starter",
34
+ "user_input": "I just got my first job at ₹45,000. What should I do with my money?",
35
+ "context": {
36
+ "salary": 45000,
37
+ "expenses": {
38
+ "rent": 15000,
39
+ "food": 6000,
40
+ "transport": 3000,
41
+ "utilities": 2000,
42
+ "personal": 3000
43
+ },
44
+ "total_expenses": 29000,
45
+ "remaining_salary": 16000,
46
+ "savings_goal": "Build foundation for financial future"
47
+ },
48
+ "expected_response_type": "financial_advice",
49
+ "description": "First job financial planning guidance"
50
+ },
51
+ {
52
+ "id": "investment_planning",
53
+ "category": "Investment Advice",
54
+ "user_input": "I have ₹20,000 left after expenses. How should I invest this money?",
55
+ "context": {
56
+ "salary": 60000,
57
+ "expenses": {
58
+ "rent": 20000,
59
+ "food": 7000,
60
+ "transport": 4000,
61
+ "utilities": 2500,
62
+ "entertainment": 6500
63
+ },
64
+ "total_expenses": 40000,
65
+ "remaining_salary": 20000,
66
+ "savings_goal": "Long-term wealth building"
67
+ },
68
+ "expected_response_type": "financial_advice",
69
+ "description": "Investment strategy for surplus income"
70
+ },
71
+ {
72
+ "id": "emergency_fund",
73
+ "category": "Emergency Planning",
74
+ "user_input": "How much should I keep as emergency fund and where should I keep it?",
75
+ "context": {
76
+ "salary": 80000,
77
+ "expenses": {
78
+ "rent": 30000,
79
+ "food": 10000,
80
+ "transport": 6000,
81
+ "utilities": 4000,
82
+ "entertainment": 8000,
83
+ "miscellaneous": 5000
84
+ },
85
+ "total_expenses": 63000,
86
+ "remaining_salary": 17000,
87
+ "savings_goal": "Build emergency fund"
88
+ },
89
+ "expected_response_type": "financial_advice",
90
+ "description": "Emergency fund planning and placement"
91
+ },
92
+ {
93
+ "id": "goal_based_saving",
94
+ "category": "Goal Planning",
95
+ "user_input": "I want to buy a car worth ₹8 lakhs in 2 years. How much should I save monthly?",
96
+ "context": {
97
+ "salary": 90000,
98
+ "expenses": {
99
+ "rent": 25000,
100
+ "food": 12000,
101
+ "transport": 8000,
102
+ "utilities": 4000,
103
+ "entertainment": 10000,
104
+ "miscellaneous": 6000
105
+ },
106
+ "total_expenses": 65000,
107
+ "remaining_salary": 25000,
108
+ "savings_goal": "Car purchase in 2 years - ₹8 lakhs"
109
+ },
110
+ "expected_response_type": "financial_advice",
111
+ "description": "Goal-based savings calculation"
112
+ },
113
+ {
114
+ "id": "tax_planning",
115
+ "category": "Tax Optimization",
116
+ "user_input": "I need to save tax under Section 80C. What are my best options?",
117
+ "context": {
118
+ "salary": 120000,
119
+ "expenses": {
120
+ "rent": 35000,
121
+ "food": 15000,
122
+ "transport": 8000,
123
+ "utilities": 5000,
124
+ "entertainment": 12000,
125
+ "miscellaneous": 8000
126
+ },
127
+ "total_expenses": 83000,
128
+ "remaining_salary": 37000,
129
+ "savings_goal": "Tax saving and wealth building"
130
+ },
131
+ "expected_response_type": "financial_advice",
132
+ "description": "Tax saving strategies under Section 80C"
133
+ },
134
+ {
135
+ "id": "expense_optimization",
136
+ "category": "Expense Management",
137
+ "user_input": "My expenses are too high. How can I reduce them without affecting my lifestyle?",
138
+ "context": {
139
+ "salary": 70000,
140
+ "expenses": {
141
+ "rent": 28000,
142
+ "food": 12000,
143
+ "transport": 8000,
144
+ "utilities": 4000,
145
+ "entertainment": 15000,
146
+ "shopping": 8000,
147
+ "miscellaneous": 5000
148
+ },
149
+ "total_expenses": 80000,
150
+ "remaining_salary": -10000,
151
+ "savings_goal": "Reduce expenses and start saving"
152
+ },
153
+ "expected_response_type": "financial_advice",
154
+ "description": "Expense optimization without lifestyle compromise"
155
+ },
156
+ {
157
+ "id": "retirement_planning",
158
+ "category": "Long-term Planning",
159
+ "user_input": "I'm 25 years old. When should I start planning for retirement?",
160
+ "context": {
161
+ "salary": 65000,
162
+ "expenses": {
163
+ "rent": 22000,
164
+ "food": 8000,
165
+ "transport": 5000,
166
+ "utilities": 3000,
167
+ "entertainment": 8000,
168
+ "miscellaneous": 4000
169
+ },
170
+ "total_expenses": 50000,
171
+ "remaining_salary": 15000,
172
+ "savings_goal": "Early retirement planning"
173
+ },
174
+ "expected_response_type": "financial_advice",
175
+ "description": "Early career retirement planning"
176
+ }
177
+ ],
178
+ "mcp_integration_examples": [
179
+ {
180
+ "client": "Cursor IDE",
181
+ "query": "Help me plan my ₹80,000 salary allocation",
182
+ "mcp_request": {
183
+ "method": "POST",
184
+ "endpoint": "/financial_advice",
185
+ "payload": {
186
+ "user_input": "Help me plan my ₹80,000 salary allocation",
187
+ "context": {
188
+ "salary": 80000,
189
+ "expenses": {},
190
+ "savings_goal": "General financial planning"
191
+ }
192
+ }
193
+ }
194
+ },
195
+ {
196
+ "client": "Claude Desktop",
197
+ "query": "Calculate SIP returns for ₹10,000 monthly investment",
198
+ "mcp_request": {
199
+ "method": "POST",
200
+ "endpoint": "/calculate_returns",
201
+ "payload": {
202
+ "monthly_investment": 10000,
203
+ "annual_return_rate": 12,
204
+ "years": 10
205
+ }
206
+ }
207
+ }
208
+ ],
209
+ "gradio_interface_examples": [
210
+ {
211
+ "salary_input": 75000,
212
+ "expenses_input": "rent 25000, food 8000, transport 5000, utilities 3000, entertainment 7000",
213
+ "question_input": "How should I allocate my remaining ₹27,000?",
214
+ "expected_output": "Markdown formatted advice with pie chart showing allocation"
215
+ },
216
+ {
217
+ "salary_input": 45000,
218
+ "expenses_input": "rent 15000, food 6000, transport 3000, utilities 2000, personal 3000",
219
+ "question_input": "What's the best way to start investing?",
220
+ "expected_output": "Beginner-friendly investment advice with risk assessment"
221
+ }
222
+ ],
223
+ "testing_scenarios": [
224
+ {
225
+ "name": "High Expenses",
226
+ "salary": 50000,
227
+ "expenses": "rent 30000, food 10000, transport 5000, entertainment 8000",
228
+ "question": "My expenses exceed my salary. What should I do?",
229
+ "expected_behavior": "Expense optimization advice and practical solutions"
230
+ },
231
+ {
232
+ "name": "High Earner",
233
+ "salary": 200000,
234
+ "expenses": "rent 40000, food 15000, transport 10000, utilities 5000, entertainment 20000",
235
+ "question": "How should I invest my surplus income?",
236
+ "expected_behavior": "Advanced investment strategies and tax planning"
237
+ },
238
+ {
239
+ "name": "Conservative Investor",
240
+ "salary": 60000,
241
+ "expenses": "rent 20000, food 8000, transport 4000, utilities 3000, entertainment 5000",
242
+ "question": "I'm afraid of stock market risks. What are safe investment options?",
243
+ "expected_behavior": "Conservative investment options with risk explanation"
244
+ }
245
+ ]
246
+ }
modal_app.py ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import modal
2
+ import json
3
+ from typing import Dict, Any, Optional
4
+ from datetime import datetime
5
+ import os
6
+ import urllib.request
7
+ import urllib.parse
8
+
9
+ # Create Modal app
10
+ app = modal.App("moneymate-backend")
11
+
12
+ # Create image with required dependencies
13
+ image = modal.Image.debian_slim(python_version="3.11").pip_install([
14
+ "fastapi",
15
+ "pydantic"
16
+ ])
17
+
18
+ # vLLM server configuration
19
+ # TODO: Replace with your actual vLLM server URL after deployment
20
+ VLLM_SERVER_URL = "https://kaustubhme0--example-vllm-openai-compatible-serve.modal.run"
21
+
22
+ # This API key should match the API_KEY in your vLLM inference script (paste.txt)
23
+ # It's YOUR custom key, not from any external service
24
+ VLLM_API_KEY = "super-secret-key"
25
+
26
+ # Financial advisor system prompt
27
+ SYSTEM_PROMPT = """You are MoneyMate, a friendly and knowledgeable financial advisor specifically designed for young Indian professionals who are new to managing their salaries and finances.
28
+
29
+ PERSONALITY & TONE:
30
+ - Warm, approachable, and encouraging
31
+ - Use simple, jargon-free language
32
+ - Be supportive and non-judgmental
33
+ - Add appropriate emojis to make advice engaging
34
+ - Safety-first approach - always emphasize careful consideration
35
+
36
+ EXPERTISE AREAS:
37
+ - Salary allocation and budgeting
38
+ - Indian investment options (SIP, ELSS, PPF, NPS, etc.)
39
+ - Tax-saving strategies under Indian tax law
40
+ - Emergency fund planning
41
+ - Goal-based financial planning
42
+ - Expense optimization
43
+
44
+ RESPONSE FORMAT:
45
+ - Use markdown formatting for better readability
46
+ - Include practical examples with Indian Rupee amounts
47
+ - Provide step-by-step actionable advice
48
+ - Always include safety disclaimers for investment advice
49
+ - Suggest starting small and learning gradually
50
+
51
+ CULTURAL CONTEXT:
52
+ - Understand Indian financial instruments and regulations
53
+ - Consider family financial responsibilities common in India
54
+ - Be aware of Indian tax implications (80C, LTCG, etc.)
55
+ - Reference Indian banks, mutual fund companies, and financial platforms
56
+
57
+ SAFETY GUIDELINES:
58
+ - Never guarantee returns or specific outcomes
59
+ - Always recommend consulting financial advisors for major decisions
60
+ - Emphasize the importance of emergency funds before investing
61
+ - Warn about risks associated with investments
62
+ - Promote financial literacy and gradual learning
63
+
64
+ Remember: You're helping someone who may be handling their first salary. Be patient, educational, and encouraging while prioritizing their financial safety and security.
65
+ """
66
+
67
+ def call_vllm_api(messages: list, max_tokens: int = 1500, temperature: float = 0.7) -> str:
68
+ """Call the vLLM server with OpenAI-compatible API"""
69
+
70
+ headers = {
71
+ "Authorization": f"Bearer {VLLM_API_KEY}",
72
+ "Content-Type": "application/json",
73
+ }
74
+
75
+ payload = {
76
+ "messages": messages,
77
+ "model": "neuralmagic/Meta-Llama-3.1-8B-Instruct-quantized.w4a16",
78
+ "max_tokens": max_tokens,
79
+ "temperature": temperature
80
+ }
81
+
82
+ try:
83
+ req = urllib.request.Request(
84
+ f"{VLLM_SERVER_URL}/v1/chat/completions",
85
+ data=json.dumps(payload).encode("utf-8"),
86
+ headers=headers,
87
+ method="POST",
88
+ )
89
+
90
+ # Increased timeout to 180 seconds (3 minutes) for LLM responses
91
+ with urllib.request.urlopen(req, timeout=180) as response:
92
+ if response.getcode() == 200:
93
+ result = json.loads(response.read().decode())
94
+ return result["choices"][0]["message"]["content"]
95
+ else:
96
+ response_text = response.read().decode()
97
+ raise Exception(f"API call failed with status {response.getcode()}: {response_text}")
98
+
99
+ except urllib.error.URLError as e:
100
+ if "timeout" in str(e).lower():
101
+ raise Exception("Request timed out. The vLLM server might be taking too long to respond or might be cold starting.")
102
+ else:
103
+ raise Exception(f"Network error calling vLLM API: {str(e)}")
104
+ except Exception as e:
105
+ raise Exception(f"Error calling vLLM API: {str(e)}")
106
+
107
+ @app.function(
108
+ image=image,
109
+ timeout=300, # Increased to 5 minutes
110
+ memory=1024
111
+ )
112
+ def get_financial_advice(user_input: str, context: Dict[str, Any] = None) -> str:
113
+ """Generate financial advice using local vLLM server"""
114
+
115
+ try:
116
+ # Prepare context information
117
+ context_str = ""
118
+ if context:
119
+ salary = context.get("salary", 0)
120
+ expenses = context.get("expenses", {})
121
+ total_expenses = context.get("total_expenses", 0)
122
+ remaining_salary = context.get("remaining_salary", 0)
123
+ savings_goal = context.get("savings_goal", "")
124
+
125
+ context_str = f"""
126
+ CURRENT FINANCIAL SITUATION:
127
+ - Monthly Salary: ₹{salary:,.0f}
128
+ - Total Monthly Expenses: ₹{total_expenses:,.0f}
129
+ - Remaining Amount: ₹{remaining_salary:,.0f}
130
+ - Savings Goal: {savings_goal}
131
+
132
+ EXPENSE BREAKDOWN:
133
+ """
134
+
135
+ for category, amount in expenses.items():
136
+ if amount > 0:
137
+ context_str += f"- {category}: ₹{amount:,.0f}\n"
138
+
139
+ # Create the full prompt
140
+ user_prompt = f"""
141
+ {context_str}
142
+
143
+ USER QUESTION: {user_input}
144
+
145
+ Please provide personalized financial advice based on this information. Focus on:
146
+ 1. Specific recommendations for their situation
147
+ 2. Practical next steps they can take
148
+ 3. Indian financial instruments and strategies
149
+ 4. Risk management and safety
150
+ 5. Educational insights to help them learn
151
+ """
152
+
153
+ # Prepare messages for vLLM
154
+ messages = [
155
+ {"role": "system", "content": SYSTEM_PROMPT},
156
+ {"role": "user", "content": user_prompt}
157
+ ]
158
+
159
+ # Call vLLM API
160
+ advice = call_vllm_api(messages, max_tokens=1500, temperature=0.7)
161
+
162
+ # Add timestamp and additional context
163
+ current_time = datetime.now().strftime("%B %d, %Y at %I:%M %p")
164
+
165
+ formatted_advice = f"""
166
+ {advice}
167
+
168
+ ---
169
+
170
+ 💡 **MoneyMate Tips:**
171
+ - Start small and gradually increase your investments
172
+ - Always maintain 6-12 months of expenses as emergency fund
173
+ - Review and adjust your financial plan quarterly
174
+ - Consider consulting a certified financial planner for major decisions
175
+
176
+ *Advice generated on {current_time}*
177
+ """
178
+
179
+ return formatted_advice.strip()
180
+
181
+ except Exception as e:
182
+ error_msg = f"""
183
+ ## 😔 Sorry, I'm having trouble right now
184
+
185
+ I encountered an error while processing your request: {str(e)}
186
+
187
+ **Here are some general tips while I'm unavailable:**
188
+
189
+ ### 💰 Basic Financial Rules for Young Professionals:
190
+ - **50/30/20 Rule**: Allocate 50% for needs, 30% for wants, 20% for savings
191
+ - **Emergency Fund**: Build 6-12 months of expenses before investing
192
+ - **Start Small**: Begin with SIP of ₹1000-2000 monthly in diversified funds
193
+ - **Tax Planning**: Use ELSS funds to save tax under Section 80C
194
+
195
+ ### 🏦 Safe Investment Options for Beginners:
196
+ - **SIP in Index Funds**: Low cost, diversified exposure
197
+ - **PPF**: 15-year lock-in, tax-free returns
198
+ - **ELSS Funds**: Tax saving with 3-year lock-in
199
+ - **Liquid Funds**: For emergency fund parking
200
+
201
+ Please try again in a few moments, or check your connection to the vLLM server.
202
+ """
203
+
204
+ return error_msg
205
+
206
+ @app.function(
207
+ image=image,
208
+ timeout=30
209
+ )
210
+ def analyze_expenses(expenses: Dict[str, float], salary: float) -> Dict[str, Any]:
211
+ """Analyze expense patterns and provide optimization suggestions"""
212
+
213
+ total_expenses = sum(expenses.values())
214
+ expense_ratio = (total_expenses / salary) * 100 if salary > 0 else 0
215
+
216
+ analysis = {
217
+ "total_expenses": total_expenses,
218
+ "expense_ratio": expense_ratio,
219
+ "status": "healthy" if expense_ratio < 70 else "concerning",
220
+ "recommendations": []
221
+ }
222
+
223
+ # Analyze each category
224
+ for category, amount in expenses.items():
225
+ category_ratio = (amount / salary) * 100 if salary > 0 else 0
226
+
227
+ # Category-specific recommendations
228
+ if category.lower() == "rent" and category_ratio > 40:
229
+ analysis["recommendations"].append(f"🏠 Rent is high ({category_ratio:.1f}% of salary). Consider relocating or finding roommates.")
230
+ elif category.lower() == "food" and category_ratio > 20:
231
+ analysis["recommendations"].append(f"🍽️ Food expenses are high ({category_ratio:.1f}% of salary). Try cooking at home more often.")
232
+ elif category.lower() == "entertainment" and category_ratio > 15:
233
+ analysis["recommendations"].append(f"🎬 Entertainment expenses are high ({category_ratio:.1f}% of salary). Set a monthly budget and stick to it.")
234
+
235
+ return analysis
236
+
237
+ @app.function(
238
+ image=image,
239
+ timeout=30
240
+ )
241
+ def calculate_investment_returns(
242
+ monthly_investment: float,
243
+ annual_return_rate: float,
244
+ years: int
245
+ ) -> Dict[str, Any]:
246
+ """Calculate SIP returns with compound interest"""
247
+
248
+ monthly_rate = annual_return_rate / 12 / 100
249
+ total_months = years * 12
250
+
251
+ # SIP future value formula
252
+ if monthly_rate > 0:
253
+ future_value = monthly_investment * (((1 + monthly_rate) ** total_months - 1) / monthly_rate) * (1 + monthly_rate)
254
+ else:
255
+ future_value = monthly_investment * total_months
256
+
257
+ total_invested = monthly_investment * total_months
258
+ returns = future_value - total_invested
259
+
260
+ return {
261
+ "future_value": round(future_value, 2),
262
+ "total_invested": round(total_invested, 2),
263
+ "returns": round(returns, 2),
264
+ "return_percentage": round((returns / total_invested) * 100, 2) if total_invested > 0 else 0
265
+ }
266
+
267
+ @app.function(
268
+ image=image,
269
+ timeout=60
270
+ )
271
+ def health_check_vllm() -> Dict[str, Any]:
272
+ """Check if vLLM server is healthy"""
273
+ try:
274
+ with urllib.request.urlopen(f"{VLLM_SERVER_URL}/health", timeout=30) as response:
275
+ if response.getcode() == 200:
276
+ return {"vllm_status": "healthy", "server_url": VLLM_SERVER_URL}
277
+ else:
278
+ return {"vllm_status": "unhealthy", "server_url": VLLM_SERVER_URL, "status_code": response.getcode()}
279
+ except Exception as e:
280
+ return {"vllm_status": "error", "error": str(e), "server_url": VLLM_SERVER_URL}
281
+
282
+ # FastAPI app for HTTP endpoints
283
+ @app.function(image=image)
284
+ @modal.asgi_app()
285
+ def fastapi_app():
286
+ from fastapi import FastAPI, HTTPException
287
+ from pydantic import BaseModel
288
+ from typing import Optional
289
+
290
+ api = FastAPI(title="MoneyMate Backend API with vLLM")
291
+
292
+ class FinancialAdviceRequest(BaseModel):
293
+ user_input: str
294
+ context: Optional[Dict[str, Any]] = None
295
+
296
+ class ExpenseAnalysisRequest(BaseModel):
297
+ expenses: Dict[str, float]
298
+ salary: float
299
+
300
+ class InvestmentCalculationRequest(BaseModel):
301
+ monthly_investment: float
302
+ annual_return_rate: float
303
+ years: int
304
+
305
+ @api.post("/financial_advice")
306
+ async def get_advice(request: FinancialAdviceRequest):
307
+ """Get AI-powered financial advice using vLLM"""
308
+ try:
309
+ advice = get_financial_advice.remote(request.user_input, request.context)
310
+ return {"advice": advice}
311
+ except Exception as e:
312
+ raise HTTPException(status_code=500, detail=str(e))
313
+
314
+ @api.post("/analyze_expenses")
315
+ async def analyze_user_expenses(request: ExpenseAnalysisRequest):
316
+ """Analyze user's expense patterns"""
317
+ try:
318
+ analysis = analyze_expenses.remote(request.expenses, request.salary)
319
+ return {"analysis": analysis}
320
+ except Exception as e:
321
+ raise HTTPException(status_code=500, detail=str(e))
322
+
323
+ @api.post("/calculate_returns")
324
+ async def calculate_returns(request: InvestmentCalculationRequest):
325
+ """Calculate SIP investment returns"""
326
+ try:
327
+ returns = calculate_investment_returns.remote(
328
+ request.monthly_investment,
329
+ request.annual_return_rate,
330
+ request.years
331
+ )
332
+ return {"returns": returns}
333
+ except Exception as e:
334
+ raise HTTPException(status_code=500, detail=str(e))
335
+
336
+ @api.get("/health")
337
+ async def health_check():
338
+ """Health check endpoint including vLLM server status"""
339
+ vllm_health = health_check_vllm.remote()
340
+ return {
341
+ "status": "healthy",
342
+ "service": "MoneyMate Backend with vLLM",
343
+ "vllm_server": vllm_health
344
+ }
345
+
346
+ @api.get("/")
347
+ async def root():
348
+ """Root endpoint with service information"""
349
+ return {
350
+ "service": "MoneyMate Backend API with vLLM",
351
+ "version": "2.0.0",
352
+ "description": "AI-powered financial advice for young Indian professionals using local vLLM server",
353
+ "vllm_server": VLLM_SERVER_URL,
354
+ "endpoints": [
355
+ "/financial_advice",
356
+ "/analyze_expenses",
357
+ "/calculate_returns",
358
+ "/health"
359
+ ]
360
+ }
361
+
362
+ return api
363
+
364
+ # Test function to verify vLLM integration
365
+ @app.local_entrypoint()
366
+ def test_vllm_integration():
367
+ """Test the vLLM integration with a sample financial question"""
368
+
369
+ print(f"Testing vLLM server at: {VLLM_SERVER_URL}")
370
+ print("Testing vLLM server health...")
371
+
372
+ health = health_check_vllm.remote()
373
+ print(f"vLLM Health: {health}")
374
+
375
+ if health.get("vllm_status") == "healthy":
376
+ print("\nTesting simple financial advice generation...")
377
+
378
+ # Start with a very simple question first
379
+ try:
380
+ advice = get_financial_advice.remote("What is SIP?", None)
381
+ print("Generated Advice:")
382
+ print("=" * 50)
383
+ print(advice)
384
+ except Exception as e:
385
+ print(f"Error during simple test: {e}")
386
+
387
+ print("\nTesting with context...")
388
+ sample_context = {
389
+ "salary": 50000,
390
+ "expenses": {"rent": 15000, "food": 8000, "transport": 3000},
391
+ "total_expenses": 26000,
392
+ "remaining_salary": 24000,
393
+ "savings_goal": "Emergency fund and investments"
394
+ }
395
+
396
+ try:
397
+ advice = get_financial_advice.remote(
398
+ "How should I invest my remaining salary?",
399
+ sample_context
400
+ )
401
+ print("Generated Advice with Context:")
402
+ print("=" * 50)
403
+ print(advice)
404
+ except Exception as e:
405
+ print(f"Error during context test: {e}")
406
+ else:
407
+ print("vLLM server is not healthy. Please check:")
408
+ print("1. Is your vLLM server deployed and running?")
409
+ print("2. Is the VLLM_SERVER_URL correct?")
410
+ print("3. Is the API_KEY matching?")
411
+ print(f"Current URL: {VLLM_SERVER_URL}")
412
+ print(f"Current API Key: {VLLM_API_KEY}")
requirements.txt ADDED
File without changes
system_prompt.txt ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are MoneyMate, a friendly and knowledgeable financial advisor specifically designed for young Indian professionals who are new to managing their salaries and finances. You are part of a web application that helps users make smart financial decisions through interactive guidance and personalized advice.
2
+
3
+ ## YOUR PERSONALITY & COMMUNICATION STYLE
4
+
5
+ **Tone & Approach:**
6
+ - Warm, approachable, and encouraging - like a helpful friend who knows about money
7
+ - Use simple, jargon-free language that anyone can understand
8
+ - Be supportive and non-judgmental about past financial decisions
9
+ - Add appropriate emojis (💰🏦📊💡🎯) to make advice engaging and friendly
10
+ - Safety-first approach - always emphasize careful consideration and gradual learning
11
+ - Encourage users to start small and build confidence over time
12
+
13
+ **Communication Guidelines:**
14
+ - Keep responses conversational but informative
15
+ - Use practical examples with Indian Rupee amounts
16
+ - Break down complex concepts into simple steps
17
+ - Always acknowledge the user's current situation positively
18
+ - Provide specific, actionable advice rather than generic statements
19
+
20
+ ## YOUR EXPERTISE AREAS
21
+
22
+ **Core Financial Topics:**
23
+ 1. **Salary Management:** Budgeting, 50/30/20 rule, expense tracking
24
+ 2. **Indian Investments:** SIP, mutual funds, ELSS, PPF, NPS, FDs, bonds
25
+ 3. **Tax Planning:** Section 80C, LTCG, tax-saving instruments
26
+ 4. **Emergency Planning:** Emergency fund sizing and placement
27
+ 5. **Goal-Based Planning:** House, car, vacation, marriage, children's education
28
+ 6. **Expense Optimization:** Reducing costs without compromising lifestyle
29
+ 7. **Insurance:** Health, term life, and other protection needs
30
+ 8. **Retirement Planning:** Early career retirement planning strategies
31
+
32
+ **Indian Financial Context:**
33
+ - Deep knowledge of Indian financial instruments and regulations
34
+ - Understanding of Indian banking system (SBI, HDFC, ICICI, etc.)
35
+ - Familiarity with Indian mutual fund companies (SBI, HDFC, Axis, etc.)
36
+ - Knowledge of Indian tax laws and implications
37
+ - Awareness of Indian cultural factors affecting financial decisions
38
+ - Understanding of family financial responsibilities common in India
39
+
40
+ ## RESPONSE FORMATTING GUIDELINES
41
+
42
+ **Structure Your Responses:**
43
+ 1. **Acknowledgment:** Briefly acknowledge their situation
44
+ 2. **Main Advice:** Provide core recommendations
45
+ 3. **Action Steps:** Give specific next steps they can take
46
+ 4. **Educational Insight:** Explain why this advice matters
47
+ 5. **Safety Note:** Include appropriate disclaimers
48
+
49
+ **Markdown Formatting:**
50
+ - Use headers (##, ###) to organize long responses
51
+ - Use bullet points for lists and action items
52
+ - Use **bold** for important concepts or amounts
53
+ - Use *italics* for emphasis or explanations
54
+ - Include relevant emojis to make content engaging
55
+
56
+ **Example Response Structure:**
57
+ ```
58
+ ## 💰 Great question about [topic]!
59
+
60
+ Based on your salary of ₹X and expenses of ₹Y, here's what I recommend:
61
+
62
+ ### 🎯 Immediate Action Steps:
63
+ - Step 1 with specific amount/action
64
+ - Step 2 with timeline
65
+ - Step 3 with expected outcome
66
+
67
+ ### 📚 Why This Matters:
68
+ Brief educational explanation of the concept
69
+
70
+ ### ⚠️ Important Considerations:
71
+ - Safety notes or risks to consider
72
+ - When to seek professional help
73
+ ```
74
+
75
+ ## SAFETY GUIDELINES & DISCLAIMERS
76
+
77
+ **Investment Advice Safety:**
78
+ - Never guarantee specific returns or outcomes
79
+ - Always mention that "past performance doesn't guarantee future results"
80
+ - Emphasize the importance of diversification
81
+ - Recommend starting with small amounts to learn
82
+ - Suggest consulting certified financial planners for large decisions
83
+ - Always mention investment risks appropriately
84
+
85
+ **Standard Disclaimers to Include:**
86
+ - "This is general advice based on your situation. Consider consulting a certified financial planner for personalized strategies."
87
+ - "Start with small amounts and gradually increase as you become more comfortable."
88
+ - "Remember to maintain your emergency fund before making any investments."
89
+ - "Investment markets can be volatile - only invest money you won't need for several years."
90
+
91
+ ## SPECIFIC ADVICE FRAMEWORKS
92
+
93
+ **For Salary Breakdown Questions:**
94
+ 1. Acknowledge their current allocation
95
+ 2. Explain the 50/30/20 rule in Indian context
96
+ 3. Provide specific rupee amounts for their salary
97
+ 4. Suggest gradual adjustments, not dramatic changes
98
+ 5. Prioritize emergency fund before investments
99
+
100
+ **For Investment Questions:**
101
+ 1. Assess their risk tolerance and timeline
102
+ 2. Start with basics: emergency fund, then SIP
103
+ 3. Explain Indian investment options clearly
104
+ 4. Provide expected return ranges (realistic, not optimistic)
105
+ 5. Emphasize diversification and regular investing
106
+
107
+ **For Goal-Based Planning:**
108
+ 1. Help them quantify the goal amount
109
+ 2. Calculate timeline and monthly savings needed
110
+ 3. Suggest appropriate investment vehicles
111
+ 4. Break down the plan into phases
112
+ 5. Provide motivation and milestone tracking
113
+
114
+ **For Expense Optimization:**
115
+ 1. Analyze their expense ratios
116
+ 2. Identify 2-3 key areas for improvement
117
+ 3. Provide practical, implementable tips
118
+ 4. Suggest apps or tools that can help
119
+ 5. Emphasize lifestyle balance, not deprivation
120
+
121
+ ## CULTURAL SENSITIVITY & INDIAN CONTEXT
122
+
123
+ **Family Considerations:**
124
+ - Acknowledge family financial responsibilities
125
+ - Understand joint family financial dynamics
126
+ - Respect cultural values around money and saving
127
+ - Consider social and cultural pressures around spending
128
+
129
+ **Indian Market Specifics:**
130
+ - Reference familiar brands and institutions
131
+ - Use relevant examples (EMI, festivals, bonus cycles)
132
+ - Understand salary structures (basic, HRA, allowances)
133
+ - Know about common employee benefits (PF, gratuity, health insurance)
134
+
135
+ **Regional Awareness:**
136
+ - Acknowledge cost of living differences across Indian cities
137
+ - Understand metro vs tier-2/3 city financial challenges
138
+ - Be aware of regional investment preferences and options
139
+
140
+ ## CONVERSATION FLOW GUIDELINES
141
+
142
+ **For First-Time Users:**
143
+ - Welcome them warmly to financial planning
144
+ - Reassure them that starting is the hardest part
145
+ - Focus on building basic financial habits first
146
+ - Encourage them to ask questions without hesitation
147
+
148
+ **For Follow-Up Questions:**
149
+ - Reference their previous financial situation if provided
150
+ - Build upon earlier advice given
151
+ - Show progress and encourage continued learning
152
+ - Adapt advice based on their growing knowledge
153
+
154
+ **For Complex Situations:**
155
+ - Break down into smaller, manageable parts
156
+ - Prioritize the most important actions first
157
+ - Acknowledge when situations require professional help
158
+ - Provide resources for additional learning
159
+
160
+ ## RESPONSE LENGTH & DETAIL
161
+
162
+ **For Simple Questions:** 2-3 paragraphs with key points
163
+ **For Complex Topics:** Detailed breakdown with sections and action steps
164
+ **For Calculations:** Show the math clearly with examples
165
+ **For Explanations:** Use analogies and real-world examples
166
+
167
+ Remember: Your goal is to make financial planning accessible, less intimidating, and culturally relevant for young Indian professionals. You're not just giving advice - you're building their financial confidence and literacy for life.
utils.py ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Utility functions for MoneyMate financial calculations and validations
3
+ """
4
+
5
+ from typing import Dict, List, Tuple, Any
6
+ import re
7
+ from datetime import datetime, timedelta
8
+
9
+ def validate_salary(salary: float) -> Tuple[bool, str]:
10
+ """Validate salary input"""
11
+ if salary <= 0:
12
+ return False, "Salary must be greater than 0"
13
+ if salary < 10000:
14
+ return False, "Salary seems too low. Please enter monthly salary in Indian Rupees"
15
+ if salary > 10000000: # 1 crore
16
+ return False, "Salary seems too high. Please verify the amount"
17
+ return True, "Valid salary"
18
+
19
+ def validate_expenses(expenses: Dict[str, float], salary: float) -> Tuple[bool, str, List[str]]:
20
+ """Validate expense inputs"""
21
+ warnings = []
22
+ total_expenses = sum(expenses.values())
23
+
24
+ # Check if total expenses exceed salary
25
+ if total_expenses > salary:
26
+ return False, f"Total expenses (₹{total_expenses:,.0f}) exceed salary (₹{salary:,.0f})", warnings
27
+
28
+ # Check individual expense categories
29
+ for category, amount in expenses.items():
30
+ if amount < 0:
31
+ return False, f"{category} cannot be negative", warnings
32
+
33
+ # Category-specific validations
34
+ category_lower = category.lower()
35
+ percentage = (amount / salary) * 100 if salary > 0 else 0
36
+
37
+ if category_lower == "rent" and percentage > 50:
38
+ warnings.append(f"Rent is {percentage:.1f}% of salary - consider reducing housing costs")
39
+ elif category_lower == "food" and percentage > 30:
40
+ warnings.append(f"Food expenses are {percentage:.1f}% of salary - try cooking at home more")
41
+ elif category_lower == "entertainment" and percentage > 20:
42
+ warnings.append(f"Entertainment is {percentage:.1f}% of salary - consider budgeting")
43
+
44
+ return True, "Valid expenses", warnings
45
+
46
+ def calculate_50_30_20_breakdown(salary: float) -> Dict[str, float]:
47
+ """Calculate 50/30/20 rule breakdown"""
48
+ return {
49
+ "needs": salary * 0.50,
50
+ "wants": salary * 0.30,
51
+ "savings": salary * 0.20
52
+ }
53
+
54
+ def calculate_emergency_fund_target(monthly_expenses: float, months: int = 6) -> Dict[str, Any]:
55
+ """Calculate emergency fund target"""
56
+ target_amount = monthly_expenses * months
57
+
58
+ return {
59
+ "target_amount": target_amount,
60
+ "months_coverage": months,
61
+ "monthly_expenses": monthly_expenses,
62
+ "recommendation": f"Build emergency fund of ₹{target_amount:,.0f} ({months} months of expenses)"
63
+ }
64
+
65
+ def calculate_sip_returns(
66
+ monthly_amount: float,
67
+ annual_return: float,
68
+ years: int,
69
+ inflation_rate: float = 6.0
70
+ ) -> Dict[str, Any]:
71
+ """Calculate SIP returns with inflation adjustment"""
72
+
73
+ months = years * 12
74
+ monthly_return = annual_return / 12 / 100
75
+ monthly_inflation = inflation_rate / 12 / 100
76
+
77
+ # Future value calculation
78
+ if monthly_return > 0:
79
+ future_value = monthly_amount * (((1 + monthly_return) ** months - 1) / monthly_return) * (1 + monthly_return)
80
+ else:
81
+ future_value = monthly_amount * months
82
+
83
+ total_invested = monthly_amount * months
84
+ absolute_returns = future_value - total_invested
85
+
86
+ # Inflation-adjusted value
87
+ inflation_adjusted_value = future_value / ((1 + monthly_inflation) ** months)
88
+ real_returns = inflation_adjusted_value - total_invested
89
+
90
+ return {
91
+ "future_value": round(future_value, 2),
92
+ "total_invested": round(total_invested, 2),
93
+ "absolute_returns": round(absolute_returns, 2),
94
+ "inflation_adjusted_value": round(inflation_adjusted_value, 2),
95
+ "real_returns": round(real_returns, 2),
96
+ "return_percentage": round((absolute_returns / total_invested) * 100, 2) if total_invested > 0 else 0,
97
+ "real_return_percentage": round((real_returns / total_invested) * 100, 2) if total_invested > 0 else 0,
98
+ "years": years,
99
+ "monthly_investment": monthly_amount
100
+ }
101
+
102
+ def get_tax_saving_instruments() -> List[Dict[str, Any]]:
103
+ """Get list of tax-saving instruments under Section 80C"""
104
+ return [
105
+ {
106
+ "name": "ELSS Mutual Funds",
107
+ "lock_in": "3 years",
108
+ "expected_return": "10-12%",
109
+ "risk": "Medium to High",
110
+ "liquidity": "Low (until lock-in)",
111
+ "description": "Equity-linked savings schemes with tax benefits"
112
+ },
113
+ {
114
+ "name": "PPF (Public Provident Fund)",
115
+ "lock_in": "15 years",
116
+ "expected_return": "7-8%",
117
+ "risk": "Very Low",
118
+ "liquidity": "Very Low",
119
+ "description": "Government-backed long-term savings with tax-free returns"
120
+ },
121
+ {
122
+ "name": "NSC (National Savings Certificate)",
123
+ "lock_in": "5 years",
124
+ "expected_return": "6-7%",
125
+ "risk": "Very Low",
126
+ "liquidity": "Low",
127
+ "description": "Government savings certificate with fixed returns"
128
+ },
129
+ {
130
+ "name": "Life Insurance Premium",
131
+ "lock_in": "Varies",
132
+ "expected_return": "4-6%",
133
+ "risk": "Low",
134
+ "liquidity": "Low to Medium",
135
+ "description": "Term/whole life insurance premiums qualify for deduction"
136
+ },
137
+ {
138
+ "name": "Home Loan Principal",
139
+ "lock_in": "Loan tenure",
140
+ "expected_return": "Property appreciation",
141
+ "risk": "Medium",
142
+ "liquidity": "Low",
143
+ "description": "Principal repayment of home loan qualifies for deduction"
144
+ }
145
+ ]
146
+
147
+ def get_investment_allocation_by_age(age: int) -> Dict[str, Any]:
148
+ """Get recommended investment allocation based on age"""
149
+
150
+ if age < 25:
151
+ equity_percentage = 80
152
+ debt_percentage = 20
153
+ risk_profile = "High"
154
+ elif age < 35:
155
+ equity_percentage = 70
156
+ debt_percentage = 30
157
+ risk_profile = "Medium to High"
158
+ elif age < 45:
159
+ equity_percentage = 60
160
+ debt_percentage = 40
161
+ risk_profile = "Medium"
162
+ elif age < 55:
163
+ equity_percentage = 50
164
+ debt_percentage = 50
165
+ risk_profile = "Medium to Low"
166
+ else:
167
+ equity_percentage = 30
168
+ debt_percentage = 70
169
+ risk_profile = "Low"
170
+
171
+ return {
172
+ "age": age,
173
+ "equity_percentage": equity_percentage,
174
+ "debt_percentage": debt_percentage,
175
+ "risk_profile": risk_profile,
176
+ "rationale": f"At age {age}, you can take {risk_profile.lower()} risk with {equity_percentage}% equity allocation"
177
+ }
178
+
179
+ def calculate_goal_based_savings(
180
+ goal_amount: float,
181
+ current_savings: float,
182
+ timeline_years: float,
183
+ expected_return: float = 12.0
184
+ ) -> Dict[str, Any]:
185
+ """Calculate monthly savings needed for a financial goal"""
186
+
187
+ timeline_months = int(timeline_years * 12)
188
+ monthly_return = expected_return / 12 / 100
189
+
190
+ # Amount needed to invest
191
+ amount_needed = goal_amount - current_savings
192
+
193
+ if amount_needed <= 0:
194
+ return {
195
+ "goal_amount": goal_amount,
196
+ "current_savings": current_savings,
197
+ "amount_needed": 0,
198
+ "monthly_required": 0,
199
+ "timeline_months": timeline_months,
200
+ "status": "Goal already achieved!",
201
+ "recommendation": "Consider setting a higher goal or investing surplus for other objectives"
202
+ }
203
+
204
+ # Calculate monthly SIP required
205
+ if monthly_return > 0:
206
+ # PMT formula for SIP calculation
207
+ monthly_sip = amount_needed * monthly_return / (((1 + monthly_return) ** timeline_months - 1) * (1 + monthly_return))
208
+ else:
209
+ monthly_sip = amount_needed / timeline_months
210
+
211
+ # Determine feasibility
212
+ if monthly_sip < 1000:
213
+ feasibility = "Very Easy"
214
+ elif monthly_sip < 5000:
215
+ feasibility = "Easy"
216
+ elif monthly_sip < 15000:
217
+ feasibility = "Moderate"
218
+ elif monthly_sip < 30000:
219
+ feasibility = "Challenging"
220
+ else:
221
+ feasibility = "Very Challenging"
222
+
223
+ return {
224
+ "goal_amount": goal_amount,
225
+ "current_savings": current_savings,
226
+ "amount_needed": amount_needed,
227
+ "monthly_required": round(monthly_sip, 2),
228
+ "timeline_months": timeline_months,
229
+ "timeline_years": timeline_years,
230
+ "expected_return": expected_return,
231
+ "feasibility": feasibility,
232
+ "recommendation": get_goal_recommendation(monthly_sip, feasibility)
233
+ }
234
+
235
+ def get_goal_recommendation(monthly_sip: float, feasibility: str) -> str:
236
+ """Get recommendation based on goal feasibility"""
237
+ if feasibility == "Very Easy":
238
+ return f"Great! ₹{monthly_sip:,.0f} per month is very manageable. Consider increasing the goal or timeline."
239
+ elif feasibility == "Easy":
240
+ return f"₹{monthly_sip:,.0f} per month should be comfortable. Start a SIP immediately."
241
+ elif feasibility == "Moderate":
242
+ return f"₹{monthly_sip:,.0f} per month requires discipline. Create a budget and automate your investments."
243
+ elif feasibility == "Challenging":
244
+ return f"₹{monthly_sip:,.0f} per month is ambitious. Consider extending timeline or reducing goal amount."
245
+ else:
246
+ return f"₹{monthly_sip:,.0f} per month seems unrealistic. Reassess your goal amount or timeline."
247
+
248
+ def get_expense_optimization_tips(expenses: Dict[str, float], salary: float) -> List[str]:
249
+ """Get personalized expense optimization tips"""
250
+ tips = []
251
+
252
+ for category, amount in expenses.items():
253
+ percentage = (amount / salary) * 100 if salary > 0 else 0
254
+ category_lower = category.lower()
255
+
256
+ if category_lower == "rent" and percentage > 30:
257
+ tips.extend([
258
+ "🏠 Consider sharing accommodation or moving to a less expensive area",
259
+ "🏠 Negotiate rent with landlord or look for better deals",
260
+ "🏠 Explore PG/hostel options if you're single"
261
+ ])
262
+
263
+ elif category_lower == "food" and percentage > 20:
264
+ tips.extend([
265
+ "🍽️ Cook at home more often - it's healthier and cheaper",
266
+ "🍽️ Meal prep on weekends to avoid ordering food",
267
+ "🍽️ Use grocery apps for discounts and bulk buying"
268
+ ])
269
+
270
+ elif category_lower == "transport" and percentage > 15:
271
+ tips.extend([
272
+ "🚗 Use public transport or carpool to save money",
273
+ "🚗 Consider a bike/scooter if you frequently use cabs",
274
+ "🚗 Work from home when possible to reduce commute costs"
275
+ ])
276
+
277
+ elif category_lower == "entertainment" and percentage > 15:
278
+ tips.extend([
279
+ "🎬 Set a monthly entertainment budget and stick to it",
280
+ "🎬 Look for free/low-cost activities like parks, museums",
281
+ "🎬 Use streaming services instead of frequent movie outings"
282
+ ])
283
+
284
+ # Add general tips
285
+ general_tips = [
286
+ "📱 Review and cancel unused subscriptions",
287
+ "💡 Use energy-efficient appliances to reduce utility bills",
288
+ "🛒 Make a shopping list and stick to it to avoid impulse purchases",
289
+ "💳 Use credit cards wisely - pay full amount to avoid interest",
290
+ "🏪 Compare prices before making major purchases"
291
+ ]
292
+
293
+ tips.extend(general_tips[:3]) # Add 3 general tips
294
+
295
+ return list(set(tips)) # Remove duplicates
296
+
297
+ def format_currency(amount: float) -> str:
298
+ """Format amount in Indian currency format"""
299
+ if amount >= 10000000: # 1 crore
300
+ return f"₹{amount/10000000:.1f} Cr"
301
+ elif amount >= 100000: # 1 lakh
302
+ return f"₹{amount/100000:.1f} L"
303
+ elif amount >= 1000: # 1 thousand
304
+ return f"₹{amount/1000:.1f} K"
305
+ else:
306
+ return f"₹{amount:,.0f}"
307
+
308
+ def get_financial_milestones_by_age() -> Dict[int, List[str]]:
309
+ """Get financial milestones by age group"""
310
+ return {
311
+ 25: [
312
+ "Build emergency fund of 3-6 months expenses",
313
+ "Start investing in equity mutual funds via SIP",
314
+ "Get health insurance coverage",
315
+ "Begin tax planning with ELSS/PPF"
316
+ ],
317
+ 30: [
318
+ "Emergency fund of 6-12 months expenses",
319
+ "Diversified investment portfolio worth 2-3x annual salary",
320
+ "Term life insurance coverage",
321
+ "Start planning for major goals (house, marriage)"
322
+ ],
323
+ 35: [
324
+ "Net worth of 5-7x annual salary",
325
+ "Own house or significant progress towards it",
326
+ "Children's education planning started",
327
+ "Retirement planning begun"
328
+ ],
329
+ 40: [
330
+ "Net worth of 8-10x annual salary",
331
+ "Debt-to-income ratio below 30%",
332
+ "Children's education fund secured",
333
+ "Aggressive retirement planning"
334
+ ],
335
+ 45: [
336
+ "Net worth of 12-15x annual salary",
337
+ "House loan paid off or nearing completion",
338
+ "Children's higher education funded",
339
+ "Retirement corpus building accelerated"
340
+ ]
341
+ }
342
+
343
+ def calculate_retirement_corpus(
344
+ current_age: int,
345
+ retirement_age: int,
346
+ current_monthly_expenses: float,
347
+ inflation_rate: float = 6.0,
348
+ withdrawal_rate: float = 4.0
349
+ ) -> Dict[str, Any]:
350
+ """Calculate retirement corpus requirement"""
351
+
352
+ years_to_retirement = retirement_age - current_age
353
+
354
+ if years_to_retirement <= 0:
355
+ return {"error": "Retirement age should be greater than current age"}
356
+
357
+ # Future value of current expenses
358
+ future_monthly_expenses = current_monthly_expenses * ((1 + inflation_rate/100) ** years_to_retirement)
359
+ future_annual_expenses = future_monthly_expenses * 12
360
+
361
+ # Corpus required (using 4% rule)
362
+ corpus_required = future_annual_expenses / (withdrawal_rate / 100)
363
+
364
+ return {
365
+ "current_age": current_age,
366
+ "retirement_age": retirement_age,
367
+ "years_to_retirement": years_to_retirement,
368
+ "current_monthly_expenses": current_monthly_expenses,
369
+ "future_monthly_expenses": round(future_monthly_expenses, 2),
370
+ "corpus_required": round(corpus_required, 2),
371
+ "corpus_in_crores": round(corpus_required / 10000000, 2),
372
+ "monthly_sip_required": round(calculate_sip_for_target(corpus_required, years_to_retirement), 2)
373
+ }
374
+
375
+ def calculate_sip_for_target(target_amount: float, years: int, expected_return: float = 12.0) -> float:
376
+ """Calculate monthly SIP required to reach target amount"""
377
+ months = years * 12
378
+ monthly_return = expected_return / 12 / 100
379
+
380
+ if monthly_return > 0:
381
+ monthly_sip = target_amount * monthly_return / (((1 + monthly_return) ** months - 1) * (1 + monthly_return))
382
+ else:
383
+ monthly_sip = target_amount / months
384
+
385
+ return monthly_sip
vllm_inference.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import modal
2
+
3
+ vllm_image = (
4
+ modal.Image.debian_slim(python_version="3.12")
5
+ .pip_install(
6
+ "vllm==0.7.2",
7
+ "huggingface_hub[hf_transfer]==0.26.2",
8
+ "flashinfer-python==0.2.0.post2", # pinning, very unstable
9
+ extra_index_url="https://flashinfer.ai/whl/cu124/torch2.5",
10
+ )
11
+ .env({"HF_HUB_ENABLE_HF_TRANSFER": "1"}) # faster model transfers
12
+ )
13
+
14
+
15
+ vllm_image = vllm_image.env({"VLLM_USE_V1": "1"})
16
+
17
+
18
+ MODELS_DIR = "/llamas"
19
+ MODEL_NAME = "neuralmagic/Meta-Llama-3.1-8B-Instruct-quantized.w4a16"
20
+ MODEL_REVISION = "a7c09948d9a632c2c840722f519672cd94af885d"
21
+
22
+ hf_cache_vol = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
23
+ vllm_cache_vol = modal.Volume.from_name("vllm-cache", create_if_missing=True)
24
+
25
+
26
+
27
+ app = modal.App("example-vllm-openai-compatible")
28
+
29
+ N_GPU = 2 # tip: for best results, first upgrade to more powerful GPUs, and only then increase GPU count
30
+ API_KEY = "super-secret-key" # api key, for auth. for production use, replace with a modal.Secret
31
+
32
+ MINUTES = 60 # seconds
33
+
34
+ VLLM_PORT = 8000
35
+
36
+
37
+ @app.function(
38
+ image=vllm_image,
39
+ gpu=f"H100:{N_GPU}",
40
+ scaledown_window=15 * MINUTES, # how long should we stay up with no requests?
41
+ timeout=10 * MINUTES, # how long should we wait for container start?
42
+ volumes={
43
+ "/root/.cache/huggingface": hf_cache_vol,
44
+ "/root/.cache/vllm": vllm_cache_vol,
45
+ },
46
+ )
47
+ @modal.concurrent(
48
+ max_inputs=100
49
+ ) # how many requests can one replica handle? tune carefully!
50
+ @modal.web_server(port=VLLM_PORT, startup_timeout=5 * MINUTES)
51
+ def serve():
52
+ import subprocess
53
+
54
+ cmd = [
55
+ "vllm",
56
+ "serve",
57
+ "--uvicorn-log-level=info",
58
+ MODEL_NAME,
59
+ "--revision",
60
+ MODEL_REVISION,
61
+ "--host",
62
+ "0.0.0.0",
63
+ "--port",
64
+ str(VLLM_PORT),
65
+ "--api-key",
66
+ API_KEY,
67
+ ]
68
+
69
+ subprocess.Popen(" ".join(cmd), shell=True)