import gradio as gr import anthropic import json # ── constants ────────────────────────────────────────────────────────────── EXPENSE_CATS = [ "Housing", "Food & groceries", "Transport", "Utilities", "Entertainment", "Health", "Education", "Clothing", "Other" ] CAT_LIMITS = { "Housing": 30, "Food & groceries": 15, "Transport": 15, "Utilities": 10, "Entertainment": 10, "Health": 10, "Education": 10, "Clothing": 5, "Other": 10 } # ── helpers ──────────────────────────────────────────────────────────────── def fmt(n: float) -> str: return f"${round(n):,}" def pct(part: float, whole: float, decimals: int = 1) -> str: if whole == 0: return "0%" return f"{round(part / whole * 100, decimals)}%" # ── core calculation ─────────────────────────────────────────────────────── def calculate_budget( gross_income: float, fed_tax_pct: float, state_tax_pct: float, fica_pct: float, medicare_pct: float, k401_pct: float, housing: float, food: float, transport: float, utilities: float, entertainment: float, health: float, education: float, clothing: float, other: float, api_key: str, ): if gross_income <= 0: return ( "⚠️ Please enter a valid gross income.", "", "", "", "", "", "" ) # ── deductions ── fed_amt = gross_income * fed_tax_pct / 100 state_amt = gross_income * state_tax_pct / 100 fica_amt = gross_income * fica_pct / 100 medicare_amt = gross_income * medicare_pct / 100 k401_amt = gross_income * k401_pct / 100 total_tax = fed_amt + state_amt + fica_amt + medicare_amt total_ded = total_tax + k401_amt takehome = gross_income - total_ded # ── income flow summary ── flow_md = f"""## Income flow | | Amount | % of gross | |---|---|---| | **Gross income** | **{fmt(gross_income)}** | 100% | | Federal tax | -{fmt(fed_amt)} | {pct(fed_amt, gross_income)} | | State tax | -{fmt(state_amt)} | {pct(state_amt, gross_income)} | | Social Security | -{fmt(fica_amt)} | {pct(fica_amt, gross_income)} | | Medicare | -{fmt(medicare_amt)} | {pct(medicare_amt, gross_income)} | | 401(k) | -{fmt(k401_amt)} | {pct(k401_amt, gross_income)} | | **Take-home pay** | **{fmt(takehome)}** | {pct(takehome, gross_income)} | """ # ── expense breakdown ── expenses = { "Housing": housing, "Food & groceries": food, "Transport": transport, "Utilities": utilities, "Entertainment": entertainment, "Health": health, "Education": education, "Clothing": clothing, "Other": other, } used = {k: v for k, v in expenses.items() if v > 0} total_exp = sum(used.values()) remaining = takehome - total_exp exp_rows = [] for cat, amt in used.items(): limit = CAT_LIMITS.get(cat, 10) cat_pct = round(amt / takehome * 100, 1) if takehome > 0 else 0 status = "✅" if cat_pct <= limit * 0.8 else ("⚠️" if cat_pct <= limit else "🔴") exp_rows.append( f"| {status} {cat} | {fmt(amt)} | {cat_pct}% | {limit}% |" ) exp_md = f"""## Spending breakdown (vs take-home) | Category | Amount | % of take-home | Limit | |---|---|---|---| {''.join(chr(10) + r for r in exp_rows)} **Total expenses:** {fmt(total_exp)}  |  **Remaining:** {fmt(remaining)} ({pct(remaining, takehome)} of take-home) """ # ── summary metrics ── rem_emoji = "✅" if remaining >= 0 else "🔴" summary_md = f"""## Summary | | | |---|---| | Gross income | {fmt(gross_income)} | | Total taxes | {fmt(total_tax)} ({pct(total_tax, gross_income)}) | | 401(k) saved | {fmt(k401_amt)} ({pct(k401_amt, gross_income)}) | | Take-home | {fmt(takehome)} | | Total expenses | {fmt(total_exp)} | | {rem_emoji} Remaining | {fmt(remaining)} | """ # ── AI advice via streaming ── if not api_key.strip(): ai_advice = "⚠️ Enter your Anthropic API key to get personalized AI advice." else: exp_lines = "\n".join( f"- {cat}: {fmt(amt)} ({round(amt/takehome*100,1) if takehome else 0}% of take-home, limit {CAT_LIMITS.get(cat,10)}%)" for cat, amt in used.items() ) prompt = f"""You are a friendly, practical personal finance advisor. Analyze this monthly budget and give 5-6 specific, actionable suggestions. Be direct — use numbered points. Reference the actual dollar amounts. BUDGET: Gross income: {fmt(gross_income)}/month Taxes: {fmt(total_tax)} ({pct(total_tax, gross_income)} of gross) 401(k): {fmt(k401_amt)} ({pct(k401_amt, gross_income)} of gross) Take-home: {fmt(takehome)}/month EXPENSES: {exp_lines} Total expenses: {fmt(total_exp)} Remaining: {fmt(remaining)} ({pct(remaining, gross_income)} of gross) Give honest advice referencing the real numbers. 1-2 sentences per point.""" try: client = anthropic.Anthropic(api_key=api_key.strip()) ai_advice = "" with client.messages.stream( model="claude-sonnet-4-5", max_tokens=1000, messages=[{"role": "user", "content": prompt}], ) as stream: for text in stream.text_stream: ai_advice += text yield flow_md, exp_md, summary_md, ai_advice, \ fmt(gross_income), fmt(takehome), fmt(remaining) return except anthropic.AuthenticationError: ai_advice = "❌ Invalid API key. Check your Anthropic API key and try again." except Exception as e: ai_advice = f"❌ Error calling AI: {str(e)}" yield flow_md, exp_md, summary_md, ai_advice, \ fmt(gross_income), fmt(takehome), fmt(remaining) # ── Gradio UI ────────────────────────────────────────────────────────────── with gr.Blocks( title="AI Budget Advisor", theme=gr.themes.Soft(primary_hue="violet"), css=""" .section-header { font-weight: 600; font-size: 1rem; margin-bottom: 4px; } .metric-box { text-align: center; padding: 8px; } """ ) as demo: gr.Markdown("# 💰 AI Budget Advisor") gr.Markdown( "Enter your gross income, pre-tax deductions, and monthly expenses. " "Add your [Anthropic API key](https://console.anthropic.com/) to get AI-powered personalized advice." ) with gr.Row(): api_key_input = gr.Textbox( label="🔑 Anthropic API key", placeholder="sk-ant-...", type="password", scale=2, ) gr.Markdown("---") # ── INCOME ── with gr.Row(): with gr.Column(): gr.Markdown("### 📥 Gross income (before taxes)") gross_income = gr.Number(label="Monthly gross income ($)", value=5000, minimum=0) # ── PRE-TAX DEDUCTIONS ── gr.Markdown("### 🏛️ Pre-tax deductions") with gr.Row(): fed_tax = gr.Slider(0, 50, value=22, step=0.5, label="Federal income tax (%)") state_tax = gr.Slider(0, 15, value=5, step=0.5, label="State income tax (%)") with gr.Row(): fica = gr.Slider(0, 10, value=6.2, step=0.1, label="Social Security / FICA (%)") medicare = gr.Slider(0, 5, value=1.45, step=0.05, label="Medicare (%)") with gr.Row(): k401 = gr.Slider(0, 30, value=6, step=0.5, label="401(k) contribution (%)") gr.Markdown("---") # ── EXPENSES ── gr.Markdown("### 💸 Monthly expenses") with gr.Row(): housing = gr.Number(label="Housing ($)", value=1200, minimum=0) food = gr.Number(label="Food & groceries ($)", value=400, minimum=0) transport = gr.Number(label="Transport ($)", value=200, minimum=0) with gr.Row(): utilities = gr.Number(label="Utilities ($)", value=150, minimum=0) entertainment = gr.Number(label="Entertainment ($)", value=100, minimum=0) health = gr.Number(label="Health ($)", value=0, minimum=0) with gr.Row(): education = gr.Number(label="Education ($)", value=0, minimum=0) clothing = gr.Number(label="Clothing ($)", value=0, minimum=0) other = gr.Number(label="Other ($)", value=0, minimum=0) gr.Markdown("---") calc_btn = gr.Button("🧮 Calculate + get AI advice", variant="primary", size="lg") # ── OUTPUTS ── gr.Markdown("---") gr.Markdown("## Results") with gr.Row(): out_gross = gr.Textbox(label="Gross income", interactive=False) out_takehome = gr.Textbox(label="Take-home pay", interactive=False) out_remain = gr.Textbox(label="Remaining", interactive=False) with gr.Row(): with gr.Column(): out_flow = gr.Markdown(label="Income flow") with gr.Column(): out_summary = gr.Markdown(label="Summary") out_expenses = gr.Markdown(label="Spending breakdown") gr.Markdown("### 🤖 AI budget advisor") out_ai = gr.Markdown(label="AI advice", value="_AI advice will appear here after you calculate._") # ── wire up ── calc_btn.click( fn=calculate_budget, inputs=[ gross_income, fed_tax, state_tax, fica, medicare, k401, housing, food, transport, utilities, entertainment, health, education, clothing, other, api_key_input, ], outputs=[out_flow, out_expenses, out_summary, out_ai, out_gross, out_takehome, out_remain], ) gr.Markdown( "---\n" "_Powered by [Claude](https://anthropic.com). " "This is a financial planning tool, not professional financial advice._" ) if __name__ == "__main__": demo.launch()