sammy786 commited on
Commit
6d56669
Β·
verified Β·
1 Parent(s): 0432634

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -130
app.py CHANGED
@@ -188,7 +188,7 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
188
 
189
  print(f"πŸ” KEYS: {list(result.keys())}")
190
 
191
- # Extract data
192
  card_id = result.get('recommended_card', 'Unknown')
193
  rewards_earned = float(result.get('rewards_earned', 0))
194
  rewards_rate = result.get('rewards_rate', 'N/A')
@@ -196,48 +196,98 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
196
  reasoning = result.get('reasoning', 'No reasoning provided')
197
  alternatives = result.get('alternative_options', [])
198
  warnings = result.get('warnings', [])
199
- annual_impact = result.get('annual_impact', {})
200
 
201
  # Map card_id to card_name
202
  card_name_map = {
203
  'c_citi_custom_cash': 'Citi Custom Cash',
204
  'c_amex_gold': 'American Express Gold',
205
  'c_chase_sapphire_reserve': 'Chase Sapphire Reserve',
206
- 'c_chase_freedom_unlimited': 'Chase Freedom Unlimited'
 
 
 
 
207
  }
208
  card_name = card_name_map.get(card_id, card_id.replace('c_', '').replace('_', ' ').title())
209
 
210
- # Extract card-specific details from API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  card_details = result.get('card_details', {})
212
- reward_rate_value = card_details.get('reward_rate', 0) # e.g., 5 for 5%
213
- monthly_cap = card_details.get('monthly_cap', None) # e.g., 500
214
- annual_cap = card_details.get('annual_cap', None)
215
- base_rate = card_details.get('base_rate', 1) # Fallback rate after cap
216
- annual_fee = card_details.get('annual_fee', 0)
217
 
218
- print(f"βœ… PARSED: {card_name}, ${rewards_earned}, {rewards_rate}, {confidence}")
 
 
 
 
 
 
 
 
 
 
219
 
220
- # ========== DYNAMIC CALCULATION ==========
 
 
 
 
221
 
222
- # Get actual potential savings and score from API
223
- potential_savings = annual_impact.get('potential_savings', 0)
224
- optimization_score = annual_impact.get('optimization_score', 0)
225
 
226
- # Calculate annual projection dynamically
227
  amount_float = float(amount)
228
 
229
- # Determine frequency assumption (you can make this smarter based on category)
230
  frequency_map = {
231
  'Groceries': 52, # Weekly
232
- 'Restaurants': 52, # Weekly
233
- 'Gas Stations': 52, # Weekly
234
- 'Fast Food': 52, # Weekly
235
  'Airlines': 4, # Quarterly
236
  'Hotels': 12, # Monthly
237
- 'Online Shopping': 24, # Bi-weekly
238
- 'Entertainment': 24, # Bi-weekly
239
  }
240
- frequency = frequency_map.get(category, 26) # Default: bi-weekly
241
  frequency_label = {
242
  52: 'weekly',
243
  26: 'bi-weekly',
@@ -248,17 +298,15 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
248
 
249
  annual_spend = amount_float * frequency
250
 
251
- # Calculate rewards based on card structure
252
  if monthly_cap:
253
- # Card has monthly cap (e.g., Citi Custom Cash: $500/month at 5%)
254
  monthly_cap_annual = monthly_cap * 12
255
 
256
  if annual_spend <= monthly_cap_annual:
257
- # All spending is within cap
258
  high_rate_spend = annual_spend
259
  low_rate_spend = 0
260
  else:
261
- # Some spending exceeds cap
262
  high_rate_spend = monthly_cap_annual
263
  low_rate_spend = annual_spend - monthly_cap_annual
264
 
@@ -266,36 +314,16 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
266
  low_rate_rewards = low_rate_spend * (base_rate / 100)
267
  total_rewards = high_rate_rewards + low_rate_rewards
268
 
269
- # Baseline comparison (1% card)
270
- baseline_rewards = annual_spend * 0.01
271
- net_benefit = total_rewards - baseline_rewards - annual_fee
272
-
273
- # Build calculation table
274
- calc_table = f"""
275
- | Spending Tier | Annual Amount | Rate | Rewards |
276
  |---------------|---------------|------|---------|
277
  | First ${monthly_cap}/month | ${high_rate_spend:.2f} | {reward_rate_value}% | ${high_rate_rewards:.2f} |
278
  | Remaining spend | ${low_rate_spend:.2f} | {base_rate}% | ${low_rate_rewards:.2f} |
279
  | **Subtotal** | **${annual_spend:.2f}** | - | **${total_rewards:.2f}** |
280
  | Annual fee | - | - | -${annual_fee:.2f} |
281
- | **Net Rewards** | - | - | **${total_rewards - annual_fee:.2f}** |
282
- """
283
 
284
- comparison_text = f"""
285
- **With {card_name}:**
286
- - High rate earnings: ${high_rate_rewards:.2f}
287
- - Base rate earnings: ${low_rate_rewards:.2f}
288
- - Annual fee: -${annual_fee:.2f}
289
- - **Net total: ${total_rewards - annual_fee:.2f}/year**
290
-
291
- **With Baseline 1% Card:**
292
- - All spending at 1%: ${baseline_rewards:.2f}/year
293
-
294
- **Net Benefit: ${net_benefit:.2f}/year**
295
- """
296
-
297
  elif annual_cap:
298
- # Card has annual cap (e.g., Amex Gold: $25,000/year at 4x)
299
  if annual_spend <= annual_cap:
300
  high_rate_spend = annual_spend
301
  low_rate_spend = 0
@@ -307,83 +335,83 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
307
  low_rate_rewards = low_rate_spend * (base_rate / 100)
308
  total_rewards = high_rate_rewards + low_rate_rewards
309
 
310
- baseline_rewards = annual_spend * 0.01
311
- net_benefit = total_rewards - baseline_rewards - annual_fee
312
-
313
- calc_table = f"""
314
- | Spending Tier | Annual Amount | Rate | Rewards |
315
  |---------------|---------------|------|---------|
316
  | Up to ${annual_cap:,.0f}/year | ${high_rate_spend:.2f} | {reward_rate_value}% | ${high_rate_rewards:.2f} |
317
  | Above cap | ${low_rate_spend:.2f} | {base_rate}% | ${low_rate_rewards:.2f} |
318
  | **Subtotal** | **${annual_spend:.2f}** | - | **${total_rewards:.2f}** |
319
  | Annual fee | - | - | -${annual_fee:.2f} |
320
- | **Net Rewards** | - | - | **${total_rewards - annual_fee:.2f}** |
321
- """
322
 
323
- comparison_text = f"""
324
- **With {card_name}:**
325
- - High rate earnings: ${high_rate_rewards:.2f}
326
- - Base rate earnings: ${low_rate_rewards:.2f}
327
- - Annual fee: -${annual_fee:.2f}
328
- - **Net total: ${total_rewards - annual_fee:.2f}/year**
329
-
330
- **With Baseline 1% Card:**
331
- - All spending at 1%: ${baseline_rewards:.2f}/year
332
-
333
- **Net Benefit: ${net_benefit:.2f}/year**
334
- """
335
-
336
  else:
337
- # No cap - flat rate on all spending
338
  total_rewards = annual_spend * (reward_rate_value / 100)
339
- baseline_rewards = annual_spend * 0.01
340
- net_benefit = total_rewards - baseline_rewards - annual_fee
341
 
342
- calc_table = f"""
343
- | Spending Tier | Annual Amount | Rate | Rewards |
344
  |---------------|---------------|------|---------|
345
  | All spending | ${annual_spend:.2f} | {reward_rate_value}% | ${total_rewards:.2f} |
346
  | Annual fee | - | - | -${annual_fee:.2f} |
347
- | **Net Rewards** | - | - | **${total_rewards - annual_fee:.2f}** |
348
- """
349
-
350
- comparison_text = f"""
351
- **With {card_name}:**
352
- - Earnings at {reward_rate_value}%: ${total_rewards:.2f}
 
 
 
353
  - Annual fee: -${annual_fee:.2f}
354
- - **Net total: ${total_rewards - annual_fee:.2f}/year**
355
 
356
  **With Baseline 1% Card:**
357
  - All spending at 1%: ${baseline_rewards:.2f}/year
358
 
359
- **Net Benefit: ${net_benefit:.2f}/year**
360
- """
361
 
362
- # Dynamic optimization score breakdown
363
- score_breakdown = annual_impact.get('score_breakdown', {})
364
- if not score_breakdown:
365
- # Generate based on optimization_score
366
- score_breakdown = {
367
- 'reward_rate': min(30, int(optimization_score * 0.3)),
368
- 'cap_availability': min(25, int(optimization_score * 0.25)),
369
- 'annual_fee': min(20, int(optimization_score * 0.2)),
370
- 'category_match': min(20, int(optimization_score * 0.2)),
371
- 'penalties': max(-5, int((optimization_score - 100) * 0.05))
372
- }
373
 
374
- score_details = f"""
375
- **Score Components:**
376
- - {"βœ…" if score_breakdown.get('reward_rate', 0) > 20 else "⚠️"} Reward rate: **+{score_breakdown.get('reward_rate', 0)} points**
377
- - {"βœ…" if score_breakdown.get('cap_availability', 0) > 15 else "⚠️"} Cap availability: **+{score_breakdown.get('cap_availability', 0)} points**
378
- - {"βœ…" if score_breakdown.get('annual_fee', 0) > 15 else "⚠️"} Annual fee value: **+{score_breakdown.get('annual_fee', 0)} points**
379
- - {"βœ…" if score_breakdown.get('category_match', 0) > 15 else "⚠️"} Category match: **+{score_breakdown.get('category_match', 0)} points**
380
- - {"⚠️" if score_breakdown.get('penalties', 0) < 0 else "βœ…"} Limitations: **{score_breakdown.get('penalties', 0)} points**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
 
382
  **Total: {optimization_score}/100**
383
- """
 
 
 
 
 
 
384
 
385
  # ========== FORMAT OUTPUT ==========
386
-
387
  output = f"""## πŸ€– AI Agent Recommendation
388
 
389
  ### πŸ’³ Recommended Card: **{card_name}**
@@ -397,8 +425,7 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
397
 
398
  {reasoning}
399
 
400
- ---
401
- """
402
 
403
  # Add alternatives
404
  if alternatives:
@@ -415,12 +442,11 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
415
  for warning in warnings:
416
  output += f"- {warning}\n"
417
 
418
- # Add dynamic annual impact
419
- if annual_impact:
420
- output += f"""
421
  ### πŸ’° Annual Impact
422
 
423
- - **Potential Savings:** ${potential_savings:.2f}/year
424
  - **Optimization Score:** {optimization_score}/100
425
 
426
  <details>
@@ -430,10 +456,11 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
430
 
431
  **Step 1: Estimate Annual Spending**
432
 
433
- Current transaction: amountfloat:.2fatmerchantCategory:categoryFrequencyassumption:frequencylabel.capitalize()Annualestimate:{amount_float:.2f} at {merchant}
434
- Category: {category}
435
- Frequency assumption: {frequency_label.capitalize()}
436
- Annual estimate: amountf​loat:.2fatmerchantCategory:categoryFrequencyassumption:frequencyl​abel.capitalize()Annualestimate:{amount_float:.2f} Γ— {frequency} = ${annual_spend:.2f}
 
437
  **Step 2: Calculate Rewards with {card_name}**
438
 
439
  {calc_table}
@@ -448,13 +475,6 @@ Annual estimate: amountf​loat:.2fatmerchantCategory:categoryFrequencyassumptio
448
 
449
  {score_details}
450
 
451
- **Score Ranges:**
452
- - 90-100: Optimal choice βœ…
453
- - 80-89: Great choice πŸ‘
454
- - 70-79: Good choice πŸ‘Œ
455
- - 60-69: Acceptable ⚠️
456
- - <60: Suboptimal ❌
457
-
458
  ---
459
 
460
  #### πŸ” Card Details:
@@ -466,27 +486,22 @@ Annual estimate: amountf​loat:.2fatmerchantCategory:categoryFrequencyassumptio
466
 
467
  </details>
468
 
469
- ---
470
- """
471
 
472
  # Add transaction details
473
- output += f"""
474
- ### πŸ“Š Transaction Details:
475
-
476
  - **Amount:** ${amount_float:.2f}
477
  - **Merchant:** {merchant}
478
  - **Category:** {category}
479
- - **MCC Code:** {transaction['mcc']}
480
- """
481
 
482
  chart = create_agent_recommendation_chart_enhanced(result)
483
  yield output, chart
484
-
485
  except Exception as e:
486
  import traceback
487
  print(f"❌ ERROR: {traceback.format_exc()}")
488
- yield f"❌ **Error:** {str(e)}", None
489
-
490
 
491
  def create_agent_recommendation_chart_enhanced(result: Dict) -> go.Figure:
492
  try:
 
188
 
189
  print(f"πŸ” KEYS: {list(result.keys())}")
190
 
191
+ # ========== EXTRACT BASIC DATA ==========
192
  card_id = result.get('recommended_card', 'Unknown')
193
  rewards_earned = float(result.get('rewards_earned', 0))
194
  rewards_rate = result.get('rewards_rate', 'N/A')
 
196
  reasoning = result.get('reasoning', 'No reasoning provided')
197
  alternatives = result.get('alternative_options', [])
198
  warnings = result.get('warnings', [])
 
199
 
200
  # Map card_id to card_name
201
  card_name_map = {
202
  'c_citi_custom_cash': 'Citi Custom Cash',
203
  'c_amex_gold': 'American Express Gold',
204
  'c_chase_sapphire_reserve': 'Chase Sapphire Reserve',
205
+ 'c_chase_freedom_unlimited': 'Chase Freedom Unlimited',
206
+ 'c_chase_sapphire_preferred': 'Chase Sapphire Preferred',
207
+ 'c_capital_one_venture': 'Capital One Venture',
208
+ 'c_discover_it': 'Discover it',
209
+ 'c_wells_fargo_active_cash': 'Wells Fargo Active Cash'
210
  }
211
  card_name = card_name_map.get(card_id, card_id.replace('c_', '').replace('_', ' ').title())
212
 
213
+ # ========== CARD DATABASE (FALLBACK) ==========
214
+ # If API doesn't provide card_details, use this database
215
+ CARD_DATABASE = {
216
+ 'c_citi_custom_cash': {
217
+ 'reward_rate': 5.0,
218
+ 'monthly_cap': 500,
219
+ 'base_rate': 1.0,
220
+ 'annual_fee': 0,
221
+ 'cap_type': 'monthly'
222
+ },
223
+ 'c_amex_gold': {
224
+ 'reward_rate': 4.0,
225
+ 'annual_cap': 25000,
226
+ 'base_rate': 1.0,
227
+ 'annual_fee': 250,
228
+ 'cap_type': 'annual'
229
+ },
230
+ 'c_chase_sapphire_reserve': {
231
+ 'reward_rate': 3.0,
232
+ 'monthly_cap': None,
233
+ 'base_rate': 3.0,
234
+ 'annual_fee': 550,
235
+ 'cap_type': 'none'
236
+ },
237
+ 'c_chase_freedom_unlimited': {
238
+ 'reward_rate': 1.5,
239
+ 'monthly_cap': None,
240
+ 'base_rate': 1.5,
241
+ 'annual_fee': 0,
242
+ 'cap_type': 'none'
243
+ },
244
+ 'c_chase_sapphire_preferred': {
245
+ 'reward_rate': 2.0,
246
+ 'monthly_cap': None,
247
+ 'base_rate': 2.0,
248
+ 'annual_fee': 95,
249
+ 'cap_type': 'none'
250
+ }
251
+ }
252
+
253
+ # ========== GET CARD DETAILS (API OR FALLBACK) ==========
254
  card_details = result.get('card_details', {})
 
 
 
 
 
255
 
256
+ if not card_details or not card_details.get('reward_rate'):
257
+ # Use fallback database
258
+ card_details = CARD_DATABASE.get(card_id, {
259
+ 'reward_rate': 1.0,
260
+ 'monthly_cap': None,
261
+ 'annual_cap': None,
262
+ 'base_rate': 1.0,
263
+ 'annual_fee': 0,
264
+ 'cap_type': 'none'
265
+ })
266
+ print(f"⚠️ Using fallback card details for {card_id}")
267
 
268
+ reward_rate_value = card_details.get('reward_rate', 1.0)
269
+ monthly_cap = card_details.get('monthly_cap', None)
270
+ annual_cap = card_details.get('annual_cap', None)
271
+ base_rate = card_details.get('base_rate', 1.0)
272
+ annual_fee = card_details.get('annual_fee', 0)
273
 
274
+ print(f"βœ… CARD DETAILS: {reward_rate_value}%, cap={monthly_cap or annual_cap}, fee=${annual_fee}")
 
 
275
 
276
+ # ========== CALCULATE ANNUAL PROJECTION ==========
277
  amount_float = float(amount)
278
 
279
+ # Frequency assumptions by category
280
  frequency_map = {
281
  'Groceries': 52, # Weekly
282
+ 'Restaurants': 52,
283
+ 'Gas Stations': 52,
284
+ 'Fast Food': 52,
285
  'Airlines': 4, # Quarterly
286
  'Hotels': 12, # Monthly
287
+ 'Online Shopping': 24,
288
+ 'Entertainment': 24,
289
  }
290
+ frequency = frequency_map.get(category, 26)
291
  frequency_label = {
292
  52: 'weekly',
293
  26: 'bi-weekly',
 
298
 
299
  annual_spend = amount_float * frequency
300
 
301
+ # ========== TIERED CALCULATION ==========
302
  if monthly_cap:
303
+ # Monthly cap logic (e.g., Citi Custom Cash)
304
  monthly_cap_annual = monthly_cap * 12
305
 
306
  if annual_spend <= monthly_cap_annual:
 
307
  high_rate_spend = annual_spend
308
  low_rate_spend = 0
309
  else:
 
310
  high_rate_spend = monthly_cap_annual
311
  low_rate_spend = annual_spend - monthly_cap_annual
312
 
 
314
  low_rate_rewards = low_rate_spend * (base_rate / 100)
315
  total_rewards = high_rate_rewards + low_rate_rewards
316
 
317
+ calc_table = f"""| Spending Tier | Annual Amount | Rate | Rewards |
 
 
 
 
 
 
318
  |---------------|---------------|------|---------|
319
  | First ${monthly_cap}/month | ${high_rate_spend:.2f} | {reward_rate_value}% | ${high_rate_rewards:.2f} |
320
  | Remaining spend | ${low_rate_spend:.2f} | {base_rate}% | ${low_rate_rewards:.2f} |
321
  | **Subtotal** | **${annual_spend:.2f}** | - | **${total_rewards:.2f}** |
322
  | Annual fee | - | - | -${annual_fee:.2f} |
323
+ | **Net Rewards** | - | - | **${total_rewards - annual_fee:.2f}** |"""
 
324
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  elif annual_cap:
326
+ # Annual cap logic (e.g., Amex Gold)
327
  if annual_spend <= annual_cap:
328
  high_rate_spend = annual_spend
329
  low_rate_spend = 0
 
335
  low_rate_rewards = low_rate_spend * (base_rate / 100)
336
  total_rewards = high_rate_rewards + low_rate_rewards
337
 
338
+ calc_table = f"""| Spending Tier | Annual Amount | Rate | Rewards |
 
 
 
 
339
  |---------------|---------------|------|---------|
340
  | Up to ${annual_cap:,.0f}/year | ${high_rate_spend:.2f} | {reward_rate_value}% | ${high_rate_rewards:.2f} |
341
  | Above cap | ${low_rate_spend:.2f} | {base_rate}% | ${low_rate_rewards:.2f} |
342
  | **Subtotal** | **${annual_spend:.2f}** | - | **${total_rewards:.2f}** |
343
  | Annual fee | - | - | -${annual_fee:.2f} |
344
+ | **Net Rewards** | - | - | **${total_rewards - annual_fee:.2f}** |"""
 
345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  else:
347
+ # No cap - flat rate
348
  total_rewards = annual_spend * (reward_rate_value / 100)
 
 
349
 
350
+ calc_table = f"""| Spending Tier | Annual Amount | Rate | Rewards |
 
351
  |---------------|---------------|------|---------|
352
  | All spending | ${annual_spend:.2f} | {reward_rate_value}% | ${total_rewards:.2f} |
353
  | Annual fee | - | - | -${annual_fee:.2f} |
354
+ | **Net Rewards** | - | - | **${total_rewards - annual_fee:.2f}** |"""
355
+
356
+ # ========== BASELINE COMPARISON ==========
357
+ baseline_rewards = annual_spend * 0.01
358
+ net_rewards = total_rewards - annual_fee
359
+ net_benefit = net_rewards - baseline_rewards
360
+
361
+ comparison_text = f"""**With {card_name}:**
362
+ - Earnings: ${total_rewards:.2f}
363
  - Annual fee: -${annual_fee:.2f}
364
+ - **Net total: ${net_rewards:.2f}/year**
365
 
366
  **With Baseline 1% Card:**
367
  - All spending at 1%: ${baseline_rewards:.2f}/year
368
 
369
+ **Net Benefit: ${net_benefit:+.2f}/year** {"πŸŽ‰" if net_benefit > 0 else "⚠️"}"""
 
370
 
371
+ # ========== OPTIMIZATION SCORE ==========
372
+ # Calculate score based on performance
373
+ max_possible_rewards = annual_spend * 0.06 # Theoretical max (6%)
 
 
 
 
 
 
 
 
374
 
375
+ if max_possible_rewards > 0:
376
+ performance_ratio = (net_rewards / max_possible_rewards) * 100
377
+
378
+ # Bonus for beating baseline
379
+ if net_rewards > baseline_rewards:
380
+ improvement = (net_rewards - baseline_rewards) / baseline_rewards
381
+ baseline_bonus = min(improvement * 20, 20)
382
+ else:
383
+ baseline_bonus = -10 # Penalty for underperforming
384
+
385
+ optimization_score = int(min(performance_ratio + baseline_bonus, 100))
386
+ else:
387
+ optimization_score = 0
388
+
389
+ # Score breakdown
390
+ score_breakdown = {
391
+ 'reward_rate': min(30, int(optimization_score * 0.30)),
392
+ 'cap_availability': min(25, int(optimization_score * 0.25)),
393
+ 'annual_fee': min(20, int(optimization_score * 0.20)),
394
+ 'category_match': min(20, int(optimization_score * 0.20)),
395
+ 'penalties': max(-5, int((optimization_score - 100) * 0.05))
396
+ }
397
+
398
+ score_details = f"""**Score Components:**
399
+ - {"βœ…" if score_breakdown['reward_rate'] > 20 else "⚠️"} Reward rate: **+{score_breakdown['reward_rate']} points**
400
+ - {"βœ…" if score_breakdown['cap_availability'] > 15 else "⚠️"} Cap availability: **+{score_breakdown['cap_availability']} points**
401
+ - {"βœ…" if score_breakdown['annual_fee'] > 15 else "⚠️"} Annual fee value: **+{score_breakdown['annual_fee']} points**
402
+ - {"βœ…" if score_breakdown['category_match'] > 15 else "⚠️"} Category match: **+{score_breakdown['category_match']} points**
403
+ - {"⚠️" if score_breakdown['penalties'] < 0 else "βœ…"} Limitations: **{score_breakdown['penalties']} points**
404
 
405
  **Total: {optimization_score}/100**
406
+
407
+ **Score Ranges:**
408
+ - 90-100: Optimal choice βœ…
409
+ - 80-89: Great choice πŸ‘
410
+ - 70-79: Good choice πŸ‘Œ
411
+ - 60-69: Acceptable ⚠️
412
+ - <60: Suboptimal ❌"""
413
 
414
  # ========== FORMAT OUTPUT ==========
 
415
  output = f"""## πŸ€– AI Agent Recommendation
416
 
417
  ### πŸ’³ Recommended Card: **{card_name}**
 
425
 
426
  {reasoning}
427
 
428
+ ---"""
 
429
 
430
  # Add alternatives
431
  if alternatives:
 
442
  for warning in warnings:
443
  output += f"- {warning}\n"
444
 
445
+ # Add annual impact with expandable details
446
+ output += f"""
 
447
  ### πŸ’° Annual Impact
448
 
449
+ - **Potential Savings:** ${net_benefit:.2f}/year
450
  - **Optimization Score:** {optimization_score}/100
451
 
452
  <details>
 
456
 
457
  **Step 1: Estimate Annual Spending**
458
 
459
+ Current transaction: ${amount_float:.2f} at {merchant}
460
+ Category: {category}
461
+ Frequency assumption: {frequency_label.capitalize()}
462
+ Annual estimate: ${amount_float:.2f} Γ— {frequency} = **${annual_spend:.2f}**
463
+
464
  **Step 2: Calculate Rewards with {card_name}**
465
 
466
  {calc_table}
 
475
 
476
  {score_details}
477
 
 
 
 
 
 
 
 
478
  ---
479
 
480
  #### πŸ” Card Details:
 
486
 
487
  </details>
488
 
489
+ ---"""
 
490
 
491
  # Add transaction details
492
+ output += f"""### πŸ“Š Transaction Details:
 
 
493
  - **Amount:** ${amount_float:.2f}
494
  - **Merchant:** {merchant}
495
  - **Category:** {category}
496
+ - **MCC Code:** {transaction['mcc']}"""
 
497
 
498
  chart = create_agent_recommendation_chart_enhanced(result)
499
  yield output, chart
500
+
501
  except Exception as e:
502
  import traceback
503
  print(f"❌ ERROR: {traceback.format_exc()}")
504
+ yield f"❌ **Error:** {str(e)}", None
 
505
 
506
  def create_agent_recommendation_chart_enhanced(result: Dict) -> go.Figure:
507
  try: