Sulaiman8 commited on
Commit
0ce490e
·
verified ·
1 Parent(s): 15be85a

Update recommender/retrieval_ranking.py

Browse files
Files changed (1) hide show
  1. recommender/retrieval_ranking.py +215 -214
recommender/retrieval_ranking.py CHANGED
@@ -1,215 +1,216 @@
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
- 2Select 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
- 3Explain 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
 
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
+ from data import df
8
+
9
+ # Function to generate direct query using Gemini
10
+ def convert_to_direct_query_gradio(user_query,preferences):
11
+ genai.configure(api_key=os.environ.get("api_key_2"))
12
+ model2 = genai.GenerativeModel('gemini-1.5-flash-latest')
13
+
14
+ preferences_text = ""
15
+ if preferences:
16
+ preferences_text = "User selected preferences: " + ", ".join(preferences) + "."
17
+ # Prompt with examples for indirect-to-direct conversion
18
+ prompt = f"""
19
+ You are an AI assistant that refines user queries to make them optimized for information retrieval while keeping the original intent.
20
+
21
+ Instructions:
22
+ - Identify the main focus from the query.
23
+ - Reformat the query in a structured way for better retrieval.
24
+ - Ensure the most important feature appears first.
25
+ - Use precise keywords that match credit card benefits.
26
+ - Include the preferences also in the final query.
27
+ - Do NOT introduce new benefits not mentioned by the user.
28
+ - 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.
29
+ - If the query includes terms like "beginner", "entry-level", or "low credit score", include essential features such as cashback, reward points, and basic offers.
30
+ - If the query is empty, generate a useful retrieval-focused query based solely on preferences.
31
+
32
+ Examples:
33
+
34
+ Example 1
35
+ - User Query: "I drive a lot for work and want a credit card with good fuel rewards and travel perks."
36
+ - Optimized Query: "Category: Fuel Rewards | Best credit cards for high fuel spending with maximum rewards & fuel surcharge waiver. Travel perks preferred but secondary."
37
+
38
+ Example 2
39
+ - User Query: "I mostly shop online and want a card that gives high cashback on e-commerce purchases. Food delivery perks would be nice."
40
+ - Optimized Query: "Category: Online Shopping | Credit cards with best cashback on e-commerce platforms like Amazon, Flipkart. Food delivery benefits secondary."
41
+
42
+ Example 3
43
+ - User Query: "I eat out a lot and also order food from Swiggy/Zomato. I want the best dining discounts and food delivery cashback."
44
+ - Optimized Query: "Category: Dining & Food Delivery | Top credit cards offering the best dining discounts at restaurants and cashback on Swiggy/Zomato orders."
45
+
46
+ Now, optimize the following preferences and user query:
47
+
48
+ Preferences: "{preferences_text}"
49
+ User Query: "{user_query}"
50
+ """
51
+
52
+ print("rewriting")
53
+ response = model2.generate_content(prompt)
54
+
55
+ print(response.text)
56
+ return response.text
57
+
58
+ # Function to generate multiple focused subqueries from a user query
59
+ def generate_multi_queries(direct_query, n=3):
60
+ genai.configure(api_key=os.environ.get("api_key_2"))
61
+ model2 = genai.GenerativeModel('gemini-1.5-flash-latest')
62
+ prompt = f"""
63
+ The following is a detailed credit card search query:
64
+ "{direct_query}"
65
+ Generate {n} distinct subqueries that **collectively cover all the important features** from the original query.
66
+ Each subquery should emphasize a **different combination** of the features (e.g., lounge access, travel insurance, low foreign transaction fees, hotel discounts, etc.).
67
+ Keep the same format: "Category: ... | ...". Make sure all features from the original query are represented across the {n} queries.
68
+ Output only the subqueries, one per line. Do not include any explanations, numbering, or formatting — just plain queries separated by newline characters.
69
+ """
70
+ response = model2.generate_content(prompt)
71
+ # print(response.text)
72
+ queries = [q.strip() for q in response.text.strip().split('\n') if q.strip()]
73
+
74
+ return queries
75
+
76
+ #retrieval
77
+ #Cross-Encoder Model
78
+ cross_encoder = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2")
79
+
80
+ def rerank_cards_mini(query, cards, top_n=5):
81
+ card_descriptions = [card["description"] for card in cards]
82
+ query_card_pairs = [[query, desc] for desc in card_descriptions]
83
+ scores = cross_encoder.predict(query_card_pairs)
84
+
85
+ #Normalizing
86
+ if len(scores) == 0 or (max(scores) - min(scores)) == 0:
87
+ scores = [1.0] * len(cards)
88
+ else:
89
+ scores = np.array(scores)
90
+ scores = (scores - scores.min()) / (scores.max() - scores.min())
91
+
92
+ # Sort cards by descending score
93
+ ranked_cards = sorted(zip(scores, cards), key=lambda x: x[0], reverse=True)
94
+ top_cards = [card for _, card in ranked_cards[:top_n]]
95
+
96
+ return top_cards
97
+
98
+ def retrieve_relevant_cards(user_query, index, chunk_name_mapping, df, top_k=15):
99
+ genai.configure(api_key=os.environ.get("api_key_2"))
100
+ model_name = "models/text-embedding-004"
101
+ print(f"\nUser Query: {user_query}")
102
+
103
+ # Generate and normalize query embedding
104
+ query_embedding = genai.embed_content(
105
+ model=model_name,
106
+ content=user_query,
107
+ task_type="RETRIEVAL_QUERY"
108
+ )["embedding"]
109
+ # query_embedding=model.encode(user_query)
110
+ query_embedding = np.array(query_embedding, dtype=np.float32)
111
+ faiss.normalize_L2(query_embedding.reshape(1, -1))
112
+
113
+ # Search FAISS index for top-k chunks
114
+ D, I = index.search(np.expand_dims(query_embedding, axis=0), top_k * 5)
115
+ similarity_scores = D[0]
116
+
117
+ # Map chunks back to unique card names with highest similarity per card
118
+ card_similarity = defaultdict(float)
119
+ card_dict = {card["name"]: card for card in df.to_dict(orient="records")}
120
+ unique_cards = {}
121
+
122
+ for i, chunk_idx in enumerate(I[0]):
123
+ if chunk_idx == -1:
124
+ continue
125
+ card_name = chunk_name_mapping[chunk_idx]
126
+ if card_name not in unique_cards or similarity_scores[i] > card_similarity[card_name]:
127
+ card_similarity[card_name] = similarity_scores[i]
128
+ unique_cards[card_name] = {
129
+ "name": card_name,
130
+ "description": card_dict[card_name]["description"],
131
+ "similarity": similarity_scores[i]
132
+ }
133
+
134
+ # Sort by similarity and take top_k
135
+ ordered_cards = sorted(unique_cards.values(), key=lambda x: x["similarity"], reverse=True)[:top_k]
136
+
137
+ print("\nTop Recommended Cards:")
138
+ for card in ordered_cards:
139
+ print(f"- {card['name']} (Similarity: {card['similarity']:.4f})")
140
+
141
+ return ordered_cards
142
+
143
+ def retrieve_and_rank_cards(faiss_index,filtered_mapping,direct_query, queries, top_k=10):
144
+ all_retrieved=[]
145
+ for query in queries:
146
+ results = retrieve_relevant_cards(
147
+ query, faiss_index, filtered_mapping, df, 10
148
+ )
149
+
150
+ reranked_cards = results[:5]
151
+
152
+ all_retrieved.extend(reranked_cards)
153
+
154
+ # Removing duplicates
155
+ seen = set()
156
+ unique_cards = []
157
+ for card in all_retrieved:
158
+ if card["name"] not in seen:
159
+ seen.add(card["name"])
160
+ unique_cards.append(card)
161
+ if not unique_cards:
162
+ return unique_cards
163
+ reranked_cards = rerank_cards_mini(direct_query, unique_cards, top_n=5)
164
+ return reranked_cards
165
+
166
+
167
+ def generate_credit_card_recommendation_gemini(indirect_query,user_query,retrieved_cards, max_new_tokens=500):
168
+ genai.configure(api_key=os.environ.get("api_key_3"))
169
+ model3 = genai.GenerativeModel('gemini-1.5-flash-latest')
170
+ print("using gemini")
171
+ if not retrieved_cards:
172
+ return "Unfortunately, no credit cards match your eligibility criteria."
173
+ for card in retrieved_cards:
174
+ print(f"Name: {card['name']}")
175
+ print(f"Description: {card['description']}\n")
176
+
177
+ # Formatting retrieved cards for input context
178
+ cards_info = "\n\n".join([
179
+ f"{card['name']}\n"
180
+ f"- Description: {card['description']}\n\n"
181
+ for card in retrieved_cards
182
+ ])
183
+
184
+ # Gemini Prompt
185
+ gemini_prompt = f"""
186
+ You are a financial analyst specializing in credit cards and rewards optimization.
187
+ Your task is to analyze and select the best credit card based on user priorities.
188
+
189
+ ### User's Query:
190
+ {indirect_query+user_query}
191
+
192
+ ### Available Credit Cards:
193
+ {cards_info}
194
+
195
+ ### Instructions:
196
+ 1Analyze the user's need from the given query .
197
+ 2Select 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.
198
+ 3️ Explain why it's the best choice (list benefits concisely).
199
+ 4 Do not assume and include benefits or features which is nor explicitly mentioned in the card description.
200
+ 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.
201
+ 6 Do not include extra symbols like * or #, just generate a textual response maybe with numbers for points.
202
+ 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.
203
+ 8 If user asks for fd based cards and the descriptions do not mention it just pick a best card from the given list.
204
+
205
+ ### Expected Output Format:
206
+ Best Card: [Card Name]
207
+ Why It’s the Best:
208
+ 1️ [Primary Benefit] : [Explanation]
209
+ 2️ [Additional Perks] : [Explanation]
210
+ 3️ [Final Justification] : [Why it's the best fit]
211
+ """
212
+
213
+ # Generate response using Gemini
214
+ response = model3.generate_content(gemini_prompt)
215
+
216
  return response.text