Update app.py
Browse files
app.py
CHANGED
|
@@ -21,9 +21,16 @@ create_spending_chart,
|
|
| 21 |
create_card_performance_chart
|
| 22 |
)
|
| 23 |
import plotly.graph_objects as go
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
-
# ===================== Initialize API client =====================
|
| 26 |
-
client = RewardPilotClient()
|
| 27 |
|
| 28 |
# ===================== Main Recommendation Function =====================
|
| 29 |
def get_recommendation(
|
|
@@ -90,6 +97,154 @@ def get_recommendation(
|
|
| 90 |
|
| 91 |
return formatted_text, comparison_table, stats
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
# ===================== Sample Transaction Examples =====================
|
| 94 |
EXAMPLES = [
|
| 95 |
["u_alice", "Groceries", "Whole Foods", 125.50, False, "", "2025-01-15"],
|
|
@@ -193,6 +348,10 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
|
|
| 193 |
---
|
| 194 |
"""
|
| 195 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
|
| 197 |
# Ensure all tabs are siblings at the same level
|
| 198 |
with gr.Tabs():
|
|
@@ -297,25 +456,12 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
|
|
| 297 |
gr.Markdown("### π Card Comparison")
|
| 298 |
comparison_output = gr.Markdown()
|
| 299 |
|
| 300 |
-
|
| 301 |
recommend_btn.click(
|
| 302 |
-
fn=
|
| 303 |
-
inputs=[
|
| 304 |
-
|
| 305 |
-
merchant_dropdown,
|
| 306 |
-
category_dropdown,
|
| 307 |
-
amount_input,
|
| 308 |
-
use_custom_mcc,
|
| 309 |
-
custom_mcc_input,
|
| 310 |
-
date_input
|
| 311 |
-
],
|
| 312 |
-
outputs=[
|
| 313 |
-
recommendation_output,
|
| 314 |
-
comparison_output,
|
| 315 |
-
stats_output
|
| 316 |
-
]
|
| 317 |
)
|
| 318 |
-
|
| 319 |
# Examples
|
| 320 |
gr.Markdown("### π Example Transactions")
|
| 321 |
gr.Examples(
|
|
@@ -579,7 +725,65 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
|
|
| 579 |
analytics_status
|
| 580 |
]
|
| 581 |
)
|
| 582 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
# ========== Tab 3: About ==========
|
| 584 |
with gr.Tab("βΉοΈ About"):
|
| 585 |
gr.Markdown(
|
|
@@ -589,6 +793,20 @@ Get AI-powered credit card recommendations that maximize your rewards based on:
|
|
| 589 |
RewardPilot is an AI-powered credit card recommendation system built using the **Model Context Protocol (MCP)** architecture.
|
| 590 |
|
| 591 |
### ποΈ Architecture
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 592 |
|
| 593 |
The system consists of multiple microservices:
|
| 594 |
|
|
|
|
| 21 |
create_card_performance_chart
|
| 22 |
)
|
| 23 |
import plotly.graph_objects as go
|
| 24 |
+
import gradio as gr
|
| 25 |
+
from utils.api_client import RewardPilotClient
|
| 26 |
+
from utils.formatters import format_recommendation, format_analytics
|
| 27 |
+
from utils.llm_explainer import get_llm_explainer
|
| 28 |
+
import config
|
| 29 |
+
|
| 30 |
+
# Initialize clients
|
| 31 |
+
client = RewardPilotClient(config.ORCHESTRATOR_URL)
|
| 32 |
+
llm = get_llm_explainer()
|
| 33 |
|
|
|
|
|
|
|
| 34 |
|
| 35 |
# ===================== Main Recommendation Function =====================
|
| 36 |
def get_recommendation(
|
|
|
|
| 97 |
|
| 98 |
return formatted_text, comparison_table, stats
|
| 99 |
|
| 100 |
+
|
| 101 |
+
def get_recommendation_with_ai(user_id, merchant, category, amount):
|
| 102 |
+
"""Get card recommendation with LLM-powered explanation"""
|
| 103 |
+
|
| 104 |
+
try:
|
| 105 |
+
# Get base recommendation from orchestrator
|
| 106 |
+
result = client.get_recommendation(
|
| 107 |
+
user_id=user_id,
|
| 108 |
+
merchant=merchant,
|
| 109 |
+
category=category,
|
| 110 |
+
amount=float(amount)
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
if not result.get('success'):
|
| 114 |
+
return f"β Error: {result.get('error', 'Unknown error')}", None
|
| 115 |
+
|
| 116 |
+
data = result['data']
|
| 117 |
+
|
| 118 |
+
# Generate LLM explanation if enabled
|
| 119 |
+
ai_explanation = ""
|
| 120 |
+
if config.LLM_ENABLED:
|
| 121 |
+
try:
|
| 122 |
+
ai_explanation = llm.explain_recommendation(
|
| 123 |
+
card=data['recommended_card'],
|
| 124 |
+
rewards=data['rewards_earned'],
|
| 125 |
+
rewards_rate=data['rewards_rate'],
|
| 126 |
+
merchant=merchant,
|
| 127 |
+
category=category,
|
| 128 |
+
amount=float(amount),
|
| 129 |
+
warnings=data.get('warnings'),
|
| 130 |
+
annual_potential=data.get('annual_potential'),
|
| 131 |
+
alternatives=data.get('alternatives', [])
|
| 132 |
+
)
|
| 133 |
+
except Exception as e:
|
| 134 |
+
print(f"LLM explanation failed: {e}")
|
| 135 |
+
ai_explanation = ""
|
| 136 |
+
|
| 137 |
+
# Format output with AI explanation
|
| 138 |
+
output = f"""
|
| 139 |
+
## π― Recommendation for ${amount} at {merchant}
|
| 140 |
+
|
| 141 |
+
### π³ Best Card: **{data['recommended_card']}**
|
| 142 |
+
|
| 143 |
+
**Rewards Earned:** ${data['rewards_earned']:.2f} ({data['rewards_rate']})
|
| 144 |
+
|
| 145 |
+
"""
|
| 146 |
+
|
| 147 |
+
# Add AI explanation if available
|
| 148 |
+
if ai_explanation:
|
| 149 |
+
output += f"""
|
| 150 |
+
### π€ AI Insight
|
| 151 |
+
|
| 152 |
+
{ai_explanation}
|
| 153 |
+
|
| 154 |
+
---
|
| 155 |
+
|
| 156 |
+
"""
|
| 157 |
+
|
| 158 |
+
# Add detailed breakdown
|
| 159 |
+
output += f"""
|
| 160 |
+
### π Breakdown
|
| 161 |
+
|
| 162 |
+
- **Category:** {category}
|
| 163 |
+
- **Annual Potential:** ${data.get('annual_potential', 0):.2f}
|
| 164 |
+
- **Optimization Score:** {data.get('optimization_score', 'N/A')}/100
|
| 165 |
+
"""
|
| 166 |
+
|
| 167 |
+
# Add warnings
|
| 168 |
+
if data.get('warnings'):
|
| 169 |
+
output += "\n\n### β οΈ Warnings\n\n"
|
| 170 |
+
for warning in data['warnings']:
|
| 171 |
+
output += f"- {warning}\n"
|
| 172 |
+
|
| 173 |
+
# Add alternatives
|
| 174 |
+
if data.get('alternatives'):
|
| 175 |
+
output += "\n\n### π Alternative Options\n\n"
|
| 176 |
+
for alt in data['alternatives'][:3]:
|
| 177 |
+
output += f"- **{alt['card']}:** ${alt['rewards']:.2f} ({alt['rate']})\n"
|
| 178 |
+
|
| 179 |
+
# Create visualization (your existing chart code)
|
| 180 |
+
chart = create_rewards_comparison_chart(data)
|
| 181 |
+
|
| 182 |
+
return output, chart
|
| 183 |
+
|
| 184 |
+
except Exception as e:
|
| 185 |
+
return f"β Error: {str(e)}", None
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def get_analytics_with_insights(user_id):
|
| 189 |
+
"""Get analytics with LLM-generated insights"""
|
| 190 |
+
|
| 191 |
+
try:
|
| 192 |
+
# Get analytics data
|
| 193 |
+
result = client.get_user_analytics(user_id)
|
| 194 |
+
|
| 195 |
+
if not result.get('success'):
|
| 196 |
+
return f"β Error: {result.get('error', 'Unknown error')}", None, None, None
|
| 197 |
+
|
| 198 |
+
data = result['data']
|
| 199 |
+
|
| 200 |
+
# Generate AI insights if enabled
|
| 201 |
+
ai_insights = ""
|
| 202 |
+
if config.LLM_ENABLED:
|
| 203 |
+
try:
|
| 204 |
+
ai_insights = llm.generate_spending_insights(
|
| 205 |
+
user_id=user_id,
|
| 206 |
+
total_spending=data['total_spending'],
|
| 207 |
+
total_rewards=data['total_rewards'],
|
| 208 |
+
optimization_score=data['optimization_score'],
|
| 209 |
+
top_categories=data.get('category_breakdown', []),
|
| 210 |
+
recommendations_count=data.get('optimized_count', 0)
|
| 211 |
+
)
|
| 212 |
+
except Exception as e:
|
| 213 |
+
print(f"AI insights generation failed: {e}")
|
| 214 |
+
ai_insights = ""
|
| 215 |
+
|
| 216 |
+
# Format metrics
|
| 217 |
+
metrics = f"""
|
| 218 |
+
## π Your Rewards Analytics
|
| 219 |
+
|
| 220 |
+
### Key Metrics
|
| 221 |
+
|
| 222 |
+
- **π° Total Rewards:** ${data['total_rewards']:.2f}
|
| 223 |
+
- **π Potential Savings:** ${data['potential_savings']:.2f}/year
|
| 224 |
+
- **β Optimization Score:** {data['optimization_score']}/100
|
| 225 |
+
- **β
Optimized Transactions:** {data.get('optimized_count', 0)}
|
| 226 |
+
"""
|
| 227 |
+
|
| 228 |
+
# Add AI insights
|
| 229 |
+
if ai_insights:
|
| 230 |
+
metrics += f"""
|
| 231 |
+
### π€ Personalized Insights
|
| 232 |
+
|
| 233 |
+
{ai_insights}
|
| 234 |
+
|
| 235 |
+
---
|
| 236 |
+
"""
|
| 237 |
+
|
| 238 |
+
# Create charts (your existing chart code)
|
| 239 |
+
spending_chart = create_spending_chart(data)
|
| 240 |
+
rewards_chart = create_rewards_distribution_chart(data)
|
| 241 |
+
optimization_chart = create_optimization_gauge(data['optimization_score'])
|
| 242 |
+
|
| 243 |
+
return metrics, spending_chart, rewards_chart, optimization_chart
|
| 244 |
+
|
| 245 |
+
except Exception as e:
|
| 246 |
+
return f"β Error: {str(e)}", None, None, None
|
| 247 |
+
|
| 248 |
# ===================== Sample Transaction Examples =====================
|
| 249 |
EXAMPLES = [
|
| 250 |
["u_alice", "Groceries", "Whole Foods", 125.50, False, "", "2025-01-15"],
|
|
|
|
| 348 |
---
|
| 349 |
"""
|
| 350 |
)
|
| 351 |
+
# LLM Status Indicator
|
| 352 |
+
llm_status = "π€ **AI Explanations:** β
Enabled" if config.LLM_ENABLED else "β οΈ **AI Explanations:** Disabled (using rule-based mode)"
|
| 353 |
+
gr.Markdown(llm_status)
|
| 354 |
+
|
| 355 |
|
| 356 |
# Ensure all tabs are siblings at the same level
|
| 357 |
with gr.Tabs():
|
|
|
|
| 456 |
gr.Markdown("### π Card Comparison")
|
| 457 |
comparison_output = gr.Markdown()
|
| 458 |
|
| 459 |
+
|
| 460 |
recommend_btn.click(
|
| 461 |
+
fn=get_recommendation_with_ai,
|
| 462 |
+
inputs=[user_input, merchant_input, category_input, amount_input],
|
| 463 |
+
outputs=[recommendation_output, recommendation_chart]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
)
|
|
|
|
| 465 |
# Examples
|
| 466 |
gr.Markdown("### π Example Transactions")
|
| 467 |
gr.Examples(
|
|
|
|
| 725 |
analytics_status
|
| 726 |
]
|
| 727 |
)
|
| 728 |
+
# Tab 3: Chat (NEW!)
|
| 729 |
+
with gr.Tab("π¬ Ask AI"):
|
| 730 |
+
gr.Markdown("## Chat with RewardPilot AI")
|
| 731 |
+
gr.Markdown("*Ask questions about credit cards, rewards, and your spending*")
|
| 732 |
+
|
| 733 |
+
chatbot = gr.Chatbot(height=400, label="AI Assistant")
|
| 734 |
+
with gr.Row():
|
| 735 |
+
msg = gr.Textbox(
|
| 736 |
+
placeholder="Ask me anything about credit cards...",
|
| 737 |
+
label="Your Question",
|
| 738 |
+
scale=4
|
| 739 |
+
)
|
| 740 |
+
send_btn = gr.Button("Send", variant="primary", scale=1)
|
| 741 |
+
|
| 742 |
+
chat_user = gr.Dropdown(
|
| 743 |
+
choices=["u_alice", "u_bob", "u_charlie"],
|
| 744 |
+
label="Your Profile",
|
| 745 |
+
value="u_alice",
|
| 746 |
+
visible=True
|
| 747 |
+
)
|
| 748 |
+
|
| 749 |
+
def respond(message, chat_history, user_id):
|
| 750 |
+
"""Handle chat responses"""
|
| 751 |
+
if not message.strip():
|
| 752 |
+
return "", chat_history
|
| 753 |
+
|
| 754 |
+
# Get user context
|
| 755 |
+
try:
|
| 756 |
+
analytics = client.get_user_analytics(user_id)
|
| 757 |
+
user_context = {
|
| 758 |
+
'cards': analytics.get('data', {}).get('cards', []),
|
| 759 |
+
'monthly_spending': analytics.get('data', {}).get('total_spending', 0),
|
| 760 |
+
'top_category': analytics.get('data', {}).get('top_category', 'Unknown')
|
| 761 |
+
}
|
| 762 |
+
except:
|
| 763 |
+
user_context = {}
|
| 764 |
+
|
| 765 |
+
# Generate AI response
|
| 766 |
+
if config.LLM_ENABLED:
|
| 767 |
+
bot_response = llm.chat_response(message, user_context, chat_history)
|
| 768 |
+
else:
|
| 769 |
+
bot_response = "I'm currently in fallback mode. Please ask specific questions about your cards or transactions."
|
| 770 |
+
|
| 771 |
+
chat_history.append((message, bot_response))
|
| 772 |
+
return "", chat_history
|
| 773 |
+
|
| 774 |
+
msg.submit(respond, [msg, chatbot, chat_user], [msg, chatbot])
|
| 775 |
+
send_btn.click(respond, [msg, chatbot, chat_user], [msg, chatbot])
|
| 776 |
+
|
| 777 |
+
# Example questions
|
| 778 |
+
gr.Examples(
|
| 779 |
+
examples=[
|
| 780 |
+
["Which card should I use at Costco?"],
|
| 781 |
+
["How can I maximize my grocery rewards?"],
|
| 782 |
+
["What's the best travel card for international trips?"],
|
| 783 |
+
["Am I close to any spending caps?"],
|
| 784 |
+
],
|
| 785 |
+
inputs=[msg]
|
| 786 |
+
)
|
| 787 |
# ========== Tab 3: About ==========
|
| 788 |
with gr.Tab("βΉοΈ About"):
|
| 789 |
gr.Markdown(
|
|
|
|
| 793 |
RewardPilot is an AI-powered credit card recommendation system built using the **Model Context Protocol (MCP)** architecture.
|
| 794 |
|
| 795 |
### ποΈ Architecture
|
| 796 |
+
- π― **Model Context Protocol (MCP)** architecture
|
| 797 |
+
- π€ **LLM-powered explanations** using Llama 3.2
|
| 798 |
+
- π **RAG (Retrieval-Augmented Generation)** for card benefits
|
| 799 |
+
- π **ML-based spending forecasts**
|
| 800 |
+
- π **Interactive visualizations**
|
| 801 |
+
|
| 802 |
+
### Features
|
| 803 |
+
|
| 804 |
+
- Smart card recommendations for every purchase
|
| 805 |
+
- AI-generated personalized insights
|
| 806 |
+
- Visual analytics dashboard
|
| 807 |
+
- Conversational AI assistant
|
| 808 |
+
- Real-time cap warnings
|
| 809 |
+
- Multi-card comparison
|
| 810 |
|
| 811 |
The system consists of multiple microservices:
|
| 812 |
|