| import gradio as gr |
| import anthropic |
| import json |
|
|
| |
| 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 |
| } |
|
|
| |
| 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)}%" |
|
|
|
|
| |
| 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.", |
| "", "", "", "", "", "" |
| ) |
|
|
| |
| 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 |
|
|
| |
| 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)} | |
| """ |
|
|
| |
| 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) |
| """ |
|
|
| |
| 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)} | |
| """ |
|
|
| |
| 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) |
|
|
|
|
| |
| 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("---") |
|
|
| |
| with gr.Row(): |
| with gr.Column(): |
| gr.Markdown("### ๐ฅ Gross income (before taxes)") |
| gross_income = gr.Number(label="Monthly gross income ($)", value=5000, minimum=0) |
|
|
| |
| 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("---") |
|
|
| |
| 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") |
|
|
| |
| 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._") |
|
|
| |
| 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() |
|
|