rairo commited on
Commit
893c696
·
verified ·
1 Parent(s): f5d4c6e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -80
app.py CHANGED
@@ -1,20 +1,20 @@
1
  import streamlit as st
2
  import pandas as pd
3
- import google.generativeai as genai
4
  from langchain.memory import ConversationBufferMemory
5
  from langchain_google_genai import ChatGoogleGenerativeAI
 
 
 
 
6
  from langchain.agents import initialize_agent, Tool
7
  from langchain.agents.agent_types import AgentType
8
  from difflib import get_close_matches
9
  from dotenv import load_dotenv
10
- from fpdf import FPDF
11
- from PIL import Image
12
- import PyPDF2
13
- import os
14
 
15
  # Load environment variables
16
  load_dotenv()
17
 
 
18
  genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))
19
 
20
  def configure_gemini(api_key):
@@ -22,7 +22,12 @@ def configure_gemini(api_key):
22
  return genai.GenerativeModel('gemini-2.0-flash-thinking-exp')
23
 
24
  model = configure_gemini(os.environ['GOOGLE_API_KEY'])
25
- llm_flash_exp = ChatGoogleGenerativeAI(model="gemini-2.0-flash-exp", max_retries=2)
 
 
 
 
 
26
 
27
  class SmartShoppingAssistant:
28
  def __init__(self, products_df):
@@ -33,35 +38,126 @@ class SmartShoppingAssistant:
33
  def find_closest_product(self, product_name, threshold=0.6):
34
  matches = get_close_matches(
35
  product_name.upper(),
36
- self.df[self.df['IsAvailable'] == "Yes"]['ProductName'].str.upper().tolist(),
37
- n=5,
38
  cutoff=threshold
39
  )
40
  return matches if matches else []
41
 
42
- def search_products_fuzzy(self, query):
43
- results = []
44
- closest_matches = self.find_closest_product(query)
45
- for match in closest_matches:
46
- matched_products = self.df[self.df['ProductName'].str.upper() == match.upper()]
47
- if not matched_products.empty:
48
- results.append(matched_products.iloc[0].to_dict())
49
- return results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  def setup_agent(self):
 
52
  def search_products(query):
53
- results = self.search_products_fuzzy(query)
54
- if results:
55
- return "\n".join([f"{res['ProductName']} - ${res['RetailPrice']}" for res in results])
56
- return "No products found."
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- tools = [Tool(name="Product Search", func=search_products, description="Search for products in the supermarket")]
59
- self.agent = initialize_agent(tools=tools, memory=self.memory, llm=llm_flash_exp, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- def process_query(self, query):
62
- return self.agent.run(f"Find the best matches for: {query}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  def extract_text_from_image(self, image):
 
65
  prompt = """
66
  Analyze this image and extract products and their quantities.
67
  If quantities aren't specified, make reasonable assumptions based on typical shopping patterns.
@@ -74,6 +170,7 @@ class SmartShoppingAssistant:
74
  return f"Error processing image: {str(e)}"
75
 
76
  def extract_text_from_pdf(self, pdf_file):
 
77
  try:
78
  pdf_reader = PyPDF2.PdfReader(pdf_file)
79
  text = ""
@@ -83,83 +180,64 @@ class SmartShoppingAssistant:
83
  except Exception as e:
84
  return f"Error processing PDF: {str(e)}"
85
 
86
- def generate_receipt(cart_items):
87
- pdf = FPDF()
88
- pdf.add_page()
89
- pdf.set_font("Arial", size=12)
90
- pdf.cell(200, 10, txt="Supermarket Receipt", ln=True, align='C')
91
- pdf.ln(10)
92
- total = 0
93
- for item in cart_items:
94
- line = f"{item['ProductName']} - ${item['RetailPrice']}"
95
- pdf.cell(200, 10, txt=line, ln=True)
96
- total += item['RetailPrice']
97
- pdf.ln(10)
98
- pdf.cell(200, 10, txt=f"Total: ${total:.2f}", ln=True)
99
- receipt_path = "receipt.pdf"
100
- pdf.output(receipt_path)
101
- return receipt_path
102
-
103
  def main():
104
  st.set_page_config(page_title="Smart Shopping Assistant", layout="wide")
105
  st.title("🛒 Smart Shopping Assistant")
106
 
107
  @st.cache_data
108
  def load_product_data():
109
- return pd.read_csv('supermarket4i.csv')
110
 
111
  df = load_product_data()
112
  assistant = SmartShoppingAssistant(df)
113
 
114
  with st.sidebar:
115
  st.header("Upload Shopping List")
116
- uploaded_file = st.file_uploader("Upload an image or PDF of your shopping list", type=['png', 'jpg', 'jpeg', 'pdf'])
 
 
 
 
117
  if uploaded_file:
118
  try:
119
  if uploaded_file.type.startswith('image'):
120
- image = Image.open(uploaded_file)
121
- extracted_text = assistant.extract_text_from_image(image)
122
- st.session_state.query = extracted_text
 
123
  elif uploaded_file.type == 'application/pdf':
124
- extracted_text = assistant.extract_text_from_pdf(uploaded_file)
125
- st.session_state.query = extracted_text
 
126
  except Exception as e:
127
  st.error(f"Error processing file: {str(e)}")
 
 
128
 
129
- if 'cart' not in st.session_state:
130
- st.session_state.cart = []
131
-
132
- query = st.text_input("Search for a product:")
133
- if st.button("Search"):
134
- if query:
135
- results = assistant.search_products_fuzzy(query)
136
- if results:
137
- for res in results:
138
- col1, col2 = st.columns([3, 1])
139
- with col1:
140
- st.write(f"**{res['ProductName']}** - ${res['RetailPrice']}")
141
- with col2:
142
- if st.button("Add to Cart", key=res['ProductName']):
143
- st.session_state.cart.append(res)
144
- st.rerun()
145
  else:
146
- st.warning("No matching products found.")
147
 
148
- st.header("Shopping Cart")
149
- if st.session_state.cart:
150
- total = sum(item['RetailPrice'] for item in st.session_state.cart)
151
- for item in st.session_state.cart:
152
- st.write(f"{item['ProductName']} - ${item['RetailPrice']}")
153
- st.write(f"**Total: ${total:.2f}**")
154
-
155
- if st.button("Checkout"):
156
- receipt_path = generate_receipt(st.session_state.cart)
157
- with open(receipt_path, "rb") as file:
158
- st.download_button(label="Download Receipt", data=file, file_name="receipt.pdf", mime="application/pdf")
159
  st.session_state.cart = []
160
- st.rerun()
161
- else:
162
- st.write("Your cart is empty.")
163
 
164
  if __name__ == "__main__":
165
- main()
 
1
  import streamlit as st
2
  import pandas as pd
 
3
  from langchain.memory import ConversationBufferMemory
4
  from langchain_google_genai import ChatGoogleGenerativeAI
5
+ import google.generativeai as genai
6
+ from PIL import Image
7
+ import PyPDF2
8
+ import os
9
  from langchain.agents import initialize_agent, Tool
10
  from langchain.agents.agent_types import AgentType
11
  from difflib import get_close_matches
12
  from dotenv import load_dotenv
 
 
 
 
13
 
14
  # Load environment variables
15
  load_dotenv()
16
 
17
+ # Configure Google API
18
  genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))
19
 
20
  def configure_gemini(api_key):
 
22
  return genai.GenerativeModel('gemini-2.0-flash-thinking-exp')
23
 
24
  model = configure_gemini(os.environ['GOOGLE_API_KEY'])
25
+
26
+ # Initialize Gemini models
27
+ llm_flash_exp = ChatGoogleGenerativeAI(
28
+ model="gemini-2.0-flash-exp",
29
+ max_retries=2
30
+ )
31
 
32
  class SmartShoppingAssistant:
33
  def __init__(self, products_df):
 
38
  def find_closest_product(self, product_name, threshold=0.6):
39
  matches = get_close_matches(
40
  product_name.upper(),
41
+ self.df['ProductName'].str.upper().tolist(),
42
+ n=3, # Increased to get more potential matches
43
  cutoff=threshold
44
  )
45
  return matches if matches else []
46
 
47
+ def match_products_with_catalogue(self, extracted_items):
48
+ """Match extracted items with catalogue products using Gemini"""
49
+ product_list = self.df['ProductName'].tolist()
50
+ product_string = ", ".join(product_list)
51
+
52
+ prompt = f"""
53
+ Given these extracted items and quantities:
54
+ {extracted_items}
55
+
56
+ And this product catalogue:
57
+ {product_string}
58
+
59
+ Match each item with the most appropriate product from the catalogue.
60
+ For each item, provide:
61
+ 1. The exact product name from the catalogue
62
+ 2. The quantity (if specified, otherwise assume 1)
63
+ 3. Any specific requirements (brand, size, etc.)
64
+
65
+ Format the response as:
66
+ ProductName == "MATCHED_PRODUCT" quantity: NUMBER or ProductName == "MATCHED_PRODUCT" quantity: NUMBER
67
+
68
+ Only include products that have good matches in the catalogue.
69
+ """
70
+
71
+ try:
72
+ matches = llm_flash_exp.predict(prompt)
73
+ return matches.strip()
74
+ except Exception as e:
75
+ return f"Error matching products: {str(e)}"
76
 
77
+ def search_products_fuzzy(self, product_names_with_quantities):
78
+ """Search for products using fuzzy matching with quantity information"""
79
+ results = pd.DataFrame()
80
+ for item in product_names_with_quantities:
81
+ product_info = item.split('quantity:')
82
+ product_name = product_info[0].strip()
83
+ quantity = int(product_info[1].strip()) if len(product_info) > 1 else 1
84
+
85
+ # Clean up product name
86
+ if 'ProductName ==' in product_name:
87
+ product_name = product_name.split('==')[1].strip(' "\'')
88
+
89
+ closest_matches = self.find_closest_product(product_name)
90
+ for match in closest_matches:
91
+ matched_products = self.df[self.df['ProductName'].str.upper() == match.upper()]
92
+ if not matched_products.empty:
93
+ matched_products['Quantity'] = quantity
94
+ results = pd.concat([results, matched_products])
95
+ break
96
+
97
+ return results
98
+
99
  def setup_agent(self):
100
+ """Set up the LangChain agent with necessary tools"""
101
  def search_products(query):
102
+ try:
103
+ # Split into individual product entries
104
+ product_entries = [entry.strip() for entry in query.split('or')]
105
+
106
+ results = self.search_products_fuzzy(product_entries)
107
+ if not results.empty:
108
+ # Format results with quantity
109
+ formatted_results = results.apply(
110
+ lambda x: f"{x['ProductName']} (Quantity: {x['Quantity']})\nPrice: ${x['RetailPrice']:.2f}\n",
111
+ axis=1
112
+ )
113
+ return "\n".join(formatted_results)
114
+ return "No products found matching your criteria."
115
+ except Exception as e:
116
+ return f"Error executing query: {str(e)}"
117
 
118
+ tools = [
119
+ Tool(
120
+ name="Product Search",
121
+ func=search_products,
122
+ description="Search for products in the supermarket database using fuzzy matching"
123
+ )
124
+ ]
125
+
126
+ self.agent = initialize_agent(
127
+ tools=tools,
128
+ memory=self.memory,
129
+ llm=llm_flash_exp,
130
+ agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
131
+ verbose=True,
132
+ max_iterations=3
133
+ )
134
 
135
+ def process_natural_language_query(self, query):
136
+ """Process natural language query with two-step matching"""
137
+ try:
138
+ # First step: Extract items and quantities
139
+ extraction_prompt = f"""
140
+ Extract the products and their quantities from this shopping request.
141
+ Include any specific requirements mentioned.
142
+
143
+ Shopping request: {query}
144
+
145
+ Format each item with its quantity (assume 1 if not specified).
146
+ """
147
+
148
+ extracted_items = llm_flash_exp.predict(extraction_prompt)
149
+
150
+ # Second step: Match with catalogue
151
+ matched_products = self.match_products_with_catalogue(extracted_items)
152
+
153
+ # Third step: Search and return results
154
+ result = self.agent.run(f"Search for products matching the specified names: {matched_products}")
155
+ return result
156
+ except Exception as e:
157
+ return f"Error processing query: {str(e)}"
158
 
159
  def extract_text_from_image(self, image):
160
+ """Extract text from uploaded image using Gemini"""
161
  prompt = """
162
  Analyze this image and extract products and their quantities.
163
  If quantities aren't specified, make reasonable assumptions based on typical shopping patterns.
 
170
  return f"Error processing image: {str(e)}"
171
 
172
  def extract_text_from_pdf(self, pdf_file):
173
+ """Extract text from uploaded PDF"""
174
  try:
175
  pdf_reader = PyPDF2.PdfReader(pdf_file)
176
  text = ""
 
180
  except Exception as e:
181
  return f"Error processing PDF: {str(e)}"
182
 
183
+ # Main function remains the same
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  def main():
185
  st.set_page_config(page_title="Smart Shopping Assistant", layout="wide")
186
  st.title("🛒 Smart Shopping Assistant")
187
 
188
  @st.cache_data
189
  def load_product_data():
190
+ return pd.read_csv('supermarket4.csv')
191
 
192
  df = load_product_data()
193
  assistant = SmartShoppingAssistant(df)
194
 
195
  with st.sidebar:
196
  st.header("Upload Shopping List")
197
+ uploaded_file = st.file_uploader(
198
+ "Upload an image or PDF of your shopping list",
199
+ type=['png', 'jpg', 'jpeg', 'pdf']
200
+ )
201
+
202
  if uploaded_file:
203
  try:
204
  if uploaded_file.type.startswith('image'):
205
+ with st.spinner("Extracting items from image..."):
206
+ image = Image.open(uploaded_file)
207
+ extracted_text = assistant.extract_text_from_image(image)
208
+ st.session_state.query = extracted_text
209
  elif uploaded_file.type == 'application/pdf':
210
+ with st.spinner("Extracting items from PDF..."):
211
+ extracted_text = assistant.extract_text_from_pdf(uploaded_file)
212
+ st.session_state.query = extracted_text
213
  except Exception as e:
214
  st.error(f"Error processing file: {str(e)}")
215
+
216
+ col1, col2 = st.columns([2, 1])
217
 
218
+ with col1:
219
+ st.header("Search Products")
220
+ query = st.text_area(
221
+ "Describe what you're looking for (include quantities if needed):",
222
+ height=100,
223
+ placeholder="Example: 2 boxes of healthy breakfast cereals under $5, 1 gallon of milk",
224
+ value=st.session_state.get('query', '')
225
+ )
226
+
227
+ if st.button("Search"):
228
+ if query:
229
+ with st.spinner("Searching for products..."):
230
+ results = assistant.process_natural_language_query(query)
231
+ st.write("### Results")
232
+ st.write(results)
 
233
  else:
234
+ st.warning("Please enter a search query or upload a shopping list.")
235
 
236
+ with col2:
237
+ st.header("Shopping Cart")
238
+ if 'cart' not in st.session_state:
 
 
 
 
 
 
 
 
239
  st.session_state.cart = []
240
+ st.write("Your cart is empty" if not st.session_state.cart else "Cart items here")
 
 
241
 
242
  if __name__ == "__main__":
243
+ main()