Aashu1308 commited on
Commit
391b9f9
·
1 Parent(s): 577d85e

Added app model requirements and dockerfile without .env

Browse files
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .env
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM docker.io/tensorflow/tensorflow:2.18.0
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y python3-pip && rm -rf /var/lib/apt/lists/*
6
+
7
+ COPY requirements.txt .
8
+
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ COPY app_web.py .
12
+
13
+ COPY model/ ./model/
14
+
15
+ EXPOSE 7860
16
+
17
+ CMD ["python", "app_web.py"]
app.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pandas as pd
3
+ from tensorflow.keras.models import load_model
4
+ import joblib
5
+ from openai import OpenAI
6
+ from dotenv import load_dotenv
7
+ import os
8
+ import json
9
+ import gradio as gr
10
+ import logging
11
+
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+
15
+ load_dotenv()
16
+ API = os.environ.get("OPENROUTER_API_KEY")
17
+ logger.info(f"API Key loaded: {bool(API_KEY)}")
18
+
19
+
20
+ # Baselines
21
+ BASELINE_LOWER = {
22
+ 'Household': 30,
23
+ 'Food': 40,
24
+ 'Shopping': 7,
25
+ 'Transportation': 5,
26
+ 'Health & Fitness': 5,
27
+ 'Entertainment': 5,
28
+ 'Beauty': 4,
29
+ 'Investment': 4,
30
+ }
31
+ BASELINE_UPPER = {
32
+ 'Household': 11,
33
+ 'Food': 10,
34
+ 'Shopping': 13,
35
+ 'Transportation': 11,
36
+ 'Health & Fitness': 10,
37
+ 'Entertainment': 18,
38
+ 'Beauty': 8,
39
+ 'Investment': 19,
40
+ }
41
+
42
+
43
+ # Load model and scaler
44
+ def load_financial_model(
45
+ model_path='model/fuzz_dnn_full_model.keras',
46
+ scaler_path='model/fuzzy_dnn_scaler.pkl',
47
+ ):
48
+ logger.info("Starting to load model and scaler")
49
+ try:
50
+ model = load_model(model_path)
51
+ scaler = joblib.load(scaler_path)
52
+ logger.info("Model and scaler loaded successfully")
53
+ return model, scaler
54
+ except Exception as e:
55
+ print(f"Error loading model or scaler: {e}")
56
+ logger.error(f"Error loading model or scaler: {e}")
57
+ return None, None
58
+
59
+
60
+ # Prepare features
61
+ def prepare_features(df):
62
+ df['spend_deviation_ratio'] = df['Percent_Spend'] / (df['Deviation'].abs() + 1)
63
+ return df[['Percent_Spend', 'Deviation', 'spend_deviation_ratio']]
64
+
65
+
66
+ # Determine income level
67
+ def determine_income_level(total_spending):
68
+ return 'upper' if total_spending >= 5000 else 'lower'
69
+
70
+
71
+ # Predict spending pattern
72
+ def predict_spending_pattern(model, scaler, input_data):
73
+ total_spending = sum(input_data.values())
74
+ income_level = determine_income_level(total_spending)
75
+ baseline = BASELINE_UPPER if income_level == 'upper' else BASELINE_LOWER
76
+
77
+ percent_spend = {k: (v / total_spending) * 100 for k, v in input_data.items()}
78
+ rows = []
79
+ for category, spend_percent in percent_spend.items():
80
+ deviation = spend_percent - baseline.get(category, 0)
81
+ rows.append(
82
+ {
83
+ 'Category': category,
84
+ 'Percent_Spend': spend_percent,
85
+ 'Deviation': deviation,
86
+ }
87
+ )
88
+
89
+ pred_df = pd.DataFrame(rows)
90
+ X = prepare_features(pred_df)
91
+ X_scaled = scaler.transform(X)
92
+ predictions = model.predict(X_scaled, verbose=0)
93
+
94
+ results = pd.DataFrame(
95
+ {
96
+ 'Category': pred_df['Category'],
97
+ 'Percent_Spend': pred_df['Percent_Spend'],
98
+ 'Deviation': pred_df['Deviation'],
99
+ 'Raw_Score': predictions.flatten(),
100
+ 'Prediction': ['Good' if pred >= 0.6 else 'Bad' for pred in predictions],
101
+ }
102
+ )
103
+ return (
104
+ results.sort_values('Percent_Spend', ascending=False),
105
+ total_spending,
106
+ income_level,
107
+ )
108
+
109
+
110
+ # Suggest spending pattern
111
+ def suggest_spending_pattern(results, total_spending, input_data, income_level):
112
+ results = results.copy()
113
+ suggested_spending = {}
114
+ bad_categories = results[results['Prediction'] == 'Bad']
115
+ good_categories = results[results['Prediction'] == 'Good']
116
+ baseline = BASELINE_UPPER if income_level == 'upper' else BASELINE_LOWER
117
+
118
+ if not bad_categories.empty:
119
+ total_to_redistribute = sum(
120
+ input_data[row['Category']]
121
+ * min(max(abs(row['Deviation']) * 0.1, 0.25), 0.50)
122
+ for _, row in bad_categories.iterrows()
123
+ if row['Category'] not in ['Household', 'Food']
124
+ )
125
+ good_total = sum(input_data[cat] for cat in good_categories['Category'])
126
+ distribution_weights = {
127
+ cat: input_data[cat] / good_total if good_total > 0 else 0
128
+ for cat in good_categories['Category']
129
+ }
130
+
131
+ for category in input_data:
132
+ original = float(input_data[category])
133
+ baseline_dollars = total_spending * (baseline[category] / 100)
134
+ if category in bad_categories['Category'].values and category not in [
135
+ 'Household',
136
+ 'Food',
137
+ ]:
138
+ reduction = min(
139
+ max(
140
+ abs(
141
+ results[results['Category'] == category][
142
+ 'Deviation'
143
+ ].values[0]
144
+ )
145
+ * 0.1,
146
+ 0.25,
147
+ ),
148
+ 0.50,
149
+ )
150
+ suggested = original * (1 - reduction)
151
+ else:
152
+ weight = distribution_weights.get(category, 0)
153
+ increase = total_to_redistribute * weight
154
+ suggested = max(
155
+ original + increase,
156
+ baseline_dollars if category in ['Household', 'Food'] else original,
157
+ )
158
+ suggested_spending[category] = (original, round(suggested, 2))
159
+ else:
160
+ suggested_spending = {
161
+ cat: (float(val), float(val)) for cat, val in input_data.items()
162
+ }
163
+ return suggested_spending
164
+
165
+
166
+ # Format for Mistral
167
+ def format_for_mistral(
168
+ results, suggested_spending, total_spending, income_level, input_data
169
+ ):
170
+ return {
171
+ "total_spending": total_spending,
172
+ "income_level": income_level,
173
+ "categories": [
174
+ {
175
+ "category": row['Category'],
176
+ "percent_spend": round(row['Percent_Spend'], 2),
177
+ "actual_dollars": round(input_data[row['Category']], 2),
178
+ "deviation": round(row['Deviation'], 2),
179
+ "prediction": row['Prediction'],
180
+ "suggested_dollars": suggested_spending[row['Category']][1],
181
+ }
182
+ for _, row in results.iterrows()
183
+ ],
184
+ }
185
+
186
+
187
+ # Get spending summary (Mistral API call)
188
+ def get_spending_summary(spending_data):
189
+ client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=API)
190
+ analysis_prompt = f"""
191
+ You are a financial counselor analyzing a ${spending_data['total_spending']} monthly budget for a {spending_data['income_level']} income individual. Follow these strict rules:
192
+ ### Financial Literacy Summary
193
+ #### Praise
194
+ For each 'Good' category:
195
+ ⚠️ **Only show if ALL conditions met:**
196
+ - `prediction` = 'Good'
197
+ - `abs(deviation)` < 2%
198
+ ✅ **{{category}} (${{actual_dollars}})** -
199
+ Explain using:
200
+ 1. "% vs baseline: {{percent_spend}}% ({{deviation:+.2f}}% vs {{baseline}}%)"
201
+ 2. Practical benefit
202
+ 3. Savings impact ONLY if `deviation` > 0
203
+ #### Suggestions
204
+ ⚠️ **Only show if ALL conditions met:**
205
+ - `prediction` = 'Bad'
206
+ - `abs(deviation)` > 2%
207
+ - `suggested_dollars` ≠ `actual_dollars`
208
+ For each 'Bad' category:
209
+ ⚠️ **{{category}} (${{actual_dollars}} → ${{suggested_dollars}})** -
210
+ Structure as:
211
+ 1. If suggested INCREASE: "Prioritize {{category}} by adding ${{suggested_dollars - actual_dollars}}..."
212
+ 2. If suggested DECREASE: "Reduce {{category}} by ${{actual_dollars - suggested_dollars}}..."
213
+ #### Key Principle
214
+ Identify the MOST URGENT issue using largest absolute deviation...
215
+ **Baseline Reference ({spending_data['income_level'].capitalize()} Income):**
216
+ {'Food (10%), Household (11%), Shopping (13%), Transportation (11%), Health & Fitness (10%), Entertainment (18%), Beauty (8%), Investment (19%)' if spending_data['income_level'] == 'upper' else 'Food (40%), Household (30%), Shopping (7%), Transportation (5%), Health & Fitness (5%), Entertainment (5%), Beauty (4%), Investment (4%)'}
217
+ **Data:**
218
+ {json.dumps(spending_data, indent=2)}
219
+ **Begin Analysis:**
220
+ """
221
+ try:
222
+ response = client.chat.completions.create(
223
+ model="mistralai/mistral-small-24b-instruct-2501:free",
224
+ messages=[{"role": "user", "content": analysis_prompt}],
225
+ temperature=0.5,
226
+ )
227
+ return response.choices[0].message.content
228
+ except Exception as e:
229
+ return f"Error calling Mistral API: {e}"
230
+
231
+
232
+ # Gradio interface function
233
+ def analyze_spending(
234
+ household,
235
+ food,
236
+ shopping,
237
+ transportation,
238
+ health_fitness,
239
+ entertainment,
240
+ beauty,
241
+ investment,
242
+ ):
243
+ input_data = {
244
+ 'Household': float(household),
245
+ 'Food': float(food),
246
+ 'Shopping': float(shopping),
247
+ 'Transportation': float(transportation),
248
+ 'Health & Fitness': float(health_fitness),
249
+ 'Entertainment': float(entertainment),
250
+ 'Beauty': float(beauty),
251
+ 'Investment': float(investment),
252
+ }
253
+ logger.info("Before loading model")
254
+ model, scaler = load_financial_model()
255
+ logger.info("After loading model")
256
+ if model is None or scaler is None:
257
+ return "Error: Model or scaler failed to load.", None, None, None
258
+
259
+ results, total_spending, income_level = predict_spending_pattern(
260
+ model, scaler, input_data
261
+ )
262
+ suggested_spending = suggest_spending_pattern(
263
+ results, total_spending, input_data, income_level
264
+ )
265
+ spending_data = format_for_mistral(
266
+ results, suggested_spending, total_spending, income_level, input_data
267
+ )
268
+ summary = get_spending_summary(spending_data)
269
+
270
+ # Format suggested adjustments as a DataFrame
271
+ suggested_df = pd.DataFrame(
272
+ [(cat, orig, sugg) for cat, (orig, sugg) in suggested_spending.items()],
273
+ columns=['Category', 'Original ($)', 'Suggested ($)'],
274
+ )
275
+
276
+ return (
277
+ f"## Spending Analysis ({income_level.capitalize()} Income)\nTotal Spending: ${total_spending:.2f}",
278
+ results, # For DataFrame display
279
+ suggested_df, # For DataFrame display
280
+ summary, # Financial summary
281
+ )
282
+
283
+
284
+ # Gradio UI
285
+ logger.info("Setting up Gradio interface")
286
+ with gr.Blocks(
287
+ title="Personal Finance Assistant", css=".gr-button {margin-top: 10px}"
288
+ ) as demo:
289
+ gr.Markdown("# Personal Finance Assistant")
290
+ gr.Markdown("Enter your monthly spending in each category ($):")
291
+ with gr.Row():
292
+ household = gr.Textbox(label="Household", value="500")
293
+ food = gr.Textbox(label="Food", value="100")
294
+ shopping = gr.Textbox(label="Shopping", value="950")
295
+ transportation = gr.Textbox(label="Transportation", value="100")
296
+ with gr.Row():
297
+ health_fitness = gr.Textbox(label="Health & Fitness", value="200")
298
+ entertainment = gr.Textbox(label="Entertainment", value="200")
299
+ beauty = gr.Textbox(label="Beauty", value="100")
300
+ investment = gr.Textbox(label="Investment", value="100")
301
+
302
+ submit_btn = gr.Button("Analyze")
303
+
304
+ # Output components
305
+ with gr.Column():
306
+ loading = gr.Markdown("### Analysis Results\n*Waiting for input...*")
307
+ title = gr.Markdown()
308
+ current_spending = gr.DataFrame(label="Current Spending")
309
+ suggested_adjustments = gr.DataFrame(label="Suggested Adjustments")
310
+ financial_summary = gr.Markdown()
311
+
312
+ # Handle click with loading state
313
+ def start_loading():
314
+ return "### Analysis Results\n*Processing your spending data...*"
315
+
316
+ submit_btn.click(fn=start_loading, inputs=None, outputs=loading).then(
317
+ fn=analyze_spending,
318
+ inputs=[
319
+ household,
320
+ food,
321
+ shopping,
322
+ transportation,
323
+ health_fitness,
324
+ entertainment,
325
+ beauty,
326
+ investment,
327
+ ],
328
+ outputs=[title, current_spending, suggested_adjustments, financial_summary],
329
+ queue=True,
330
+ )
331
+ logger.info("Launching Gradio server")
332
+ demo.launch(server_name="0.0.0.0", server_port=7860)
model/fuzz_dnn_full_model.keras ADDED
Binary file (192 kB). View file
 
model/fuzzy_dnn_scaler.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9561bf7e8c89b9a9d36ff8cd06d9537808d297d6f93a334a628bfe542d51a0a1
3
+ size 1039
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ numpy
2
+ pandas
3
+ joblib
4
+ openai
5
+ python-dotenv
6
+ gradio
7
+ scikit-learn