Update app.py
Browse files
app.py
CHANGED
|
@@ -8,6 +8,7 @@ import gradio as gr
|
|
| 8 |
import plotly.graph_objects as go
|
| 9 |
import httpx
|
| 10 |
import logging
|
|
|
|
| 11 |
|
| 12 |
logging.basicConfig(level=logging.INFO)
|
| 13 |
logger = logging.getLogger(__name__)
|
|
@@ -446,6 +447,7 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
|
|
| 446 |
|
| 447 |
yield f"β Invalid response: No recommendation found", None
|
| 448 |
return
|
|
|
|
| 449 |
|
| 450 |
|
| 451 |
card_id = recommendation.get('recommended_card', 'Unknown')
|
|
@@ -493,6 +495,26 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
|
|
| 493 |
print(f" Base rate: {base_rate}%")
|
| 494 |
print(f" Annual fee: ${annual_fee}")
|
| 495 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
|
| 497 |
# β
Calculate baseline comparison
|
| 498 |
baseline_rewards = annual_spending * 0.01
|
|
@@ -574,7 +596,25 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
|
|
| 574 |
|
| 575 |
---
|
| 576 |
"""
|
| 577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
if alternatives:
|
| 579 |
output += "\n### π Alternative Options\n\n"
|
| 580 |
output += "| Rank | Card | Rewards | Rate | Why Ranked Here? |\n"
|
|
@@ -656,6 +696,12 @@ def get_recommendation_with_agent(user_id, merchant, category, amount):
|
|
| 656 |
|
| 657 |
# β
Create chart
|
| 658 |
chart = create_agent_recommendation_chart_enhanced(recommendation)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 659 |
|
| 660 |
yield output, chart
|
| 661 |
|
|
@@ -729,6 +775,13 @@ client = RewardPilotClient(config.ORCHESTRATOR_URL)
|
|
| 729 |
# llm = get_llm_explainer()
|
| 730 |
gemini = get_gemini_explainer()
|
| 731 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 732 |
def get_recommendation(
|
| 733 |
user_id: str,
|
| 734 |
merchant: str,
|
|
@@ -2971,11 +3024,52 @@ with gr.Blocks(
|
|
| 2971 |
|
| 2972 |
|
| 2973 |
def respond(message, chat_history, user_id, use_voice=False, voice_name="Rachel", voice_speed=1.0):
|
| 2974 |
-
"""Enhanced chat with OpenAI GPT-4 and optional voice output"""
|
| 2975 |
|
| 2976 |
if not message.strip():
|
| 2977 |
return "", chat_history, None
|
| 2978 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2979 |
# Get user context (your existing logic)
|
| 2980 |
user_context = {}
|
| 2981 |
try:
|
|
@@ -3010,11 +3104,8 @@ with gr.Blocks(
|
|
| 3010 |
}
|
| 3011 |
]
|
| 3012 |
|
| 3013 |
-
# Build messages (your existing logic)
|
| 3014 |
-
|
| 3015 |
-
{
|
| 3016 |
-
"role": "system",
|
| 3017 |
-
"content": f"""You are CardWise AI, an expert credit card rewards optimizer.
|
| 3018 |
|
| 3019 |
User Context:
|
| 3020 |
- User ID: {user_context.get('user_id', 'Unknown')}
|
|
@@ -3024,6 +3115,21 @@ with gr.Blocks(
|
|
| 3024 |
|
| 3025 |
When voice mode is enabled, keep responses concise and conversational (under 200 words).
|
| 3026 |
Be helpful, actionable, and friendly."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3027 |
}
|
| 3028 |
]
|
| 3029 |
|
|
@@ -3088,6 +3194,10 @@ with gr.Blocks(
|
|
| 3088 |
else:
|
| 3089 |
bot_response = response_message.content
|
| 3090 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3091 |
print(f"β
GPT-4 response generated")
|
| 3092 |
|
| 3093 |
# Generate voice if enabled
|
|
@@ -3095,8 +3205,12 @@ with gr.Blocks(
|
|
| 3095 |
if use_voice and voice_assistant.enabled:
|
| 3096 |
try:
|
| 3097 |
logger.info(f"π€ Generating voice with {voice_name}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3098 |
audio_bytes = voice_assistant.text_to_speech(
|
| 3099 |
-
text=
|
| 3100 |
voice_name=voice_name
|
| 3101 |
)
|
| 3102 |
|
|
@@ -3124,7 +3238,6 @@ with gr.Blocks(
|
|
| 3124 |
chat_history.append((message, bot_response))
|
| 3125 |
return "", chat_history, audio_output
|
| 3126 |
|
| 3127 |
-
|
| 3128 |
|
| 3129 |
|
| 3130 |
def generate_fallback_response(message: str, user_context: dict) -> str:
|
|
@@ -3644,7 +3757,225 @@ with gr.Blocks(
|
|
| 3644 |
outputs=[receipt_output, receipt_chart]
|
| 3645 |
)
|
| 3646 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3647 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3648 |
# ==================== TAB 6: RESOURCES (About + Agent Insight + API Docs) ====================
|
| 3649 |
with gr.Tab("βΉοΈ Resources"):
|
| 3650 |
with gr.Tabs():
|
|
|
|
| 8 |
import plotly.graph_objects as go
|
| 9 |
import httpx
|
| 10 |
import logging
|
| 11 |
+
from utils.llamaindex_rag import get_card_benefits_rag, initialize_rag
|
| 12 |
|
| 13 |
logging.basicConfig(level=logging.INFO)
|
| 14 |
logger = logging.getLogger(__name__)
|
|
|
|
| 447 |
|
| 448 |
yield f"β Invalid response: No recommendation found", None
|
| 449 |
return
|
| 450 |
+
|
| 451 |
|
| 452 |
|
| 453 |
card_id = recommendation.get('recommended_card', 'Unknown')
|
|
|
|
| 495 |
print(f" Base rate: {base_rate}%")
|
| 496 |
print(f" Annual fee: ${annual_fee}")
|
| 497 |
|
| 498 |
+
llamaindex_context = None
|
| 499 |
+
spending_warnings = None
|
| 500 |
+
|
| 501 |
+
if rag.enabled: # rag was initialized at the top of the file
|
| 502 |
+
logger.info("π Fetching RAG context...")
|
| 503 |
+
|
| 504 |
+
# Get card-specific context
|
| 505 |
+
llamaindex_context = rag.get_card_context(
|
| 506 |
+
card_name=card_name, # This variable already exists in your code
|
| 507 |
+
merchant=merchant,
|
| 508 |
+
category=category
|
| 509 |
+
)
|
| 510 |
+
|
| 511 |
+
# Get spending warnings
|
| 512 |
+
spending_warnings = rag.get_spending_warnings(
|
| 513 |
+
card_name=card_name,
|
| 514 |
+
category=category,
|
| 515 |
+
amount=amount
|
| 516 |
+
)
|
| 517 |
+
|
| 518 |
|
| 519 |
# β
Calculate baseline comparison
|
| 520 |
baseline_rewards = annual_spending * 0.01
|
|
|
|
| 596 |
|
| 597 |
---
|
| 598 |
"""
|
| 599 |
+
|
| 600 |
+
if llamaindex_context:
|
| 601 |
+
output += f"""
|
| 602 |
+
|
| 603 |
+
---
|
| 604 |
+
|
| 605 |
+
### π Card Benefits & Details
|
| 606 |
+
**Powered by LlamaIndex RAG**
|
| 607 |
+
|
| 608 |
+
{llamaindex_context}
|
| 609 |
+
"""
|
| 610 |
+
|
| 611 |
+
# β
NEW: Add spending warnings
|
| 612 |
+
if spending_warnings:
|
| 613 |
+
output += f"""
|
| 614 |
+
|
| 615 |
+
### β οΈ Important to Know
|
| 616 |
+
{spending_warnings}
|
| 617 |
+
"""
|
| 618 |
if alternatives:
|
| 619 |
output += "\n### π Alternative Options\n\n"
|
| 620 |
output += "| Rank | Card | Rewards | Rate | Why Ranked Here? |\n"
|
|
|
|
| 696 |
|
| 697 |
# β
Create chart
|
| 698 |
chart = create_agent_recommendation_chart_enhanced(recommendation)
|
| 699 |
+
if llamaindex_context or spending_warnings:
|
| 700 |
+
output += f"""
|
| 701 |
+
---
|
| 702 |
+
|
| 703 |
+
*π€ Enhanced with AI-powered knowledge retrieval using LlamaIndex + OpenAI Embeddings*
|
| 704 |
+
"""
|
| 705 |
|
| 706 |
yield output, chart
|
| 707 |
|
|
|
|
| 775 |
# llm = get_llm_explainer()
|
| 776 |
gemini = get_gemini_explainer()
|
| 777 |
|
| 778 |
+
logger.info("π Initializing LlamaIndex RAG...")
|
| 779 |
+
card_rag = initialize_rag()
|
| 780 |
+
if card_rag.enabled:
|
| 781 |
+
logger.info("β
RAG system ready")
|
| 782 |
+
else:
|
| 783 |
+
logger.warning("β οΈ RAG system not available")
|
| 784 |
+
|
| 785 |
def get_recommendation(
|
| 786 |
user_id: str,
|
| 787 |
merchant: str,
|
|
|
|
| 3024 |
|
| 3025 |
|
| 3026 |
def respond(message, chat_history, user_id, use_voice=False, voice_name="Rachel", voice_speed=1.0):
|
| 3027 |
+
"""Enhanced chat with OpenAI GPT-4, LlamaIndex RAG, and optional voice output"""
|
| 3028 |
|
| 3029 |
if not message.strip():
|
| 3030 |
return "", chat_history, None
|
| 3031 |
|
| 3032 |
+
# β
NEW: Check if question is about card benefits (RAG integration)
|
| 3033 |
+
rag = get_card_benefits_rag()
|
| 3034 |
+
rag_context = None
|
| 3035 |
+
|
| 3036 |
+
if rag.enabled:
|
| 3037 |
+
# Detect if question is about specific card
|
| 3038 |
+
card_keywords = ["amex", "gold", "chase", "sapphire", "reserve", "freedom", "unlimited",
|
| 3039 |
+
"citi", "double cash", "discover", "card", "benefit", "reward", "point",
|
| 3040 |
+
"cashback", "annual fee", "cap", "limit", "grocery", "dining", "travel"]
|
| 3041 |
+
|
| 3042 |
+
message_lower = message.lower()
|
| 3043 |
+
if any(keyword in message_lower for keyword in card_keywords):
|
| 3044 |
+
logger.info("π Detected card-specific question, querying RAG...")
|
| 3045 |
+
|
| 3046 |
+
# Extract card name (simple heuristic)
|
| 3047 |
+
card_name = None
|
| 3048 |
+
if "sapphire reserve" in message_lower or "csr" in message_lower:
|
| 3049 |
+
card_name = "Chase Sapphire Reserve"
|
| 3050 |
+
elif "freedom unlimited" in message_lower or "cfu" in message_lower:
|
| 3051 |
+
card_name = "Chase Freedom Unlimited"
|
| 3052 |
+
elif "double cash" in message_lower:
|
| 3053 |
+
card_name = "Citi Double Cash"
|
| 3054 |
+
elif "discover" in message_lower:
|
| 3055 |
+
card_name = "Discover it"
|
| 3056 |
+
elif "amex gold" in message_lower or "american express gold" in message_lower:
|
| 3057 |
+
card_name = "Amex Gold"
|
| 3058 |
+
elif "gold" in message_lower and ("amex" in message_lower or "american express" in message_lower):
|
| 3059 |
+
card_name = "Amex Gold"
|
| 3060 |
+
|
| 3061 |
+
# Query RAG if card was identified
|
| 3062 |
+
if card_name:
|
| 3063 |
+
try:
|
| 3064 |
+
rag_context = rag.query_benefits(card_name, message)
|
| 3065 |
+
if rag_context:
|
| 3066 |
+
logger.info(f"β
RAG context retrieved: {len(rag_context)} chars for {card_name}")
|
| 3067 |
+
else:
|
| 3068 |
+
logger.warning(f"β οΈ RAG returned no context for {card_name}")
|
| 3069 |
+
except Exception as e:
|
| 3070 |
+
logger.error(f"β RAG query failed: {e}")
|
| 3071 |
+
rag_context = None
|
| 3072 |
+
|
| 3073 |
# Get user context (your existing logic)
|
| 3074 |
user_context = {}
|
| 3075 |
try:
|
|
|
|
| 3104 |
}
|
| 3105 |
]
|
| 3106 |
|
| 3107 |
+
# Build messages (your existing logic with RAG enhancement)
|
| 3108 |
+
system_content = f"""You are CardWise AI, an expert credit card rewards optimizer.
|
|
|
|
|
|
|
|
|
|
| 3109 |
|
| 3110 |
User Context:
|
| 3111 |
- User ID: {user_context.get('user_id', 'Unknown')}
|
|
|
|
| 3115 |
|
| 3116 |
When voice mode is enabled, keep responses concise and conversational (under 200 words).
|
| 3117 |
Be helpful, actionable, and friendly."""
|
| 3118 |
+
|
| 3119 |
+
# β
NEW: Add RAG context if available
|
| 3120 |
+
if rag_context:
|
| 3121 |
+
system_content += f"""
|
| 3122 |
+
|
| 3123 |
+
π KNOWLEDGE BASE CONTEXT:
|
| 3124 |
+
{rag_context}
|
| 3125 |
+
|
| 3126 |
+
Use this information to provide accurate, detailed answers about card benefits.
|
| 3127 |
+
Always cite specific details from the knowledge base when relevant."""
|
| 3128 |
+
|
| 3129 |
+
messages = [
|
| 3130 |
+
{
|
| 3131 |
+
"role": "system",
|
| 3132 |
+
"content": system_content
|
| 3133 |
}
|
| 3134 |
]
|
| 3135 |
|
|
|
|
| 3194 |
else:
|
| 3195 |
bot_response = response_message.content
|
| 3196 |
|
| 3197 |
+
# β
NEW: Add RAG attribution if context was used
|
| 3198 |
+
if rag_context:
|
| 3199 |
+
bot_response += "\n\n*π Enhanced with knowledge base using LlamaIndex*"
|
| 3200 |
+
|
| 3201 |
print(f"β
GPT-4 response generated")
|
| 3202 |
|
| 3203 |
# Generate voice if enabled
|
|
|
|
| 3205 |
if use_voice and voice_assistant.enabled:
|
| 3206 |
try:
|
| 3207 |
logger.info(f"π€ Generating voice with {voice_name}")
|
| 3208 |
+
|
| 3209 |
+
# β
IMPROVED: Clean response for voice (remove RAG attribution)
|
| 3210 |
+
voice_text = bot_response.replace("*π Enhanced with knowledge base using LlamaIndex*", "").strip()
|
| 3211 |
+
|
| 3212 |
audio_bytes = voice_assistant.text_to_speech(
|
| 3213 |
+
text=voice_text,
|
| 3214 |
voice_name=voice_name
|
| 3215 |
)
|
| 3216 |
|
|
|
|
| 3238 |
chat_history.append((message, bot_response))
|
| 3239 |
return "", chat_history, audio_output
|
| 3240 |
|
|
|
|
| 3241 |
|
| 3242 |
|
| 3243 |
def generate_fallback_response(message: str, user_context: dict) -> str:
|
|
|
|
| 3757 |
outputs=[receipt_output, receipt_chart]
|
| 3758 |
)
|
| 3759 |
|
| 3760 |
+
|
| 3761 |
+
# ==================== TAB: CARD KNOWLEDGE BASE (RAG) ====================
|
| 3762 |
+
with gr.Tab("π Knowledge Base"):
|
| 3763 |
+
rag = get_card_benefits_rag()
|
| 3764 |
|
| 3765 |
+
if rag.enabled:
|
| 3766 |
+
gr.HTML("""
|
| 3767 |
+
<div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| 3768 |
+
padding: 30px; border-radius: 16px; color: white; margin-bottom: 25px;">
|
| 3769 |
+
<h2 style="margin: 0 0 15px 0;">π AI-Powered Card Knowledge Base</h2>
|
| 3770 |
+
<p style="margin: 0; font-size: 17px; line-height: 1.7;">
|
| 3771 |
+
Search our comprehensive credit card database using <strong>LlamaIndex RAG</strong>
|
| 3772 |
+
powered by OpenAI embeddings and GPT-4.
|
| 3773 |
+
</p>
|
| 3774 |
+
<div style="background: rgba(255,255,255,0.15); padding: 15px;
|
| 3775 |
+
border-radius: 10px; margin-top: 15px;">
|
| 3776 |
+
<p style="margin: 0; font-size: 15px;">
|
| 3777 |
+
π <strong>Semantic Search:</strong> Ask natural language questions<br>
|
| 3778 |
+
π― <strong>Accurate Answers:</strong> Powered by vector embeddings<br>
|
| 3779 |
+
β‘ <strong>Real-time:</strong> Instant retrieval from knowledge base
|
| 3780 |
+
</p>
|
| 3781 |
+
</div>
|
| 3782 |
+
</div>
|
| 3783 |
+
""")
|
| 3784 |
+
|
| 3785 |
+
gr.Markdown("## π Search Card Benefits")
|
| 3786 |
+
|
| 3787 |
+
with gr.Row():
|
| 3788 |
+
kb_card = gr.Dropdown(
|
| 3789 |
+
choices=[
|
| 3790 |
+
"Amex Gold",
|
| 3791 |
+
"American Express Gold",
|
| 3792 |
+
"Chase Sapphire Reserve",
|
| 3793 |
+
"Chase Freedom Unlimited",
|
| 3794 |
+
"Citi Double Cash",
|
| 3795 |
+
"Discover it"
|
| 3796 |
+
],
|
| 3797 |
+
label="π³ Select Card",
|
| 3798 |
+
value="Amex Gold",
|
| 3799 |
+
scale=1
|
| 3800 |
+
)
|
| 3801 |
+
kb_query = gr.Textbox(
|
| 3802 |
+
label="β Your Question",
|
| 3803 |
+
placeholder="e.g., Does this card work at Costco for groceries?",
|
| 3804 |
+
scale=3
|
| 3805 |
+
)
|
| 3806 |
+
|
| 3807 |
+
kb_search_btn = gr.Button("π Search Knowledge Base", variant="primary", size="lg")
|
| 3808 |
+
|
| 3809 |
+
kb_result = gr.Markdown(value="*Enter a question and click Search*")
|
| 3810 |
+
|
| 3811 |
+
def search_knowledge_base(card, question):
|
| 3812 |
+
"""Search card benefits using LlamaIndex RAG"""
|
| 3813 |
+
if not question or not question.strip():
|
| 3814 |
+
return "β οΈ Please enter a question"
|
| 3815 |
+
|
| 3816 |
+
rag = get_card_benefits_rag()
|
| 3817 |
+
|
| 3818 |
+
if not rag.enabled:
|
| 3819 |
+
return """
|
| 3820 |
+
## β οΈ RAG Not Available
|
| 3821 |
+
|
| 3822 |
+
The LlamaIndex RAG system is not currently enabled. This could be due to:
|
| 3823 |
+
|
| 3824 |
+
1. Missing dependencies (llama-index not installed)
|
| 3825 |
+
2. No OpenAI API key configured
|
| 3826 |
+
3. No card benefit documents in data/card_benefits/
|
| 3827 |
+
|
| 3828 |
+
Please check the logs for more details.
|
| 3829 |
+
"""
|
| 3830 |
+
|
| 3831 |
+
try:
|
| 3832 |
+
logger.info(f"π Knowledge base search: {card} - {question}")
|
| 3833 |
+
result = rag.query_benefits(card, question)
|
| 3834 |
+
|
| 3835 |
+
if result:
|
| 3836 |
+
return f"""
|
| 3837 |
+
## π― Answer for {card}
|
| 3838 |
+
|
| 3839 |
+
{result}
|
| 3840 |
+
|
| 3841 |
+
---
|
| 3842 |
+
|
| 3843 |
+
### π Search Details
|
| 3844 |
+
- **Card:** {card}
|
| 3845 |
+
- **Question:** {question}
|
| 3846 |
+
- **Source:** LlamaIndex RAG with GPT-4
|
| 3847 |
+
- **Embeddings:** OpenAI text-embedding-3-small
|
| 3848 |
+
|
| 3849 |
+
*π‘ This answer was retrieved using semantic search across our card benefits knowledge base.*
|
| 3850 |
+
"""
|
| 3851 |
+
else:
|
| 3852 |
+
return f"""
|
| 3853 |
+
## β No Results Found
|
| 3854 |
+
|
| 3855 |
+
Could not find relevant information about **{card}** for your question:
|
| 3856 |
+
|
| 3857 |
+
> {question}
|
| 3858 |
+
|
| 3859 |
+
### π‘ Suggestions:
|
| 3860 |
+
- Try rephrasing your question
|
| 3861 |
+
- Check if the card name is correct
|
| 3862 |
+
- Ask about specific features (earning rates, caps, exclusions)
|
| 3863 |
+
"""
|
| 3864 |
+
|
| 3865 |
+
except Exception as e:
|
| 3866 |
+
logger.error(f"β Knowledge base search failed: {e}")
|
| 3867 |
+
return f"## β Search Error\n\nAn error occurred: {str(e)}"
|
| 3868 |
+
|
| 3869 |
+
kb_search_btn.click(
|
| 3870 |
+
fn=search_knowledge_base,
|
| 3871 |
+
inputs=[kb_card, kb_query],
|
| 3872 |
+
outputs=[kb_result]
|
| 3873 |
+
)
|
| 3874 |
+
|
| 3875 |
+
gr.Markdown("---")
|
| 3876 |
+
gr.Markdown("### π‘ Example Questions")
|
| 3877 |
+
|
| 3878 |
+
gr.Examples(
|
| 3879 |
+
examples=[
|
| 3880 |
+
["Amex Gold", "Does this card work at Costco for groceries?"],
|
| 3881 |
+
["Amex Gold", "What's the annual spending cap on grocery purchases?"],
|
| 3882 |
+
["Chase Sapphire Reserve", "What travel benefits does this card offer?"],
|
| 3883 |
+
["Chase Sapphire Reserve", "Does Uber count as travel for earning points?"],
|
| 3884 |
+
["Chase Freedom Unlimited", "What's the earning rate on dining?"],
|
| 3885 |
+
["Citi Double Cash", "How does the 2% cash back work?"],
|
| 3886 |
+
["Discover it", "What are the rotating categories this quarter?"],
|
| 3887 |
+
],
|
| 3888 |
+
inputs=[kb_card, kb_query],
|
| 3889 |
+
label="Try these questions"
|
| 3890 |
+
)
|
| 3891 |
+
|
| 3892 |
+
# Card comparison feature
|
| 3893 |
+
gr.Markdown("---")
|
| 3894 |
+
gr.Markdown("## βοΈ Compare Cards")
|
| 3895 |
+
|
| 3896 |
+
with gr.Row():
|
| 3897 |
+
compare_card1 = gr.Dropdown(
|
| 3898 |
+
choices=["Amex Gold", "Chase Sapphire Reserve", "Chase Freedom Unlimited",
|
| 3899 |
+
"Citi Double Cash", "Discover it"],
|
| 3900 |
+
label="Card 1",
|
| 3901 |
+
value="Amex Gold"
|
| 3902 |
+
)
|
| 3903 |
+
compare_card2 = gr.Dropdown(
|
| 3904 |
+
choices=["Amex Gold", "Chase Sapphire Reserve", "Chase Freedom Unlimited",
|
| 3905 |
+
"Citi Double Cash", "Discover it"],
|
| 3906 |
+
label="Card 2",
|
| 3907 |
+
value="Chase Sapphire Reserve"
|
| 3908 |
+
)
|
| 3909 |
+
compare_category = gr.Dropdown(
|
| 3910 |
+
choices=["Dining", "Groceries", "Travel", "Gas", "General Spending"],
|
| 3911 |
+
label="Category",
|
| 3912 |
+
value="Dining"
|
| 3913 |
+
)
|
| 3914 |
+
|
| 3915 |
+
compare_btn = gr.Button("βοΈ Compare Cards", variant="secondary", size="lg")
|
| 3916 |
+
compare_result = gr.Markdown(value="*Select cards and click Compare*")
|
| 3917 |
+
|
| 3918 |
+
def compare_cards_ui(card1, card2, category):
|
| 3919 |
+
"""Compare two cards for a specific category"""
|
| 3920 |
+
if card1 == card2:
|
| 3921 |
+
return "β οΈ Please select two different cards"
|
| 3922 |
+
|
| 3923 |
+
rag = get_card_benefits_rag()
|
| 3924 |
+
|
| 3925 |
+
if not rag.enabled:
|
| 3926 |
+
return "β οΈ RAG not available"
|
| 3927 |
+
|
| 3928 |
+
try:
|
| 3929 |
+
result = rag.compare_cards(card1, card2, category)
|
| 3930 |
+
|
| 3931 |
+
if result:
|
| 3932 |
+
return f"""
|
| 3933 |
+
## βοΈ Comparison: {card1} vs {card2}
|
| 3934 |
+
|
| 3935 |
+
### Category: {category}
|
| 3936 |
+
|
| 3937 |
+
{result}
|
| 3938 |
+
|
| 3939 |
+
---
|
| 3940 |
+
|
| 3941 |
+
*π Powered by LlamaIndex RAG*
|
| 3942 |
+
"""
|
| 3943 |
+
else:
|
| 3944 |
+
return "β Could not generate comparison"
|
| 3945 |
+
|
| 3946 |
+
except Exception as e:
|
| 3947 |
+
logger.error(f"β Comparison failed: {e}")
|
| 3948 |
+
return f"β Error: {str(e)}"
|
| 3949 |
+
|
| 3950 |
+
compare_btn.click(
|
| 3951 |
+
fn=compare_cards_ui,
|
| 3952 |
+
inputs=[compare_card1, compare_card2, compare_category],
|
| 3953 |
+
outputs=[compare_result]
|
| 3954 |
+
)
|
| 3955 |
+
|
| 3956 |
+
else:
|
| 3957 |
+
# RAG not available
|
| 3958 |
+
gr.HTML("""
|
| 3959 |
+
<div style="background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
|
| 3960 |
+
padding: 30px; border-radius: 16px; color: white; margin-bottom: 25px;">
|
| 3961 |
+
<h2 style="margin: 0 0 15px 0;">β οΈ Knowledge Base Not Available</h2>
|
| 3962 |
+
<p style="margin: 0; font-size: 17px;">
|
| 3963 |
+
The LlamaIndex RAG system requires additional setup.
|
| 3964 |
+
</p>
|
| 3965 |
+
</div>
|
| 3966 |
+
""")
|
| 3967 |
+
|
| 3968 |
+
gr.Markdown("""
|
| 3969 |
+
## π§ Setup Required
|
| 3970 |
+
|
| 3971 |
+
To enable the Knowledge Base:
|
| 3972 |
+
|
| 3973 |
+
1. Install dependencies: `pip install llama-index`
|
| 3974 |
+
2. Add OpenAI API key
|
| 3975 |
+
3. Create card benefit documents in `data/card_benefits/`
|
| 3976 |
+
4. Restart the application
|
| 3977 |
+
""")
|
| 3978 |
+
|
| 3979 |
# ==================== TAB 6: RESOURCES (About + Agent Insight + API Docs) ====================
|
| 3980 |
with gr.Tab("βΉοΈ Resources"):
|
| 3981 |
with gr.Tabs():
|