Upload all the files
Browse files- app.py +3 -0
- cards_eligibility_updated.csv +89 -0
- credit_card_data_updated.csv +89 -0
- data.py +205 -0
- langgraph_pipeline.py +200 -0
- nodes/agent.py +75 -0
- nodes/chat.py +128 -0
- nodes/compare.py +85 -0
- nodes/format.py +210 -0
- nodes/intent.py +183 -0
- pydantic_schema.py +53 -0
- recommender/graph_retrieval.py +284 -0
- recommender/vectordb.py +150 -0
- recommender/vectordb_retrieval.py +93 -0
- requirements.txt +29 -0
- ui/gradio_interface.py +362 -0
app.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from ui.gradio_interface import demo
|
| 2 |
+
|
| 3 |
+
demo.launch(share=True)
|
cards_eligibility_updated.csv
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Name,Bank,Minimum Age,Maximum Age,Minimum Income (LPA),Minimum Credit Score,Joining fee,Annual fee,Issuer Link
|
| 2 |
+
Cashback SBI Card,SBI,21,70,5,750,999,999,https://www.sbicard.com/en/personal/credit-cards/rewards/cashback-sbi-card.page
|
| 3 |
+
BPCL SBI Card Octane,SBI,21,70,3.6,700,1499,1499,https://www.sbicard.com/en/personal/credit-cards/travel/bpcl-sbi-card-octane.page
|
| 4 |
+
SBI Card ELITE,SBI,21,70,10,750,4999,4999,https://www.sbicard.com/en/personal/credit-cards/lifestyle/sbi-card-elite.page
|
| 5 |
+
Yatra SBI Card,SBI,21,70,3,700,499,499,https://www.sbicard.com/en/personal/credit-cards/travel/yatra-sbi-card.page
|
| 6 |
+
IRCTC SBI Card Premier,SBI,21,70,4,700,1499,1499,https://www.sbicard.com/en/personal/credit-cards/travel/irctc-premier-card.page
|
| 7 |
+
SBI SimplyCLICK Credit Card,SBI,21,70,3,700,499,499,https://www.sbicard.com/en/personal/credit-cards/shopping/simplyclick-sbi-card.page
|
| 8 |
+
SBI SimplySAVE Credit Card,SBI,21,70,3,700,499,499,https://www.sbicard.com/en/personal/credit-cards/shopping/simplysave-sbi-card.page
|
| 9 |
+
SBI Card Miles Elite,SBI,21,70,8,750,4999,4999,https://www.sbicard.com/en/personal/credit-cards/travel/sbi-card-miles-elite.page
|
| 10 |
+
Titan SBI Credit Card,SBI,21,70,3,700,2999,2999,https://www.sbicard.com/en/personal/credit-cards/shopping/titan-sbi-card.page
|
| 11 |
+
HDFC Diners Club Privilege Credit Card,HDFC,21,65,8.4,750,1000,1000,https://www.hdfcbank.com/personal/pay/cards/credit-cards/diners-privilege
|
| 12 |
+
HDFC Regalia Credit Card,HDFC,21,65,12,750,2500,2500,https://cardinsider.com/hdfc-bank/hdfc-bank-regalia-credit-card/
|
| 13 |
+
HDFC MoneyBack+ Credit Card,HDFC,21,65,2.4,700,500,500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/moneyback-plus
|
| 14 |
+
HDFC Millennia Credit Card,HDFC,21,40,6,700,1000,1000,https://www.hdfcbank.com/personal/pay/cards/millennia-cards/millennia-cc-new
|
| 15 |
+
HDFC Infinia Credit Card,HDFC,21,65,60,750,12500,12500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/infinia-credit-card
|
| 16 |
+
HDFC Diners Club Black Credit Card,HDFC,21,65,21,750,10000,10000,https://www.hdfcbank.com/personal/pay/cards/credit-cards/diners-club-black
|
| 17 |
+
6E Rewards XL Indigo HDFC Credit Card,HDFC,21,65,6,700,1500,1500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/6e-rewards-xl-indigo-hdfc-bank-credit-card
|
| 18 |
+
HDFC Intermiles Signature Credit Card,HDFC,21,65,9,750,2500,2500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/intermiles-hdfc-bank-signature
|
| 19 |
+
IndianOil HDFC Credit Card,HDFC,21,65,2,700,500,500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/indianoil-hdfc-bank-credit-card
|
| 20 |
+
HDFC Regalia Gold Credit Card,HDFC,21,65,12,750,2500,2500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/regalia-gold-credit-card
|
| 21 |
+
IRCTC HDFC Bank Credit Card,HDFC,21,65,6,700,500,500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/irctc-credit-card
|
| 22 |
+
Marriott Bonvoy HDFC Bank Credit Card,HDFC,21,60,12,750,3000,3000,https://www.hdfcbank.com/personal/pay/cards/credit-cards/marriott-bonvoy-credit-card
|
| 23 |
+
Swiggy HDFC Bank Credit Card,HDFC,21,60,1.8,750,500,500,https://www.hdfcbank.com/personal/pay/cards/credit-cards/swiggy-hdfc-bank-credit-card
|
| 24 |
+
Axis Bank Reserve Credit Card,Axis,18,70,48,750,50000,50000,https://www.axisbank.com/retail/cards/credit-card/reserve-credit-card/index
|
| 25 |
+
Axis Magnus Credit Card,Axis,18,70,24,750,12500,12500,https://www.axisbank.com/retail/cards/credit-card/axis-bank-magnus-card
|
| 26 |
+
Axis Atlas Credit Card,Axis,18,70,12,700,5000,5000,https://www.axisbank.com/retail/cards/credit-card/axis-bank-atlas-credit-card
|
| 27 |
+
Axis Bank Horizon Credit Card,Axis,18,70,6,700,3000,3000,https://www.axisbank.com/retail/cards/credit-card/axis-horizon-credit-card
|
| 28 |
+
Axis Bank SELECT Credit Card,Axis,18,70,9,700,3000,3000,https://www.axisbank.com/retail/cards/credit-card/axis-bank-select-credit-card
|
| 29 |
+
Flipkart Axis Bank Credit Card,Axis,18,70,1.8,700,500,500,https://www.axisbank.com/retail/cards/credit-card/flipkart-axisbank-credit-card
|
| 30 |
+
Axis My Zone Credit Card,Axis,18,70,6,700,500,500,https://www.axisbank.com/retail/cards/credit-card/my-zone-credit-card
|
| 31 |
+
IndianOil Axis Bank Credit Card,Axis,18,70,6,700,500,500,https://www.axisbank.com/retail/cards/credit-card/indianoil-axis-bank-credit-card
|
| 32 |
+
Axis Bank ACE Credit Card,Axis,18,70,6,700,499,499,https://www.axisbank.com/retail/cards/credit-card/axis-bank-ace-credit-card
|
| 33 |
+
Amazon Pay ICICI Credit Card,ICICI,18,60,3,750,0,0,https://www.icicibank.com/personal-banking/cards/credit-card/amazon-pay-credit-card/index
|
| 34 |
+
ICICI Bank Sapphiro Credit Card,ICICI,21,65,3,750,6500,3500,https://www.icicibank.com/personal-banking/cards/credit-card/sapphiro-credit-card/key-privileges
|
| 35 |
+
MakeMyTrip ICICI Bank Signature Credit Card,ICICI,21,65,3,750,2500,0,https://www.icicibank.com/personal-banking/cards/credit-card/makemytrip/signature-card/key-privileges
|
| 36 |
+
ICICI Coral Credit Card,ICICI,18,70,1,750,500,500,https://www.icicibank.com/personal-banking/cards/credit-card/coral-credit-card/benefits
|
| 37 |
+
ICICI Emeralde Private Metal Credit Card,ICICI,21,65,36,700,12499,12499,https://www.icicibank.com/personal-banking/cards/credit-card/emeralde-private-metal-credit-card
|
| 38 |
+
ICICI HPCL Super Saver Credit Card,ICICI,21,65,3,750,500,500,https://www.icicibank.com/personal-banking/cards/credit-card/hpcl-super-saver
|
| 39 |
+
Adani One ICICI Bank Signature Credit Card,ICICI,21,65,3,750,5000,5000,https://www.icicibank.com/personal-banking/cards/credit-card/adani-icici-credit-card
|
| 40 |
+
YES Private Credit Card,YES,21,60,4.8,700,50000,10000,https://www.yesbank.in/personal-banking/yes-private-credit-card
|
| 41 |
+
YES Bank Marquee Credit Card,YES,21,60,3.5,700,9999,4999,https://www.yesbank.in/personal-banking/yes-individual/cards/credit-cards/marquee-credit-card
|
| 42 |
+
YES Bank RESERV Credit Card,YES,21,60,3.5,700,2499,2499,"https://www.yesbank.in/personal-banking/cards/credit-card/reserv-credit-card#:~:text=What%20comes%20with%20this%20card%3F%20What%27s%20more%3F%20How,the%20ultimate%20in%20convenience%20and%20luxury.%20Apply%20today%21"
|
| 43 |
+
YES Bank Paisabazaar PaisaSave Credit Card,YES,21,60,3.5,700,0,499,https://www.yesbank.in/personal-banking/yes-individual/cards/credit-cards/paisabazaar-credit-card
|
| 44 |
+
YES Bank Wellness Plus Credit Card,YES,21,60,5,700,1499,1499,https://www.yesbank.in/personal-banking/yes-individual/cards/credit-cards/wellness-plus-cards
|
| 45 |
+
YES Bank ELITE+ Credit Card,YES,21,60,3.5,700,999,999,https://www.yesbank.in/personal-banking/yes-individual/cards/credit-cards/elite-plus-credit-card
|
| 46 |
+
YES Bank SELECT Credit Card,YES,21,60,3.5,700,599,599,https://www.yesbank.in/personal-banking/yes-individual/cards/credit-cards/select-credit-card
|
| 47 |
+
YES Bank ACE Credit Card,YES,21,60,3,700,499,499,https://www.yesbank.in/personal-banking/yes-individual/cards/credit-cards/ace-credit-card
|
| 48 |
+
HSBC Taj Credit Card,HSBC,25,75,40,750,110000,110000,https://www.hsbc.co.in/credit-cards/products/taj/
|
| 49 |
+
HSBC TravelOne Credit Card,HSBC,18,65,4,750,4999,4999,https://www.hsbc.co.in/credit-cards/products/travelone/
|
| 50 |
+
HSBC Premier MasterCard Credit Card,HSBC,21,65,5,750,12000,20000,https://www.hsbc.co.in/credit-cards/products/premier/
|
| 51 |
+
HSBC Live+ Credit Card,HSBC,18,65,4,750,999,999,https://www.hsbc.co.in/credit-cards/products/live-plus/
|
| 52 |
+
HSBC Visa Platinum Credit Card,HSBC,18,65,4,750,0,0,https://www.hsbc.co.in/credit-cards/products/visa-platinum/
|
| 53 |
+
IDFC FIRST Power+ Credit Card,IDFC,21,65,3,750,499,499,https://www.idfcfirstbank.com/credit-card/hpcl-power-fuel-credit-card
|
| 54 |
+
IDFC FIRST Wealth Credit Card,IDFC,21,65,36,750,0,0,https://www.idfcfirstbank.com/credit-card/wealth
|
| 55 |
+
IDFC FIRST Select Credit Card,IDFC,21,65,12,750,0,0,https://www.idfcfirstbank.com/credit-card/select
|
| 56 |
+
IDFC FIRST Millennia Credit Card,IDFC,21,65,3,750,0,0,https://www.idfcfirstbank.com/credit-card/millennia
|
| 57 |
+
IDFC FIRST Mayura Credit Card,IDFC,21,65,25,750,5999,5999,https://www.idfcfirstbank.com/credit-card/metal-credit-card/mayura
|
| 58 |
+
IDFC FIRST Ashva Credit Card,IDFC,21,65,3,750,2999,2999,https://www.idfcfirstbank.com/credit-card/metal-credit-card/ashva
|
| 59 |
+
American Express Platinum Card,American Express,18,60,6,750,66000,66000,https://www.americanexpress.com/in/charge-cards/platinum-card/?linknav=in-pc-cc-type-premium-CreditCards-CardTypes-PremiumCards-PlatinumCard-LearnMore
|
| 60 |
+
American Express Platinum Travel Credit Card,American Express,18,60,6,750,5000,5000,https://www.americanexpress.com/in/credit-cards/platinum-travel-credit-card/?linknav=in-amex-cardshop-allcards-learn-AmericanExpressPlatinumTravel-carousel
|
| 61 |
+
American Express Membership Rewards Credit Card,American Express,18,60,6,750,1000,4500,https://www.americanexpress.com/in/credit-cards/membership-rewards-card/?linknav=in-amex-cardshop-allcards-learn-AmericanExpressMembershipRewards-carousel
|
| 62 |
+
American Express Platinum ReserveSM Credit Card,American Express,18,60,6,750,10000,10000,https://www.americanexpress.com/in/credit-cards/platinum-reserve-credit-card/?linknav=in-amex-cardshop-details-browse-AmericanExpressPlatinumReserve
|
| 63 |
+
American Express Gold Card,American Express,18,60,6,750,1000,4500,https://www.americanexpress.com/in/charge-cards/gold-card/?linknav=in-amex-cardshop-details-browse-AmericanExpressGold
|
| 64 |
+
American Express SmartEarn Credit Card,American Express,18,60,4.5,750,495,495,https://www.americanexpress.com/in/credit-cards/smart-earn-credit-card/?linknav=in-amex-cardshop-details-browse-SmartEarnCreditCard
|
| 65 |
+
American Express PAYBACK Credit Card,American Express,18,60,6,750,750,1500,https://www.americanexpress.com/in/credit-cards/payback-card/?linknav=in-amex-cardshop-details-browse-AmericanExpressPayback
|
| 66 |
+
IDFC FIRST EARN Credit Card,IDFC,18,65,2.5,0,500,500,https://www.idfcfirstbank.com/credit-card/secured-rupay-credit-card
|
| 67 |
+
IDFC FIRST WoW Credit Card,IDFC,18,65,2.5,0,0,0,https://www.idfcfirstbank.com/credit-card/wow
|
| 68 |
+
Magnet FD Backed Credit Card,SMB,18,65,2.5,0,0,0,"https://cardinsider.com/fintech-cards/magnet-fd-backed-credit-card/#:~:text=Offering%20lifetime%20free%20membership%2C%20the%20Magnet%20Credit%20Card,Bank%2C%20earning%20up%20to%207%25%20interest%20per%20annum."
|
| 69 |
+
OneCard Metal Credit Card,Federal Bank,18,65,4,700,0,0,https://apply.getonecard.app/onecardweb/signUp
|
| 70 |
+
AU Nomo Credit Card,AU Bank,18,75,2.5,0,199,0,https://www.aubank.in/personal-banking/credit-cards/nomo-credit-card%20
|
| 71 |
+
SBI Unnati Credit Card,SBI,18,65,3,0,0,499,https://www.sbicard.com/en/personal/credit-cards/shopping/sbi-card-unnati.page
|
| 72 |
+
IDBI Bank Imperium Platinum Credit Card,IDBI,18,65,2.5,0,499,499,https://www.idbibank.in/Imperium-credit-card.aspx
|
| 73 |
+
SBI Student Plus Advantage Credit Card,SBI,18,60,2.5,0,0,0,https://www.bankbazaar.com/credit-card/sbi-student-plus-advantage-credit-card.html
|
| 74 |
+
ICICI Instant Platinum Credit Card,ICICI,18,65,2.5,0,0,0,https://www.icicibank.com/personal-banking/cards/credit-card/platinum-credit-key-privileges
|
| 75 |
+
Kotak 811 #DreamDifferent Credit Card,Kotak Mahindra Bank,18,75,2.5,0,0,0,https://www.kotak.com/en/personal-banking/cards/credit-cards/811-dream-different-credit-card.html
|
| 76 |
+
HDFC Bank Diners Club Black Metal Edition Credit Card,HDFC,21,65,21,750,10000,10000,https://www.hdfcbank.com/personal/pay/cards/credit-cards/diners-club-black-metal-edition
|
| 77 |
+
Tata Neu Infinity HDFC Bank Credit Card,HDFC,21,65,12,750,1499,1499,https://www.hdfcbank.com/personal/pay/cards/credit-cards/tata-neu-infinity-hdfc-bank-credit-card
|
| 78 |
+
Times Black ICICI Bank Credit Card,ICICI,21,60,24,750,20000,20000,https://www.icicibank.com/personal-banking/cards/credit-card/times-black-icici-credit-card
|
| 79 |
+
HDFC Bank BizBlack Metal Edition Credit Card,HDFC,21,65,30,750,10000,10000,https://www.hdfcbank.com/personal/pay/cards/business-credit-cards/biz-black
|
| 80 |
+
HDFC Bank BizPower Credit Card,HDFC,21,65,12,750,2500,2500,https://www.hdfcbank.com/personal/pay/cards/business-credit-cards/biz-power
|
| 81 |
+
Emirates Skywards ICICI Bank Rubyx Credit Card,ICICI,21,65,9,700,1000,1000,https://www.icicibank.com/personal-banking/cards/credit-card/emirates-rubyx-credit-card
|
| 82 |
+
EazyDiner IndusInd Bank Credit Card,IndusInd,21,65,2.4,750,1999,1999,https://www.indusind.com/in/en/personal/cards/credit-card/eazydiner-credit-card.html?utm_source=Yahoo_Organic_Search
|
| 83 |
+
Axis Bank Vistara Infinite Credit Card,Axis,18,70,6,750,10000,10000,https://www.axisbank.com/retail/cards/credit-card/axis-bank-vistara-infinite-credit-card
|
| 84 |
+
Axis Bank Neo Credit Card,Axis,18,70,2.5,700,250,250,https://www.axisbank.com/retail/cards/credit-card/neo-credit-card
|
| 85 |
+
Axis Bank Burgundy Private Credit Card,Axis,18,70,120,750,50000,50000,https://www.axisbank.com/burgundyprivate/creditcard
|
| 86 |
+
Club Vistara IndusInd Bank Explorer Credit Card,IndusInd,21,70,7,750,40000,10000,https://www.indusind.com/in/en/personal/cards/credit-card/club-vistara-indusInd-bank-explorer-credit-card.html?utm_source=Yahoo_Organic_Search
|
| 87 |
+
ixigo AU Credit Card,AU Bank,21,60,3,700,0,0,https://www.ixigo.com/payments/cbcc
|
| 88 |
+
RBL World Safari Credit Card,RBL,21,70,3,750,3000,3000,https://www.rblbank.com/personal-banking/cards/credit-cards/world-safari-credit-card
|
| 89 |
+
Scapia Federal Credit Card,Federal Bank,21,70,5,700,0,0,https://www.federalbank.co.in/scapia
|
credit_card_data_updated.csv
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name,description
|
| 2 |
+
Cashback SBI Card,"5% cashback across all online spends with no merchant restriction; 1% cashback across all offline spends; Earn cashback of up to Rs. 5,000 per month; Renewal fee reversed on spending at least Rs. 2 lakh in a year; 1% fuel surcharge waiver of up to Rs. 100 per month"
|
| 3 |
+
BPCL SBI Card Octane,"25 reward points per Rs. 100 spent on BPCL fuel, lubricants & Bharat Gas; 1 reward point on others; 1% fuel surcharge waiver, up to Rs. 100 per month, on BPCL fuel spends of up to Rs. 4,000; 6,000 reward points as welcome bonus on fee payment, 1 RP = Rs. 0.25; 4 complimentary visits per year to domestic Visa lounges in India; Fee waived on Rs. 2 lakh annual spends & Rs. 2,000 e-gift voucher on Rs. 3 lakh annual spends"
|
| 4 |
+
SBI Card ELITE,"2 reward points per Rs. 100 spent on other categories; Up to 50,000 bonus reward points as milestone benefits; 6 complimentary international lounge access in a year; Up to 8 complimentary domestic lounge access per year, 2 per quarter; Free movie tickets worth Rs. 6,000 in a year; Low forex mark-up fee of 1.99%"
|
| 5 |
+
Yatra SBI Card,"Rs. 1,000 off on domestic flight bookings on a minimum transaction of Rs. 5,000 at Yatra; Rs. 4,000 off on international flight bookings on a minimum transaction of Rs. 40,000 at Yatra; 20% off on domestic hotel bookings on a minimum transaction value of Rs. 3,000 at Yatra; 6 reward points per Rs. 100 spent on departmental stores, grocery, dining, movies, entertainment & international spends; 1 reward point per Rs. 100 spent on other categories; 1% fuel surcharge across all petrol pumps in India with maximum surcharge waiver of Rs. 100 monthly"
|
| 6 |
+
IRCTC SBI Card Premier,"2,500 and 5,000 reward points on Rs. 50,000 and Rs. 1 lakh annual travel spends respectively; Rs. 10 lakh rail accident insurance, Rs. 50 lakh air accident cover & Rs. 1 lakh fraud liability cover; Save 1% transaction charges on IRCTC railway tickets & 1.8% transaction charges on IRCTC airline tickets; 8 complimentary visits to railway lounge in a year (Max. 2 per quarter); 3 reward points per Rs. 125 spent on dining & utilities & 1 reward point per non-fuel retail spends; Earn up to 10% Reward Points on AC1, AC2, AC3, Executive Chair Car & Chair Car bookings via IRCTC website or app; Get 1500 Reward Points equivalent to INR 1500 on payment of first annual fee; 1% fuel surcharge across all petrol pumps in India, on transactions of Rs. 500 - Rs. 4,000, exclusive of GST and other charges"
|
| 7 |
+
SBI SimplyCLICK Credit Card,"1 reward point for every Rs. 100 spent on other categories; Amazon India gift card worth Rs. 500 on joining fee payment; Cleartrip/ Yatra e-voucher of Rs. 2,000 each on reaching annual online spending milestones of Rs. 1 lakh and Rs. 2 lakh; Annual fee waived on reaching Rs. 1 lakh annual spends; 1% fuel surcharge waiver on fuel spends between Rs. 500 to Rs. 3,000; Earn 10X Reward Points on online spends with exclusive partners: Apollo 24x7/BookMyShow/Cleartrip/ Dominos/ IGP/ Myntra/ Netmeds/ Yatra. Earn 5X Reward Points on all other online spends"
|
| 8 |
+
SBI SimplySAVE Credit Card,"2,000 bonus reward points on card activation; 10x rewards on movies, dining, departmental stores and grocery spends; Annual fee waiver on spending Rs. 1 lakh in a year; 1% fuel surcharge waiver on transactions between Rs. 500 to Rs. 3,000, up to Rs. 100 per statement cycle"
|
| 9 |
+
SBI Card Miles Elite,"2 Travel Credits per Rs. 200 spent with up to 1:1 redemption ratio for AirMiles or partner points; 5,000 Travel Credits as joining bonus on spending Rs. 1 lakh within the first 60 days; 1 additional domestic lounge visit with every Rs. 1 lakh spent in a year; 1.99% forex mark-up fee; 20,000 Travel Credits and annual fee reversal on Rs. 12 lakh and Rs. 15 lakh annual spends respectively; 8 complimentary Domestic Airport Lounge visits every year (max. 2 visits per quarter); Complimentary Priority Pass Membership worth $99 and enjoy access to 1000+ International Airport Lounges worldwide."
|
| 10 |
+
Titan SBI Credit Card,"3% value-back on spends at Tanishq (Quarterly capping of Rs. 25,000); 5% cashback on spends at Mia/Caratlane/Zoya (Quarterly capping of Rs. 10,000); 7.5% cashback on spends at World of Titan, Taneira, Titan Eye+, Helios, Fastrack, Skinn, Irth & Sonata (Quarterly capping of Rs. 10,000); 6 reward points per Rs. 100 on other spends; 8 complimentary Domestic Airport Lounges in an year , 2 Per Quarter; 1% fuel surcharge across all petrol pumps in India, on transactions up to Rs. 3,000, exclusive of GST and other charges; Get 12000 Reward Points worth Rs. 3000 on payment of annual fees as a welcome benefit"
|
| 11 |
+
HDFC Diners Club Privilege Credit Card,"4 reward points per Rs. 150 spent on other categories; Up to 20% off on dining via Swiggy Dineout; Buy 1 Get 1 Free, up to Rs. 250 off per ticket, on BookMyShow weekend shows for max. 2 free tickets/month; Marriott/ Decathlon/ O2 Spa/ Barbeque Nation/ Lakme Salon Rs. 1,500 voucher on Rs. 1.5 lakh quarterly spends; Swiggy One & Times Prime annual memberships on Rs. 75,000 spends within the first 90 days; 2 complimentary airport lounge access worldwide per quarter on Rs. 15,000 quarterly spends; Forex mark-up fee of 3.5%"
|
| 12 |
+
HDFC Regalia Credit Card,"Low forex markup fee of 2%; 10,000 rewards on Rs. 5 lakh annual spends & 5,000 rewards on Rs. 8 lakh annual spends; Up to 20% off on dining via Swiggy Dineout; Insurance cover for air accident death, lost card liability & emergency overseas hospitalisation; 1% fuel surcharge waiver up to Rs. 500 per month for fuel spends of Rs. 400 to Rs. 5,000; 8 free airport lounge access per year within India; 6 airport lounge access every year outside India through the Priority Pass; 4 reward points every time one spends Rs. 150; 1 RP = Rs. 0.5 for redemption against flights and hotel bookings on HDFC Smartbuy website, 1 RP = up to Rs. 0.35 if you want to redeem against vouchers and poducts, 1 RP = Rs 0.20 for cash back, and Air miles conversion at a value of 1RP = 0.5 air mile. "
|
| 13 |
+
HDFC MoneyBack+ Credit Card,"2 CashPoints per Rs. 150 spent on other categories; 10X CashPoints (3.3% Valueback) on Amazon, Flipkart, Swiggy, Reliance Smart SuperStore & BigBasket; 500 CashPoints as welcome benefit after the payment of joining fee; Gift vouchers worth up to Rs. 2,000 in a year as milestone benefits; CashPoints can be used for redemption against travel benefits like Flight (Air miles) & Hotel bookings, value of 1 CashPoint = ₹0.25"
|
| 14 |
+
HDFC Millennia Credit Card,"On spending Rs. 1 Lakh in a quarter: Either avail 1 complimentary lounge access OR a voucher worth Rs. 1,000 from the list of associated brands;5% Cashback on Amazon, BookMyShow, Cult.fit, Flipkart, Myntra, Sony LIV, Swiggy, Tata CLiQ, Uber and Zomato, 1% cashback on other spends; 1% fuel surcharge waiver on transactions between Rs. 400 and Rs. 5,000; CashPoints can be used for redemption against travel benefits like Flight (Air miles) & Hotel bookings at value of 1 CashPoint = ₹0.30; Value of 1 CashPoint = ₹1 for cashback"
|
| 15 |
+
HDFC Infinia Credit Card,"1 RP = 1 Air Mile where United MileagePlus is one of the supported airline partners or Rs. 1 for travel bookings, Apple products and Tanishq vouchers purchased via SmartBuy; Convert your Reward Points to Turkish Airlines Miles&Smiles at a rate of 2 RPs = 1 Mile ; Invite-only card with Club Marriott membership and 12,500 reward points on card activation & renewal; 5 reward points on every Rs. 150 spent, including spends on insurance, utilities and education; Access to unlimited golf coaching in India & golf games across the world; Book 3 nights stay at participating ITC hotels and pay for 2 with 1+1 complimentary weekend buffet; Low forex mark-up fee of 2%; One Reward Point converts to one Accor Live Limitless Point, effectively a 2:1 conversion ratio; Unlimited airport lounge access across the globe for Primary and Add-on member; 1% fuel surcharge waiver at all fuel stations across India on transactions between Rs.400 and Rs.1,00,000, No Reward Points are earned on fuel transactions"
|
| 16 |
+
HDFC Diners Club Black Credit Card,"Up to 10X reward points on SmartBuy and 2X on weekend dining with up to 1:1 redemption ratio; Annual memberships of Club Marriott, Forbes, Amazon, Swiggy One (3 months) and MMT BLACK on joining; Low forex markup fee of 2% and renewal of annual complimentary memberships on Rs. 8 lakh annual spends; Any 2 vouchers of Rs. 500 from Ola cabs, cult.fit Live, BookMyShow, or TataCliQ on Rs. 80,000 monthly spends; 6 complimentary golf games in a calendar quarter; Unlimited domestic and international Airport lounge access for primary and add-on card members; 1 Reward Point is equivalent to 1 Club Vistara Point"
|
| 17 |
+
6E Rewards XL Indigo HDFC Credit Card,"5% 6E Rewards per Rs. 100 spent on IndiGo website or mobile app; 3% 6E Rewards per Rs. 100 spent on dining, grocery, and entertainment spends; 2% 6E Rewards per Rs. 100 for all other retail spends; 1 complimentary 6E Prime Add-On and Accor Hotel stay voucher as welcome benefit; 8 complimentary domestic lounge visits in a year; Forex-Markup fee of 2.5%; 1 Complimentary flight ticket voucher worth INR 3,000 as welcome benefit; The value of 1 6E Reward = INR 1"
|
| 18 |
+
HDFC Intermiles Signature Credit Card,"Discount voucher of Rs. 750 on return flight tickets booked via InterMiles for the first year; Discount voucher of Rs. 2,000 on hotel bookings for the first year as renewal benefit; 3,000 InterMiles on spending Rs. 6,000 within the first 30 days of the Anniversary date; 5% off on Economy Class ticket & 10% off on Business Class ticket booking of Etihad Airways; Complimentary Dineout Membership; Earn 6 InterMiles on every Rs. 150 spent on retail spends; Earn 12 InterMiles on every Rs. 150 spent on every flight ticket booked on http://flights.intermiles.com, Earn 12 InterMiles on every Rs. 150 spent on every Hotel booked on Interbook.intermiles.com; 16 complimentary Lounge visits Per Year (4 per quarter)."
|
| 19 |
+
IndianOil HDFC Credit Card,"Earn up to 50 Litres of Free fuel annually; Earn 5% of your spends as Fuel Points on Groceries and Bill Payments; 5% fuel points on spends done at IndianOil outlets; 1% fuel surcharge waiver, up to Rs. 250 per month, for IOCL fuel spends of Rs. 400 and above; 1 fuel point per Rs. 150 spent on other purchases, including UPI, where 1 fuel point = Up to Rs. 0.96; Complimentary IndianOil XTRAREWARDS Program (IXRP) membership on joining; Annual fee waived off on spending Rs. 50,000 in a year; 1 Fuel Point = ₹0.96"
|
| 20 |
+
HDFC Regalia Gold Credit Card,"Complimentary Swiggy One & MMT Black Elite Memberships; Gift voucher worth Rs. 2,500 on the payment of joining fee; 6 complimentary international lounge visits & 12 complimentary domestic airport lounge visits per year; Marks & Spencer, Reliance Digital, Myntra or Marriott vouchers worth Rs. 1,500 on Rs. 1.5 Lakh quarterly spends; Flight vouchers worth Rs. 5,000 on annual spends of Rs. 5 Lakh; 5X Reward Points on spends at Marks & Spencer, Myntra, Nykaa & Reliance Digital!, 4 Reward Points on every Rs.150 spent on retail); Forex-Markup fee of 2%; Airmiles conversion a value of 1RP = upto 0.5 airmiles; Products and Vouchers via Netbanking or SmartBuy at a value of 1 RP = upto Rs 0.35"
|
| 21 |
+
IRCTC HDFC Bank Credit Card,"Gift voucher worth Rs. 500 on reaching the retail spending milestone of Rs. 30,000 every quarter; 1% transaction charges waiver, up to Rs. 1,000 in a month on IRCTC website and app; 1% fuel surcharge is waived off on transactions between Rs. 400 to Rs. 5,000; 5 Reward Point for every INR 100 spent on IRCTC ticketing website and Rail Connect App, 1 Reward Point for every INR 100 spent on all other spends; 5% cashback on train ticket bookings via HDFC Bank SmartBuy; 8 complimentary access to select IRCTC Executive Lounges every year (2 per quarter); Conversion rate of 1 Reward Point = INR 1"
|
| 22 |
+
Marriott Bonvoy HDFC Bank Credit Card,"1 Free Night Award and 10 Elite Night Credits on first eligible spend transaction or fee levy on the card; Complimentary Marriott Bonvoy Silver Elite Status; Earn 8 Marriott Bonvoy Points per Rs. 150 spent at hotels participating in Marriott Bonvoy; Earn 4 Marriott Bonvoy Points per Rs. 150 spent on travel, dining & entertainment; Earn 2 Marriott Bonvoy Points per Rs. 150 spent on all other applicable purchases; Complimentary Golf Access 2 per quarter across the world (Green Fee Waiver); 3.5% forex-markup fee; 12 Complimentary domestic airport lounge access and 12 Complimentary international airport lounge access; 1 Free Night Award and 10 Elite Night Credits on first eligible spend transaction or fee levy on the card; Earn up to 3 Free Nights on ₹6L, ₹9L & ₹15L annual spend;Enjoy the exclusive Concierge Services on your Marriott Bonvoy HDFC Bank Credit Card. Customers can connect the concierge number from 9a.m-9p.m.;Delay or loss of checked-in baggage cover of up to $250. Loss of travel documents cover of up to $250. Flight delay cover of up to $250; Air accidental cover of up to $12,500. Emergency medical expenses up to $18,750; Credit shield of up to INR100,000. Loss liability cover of up to INR100,000; 3,000 Marriott points equal 1,000 United miles. Additionally, for every 60,000 Marriott points transferred, you receive a bonus of 10,000 United miles, allowing you to earn a total of 30,000 United miles for 60,000 Marriott points."
|
| 23 |
+
Swiggy HDFC Bank Credit Card,"10% Cashback on Swiggy application (Food ordering, Instamart, Dineout & Genie).5% Cashback on online spends across online MCCs.1% Cashback on other categories;Complimentary Swiggy One Membership for 3 months on card activation; Access to golf courses worldwide and 12 free lessons per year. 4 complimentary rounds of green fees per calendar year. Discounted golf services at 50% of the green fee beyond complimentary sessions;Event of losing your Swiggy HDFC Bank Credit Card, on reporting it immediately to our 24-hour call centre, you have zero liability on any fraudulent transactions made on your Credit Card;Up to 50 days of interest-free period;Exclusions for Spends of Rs. 2,00,000 and above for eligibility of renewal year fee waiver are as follows: Cash on Call, Balance Transfer and Cash Withdrawal."
|
| 24 |
+
Axis Bank Reserve Credit Card,"Up to 30 EDGE reward points for every Rs. 200 spent; B1G1 offer on BookMyShow tickets with up to 5 offer tickets per month; ITC Culinaire, Accorplus, Club Marriott & EazyDiner Prime memberships & offers on Postcard & Oberoi hotels; 50 free golf rounds every year across select golf courses in India; Five Reward Points convert to two Accor Live Limitless Points; Five Reward Points convert to two Club ITC Points.; Edge Reward Points can also be redeemed for Edge Miles to international and domestic airline programs at the ratio of 5:2 for Etihad Airways, Qatar Airways, Marriott International, United airlines, etc.; EDGE REWARD Points earned can be transferred to Turkish Airlines Miles&Smiles at a 5:2 ratio, with an annual cap of 5,00,000 RPs; Unlimited domestic airport lounge access; 15000 reward pointson card activation, 15 edge reward points for every Rs.200 spent, 2X reward points on international spends; Forex-markup fee of 1.77%"
|
| 25 |
+
Axis Magnus Credit Card,"Low forex mark-up fee of 2%; Conversion of rewards into Air Miles or Hotel Loyalty points (5:2);Convert EDGE REWARDS Points to United MileagePlus miles at a 5:2 ratio — 5 EDGE Points convert to 2 United miles ; Convert EDGE REWARD Points to airline loyalty programs, such as Etihad Guest Miles at a 5:2 ratio capped at 100,000 points per calendar year;5 EDGE Points is equivalent to 2 Club Vistara Points. ; You can transfer EDGE REWARD Points to Air India Maharaja Club points at a 5:2 ratio, where 1 EDGE Reward Point = ₹0.20, with a maximum limit of 400,000 points per year ; Transfer Axis EDGE REWARDS Points to Turkish Airlines Miles&Smiles at a 5:2 ratio (5 RPs = 2 Turkish Miles), with a maximum of 100,000 RPs per year ;Luxe gift card/ The Postcard Hotels/ Yatra voucher worth Rs. 12,500 on making the first transaction within 30 days from card issuance; Five EDGE REWARD Points convert to two Marriott Bonvoy Points; Five EDGE Reward Points convert to two Accor Live Limitless Points; Five EDGE Reward Points convert to two Club ITC Points; Unlimited international and domestic airport lounge access; 30% discount on fine dining at over 4000 restaurants in India"
|
| 26 |
+
Axis Atlas Credit Card,"2,500 bonus EDGE Miles as welcome benefit; Up to 5 EDGE Miles per Rs. 100 spent on travel and 2 EDGE Miles per 200 spent on ther spends; Redeem EDGE Miles at value of 1 EDGE Mile = 2 Partner Points; Convert EDGE Miles to United MileagePlus miles at a 1:2 ratio — 1 EDGE Mile gives you 2 United miles; EDGE Miles can be redeemed for Etihad Guest Miles with partner airline like Etihad at 30,000 EDGE Miles per calendar year;1 Edge Mile equals 2 Club Vistara Points ; Transfer EDGE Miles to Air India Maharaja Club points, with a limit of 120,000 within the overall annual cap of 150,000 EDGE Miles; Transfer EDGE Miles earned to Turkish Airlines Miles&Smiles at a 1:1 ratio, with a maximum of 30,000 EDGE Miles per calendar year ;Milestone benefits of up to 5,000 EDGE Miles; Up to 18 complimentary domestic lounge visits in a year; Two EDGE Miles convert to one Marriott Bonvoy Point; One Edge Mile converts to two Accor Live Limitless Points; One Edge Mile converts to two Club ITC Points; 10000 EDGE miles on achieveig milestone benefits; 5000 EDGE Miles on completion of year"
|
| 27 |
+
Axis Bank Horizon Credit Card,"5 EDGE Miles on every Rs. 100 spent on Axis Bank Travel EDGE portal and direct airline websites/counters; Transfer EDGE Miles to Air India Flying Returns at a 1:1 ratio, with up to 400,000 miles allowed per year ; 2 EDGE Miles on every Rs. 100 spent on other categories; 25% off, up to Rs. 800 per month, on dining via EazyDiner, on minimum order of Rs. 2,500; 5,000 EDGE Miles on the first card spend of min. Rs. 1,000 made within 30 days of card issuance; 1,500 EDGE Miles as card renewal bonus"
|
| 28 |
+
Axis Bank SELECT Credit Card,"10 EDGE Reward Points on every Rs. 200 spent; Redeem reward Points to Turkish Airlines Miles&Smiles at a 10:1 ratio, where 10 Reward Points = 1 Turkish Mile ; Flat Rs. 500 off per month on BigBasket on minimum spend of Rs. 3,000; Rs. 200 off on Swiggy, twice a month, on minimum spend of Rs. 1,000; Up to 12 complimentary international lounge visits in a year & 2 domestic lounge visits per quarter; Up to 12 complimentary golf rounds in a year; Five EDGE Reward Points convert to one Club ITC Point; Rs.250 off on movie tickets twice per month; 1000 EDGE Reward points worth RS.2000 on first transaction; 1% fuel surcharge waiver; Earn 10 EDGE Points per ₹200 spent. Earn 2X EDGE Points on 'Retail Shopping'. 2X EDGE Points on spends up to ₹20,000/month and 10 points/₹200 beyond that; Get up to 15% off on flights & hotels via Goibibo and MakeMyTrip, 10% off on Swiggy, and 10% off up to ₹1000 on Tira every Wednesday."
|
| 29 |
+
Flipkart Axis Bank Credit Card,"1% cashback across other spends; 4 complimentary domestic airport lounge visits in a year on spending at least Rs. 50,000 in a year; Activation benefits worth Rs. 600 from Flipkart and Swiggy; 1% fuel surcharge waiver on transactions between Rs. 400 and Rs. 4,000; 7.5% cashback on Myntra (capped at ₹4,000/quarter). 5% cashback on Flipkart & Cleartrip (capped at ₹4,000/quarter/merchant). 4% cashback on preferred merchants; Get up to 15% off on flights & hotels via Goibibo and MakeMyTrip, 10% off on Swiggy and Amazon Fresh, and 10% off up to ₹1000 on Tira every Wednesday."
|
| 30 |
+
Axis My Zone Credit Card,"Flat Rs. 120 off on Swiggy on a minimum spend of Rs. 500, twice a month; Buy 1 Get 1 movie ticket free on bookings via District App, up to Rs. 200 off per month; Up to Rs. 1,000 off on Ajio on a minimum transaction of Rs. 2,999; Renewal of SonyLiv Premium annual subscription on spending Rs. 1.5 Lakh in a year; Up to 15% off (max. Rs. 500) on dining via EazyDiner, twice a month on a min. spend of Rs. 2,500; 4 EDGE reward points ofr every Rs.200 spent; Get up to 15% off on flights & hotels via Goibibo and MakeMyTrip, 10% off on Swiggy, and 10% off up to ₹1000 on Tira every Wednesday."
|
| 31 |
+
IndianOil Axis Bank Credit Card,"4% back as 20 EDGE reward points per Rs. 100 spent on IOCL fuel; 1% fuel surcharge waiver of up to Rs. 50 per month on fuel spends of Rs. 400 to Rs. 4,000; 1 EDGE reward point per Rs. 100 spent on other spends, 1 EDGE reward point = Rs. 0.20; 100% cashback, up to Rs. 250, as EDGE rewards on the 1st fuel spend made within the 1st 30 days; Annual fee waived off on Rs. 3.5 lakh annual spends and up to 15% off on dining via EazyDiner; Up to 10% off on movie tickets via BookMyShow. Dining offers at top restaurants under Axis Bank Dining Delights; Get up to 15% off on flights & hotels via Goibibo and MakeMyTrip, 10% off on Swiggy, and 10% off up to ₹1000 on Tira every Wednesday. "
|
| 32 |
+
Axis Bank ACE Credit Card,"1.5% cashback on other offline & online spends; 4 domestic lounge access per year on spending Rs. 50,000 or more in the previous quarter; 1% fuel surcharge waiver across all fuel stations on transactions between Rs. 400 to Rs. 4,000; 5% cashback on bill payments (electricity,internet, gas and more), 4% cashback on Swiggy, Zomato and Ola"
|
| 33 |
+
Amazon Pay ICICI Credit Card,"5% cashback on Amazon spends for Prime members; 3% cashback on Amazon spends for Non-prime members; 2% cashback on spends at Amazon Pay partner merchants; 1% cashback on other spends; 1% waiver on fuel surcharge across all petrol pumps in India"
|
| 34 |
+
ICICI Coral Credit Card,"2 reward points per Rs. 100 spent and 1 reward point per Rs. 100 spent on utilities & insurance; 25% discount, up to Rs. 100 each on BookMyShow and Inox on purchase of minimum 2 movie tickets per transaction, valid twice a month; 1 complimentary railway lounge access per quarter, on spending Rs. 5,000 or above in the previous quarter; 2,000 bonus reward points on spending Rs. 2 Lakh and additional 1,000 reward points on spending Rs. 1 Lakh; 1% fuel surcharge waiver on HPCL petrol pumps; 1 complimentary access to select airport lounges in India each quarter,by spending Rs 75,000 in the preceding calendar quarter"
|
| 35 |
+
ICICI HPCL Super Saver Credit Card,"4% cashback, max. Rs. 200 per month, on HPCL spends & 1% on fuel surcharge; Save additional 1.5% as 6 reward points per Rs. 100 spent on HPCL fuel via the HP Pay app; 2 reward points per Rs. 100 spent on other retail purchases, 1 RP = Rs. 0.25; Rs. 100 cashback on recharge via HP Pay app & 2,000 reward points as joining bonus; 1 domestic airport lounge access per quarter on spending Rs. 35,000 in the previous quarter; 25% off up to ₹100 on movie tickets each on BookMyShow and INOX, twice a month;2,000 reward points as joining benefits; Forex-markup fee of 3.5%"
|
| 36 |
+
MakeMyTrip ICICI Bank Signature Credit Card,"4 My Cash per Rs. 200 spent on MMT hotel/ holiday bookings & 2 My Cash per Rs. 200 spent on MMT flight bookings; Up to 4,000 My Cash every year as milestone benefit; 2 complimentary domestic lounge access every quarter; 1 complimentary railway lounge access every quarter; 25% off up to Rs. 150 twice a month on booking min. 2 movie tickets via BookMyShow and INOX; 1% waiver on fuel transactions worth ₹400 - ₹4,000 at HPCL pumps on ICICI Bank POS Machines; dining discounts with Culinary Treats Programme; 1,100 myCash on spend of ₹2.5 lakh & 4,000 myCash on spend of ₹5 lakh; Forex-markup fee of 3.5 %"
|
| 37 |
+
Adani One ICICI Bank Signature Credit Card,"Activation benefits worth Rs. 9,000; 4 premium lounge upgrades every quarter; 4 valet parking every year and 4 premium car parking every year; 2 porter services on every Rs.1 Lakh spent (Max. 8 per year); 50% off, up to Rs. 500, twice a month on BookMyShow; Dining discounts with Culinary Treats Programme; 1% fuel surcharge waiver on all fuel transactions at any fuel outlet; Forex Markup of 3.5%"
|
| 38 |
+
ICICI Bank Sapphiro Credit Card,"2 reward points per Rs. 100 spent domestically; 1 reward point per Rs. 100 spent on utility & insurance; Complimentary golf round/ lesson, up to 4 a month, on every Rs. 50,000 retail spends in the previous month; Buy 1 and get up to Rs. 500 off on the 2nd ticket, twice every month via BookMyShow; 4,000 reward points on annual spends of Rs. 4 lakh; 2,000 reward points on every Rs. 1 lakh spent in an anniversary year; Welcome Vouchers from Tata CLiQ, EazyDiner, Da Milano, EaseMyTrip & Tata Croma; 2 complimentary international airport lounge accesses annually 4 complimentary Domestic airport lounge accesses for each quarter; Dining discounts with Culinary Treats Programme; Forex Markup of 3.5%"
|
| 39 |
+
ICICI Emeralde Private Metal Credit Card,"Rs. 3,000 EaseMyTrip vouchers on reaching each annual spending milestone of Rs. 4 Lakh and Rs. 8 Lakh; Unlimited golf lessons or rounds every month on creating a Golftripz account; EazyDiner membership, up to 50% off on dining in India & Dubai; 2,000 EazyPoints on joining; Buy 1 ticket and get up to Rs. 750 off on the 2nd ticket on BookMyShow bookings, twice a month; 1% fuel surcharge waiver of up to Rs. 1,000 per month on fuel transactions below Rs. 4,000; Low forex markup fee of 2%; Complimentary Taj Epicure Membership; Redeem reward points at up to ₹1 per point or transfer them to Air India Maharaja Club at a 1:1 ratio; Unlimited International airport lounge access; Unlimited Domestic airport lounge access"
|
| 40 |
+
YES Private Credit Card,"40 and 20 Reward points per Rs. 200 spent on international and domestic purchases respectively; $100 savings per person on Globus and Monograms vacations; Offers at Mandarin Oriental Hong Kong, Parkroyal on Pickering & Shangri-la (Singapore) & Intercontinental Chennai Mahabalipuram Resort; B1G1 movie offer with up to 4 free tickets with max. Rs. 250 off on each per month and 50% off, max. Rs. 1,000, on events; 12 free domestic golf lessons with 12 free rounds of green fees for cardholders & 4 for guests per year; Offers on select international golf courses; 12 complimentatry International airport lounge access; Unlimited Domestic airport lounge access for both the Primary and Add-on Cardmember; 1 Reward Point= upto INR 0.25; 50,000 Reward points on total retail spend of Rs. 10 Lakhs in 1st 90 days from Credit Card opening date and 2 Lakh reward points as welcome benefits"
|
| 41 |
+
YES Bank Marquee Credit Card,"1% forex mark-up fee; Up to 12 complimentary golf lessons in a year & 4 green fee waivers at select golf courses in India; 40,000 and 20,000 YES Rewardz Points on joining and renewal fee payments respectively; B1G1 offer with up to Rs. 800 off per BookMyShow movie ticket with max. 3 free tickets per month; Min. 15% off on dining via Dining Fiesta program; 36 YES Rewardz Points per ₹200 on online, 18 on offline; 4 YES Points = 1 Club Vistara Point; 6 complimentary Domestic Airport Lounge visit in India per calendar quarter for Primary & Add-on Cardmember,2 Complimentary Domestic lounge visits for guests in a quarter; Unlimited Access to airport lounges internationally for Primary & Add-on card holders; 1% Fuel surcharge waiver at fuel stations across India for transaction between INR 400 to INR 5,000"
|
| 42 |
+
YES Bank RESERV Credit Card,"6 YES Rewardz Points per Rs. 200 spent on other categories; Low forex mark-up fee of 1.75%; 8,000 reward points on renewal fee payment; 25% off up to Rs. 250 per month on BookMyShow movie tickets; 4 complimentary rounds of green fees per year (1 per month), 1 golf game per month at select golf courses; Choice to subscribe to 3X/5X rewards plans by paying Rs. 3,500/Rs. 5,000 per year; Complimentary International airport lounge access; 3 complimentary lounge visits per quarter; 1% fuel surcharge waiver between Rs 400 and Rs 5000."
|
| 43 |
+
YES Bank Paisabazaar PaisaSave Credit Card,"Earned Cashback Points can be redeemed as statement credit in 1:1 ratio at zero redemption fee;3% Cashback on Online Spends;1.5% cashback on online spends made after reaching the monthly capping; Renewal fee waived off on reaching Rs. 1.2 lakh annual spends; 1% fuel surcharge waiver, up to Rs. 250 per month, on all fuel spends worth Rs. 500 to Rs. 3,000"
|
| 44 |
+
YES Bank Wellness Plus Credit Card,"12 complimentary fitness sessions every month, including gym, yoga, Zumba, etc.; Annual complimentary preventive health check-ups across 31 parameters; Annual complimentary eye and dental check-ups; 6 reward points per Rs. 200 spent on other purchases; 10 Reward Points = 1 InterMile or 1 Club Vistara Point; Up to 2 complimentary visits to domestic airport lounges every calendar quarter; 1% fuel surcharge waiver at all fuel stations across India for transactions between INR 400 to INR 5,000."
|
| 45 |
+
YES Bank ELITE+ Credit Card,"4 YES Rewardz Points per Rs. 200 on select categories; 10 YES Rewardz Points = 1 InterMile or 1 CV Point; 4 rounds of green fee waiver and 12 complimentary golf lessons per year, max. 1 each in a month; 25% off, up to Rs. 250 per month, on movie tickets booked via BookMyShow; Renewal fee waived on reaching Rs. 2 lakh annual spends; Forex markup of 2.75%; Earn 3x or 5x YES Rewardz Points on paying additional fee worth Rs. 1,500 or Rs. 2,500 respectively; Access to airport lounges internationally for Mastercard and Visa cards; 2 complimentary Domestic Airport Lounge visit in India per calendar quarter"
|
| 46 |
+
YES Bank SELECT Credit Card,"2 YES Rewardz Points per Rs. 200 select category spends; 4 complimentary rounds of green fees per year at golf courses in India; Renewal fee waived off on spending Rs. 1 lakh during the previous year; Up to 1 complimentary visit to domestic airport lounges per quarter; Preferential forex markup of 2.75%; Earn 3x or 5x YES Rewardz Points on paying additional fee of Rs. 1,000 or Rs. 2,000 respectively; 1% surcharge waiver for transaction between INR 400 – INR 5,000"
|
| 47 |
+
YES Bank ACE Credit Card,"2 YES Rewardz Points per Rs. 200 spent on other categories, like recharge, utility, insurance, govt. and education; Annual fee waived on spending minimum Rs. 50,000 during the previous year; Joining fee reversed on making Rs. 5,000 spends within the first 30 days; Forex mark-up fee of 2.75%; 1% fuel surcharge waiver, up to Rs. 100 per month, on fuel spends worth Rs. 400 to Rs. 5,000; Credit shield cover of Rs. 1 lakh and lost card liability cover of Rs. 1.3 lakh"
|
| 48 |
+
HSBC Taj Credit Card,"Taj InnerCircle Platinum NeuPass membership covering benefits like priority/early check-ins, late check-outs, dedicated 24/7 Platinum concierge service, and more; Free access to premier executive business club- The Chambers Lounge (12 visits/year) and Taj Club Lounge (up to 12 times a year), with tea/coffee for 2; Unlimited room upgrades to the next level of room or suite at participating hotels (on up to 5 consecutive night stays); 25% savings on the best available rate for room/suite at Taj Palaces, Safaris and other properties; 4 free set meal for two vouchers at Taj and other participating restaurants; 25% savings on food and beverages, including Qmin deliveries; Complimentary 60-minute spa treatment (up to 4 a year), plus access to the sauna and steam room; Free swimming pool access for 2 (up to 12 times a year); Earn 1.5 reward points for every INR 100 you spend on standard purchase, 5 reward points for every INR 100 you spend with Taj, SeleQtions, Gateway, Vivanta hotels and amã Stays & Trails bungalows, 1.5 reward points = INR 1 in your HSBC Taj Credit Card Wallet balance; 50% off at Starbucks, 4 times/month (max ₹150); 25% off at Tira (min spend ₹2,499; max ₹750); 10% off at Just In Time on select watches; Up to 5% off on Emirates flights (code: INHSBCT); 2-for-1 movie & event tickets via BookMyShow (max ₹750/ticket); 12% off at Zomato (min spend ₹2,499; max ₹500); 10% off at Swiggy on food/grocery orders; 5% off at Croma (min spend ₹20,000; max ₹2,000).; Up to ₹20,000 off on business class tickets via MakeMyTrip.;VISA Infinite benefits as per VISA site."
|
| 49 |
+
HSBC TravelOne Credit Card,"Cashback worth Rs. 1,000; Postcard Hotels voucher worth Rs. 3,000 and 3 months EazyDiner Prime membership on card activation; 4 reward points on every Rs. 100 spent on flights, travel aggregators, and in foreign currency; 2 reward points on every Rs. 100 spent on other spending categories; Value of 1 reward point = 1 Air Mile or Hotel Point; 10,000 bonus reward points on spending more than Rs. 12 lakh in a year; 6 domestic and 4 international airport lounge access per year; 4 rounds and 12 golf lessons per year, capped at 1 per month; Up to 20% off at duty-free shopping via the AdaniOne app or website; Up to 50% off with the District app by Zomato"
|
| 50 |
+
HSBC Premier MasterCard Credit Card,"12,000 Taj Experiences Gift Card, Complimentary Taj Epicure Membership and EazyDiner Prime Membership on activation; 3 reward points per Rs. 100 across all spends; Conversion into air miles in 1:1 ratio against Apple Product purchase and Ailines; Convert your Reward Points into Air India Airmile program at a 1:1 ratio; 1 Reward Point = 1 Maharaja Club Point. ; Unlimited complimentary domestic lounge visits; Unlimited complimentary international lounge visits with 8 free guest visits in a year; Low forex mark-up fee of 0.99%; Buy One Get One offer on BookMyShow"
|
| 51 |
+
HSBC Live+ Credit Card,"4 complimentary domestic airport lounge visits per year; Rs. 1,000 cashback on making Rs. 20,000 worth of spends within the first 30 days of card issuance; Up to 15% off at partner restaurants via Live+ Dining Programme; Annual fee waived off on annual spends worth more than Rs. 2 lakh; 10% accelerated cashback, up to INR1,000 a month, on all dining, food delivery and grocery spends,1.5% unlimited cashback on most other spends"
|
| 52 |
+
HSBC Visa Platinum Credit Card,"2 reward points per Rs. 150 spent; 5X rewards up to 15,000 per year on spends made over Rs. 4 lakh annual spends; Rewards redemption for InterMiles, Club Vistara points, and airmiles from British & Etihad Airways; Fuel surcharge waiver of up to Rs. 3,000 per year on fuel spends of Rs. 400 to Rs. 4,000; Zero lost card liability cover of up to Rs. 3 lakh"
|
| 53 |
+
IDFC FIRST Power+ Credit Card,"5% savings as 30 rewards per Rs. 150 spent on HPCL fuel & LPG; 1.5% savings as 6 Happy Coins per Rs. 100 spent on HP Pay app; 3 rewards per Rs. 150 spent on UPI & other purchases, 1 RP = Rs. 0.25; 1 complimentary domestic airport lounge access per quarter on min. monthly spends of Rs. 20,000; 25% off, up to Rs. 100, on movie tickets once per month & annual fee waived on Rs. 1.5 lakh annual spends; Up to 8.83% savings on hotel & flight bookings through IDFC FIRST Mobile App; Personal Accident Cover of ₹2,00,000 and Lost Card Liability Cover of ₹25,000"
|
| 54 |
+
IDFC FIRST Wealth Credit Card,"3x rewards on monthly spends of up to Rs. 20,000 and 10x rewards on spends after this spend threshold; 1 reward point per Rs. 150 spent on insurance and utility; low forex mark-up fee of 1.5%; Buy 1 get 1 offer with up to Rs. 250 off twice a month on movie tickets on District by Zomato app; Complimentary golf round per Rs. 20,000 monthly spends, up to 2 rounds/month; 5% cashback on 1st EMI transaction and Rs. 500 gift voucher as welcome benefit"
|
| 55 |
+
IDFC FIRST Select Credit Card,"1 reward point per Rs. 150 on utility and insurance premium spends; 2 complimentary domestic airport lounge access in a quarter on spending Rs. 20,000 in a month; Buy 1 Get 1 offer on movie tickets, twice a month on District by Zomato app; Gift vouchers worth Rs. 500 and 5% cashback on 1st EMI transaction as welcome benefit; Up to 12 complimentary golf lessons per year; low forex mark-up of 1.99%; 4 complimentary visits to lounges at select Indian railway terminals; 1 Reward point = ₹0.25"
|
| 56 |
+
IDFC FIRST Millennia Credit Card,"Up to 3X reward points on all UPI spends via RuPay card; 1x reward points on insurance and utility spends; 25% off up to Rs. 100 once a month on movie tickets on District by Zomato app; 4 complimentary railway lounge visits per quarter; Rs. 500 gift voucher and 5% cashback on the 1st EMI transaction as welcome bonus; 1% fuel surcharge waiver up to Rs. 200 per month on fuel spends of Rs. 200 to Rs. 5,000; 1 Reward point = ₹0.25"
|
| 57 |
+
IDFC FIRST Mayura Credit Card,"Welcome benefit: Max cashback of ₹4,000 on four transactions of ₹1,000+ within 60 days; Earn up to 10X Reward Points per ₹150 on incremental spends above ₹20,000/month, 3X on rent, wallet load, education, government transactions; Reward Points worth ₹0.50 for travel via IDFC Travel & Shop; Buy 1 Get 1 Free movie tickets (up to ₹500 discount) twice monthly; Free Trip Cancellation cover (CFAR Insurance) up to ₹50,000 twice yearly; 4 complimentary domestic airport lounge visits each quarter plus 1 guest visit; 4 complimentary international airport lounge visits each quarter; 4 complimentary railway lounge access each quarter; 2 free golf rounds/lessons each month; Zero (0%) Foreign Currency Markup; 1% Fuel Surcharge Waiver up to ₹300 per month; Comprehensive insurance: Purchase Protection, Travel Insurance, Personal Accident Cover; Earn 50X Bonus Reward Points* on Hotel Bookings, 30X Bonus Reward Points* on Flight Bookings; Up to 10X Reward Points incremental spends above ₹ 20,000 in a statement cycle.;1X = 1 Reward Point per ₹150 spent"
|
| 58 |
+
IDFC FIRST Ashva Credit Card,"1X rewards = 1 Reward Point per Rs. 150 spent; 7,500 Reward Points on reaching Rs. 8 lakh annual spends post annual fee payment; 1% forex mark-up and reimbursement of up to Rs. 25,000 on flight/hotel booking cancellations, twice a year; Buy 1 and get up to Rs. 400 off on the 2nd movie ticket via BookMyShow, twice every month; Up to 2 golf rounds per month, 1st on Rs. 20,000 monthly spends and 2nd on Rs. 40,000 monthly spends; 4 complimentary domestic airport lounge visits each quarter; 2 complimentary International airport lounge visits each quarter; 10X Reward Points incremental spends above ₹20,000 in a statement cycle, 5X Reward Points on spends up to ₹20,000 in a statement cycle, 3X Reward Points on Rent, Education, Government and Wallet Load transactions. 1X Reward Points on Insurance and utility; Earn 15,000 bonus reward points every month with 50X Bonus Reward Points* on Hotel Bookings,30X Bonus Reward Points* on Flight Bookings"
|
| 59 |
+
American Express Platinum Card,"Benefits worth INR 44,300* every time you book at one of the 1000+ properties at one of the 1000+ properties under the Fine Hotels & Resorts collection;The American Express Global Lounge Collection include access to best-in-class airport lounges like The Centurion Lounge, Delta Lounge and Priority pass lounges;Air Accident Insurance,Overseas Medical Insurance,Purchase Protection;On spends on INR 20 Lakhs in membership year and payment of renewal fee, get vouchers upto INR 35,000* as renewal benefit on the product;25% off at Taj, SeleQtions and Vivanta Hotels 50% off on Suites at Oberoi Hotels and Resorts;Get 3X Membership Reward points on everything, every time you spend abroad;Get up to 20X* Rewards on 20+ luxury brands with Rewards Xcelerator.Also earn 1 Membership Reward points for every INR 40 spent with your Card.Choose to Pay with points or redeem your points for over 500 options across travel, dining, accessories and more.Earn 5X* Membership Reward points on every purchase through Reward Multiplier; Vouchers Worth ₹60,000 of Taj Hotels, Luxe Gift Card, and The Postcard Hotels & Resorts; Membership Rewards Points can be transferred to Marriott Bonvoy points at a 1:1 ratio,These Marriott points can then be transferred to United MileagePlus miles at a 3:1 ratio; Membership Rewards Points can be transferred to Emirates Skywards at a ratio of 2:1, with a minimum transfer of 800 MR points required; Membership Rewards Points can be transferred to Etihad Guest Miles at a ratio of 2 MR Points = 1 Etihad Guest Mile, with a maximum redemption limit of 1,600 MR Points. "
|
| 60 |
+
American Express Platinum Travel Credit Card,"Enjoy 8 complimentary visits per year (limited to two complimentary visit per quarter) to airport lounges across India;Get a Taj Experiences E-Gift Card worth Rs. 10,000 from the Taj, SeleQtions and Vivanta Hotels5;Spend11 Rs 1.9 lakhs in a Cardmembership year7 and get 15,000 Membership Rewards® points that can be redeemed on Platinum Travel Collection or on Amex Travel Online3;Earn 1 Membership Rewards Point for every Rs. 50 spent except for spend on Fuel, Insurance, Utilities, Cash Transactions and EMI conversion at Point of Sale;Spending11 Rs. 15,000 within 90 days of Cardmembership entitles you to a Welcome Gift of 10,000 Membership Reward® points. Reaching spend threshold of Rs. 1.9 Lakhs every membership year and get 15,000 bonus Membership Rewards® points that can be redeemed on Platinum Travel Collection or Pay with Points option in Amex Travel Online.Reaching spend threshold of Rs. 4 Lakhs every membership year and get additional 25,000 bonus Membership Rewards® points that can be redeemed on Platinum Travel Collection or Pay with Points option in Amex Travel Online and a Taj Stay voucher5 worth Rs. 10,000 from Taj, SeleQtions and Vivanta Hotels;0% Convenience fee on fueling up at HPCL; One Membership Rewards Point converts to one Marriott Bonvoy Point; Membership Rewards Points can be transferred to Marriott Bonvoy points at a 1:1 ratio,These Marriott points can then be transferred to United MileagePlus miles at a 3:1 ratio; 3 Membership Points can be converted to 1 Club Vistara Point. "
|
| 61 |
+
American Express Membership Rewards Credit Card,"Earn 1 Membership Rewards® Point for every Rs. 50 spent except for spend on Fuel, Insurance, Utilities, Cash Transactions and EMI conversion at Point of Sale;The Annual Membership Renewal fee of Rs.4500 will be 100% waived off if total spends on American Express Credit Card in the immediately preceding membership year is Rs.1,50,000 and above; else 50% annual membership renewal fee will be waived off if total spends on American Express Credit Card in the immediately preceding membership year is Rs.90,000 to Rs.1,49,999;Earn 1,000 Bonus Membership Rewards Points for simply using your card 4 times on transactions of Rs. 1,500 or above in a calendar month;Earn an additional 1,000 Membership Rewards points simply by spending INR 20,000 or more in a calendar month;The 18 and 24 Karat Gold Collections offer exclusive rewards for MR points redemption. With 24,000 MR points, users can choose high-value vouchers (e.g., Taj Rs. 14,000, Amazon Rs. 8,000). With 18,000 MR points, users get slightly lower-value vouchers (e.g., Taj Rs. 9,000, Amazon Rs. 6,000). Options include top brands like Taj, Shoppers Stop, Tanishq, Amazon, Flipkart, and more; One Membership Rewards Point converts to one Marriott Bonvoy Point; Membership Rewards Points can be transferred to Marriott Bonvoy points at a 1:1 ratio,These Marriott points can then be transferred to United MileagePlus miles at a 3:1 ratio. "
|
| 62 |
+
American Express Platinum ReserveSM Credit Card,"Earn 1 Membership Rewards® Point for every Rs. 50 spent except for spend on Fuel, Insurance, Utilities, Cash Transactions and EMI conversion at Point of Sale;The Annual Membership Renewal fee of Rs.10,000 will be 100% waived off if total spends on American Express Platinum Reserve Credit Card in the immediately preceding membership year is Rs.10,00,0008 and above;Enjoy movie or online shopping vouchers worth Rs.12,0008 per year on spending Rs. 50,000 every month;Add luxury to your lifestyle with complimentary Accor Plus Traveller, Taj Epicure and EazyDiner Prime membership;Complimentary access to international and domestic airport lounges across India. Also, enjoy complimentary;0% Convenience fee on fueling up at HPCL;Enjoy exciting discounts of up to 20% every time you dine at select restaurants;A dedicated Concierge service to fulfil your requests coupled with 24x7 Card related assistance.; One Membership Rewards Point converts to one Marriott Bonvoy Point; Membership Rewards Points can be transferred to Marriott Bonvoy points at a 1:1 ratio,These Marriott points can then be transferred to United MileagePlus miles at a 3:1 ratio; Membership Rewards Points can be transferred to Emirates Skywards at a ratio of 2:1, with a minimum transfer of 800 MR points required. "
|
| 63 |
+
American Express Gold Card,"Earn 1 Membership Rewards Point for every Rs. 50 spent including utilities spend; Earn 1,000 bonus points on 6 transactions of ₹1,000 each every calendar month;Redeem your Points from the fabulous 18 and 24 Karat Gold Collection;Earn 5X Membership Reward points on every purchase through Reward Multiplier;Enjoy access to valuable travel benefits under The Hotel Collection enjoy a room upgrade (where available) and a US$100 hotel credit when you stay two consecutive nights at over 400 hotels worldwide including Hilton Hotels, Intercontinental and Hyatt Hotels;Enjoy up to 20% off every time you dine at select restaurant partners;0% Convenience fee on fueling up at HPCL; Membership Rewards Points can be transferred to Marriott Bonvoy points at a 1:1 ratio,These Marriott points can then be transferred to United MileagePlus miles at a 3:1 ratio; Membership Rewards Points can be transferred to Emirates Skywards at a ratio of 2:1, with a minimum transfer of 800 MR points required."
|
| 64 |
+
American Express SmartEarn Credit Card,"Rs. 500 cashback as a Welcome Gift in form of statement credit on eligible spends of Rs. 10,000 in the first 90 days of Cardmembership;Earn Accelerated Membership Rewards® points when you spend on Amazon, Flipkart, Uber, BookMyShow, Zomato, EaseMyTrip and many more;Get a renewal fee waiver on eligible spends of Rs.40,000 and above in the previous year of Cardmembership;Earn vouchers worth ₹500 upon reaching the spend milestones of ₹1.20 lacs, ₹1.80 lacs, and ₹2.40 lacs respectively in a Cardmembership year;Transact on Zomato, Ajio, Nykaa, BookMyShow, Uber and many more and get 10X rewards for every Rs. 50 spent;Transact on Amazon and get 5X rewards for every Rs. 50 spent;On Spending INR 2500 in a month on our 10X Partner (Zomato), you will earn 500 Membership Rewards points. On Spending INR 2500 in a month on our 10X Partner (BookMyShow, Flipkart, Uber and other partners), you will earn 500 Membership Rewards points. On Spending INR 2500 in a month on our 5X Partner (Amazon), you will earn 250 Membership Rewards points;0% Convenience fee on fuel purchase at HPCL for transactions less than Rs. 5,000 and 1% Convenience fee on fuel purchases per transaction is applicable for all transactions of Rs. 5,000 and above; Membership Rewards Points can be transferred to Marriott Bonvoy points at a 1:1 ratio,These Marriott points can then be transferred to United MileagePlus miles at a 3:1 ratio. "
|
| 65 |
+
American Express PAYBACK Credit Card,"Earn 2 PAYBACK Points for every Rs. 100 spent except for spend on Fuel, Insurance, Utilities, Cash Transactions and EMI conversion at Point of Sale.Additionally, get multiple PAYBACK Points across more than 50 PAYBACK partners;Gift vouchers worth Rs. 7,000 on spending Rs. 2.5 lacs in a Cardmembership year.You will earn gift vouchers voucher worth Rs. 2,000 on spending Rs. 1.25 lacs in a Cardmembership year. Additionally, you will earn gift vouchers worth Rs. 5,000 on spending Rs. 2.5 lacs in a Cardmembership year;Get Flipkart voucher worth Rs. 7503 on taking 3 transactions within 60 days of Cardmembership;Enjoy up to 20% off at select restaurants across Delhi, Mumbai, Bangalore and Chennai;0% Convenience fee on fuel purchase at HPCL for transactions less than Rs. 25,000 and 0.3% Convenience fee on fuel purchases per transaction is applicable for all transactions of Rs. 25,000 and above; Membership Rewards Points can be transferred to Marriott Bonvoy points at a 1:1 ratio,These Marriott points can then be transferred to United MileagePlus miles at a 3:1 ratio. "
|
| 66 |
+
IDFC FIRST EARN Credit Card,"Get 100% cashback upto ₹200 on making four UPI transactions with the IDFC Mobile Banking app within 15 days of card issue. Cardholders shall get ₹50 cashback on each of the four transactions.; 1% cashback on all UPI spends made with the IDFC mobile banking app; 0.5% cash back on other eligible spends, including UPI transitions through other apps, utility bill payments, and insurance,cashback is capped at ₹500; 25% discount on movie ticket bookings with Paytm Movies,Save upto ₹1,200 each year on movie tickets alone; Get complimentary Roadside Assistance offered for free with this card."
|
| 67 |
+
IDFC FIRST WoW Credit Card,"4X (4RPs) Reward Points on every eligible spend you make on the card, no reward points shall be awarded for fuel, EMI transactions, or cash withdrawals; Utility spends shall be awarded 1 Reward Point; 50% discount of up to Rs. 300 on movie ticket bookings made through BookMyShow and additionally, moviegoers can receive discounts on food and beverages (up to Rs. 200) when using this card; Non-movie ticket bookings through BookMyShow are eligible for a 50% discount of up to Rs. 500; 1% fuel surcharge waiver across all the fuel stations in India upto Rs.200 per month; 4 complimentary Road Side Assistance worth Rs. 1399 all over India with the Global Assure; Provides insurance covers for Personal Accident, Card Liability Cover, Credit Shield Cover and Purchase Protection; Zero (0%) Foreign Currency Markup"
|
| 68 |
+
Magnet FD Backed Credit Card,"5,000 ZET Coins valued at ₹500. These coins can be used to purchase vouchers for popular brands such as Amazon, Flipkart, and Swiggy; This card charges a low Forex fee of 2.49%; The Magnet Credit Card is a lifetime free offering, meaning there are no joining or annual charges "
|
| 69 |
+
OneCard Metal Credit Card,"1 Reward Point for every ₹50 spent. Fractional points are awarded for amounts less than ₹50; 5x points on your top two spending categories each month, calculated based on your monthly spending; One Card is a lifetime free credit card therefore there is no annual fee; 1% forex markup-fee on international transactions"
|
| 70 |
+
AU Nomo Credit Card,"500 Reward Points as a joining benefit worth Rs. 125; 2 Reward Points/₹100 spent on retail purchases, 1 Reward Point/₹100 spent on utility and insurance transactions, the value of 1 Reward Point is ₹0.25; 0.99% forex markup-fee on international transactions; 2 domestic airport lounge accesses on spending ₹20,000 in a calendar quarter; 2 complimentary railway lounge access"
|
| 71 |
+
SBI Unnati Credit Card,"1 reward point for every ₹100 spent; ₹500 cashback for spending ₹50,000 or more within a year; 1% waiver on fuel transactions between ₹500 and ₹3,000, with a maximum waiver of ₹100 per statement cycle"
|
| 72 |
+
IDBI Bank Imperium Platinum Credit Card,"500 Delight Points on every ₹1,500 spent within the first 30 days of card issuance and an additional 300 Delight Points on transactions between days 31 to 90 post-issuance; 2 Delight Points for every ₹150 spent on shopping, travelling, and movies, 1 reward point equals ₹0.25; 1% waiver on fuel transactions between ₹400 and ₹4,000, with a maximum waiver of ₹300 per month"
|
| 73 |
+
SBI Student Plus Advantage Credit Card,"Annual fee is waived on spending ₹35,000 in a year; Save on your utility bills by getting a 2.5% cashback on eligible purchases; 2.5% waiver on fuel transactions between ₹500 and ₹3,000; 1 reward point for every ₹100 spent on all other transactions and redeem them for exciting gifts and vouchers"
|
| 74 |
+
ICICI Instant Platinum Credit Card,"25% discount of up to Rs 100 on purchase of minimum 2 tickets on BookMyShow; Dining Offers on TGI Fridays, Café Coffee Day, Pizza Hut, etc through ICICI Bank Culinary Treats Program; Redeem reward points against multiple categories like Electronics, Travel & Luggage, Health & Beauty, Fashion and Lifestyle, e-Vouchers and Home & Kitchen"
|
| 75 |
+
Kotak 811 #DreamDifferent Credit Card,"1% fuel surcharge waiver on all petrol pumps for transactions between Rs. 500 and Rs. 3,000 (Maximum Rs. 3,500 per year); 500 bonus reward points on Rs 5,000 spends in the first 45 days; Cashback of Rs 750 or Get 4 PVR Tickets with the milestone program; No Annual fee"
|
| 76 |
+
HDFC Bank Diners Club Black Metal Edition Credit Card,"Complimentary Club Marriott, Swiggy One, and Amazon Prime memberships as welcome benefits (on spends of Rs. 1.5 lakh within 90 days); 5 reward points per Rs. 150 spent; 10X reward points on SmartBuy spends; 2X reward points on weekend dining; Unlimited complimentary domestic lounge access for primary and add-on cardholders; Unlimited complimentary international lounge access for primary and add-on cardholders; 6 complimentary golf games per quarter; Air accident insurance worth Rs. 2 crore; Emergency overseas hospitalization worth Rs. 50 lakh; Annual fee waived on spending Rs. 8 lakh in a year; 2% forex mark-up fee; One Reward Point converts to one Accor Live Limitless Point, which is a 2:1 conversion; Convert reward points into United miles at a value of 0.5 to 1 Airmile per reward point; Reward Points can be converted to Etihad Guest Miles at a ratio of 0.5 Reward Point to 1 Etihad Guest Mile; 1 Reward Point is equivalent to 1 Club Vistara Point; Reward Points earned through the Diners Club Metal Credit Card can be converted to Turkish Airlines Miles&Smiles at a rate of 1 Reward Point = 1 Turkish Mile"
|
| 77 |
+
Tata Neu Infinity HDFC Bank Credit Card,"1,499 NeuCoins welcome benefit on Tata Neu App (equivalent to joining fee, on one transaction within 30 days); Up to 10% NeuCoins on Tata Neu app/website; 5% NeuCoins on partner Tata brands (AirAsia India, Tata 1mg, Tata CLiQ, Croma, cult.fit, Big Basket, Westside, IHCL); 1.5% NeuCoins on UPI spends (max 500 NeuCoins/month); 8 complimentary domestic lounge access annually (2/quarter, unlocked on Rs. 50,000 spend/quarter); 4 complimentary international lounge visits annually for primary/add-on cardholders (1/quarter, Visa variant via Priority Pass, Rupay variant direct); Air accident insurance worth Rs. 1 crore; Emergency overseas hospitalization cover worth Rs. 15 lakhs; Lost card liability cover up to Rs. 9 lakhs; Annual fee waived on Rs. 3 lakh annual spends; 1% fuel surcharge waiver (Rs. 400-Rs. 5,000 transactions, max Rs. 500/month); 2% forex mark-up fee"
|
| 78 |
+
Times Black ICICI Bank Credit Card,"Rs. 10,000 EaseMyTrip Hotel Voucher, Rs. 10,000 Onevasco & More Visa Services, Rs. 3,000 Toni & Guy Voucher, and Zomato Gold Membership as welcome benefits; 2.5% reward points on international spends; 2% reward points on domestic spends (including insurance, utilities, education, government payments); Unlimited complimentary domestic airport lounge access; Unlimited complimentary international airport lounge access via Priority Pass; Low 1.49% forex mark-up fee; Comprehensive insurance cover (Rs. 3 crore personal accident, Rs. 1.4 lakh purchase protection, Rs. 1 lakh credit shield, etc.); Annual fee waived on Rs. 25 lakh annual spends; 1% fuel surcharge waiver up to Rs. 1,000 per statement cycle; No cash withdrawal charges; Up to 35% discount on Avis rental cars; 24/7 domestic and international concierge services; Luxury airport transfers by luxury sedan or helicopter (on Rs. 5 lakh spend annually)"
|
| 79 |
+
HDFC Bank BizBlack Metal Edition Credit Card,"Complimentary Club Marriott Annual Membership and Taj Stay voucher worth Rs. 5,000 as welcome benefits (on Rs. 1.5 lakh spend within 90 days); 5X reward points on select business spends (bill payments via Payzapp/Smartbuy, Income Tax/Advance Tax, Hotel/Flight bookings via MMT MyBiz, Business Productivity Tools); 5 reward points per Rs. 150 on other spends; Unlimited complimentary domestic airport lounge access; Unlimited complimentary international airport lounge access; 6 complimentary golf games per quarter; Credit liability cover up to Rs. 9 lakh; Annual fee waived on Rs. 7.5 lakh annual spends; 2% forex mark-up fee; Reward points redeemable for vouchers, flight/hotel bookings, and cash against statement (1 RP = up to Rs. 1); Milestone benefits: Rs. 5,000 SmartBuy Flight/Taj Stay voucher on Rs. 5 lakh spend (up to 4 vouchers/Rs. 20,000 annually); Discounts on dining via Swiggy Dineout and Club Marriott; Wellness benefits at spas, salons, gyms"
|
| 80 |
+
HDFC Bank BizPower Credit Card,"Amazon Prime Annual and Biz Prime (6 Months) Memberships as welcome benefits (on any transaction within 30 days); 5X reward points on select business spends (Google Ads, Reliance Digital, bill payments via SmartPay & PayZapp, Income Tax/Advance Tax, Vendor/Supplier & GST payments via Swifti & SmartHub Vyapaar, Hotel/Flight bookings on MMT MyBiz, Business Software Purchase via SmartBuy); 4 reward points per Rs. 150 on other spends; Up to 16 complimentary domestic airport lounge visits annually (2 per quarter, plus 2 additional on Rs. 75,000 spend per quarter); 6 complimentary international lounge visits annually with Priority Pass; Flat Rs. 200 discount per transaction on Swiggy Dineout (min Rs. 2,000 MOV); Renewal fee waived on Rs. 4 lakh annual spends; 2% forex mark-up fee; Reward points redeemable for cashback, vouchers, airmiles, flight/hotel bookings; Quarterly milestone benefits: Rs. 2,000 vouchers (MakeMyTrip, Reliance Digital) on Rs. 2.5 lakh spend per quarter; Credit liability cover of Rs. 9 lakh; Business insurance purchasable; Contactless payments up to Rs. 5,000; Convert large purchases to EMIs"
|
| 81 |
+
Emirates Skywards ICICI Bank Rubyx Credit Card,"1.5 Emirates Skyward Miles on every retail spend of Rs. 100; 1 Skyward Mile on every spend of Rs. 100 on utility and insurance categories; Skyward Miles automatically credited to Emirates Skywards account; 1 complimentary domestic lounge access every quarter (on Rs. 75,000 spend in previous quarter); 25% discount up to Rs. 100 on movie tickets via BookMyShow (twice monthly, min two tickets); 15% or more discount on dining via ICICI Bank’s Culinary Treats Programme; 1% fuel surcharge waiver on transactions up to Rs. 4,000; Joining and annual fee of Rs. 1,000 + GST; 3.5% foreign currency mark-up fee"
|
| 82 |
+
EazyDiner IndusInd Bank Credit Card,"2,000 bonus EazyPoints, complimentary EazyDiner Prime annual membership, and Postcard Hotel Stay Voucher worth Rs. 5,000 as welcome benefits; 50% discount on dining via PayEazy on EazyDiner (25% instant + additional 25% up to Rs. 1,000); Free premium alcoholic beverage at selected restaurants; 10 reward points per Rs. 100 on dining, shopping, and entertainment spends; 4 reward points per Rs. 100 on all other spends; Two complimentary movie tickets up to Rs. 200 monthly via BookMyShow; 2 complimentary domestic airport lounge access per quarter (8 annually); 1% fuel surcharge waiver on transactions between Rs. 500 and Rs. 3,000 (capped at Rs. 250/cycle); Reward points redeemable for bill payments via PayEazy on EazyDiner; Joining and renewal fee of Rs. 1,999 + GST; 3.5% foreign currency mark-up fee"
|
| 83 |
+
Axis Bank Vistara Infinite Credit Card,"Receive 1 complimentary Business Class ticket voucher as a welcome benefit and upon renewal; Earn 6 Club Vistara (CV) Points for every Rs. 200 spent; Get up to 4 additional Business Class tickets on achieving spending milestones annually; Enjoy 2 complimentary domestic lounge visits per quarter; Benefit from a complimentary Club Vistara Gold membership; Avail up to 40% discount at partner restaurants via the EazyDiner program; Access 6 complimentary golf rounds at premium courses in India annually; Includes purchase protection cover up to Rs. 1,00,000 and insurance for loss of travel documents or luggage up to USD 300; 1% fuel surcharge waiver on transactions between Rs. 400 & Rs. 4,000; Access to 24x7 concierge services"
|
| 84 |
+
Axis Bank Neo Credit Card,"Lifetime free card with no joining or annual fee; 100% cashback up to Rs. 300 on the first utility bill payment within 30 days; 40% off on Zomato orders twice a month; 10% off on Myntra, Blinkit, and BookMyShow; Earn 1 EDGE Reward Point for every Rs. 200 spent; Get 15% off on dining at partner restaurants; 5% discount on utility bill payments via Paytm; 1% fuel surcharge waiver on transactions between Rs. 400 and Rs. 4,000; Linkable with UPI apps as a RuPay variant"
|
| 85 |
+
Axis Bank Burgundy Private Credit Card,"15 Edge Reward Points for every Rs. 200 spent; Complimentary Eazy Diner, Club Marriott, Taj Epicure, and Accor Plus memberships; Up to 5 complimentary movie tickets per month on BookMyShow; Unlimited complimentary domestic and international lounge access; 50 complimentary golf rounds every year; No cash advance charges & no forex markup fee; Air accident insurance coverage worth Rs. 4.5 crores; 24*7 concierge services; 1% fuel surcharge waiver up to Rs. 400 per month"
|
| 86 |
+
Club Vistara IndusInd Bank Explorer Credit Card,"Complimentary Club Vistara Gold Membership; Business class ticket voucher on joining; Exclusive gift vouchers worth Rs 25,000; Earn up to 8 CV Points per Rs. 200 spent on Vistara & travel; Buy 1 Get 1 Free on movie tickets via BookMyShow twice monthly; Rs. 3,000 off on dining bills twice yearly; 1 complimentary domestic lounge visit quarterly; 4 complimentary international lounge access quarterly via Priority Pass; Air accident cover worth Rs. 2.5 crores; Zero Foreign Currency Markup Charges; 1% fuel surcharge waiver; No cash advance charges; Roadside assistance benefits"
|
| 87 |
+
ixigo AU Credit Card,"Lifetime free card; Welcome benefit: 1000 RPs & Rs. 1000 ixigo Money Voucher on first transaction; Earn 10 RPs per ₹200 for online spends, 5 RPs per ₹200 for offline; 10% discount on flight, bus, & hotel bookings via ixigo; PG charges waived on train bookings twice monthly; 8 complimentary domestic lounge accesses per year; 1 complimentary international lounge access per year; 8 complimentary railway lounge accesses per year; Zero Foreign Currency Markup; 1% Fuel Surcharge Waiver; Credit Shield up to Rs. 1 Lakh; RuPay variant supports UPI transactions"
|
| 88 |
+
RBL World Safari Credit Card,"Welcome benefit: MakeMyTrip Voucher Worth ₹3,000; Earn 5 Travel Points per ₹100 on travel, 2 Travel Points per ₹100 on other eligible spends; Up to 25,000 bonus travel points annually on milestone spends; Taj Experience Gift Card worth ₹10,000 on annual spending of ₹7.5 lacs; 2 complimentary domestic lounge accesses each quarter (8/year); 2 complimentary international lounge accesses each calendar year via Priority Pass; 4 complimentary golf rounds/lessons per year for Mastercard variant; Zero Foreign Currency Markup; 1% fuel surcharge waiver; Travel insurance cover worth USD 50,000 for international travels (medical, flight delay, lost baggage/passport, personal liability); Earn 2 points per ₹100 on domestic retail spends and 5 points per ₹100 on travel spends (no points on international transactions); Get 10,000 points on ₹2.5L spend, 15,000 points on ₹5L, and vouchers worth ₹10,000 on ₹7.5L annual spend"
|
| 89 |
+
Scapia Federal Credit Card,"Lifetime free card; Earn 20% Scapia Coins on travel bookings via Scapia app; Earn 10% Scapia Coins on all other eligible online and offline transactions; Unlimited domestic lounge access on monthly spends of ₹10,000 (Visa) or ₹15,000 (RuPay); Zero Foreign Currency Markup; No Cost EMI option for flight and hotel bookings on Scapia App; Scapia Coins redeemable for 100% of travel bookings on Scapia app; 1% Fuel Surcharge Waiver; RuPay variant available, earns rewards on UPI spends;Value of 5 Scapia coins = ₹1"
|
data.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import threading
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from typing import Dict
|
| 6 |
+
import psutil
|
| 7 |
+
import google.generativeai as genai
|
| 8 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 9 |
+
from langchain_community.chat_models import ChatOpenAI
|
| 10 |
+
from neo4j import GraphDatabase
|
| 11 |
+
from datetime import datetime
|
| 12 |
+
import sys
|
| 13 |
+
import time
|
| 14 |
+
|
| 15 |
+
# accessing and defining the llm served in the cluster
|
| 16 |
+
from langchain_community.chat_models import ChatOpenAI
|
| 17 |
+
|
| 18 |
+
NGROK_URL = "https://636e8b23c1fd.ngrok-free.app"
|
| 19 |
+
VLLM_MODEL_NAME = "hugging-quants/Meta-Llama-3.1-8B-Instruct-AWQ-INT4"
|
| 20 |
+
|
| 21 |
+
llm1 = ChatOpenAI(
|
| 22 |
+
openai_api_key="EMPTY",
|
| 23 |
+
openai_api_base=f"{NGROK_URL}/v1",
|
| 24 |
+
model_name=VLLM_MODEL_NAME,
|
| 25 |
+
temperature=0.0,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
# graph db credentials and gemini api key management
|
| 29 |
+
|
| 30 |
+
NEO4J_URI = "neo4j+s://83c91252.databases.neo4j.io"
|
| 31 |
+
NEO4J_USER = "neo4j"
|
| 32 |
+
NEO4J_PASS = "TKUo104Fqm6BtDNJ2eeb6RRM8xIBS80kdJKLOFv_CkI"
|
| 33 |
+
|
| 34 |
+
logging.basicConfig(
|
| 35 |
+
level=logging.INFO,
|
| 36 |
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
| 37 |
+
handlers=[logging.StreamHandler()]
|
| 38 |
+
)
|
| 39 |
+
logger = logging.getLogger(__name__)
|
| 40 |
+
|
| 41 |
+
API_KEYS = [
|
| 42 |
+
"AIzaSyC4qW9fQAbiCS30xY-Tq086AoRB8lUfxes",
|
| 43 |
+
"AIzaSyAxa91t80DFbtZG0Z8pyHlGG2X0v-sYSRc",
|
| 44 |
+
"AIzaSyDGc7l82E1sa4GarTfGiTXC6dLD7Crk8oo",
|
| 45 |
+
"AIzaSyC463t1T7I6AkKFt9z0eP85KpBVJsGljHc",
|
| 46 |
+
"AIzaSyAHoi9xbYAThtjXlyF_IKFtruoWYoUCjJQ"
|
| 47 |
+
]
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
model_key_counters: Dict[str, int] = {
|
| 51 |
+
"gemini-2.5-flash": 0,
|
| 52 |
+
"gemini-2.0-flash-lite": 0,
|
| 53 |
+
"gemini-2.0-flash": 0,
|
| 54 |
+
"gemini-2.5-flash-lite-preview-06-17": 0,
|
| 55 |
+
"models/embedding-001": 0,
|
| 56 |
+
"models/text-embedding-004": 0
|
| 57 |
+
}
|
| 58 |
+
counter_lock = threading.Lock()
|
| 59 |
+
|
| 60 |
+
# Usage and Error Tracking
|
| 61 |
+
key_usage: Dict[tuple, int] = {(model, key): 0 for model in model_key_counters for key in API_KEYS}
|
| 62 |
+
key_errors: Dict[tuple, int] = {(model, key): 0 for model in model_key_counters for key in API_KEYS}
|
| 63 |
+
MAX_ERRORS_PER_KEY = 50
|
| 64 |
+
|
| 65 |
+
# Lock for genai API calls
|
| 66 |
+
genai_lock = threading.Lock()
|
| 67 |
+
|
| 68 |
+
# Chat Model Cache
|
| 69 |
+
model_cache: Dict[str, ChatGoogleGenerativeAI | genai.GenerativeModel] = {}
|
| 70 |
+
|
| 71 |
+
def select_key_for_model(model_name: str) -> str:
|
| 72 |
+
"""Select the next API key for the model using round-robin, skipping unreliable keys."""
|
| 73 |
+
with counter_lock:
|
| 74 |
+
available_keys = [k for k in API_KEYS if key_errors[(model_name, k)] < MAX_ERRORS_PER_KEY]
|
| 75 |
+
if not available_keys:
|
| 76 |
+
logger.warning(f"All keys for {model_name} exhausted. Resetting errors.")
|
| 77 |
+
for k in API_KEYS:
|
| 78 |
+
key_errors[(model_name, k)] = 0
|
| 79 |
+
available_keys = API_KEYS
|
| 80 |
+
index = model_key_counters[model_name] % len(available_keys)
|
| 81 |
+
selected_key = available_keys[index]
|
| 82 |
+
model_key_counters[model_name] = (index + 1) % len(available_keys)
|
| 83 |
+
key_usage[(model_name, selected_key)] += 1
|
| 84 |
+
logger.info(f"Key {selected_key[:10]}... selected for {model_name}, usage: {key_usage[(model_name, selected_key)]}")
|
| 85 |
+
return selected_key
|
| 86 |
+
|
| 87 |
+
def get_model(model_name: str, use_chat: bool) -> ChatGoogleGenerativeAI | genai.GenerativeModel:
|
| 88 |
+
"""Get or create a ChatGoogleGenerativeAI model with a selected key."""
|
| 89 |
+
key = select_key_for_model(model_name)
|
| 90 |
+
cache_key = f"{model_name}_{key}_{'chat' if use_chat else 'genai'}"
|
| 91 |
+
if cache_key not in model_cache:
|
| 92 |
+
try:
|
| 93 |
+
genai.configure(api_key=key)
|
| 94 |
+
if use_chat:
|
| 95 |
+
model = ChatGoogleGenerativeAI(
|
| 96 |
+
model=model_name,
|
| 97 |
+
temperature=0.7,
|
| 98 |
+
google_api_key=key
|
| 99 |
+
)
|
| 100 |
+
else:
|
| 101 |
+
model = genai.GenerativeModel(model_name)
|
| 102 |
+
|
| 103 |
+
model_cache[cache_key] = model
|
| 104 |
+
logger.info(f"Created Model for {model_name} with key {key[:10]}...")
|
| 105 |
+
except Exception as e:
|
| 106 |
+
key_errors[(model_name, key)] += 1
|
| 107 |
+
logger.error(f"Error creating model for {model_name}: {str(e)}")
|
| 108 |
+
if key_errors[(model_name, key)] >= MAX_ERRORS_PER_KEY:
|
| 109 |
+
logger.warning(f"Key {key[:10]}... unreliable for {model_name}")
|
| 110 |
+
return get_model(model_name) # Retry
|
| 111 |
+
return model_cache[cache_key]
|
| 112 |
+
|
| 113 |
+
def call_genai_embedding_api(model_name: str, *args, **kwargs):
|
| 114 |
+
"""Call genai API with selected key, using a lock."""
|
| 115 |
+
key = select_key_for_model(model_name)
|
| 116 |
+
with genai_lock:
|
| 117 |
+
try:
|
| 118 |
+
genai.configure(api_key=key)
|
| 119 |
+
return genai.embed_content(*args, **kwargs)
|
| 120 |
+
except Exception as e:
|
| 121 |
+
key_errors[(model_name, key)] += 1
|
| 122 |
+
logger.error(f"API call failed for {model_name}: {str(e)}")
|
| 123 |
+
if key_errors[(model_name, key)] >= MAX_ERRORS_PER_KEY:
|
| 124 |
+
logger.warning(f"Key {key[:10]}... unreliable for {model_name}")
|
| 125 |
+
raise
|
| 126 |
+
|
| 127 |
+
def reset_key_metrics():
|
| 128 |
+
"""Reset usage and error counts for all (model, key) pairs."""
|
| 129 |
+
with counter_lock:
|
| 130 |
+
for (model, key) in key_usage:
|
| 131 |
+
key_usage[(model, key)] = 0
|
| 132 |
+
key_errors[(model, key)] = 0
|
| 133 |
+
logger.info("Reset all API key usage and error counts for all models.")
|
| 134 |
+
|
| 135 |
+
# Schedule metrics reset every 8 hours
|
| 136 |
+
def schedule_reset():
|
| 137 |
+
while True:
|
| 138 |
+
try:
|
| 139 |
+
time.sleep(8 * 60 * 60) # Reset every 8 hours
|
| 140 |
+
reset_key_metrics()
|
| 141 |
+
except Exception as e:
|
| 142 |
+
logging.error(f"Error in reset_key_metrics: {e}")
|
| 143 |
+
|
| 144 |
+
# CPU monitoring every 20 seconds
|
| 145 |
+
def monitor_cpu():
|
| 146 |
+
while True:
|
| 147 |
+
try:
|
| 148 |
+
cpu_usage = psutil.cpu_percent(interval=20) # Combine interval with sleep
|
| 149 |
+
logging.info(f"CPU Utilization: {cpu_usage}%")
|
| 150 |
+
except Exception as e:
|
| 151 |
+
logging.error(f"Error in CPU monitoring: {e}")
|
| 152 |
+
|
| 153 |
+
# Start threads
|
| 154 |
+
reset_thread = threading.Thread(target=schedule_reset, daemon=True)
|
| 155 |
+
monitor_thread = threading.Thread(target=monitor_cpu, daemon=True)
|
| 156 |
+
reset_thread.start()
|
| 157 |
+
monitor_thread.start()
|
| 158 |
+
|
| 159 |
+
genai.configure(api_key=os.getenv("GEMINI_API_KEY_1"))
|
| 160 |
+
|
| 161 |
+
_driver = None
|
| 162 |
+
def get_driver():
|
| 163 |
+
global _driver
|
| 164 |
+
if _driver is None:
|
| 165 |
+
_driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER,NEO4J_PASS))
|
| 166 |
+
return _driver
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def debug_print(category, message):
|
| 171 |
+
"""
|
| 172 |
+
Print a debug message with timestamp and category, flushed immediately.
|
| 173 |
+
|
| 174 |
+
Args:
|
| 175 |
+
category (str): Category of the debug message (e.g., NODE, AGENT, TOOL)
|
| 176 |
+
message (str): The message to print
|
| 177 |
+
"""
|
| 178 |
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 179 |
+
print(f"[{timestamp}] [{category}] {message}", flush=True, file=sys.stdout)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
# --- Data Loading ---
|
| 183 |
+
eligibility_df = pd.read_csv("cards_eligibility_updated.csv")
|
| 184 |
+
df = pd.read_csv("credit_card_data_updated.csv")
|
| 185 |
+
# print(df.head())
|
| 186 |
+
card_descriptions = dict(zip(df["name"], df["description"]))
|
| 187 |
+
features = ['Fuel Surcharge Waiver', 'Insurance', 'Shopping Benefits', 'Airport Lounge Access', 'Co-Branded', 'Daily Spends (Grocery)', 'Dining Benefits', 'Domestic Travel Benefits', 'Entertainment', 'General Reward Points', 'Movie Benefits', 'Rupay Network Support', 'Student', 'UPI Transaction Support', 'Welcome Bonus', 'International Travel Benefits', 'premium', 'Flight Discounts', 'Hotel Benefits', 'Travel Benefits', 'Railway Benefits', 'Railway Lounge', 'Utility', 'Beginners (Entry Level)', 'E-commerce Platform Benefits', 'Air Miles', 'Jewellery Spends', 'Concierge Services', 'Food Delivery Benefits', 'Lifestyle & Luxury Perks', 'Spa Access Benefits', 'Golf Access & Perks', 'Super Premium', 'Frequent Flyer Benefits', 'Health Benefits', 'Rent Payment Benefits', 'Education', 'Lifetime Free', 'Roadside Assistance', 'EMI Conversion Options', 'No Forex Markup Fee', 'Secured FD Based', 'Cashback', 'Fuel Benefits', 'Business']
|
| 188 |
+
df_all_cards = pd.read_csv("credit_card_data_updated.csv")
|
| 189 |
+
all_card_names = df_all_cards["name"].tolist()
|
| 190 |
+
all_card_lookup = dict(zip(df_all_cards["name"], df_all_cards["description"]))
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
# --- Eligibility Lookup (now global) ---
|
| 194 |
+
eligibility_lookup = {}
|
| 195 |
+
for _, row in eligibility_df.iterrows():
|
| 196 |
+
name = row["Name"].strip()
|
| 197 |
+
eligibility_lookup[name] = {
|
| 198 |
+
"bank": row["Bank"],
|
| 199 |
+
"age_range": f"{row['Minimum Age']} to {row['Maximum Age']}",
|
| 200 |
+
"income": f"{row['Minimum Income (LPA)']} LPA",
|
| 201 |
+
"credit_score": row["Minimum Credit Score"],
|
| 202 |
+
"joining_fee": row["Joining fee"],
|
| 203 |
+
"annual_fee": row["Annual fee"],
|
| 204 |
+
"issuer_link": row.get("Issuer Link", "").strip()
|
| 205 |
+
}
|
langgraph_pipeline.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langgraph.graph import StateGraph, END
|
| 2 |
+
from typing import Literal
|
| 3 |
+
from data import debug_print
|
| 4 |
+
from langchain_core.messages import AIMessage
|
| 5 |
+
from langgraph.prebuilt import ToolNode,tools_condition
|
| 6 |
+
from nodes.agent import agent_node,TOOLS
|
| 7 |
+
from nodes.intent import oos_handler_node,general_info_handler_node,intent_classifier_node,CreditCardState
|
| 8 |
+
from nodes.format import format_output_node
|
| 9 |
+
from nodes.compare import compare_node_fn
|
| 10 |
+
from nodes.chat import router_node,tool_node,expert_agent_node
|
| 11 |
+
from recommender.graph_retrieval import neo4j_error_handler_node,neo4j_retrieval_node
|
| 12 |
+
from recommender.vectordb import query_refiner_node
|
| 13 |
+
from recommender.vectordb_retrieval import ranked_card_retrieval_node
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# Main Graph flow
|
| 17 |
+
graph = StateGraph(CreditCardState)
|
| 18 |
+
graph.add_node("intent_classifier", intent_classifier_node)
|
| 19 |
+
graph.add_node("general_info_handler", general_info_handler_node)
|
| 20 |
+
graph.add_node("oos_handler", oos_handler_node)
|
| 21 |
+
graph.add_node("query_refiner", query_refiner_node)
|
| 22 |
+
graph.add_node("neo4j_retriever", neo4j_retrieval_node)
|
| 23 |
+
graph.add_node("neo4j_error_handler", neo4j_error_handler_node)
|
| 24 |
+
graph.add_node("ranked_card_retrieval", ranked_card_retrieval_node)
|
| 25 |
+
graph.add_node("agent", agent_node)
|
| 26 |
+
graph.add_node("format_output", format_output_node)
|
| 27 |
+
|
| 28 |
+
graph.set_entry_point("intent_classifier")
|
| 29 |
+
|
| 30 |
+
def route_after_intent_classification(state: CreditCardState):
|
| 31 |
+
intent = state["intent"]
|
| 32 |
+
debug_print("ROUTE", f"Intent classification routing with intent: '{intent}'")
|
| 33 |
+
|
| 34 |
+
if intent == "credit-card-recommendation":
|
| 35 |
+
return "query_refiner"
|
| 36 |
+
elif intent == "general-credit-related":
|
| 37 |
+
return "general_info_handler"
|
| 38 |
+
else:
|
| 39 |
+
return "oos_handler"
|
| 40 |
+
|
| 41 |
+
def route_after_format_output(state: CreditCardState):
|
| 42 |
+
if state.get("trigger_compare", False):
|
| 43 |
+
return "compare_node"
|
| 44 |
+
elif state.get("trigger_chat", False):
|
| 45 |
+
return "chat_node"
|
| 46 |
+
else:
|
| 47 |
+
return END
|
| 48 |
+
|
| 49 |
+
graph.add_conditional_edges(
|
| 50 |
+
"intent_classifier",
|
| 51 |
+
route_after_intent_classification,
|
| 52 |
+
{
|
| 53 |
+
"query_refiner": "query_refiner",
|
| 54 |
+
"general_info_handler": "general_info_handler",
|
| 55 |
+
"oos_handler": "oos_handler",
|
| 56 |
+
},
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
graph.add_edge("general_info_handler", END)
|
| 60 |
+
graph.add_edge("oos_handler", END)
|
| 61 |
+
graph.add_edge("query_refiner", "neo4j_retriever")
|
| 62 |
+
|
| 63 |
+
def route_after_neo4j_retriever(state: CreditCardState):
|
| 64 |
+
debug_print("ROUTE", f"neo4j_error: {state.get('neo4j_error')}")
|
| 65 |
+
if state.get("neo4j_error", False):
|
| 66 |
+
return "neo4j_error_handler"
|
| 67 |
+
else:
|
| 68 |
+
return "ranked_card_retrieval"
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
graph.add_conditional_edges(
|
| 72 |
+
"neo4j_retriever",
|
| 73 |
+
route_after_neo4j_retriever,
|
| 74 |
+
{
|
| 75 |
+
"neo4j_error_handler": "neo4j_error_handler",
|
| 76 |
+
"ranked_card_retrieval": "ranked_card_retrieval",
|
| 77 |
+
},
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
graph.add_edge("neo4j_error_handler", END)
|
| 81 |
+
graph.add_edge("ranked_card_retrieval", "agent")
|
| 82 |
+
|
| 83 |
+
graph.add_edge("agent", "format_output")
|
| 84 |
+
graph.add_edge("format_output",END)
|
| 85 |
+
|
| 86 |
+
app = graph.compile()
|
| 87 |
+
|
| 88 |
+
# invoking function
|
| 89 |
+
async def run_langgraph_pipeline(
|
| 90 |
+
query: str,
|
| 91 |
+
preferences: str,
|
| 92 |
+
query_intent: bool,
|
| 93 |
+
include_cobranded: bool,
|
| 94 |
+
use_eligibility: bool = False,
|
| 95 |
+
age=None,
|
| 96 |
+
income=None,
|
| 97 |
+
cibil=None,
|
| 98 |
+
min_joining_fee=None,
|
| 99 |
+
max_joining_fee=None,
|
| 100 |
+
min_annual_fee=None,
|
| 101 |
+
max_annual_fee=None
|
| 102 |
+
):
|
| 103 |
+
debug_print("PIPELINE", f"Starting pipeline with query: '{query}'")
|
| 104 |
+
debug_print("PIPELINE", f"Preferences: '{preferences}'")
|
| 105 |
+
debug_print("PIPELINE", f"Query intent: {query_intent}, Include cobranded: {include_cobranded}")
|
| 106 |
+
debug_print("PIPELINE", f"Eligibility: {use_eligibility}, Age: {age}, Income: {income}, CIBIL: {cibil}")
|
| 107 |
+
debug_print("PIPELINE", f"Join fee: {min_joining_fee}-{max_joining_fee}, Annual fee: {min_annual_fee}-{max_annual_fee}")
|
| 108 |
+
|
| 109 |
+
inputs = {
|
| 110 |
+
"query": query,
|
| 111 |
+
"preferences": preferences,
|
| 112 |
+
"query_intent": query_intent,
|
| 113 |
+
"include_cobranded": include_cobranded,
|
| 114 |
+
"use_eligibility": use_eligibility,
|
| 115 |
+
"age": age,
|
| 116 |
+
"income": income,
|
| 117 |
+
"cibil": cibil,
|
| 118 |
+
"min_joining_fee": min_joining_fee,
|
| 119 |
+
"max_joining_fee": max_joining_fee,
|
| 120 |
+
"min_annual_fee": min_annual_fee,
|
| 121 |
+
"max_annual_fee": max_annual_fee,
|
| 122 |
+
"agent_outcome": None,
|
| 123 |
+
"messages": [],
|
| 124 |
+
"trigger_chat": False,
|
| 125 |
+
"trigger_compare": False,
|
| 126 |
+
"selected_cards": [],
|
| 127 |
+
"user_message": "",
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
debug_print("PIPELINE", f"Invoking LangGraph app")
|
| 131 |
+
result = await app.ainvoke(inputs)
|
| 132 |
+
debug_print("PIPELINE", f"LangGraph execution complete")
|
| 133 |
+
card_lookup = result.get("card_lookup", {})
|
| 134 |
+
for name, desc in card_lookup.items():
|
| 135 |
+
debug_print("PIPELINE_CARD_LOOKUP", f"{name} -> Description length: {len(desc) if isinstance(desc, str) else 'N/A'}")
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
debug_print("PIPELINE", f"Pipeline complete, returning results")
|
| 139 |
+
return (
|
| 140 |
+
result.get("top_card", "No top card found"),
|
| 141 |
+
result.get("top_card_description", []),
|
| 142 |
+
result.get("card_rows", []),
|
| 143 |
+
result.get("card_names", []),
|
| 144 |
+
result.get("card_lookup", {}),
|
| 145 |
+
result.get("card_links", [])
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
#utility graph for chat and compare features
|
| 149 |
+
|
| 150 |
+
def passthrough_node(state: CreditCardState) -> CreditCardState:
|
| 151 |
+
return state
|
| 152 |
+
|
| 153 |
+
def utility_router(state: CreditCardState):
|
| 154 |
+
if state.get("trigger_compare", False):
|
| 155 |
+
return "compare_node"
|
| 156 |
+
elif state.get("trigger_chat", False):
|
| 157 |
+
return "chat_agent"
|
| 158 |
+
else:
|
| 159 |
+
raise ValueError("No trigger flag set for utility graph.")
|
| 160 |
+
|
| 161 |
+
def should_call_tool(state: CreditCardState):
|
| 162 |
+
if state['router_decision'].decision == "call_tool":
|
| 163 |
+
return "call_tool"
|
| 164 |
+
else:
|
| 165 |
+
return "answer_question"
|
| 166 |
+
|
| 167 |
+
utility_graph = StateGraph(CreditCardState)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
utility_graph.add_node("router", passthrough_node)
|
| 171 |
+
utility_graph.add_node("compare_node", compare_node_fn)
|
| 172 |
+
utility_graph.add_node("chat_router", router_node)
|
| 173 |
+
utility_graph.add_node("call_tool", tool_node)
|
| 174 |
+
utility_graph.add_node("answer_question", expert_agent_node)
|
| 175 |
+
|
| 176 |
+
utility_graph.set_entry_point("router")
|
| 177 |
+
|
| 178 |
+
utility_graph.add_conditional_edges(
|
| 179 |
+
"router",
|
| 180 |
+
utility_router,
|
| 181 |
+
{
|
| 182 |
+
"compare_node": "compare_node",
|
| 183 |
+
"chat_agent": "chat_router",
|
| 184 |
+
},
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
utility_graph.add_conditional_edges(
|
| 188 |
+
"chat_router",
|
| 189 |
+
should_call_tool,
|
| 190 |
+
{
|
| 191 |
+
"call_tool": "call_tool",
|
| 192 |
+
"answer_question": "answer_question",
|
| 193 |
+
}
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
utility_graph.add_edge("call_tool", "answer_question")
|
| 197 |
+
utility_graph.add_edge("answer_question", END)
|
| 198 |
+
utility_graph.add_edge("compare_node", END)
|
| 199 |
+
|
| 200 |
+
utility_app = utility_graph.compile()
|
nodes/agent.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.messages import AIMessage
|
| 2 |
+
from data import debug_print,llm1
|
| 3 |
+
from nodes.intent import get_pretty_state_string,CreditCardState
|
| 4 |
+
from langchain_core.messages import AIMessage, SystemMessage, HumanMessage
|
| 5 |
+
|
| 6 |
+
#generating the final response from the top 5 cards
|
| 7 |
+
|
| 8 |
+
async def agent_node(state: CreditCardState):
|
| 9 |
+
debug_print("NODE", f"Entered agent_node with state:\n{get_pretty_state_string(state)}\n")
|
| 10 |
+
|
| 11 |
+
full_query = state["raw_query"]
|
| 12 |
+
selected_preferences = f"\nUser Preferences: {state['preferences']}\n" if state["preferences"] else ""
|
| 13 |
+
|
| 14 |
+
ranked_cards_raw = state.get("ranked_cards", [])
|
| 15 |
+
formatted_cards = ""
|
| 16 |
+
if isinstance(ranked_cards_raw, list):
|
| 17 |
+
for idx, card in enumerate(ranked_cards_raw, 1):
|
| 18 |
+
name = card.get("name", "Unnamed Card")
|
| 19 |
+
desc = card.get("description", "")
|
| 20 |
+
formatted_cards += f"Card {idx}: {name}; {desc}\n"
|
| 21 |
+
else:
|
| 22 |
+
formatted_cards = str(ranked_cards_raw)
|
| 23 |
+
ranked_cards = formatted_cards.strip()
|
| 24 |
+
|
| 25 |
+
input_message = f"""## User Query: {full_query}
|
| 26 |
+
{selected_preferences}
|
| 27 |
+
Ranked Cards: {ranked_cards}
|
| 28 |
+
|
| 29 |
+
### Instructions:
|
| 30 |
+
You are given a user query and a list of ranked cards.
|
| 31 |
+
1. First, assess whether the provided card list is sufficient and relevant to confidently answer the user's query.
|
| 32 |
+
- If the list is not relevant or lacks information, state that more information is needed.
|
| 33 |
+
2. If the list is relevant, follow these strict rules to select and explain the best one:
|
| 34 |
+
1. Analyze the user's need from the given query.
|
| 35 |
+
2. Select the best card based only on the details in the descriptions.
|
| 36 |
+
3. Explain why it's the best choice (mention only what's explicitly written).
|
| 37 |
+
4. Do not assume any benefit that is not stated.
|
| 38 |
+
5. Use simple, structured output with no symbols like * or #.
|
| 39 |
+
6. If the user asks for FD-based cards or is a beginner, assume all given cards are FD-based and choose the best.
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
messages_for_llm_input = []
|
| 43 |
+
messages_for_llm_input.append(SystemMessage(content="You are a credit card recommendation agent."))
|
| 44 |
+
|
| 45 |
+
# Add the current human input message
|
| 46 |
+
messages_for_llm_input.append(HumanMessage(content=input_message))
|
| 47 |
+
|
| 48 |
+
try:
|
| 49 |
+
debug_print("AGENT", "Starting vLLM agent generation via ChatOpenAI...")
|
| 50 |
+
|
| 51 |
+
response_obj = await llm1.ainvoke(
|
| 52 |
+
messages_for_llm_input,
|
| 53 |
+
config={
|
| 54 |
+
"max_tokens": 512,
|
| 55 |
+
"temperature": 0.7,
|
| 56 |
+
"top_p": 0.9,
|
| 57 |
+
}
|
| 58 |
+
)
|
| 59 |
+
response_text = response_obj.content
|
| 60 |
+
|
| 61 |
+
debug_print("AGENT", f"Decoded Response (truncated):\n{response_text[:500]}...")
|
| 62 |
+
|
| 63 |
+
except Exception as e:
|
| 64 |
+
error_str = str(e)
|
| 65 |
+
debug_print("ERROR", f"Error during generation in agent_node: {error_str}")
|
| 66 |
+
return {
|
| 67 |
+
"messages": [
|
| 68 |
+
AIMessage(
|
| 69 |
+
content=f"Oops! An error occurred during AI generation: {error_str}",
|
| 70 |
+
additional_kwargs={"error": True}
|
| 71 |
+
)
|
| 72 |
+
]
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
return {"messages": [AIMessage(content=response_text)]}
|
nodes/chat.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from data import all_card_lookup,eligibility_lookup,debug_print,llm1
|
| 2 |
+
from pydantic_schema import RouterResult
|
| 3 |
+
from nodes.intent import CreditCardState
|
| 4 |
+
from langchain_core.tools import tool
|
| 5 |
+
from typing import List
|
| 6 |
+
from langgraph.prebuilt import ToolNode
|
| 7 |
+
from langchain_core.messages import SystemMessage, HumanMessage
|
| 8 |
+
import pprint
|
| 9 |
+
|
| 10 |
+
# Chat node with tool
|
| 11 |
+
@tool
|
| 12 |
+
def fetch_card_details(card_names: List[str]) -> str:
|
| 13 |
+
"""Fetches details for a list of credit cards from the database.
|
| 14 |
+
Use this tool to get information about specific credit cards when the user asks about them by name.
|
| 15 |
+
This is useful for comparing cards or getting details about cards not in the initial recommendation.
|
| 16 |
+
Args:
|
| 17 |
+
card_names: A list of exact credit card names to fetch details for.
|
| 18 |
+
Returns:
|
| 19 |
+
A string containing the details of the requested cards.
|
| 20 |
+
"""
|
| 21 |
+
debug_print("TOOL", f"fetch_card_details called with cards: {card_names}")
|
| 22 |
+
case_insensitive_desc_lookup = {k.lower(): v for k, v in all_card_lookup.items()}
|
| 23 |
+
case_insensitive_elig_lookup = {k.lower(): v for k, v in eligibility_lookup.items()}
|
| 24 |
+
details = []
|
| 25 |
+
for card_name in card_names:
|
| 26 |
+
lookup_key = card_name.lower()
|
| 27 |
+
|
| 28 |
+
description = case_insensitive_desc_lookup.get(lookup_key, "Description not found.")
|
| 29 |
+
eligibility = case_insensitive_elig_lookup.get(lookup_key, "No eligibility or fee information available.")
|
| 30 |
+
|
| 31 |
+
card_details = f"Card: {card_name}\nDescription: {description}\n\nEligibility & Fees:\n{eligibility}"
|
| 32 |
+
details.append(card_details)
|
| 33 |
+
|
| 34 |
+
if not details:
|
| 35 |
+
return "Could not find details for the requested cards. Please check the card names."
|
| 36 |
+
|
| 37 |
+
return "\n\n---\n\n".join(details)
|
| 38 |
+
|
| 39 |
+
CHAT_TOOLS = [fetch_card_details]
|
| 40 |
+
chat_tool_node = ToolNode(CHAT_TOOLS)
|
| 41 |
+
|
| 42 |
+
#decides if tool call is required
|
| 43 |
+
async def router_node(state: CreditCardState) -> dict:
|
| 44 |
+
debug_print("NODE", "Entering VISIBLE Chain-of-Thought router_node")
|
| 45 |
+
|
| 46 |
+
known_card_names = state['card_names']
|
| 47 |
+
user_query = state['messages'][-1].content
|
| 48 |
+
known_cards_sentence = ", ".join(f"'{name}'" for name in known_card_names)
|
| 49 |
+
|
| 50 |
+
# Prompt to generate scratchpad/reasoning.
|
| 51 |
+
think_prompt = f"""
|
| 52 |
+
You are an expert routing agent. Your job is to analyze a user's query and write down your reasoning for whether a new credit card needs to be fetched.
|
| 53 |
+
|
| 54 |
+
**Follow these steps:**
|
| 55 |
+
1. **Identify:** Find the specific credit card names mentioned in the User Query.
|
| 56 |
+
2. **Compare:** Check if those names exist in the list of Known Card Names.
|
| 57 |
+
3. **Conclude:** State your final conclusion about whether a tool is needed.
|
| 58 |
+
|
| 59 |
+
**Known Card Names :** We already have information on the following cards: {known_cards_sentence}.
|
| 60 |
+
**User Query:** "{user_query}"
|
| 61 |
+
|
| 62 |
+
**Your Reasoning Scratchpad:**
|
| 63 |
+
"""
|
| 64 |
+
|
| 65 |
+
debug_print("CoT", "Generating reasoning scratchpad...")
|
| 66 |
+
|
| 67 |
+
reasoning_response = await llm1.ainvoke([HumanMessage(content=think_prompt)])
|
| 68 |
+
scratchpad = reasoning_response.content
|
| 69 |
+
debug_print("CoT", f"Generated Scratchpad:\n---SCRATCHPAD---\n{scratchpad}\n----------------")
|
| 70 |
+
|
| 71 |
+
decide_prompt = f"""
|
| 72 |
+
Based on the following reasoning scratchpad, provide your final decision in the required JSON format.
|
| 73 |
+
**Required Format**
|
| 74 |
+
1. `decision`: This must be either "call_tool" or "answer_from_context".
|
| 75 |
+
2. `card_names_to_fetch`: If the decision is "call_tool", this must be a list of the new card names found in the query. Otherwise, it should be null.
|
| 76 |
+
|
| 77 |
+
**Reasoning Scratchpad:**
|
| 78 |
+
{scratchpad}
|
| 79 |
+
|
| 80 |
+
**Final JSON Output:**
|
| 81 |
+
"""
|
| 82 |
+
|
| 83 |
+
json_schema = RouterResult.model_json_schema()
|
| 84 |
+
|
| 85 |
+
debug_print("CoT", "Generating final decision from scratchpad...")
|
| 86 |
+
final_response = await llm1.ainvoke(
|
| 87 |
+
[HumanMessage(content=decide_prompt)],
|
| 88 |
+
extra_body={"guided_json": json_schema}
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
router_decision = RouterResult.model_validate_json(final_response.content)
|
| 92 |
+
debug_print("ROUTER", f"Final Decision: {router_decision}")
|
| 93 |
+
|
| 94 |
+
return {"router_decision": router_decision}
|
| 95 |
+
|
| 96 |
+
#calls the tool
|
| 97 |
+
def tool_node(state: CreditCardState) -> dict:
|
| 98 |
+
debug_print("NODE", "Entering tool_node")
|
| 99 |
+
card_names = state['router_decision'].card_names_to_fetch
|
| 100 |
+
if not card_names: return {"new_card_info": None}
|
| 101 |
+
new_info = fetch_card_details.invoke({"card_names": card_names})
|
| 102 |
+
return {"new_card_info": new_info}
|
| 103 |
+
|
| 104 |
+
#combines the tool results and generates the final reply
|
| 105 |
+
async def expert_agent_node(state: CreditCardState) -> dict:
|
| 106 |
+
print("\n" + "="*60)
|
| 107 |
+
debug_print("EXPERT_AGENT_ENTRY", "Full state entering expert_agent_node:")
|
| 108 |
+
pprint.pprint(state, indent=2)
|
| 109 |
+
print("="*60 + "\n")
|
| 110 |
+
|
| 111 |
+
system_prompt_from_ui = state['messages'][0].content
|
| 112 |
+
new_card_info = state.get("new_card_info")
|
| 113 |
+
chat_history = state['messages'][1:]
|
| 114 |
+
|
| 115 |
+
if new_card_info:
|
| 116 |
+
final_system_prompt = system_prompt_from_ui + "\n\n<Newly_Fetched_Information>\n" + new_card_info + "\n</Newly_Fetched_Information>"
|
| 117 |
+
else:
|
| 118 |
+
final_system_prompt = system_prompt_from_ui
|
| 119 |
+
|
| 120 |
+
messages_to_send = [SystemMessage(content=final_system_prompt)] + chat_history
|
| 121 |
+
|
| 122 |
+
print("\n" + "-"*60)
|
| 123 |
+
debug_print("EXPERT_AGENT_PROMPT", "Final prompt being sent to LLM:")
|
| 124 |
+
pprint.pprint(messages_to_send)
|
| 125 |
+
print("-"*60 + "\n")
|
| 126 |
+
|
| 127 |
+
response = await llm1.ainvoke(messages_to_send)
|
| 128 |
+
return {"messages": [response]}
|
nodes/compare.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from data import debug_print,eligibility_lookup,llm1
|
| 2 |
+
from nodes.intent import get_pretty_state_string,CreditCardState
|
| 3 |
+
from langchain_core.messages import SystemMessage, HumanMessage
|
| 4 |
+
|
| 5 |
+
#comparison node
|
| 6 |
+
async def compare_node_fn(state: CreditCardState) -> CreditCardState:
|
| 7 |
+
debug_print("NODE", f"Entering compare_node_fn with state:\n {get_pretty_state_string(state)}\n")
|
| 8 |
+
|
| 9 |
+
selected_cards = state.get("selected_cards", [])
|
| 10 |
+
card_lookup = state.get("card_lookup", {})
|
| 11 |
+
|
| 12 |
+
debug_print("COMPARE_NODE", f"Selected cards: {selected_cards}")
|
| 13 |
+
debug_print("COMPARE_NODE", f"Card lookup keys: {list(card_lookup.keys())}")
|
| 14 |
+
if not selected_cards or len(selected_cards) < 2:
|
| 15 |
+
debug_print("COMPARE_NODE", "Not enough cards selected for comparison")
|
| 16 |
+
state["comparison_result"] = "Please select at least two cards to compare."
|
| 17 |
+
return state
|
| 18 |
+
|
| 19 |
+
comparison_details = []
|
| 20 |
+
for name in selected_cards:
|
| 21 |
+
eligibility_info = eligibility_lookup.get(name, "No eligibility or fee information available.")
|
| 22 |
+
desc = card_lookup.get(name, "No description available.")
|
| 23 |
+
full_desc = f"{desc}\n\nEligibility & Fees:\n{eligibility_info}"
|
| 24 |
+
comparison_details.append(f"{name}: {full_desc}")
|
| 25 |
+
debug_print("COMPARE_NODE", f"Added card '{name}' with description length: {len(desc)}")
|
| 26 |
+
|
| 27 |
+
use_table_format = len(selected_cards) <= 3
|
| 28 |
+
|
| 29 |
+
#prompts for tabular and normal markdwon responses
|
| 30 |
+
if use_table_format:
|
| 31 |
+
comparison_prompt = (
|
| 32 |
+
"Compare the following credit cards using a clean and concise **markdown table**. The table should have the following structure:\n\n"
|
| 33 |
+
"| Feature | [Card 1 Name] | [Card 2 Name] |\n"
|
| 34 |
+
"|---------|---------------|---------------|\n"
|
| 35 |
+
"| Benefits | [3-4 key benefits]<br>[as concise bullet points] | [3-4 key benefits]<br>[as concise bullet points] |\n"
|
| 36 |
+
"| Fees & Eligibility | [Key fees & eligibility as bullet points] | [Key fees & eligibility as bullet points] |\n"
|
| 37 |
+
"| Limitations | [List any limitations as bullet points] | [List any limitations as bullet points] |\n"
|
| 38 |
+
"| Ideal User | [A single short sentence] | [A single short sentence] |\n\n"
|
| 39 |
+
"Instructions:\n"
|
| 40 |
+
"1. **STRICTLY adhere to the table structure.**\n"
|
| 41 |
+
"2. **DO NOT GUESS OR HALLUCINATE.** Use only the card data provided below.\n"
|
| 42 |
+
"3. For `Benefits`, extract and list **ONLY** the 3-4 most important benefits. Keep each bullet point very short.\n"
|
| 43 |
+
"4. For `Fees & Eligibility`, list the key fees (joining/annual) and income/age as separate concise bullet points.\n"
|
| 44 |
+
"5. For `Limitations`, extract only the top 1-2 limitations. If none are listed, write 'None listed in the provided data.'.\n"
|
| 45 |
+
"6. For `Ideal User`, provide a single sentence describing the best fit for the card.\n"
|
| 46 |
+
"7. After the table, give a brief, one-sentence recommendation.\n"
|
| 47 |
+
"8. Use `<br>` for line breaks inside table cells. Do not use newline characters (`\\n`) or extra `|` inside any cell.\n\n"
|
| 48 |
+
"CARD DATA:\n\n"
|
| 49 |
+
+ "\n\n".join(comparison_details)
|
| 50 |
+
)
|
| 51 |
+
else:
|
| 52 |
+
comparison_prompt = (
|
| 53 |
+
"Compare the following credit cards based on their benefits, fees, eligibility, and ideal use cases.\n\n"
|
| 54 |
+
"Instructions:\n"
|
| 55 |
+
"1. **STRICTLY** use only the card data provided below. Do NOT guess, assume, or hallucinate any information.\n"
|
| 56 |
+
"2. Keep the output short, structured, and highly readable.\n"
|
| 57 |
+
"3. Use markdown format with clear section headers like 'Comparison' and 'Recommendation'.\n"
|
| 58 |
+
"4. Use a concise list of bullet points for each card's benefits, fees, and limitations.\n"
|
| 59 |
+
"5. Conclude with a recommendation on which card suits which type of user.\n\n"
|
| 60 |
+
"CARD DATA:\n\n"
|
| 61 |
+
+ "\n\n".join(comparison_details)
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
debug_print("COMPARE_NODE", "Calling Llama for comparison")
|
| 65 |
+
|
| 66 |
+
try:
|
| 67 |
+
messages = [
|
| 68 |
+
SystemMessage(content="You are an expert in comparing credit card products."),
|
| 69 |
+
HumanMessage(content=comparison_prompt)
|
| 70 |
+
]
|
| 71 |
+
|
| 72 |
+
response_obj = await llm1.ainvoke(
|
| 73 |
+
messages,
|
| 74 |
+
config={"max_tokens": 2048, "temperature": 0.0}
|
| 75 |
+
)
|
| 76 |
+
response_content = response_obj.content
|
| 77 |
+
debug_print("COMPARE_NODE", f"Response received with length: {len(response_content)}")
|
| 78 |
+
print(response_content)
|
| 79 |
+
state["comparison_result"] = response_content
|
| 80 |
+
|
| 81 |
+
except Exception as e:
|
| 82 |
+
debug_print("ERROR", f"Error during comparison: {e}")
|
| 83 |
+
state["comparison_result"] = "An error occurred while comparing the cards. Please try again."
|
| 84 |
+
|
| 85 |
+
return state
|
nodes/format.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from data import debug_print,df,eligibility_df,llm1
|
| 2 |
+
from nodes.intent import get_pretty_state_string,CreditCardState
|
| 3 |
+
from pydantic_schema import CreditCardRecommendation
|
| 4 |
+
from langchain_core.messages import HumanMessage,AIMessage
|
| 5 |
+
import pprint
|
| 6 |
+
|
| 7 |
+
#formatting and structuring the final response
|
| 8 |
+
def extract_card_info_combined(agent_response: CreditCardRecommendation) -> str:
|
| 9 |
+
try:
|
| 10 |
+
best_card = agent_response.best_card
|
| 11 |
+
explanation_list = agent_response.explanation
|
| 12 |
+
explanation_list_points = [f"<li style='color: black;'>{point}</li>" for point in explanation_list]
|
| 13 |
+
explanation = " ".join(explanation_list_points)
|
| 14 |
+
explanation = "<ul> " + explanation + " </ul>"
|
| 15 |
+
|
| 16 |
+
debug_print("UTIL", f"Extracted best card: '{best_card}'")
|
| 17 |
+
debug_print("UTIL", f"Explanation length: {len(explanation)}")
|
| 18 |
+
except Exception as e:
|
| 19 |
+
debug_print("ERROR", f"Failed to parse Gemini response as JSON: {str(e)}")
|
| 20 |
+
best_card = "N/A"
|
| 21 |
+
explanation = "No explanation provided."
|
| 22 |
+
|
| 23 |
+
best_card_block = (
|
| 24 |
+
f"<strong style='color: black;'>Best Card:</strong> {best_card}\n\n"
|
| 25 |
+
f"<strong style='color: black;'>Why It's The Best:</strong> \n{explanation}"
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
return best_card_block
|
| 29 |
+
|
| 30 |
+
def build_card_rows(card_names):
|
| 31 |
+
debug_print("UTIL", f"Building card rows for {len(card_names)} cards")
|
| 32 |
+
|
| 33 |
+
card_rows = []
|
| 34 |
+
card_links = []
|
| 35 |
+
for name in card_names:
|
| 36 |
+
match = df[df["name"] == name]
|
| 37 |
+
joining_fee = "N/A"
|
| 38 |
+
annual_fee = "N/A"
|
| 39 |
+
issuer_link = "N/A"
|
| 40 |
+
description = ""
|
| 41 |
+
|
| 42 |
+
if name in eligibility_df["Name"].values:
|
| 43 |
+
row = eligibility_df[eligibility_df["Name"] == name].iloc[0]
|
| 44 |
+
joining_fee = str(row.get("Joining fee", "N/A"))
|
| 45 |
+
annual_fee = str(row.get("Annual fee", "N/A"))
|
| 46 |
+
issuer_link = row.get("Issuer Link", "N/A")
|
| 47 |
+
if not match.empty:
|
| 48 |
+
description = match.iloc[0].get("description", "")
|
| 49 |
+
card_rows.append([name, joining_fee, annual_fee, description])
|
| 50 |
+
card_links.append(issuer_link)
|
| 51 |
+
|
| 52 |
+
debug_print("UTIL", f"Built {len(card_rows)} card rows")
|
| 53 |
+
return card_rows, card_links
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
async def format_output_node(state: CreditCardState):
|
| 57 |
+
debug_print("NODE", f"Entering format_output_node with state:\n {get_pretty_state_string(state)}\n")
|
| 58 |
+
top_card_html = '''
|
| 59 |
+
<div style="
|
| 60 |
+
background-color: #fff3e0;
|
| 61 |
+
color: #212121;
|
| 62 |
+
border-radius: 16px;
|
| 63 |
+
padding: 20px;
|
| 64 |
+
border: 2px solid #ffa726;
|
| 65 |
+
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
|
| 66 |
+
margin-bottom: 16px;
|
| 67 |
+
font-family: sans-serif;
|
| 68 |
+
font-size: 8px;
|
| 69 |
+
">
|
| 70 |
+
<pre style="white-space: pre-wrap; font-size: 13px; color: #212121;">{message}</pre>
|
| 71 |
+
</div>
|
| 72 |
+
'''
|
| 73 |
+
if not state.get("ranked_cards"):
|
| 74 |
+
debug_print("NODE", "No eligible cards in ranked_cards. Skipping agent interpretation.")
|
| 75 |
+
message = "There are no eligible cards available based on your profile at this time."
|
| 76 |
+
|
| 77 |
+
return {
|
| 78 |
+
"top_card_html": "",
|
| 79 |
+
"top_card": "",
|
| 80 |
+
"top_card_description": [message],
|
| 81 |
+
"card_rows": [],
|
| 82 |
+
"card_names": [],
|
| 83 |
+
"card_lookup": {},
|
| 84 |
+
"card_links": []
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
if "messages" not in state or not state["messages"]:
|
| 89 |
+
debug_print("NODE", "No messages found in state, returning empty output")
|
| 90 |
+
return {
|
| 91 |
+
"top_card_html": top_card_html.format(message="No messages found"),
|
| 92 |
+
"top_card": "No messages found",
|
| 93 |
+
"top_card_description": [],
|
| 94 |
+
"card_rows": [],
|
| 95 |
+
"card_names": [],
|
| 96 |
+
"card_lookup": {}
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
last_message = state["messages"][-1]
|
| 100 |
+
|
| 101 |
+
if isinstance(last_message, AIMessage):
|
| 102 |
+
print(f"Agent: {last_message.content}")
|
| 103 |
+
|
| 104 |
+
model = llm1
|
| 105 |
+
|
| 106 |
+
prompt_template = """
|
| 107 |
+
**SYSTEM ROLE**
|
| 108 |
+
You are an expert content transformation agent. Your task is to analyze the final message from an AI assistant and create a structured JSON response.
|
| 109 |
+
|
| 110 |
+
**CONTEXT**
|
| 111 |
+
- The user's original request was: "{user_query}"
|
| 112 |
+
- The AI assistant's final message is:
|
| 113 |
+
{message}
|
| 114 |
+
|
| 115 |
+
**EXTRACTION RULES**
|
| 116 |
+
You must follow these rules precisely:
|
| 117 |
+
1. **Analyze Outcome:** Determine if a specific card was recommended (SUCCESS_CASE) or if no suitable card was found (FAILURE_CASE).
|
| 118 |
+
2. **SUCCESS_CASE (A card was recommended):**
|
| 119 |
+
- Set `card_found` to `True`.
|
| 120 |
+
- Extract the exact name of the recommended card for `best_card`.
|
| 121 |
+
- Structure the reasons and benefits as a list of strings for the `explanation` field.
|
| 122 |
+
- `reply_if_card_not_found` MUST be `null`.
|
| 123 |
+
3. **FAILURE_CASE (No single card was recommended):**
|
| 124 |
+
- Set `card_found` to `False`.
|
| 125 |
+
- Rephrase the assistant's message into a single, user-friendly `reply_if_card_not_found`.
|
| 126 |
+
- `best_card` and `explanation` fields MUST be `null`.
|
| 127 |
+
|
| 128 |
+
**EXAMPLE**
|
| 129 |
+
- IF the AI_Agent_Message is: "The user's goal is to minimize debt... I can suggest exploring options like the Axis Bank Burgundy Private Credit Card... Another option could be the SBI SimplySAVE Credit Card..."
|
| 130 |
+
- THEN the `reply_if_card_not_found` field in your JSON should be: "While I couldn't find a single credit card that perfectly aligns with your goal of minimizing debt, my research identified a couple of different approaches you could consider. For those with a high net worth, premium cards like the Axis Bank Burgundy Private might offer very low interest rates... For a more accessible option, cards like the SBI SimplySAVE... I suggest exploring these two types of cards to see which strategy best fits your financial profile."
|
| 131 |
+
|
| 132 |
+
**FINAL INSTRUCTION**
|
| 133 |
+
Your entire response MUST be a valid JSON object that conforms to the required schema. Do not add any other text or formatting.
|
| 134 |
+
"""
|
| 135 |
+
|
| 136 |
+
# Extracting the JSON schema from your Pydantic model
|
| 137 |
+
json_schema = CreditCardRecommendation.model_json_schema()
|
| 138 |
+
|
| 139 |
+
prompt = prompt_template.format(user_query=state.get("raw_query", ""), message=last_message.content)
|
| 140 |
+
|
| 141 |
+
try:
|
| 142 |
+
response = await model.ainvoke(
|
| 143 |
+
[HumanMessage(content=prompt)],
|
| 144 |
+
extra_body={"guided_json": json_schema}
|
| 145 |
+
)
|
| 146 |
+
debug_print("STRUCTURED_OUTPUT", f"Raw JSON string from LLM: {response.content}")
|
| 147 |
+
|
| 148 |
+
# Parsing the JSON string response and creating the Pydantic object
|
| 149 |
+
structured_response = CreditCardRecommendation.model_validate_json(response.content)
|
| 150 |
+
debug_print("STRUCTURED_OUTPUT", "Successfully parsed into Pydantic object:")
|
| 151 |
+
pprint.pprint(structured_response.model_dump(), indent=2)
|
| 152 |
+
|
| 153 |
+
except Exception as e:
|
| 154 |
+
debug_print("ERROR", f"Error invoking LLM with structured output: {str(e)}")
|
| 155 |
+
return {
|
| 156 |
+
"top_card_html": top_card_html.format(message="Error processing AI response"),
|
| 157 |
+
"top_card": "Error processing AI response",
|
| 158 |
+
"top_card_description": [],
|
| 159 |
+
"card_rows": [],
|
| 160 |
+
"card_names": [],
|
| 161 |
+
"card_lookup": {}
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
if not structured_response.card_found:
|
| 165 |
+
user_reply = structured_response.reply_if_card_not_found or "Unfortunately, a suitable card could not be found for your specific query"
|
| 166 |
+
debug_print("NODE", f"No specific card found. Generated User Reply: {user_reply}")
|
| 167 |
+
final_html_output = top_card_html.format(message=user_reply)
|
| 168 |
+
return {
|
| 169 |
+
"top_card_html": final_html_output,
|
| 170 |
+
"top_card": user_reply,
|
| 171 |
+
"top_card_description": [],
|
| 172 |
+
"card_rows": [],
|
| 173 |
+
"card_names": [],
|
| 174 |
+
"card_lookup": {}
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
extracted_response = extract_card_info_combined(structured_response)
|
| 178 |
+
card_names = [card["name"] if isinstance(card, dict) else card for card in state["ranked_cards"]]
|
| 179 |
+
debug_print("NODE", f"Using {len(card_names)} card names from ranked_cards")
|
| 180 |
+
top_card_html = f"""
|
| 181 |
+
<div style="
|
| 182 |
+
background-color: #fff3e0;
|
| 183 |
+
color: #212121;
|
| 184 |
+
border-radius: 16px;
|
| 185 |
+
padding: 20px;
|
| 186 |
+
border: 2px solid #ffa726;
|
| 187 |
+
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
|
| 188 |
+
margin-bottom: 16px;
|
| 189 |
+
font-family: sans-serif;
|
| 190 |
+
font-size: 8px;
|
| 191 |
+
">
|
| 192 |
+
<pre style="white-space: pre-wrap; font-size: 13px; color: #212121;">{extracted_response}</pre>
|
| 193 |
+
</div>
|
| 194 |
+
"""
|
| 195 |
+
card_rows, card_links = build_card_rows(card_names)
|
| 196 |
+
card_lookup = {row[0]: row[3] for row in card_rows}
|
| 197 |
+
debug_print("NODE", f"Built {len(card_rows)} card rows")
|
| 198 |
+
|
| 199 |
+
return {
|
| 200 |
+
"top_card_html": top_card_html.format(message=extracted_response),
|
| 201 |
+
"top_card": structured_response.best_card,
|
| 202 |
+
"top_card_description": structured_response.explanation,
|
| 203 |
+
"card_rows": card_rows,
|
| 204 |
+
"card_names": card_names,
|
| 205 |
+
"card_lookup": card_lookup,
|
| 206 |
+
"card_links": card_links
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
debug_print("NODE", "Last message was not from AI, skipping formatting.")
|
| 210 |
+
return {}
|
nodes/intent.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from data import debug_print,llm1
|
| 2 |
+
from langchain_core.messages import HumanMessage
|
| 3 |
+
from pydantic_schema import IntentClassification,RouterResult
|
| 4 |
+
import pprint
|
| 5 |
+
from langgraph.graph import add_messages
|
| 6 |
+
from langgraph.managed import IsLastStep
|
| 7 |
+
from typing import TypedDict, List, Dict, Any, Sequence,Optional
|
| 8 |
+
from typing_extensions import Annotated
|
| 9 |
+
from pydantic_schema import IntentClassification
|
| 10 |
+
|
| 11 |
+
# LangGraph State
|
| 12 |
+
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, BaseMessage
|
| 13 |
+
class CreditCardState(TypedDict):
|
| 14 |
+
raw_query: str
|
| 15 |
+
query: str
|
| 16 |
+
preferences: str
|
| 17 |
+
multi_queries: List[str]
|
| 18 |
+
excluded_cards: List[str]
|
| 19 |
+
intent: IntentClassification
|
| 20 |
+
query_intent: bool
|
| 21 |
+
include_cobranded: bool
|
| 22 |
+
use_eligibility: bool
|
| 23 |
+
age: int
|
| 24 |
+
income: float
|
| 25 |
+
cibil: int
|
| 26 |
+
min_joining_fee: float
|
| 27 |
+
max_joining_fee: float
|
| 28 |
+
min_annual_fee: float
|
| 29 |
+
max_annual_fee: float
|
| 30 |
+
cards: List[str]
|
| 31 |
+
card_links: List[str]
|
| 32 |
+
ranked_cards: List[Dict[str, Any]]
|
| 33 |
+
card_names: List[str]
|
| 34 |
+
card_lookup: Dict[str, str]
|
| 35 |
+
top_card: str
|
| 36 |
+
top_card_description: list[str]
|
| 37 |
+
card_rows: List[List[str]]
|
| 38 |
+
trigger_compare: bool
|
| 39 |
+
trigger_chat: bool
|
| 40 |
+
user_message: str
|
| 41 |
+
chat_history: List
|
| 42 |
+
selected_cards: List[str]
|
| 43 |
+
comparison_result: str
|
| 44 |
+
|
| 45 |
+
messages: Annotated[Sequence[BaseMessage], add_messages]
|
| 46 |
+
is_last_step: IsLastStep
|
| 47 |
+
neo4j_error: bool
|
| 48 |
+
router_decision: Optional["RouterResult"]
|
| 49 |
+
new_card_info: Optional[str]
|
| 50 |
+
|
| 51 |
+
def get_pretty_state_string(state: CreditCardState | dict) -> str:
|
| 52 |
+
"""
|
| 53 |
+
Returns a pretty-printed string representation of the given state dictionary.
|
| 54 |
+
Args:
|
| 55 |
+
state: The state dictionary to pretty-print.
|
| 56 |
+
Returns:
|
| 57 |
+
A string containing the pretty-printed state.
|
| 58 |
+
"""
|
| 59 |
+
return pprint.pformat(state, indent=2)
|
| 60 |
+
|
| 61 |
+
# LangGraph Nodes
|
| 62 |
+
|
| 63 |
+
async def intent_classifier_node(state: CreditCardState):
|
| 64 |
+
"""
|
| 65 |
+
Classifies the user query into one of three intents:
|
| 66 |
+
- 'credit-card-recommendation'
|
| 67 |
+
- 'general-credit-related'
|
| 68 |
+
- 'out-of-scope'
|
| 69 |
+
|
| 70 |
+
Uses LLaMA model (via vLLM) to do classification with minimal prompt + output post-processing.
|
| 71 |
+
"""
|
| 72 |
+
|
| 73 |
+
debug_print("NODE", f"Entered intent_classifier_node with state:\n{get_pretty_state_string(state)}\n")
|
| 74 |
+
|
| 75 |
+
query = state["query"]
|
| 76 |
+
|
| 77 |
+
system_prompt = SystemMessage(content="""You are an AI system that classifies a user's query into one of three categories. Your response MUST be a JSON object with a single key named "intent".
|
| 78 |
+
|
| 79 |
+
**Classification Rules:**
|
| 80 |
+
|
| 81 |
+
1. **credit-card-recommendation:** Use for queries seeking a specific card recommendation based on needs, lifestyle, or rewards.
|
| 82 |
+
2. **Also `credit-card-recommendation`:** Use for queries that are just a list of relevant keywords like "cashback", "travel", or "food, travel, cashback".
|
| 83 |
+
3. **general-credit-related:** Use for queries asking for general information ONLY about **credit cards or financial terms** (e.g., "what is APR?").
|
| 84 |
+
4. **out-of-scope:** Use for ANY query that is not about credit cards, or is inappropriate, harmful, or nonsensical.
|
| 85 |
+
|
| 86 |
+
**--- EXAMPLES ---**
|
| 87 |
+
- "Find me a good cashback card" -> {"intent": "credit-card-recommendation"}
|
| 88 |
+
- "cashback, food, travel" -> {"intent": "credit-card-recommendation"}
|
| 89 |
+
- "what is a balance transfer?" -> {"intent": "general-credit-related"}
|
| 90 |
+
- "what is the weather?" -> {"intent": "out-of-scope"}
|
| 91 |
+
""")
|
| 92 |
+
|
| 93 |
+
intent_prompt_messages = [system_prompt, HumanMessage(content=f"Classify this user query: '{query}'")]
|
| 94 |
+
|
| 95 |
+
try:
|
| 96 |
+
json_schema = IntentClassification.model_json_schema()
|
| 97 |
+
|
| 98 |
+
print("Calling the llama model for structured intent classification...")
|
| 99 |
+
|
| 100 |
+
response_obj = await llm1.ainvoke(
|
| 101 |
+
intent_prompt_messages,
|
| 102 |
+
extra_body={
|
| 103 |
+
"guided_json": json_schema,
|
| 104 |
+
"max_tokens": 30,
|
| 105 |
+
"temperature": 0.0
|
| 106 |
+
}
|
| 107 |
+
)
|
| 108 |
+
print("Response generated.")
|
| 109 |
+
|
| 110 |
+
debug_print("INTENT_RAW_JSON", response_obj.content)
|
| 111 |
+
|
| 112 |
+
structured_response = IntentClassification.model_validate_json(response_obj.content)
|
| 113 |
+
intent = structured_response.intent
|
| 114 |
+
|
| 115 |
+
debug_print("INTENT", f"Final classification: {intent}")
|
| 116 |
+
|
| 117 |
+
except Exception as e:
|
| 118 |
+
debug_print("ERROR", f"Intent classification failed: {e}")
|
| 119 |
+
intent = "out-of-scope"
|
| 120 |
+
|
| 121 |
+
return {"intent": intent}
|
| 122 |
+
|
| 123 |
+
async def general_info_handler_node(state: CreditCardState):
|
| 124 |
+
"""
|
| 125 |
+
Handles general, informational credit-card-related queries using the LLaMA model (via vLLM).
|
| 126 |
+
"""
|
| 127 |
+
|
| 128 |
+
debug_print("NODE", f"Entered general_info_handler_node with state:\n {get_pretty_state_string(state)}\n")
|
| 129 |
+
query = state["query"]
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
general_info_prompt_messages = [
|
| 133 |
+
SystemMessage(content="You are a helpful and knowledgeable financial assistant. Your task is to answer the user's general question about credit cards or related financial topics.\n\n- Provide a clear, accurate, and concise answer.\n- Do not recommend any specific credit card products. Keep the response general and educational.\n- Structure your answer for readability.\n"),
|
| 134 |
+
HumanMessage(content=f"Answer:\nUser Query: {query}\nAnswer:")
|
| 135 |
+
]
|
| 136 |
+
|
| 137 |
+
response_obj = await llm1.ainvoke(
|
| 138 |
+
general_info_prompt_messages,
|
| 139 |
+
config={"max_tokens": 200, "temperature": 0.7}
|
| 140 |
+
)
|
| 141 |
+
response = response_obj.content
|
| 142 |
+
|
| 143 |
+
if "Answer:" in response:
|
| 144 |
+
response = response.split("Answer:")[-1].strip()
|
| 145 |
+
|
| 146 |
+
return {
|
| 147 |
+
"top_card": "",
|
| 148 |
+
"top_card_description": [response],
|
| 149 |
+
"card_rows": [],
|
| 150 |
+
"card_names": [],
|
| 151 |
+
"card_lookup": {}
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
async def oos_handler_node(state: CreditCardState):
|
| 155 |
+
"""
|
| 156 |
+
Handles out-of-scope queries by using LLaMA (via vLLM) to generate a friendly refusal message.
|
| 157 |
+
It informs the user that only credit card recommendation queries are supported.
|
| 158 |
+
"""
|
| 159 |
+
|
| 160 |
+
debug_print("NODE", f"Entered oos_handler_node with state:\n {get_pretty_state_string(state)}\n")
|
| 161 |
+
|
| 162 |
+
query = state["query"]
|
| 163 |
+
|
| 164 |
+
oos_prompt_messages = [
|
| 165 |
+
SystemMessage(content="You are a specialized assistant for a credit card recommendation service.\nYour ONLY function is to politely decline out-of-scope questions.\n\n- Do NOT answer the user’s question.\n- Do NOT engage in unrelated conversation.\n- Simply explain that your scope is limited to credit card recommendations.\n"),
|
| 166 |
+
HumanMessage(content=f"Assistant:\nUser Query: {query}\nAssistant:")
|
| 167 |
+
]
|
| 168 |
+
|
| 169 |
+
response_obj = await llm1.ainvoke(
|
| 170 |
+
oos_prompt_messages,
|
| 171 |
+
config={"max_tokens": 80, "temperature": 0.7}
|
| 172 |
+
)
|
| 173 |
+
response = response_obj.content
|
| 174 |
+
if "Assistant:" in response:
|
| 175 |
+
response = response.split("Assistant:")[-1].strip()
|
| 176 |
+
|
| 177 |
+
return {
|
| 178 |
+
"top_card": "",
|
| 179 |
+
"top_card_description": [response],
|
| 180 |
+
"card_rows": [],
|
| 181 |
+
"card_names": [],
|
| 182 |
+
"card_lookup": {}
|
| 183 |
+
}
|
pydantic_schema.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel, Field
|
| 2 |
+
from typing import List,Literal, Optional
|
| 3 |
+
|
| 4 |
+
# Pydantic Models
|
| 5 |
+
|
| 6 |
+
class CreditCardRecommendation(BaseModel):
|
| 7 |
+
"""
|
| 8 |
+
Structured response for credit card recommendations.
|
| 9 |
+
Handles both successful recommendations and cases where no suitable card is found.
|
| 10 |
+
"""
|
| 11 |
+
card_found: bool = Field(
|
| 12 |
+
description="A boolean flag that MUST be `True` if a card is recommended, and `False` otherwise."
|
| 13 |
+
)
|
| 14 |
+
best_card: Optional[str] = Field(
|
| 15 |
+
default=None,
|
| 16 |
+
description="The name of the best credit card recommended. This MUST be null if card_found is false."
|
| 17 |
+
)
|
| 18 |
+
explanation: Optional[List[str]] = Field(
|
| 19 |
+
default=None,
|
| 20 |
+
description=("A numbered list explaining why the card is best: "
|
| 21 |
+
"1. Main benefit aligned to user query. "
|
| 22 |
+
"2-3. Additional perks. "
|
| 23 |
+
"4. Final justification tying everything to the query."
|
| 24 |
+
)
|
| 25 |
+
)
|
| 26 |
+
reply_if_card_not_found: Optional[str] = Field(
|
| 27 |
+
default=None,
|
| 28 |
+
description=(
|
| 29 |
+
"A user-facing message generated by rephrasing the AI agent's internal monologue when no exact card is found. "
|
| 30 |
+
"This reply should politely explain and justify that no perfect match was found and present the alternative cards or information, if available, as helpful suggestions to explore. "
|
| 31 |
+
"This field MUST be populated if and only if card_found is false."
|
| 32 |
+
)
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
class IntentClassification(BaseModel):
|
| 36 |
+
"""
|
| 37 |
+
Classifies the user query into 'credit-card-recommendation',
|
| 38 |
+
'general-credit-related', or 'out-of-scope'.
|
| 39 |
+
"""
|
| 40 |
+
intent: Literal["credit-card-recommendation", "general-credit-related", "out-of-scope"] = Field(
|
| 41 |
+
description=(
|
| 42 |
+
"The classification of the user's query. "
|
| 43 |
+
"'credit-card-recommendation' for seeking card suggestions. "
|
| 44 |
+
"'general-credit-related' for informational questions about credit cards. "
|
| 45 |
+
"'out-of-scope' for all other cases."
|
| 46 |
+
)
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
#for Chain of Thoughts(CoT) prompting
|
| 50 |
+
class RouterResult(BaseModel):
|
| 51 |
+
"""The structured output from the router agent."""
|
| 52 |
+
decision: Literal["call_tool", "answer_from_context"]
|
| 53 |
+
card_names_to_fetch: Optional[List[str]] = None
|
recommender/graph_retrieval.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from data import get_model,debug_print,get_driver,eligibility_df
|
| 2 |
+
from nodes.intent import CreditCardState
|
| 3 |
+
from neo4j.exceptions import ServiceUnavailable, TransientError
|
| 4 |
+
from langchain.tools import BaseTool
|
| 5 |
+
from typing import List
|
| 6 |
+
import time
|
| 7 |
+
import re
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
#Cypher query generation and graph retrieval
|
| 11 |
+
|
| 12 |
+
class Neo4jConnectionError(Exception):
|
| 13 |
+
pass
|
| 14 |
+
|
| 15 |
+
class Neo4jRetrievalTool(BaseTool):
|
| 16 |
+
name: str = "neo4j_card_retriever"
|
| 17 |
+
description: str = "Runs Cypher and builds FAISS on filtered cards."
|
| 18 |
+
|
| 19 |
+
def generate_cypher(self, user_query: str, query_intent: bool, include_cobranded: bool) -> str:
|
| 20 |
+
debug_print("TOOL", f"generate_cypher called with query: '{user_query}'")
|
| 21 |
+
debug_print("TOOL", f"Parameters: query_intent={query_intent}, include_cobranded={include_cobranded}")
|
| 22 |
+
|
| 23 |
+
model = get_model("gemini-2.0-flash", use_chat=False)
|
| 24 |
+
|
| 25 |
+
context_flags = f"""
|
| 26 |
+
Contextual Flags:
|
| 27 |
+
- FD Card intent: {query_intent}
|
| 28 |
+
- Include co-branded cards: {include_cobranded}
|
| 29 |
+
"""
|
| 30 |
+
prompt = """
|
| 31 |
+
You are an expert Neo4j Cypher query generator.
|
| 32 |
+
|
| 33 |
+
Given a user’s question, graph schema, and **contextual flags**, generate the correct Cypher query. The query should return only the card names`c.name as name`.
|
| 34 |
+
|
| 35 |
+
ONLY output the Cypher query. Do NOT explain anything.
|
| 36 |
+
|
| 37 |
+
---
|
| 38 |
+
|
| 39 |
+
Graph Schema:
|
| 40 |
+
- Nodes:
|
| 41 |
+
- (Card): Properties = name, bank_name, card_type, premium, co_branded
|
| 42 |
+
- (Feature): Properties = name
|
| 43 |
+
- (Partner): Properties = name
|
| 44 |
+
- Relationships:
|
| 45 |
+
- (Card)-[:HAS_FEATURE]->(Feature)
|
| 46 |
+
- (Card)-[:PARTNER_WITH]->(Partner)
|
| 47 |
+
|
| 48 |
+
Feature Inclusion Rules:
|
| 49 |
+
- Include only relevant features mentioned or implied in the refined query. If multiple applicable features from the available list match the query, include all of them together in the f.name IN [...] clause.
|
| 50 |
+
- For vague or broad terms such as “vacation”, “travel”, “frequent flyer”, or “general spending”, **analyze the available feature list below carefully** and include **all relevant travel or spending-related features** that accurately represent the intent. Do not limit to only 1-2 features include relevant features covering all aspects
|
| 51 |
+
- Forex markup fee and foreign transaction fee are the same.
|
| 52 |
+
- If FD Card intent is true then include the features if the query contains any and also include “General Cashback” or “General Reward Points”
|
| 53 |
+
- Don’t add “General Cashback” or “General Reward Points” if it is not required.
|
| 54 |
+
- If fuel is mentioned, include both `Fuel Benefits` and `Fuel Surcharge Waiver`.
|
| 55 |
+
- **ALWAYS** match features using: `f.name IN [...]` — even if there is only **one** feature.
|
| 56 |
+
|
| 57 |
+
Partner Inclusion Rules:
|
| 58 |
+
- If the user query mentions any available partner brand names, include:
|
| 59 |
+
MATCH (c)-[:PARTNER_WITH]->(p:Partner)
|
| 60 |
+
AND p.name IN [<matched partner names>]
|
| 61 |
+
- Always use `p.name IN [...]` — even if there is only one partner.
|
| 62 |
+
|
| 63 |
+
Valid values:
|
| 64 |
+
- card_type: 'FD Card' or 'Regular'
|
| 65 |
+
- premium: true (no concept of false — just include it if applicable)
|
| 66 |
+
- co_branded: true (no concept of false — just include it if applicable)
|
| 67 |
+
|
| 68 |
+
MANDATORY Condition Rules:
|
| 69 |
+
- If FD Card intent is true → include: `c.card_type = 'FD Card'`
|
| 70 |
+
- Else → include: `c.card_type = 'Regular'`
|
| 71 |
+
- If the query is based on beginners or students or people with no or low credit history then use FD Card.
|
| 72 |
+
- If the query uses words like "premium", "elite", "luxury", "exclusive", "infinia", "black", etc. → include: `AND c.premium = true`
|
| 73 |
+
- If the query includes low spending, without high spending or budget → include: `(c.premium IS NULL OR c.premium = false)`
|
| 74 |
+
- If include co-branded is false → include: `AND (c.co_branded IS NULL OR c.co_branded = false)`
|
| 75 |
+
- Use exact values for `bank_name` as in the database: ["SBI", "HDFC", "Axis", "ICICI", "YES", "HSBC", "IDFC", "American Express", "SMB", "Federal Bank", "AU Bank", "IDBI", "Kotak Mahindra Bank","IndusInd","RBL"]
|
| 76 |
+
|
| 77 |
+
---
|
| 78 |
+
|
| 79 |
+
Available features:
|
| 80 |
+
'Fuel Surcharge Waiver','Insurance','Shopping Benefits','Airport Lounge Access','Co-Branded',
|
| 81 |
+
'Daily Spends (Grocery)','Dining Benefits','Domestic Travel Benefits','Entertainment',
|
| 82 |
+
'General Reward Points','Movie Benefits','Rupay Network Support','Student',
|
| 83 |
+
'UPI Transaction Support','Welcome Bonus','International Travel Benefits','premium',
|
| 84 |
+
'Flight Discounts','Hotel Benefits','Travel Benefits','Railway Benefits','Railway Lounge',
|
| 85 |
+
'Utility','Beginners (Entry Level)','E-commerce Platform Benefits','Air Miles',
|
| 86 |
+
'Jewellery Spends','Concierge Services','Food Delivery Benefits','Lifestyle & Luxury Perks',
|
| 87 |
+
'Spa Access Benefits','Golf Access & Perks','Super Premium','Frequent Flyer Benefits',
|
| 88 |
+
'Health Benefits','Rent Payment Benefits','Education','Lifetime Free','Roadside Assistance',
|
| 89 |
+
'EMI Conversion Options','No Forex Markup Fee','Secured FD Based','Cashback','Fuel Benefits','Business'
|
| 90 |
+
|
| 91 |
+
Available partners:
|
| 92 |
+
"Marriott Bonvoy", "Accor", "Taj", "ITC", "The Postcard", "Indigo", "United Airlines", "Emirates", "Etihad", "Club Vistara", "Air India", "Turkish airlines"
|
| 93 |
+
|
| 94 |
+
---
|
| 95 |
+
Few-shot Examples:
|
| 96 |
+
|
| 97 |
+
User Query: Show premium cards with airport lounge access
|
| 98 |
+
Cypher:
|
| 99 |
+
MATCH (c:Card)-[:HAS_FEATURE]->(f:Feature)
|
| 100 |
+
WHERE f.name IN ["Airport Lounge Access"]
|
| 101 |
+
AND c.card_type = 'Regular'
|
| 102 |
+
AND c.premium = true
|
| 103 |
+
RETURN DISTINCT c.name AS name
|
| 104 |
+
|
| 105 |
+
User Query: I want FD cards with spa access and golf perks
|
| 106 |
+
Cypher:
|
| 107 |
+
MATCH (c:Card)-[:HAS_FEATURE]->(f:Feature)
|
| 108 |
+
WHERE f.name IN ["Spa Access Benefits", "Golf Access & Perks"]
|
| 109 |
+
AND c.card_type = 'FD Card'
|
| 110 |
+
RETURN DISTINCT c.name AS name
|
| 111 |
+
|
| 112 |
+
User Query: Cards that support UPI but are not co-branded
|
| 113 |
+
Cypher:
|
| 114 |
+
MATCH (c:Card)-[:HAS_FEATURE]->(f:Feature)
|
| 115 |
+
WHERE f.name IN ["UPI Transaction Support"]
|
| 116 |
+
AND c.card_type = 'Regular'
|
| 117 |
+
AND (c.co_branded IS NULL OR c.co_branded = false)
|
| 118 |
+
RETURN DISTINCT c.name AS name
|
| 119 |
+
|
| 120 |
+
User Query: Cards partnered with Indigo and Vistara that offer flight benefits
|
| 121 |
+
Cypher:
|
| 122 |
+
MATCH (c:Card)-[:PARTNER_WITH]->(p:Partner)
|
| 123 |
+
MATCH (c)-[:HAS_FEATURE]->(f:Feature)
|
| 124 |
+
WHERE f.name IN ["Flight Discounts"]
|
| 125 |
+
AND p.name IN ["Indigo", "Vistara"]
|
| 126 |
+
AND c.card_type = 'Regular'
|
| 127 |
+
RETURN DISTINCT c.name AS name
|
| 128 |
+
|
| 129 |
+
---
|
| 130 |
+
|
| 131 |
+
{context_flags}
|
| 132 |
+
|
| 133 |
+
User Query: {user_query}
|
| 134 |
+
Cypher:
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
cypher_prompt = prompt.format(context_flags=context_flags, user_query=user_query)
|
| 138 |
+
debug_print("TOOL", f"Calling Gemini to generate Cypher query, prompt length: {len(cypher_prompt)}")
|
| 139 |
+
|
| 140 |
+
cypher_code = model.generate_content(cypher_prompt).text.strip()
|
| 141 |
+
cleaned_cypher = cypher_code.strip("`").replace("cypher", "").strip()
|
| 142 |
+
|
| 143 |
+
debug_print("TOOL", f"Generated Cypher query: {cleaned_cypher}")
|
| 144 |
+
return cleaned_cypher
|
| 145 |
+
|
| 146 |
+
def _run(
|
| 147 |
+
self,
|
| 148 |
+
query_text: str,
|
| 149 |
+
query_intent: bool = False,
|
| 150 |
+
excluded_cards: List[str]=[],
|
| 151 |
+
include_cobranded: bool = True,
|
| 152 |
+
use_eligibility: bool = False,
|
| 153 |
+
age: int = None,
|
| 154 |
+
income: float = None,
|
| 155 |
+
cibil: int = None,
|
| 156 |
+
min_joining_fee: float = None,
|
| 157 |
+
max_joining_fee: float = None,
|
| 158 |
+
min_annual_fee: float = None,
|
| 159 |
+
max_annual_fee: float = None
|
| 160 |
+
):
|
| 161 |
+
debug_print("TOOL", f"neo4j_card_retriever _run called with query: '{query_text}'")
|
| 162 |
+
debug_print("TOOL", f"Eligibility filter: {use_eligibility}")
|
| 163 |
+
|
| 164 |
+
cypher = self.generate_cypher(query_text, query_intent, include_cobranded)
|
| 165 |
+
|
| 166 |
+
debug_print("TOOL", f"Executing Cypher query against Neo4j")
|
| 167 |
+
attempt = 0
|
| 168 |
+
while attempt < 3:
|
| 169 |
+
try:
|
| 170 |
+
with get_driver().session() as session:
|
| 171 |
+
matched = [rec["name"] for rec in session.run(cypher)]
|
| 172 |
+
|
| 173 |
+
break # success: exit retry loop
|
| 174 |
+
|
| 175 |
+
except (ServiceUnavailable, TransientError, Neo4jConnectionError, OSError,TimeoutError) as e:
|
| 176 |
+
attempt += 1
|
| 177 |
+
print(f"Neo4j connection error (attempt {attempt}/{3}): {e}")
|
| 178 |
+
if attempt >= 3:
|
| 179 |
+
raise Neo4jConnectionError("Failed to connect to the Neo4j database after retries.") from e
|
| 180 |
+
time.sleep(2 * attempt)
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
debug_print("TOOL", f"Neo4j returned {len(matched)} cards")
|
| 184 |
+
print(excluded_cards)
|
| 185 |
+
print("Before filtering out")
|
| 186 |
+
print(matched)
|
| 187 |
+
|
| 188 |
+
excluded_cards_clean = [normalize_card_name(c) for c in excluded_cards]
|
| 189 |
+
|
| 190 |
+
matched = [card for card in matched if normalize_card_name(card) not in excluded_cards_clean]
|
| 191 |
+
|
| 192 |
+
if use_eligibility:
|
| 193 |
+
debug_print("TOOL", f"Applying eligibility filter with: age={age}, income={income}, cibil={cibil}")
|
| 194 |
+
matched = eligibility_filter(
|
| 195 |
+
matched,
|
| 196 |
+
income,
|
| 197 |
+
cibil,
|
| 198 |
+
age,
|
| 199 |
+
min_joining_fee,
|
| 200 |
+
max_joining_fee,
|
| 201 |
+
min_annual_fee,
|
| 202 |
+
max_annual_fee
|
| 203 |
+
)
|
| 204 |
+
debug_print("TOOL", f"After eligibility filter: {len(matched)} cards remain")
|
| 205 |
+
|
| 206 |
+
return matched
|
| 207 |
+
|
| 208 |
+
def neo4j_retrieval_node(state: dict):
|
| 209 |
+
debug_print("NODE", f"Entering neo4j_retrieval_node with query: '{state['query']}'")
|
| 210 |
+
debug_print("NODE", f"Query intent: {state['query_intent']}, Include cobranded: {state['include_cobranded']}")
|
| 211 |
+
|
| 212 |
+
try:
|
| 213 |
+
tool = Neo4jRetrievalTool()
|
| 214 |
+
cards = tool._run(
|
| 215 |
+
query_text=state["query"],
|
| 216 |
+
query_intent=state["query_intent"],
|
| 217 |
+
include_cobranded=state["include_cobranded"],
|
| 218 |
+
use_eligibility=state["use_eligibility"],
|
| 219 |
+
excluded_cards=state.get("excluded_cards", []),
|
| 220 |
+
age=state["age"],
|
| 221 |
+
income=state["income"],
|
| 222 |
+
cibil=state["cibil"],
|
| 223 |
+
min_joining_fee=state["min_joining_fee"],
|
| 224 |
+
max_joining_fee=state["max_joining_fee"],
|
| 225 |
+
min_annual_fee=state["min_annual_fee"],
|
| 226 |
+
max_annual_fee=state["max_annual_fee"]
|
| 227 |
+
)
|
| 228 |
+
state["cards"] = cards
|
| 229 |
+
state["neo4j_error"] = False
|
| 230 |
+
return state
|
| 231 |
+
|
| 232 |
+
except Neo4jConnectionError as e:
|
| 233 |
+
debug_print("ERROR", f"Neo4j connection failed: {e}")
|
| 234 |
+
debug_print("NODE", "Setting neo4j_error to True due to exception")
|
| 235 |
+
state["neo4j_error"] = True
|
| 236 |
+
state["cards"] = []
|
| 237 |
+
debug_print("NODE", f"Returning from neo4j_retrieval_node with state: {state}")
|
| 238 |
+
return state
|
| 239 |
+
|
| 240 |
+
except Exception as e:
|
| 241 |
+
debug_print("ERROR", f"Unexpected exception in neo4j_retrieval_node: {type(e)} - {e}")
|
| 242 |
+
state["neo4j_error"] = True
|
| 243 |
+
state["cards"] = []
|
| 244 |
+
return state
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def neo4j_error_handler_node(state: CreditCardState):
|
| 248 |
+
message = "Sorry,the graph database is temporarily unavailable. Please try again in a few minutes."
|
| 249 |
+
print("inside neo4j handler")
|
| 250 |
+
state["top_card"] = ""
|
| 251 |
+
state["top_card_description"] = [message]
|
| 252 |
+
state["card_rows"] = []
|
| 253 |
+
state["card_names"] = []
|
| 254 |
+
state["card_lookup"] = {}
|
| 255 |
+
|
| 256 |
+
return state
|
| 257 |
+
|
| 258 |
+
# --- Eligibility Filter ---
|
| 259 |
+
def eligibility_filter(cards, user_income, user_cibil, user_age,min_joining_fee, max_joining_fee,
|
| 260 |
+
min_annual_fee, max_annual_fee):
|
| 261 |
+
eligible_cards = []
|
| 262 |
+
for card_name in cards:
|
| 263 |
+
eligibility = eligibility_df[eligibility_df["Name"] == card_name]
|
| 264 |
+
if not eligibility.empty:
|
| 265 |
+
min_income = eligibility.iloc[0]["Minimum Income (LPA)"]
|
| 266 |
+
min_cibil = eligibility.iloc[0]["Minimum Credit Score"]
|
| 267 |
+
min_age = eligibility.iloc[0]["Minimum Age"]
|
| 268 |
+
max_age = eligibility.iloc[0]["Maximum Age"]
|
| 269 |
+
joining_fee=eligibility.iloc[0]["Joining fee"]
|
| 270 |
+
annual_fee=eligibility.iloc[0]["Annual fee"]
|
| 271 |
+
if (user_income >= min_income and
|
| 272 |
+
user_cibil >= min_cibil and
|
| 273 |
+
min_age <= user_age <= max_age and
|
| 274 |
+
min_joining_fee<=joining_fee<=max_joining_fee and
|
| 275 |
+
min_annual_fee<=annual_fee<=max_annual_fee):
|
| 276 |
+
eligible_cards.append(card_name)
|
| 277 |
+
return eligible_cards
|
| 278 |
+
|
| 279 |
+
def normalize_card_name(name):
|
| 280 |
+
name = name.lower()
|
| 281 |
+
name = name.replace("+", "plus")
|
| 282 |
+
name = re.sub(r"[^a-z0-9 ]", "", name)
|
| 283 |
+
name = re.sub(r"\s+", " ", name).strip()
|
| 284 |
+
return name
|
recommender/vectordb.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from data import call_genai_embedding_api,debug_print,all_card_names,features,card_descriptions,llm1
|
| 2 |
+
from nodes.intent import get_pretty_state_string,CreditCardState
|
| 3 |
+
import faiss
|
| 4 |
+
import numpy as np
|
| 5 |
+
import json
|
| 6 |
+
import re
|
| 7 |
+
from langchain_core.messages import SystemMessage, HumanMessage
|
| 8 |
+
|
| 9 |
+
# --- FAISS Indexing ---
|
| 10 |
+
model_name = "models/text-embedding-004"
|
| 11 |
+
|
| 12 |
+
def chunk_text(text, chunk_size=1):
|
| 13 |
+
sentences = text.split("; ")
|
| 14 |
+
return ["; ".join(sentences[i:i+chunk_size]) for i in range(0, len(sentences), chunk_size)]
|
| 15 |
+
|
| 16 |
+
def get_gemini_embeddings(texts):
|
| 17 |
+
embs = []
|
| 18 |
+
for txt in texts:
|
| 19 |
+
resp = call_genai_embedding_api(model_name, model=model_name, content=txt, task_type="RETRIEVAL_DOCUMENT")
|
| 20 |
+
embs.append(np.array(resp["embedding"], dtype=np.float32))
|
| 21 |
+
return np.vstack(embs)
|
| 22 |
+
|
| 23 |
+
chunk_texts, chunk_name_mapping = [], {}
|
| 24 |
+
chunk_texts, chunk_name_mapping = [], {}
|
| 25 |
+
for card, desc in card_descriptions.items():
|
| 26 |
+
for chunk in chunk_text(desc):
|
| 27 |
+
idx = len(chunk_texts)
|
| 28 |
+
chunk_texts.append(chunk)
|
| 29 |
+
chunk_name_mapping[idx] = card
|
| 30 |
+
|
| 31 |
+
chunk_embeddings = get_gemini_embeddings(chunk_texts)
|
| 32 |
+
faiss.normalize_L2(chunk_embeddings)
|
| 33 |
+
|
| 34 |
+
print(f" Prepared {len(chunk_texts)} chunks and embeddings.")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
#Query refiner and multi query generation node
|
| 38 |
+
|
| 39 |
+
async def query_refiner_node(state: CreditCardState) -> CreditCardState:
|
| 40 |
+
debug_print("NODE", f"Entered query_refiner_node with state:\n {get_pretty_state_string(state)}\n")
|
| 41 |
+
|
| 42 |
+
user_query = state.get("query", "")
|
| 43 |
+
state["raw_query"] = user_query
|
| 44 |
+
preferences = state.get("preferences", [])
|
| 45 |
+
|
| 46 |
+
card_list_str = ", ".join(all_card_names)
|
| 47 |
+
feature_list_str = ", ".join(features)
|
| 48 |
+
|
| 49 |
+
preferences_text = ""
|
| 50 |
+
if preferences:
|
| 51 |
+
preferences_text = "User selected preferences: " + ", ".join(preferences) + "."
|
| 52 |
+
|
| 53 |
+
prompt = f"""
|
| 54 |
+
You are an AI assistant that refines user queries to make them optimized for credit card information retrieval while strictly preserving the user's original intent.
|
| 55 |
+
Your task is to restructure queries in a way that enables accurate and relevant credit card recommendations.
|
| 56 |
+
|
| 57 |
+
Instructions:
|
| 58 |
+
- Identify the main intent or feature of the query (e.g., cashback, lounge access, air miles).
|
| 59 |
+
- Format the query clearly and concisely, with the primary benefit mentioned first.
|
| 60 |
+
- Use direct and specific keywords that match real credit card benefits.
|
| 61 |
+
- Respect and incorporate any stated user preferences.
|
| 62 |
+
- Do NOT introduce any benefits not explicitly requested by the user.
|
| 63 |
+
- Retain and highlight any mention of a specific bank or card brand if provided.
|
| 64 |
+
- If the user mentions "beginner", "entry-level", or "low credit score", focus on basic features like cashback, reward points, and easy approvals.
|
| 65 |
+
|
| 66 |
+
**Card Exclusions:**
|
| 67 |
+
- Only identify cards for exclusion if the user clearly states they already have them.
|
| 68 |
+
- Do not guess or assume exclusions based on features.
|
| 69 |
+
|
| 70 |
+
**Examples:**
|
| 71 |
+
|
| 72 |
+
Example 1
|
| 73 |
+
User Query: "I drive a lot for work and want a credit card with good fuel rewards and travel perks."
|
| 74 |
+
Output:
|
| 75 |
+
{{
|
| 76 |
+
"optimized_query": "Category: Fuel Rewards | Best credit cards for high fuel spending with maximum rewards & fuel surcharge waiver. Travel perks preferred but secondary.",
|
| 77 |
+
"excluded_cards": []
|
| 78 |
+
}}
|
| 79 |
+
|
| 80 |
+
Example 2
|
| 81 |
+
User Query: "I already have a card with airline miles. Are there better options for frequent flyers?"
|
| 82 |
+
Output:
|
| 83 |
+
{{
|
| 84 |
+
"optimized_query": "Category: Travel Benefits | Credit cards with premium travel perks like airport lounge access, hotel discounts, and concierge services. Avoid repeating airline mile features already covered.",
|
| 85 |
+
"excluded_cards": []
|
| 86 |
+
}}
|
| 87 |
+
|
| 88 |
+
The following is the user's query. Please provide a JSON response.
|
| 89 |
+
|
| 90 |
+
Preferences: "{preferences_text}"
|
| 91 |
+
User Query: "{user_query}"
|
| 92 |
+
|
| 93 |
+
Output:
|
| 94 |
+
"""
|
| 95 |
+
|
| 96 |
+
print("Rewriting user query for optimization...")
|
| 97 |
+
|
| 98 |
+
try:
|
| 99 |
+
|
| 100 |
+
query_refine_messages = [
|
| 101 |
+
SystemMessage(content="You are an AI assistant that refines user queries to make them optimized for credit card information retrieval while strictly preserving the user's original intent."),
|
| 102 |
+
HumanMessage(content=prompt)
|
| 103 |
+
]
|
| 104 |
+
|
| 105 |
+
response_obj = await llm1.ainvoke(
|
| 106 |
+
query_refine_messages,
|
| 107 |
+
config={"max_tokens": 512, "temperature": 0.0}
|
| 108 |
+
)
|
| 109 |
+
clean_text = re.sub(r"^```json|```$", "", response_obj.content.strip(), flags=re.MULTILINE).strip()
|
| 110 |
+
result = json.loads(clean_text)
|
| 111 |
+
|
| 112 |
+
optimized_query = result["optimized_query"]
|
| 113 |
+
excluded_cards = result.get("excluded_cards", [])
|
| 114 |
+
|
| 115 |
+
print("Optimized:", optimized_query)
|
| 116 |
+
print("Excluded Cards:", excluded_cards)
|
| 117 |
+
|
| 118 |
+
state["query"] = optimized_query
|
| 119 |
+
state["excluded_cards"] = excluded_cards
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
#generating multiple queries
|
| 123 |
+
multi_query_prompt = f"""
|
| 124 |
+
The following is a detailed credit card search query:
|
| 125 |
+
"{optimized_query}"
|
| 126 |
+
Generate 3 distinct subqueries that **collectively cover all the important features** from the original query.
|
| 127 |
+
Each subquery should emphasize a **different combination** of the features (e.g., lounge access, travel insurance, low foreign transaction fees, hotel discounts, etc.).
|
| 128 |
+
Keep the same format: "Category: ... | ...". Make sure all features from the original query are represented across the 3 queries.
|
| 129 |
+
Output only the subqueries, one per line. Do not include any explanations, numbering, or formatting — just plain queries separated by newline characters.
|
| 130 |
+
"""
|
| 131 |
+
|
| 132 |
+
multi_query_messages = [
|
| 133 |
+
SystemMessage(content="You are an AI assistant that generates multiple search queries based on a primary query."),
|
| 134 |
+
HumanMessage(content=multi_query_prompt)
|
| 135 |
+
]
|
| 136 |
+
|
| 137 |
+
response2_obj = await llm1.ainvoke(
|
| 138 |
+
multi_query_messages,
|
| 139 |
+
config={"max_tokens": 256, "temperature": 0.7}
|
| 140 |
+
)
|
| 141 |
+
queries = [q.strip() for q in response2_obj.content.strip().split('\n') if q.strip()]
|
| 142 |
+
|
| 143 |
+
state["multi_queries"] = queries
|
| 144 |
+
print("Generated Multi-Queries:", queries)
|
| 145 |
+
except Exception as e:
|
| 146 |
+
print("LLM query refinement failed:", e)
|
| 147 |
+
state["excluded_cards"] = []
|
| 148 |
+
state["multi_queries"] = []
|
| 149 |
+
|
| 150 |
+
return state
|
recommender/vectordb_retrieval.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import faiss
|
| 2 |
+
import numpy as np
|
| 3 |
+
from sentence_transformers import CrossEncoder
|
| 4 |
+
from collections import defaultdict
|
| 5 |
+
from data import debug_print,call_genai_embedding_api
|
| 6 |
+
from nodes.intent import CreditCardState
|
| 7 |
+
from data import df
|
| 8 |
+
from recommender.vectordb import chunk_name_mapping,chunk_embeddings
|
| 9 |
+
|
| 10 |
+
#Faiss vector db retrieval
|
| 11 |
+
|
| 12 |
+
cross_encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2")
|
| 13 |
+
|
| 14 |
+
def ranked_card_retrieval_node(state: CreditCardState):
|
| 15 |
+
debug_print("NODE", f"Entering ranked_card_retrieval_node with multi-query support")
|
| 16 |
+
|
| 17 |
+
queries = state.get("multi_queries", [])
|
| 18 |
+
original_query = state["query"]
|
| 19 |
+
cards = state["cards"]
|
| 20 |
+
|
| 21 |
+
# getting the indices of the cards retrieved from neo4j graph
|
| 22 |
+
rel_idxs = [i for i, name in chunk_name_mapping.items() if name in cards]
|
| 23 |
+
if not rel_idxs:
|
| 24 |
+
debug_print("NODE", "No relevant indices found for cards, returning empty ranked_cards")
|
| 25 |
+
return {"ranked_cards": []}
|
| 26 |
+
|
| 27 |
+
#storing the chunks in a FAISS index
|
| 28 |
+
embs = chunk_embeddings[rel_idxs]
|
| 29 |
+
mapping = {j: chunk_name_mapping[i] for j, i in enumerate(rel_idxs)}
|
| 30 |
+
idx = faiss.IndexFlatIP(embs.shape[1])
|
| 31 |
+
faiss.normalize_L2(embs)
|
| 32 |
+
idx.add(embs)
|
| 33 |
+
|
| 34 |
+
all_retrieved = []
|
| 35 |
+
|
| 36 |
+
for query in queries:
|
| 37 |
+
|
| 38 |
+
query_embedding = call_genai_embedding_api(
|
| 39 |
+
"models/embedding-001",
|
| 40 |
+
model="models/embedding-001",
|
| 41 |
+
content=query,
|
| 42 |
+
task_type="RETRIEVAL_QUERY"
|
| 43 |
+
)["embedding"]
|
| 44 |
+
query_embedding = np.array(query_embedding, dtype=np.float32).reshape(1, -1)
|
| 45 |
+
faiss.normalize_L2(query_embedding)
|
| 46 |
+
|
| 47 |
+
D, I = idx.search(query_embedding, 50)
|
| 48 |
+
similarity_scores = D[0]
|
| 49 |
+
|
| 50 |
+
card_similarity = defaultdict(float)
|
| 51 |
+
card_dict = {card["name"]: card for card in df.to_dict(orient="records")}
|
| 52 |
+
unique_cards = {}
|
| 53 |
+
|
| 54 |
+
for i, chunk_idx in enumerate(I[0]):
|
| 55 |
+
if chunk_idx == -1:
|
| 56 |
+
continue
|
| 57 |
+
card_name = mapping.get(chunk_idx)
|
| 58 |
+
if card_name and (card_name not in unique_cards or similarity_scores[i] > card_similarity[card_name]):
|
| 59 |
+
card_similarity[card_name] = similarity_scores[i]
|
| 60 |
+
unique_cards[card_name] = {
|
| 61 |
+
"name": card_name,
|
| 62 |
+
"description": card_dict.get(card_name, {}).get("description", ""),
|
| 63 |
+
"similarity": similarity_scores[i]
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
all_retrieved.extend(unique_cards.values())
|
| 68 |
+
|
| 69 |
+
seen = set()
|
| 70 |
+
deduped_cards = []
|
| 71 |
+
for card in all_retrieved:
|
| 72 |
+
if card["name"] not in seen:
|
| 73 |
+
seen.add(card["name"])
|
| 74 |
+
deduped_cards.append(card)
|
| 75 |
+
|
| 76 |
+
if not deduped_cards:
|
| 77 |
+
debug_print("NODE", "No deduplicated cards found, returning empty ranked_cards")
|
| 78 |
+
return {"ranked_cards": []}
|
| 79 |
+
|
| 80 |
+
#Reranking based on original query
|
| 81 |
+
pairs = [[original_query, c["description"]] for c in deduped_cards]
|
| 82 |
+
scores = cross_encoder.predict(pairs)
|
| 83 |
+
|
| 84 |
+
if len(scores) == 0 or (max(scores) - min(scores)) == 0:
|
| 85 |
+
ranked_cards = deduped_cards[:5]
|
| 86 |
+
debug_print("NODE", "Using first 5 cards (no meaningful re-rank)")
|
| 87 |
+
else:
|
| 88 |
+
norm_scores = (np.array(scores) - np.min(scores)) / (np.max(scores) - np.min(scores))
|
| 89 |
+
sorted_cards = sorted(zip(norm_scores, deduped_cards), key=lambda x: x[0], reverse=True)
|
| 90 |
+
ranked_cards = [c for _, c in sorted_cards[:5]]
|
| 91 |
+
|
| 92 |
+
debug_print("NODE", f"Exiting ranked_card_retrieval_node with {len(ranked_cards)} ranked cards")
|
| 93 |
+
return {"ranked_cards": ranked_cards}
|
requirements.txt
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Core language/agent frameworks
|
| 2 |
+
langchain
|
| 3 |
+
langchain-community
|
| 4 |
+
langgraph
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
# Google Gemini / Generative AI
|
| 8 |
+
google-generativeai
|
| 9 |
+
langchain-google-genai
|
| 10 |
+
|
| 11 |
+
# Graph DB
|
| 12 |
+
neo4j
|
| 13 |
+
|
| 14 |
+
# Vector search and embeddings
|
| 15 |
+
faiss-cpu
|
| 16 |
+
sentence-transformers
|
| 17 |
+
|
| 18 |
+
# UI
|
| 19 |
+
gradio
|
| 20 |
+
|
| 21 |
+
# Data handling
|
| 22 |
+
pandas
|
| 23 |
+
numpy
|
| 24 |
+
datasets
|
| 25 |
+
|
| 26 |
+
# Utilities
|
| 27 |
+
duckduckgo-search
|
| 28 |
+
async-timeout
|
| 29 |
+
psutil
|
ui/gradio_interface.py
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.messages import SystemMessage, HumanMessage
|
| 2 |
+
from typing import List, Dict, Any
|
| 3 |
+
import time
|
| 4 |
+
import gradio as gr
|
| 5 |
+
from data import debug_print,all_card_names,all_card_lookup,eligibility_lookup,df
|
| 6 |
+
from nodes.intent import get_pretty_state_string
|
| 7 |
+
from langgraph_pipeline import run_langgraph_pipeline,utility_app
|
| 8 |
+
|
| 9 |
+
#Gradio UI with the fucntion calls to invoke the graphs and pass the user inputs
|
| 10 |
+
custom_css="""
|
| 11 |
+
#compare_output_markdown, #full_compare_output_markdown, #top_card_markdown {
|
| 12 |
+
min-height: 100px;
|
| 13 |
+
border: 1px solid #e0e0e0;
|
| 14 |
+
padding: 10px;
|
| 15 |
+
overflow-y: auto;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
#left_column_box, #right_column_box {
|
| 19 |
+
padding: 16px !important; /* Adds 16px of space INSIDE the bordered box */
|
| 20 |
+
}
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
with gr.Blocks(title="Agentic Credit Card Recommender", css=custom_css, theme=gr.themes.Soft()) as demo:
|
| 24 |
+
gr.Markdown("""
|
| 25 |
+
# Credit Card Recommender
|
| 26 |
+
Discover and receive personalized recommendations for credit cards based on your needs and preferences
|
| 27 |
+
""")
|
| 28 |
+
with gr.Tab("Get Recommendations"):
|
| 29 |
+
|
| 30 |
+
with gr.Row():
|
| 31 |
+
query_input = gr.Textbox(
|
| 32 |
+
label="What kind of card are you looking for?",
|
| 33 |
+
placeholder="Best cards for online shopping with fuel benefits",
|
| 34 |
+
elem_id="query-textbox",
|
| 35 |
+
scale=2
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
preferences = gr.Dropdown(
|
| 39 |
+
choices=["Cashback", "Travel", "Fuel", "Airport Lounge access", "Railways", "Dining", "Online Spends", "Grocery"],
|
| 40 |
+
multiselect=True,
|
| 41 |
+
label="Select Preferred Card Categories",
|
| 42 |
+
scale=1
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
with gr.Accordion("Eligibility & Fee Filters", open=False):
|
| 46 |
+
with gr.Row():
|
| 47 |
+
income = gr.Slider(minimum=1, maximum=60, step=1, label="Annual Income (LPA)")
|
| 48 |
+
cibil = gr.Slider(minimum=300, maximum=900, step=10, label="CIBIL Score")
|
| 49 |
+
age = gr.Slider(minimum=18, maximum=75, step=1, label="Age")
|
| 50 |
+
|
| 51 |
+
with gr.Row():
|
| 52 |
+
with gr.Group():
|
| 53 |
+
gr.Markdown("<p style='text-align:center;'>Preferred Joining Fee (₹)</p>")
|
| 54 |
+
with gr.Row():
|
| 55 |
+
min_joining_fee = gr.Number(label="Min", value=0)
|
| 56 |
+
max_joining_fee = gr.Number(label="Max", value=150000)
|
| 57 |
+
with gr.Group():
|
| 58 |
+
gr.Markdown("<p style='text-align:center;'>Preferred Annual Fee (₹)</p>")
|
| 59 |
+
with gr.Row():
|
| 60 |
+
min_annual_fee = gr.Number(label="Min", value=0)
|
| 61 |
+
max_annual_fee = gr.Number(label="Max", value=150000)
|
| 62 |
+
|
| 63 |
+
with gr.Row():
|
| 64 |
+
use_eligibility = gr.Checkbox(label="Apply Eligibility Filter", value=False)
|
| 65 |
+
fd_checkbox = gr.Checkbox(label="Beginner / Student", value=False)
|
| 66 |
+
cobrand_checkbox = gr.Checkbox(label="Include Co-branded Cards", value=True)
|
| 67 |
+
|
| 68 |
+
run_button = gr.Button("Recommend Cards", variant='primary')
|
| 69 |
+
|
| 70 |
+
top_card_recommendation = gr.Markdown(value="", elem_id="top_card_markdown")
|
| 71 |
+
with gr.Row(visible=True) as results_container:
|
| 72 |
+
with gr.Column():
|
| 73 |
+
with gr.Group(elem_id="left_column_box"):
|
| 74 |
+
recommendation_heading = gr.Markdown("### Top-Ranked Cards")
|
| 75 |
+
card_table_markdown = gr.Markdown()
|
| 76 |
+
|
| 77 |
+
with gr.Column():
|
| 78 |
+
with gr.Group(elem_id="right_column_box"):
|
| 79 |
+
card_links_heading = gr.Markdown("### 🔗 Issuer Links")
|
| 80 |
+
card_links_html = gr.HTML()
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
with gr.Column(visible=False) as chat_container:
|
| 84 |
+
with gr.Accordion("💬 Ask Follow-up Questions", open=True):
|
| 85 |
+
chatbox = gr.Chatbot(type="messages", label="Chat")
|
| 86 |
+
followup_input = gr.Textbox(label="Enter Your question", placeholder="Compare the lounge access benefits of card X and card Y")
|
| 87 |
+
followup_submit = gr.Button("Submit", variant="primary")
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
with gr.Tab("Compare Cards"):
|
| 91 |
+
with gr.Column(visible=False) as compare_recommended_cards_container:
|
| 92 |
+
gr.Markdown("## Compare Recommended Cards")
|
| 93 |
+
compare_checkboxes = gr.CheckboxGroup(choices=[], label="Select 2 or more cards to compare", info="Pick from the recommended list to see a comparison.")
|
| 94 |
+
compare_btn = gr.Button("Compare Selected Cards", variant='primary')
|
| 95 |
+
compare_output = gr.Markdown(value="", elem_id="compare_output_markdown")
|
| 96 |
+
gr.Markdown("---")
|
| 97 |
+
|
| 98 |
+
gr.Markdown("## Compare Any Cards")
|
| 99 |
+
full_compare_dropdown = gr.Dropdown(choices=all_card_names, multiselect=True, label="Select 2 or more cards", info="Compare any cards from the entire database.")
|
| 100 |
+
full_compare_btn = gr.Button("Compare Selected Cards", variant='primary')
|
| 101 |
+
full_compare_output = gr.Markdown(value="", elem_id="full_compare_output_markdown")
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def format_to_markdown(top_card_out, top_card_description_out):
|
| 105 |
+
debug_print("UI", f"Formatting top card output to markdown")
|
| 106 |
+
if not top_card_out and top_card_description_out:
|
| 107 |
+
message_block = "\n".join(f"- {desc}" for desc in top_card_description_out if desc)
|
| 108 |
+
return f"### Note\n\n{message_block}\n\n"
|
| 109 |
+
|
| 110 |
+
top_card_recommendation = f"### Best card: {top_card_out}\n\n"
|
| 111 |
+
if top_card_description_out:
|
| 112 |
+
for desc in top_card_description_out:
|
| 113 |
+
if isinstance(desc, str):
|
| 114 |
+
desc = desc.strip()
|
| 115 |
+
if desc:
|
| 116 |
+
top_card_recommendation += f"- {desc}\n"
|
| 117 |
+
|
| 118 |
+
return top_card_recommendation
|
| 119 |
+
|
| 120 |
+
def format_rows_to_markdown_table(card_rows):
|
| 121 |
+
"""Converts a list of card data into a markdown table string."""
|
| 122 |
+
if not card_rows:
|
| 123 |
+
return ""
|
| 124 |
+
|
| 125 |
+
markdown_table = "| Card Name | Joining Fee | Annual Fee |\n"
|
| 126 |
+
markdown_table += "|---|---|---|\n"
|
| 127 |
+
|
| 128 |
+
for row in card_rows:
|
| 129 |
+
name = row[0]
|
| 130 |
+
joining_fee = row[1]
|
| 131 |
+
annual_fee = row[2]
|
| 132 |
+
markdown_table += f"| {name} | {joining_fee} | {annual_fee} |\n"
|
| 133 |
+
|
| 134 |
+
return markdown_table
|
| 135 |
+
|
| 136 |
+
#main function that invokes the graph
|
| 137 |
+
async def recommend(query, preferences, fd_intent, include_cobranded, income, cibil, age, min_joining_fee, max_joining_fee, min_annual_fee, max_annual_fee, use_eligibility):
|
| 138 |
+
debug_print("UI", f"recommend called with query: '{query}'")
|
| 139 |
+
debug_print("UI", f"Preferences: {preferences}")
|
| 140 |
+
debug_print("UI", f"FD intent: {fd_intent}, Include cobranded: {include_cobranded}")
|
| 141 |
+
|
| 142 |
+
global chat_history
|
| 143 |
+
chat_history = []
|
| 144 |
+
global messages
|
| 145 |
+
messages = []
|
| 146 |
+
preferences_text=""
|
| 147 |
+
if preferences:
|
| 148 |
+
preferences_text = "User selected preferences: " + ", ".join(preferences) + "."
|
| 149 |
+
|
| 150 |
+
query = query.strip() if query else ""
|
| 151 |
+
|
| 152 |
+
if not query:
|
| 153 |
+
error_message = "Please enter a valid query."
|
| 154 |
+
debug_print("UI", f"recommend function error: {error_message}")
|
| 155 |
+
return error_message, gr.update(visible=True), gr.update(value=None), gr.update(visible=False), gr.update(visible=False), gr.update(value=None), gr.update(visible=False), [], {}, "", gr.update(visible=False),{}
|
| 156 |
+
|
| 157 |
+
try:
|
| 158 |
+
debug_print("UI", f"Calling run_langgraph_pipeline")
|
| 159 |
+
top_card_out, top_card_description_out, card_rows_out, card_names_out, card_lookup_out,card_links = await run_langgraph_pipeline(
|
| 160 |
+
query,
|
| 161 |
+
preferences_text,
|
| 162 |
+
query_intent=fd_intent,
|
| 163 |
+
include_cobranded=include_cobranded,
|
| 164 |
+
use_eligibility=use_eligibility,
|
| 165 |
+
income=income,
|
| 166 |
+
cibil=cibil,
|
| 167 |
+
age=age,
|
| 168 |
+
min_joining_fee=min_joining_fee,
|
| 169 |
+
max_joining_fee=max_joining_fee,
|
| 170 |
+
min_annual_fee=min_annual_fee,
|
| 171 |
+
max_annual_fee=max_annual_fee
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
debug_print("UI", f"Pipeline returned {len(card_rows_out)} card rows")
|
| 175 |
+
|
| 176 |
+
recommendation_visible = bool(top_card_out) or bool(top_card_description_out)
|
| 177 |
+
df_visible = bool(card_rows_out)
|
| 178 |
+
chat_container_visible = gr.update(visible=True if card_rows_out else False)
|
| 179 |
+
|
| 180 |
+
top_card_md = format_to_markdown(top_card_out, top_card_description_out)
|
| 181 |
+
card_table_md = format_rows_to_markdown_table(card_rows_out)
|
| 182 |
+
debug_print("UI", f"recommend function completed successfully")
|
| 183 |
+
|
| 184 |
+
initial_context = {
|
| 185 |
+
"query": query,
|
| 186 |
+
"top_cards": card_names_out[:5],
|
| 187 |
+
"recommendation_summary": top_card_md
|
| 188 |
+
}
|
| 189 |
+
card_links_section = "<div style='padding: 10px; border: 1px solid #444; border-radius: 8px;'>"
|
| 190 |
+
|
| 191 |
+
for name, link in zip(card_names_out, card_links):
|
| 192 |
+
card_links_section += f"<div style='margin-bottom: 8px;'><a href='{link}' target='_blank'>{name}</a></div>"
|
| 193 |
+
|
| 194 |
+
card_links_section += "</div>"
|
| 195 |
+
|
| 196 |
+
return top_card_md, gr.update(visible=recommendation_visible), card_table_md, gr.update(visible=df_visible), gr.update(visible=df_visible), card_names_out, card_lookup_out, query, chat_container_visible, initial_context,gr.update(value=card_links_section, visible=True), gr.update(value="### 🔗 Issuer Links", visible=True)
|
| 197 |
+
|
| 198 |
+
except Exception as e:
|
| 199 |
+
error_message = f"Error during recommendation: {str(e)}"
|
| 200 |
+
debug_print("ERROR", f"recommend function error: {str(e)}")
|
| 201 |
+
return error_message, gr.update(visible=False), gr.update(value=None), gr.update(visible=False), gr.update(visible=False), [], {}, "", gr.update(visible=False),{}, gr.update(visible=False),gr.update(visible=False)
|
| 202 |
+
|
| 203 |
+
#for comparison
|
| 204 |
+
async def compare_cards_via_graph(selected_cards, card_lookup):
|
| 205 |
+
state = {
|
| 206 |
+
"trigger_compare": True,
|
| 207 |
+
"trigger_chat": False,
|
| 208 |
+
"selected_cards": selected_cards,
|
| 209 |
+
"card_lookup": card_lookup
|
| 210 |
+
}
|
| 211 |
+
result = await utility_app.ainvoke(state)
|
| 212 |
+
return result.get("comparison_result", "")
|
| 213 |
+
|
| 214 |
+
async def compare_cards_wrapper(selected_cards):
|
| 215 |
+
return await compare_cards_via_graph(selected_cards, all_card_lookup)
|
| 216 |
+
|
| 217 |
+
#for chat
|
| 218 |
+
async def chat_with_agent(user_message: str, chat_history: List, messages: List, initial_context: Dict[str, Any]):
|
| 219 |
+
debug_print("UI", f"Entering chat_with_agent with user_message: '{user_message}'")
|
| 220 |
+
|
| 221 |
+
# Preparing the initial context message if this is the first question
|
| 222 |
+
if not messages:
|
| 223 |
+
|
| 224 |
+
user_query = initial_context.get("query", "No query provided")
|
| 225 |
+
top_cards = initial_context.get("top_cards", [])
|
| 226 |
+
context = "\n".join(top_cards) if top_cards else "No top cards available"
|
| 227 |
+
|
| 228 |
+
recommended_summary = initial_context.get("recommendation_summary", "No recommendations available")
|
| 229 |
+
recommended_card_info = ""
|
| 230 |
+
other_card_info = ""
|
| 231 |
+
|
| 232 |
+
for card_name in top_cards:
|
| 233 |
+
description = all_card_lookup.get(card_name, "Description not found.")
|
| 234 |
+
eligibility = eligibility_lookup.get(card_name, "No eligibility or fee information available.")
|
| 235 |
+
|
| 236 |
+
card_block = f"""Card: {card_name}
|
| 237 |
+
Description: {description}
|
| 238 |
+
|
| 239 |
+
Eligibility & Fees:
|
| 240 |
+
{eligibility}
|
| 241 |
+
|
| 242 |
+
---
|
| 243 |
+
"""
|
| 244 |
+
if card_name in recommended_summary:
|
| 245 |
+
recommended_card_info += card_block
|
| 246 |
+
else:
|
| 247 |
+
other_card_info += card_block
|
| 248 |
+
|
| 249 |
+
# System prompt with context for chat node
|
| 250 |
+
system_message = f"""
|
| 251 |
+
**Your Role: Secure Credit Card Expert**
|
| 252 |
+
You are a helpful and secure AI assistant. Your goal is to provide a comprehensive answer to the user's question using all available information.
|
| 253 |
+
|
| 254 |
+
Your knowledge base includes the following and <Newly_Fetched_Information> section if it is provided:
|
| 255 |
+
<Initial_Context>
|
| 256 |
+
<User_Requirements>{user_query}</User_Requirements>
|
| 257 |
+
<Top_Ranked_Cards_For_User_Query>{context}</Top_Ranked_Cards_For_User_Query>
|
| 258 |
+
<Recommended_Card_Info>
|
| 259 |
+
<Best_Recommended_Card>{recommended_card_info}</Best_Recommended_Card>
|
| 260 |
+
<Other_Recommended_Cards>{other_card_info}</Other_Recommended_Cards>
|
| 261 |
+
</Recommended_Card_Info>
|
| 262 |
+
</Initial_Context>
|
| 263 |
+
|
| 264 |
+
**CRITICAL RULES:**
|
| 265 |
+
1. **Synthesize All Information:** Base your answer on the `<Initial_Context>` and any `<Newly_Fetched_Information>` provided. If the user asks for a comparison, use details from both.
|
| 266 |
+
2. **Make a Recommendation:** If asked "which is best?" or "which is suitable?", analyze all available card info against the `<User_Requirements>` and provide a direct, reasoned recommendation.
|
| 267 |
+
3. **Be Direct:** Do not mention your internal processes. Just provide the final answer to the user.
|
| 268 |
+
|
| 269 |
+
**RESPONSE STYLE AND TONE:**
|
| 270 |
+
- **Be Direct and Concise:** Get straight to the point. Do not explain your internal thought process.
|
| 271 |
+
- **For Factual Questions (like "what are the fees?"):** Provide a direct, simple sentence as the answer.
|
| 272 |
+
- **For Recommendation Questions (like "which is best for me?"):** Start your response with the name of the recommended card, followed by a brief, bulleted list of the key features that make it the best fit for the user's requirements.
|
| 273 |
+
"""
|
| 274 |
+
|
| 275 |
+
# print(f"system message: {system_message}")
|
| 276 |
+
messages = [SystemMessage(content=system_message)]
|
| 277 |
+
|
| 278 |
+
else:
|
| 279 |
+
debug_print("UI", "Subsequent turn: using existing message history.")
|
| 280 |
+
top_cards = initial_context.get("top_cards", [])
|
| 281 |
+
user_query = initial_context.get("query", "No query provided")
|
| 282 |
+
|
| 283 |
+
current_turn_messages = messages + [HumanMessage(content=user_message)]
|
| 284 |
+
|
| 285 |
+
# Defining the state for the utility app
|
| 286 |
+
state = {
|
| 287 |
+
"messages": current_turn_messages,
|
| 288 |
+
"trigger_chat": True,
|
| 289 |
+
"trigger_compare": False,
|
| 290 |
+
"card_names": top_cards,
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
# invoking the graph
|
| 294 |
+
response_state = await utility_app.ainvoke(state)
|
| 295 |
+
|
| 296 |
+
updated_messages_from_agent = response_state.get("messages", [])
|
| 297 |
+
debug_print("UI", f"Chat agent response state: {get_pretty_state_string(updated_messages_from_agent)}")
|
| 298 |
+
final_response = updated_messages_from_agent[-1].content
|
| 299 |
+
chat_history.append({"role": "user", "content": user_message})
|
| 300 |
+
chat_history.append({"role": "assistant", "content": final_response})
|
| 301 |
+
return chat_history, updated_messages_from_agent, ""
|
| 302 |
+
|
| 303 |
+
card_names_state = gr.State([])
|
| 304 |
+
card_lookup_state = gr.State()
|
| 305 |
+
chat_history = gr.State([])
|
| 306 |
+
query = gr.State("")
|
| 307 |
+
initial_chat_context = gr.State({})
|
| 308 |
+
messages= gr.State([])
|
| 309 |
+
|
| 310 |
+
run_button.click(
|
| 311 |
+
fn=recommend,
|
| 312 |
+
inputs=[
|
| 313 |
+
query_input, preferences, fd_checkbox, cobrand_checkbox,
|
| 314 |
+
income, cibil, age,
|
| 315 |
+
min_joining_fee, max_joining_fee,
|
| 316 |
+
min_annual_fee, max_annual_fee,
|
| 317 |
+
use_eligibility
|
| 318 |
+
],
|
| 319 |
+
outputs=[
|
| 320 |
+
top_card_recommendation, top_card_recommendation,
|
| 321 |
+
card_table_markdown, card_table_markdown, recommendation_heading,
|
| 322 |
+
card_names_state,
|
| 323 |
+
card_lookup_state,
|
| 324 |
+
query, chat_container, initial_chat_context,card_links_html, card_links_heading
|
| 325 |
+
],
|
| 326 |
+
concurrency_limit=20
|
| 327 |
+
).then(
|
| 328 |
+
fn=lambda card_names: (gr.update(choices=card_names if card_names else [], value=[]), gr.update(visible=True if card_names else False)),
|
| 329 |
+
inputs=card_names_state,
|
| 330 |
+
outputs=[compare_checkboxes, compare_recommended_cards_container],
|
| 331 |
+
concurrency_limit=20
|
| 332 |
+
).then(
|
| 333 |
+
fn=lambda: ([], [], [], gr.update(value=""), gr.update(value="")),
|
| 334 |
+
outputs=[chat_history, chatbox, messages, compare_output, full_compare_output], # Clear both compare outputs
|
| 335 |
+
concurrency_limit=20
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
compare_btn.click(
|
| 339 |
+
fn=compare_cards_via_graph,
|
| 340 |
+
inputs=[compare_checkboxes, card_lookup_state],
|
| 341 |
+
outputs=compare_output,
|
| 342 |
+
show_progress=True,
|
| 343 |
+
concurrency_limit=20
|
| 344 |
+
)
|
| 345 |
+
|
| 346 |
+
full_compare_btn.click(
|
| 347 |
+
fn=compare_cards_wrapper,
|
| 348 |
+
inputs=[full_compare_dropdown],
|
| 349 |
+
outputs=full_compare_output,
|
| 350 |
+
show_progress=True,
|
| 351 |
+
concurrency_limit=20
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
followup_submit.click(
|
| 356 |
+
fn=chat_with_agent,
|
| 357 |
+
inputs=[followup_input, chatbox, messages, initial_chat_context],
|
| 358 |
+
outputs=[chatbox, messages, followup_input],
|
| 359 |
+
concurrency_limit=20
|
| 360 |
+
)
|
| 361 |
+
debug_print("APP", f"Credit Card Recommender Agent initialized with {len(df)} cards")
|
| 362 |
+
debug_print("APP", f"Launching Gradio UI at {time.strftime('%Y-%m-%d %H:%M:%S')}")
|