Aashu1308
minor correction
4c53aee
import numpy as np
import pandas as pd
from tensorflow.keras.models import load_model
import joblib
from openai import OpenAI
from dotenv import load_dotenv
import os
import json
import gradio as gr
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
load_dotenv()
API = os.environ.get("OPENROUTER_API_KEY")
logger.info(f"API Key loaded: {bool(API)}")
# Baselines
BASELINE_LOWER = {
'Household': 30,
'Food': 40,
'Shopping': 7,
'Transportation': 5,
'Health & Fitness': 5,
'Entertainment': 5,
'Beauty': 4,
'Investment': 4,
}
BASELINE_UPPER = {
'Household': 11,
'Food': 10,
'Shopping': 13,
'Transportation': 11,
'Health & Fitness': 10,
'Entertainment': 18,
'Beauty': 8,
'Investment': 19,
}
# Load model and scaler
def load_financial_model(
model_path='model/fuzz_dnn_full_model.keras',
scaler_path='model/fuzzy_dnn_scaler.pkl',
):
logger.info("Starting to load model and scaler")
try:
model = load_model(model_path)
scaler = joblib.load(scaler_path)
logger.info("Model and scaler loaded successfully")
return model, scaler
except Exception as e:
print(f"Error loading model or scaler: {e}")
logger.error(f"Error loading model or scaler: {e}")
return None, None
# Prepare features
def prepare_features(df):
df['spend_deviation_ratio'] = df['Percent_Spend'] / (df['Deviation'].abs() + 1)
return df[['Percent_Spend', 'Deviation', 'spend_deviation_ratio']]
# Determine income level
def determine_income_level(total_spending):
return 'upper' if total_spending >= 5000 else 'lower'
# Predict spending pattern
def predict_spending_pattern(model, scaler, input_data):
total_spending = sum(input_data.values())
income_level = determine_income_level(total_spending)
baseline = BASELINE_UPPER if income_level == 'upper' else BASELINE_LOWER
percent_spend = {k: (v / total_spending) * 100 for k, v in input_data.items()}
rows = []
for category, spend_percent in percent_spend.items():
deviation = spend_percent - baseline.get(category, 0)
rows.append(
{
'Category': category,
'Percent_Spend': spend_percent,
'Deviation': deviation,
}
)
pred_df = pd.DataFrame(rows)
X = prepare_features(pred_df)
X_scaled = scaler.transform(X)
predictions = model.predict(X_scaled, verbose=0)
results = pd.DataFrame(
{
'Category': pred_df['Category'],
'Percent_Spend': pred_df['Percent_Spend'],
'Deviation': pred_df['Deviation'],
'Raw_Score': predictions.flatten(),
'Prediction': ['Good' if pred >= 0.6 else 'Bad' for pred in predictions],
}
)
return (
results.sort_values('Percent_Spend', ascending=False),
total_spending,
income_level,
)
# Suggest spending pattern
def suggest_spending_pattern(results, total_spending, input_data, income_level):
results = results.copy()
suggested_spending = {}
bad_categories = results[results['Prediction'] == 'Bad']
good_categories = results[results['Prediction'] == 'Good']
baseline = BASELINE_UPPER if income_level == 'upper' else BASELINE_LOWER
if not bad_categories.empty:
total_to_redistribute = sum(
input_data[row['Category']]
* min(max(abs(row['Deviation']) * 0.1, 0.25), 0.50)
for _, row in bad_categories.iterrows()
if row['Category'] not in ['Household', 'Food']
)
good_total = sum(input_data[cat] for cat in good_categories['Category'])
distribution_weights = {
cat: input_data[cat] / good_total if good_total > 0 else 0
for cat in good_categories['Category']
}
for category in input_data:
original = float(input_data[category])
baseline_dollars = total_spending * (baseline[category] / 100)
if category in bad_categories['Category'].values and category not in [
'Household',
'Food',
]:
reduction = min(
max(
abs(
results[results['Category'] == category][
'Deviation'
].values[0]
)
* 0.1,
0.25,
),
0.50,
)
suggested = original * (1 - reduction)
else:
weight = distribution_weights.get(category, 0)
increase = total_to_redistribute * weight
suggested = max(
original + increase,
baseline_dollars if category in ['Household', 'Food'] else original,
)
suggested_spending[category] = (original, round(suggested, 2))
else:
suggested_spending = {
cat: (float(val), float(val)) for cat, val in input_data.items()
}
return suggested_spending
# Format for Mistral
def format_for_mistral(
results, suggested_spending, total_spending, income_level, input_data
):
return {
"total_spending": total_spending,
"income_level": income_level,
"categories": [
{
"category": row['Category'],
"percent_spend": round(row['Percent_Spend'], 2),
"actual_dollars": round(input_data[row['Category']], 2),
"deviation": round(row['Deviation'], 2),
"prediction": row['Prediction'],
"suggested_dollars": suggested_spending[row['Category']][1],
}
for _, row in results.iterrows()
],
}
# Get spending summary (Mistral API call)
def get_spending_summary(spending_data):
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=API)
analysis_prompt = f"""
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:
### Financial Literacy Summary
#### Praise
For each 'Good' category:
⚠️ **Only show if ALL conditions met:**
- `prediction` = 'Good'
- `abs(deviation)` < 2%
✅ **{{category}} (${{actual_dollars}})** -
Explain using:
1. "% vs baseline: {{percent_spend}}% ({{deviation:+.2f}}% vs {{baseline}}%)"
2. Practical benefit
3. Savings impact ONLY if `deviation` > 0
#### Suggestions
⚠️ **Only show if ALL conditions met:**
- `prediction` = 'Bad'
- `abs(deviation)` > 2%
- `suggested_dollars` ≠ `actual_dollars`
For each 'Bad' category:
⚠️ **{{category}} (${{actual_dollars}} → ${{suggested_dollars}})** -
Structure as:
1. If suggested INCREASE: "Prioritize {{category}} by adding ${{suggested_dollars - actual_dollars}}..."
2. If suggested DECREASE: "Reduce {{category}} by ${{actual_dollars - suggested_dollars}}..."
#### Key Principle
Identify the MOST URGENT issue using largest absolute deviation...
**Baseline Reference ({spending_data['income_level'].capitalize()} Income):**
{'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%)'}
**Data:**
{json.dumps(spending_data, indent=2)}
**Begin Analysis:**
"""
try:
response = client.chat.completions.create(
model="mistralai/mistral-small-24b-instruct-2501:free",
messages=[{"role": "user", "content": analysis_prompt}],
temperature=0.5,
)
return response.choices[0].message.content
except Exception as e:
return f"Error calling Mistral API: {e}"
# Gradio interface function
def analyze_spending(
household,
food,
shopping,
transportation,
health_fitness,
entertainment,
beauty,
investment,
):
input_data = {
'Household': float(household),
'Food': float(food),
'Shopping': float(shopping),
'Transportation': float(transportation),
'Health & Fitness': float(health_fitness),
'Entertainment': float(entertainment),
'Beauty': float(beauty),
'Investment': float(investment),
}
logger.info("Before loading model")
model, scaler = load_financial_model()
logger.info("After loading model")
if model is None or scaler is None:
return "Error: Model or scaler failed to load.", None, None, None
results, total_spending, income_level = predict_spending_pattern(
model, scaler, input_data
)
suggested_spending = suggest_spending_pattern(
results, total_spending, input_data, income_level
)
spending_data = format_for_mistral(
results, suggested_spending, total_spending, income_level, input_data
)
summary = get_spending_summary(spending_data)
# Format suggested adjustments as a DataFrame
suggested_df = pd.DataFrame(
[(cat, orig, sugg) for cat, (orig, sugg) in suggested_spending.items()],
columns=['Category', 'Original ($)', 'Suggested ($)'],
)
return (
f"## Spending Analysis ({income_level.capitalize()} Income)\nTotal Spending: ${total_spending:.2f}",
results, # For DataFrame display
suggested_df, # For DataFrame display
summary, # Financial summary
)
# Gradio UI
logger.info("Setting up Gradio interface")
with gr.Blocks(
title="Personal Finance Assistant", css=".gr-button {margin-top: 10px}"
) as demo:
gr.Markdown("# Personal Finance Assistant")
gr.Markdown("Enter your monthly spending in each category ($):")
with gr.Row():
household = gr.Textbox(label="Household", value="500")
food = gr.Textbox(label="Food", value="100")
shopping = gr.Textbox(label="Shopping", value="950")
transportation = gr.Textbox(label="Transportation", value="100")
with gr.Row():
health_fitness = gr.Textbox(label="Health & Fitness", value="200")
entertainment = gr.Textbox(label="Entertainment", value="200")
beauty = gr.Textbox(label="Beauty", value="100")
investment = gr.Textbox(label="Investment", value="100")
submit_btn = gr.Button("Analyze")
# Output components
with gr.Column():
loading = gr.Markdown("### Analysis Results\n*Waiting for input...*")
title = gr.Markdown()
current_spending = gr.DataFrame(label="Current Spending")
suggested_adjustments = gr.DataFrame(label="Suggested Adjustments")
financial_summary = gr.Markdown()
# Handle click with loading state
def start_loading():
return "### Analysis Results\n*Processing your spending data...*"
submit_btn.click(fn=start_loading, inputs=None, outputs=loading).then(
fn=analyze_spending,
inputs=[
household,
food,
shopping,
transportation,
health_fitness,
entertainment,
beauty,
investment,
],
outputs=[title, current_spending, suggested_adjustments, financial_summary],
queue=True,
)
logger.info("Launching Gradio server")
demo.launch(server_name="0.0.0.0", server_port=7860)