Sulaiman8 commited on
Commit
6390a72
·
verified ·
1 Parent(s): a424c82

Upload 10 files

Browse files
agents/chat.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import google.generativeai as genai
3
+ import os
4
+ from data import eligibility_df
5
+
6
+ #function for the chatbot functionality
7
+ eligibility_lookup = {}
8
+ for _, row in eligibility_df.iterrows():
9
+ card_name = row["Name"].strip()
10
+ eligibility_info = f"""
11
+ - Bank: {row['Bank']}
12
+ - Age: {row['Minimum Age']} to {row['Maximum Age']}
13
+ - Minimum Income: {row['Minimum Income (LPA)']} LPA
14
+ - Minimum Credit Score: {row['Minimum Credit Score']}
15
+ - Joining Fee: ₹{row['Joining fee']}
16
+ - Annual Fee: ₹{row['Annual fee']}
17
+ """
18
+ eligibility_lookup[card_name] = eligibility_info.strip()
19
+
20
+ # Function to handle chat interaction with Gemini
21
+ def chat_with_gemini(user_query, user_message, chat_history, card_lookup):
22
+ genai.configure(api_key=os.environ.get("api_key_4"))
23
+ model4 = genai.GenerativeModel('gemini-1.5-flash-latest')
24
+ context = ""
25
+ for name, desc in list(card_lookup.items())[:5]:
26
+ eligibility_info = eligibility_lookup.get(name, "No eligibility or fee information available.")
27
+ full_desc = f"{desc}\n\nEligibility & Fees:\n{eligibility_info}"
28
+ context += f"{name}:\n{full_desc}\n\n"
29
+ recent_user_messages = [
30
+ msg["content"] for msg in chat_history if msg["role"] == "user"
31
+ ][-5:]
32
+
33
+ conversation = f"""
34
+ You are a helpful financial assistant. A user has already shared their overall credit card preferences.
35
+
36
+ ### User’s Requirements:
37
+ {user_query}
38
+
39
+ ### Credit Card Options:
40
+ {context}
41
+
42
+ ### User's Follow-up Question:
43
+ {user_message}
44
+
45
+ ### Recent User Messages:
46
+ {recent_user_messages}
47
+
48
+ ### Instructions:
49
+ 1. Answer the user's current question clearly and concisely.
50
+ 2. Always consider the user's overall requirements above.
51
+ 3. Use only the card descriptions provided. Do not assume or invent any card benefits.
52
+ 4. If the user asks which card is best or suitable for their needs, use the user’s requirements above to select and explain.
53
+ 5. Don't ask the user to restate their requirements — they're already provided above.
54
+ """
55
+ # print(conversation)
56
+ try:
57
+ response = model4.generate_content(conversation)
58
+ gemini_response = response.text
59
+
60
+ chat_history.append({"role": "user", "content": user_message})
61
+ chat_history.append({"role": "assistant", "content": gemini_response})
62
+
63
+ except Exception as e:
64
+ error_msg = "Error: Unable to retrieve response from Gemini. Please try again later."
65
+ chat_history.append({"role": "user", "content": user_message})
66
+ chat_history.append({"role": "assistant", "content": error_msg})
67
+
68
+ return chat_history, chat_history
agents/compare.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import google.generativeai as genai
3
+ import os
4
+
5
+ #function to compare cards
6
+ def compare_selected_cards(selected_names, card_lookup):
7
+ genai.configure(api_key=os.environ.get("api_key_4"))
8
+ model4 = genai.GenerativeModel('gemini-1.5-flash-latest')
9
+ print(selected_names)
10
+ if not selected_names or len(selected_names) < 2:
11
+ return "<b style='color:red;'>Please select at least two cards to compare.</b>"
12
+
13
+ comparison_data = "\n\n".join([
14
+ f"{name}: {card_lookup.get(name)}" for name in selected_names
15
+ ])
16
+ prompt = (
17
+ f"Do not include extra symbols like (* or #), just generate a textual response maybe with numbers for points."
18
+ f"Compare the following credit cards based on their benefits. "
19
+ f"Keep the output short, structured, and very readable:\n\n"
20
+ f"{comparison_data}\n\n"
21
+ f"Use markdown format with clear section headers like 'Comparison' and 'Recommendation'. "
22
+ f"Present key differences as bullet points without using '*' symbols — use '-' instead. "
23
+ f"Make it crisp and avoid lengthy explanations. "
24
+ f"Conclude with a recommendation on which card suits which type of user."
25
+ f"DO NOT INCLUDE ANY SYMBOLS LIKE # OR *"
26
+ )
27
+ print("comparing")
28
+ try:
29
+ response = model4.generate_content(prompt)
30
+ return f"""
31
+ <div style='background-color: #f9fbe7; padding: 15px; border-radius: 10px; font-family: sans-serif;'>
32
+ <pre style='white-space: pre-wrap; font-size: 13px; color: #333;'>{response.text}</pre>
33
+ </div>
34
+ """
35
+ except Exception as e:
36
+ print("Comparison Error:", e)
37
+ return "<b style='color:red;'>Something went wrong while comparing. Please try again.</b>"
app.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from ui.gradio_interface import demo
2
+
3
+ demo.launch(share=True)
data.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import pandas as pd
3
+
4
+ #for adding bank name to the cards in the graph
5
+ eligibility_df = pd.read_csv("cards_eligibility_updated.csv")
6
+ card_to_bank = dict(zip(eligibility_df['Name'], eligibility_df['Bank']))
7
+
8
+ # Loading credit card data
9
+ df = pd.read_csv("credit_card_data_updated.csv")
10
+ card_descriptions = dict(zip(df["name"], df["description"]))
11
+
12
+ # Loading all 55 cards for comparison feature
13
+ df_all_cards = pd.read_csv("credit_card_data_updated.csv")
14
+ all_card_names = df_all_cards["name"].tolist()
15
+ all_card_lookup = dict(zip(df_all_cards["name"], df_all_cards["description"]))
16
+
17
+ with open('for_graph_construction_(expanded labels).json') as f:
18
+ card_feature_data = json.load(f)
19
+
20
+ card_features_lookup = {
21
+ card['card_name']: set(card['features'])
22
+ for card in card_feature_data
23
+ }
24
+
25
+
26
+ #function for the chatbot functionality
27
+ eligibility_lookup = {}
28
+ for _, row in eligibility_df.iterrows():
29
+ card_name = row["Name"].strip()
30
+ eligibility_info = f"""
31
+ - Bank: {row['Bank']}
32
+ - Age: {row['Minimum Age']} to {row['Maximum Age']}
33
+ - Minimum Income: {row['Minimum Income (LPA)']} LPA
34
+ - Minimum Credit Score: {row['Minimum Credit Score']}
35
+ - Joining Fee: ₹{row['Joining fee']}
36
+ - Annual Fee: ₹{row['Annual fee']}
37
+ """
38
+ eligibility_lookup[card_name] = eligibility_info.strip()
39
+
intent_classification/fd_classification.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import os
3
+
4
+ #for intent classification
5
+ def find_intent(user_query: str) -> bool:
6
+ genai.configure(api_key=os.environ.get("api_key_2"))
7
+ model2 = genai.GenerativeModel('gemini-1.5-flash-latest')
8
+ prompt = f"""
9
+ You are a helpful assistant. A user has asked the following question or made the following request:
10
+
11
+ "{user_query}"
12
+
13
+ Determine ONLY whether this query is likely about FD-based (fixed deposit backed) credit cards.
14
+ These cards typically do not require a credit score, are suited for users with low income, users who are new to credit cards/beginners, who have no/low credit score or students.
15
+
16
+ Respond with just "true" or "false" depending on whether the user's query is about such cards.
17
+ No explanation, no extra words — just true or false.
18
+ """
19
+ response = model2.generate_content(prompt)
20
+ result = response.text.strip().lower()
21
+ return result == "true"
intent_classification/retrieval_classification.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ import pandas as pd
3
+ import os
4
+ import json
5
+ from data import df_all_cards
6
+
7
+ #handling intent classification for retrieval
8
+ def handle_query_classification(user_query):
9
+ genai.configure(api_key=os.environ.get("api_key_1"))
10
+ model1 = genai.GenerativeModel('gemini-1.5-flash-latest')
11
+ prompt = f"""
12
+ You are a smart financial assistant.
13
+
14
+ ### User's Query:
15
+ {user_query}
16
+
17
+ ### Task:
18
+ Classify the user's intent into one of the following categories:
19
+ 1. "retrieve" → If the user is asking for card suggestions, recommendations, or showing cards (e.g., "suggest a card", "need a travel card") OR if they mention their lifestyle, income, spending, or needs (e.g., travel, shopping, fuel, rewards, luxury).
20
+ 2. "specific" → If the user is asking about a particular credit card by name (even if the word "card" is not used). Examples: "Tell me about HDFC Regalia", "Is SBI Elite good?".
21
+ 3. "no_retrieval" → ONLY if the query is generic (e.g., “What is credit score?”), casual chit-chat (e.g., “Hi”), or doesn’t mention any lifestyle, financial needs, or specific card names.
22
+
23
+
24
+ Respond ONLY in the following JSON format:
25
+ If intent is "no_retrieval", you MUST include a helpful 'response' field.
26
+ If intent is "retrieve" or "specific", do NOT include any response or explanation.
27
+
28
+ Respond in this exact format:
29
+ {{
30
+ "intent": "retrieve" | "specific" | "no_retrieval",
31
+ "response": "Only include this if intent is 'no_retrieval'"
32
+ }}
33
+
34
+ """
35
+
36
+ raw_response = model1.generate_content(prompt).text.strip()
37
+
38
+ # Clean any markdown formatting if present
39
+ if raw_response.startswith("```"):
40
+ raw_response = raw_response.strip("`").strip()
41
+ if raw_response.startswith("json"):
42
+ raw_response = raw_response[len("json"):].strip()
43
+
44
+ try:
45
+ parsed = json.loads(raw_response)
46
+ return parsed
47
+ except Exception as e:
48
+ print("JSON parsing error:", e)
49
+ print("Raw response from LLM:", raw_response)
50
+ raise
51
+ # result = handle_query_classification("Want to optimize my spending – travel often, premium hotels, and online shopping.")
52
+ # if result["intent"] == "no_retrieval":
53
+ # print(result['response'])
54
+
55
+ #passing the card mentioned in the user query
56
+ def find_matching_card(user_query):
57
+ lowered_query = user_query.lower()
58
+ for _, row in df_all_cards.iterrows():
59
+ if row["name"].lower() in lowered_query:
60
+ return row.to_dict()
61
+ return None
62
+
63
+
64
+ #for queries enquiring about a card
65
+ def generate_card_response_with_context(user_query, card_info):
66
+ genai.configure(api_key=os.environ.get("api_key_1"))
67
+ model1 = genai.GenerativeModel('gemini-1.5-flash-latest')
68
+ prompt = f"""
69
+ You are a helpful financial assistant. A user has asked about a specific credit card.
70
+
71
+ Card Name: {card_info.get('name')}
72
+ Description: {card_info.get('description')}
73
+
74
+ User's Question: {user_query}
75
+
76
+ Please provide a concise, relevant answer using the above card context.
77
+ """
78
+ response = model1.generate_content(prompt)
79
+ return response.text.strip()
80
+
recommender/graph_retrieval_vectordb.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import google.generativeai as genai
2
+ from neo4j import GraphDatabase
3
+ import os
4
+ import numpy as np
5
+ import faiss
6
+ from data import card_descriptions,eligibility_df
7
+
8
+ #neo4j credentials
9
+ NEO4J_URI = os.environ.get("NEO4J_URI")
10
+ NEO4J_USER = os.environ.get("NEO4J_USER")
11
+ NEO4J_PASS = os.environ.get("NEO4J_PASS")
12
+ driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASS))
13
+
14
+ #generating cypher query
15
+ def generate_cypher(user_query, query_intent, include_cobranded):
16
+ genai.configure(api_key='AIzaSyAHoi9xbYAThtjXlyF_IKFtruoWYoUCjJQ')
17
+ model3 = genai.GenerativeModel('gemini-1.5-flash-latest')
18
+ print("inside cypher query gen")
19
+
20
+ context_note = f"""
21
+ Contextual Flags:
22
+ - FD Card intent: {query_intent}
23
+ - Include co-branded cards: {include_cobranded}
24
+ """
25
+
26
+ cypher_prompt = f"""
27
+ You are an expert Neo4j Cypher query generator.
28
+
29
+ Given a user’s question, graph schema, and **contextual flags**, generate the correct Cypher query. The query should return only the cards `c`.
30
+
31
+ ONLY output the Cypher query. Do NOT explain anything.
32
+
33
+ ---
34
+
35
+ Graph Schema:
36
+ - Nodes:
37
+ - (Card): Properties = name, bank_name, card_type, premium, co_branded
38
+ - (Feature): Properties = name
39
+ - Relationships:
40
+ - (Card)-[:HAS_FEATURE]->(Feature)
41
+
42
+ Feature Inclusion Rules:
43
+ - Only include relevant features based on user query.
44
+ - Forex markup fee and foreign transaction fee are the same.
45
+ - If FD Card intent is true then include the features if the query contains any and also include “General Cashback” or “General Reward Points”
46
+ - Don’t add “General Cashback” or “General Reward Points” if it is not required.
47
+ - If fuel is mentioned, include both `Fuel Benefits` and `Fuel Surcharge Waiver`.
48
+ - **ALWAYS** match features using: `f.name IN [...]` — even if there is only **one** feature.
49
+
50
+
51
+ Valid values:
52
+ - card_type: 'FD Card' or 'Regular'
53
+ - premium: true (no concept of false — just include it if applicable)
54
+ - co_branded: true (no concept of false — just include it if applicable)
55
+
56
+ MANDATORY Condition Rules:
57
+ - If FD Card intent is true → include: `c.card_type = 'FD Card'`
58
+ - Else → include: `c.card_type = 'Regular'`
59
+ - If the query is based on beginners or students or people with no or low credit history then use FD Card.
60
+ - If the query uses words like "premium", "elite", "luxury", "exclusive", "infinia", "black", etc. → include: `AND c.premium = true`
61
+ - If the query includes low spending, without high spending or budget → include: `(c.premium IS NULL OR c.premium = false)`
62
+ - If include co-branded is false → include: `AND (c.co_branded IS NULL OR c.co_branded = false)`
63
+ - 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"]
64
+ - Do not add bank after the name of the bank if it is not mentioned in the datase list.
65
+ - These conditions are **MANDATORY**. If they apply, include them in the `WHERE` clause. Do not skip them.
66
+
67
+ ---
68
+
69
+ Available features:
70
+ "General Cashback", "Fuel Surcharge Waiver", "Fuel Benefits", "Welcome Bonus",
71
+ "Airport Lounge Access", "General Reward Points", "Domestic Travel Benefits",
72
+ "Movie Benefits", "Flight Discounts", "International Travel Benefits",
73
+ "Hotel Benefits", "Dining Benefits", "Daily Spends (Grocery)", "Railway Benefits",
74
+ "Travel Benefits", "Railway Lounge", "Insurance", "Utility",
75
+ "E-commerce Platform Benefits", "Air Miles", "Spa Access Benefits",
76
+ "Lifestyle & Luxury Perks", "Golf Access & Perks", "Online Shopping Benefits",
77
+ "UPI Transaction Support", "Health Benefits", "EMI Conversion Options",
78
+ "No Forex Markup Fee", "Roadside Assistance", "Rupay Network Support",Super Premium Cards
79
+
80
+ ---
81
+
82
+ Few-shot Examples:
83
+
84
+ User Query: Show premium cards with airport lounge access
85
+ Cypher:
86
+ MATCH (c:Card)-[:HAS_FEATURE]->(f:Feature)
87
+ WHERE f.name IN ["Airport Lounge Access"]
88
+ AND c.card_type = 'Regular'
89
+ AND c.premium = true
90
+ RETURN c
91
+
92
+ User Query: I want FD cards with spa access and golf perks
93
+ Cypher:
94
+ MATCH (c:Card)-[:HAS_FEATURE]->(f:Feature)
95
+ WHERE f.name IN ["Spa Access Benefits", "Golf Access & Perks"]
96
+ AND c.card_type = 'FD Card'
97
+ RETURN c
98
+
99
+ User Query: Cards that support UPI but are not co-branded
100
+ Cypher:
101
+ MATCH (c:Card)-[:HAS_FEATURE]->(f:Feature)
102
+ WHERE f.name IN ["UPI Transaction Support"]
103
+ AND c.card_type = 'Regular'
104
+ AND (c.co_branded IS NULL OR c.co_branded = false)
105
+ RETURN c
106
+
107
+ ---
108
+
109
+ {context_note}
110
+
111
+ User Query: {user_query}
112
+ Cypher:
113
+ """
114
+
115
+ response = model3.generate_content(cypher_prompt.strip())
116
+ cypher_code = response.text.strip()
117
+
118
+ if cypher_code.startswith("```cypher"):
119
+ cypher_code = cypher_code[len("```cypher"):].strip()
120
+ elif cypher_code.startswith("```"):
121
+ cypher_code = cypher_code[len("```"):].strip()
122
+ if cypher_code.endswith("```"):
123
+ cypher_code = cypher_code[:-3].strip()
124
+
125
+ return cypher_code
126
+
127
+ #generating embeddings (run only once)
128
+ def chunk_text(text, chunk_size=1):
129
+ sentences = text.split("; ")
130
+ return ["; ".join(sentences[i:i+chunk_size]) for i in range(0, len(sentences), chunk_size)]
131
+
132
+ def get_gemini_embeddings(text_list):
133
+ embeddings = []
134
+ print("Generating embeddings with Gemini...")
135
+ for text in text_list:
136
+ response = genai.embed_content(
137
+ model=model_name,
138
+ content=text,
139
+ task_type="RETRIEVAL_DOCUMENT")
140
+ embeddings.append(np.array(response["embedding"], dtype=np.float32))
141
+ return np.vstack(embeddings)
142
+
143
+ # Chunk all card descriptions
144
+ chunk_texts = []
145
+ chunk_name_mapping = {}
146
+ for card_idx, (card_name, desc) in enumerate(card_descriptions.items()):
147
+ chunks = chunk_text(desc)
148
+ for chunk in chunks:
149
+ chunk_index = len(chunk_texts)
150
+ chunk_texts.append(chunk)
151
+ chunk_name_mapping[chunk_index] = card_name
152
+
153
+ genai.configure(api_key=os.environ.get("api_key_2"))
154
+ model_name = "models/text-embedding-004"
155
+
156
+ #Generating embeddings
157
+ chunk_embeddings = get_gemini_embeddings(chunk_texts)
158
+ faiss.normalize_L2(chunk_embeddings)
159
+
160
+ print(f"Prepared {len(chunk_texts)} total chunks and embeddings.")
161
+
162
+ #eligibility filter
163
+ def eligibility_filter(cards, user_income, user_cibil, user_age,min_joining_fee, max_joining_fee,
164
+ min_annual_fee, max_annual_fee):
165
+ eligible_cards = []
166
+ print("inside filter")
167
+ for card_name in cards:
168
+ # print(eligibility_df.columns)
169
+
170
+ eligibility = eligibility_df[eligibility_df["Name"] == card_name]
171
+
172
+ if not eligibility.empty:
173
+ min_income = eligibility.iloc[0]["Minimum Income (LPA)"]
174
+ min_cibil = eligibility.iloc[0]["Minimum Credit Score"]
175
+ min_age = eligibility.iloc[0]["Minimum Age"]
176
+ max_age = eligibility.iloc[0]["Maximum Age"]
177
+ joining_fee=eligibility.iloc[0]["Joining fee"]
178
+ annual_fee=eligibility.iloc[0]["Annual fee"]
179
+ if (user_income >= min_income and
180
+ user_cibil >= min_cibil and
181
+ min_age <= user_age <= max_age and
182
+ min_joining_fee<=joining_fee<=max_joining_fee and
183
+ min_annual_fee<=annual_fee<=max_annual_fee):
184
+ eligible_cards.append(card_name)
185
+
186
+ return eligible_cards
187
+
188
+
189
+
190
+ #function for retrieving cards from knowledge graph
191
+ class Neo4jConnectionError(Exception):
192
+ pass
193
+
194
+ def run_cypher_query(user_query, query, use_eligibility, user_income, user_cibil, user_age,
195
+ min_joining_fee, max_joining_fee, min_annual_fee, max_annual_fee):
196
+
197
+ try:
198
+ with driver.session() as session:
199
+
200
+ result = session.run(query)
201
+ matched_cards = [record["c"] for record in result]
202
+ filtered_cards = [card["name"] for card in matched_cards]
203
+
204
+ except Exception as e:
205
+ raise Neo4jConnectionError("Failed to connect to the Neo4j database.") from e
206
+
207
+ if use_eligibility:
208
+ filtered_cards = eligibility_filter(filtered_cards, user_income, user_cibil, user_age,
209
+ min_joining_fee, max_joining_fee,
210
+ min_annual_fee, max_annual_fee)
211
+
212
+ # for card in filtered_cards:
213
+ # print("error")
214
+ # print(card["name"])
215
+ relevant_indexes = [i for i, name in chunk_name_mapping.items() if name in filtered_cards]
216
+ filtered_embeddings = chunk_embeddings[relevant_indexes]
217
+ filtered_texts = [chunk_texts[i] for i in relevant_indexes]
218
+ filtered_mapping = {i: chunk_name_mapping[idx] for i, idx in enumerate(relevant_indexes)}
219
+
220
+ # Build FAISS index
221
+ dim = filtered_embeddings.shape[1]
222
+ faiss_index = faiss.IndexFlatIP(dim)
223
+ faiss_index.add(filtered_embeddings)
224
+
225
+ print(f"FAISS index created with {len(filtered_embeddings)} filtered chunks.")
226
+ return faiss_index, filtered_mapping
recommender/recommender.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import re
3
+ import pd
4
+ import tempfile
5
+ import os
6
+ from intent_classification.fd_classification import find_intent
7
+ from intent_classification.retrieval_classification import handle_query_classification,find_matching_card,generate_card_response_with_context
8
+ from recommender.retrieval_ranking import generate_multi_queries,convert_to_direct_query_gradio,retrieve_and_rank_cards,generate_credit_card_recommendation_gemini,cross_encoder
9
+ from data import eligibility_lookup,card_features_lookup
10
+ from recommender.graph_retrieval_vectordb import generate_cypher,run_cypher_query,Neo4jConnectionError
11
+
12
+ #function to pass the retrieved cards and generated response to the UI
13
+ def recommend_cards_gradio(user_query, preferences, income, cibil, age,
14
+ min_joining_fee, max_joining_fee,
15
+ min_annual_fee, max_annual_fee,
16
+ use_eligibility=True,include_cobranded=True):
17
+ try:
18
+ # print(user_query)
19
+ if(user_query):
20
+ result = handle_query_classification(user_query)
21
+
22
+ if result["intent"] == "no_retrieval":
23
+ return (
24
+ f"<div style='background-color:#e3f2fd;padding:20px;border-radius:10px;'>"
25
+ f"<pre style='white-space:pre-wrap;font-size:13px;color:#212121;'>{result['response']}</pre></div>",
26
+ [["No retrieval required", "Answered using LLM"]],
27
+ None,
28
+ [],
29
+ {},
30
+ "Answered without retrieval"
31
+ )
32
+ elif result["intent"] == "specific":
33
+ matched_card = find_matching_card(user_query)
34
+ if matched_card:
35
+ gemini_answer = generate_card_response_with_context(user_query, matched_card)
36
+ card_name = matched_card["name"]
37
+ card_desc = matched_card["description"]
38
+ card_lookup = {card_name: card_desc}
39
+
40
+ # Constructing eligibility info if available
41
+ eligibility_info = eligibility_lookup.get(card_name, "No eligibility or fee information available.")
42
+ chat_history_entry = f"{card_name}:\n{card_desc}\n\nEligibility & Fees:\n{eligibility_info}"
43
+
44
+ return (
45
+ f"<div style='background-color:#fffde7;padding:20px;border-radius:10px;'>"
46
+ f"<pre style='white-space:pre-wrap;font-size:13px;color:#212121;'>{gemini_answer}</pre></div>",
47
+ [["Specific card detected", card_name]],
48
+ None,
49
+ [],
50
+ card_lookup,
51
+ user_query
52
+ )
53
+ else:
54
+ return (
55
+ "<b style='color:red;'>Card mentioned not found in database.</b>",
56
+ [["Card not found", "Try another card name."]],
57
+ None,
58
+ [],
59
+ {},
60
+ "Card not found"
61
+ )
62
+ direct_query = convert_to_direct_query_gradio(user_query, preferences)
63
+ queries = generate_multi_queries(direct_query)
64
+
65
+ if cibil < 700 and use_eligibility:
66
+ query_intent = True
67
+ else:
68
+ query_intent = find_intent(user_query)
69
+ print(query_intent)
70
+ cypher_query = generate_cypher(direct_query, query_intent,include_cobranded)
71
+ print("Generated Cypher:\n", cypher_query)
72
+
73
+ try:
74
+ faiss_index, filtered_mapping = run_cypher_query(
75
+ user_query, cypher_query, use_eligibility,
76
+ income, cibil, age,
77
+ min_joining_fee, max_joining_fee,
78
+ min_annual_fee, max_annual_fee
79
+ )
80
+ except Neo4jConnectionError as graph_err:
81
+ return (
82
+ "<b style='color:red;'>Graph database connection failed. Please try again later.</b>",
83
+ [["Graph database error", str(graph_err)]],
84
+ None,
85
+ [],
86
+ {},
87
+ "Graph DB connection error"
88
+ )
89
+
90
+ cards = retrieve_and_rank_cards(faiss_index, filtered_mapping, direct_query, queries, top_k=10)
91
+ gemini_summary = generate_credit_card_recommendation_gemini(user_query, direct_query, cards)
92
+
93
+ if not cards:
94
+ return (
95
+ "<b style='color:red;'>No eligible cards found.</b>",
96
+ [["No eligible cards found", "Please try a different query or check your input values."]],
97
+ None,
98
+ [],
99
+ {},
100
+ "No eligible card found"
101
+ )
102
+
103
+ match = re.search(r"f\.name IN (\[.*?\])", cypher_query)
104
+ query_features = set(ast.literal_eval(match.group(1))) if match else set()
105
+
106
+ card_rows = []
107
+ for score, card in sorted(
108
+ zip(cross_encoder.predict([[direct_query, card["description"]] for card in cards]), cards),
109
+ reverse=True,
110
+ key=lambda x: x[0]
111
+ ):
112
+ card_name = card["name"]
113
+ card_desc = card["description"]
114
+ matched_features = query_features.intersection(card_features_lookup.get(card_name, set()))
115
+ feature_str = ", ".join(matched_features) if matched_features else "None"
116
+ card_rows.append([card_name, feature_str, card_desc])
117
+
118
+
119
+ card_names = [row[0] for row in card_rows]
120
+ card_lookup = {row[0]: row[2] for row in card_rows}
121
+ top_card_html = f"""
122
+ <div style="
123
+ background-color: #fff3e0;
124
+ color: #212121;
125
+ border-radius: 16px;
126
+ padding: 20px;
127
+ border: 2px solid #ffa726;
128
+ box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
129
+ margin-bottom: 16px;
130
+ font-family: sans-serif;
131
+ font-size: 8px;
132
+ ">
133
+ <pre style="white-space: pre-wrap; font-size: 13px; color: #212121;">{gemini_summary}</pre>
134
+ </div>
135
+ """
136
+
137
+ df_cards = pd.DataFrame(card_rows, columns=["Card Name", "Matched Features", "Description"])
138
+ filename = "recommended_cards.csv"
139
+ temp_dir = tempfile.gettempdir()
140
+ file_path = os.path.join(temp_dir, filename)
141
+ df_cards.to_csv(file_path, index=False)
142
+
143
+ return top_card_html, card_rows, file_path, card_names, card_lookup, direct_query
144
+
145
+ except Exception as e:
146
+ print("Error:", e)
147
+ return (
148
+ "An unexpected error occurred. Please try again in a few minutes.",
149
+ [["Something went wrong", "Please try again."]],
150
+ None,
151
+ [],
152
+ {},
153
+ "Unexpected error occurred, please try again in a while"
154
+ )
recommender/retrieval_ranking.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import faiss
3
+ from collections import defaultdict
4
+ import os
5
+ import google.generativeai as genai
6
+ from sentence_transformers import CrossEncoder
7
+
8
+ # Function to generate direct query using Gemini
9
+ def convert_to_direct_query_gradio(user_query,preferences):
10
+ genai.configure(api_key=os.environ.get("api_key_2"))
11
+ model2 = genai.GenerativeModel('gemini-1.5-flash-latest')
12
+
13
+ preferences_text = ""
14
+ if preferences:
15
+ preferences_text = "User selected preferences: " + ", ".join(preferences) + "."
16
+ # Prompt with examples for indirect-to-direct conversion
17
+ prompt = f"""
18
+ You are an AI assistant that refines user queries to make them optimized for information retrieval while keeping the original intent.
19
+
20
+ Instructions:
21
+ - Identify the main focus from the query.
22
+ - Reformat the query in a structured way for better retrieval.
23
+ - Ensure the most important feature appears first.
24
+ - Use precise keywords that match credit card benefits.
25
+ - Include the preferences also in the final query.
26
+ - Do NOT introduce new benefits not mentioned by the user.
27
+ - If the user uses vague terms like "vacation", interpret it as travel-related benefits including: airport lounge access, international/domestic travel, hotel benefits, forex waiver.
28
+ - If the query includes terms like "beginner", "entry-level", or "low credit score", include essential features such as cashback, reward points, and basic offers.
29
+ - If the query is empty, generate a useful retrieval-focused query based solely on preferences.
30
+
31
+ Examples:
32
+
33
+ Example 1
34
+ - User Query: "I drive a lot for work and want a credit card with good fuel rewards and travel perks."
35
+ - Optimized Query: "Category: Fuel Rewards | Best credit cards for high fuel spending with maximum rewards & fuel surcharge waiver. Travel perks preferred but secondary."
36
+
37
+ Example 2
38
+ - User Query: "I mostly shop online and want a card that gives high cashback on e-commerce purchases. Food delivery perks would be nice."
39
+ - Optimized Query: "Category: Online Shopping | Credit cards with best cashback on e-commerce platforms like Amazon, Flipkart. Food delivery benefits secondary."
40
+
41
+ Example 3
42
+ - User Query: "I eat out a lot and also order food from Swiggy/Zomato. I want the best dining discounts and food delivery cashback."
43
+ - Optimized Query: "Category: Dining & Food Delivery | Top credit cards offering the best dining discounts at restaurants and cashback on Swiggy/Zomato orders."
44
+
45
+ Now, optimize the following preferences and user query:
46
+
47
+ Preferences: "{preferences_text}"
48
+ User Query: "{user_query}"
49
+ """
50
+
51
+ print("rewriting")
52
+ response = model2.generate_content(prompt)
53
+
54
+ print(response.text)
55
+ return response.text
56
+
57
+ # Function to generate multiple focused subqueries from a user query
58
+ def generate_multi_queries(direct_query, n=3):
59
+ genai.configure(api_key=os.environ.get("api_key_2"))
60
+ model2 = genai.GenerativeModel('gemini-1.5-flash-latest')
61
+ prompt = f"""
62
+ The following is a detailed credit card search query:
63
+ "{direct_query}"
64
+ Generate {n} distinct subqueries that **collectively cover all the important features** from the original query.
65
+ Each subquery should emphasize a **different combination** of the features (e.g., lounge access, travel insurance, low foreign transaction fees, hotel discounts, etc.).
66
+ Keep the same format: "Category: ... | ...". Make sure all features from the original query are represented across the {n} queries.
67
+ Output only the subqueries, one per line. Do not include any explanations, numbering, or formatting — just plain queries separated by newline characters.
68
+ """
69
+ response = model2.generate_content(prompt)
70
+ # print(response.text)
71
+ queries = [q.strip() for q in response.text.strip().split('\n') if q.strip()]
72
+
73
+ return queries
74
+
75
+ #retrieval
76
+ #Cross-Encoder Model
77
+ cross_encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2")
78
+
79
+ def rerank_cards_mini(query, cards, top_n=5):
80
+ card_descriptions = [card["description"] for card in cards]
81
+ query_card_pairs = [[query, desc] for desc in card_descriptions]
82
+ scores = cross_encoder.predict(query_card_pairs)
83
+
84
+ #Normalizing
85
+ if len(scores) == 0 or (max(scores) - min(scores)) == 0:
86
+ scores = [1.0] * len(cards)
87
+ else:
88
+ scores = np.array(scores)
89
+ scores = (scores - scores.min()) / (scores.max() - scores.min())
90
+
91
+ # Sort cards by descending score
92
+ ranked_cards = sorted(zip(scores, cards), key=lambda x: x[0], reverse=True)
93
+ top_cards = [card for _, card in ranked_cards[:top_n]]
94
+
95
+ return top_cards
96
+
97
+ def retrieve_relevant_cards(user_query, index, chunk_name_mapping, df, top_k=15):
98
+ genai.configure(api_key=os.environ.get("api_key_2"))
99
+ model_name = "models/text-embedding-004"
100
+ print(f"\nUser Query: {user_query}")
101
+
102
+ # Generate and normalize query embedding
103
+ query_embedding = genai.embed_content(
104
+ model=model_name,
105
+ content=user_query,
106
+ task_type="RETRIEVAL_QUERY"
107
+ )["embedding"]
108
+ # query_embedding=model.encode(user_query)
109
+ query_embedding = np.array(query_embedding, dtype=np.float32)
110
+ faiss.normalize_L2(query_embedding.reshape(1, -1))
111
+
112
+ # Search FAISS index for top-k chunks
113
+ D, I = index.search(np.expand_dims(query_embedding, axis=0), top_k * 5)
114
+ similarity_scores = D[0]
115
+
116
+ # Map chunks back to unique card names with highest similarity per card
117
+ card_similarity = defaultdict(float)
118
+ card_dict = {card["name"]: card for card in df.to_dict(orient="records")}
119
+ unique_cards = {}
120
+
121
+ for i, chunk_idx in enumerate(I[0]):
122
+ if chunk_idx == -1:
123
+ continue
124
+ card_name = chunk_name_mapping[chunk_idx]
125
+ if card_name not in unique_cards or similarity_scores[i] > card_similarity[card_name]:
126
+ card_similarity[card_name] = similarity_scores[i]
127
+ unique_cards[card_name] = {
128
+ "name": card_name,
129
+ "description": card_dict[card_name]["description"],
130
+ "similarity": similarity_scores[i]
131
+ }
132
+
133
+ # Sort by similarity and take top_k
134
+ ordered_cards = sorted(unique_cards.values(), key=lambda x: x["similarity"], reverse=True)[:top_k]
135
+
136
+ print("\nTop Recommended Cards:")
137
+ for card in ordered_cards:
138
+ print(f"- {card['name']} (Similarity: {card['similarity']:.4f})")
139
+
140
+ return ordered_cards
141
+
142
+ def retrieve_and_rank_cards(faiss_index,filtered_mapping,direct_query, queries, top_k=10):
143
+ all_retrieved=[]
144
+ for query in queries:
145
+ results = retrieve_relevant_cards(
146
+ query, faiss_index, filtered_mapping, df, 10
147
+ )
148
+
149
+ reranked_cards = results[:5]
150
+
151
+ all_retrieved.extend(reranked_cards)
152
+
153
+ # Removing duplicates
154
+ seen = set()
155
+ unique_cards = []
156
+ for card in all_retrieved:
157
+ if card["name"] not in seen:
158
+ seen.add(card["name"])
159
+ unique_cards.append(card)
160
+ if not unique_cards:
161
+ return unique_cards
162
+ reranked_cards = rerank_cards_mini(direct_query, unique_cards, top_n=5)
163
+ return reranked_cards
164
+
165
+
166
+ def generate_credit_card_recommendation_gemini(indirect_query,user_query,retrieved_cards, max_new_tokens=500):
167
+ genai.configure(api_key=os.environ.get("api_key_3"))
168
+ model3 = genai.GenerativeModel('gemini-1.5-flash-latest')
169
+ print("using gemini")
170
+ if not retrieved_cards:
171
+ return "Unfortunately, no credit cards match your eligibility criteria."
172
+ for card in retrieved_cards:
173
+ print(f"Name: {card['name']}")
174
+ print(f"Description: {card['description']}\n")
175
+
176
+ # Formatting retrieved cards for input context
177
+ cards_info = "\n\n".join([
178
+ f"{card['name']}\n"
179
+ f"- Description: {card['description']}\n\n"
180
+ for card in retrieved_cards
181
+ ])
182
+
183
+ # Gemini Prompt
184
+ gemini_prompt = f"""
185
+ You are a financial analyst specializing in credit cards and rewards optimization.
186
+ Your task is to analyze and select the best credit card based on user priorities.
187
+
188
+ ### User's Query:
189
+ {indirect_query+user_query}
190
+
191
+ ### Available Credit Cards:
192
+ {cards_info}
193
+
194
+ ### Instructions:
195
+ 1️ Analyze the user's need from the given query .
196
+ 2️ Select the best card based on that primary need and only from the details present in the desctiption.Do not include details which are not explicitly mentioned.
197
+ 3️ Explain why it's the best choice (list benefits concisely).
198
+ 4 Do not assume and include benefits or features which is nor explicitly mentioned in the card description.
199
+ 5 Mention the benefits only if it explicitly mentioned in the card description and if it is not mentioned just skip it and do not mention anything about it.
200
+ 6 Do not include extra symbols like * or #, just generate a textual response maybe with numbers for points.
201
+ 7️ If the user mentions they need FD-based cards or cards for students or if they have low credit score, assume all the provided cards are FD-based. Just compare them based on benefits and choose the best one.
202
+ 8 If user asks for fd based cards and the descriptions do not mention it just pick a best card from the given list.
203
+
204
+ ### Expected Output Format:
205
+ Best Card: [Card Name]
206
+ Why It’s the Best:
207
+ 1️ [Primary Benefit] : [Explanation]
208
+ 2️ [Additional Perks] : [Explanation]
209
+ 3️ [Final Justification] : [Why it's the best fit]
210
+ """
211
+
212
+ # Generate response using Gemini
213
+ response = model3.generate_content(gemini_prompt)
214
+
215
+ return response.text
ui/gradio_interface.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from agents.chat import chat_with_gemini
3
+ from agents.compare import compare_selected_cards
4
+ from data import all_card_names,all_card_lookup
5
+ from recommender.recommender import recommend_cards_gradio
6
+
7
+ # Interface with Tabs
8
+ with gr.Blocks() as demo:
9
+ gr.Markdown("# Credit Card Recommender")
10
+ gr.Markdown("Get personalized credit card suggestions based on your lifestyle and eligibility.")
11
+
12
+ with gr.Tabs():
13
+
14
+ with gr.Tab(" Get Recommendations"):
15
+ with gr.Row():
16
+ user_query = gr.Textbox(
17
+ label="Enter your query",
18
+ info="E.g., 'Best cards for international travel' or 'I want cashback cards with lounge access'"
19
+ )
20
+ preferences = gr.CheckboxGroup(
21
+ choices=["Cashback", "Travel Rewards", "Fuel Benefits", "International Lounge access",
22
+ "Domestic Lounge access", "Railway benefits", "Dining", "Shopping"],
23
+ label="Credit card categories:",
24
+ info="Select the features or benefits you want from your credit card"
25
+ )
26
+
27
+ with gr.Accordion("Eligibility filters menu", open=False):
28
+ with gr.Row():
29
+ income = gr.Slider(
30
+ minimum=1, maximum=60, step=1,
31
+ label="Annual Income (LPA) Minimum requirement is 2.5",
32
+ info="Helps filter cards based on your income eligibility (in Lakhs Per Annum)"
33
+ )
34
+ cibil = gr.Slider(
35
+ minimum=300, maximum=900, step=10,
36
+ label="CIBIL Score",
37
+ info="Most of the cards requires a credit score of 700+"
38
+ )
39
+ age = gr.Slider(
40
+ minimum=18, maximum=75, step=1,
41
+ label="Age",
42
+ info="Some cards have minimum and maximum age eligibility"
43
+ )
44
+
45
+ with gr.Row():
46
+ min_joining_fee = gr.Number(
47
+ label="Min Joining Fee (₹)", value=0,
48
+ info="Minimum one-time fee to get the card"
49
+ )
50
+ max_joining_fee = gr.Number(
51
+ label="Max Joining Fee (₹)", value=150000,
52
+ info="Maximum one-time fee to get the card"
53
+ )
54
+
55
+ with gr.Row():
56
+ min_annual_fee = gr.Number(
57
+ label="Min Annual Fee (₹)", value=0,
58
+ info="Minimum yearly fee to be paid"
59
+ )
60
+ max_annual_fee = gr.Number(
61
+ label="Max Annual Fee (₹)", value=150000,
62
+ info="Maximum yearly fee to be paid"
63
+ )
64
+
65
+ with gr.Row():
66
+ use_eligibility = gr.Checkbox(
67
+ label="Apply Eligibility Filter", value=False,
68
+ info="Enable this to get recommendations of the cards only for which you are eligible for"
69
+ )
70
+ include_cobranded = gr.Checkbox(
71
+ label="Include Co-branded Cards", value=True,
72
+ info="Include cards that are co-branded with airlines, retailers, etc."
73
+ )
74
+
75
+ submit_btn = gr.Button("Recommend Cards", variant='primary')
76
+
77
+ top_card_html = gr.HTML()
78
+ card_df = gr.Dataframe(headers=["Card Name", "Matched Features", "Description"])
79
+ card_file = gr.File(label="Download Full Recommendations (CSV)")
80
+
81
+ with gr.Tab(" Compare Cards"):
82
+ gr.Markdown("### Compare Recommended Cards")
83
+ compare_checkboxes = gr.CheckboxGroup(
84
+ choices=[], label="Select 2 or more cards to compare",
85
+ info="Pick 2+ cards from the recommended list to see a comparison"
86
+ )
87
+ compare_output = gr.HTML(value="<div style='min-height:100px'></div>", visible=True)
88
+ compare_btn = gr.Button("Compare Selected Cards", variant='primary')
89
+
90
+ gr.Markdown("### Compare Any Cards from Full List")
91
+ full_compare_dropdown = gr.Dropdown(
92
+ choices=all_card_names, multiselect=True, label="Select any 2+ cards",
93
+ info="Manually compare any cards from the full database"
94
+ )
95
+ full_compare_btn = gr.Button("Compare Selected Cards", variant='primary')
96
+ full_compare_output = gr.HTML(value="<div style='min-height:100px'></div>", visible=True)
97
+
98
+ with gr.Tab(" Ask Follow-up Questions"):
99
+ gr.Markdown("### Ask any follow-up question ")
100
+ chatbot = gr.Chatbot(type='messages')
101
+ user_query_for_chat = gr.Textbox(
102
+ label="Enter your question",
103
+ info="Ask follow-ups like 'Which card has better travel insurance?' or 'Which card has less annual fee'"
104
+ )
105
+ submit_query_btn = gr.Button("Submit Query", variant='primary')
106
+
107
+ card_names_state = gr.State()
108
+ card_lookup_state = gr.State()
109
+ chat_history = gr.State([])
110
+ query = gr.State([])
111
+
112
+ def wrapped_recommend_cards(user_query, preferences, income, cibil, age, min_joining_fee, max_joining_fee,
113
+ min_annual_fee, max_annual_fee, use_eligibility,include_cobranded):
114
+ top_html, df, file, card_names, card_lookup, direct_query = recommend_cards_gradio(
115
+ user_query, preferences, income, cibil, age, min_joining_fee, max_joining_fee,
116
+ min_annual_fee, max_annual_fee, use_eligibility,include_cobranded
117
+ )
118
+ df_label = f"Found {len(card_names)} cards"
119
+ return top_html, gr.update(value=df, label=df_label), file, card_names, card_lookup, gr.update(choices=card_names, value=[]), direct_query
120
+
121
+ submit_btn.click(
122
+ fn=wrapped_recommend_cards,
123
+ inputs=[user_query, preferences, income, cibil, age, min_joining_fee, max_joining_fee,
124
+ min_annual_fee, max_annual_fee, use_eligibility,include_cobranded],
125
+ outputs=[top_card_html, card_df, card_file, card_names_state, card_lookup_state, compare_checkboxes,query]
126
+ )
127
+
128
+ compare_btn.click(
129
+ fn=compare_selected_cards,
130
+ inputs=[compare_checkboxes, card_lookup_state],
131
+ outputs=compare_output
132
+ )
133
+
134
+ full_compare_btn.click(
135
+ fn=lambda selected: compare_selected_cards(selected, all_card_lookup),
136
+ inputs=[full_compare_dropdown],
137
+ outputs=full_compare_output
138
+ )
139
+
140
+ submit_query_btn.click(
141
+ fn=chat_with_gemini,
142
+ inputs=[query,user_query_for_chat, chat_history, card_lookup_state],
143
+ outputs=[chatbot, chat_history]
144
+ ).then(
145
+ lambda: gr.update(value=""),
146
+ inputs=[],
147
+ outputs=[user_query_for_chat]
148
+ )
149
+
150
+ #for submitting using enter button
151
+ user_query_for_chat.submit(
152
+ fn=chat_with_gemini,
153
+ inputs=[query, user_query_for_chat, chat_history, card_lookup_state],
154
+ outputs=[chatbot, chat_history],
155
+ show_progress=True
156
+ ).then(
157
+ lambda: gr.update(value=""),
158
+ inputs=[],
159
+ outputs=[user_query_for_chat]
160
+ )