Aniq-63 commited on
Commit
cf8e943
·
verified ·
1 Parent(s): d001c30

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -133
app.py CHANGED
@@ -2,7 +2,6 @@ import os
2
  import sqlite3
3
  import streamlit as st
4
  from werkzeug.security import generate_password_hash, check_password_hash
5
- from datetime import datetime
6
 
7
  from langchain_groq import ChatGroq
8
  from langchain_huggingface import HuggingFaceEmbeddings
@@ -47,13 +46,11 @@ def init_db():
47
  features TEXT NOT NULL,
48
  stock INTEGER NOT NULL DEFAULT 0)''')
49
 
50
- # Inventory log table
51
- c.execute('''CREATE TABLE IF NOT EXISTS inventory_log
52
- (id INTEGER PRIMARY KEY AUTOINCREMENT,
53
- timestamp TEXT NOT NULL,
54
- product_name TEXT NOT NULL,
55
- found INTEGER NOT NULL,
56
- username TEXT)''')
57
 
58
  # Insert default company settings if empty
59
  c.execute('SELECT COUNT(*) FROM company_settings')
@@ -71,40 +68,8 @@ def init_db():
71
 
72
  conn = init_db()
73
 
74
- # --- Inventory Management Functions ---
75
- def log_inventory_access(product_name: str, found: bool, username: str = None):
76
- """Log all inventory access attempts"""
77
- c = conn.cursor()
78
- c.execute('''INSERT INTO inventory_log
79
- (timestamp, product_name, found, username)
80
- VALUES (?, ?, ?, ?)''',
81
- (datetime.now().isoformat(), product_name, int(found), username))
82
- conn.commit()
83
-
84
- def validate_product_in_inventory(product_name: str) -> bool:
85
- """Check if product exists and is in stock"""
86
- c = conn.cursor()
87
- c.execute('''SELECT id FROM products
88
- WHERE LOWER(name) LIKE LOWER(?) AND stock > 0''',
89
- (f'%{product_name}%',))
90
- return c.fetchone() is not None
91
-
92
- def get_inventory_report():
93
- """Generate inventory report for admin"""
94
- c = conn.cursor()
95
- c.execute('''SELECT name, category, price, stock
96
- FROM products ORDER BY category, name''')
97
- return c.fetchall()
98
-
99
- def get_inventory_alerts():
100
- """Get low stock alerts"""
101
- c = conn.cursor()
102
- c.execute('''SELECT name, stock FROM products
103
- WHERE stock <= 3 AND stock > 0
104
- ORDER BY stock ASC''')
105
- return c.fetchall()
106
-
107
  # --- Admin Classes ---
 
108
  class Company:
109
  @staticmethod
110
  def get_settings():
@@ -128,15 +93,6 @@ class Product:
128
  c.execute('SELECT * FROM products')
129
  return c.fetchall()
130
 
131
- @staticmethod
132
- def search(query):
133
- c = conn.cursor()
134
- c.execute('''SELECT * FROM products
135
- WHERE LOWER(name) LIKE LOWER(?)
136
- OR LOWER(category) LIKE LOWER(?)''',
137
- (f'%{query}%', f'%{query}%'))
138
- return c.fetchall()
139
-
140
  @staticmethod
141
  def get_by_id(product_id):
142
  c = conn.cursor()
@@ -176,17 +132,21 @@ class User:
176
  @classmethod
177
  def create(cls, username, password):
178
  hashed_pw = generate_password_hash(password)
 
179
  c = conn.cursor()
180
  c.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, hashed_pw))
181
  user_id = c.lastrowid
182
  conn.commit()
 
183
  return cls(user_id, username, hashed_pw)
184
 
185
  @classmethod
186
  def get_by_username(cls, username):
 
187
  c = conn.cursor()
188
  c.execute('SELECT * FROM users WHERE username = ?', (username,))
189
  user = c.fetchone()
 
190
  if user:
191
  return cls(user[0], user[1], user[2],
192
  eval(user[3]) if user[3] else [],
@@ -195,19 +155,23 @@ class User:
195
 
196
  def update_chat_history(self, new_messages):
197
  updated_history = self.chat_history + new_messages
 
198
  c = conn.cursor()
199
  c.execute('UPDATE users SET previous_chat_history = ? WHERE id = ?',
200
  (str(updated_history), self.id))
201
  conn.commit()
202
- self.chat_history = updated_history
 
203
 
204
  def update_products_bought(self, new_products):
205
  updated_products = self.products_bought + new_products
 
206
  c = conn.cursor()
207
  c.execute('UPDATE users SET previous_products_bought = ? WHERE id = ?',
208
  (str(updated_products), self.id))
209
  conn.commit()
210
- self.products_bought = updated_products
 
211
 
212
  # --- AI Setup ---
213
  os.environ["GROQ_API_KEY"] = st.secrets["GROQ_API_KEY"]
@@ -236,23 +200,14 @@ def load_data():
236
  retriever = load_data()
237
 
238
  def retrieve_query(query: str):
239
- """Enhanced product retrieval with strict inventory checks"""
240
  docs = retriever.get_relevant_documents(query)
241
- valid_docs = [doc for doc in docs if doc.metadata.get('stock', 0) > 0]
242
-
243
- if not valid_docs:
244
- return [Document(
245
- page_content=f"No matching in-stock products found for '{query}'",
246
- metadata={"id": -1, "stock": 0}
247
- )]
248
-
249
- st.session_state.last_retrieved_docs = valid_docs
250
- return valid_docs
251
 
252
  tool = Tool(
253
  name="product_retriever",
254
  func=retrieve_query,
255
- description="Useful for retrieving product information from CURRENT INVENTORY only"
256
  )
257
 
258
  # --- Admin Dashboard ---
@@ -311,39 +266,13 @@ def admin_dashboard():
311
  st.rerun()
312
  else:
313
  st.info("No products found in database")
314
-
315
- with st.expander("Inventory Reports"):
316
- st.subheader("Current Inventory Status")
317
- inventory = get_inventory_report()
318
- if inventory:
319
- st.table(inventory)
320
- else:
321
- st.warning("No inventory data available")
322
-
323
- st.subheader("Low Stock Alerts")
324
- alerts = get_inventory_alerts()
325
- if alerts:
326
- for item in alerts:
327
- st.warning(f"{item[0]} - Only {item[1]} left!")
328
- else:
329
- st.info("No low stock alerts")
330
-
331
- st.subheader("Inventory Access Logs")
332
- c = conn.cursor()
333
- c.execute('''SELECT timestamp, product_name, found, username
334
- FROM inventory_log ORDER BY timestamp DESC LIMIT 100''')
335
- logs = c.fetchall()
336
- if logs:
337
- st.table(logs)
338
- else:
339
- st.info("No inventory access logs yet")
340
 
341
  # --- Main App ---
342
  def main():
343
  company_settings = Company.get_settings()
344
  company_name = company_settings[1]
345
 
346
- st.title(f"{company_name} AI Sales Assistant 🤖")
347
 
348
  if 'user' not in st.session_state:
349
  st.session_state.user = None
@@ -353,7 +282,7 @@ def main():
353
 
354
  # Authentication
355
  if not st.session_state.user and not st.session_state.admin_mode:
356
- st.header("Login/Register")
357
  tab1, tab2, tab3 = st.tabs(["Login", "Register", "Admin"])
358
 
359
  with tab1:
@@ -400,8 +329,8 @@ def main():
400
 
401
  else:
402
  # Chat Interface
403
- st.header(f"Welcome, {st.session_state.user.username}!")
404
- st.caption(f"You're chatting with {company_settings[3]}, your AI sales assistant")
405
 
406
  # Display Chat History
407
  for msg in st.session_state.chat_history:
@@ -425,19 +354,6 @@ def main():
425
  - Key Features: {company_settings[4]}
426
  - Agent Name: {company_settings[3]}
427
 
428
- CURRENT INVENTORY:
429
- {current_products}
430
-
431
- STRICT INVENTORY POLICY:
432
- - You MUST ONLY recommend products that exist in our CURRENT INVENTORY
433
- - NEVER suggest, mention, or describe products not in our database
434
- - If asked for unavailable items, respond: "I apologize, we don't currently carry that product."
435
-
436
- When recommending products:
437
- 1. FIRST verify the product exists in the above inventory
438
- 2. ONLY discuss products that appear in the inventory list
439
- 3. For unavailable items, DO NOT suggest alternatives unless they exist in inventory
440
-
441
  ## Inventory Management Policy
442
  1. **Stock Verification**:
443
  - ALWAYS check current stock before recommending any product
@@ -482,6 +398,9 @@ def main():
482
  - Emojis: Use sparingly (1-2 per message max)
483
  - Branding: Consistently reference {company_settings[1]} when appropriate
484
 
 
 
 
485
  ## Important Reminders
486
  1. NEVER recommend products not in our inventory
487
  2. ALWAYS verify stock before discussing any product
@@ -490,7 +409,6 @@ def main():
490
  5. For complex queries, offer to connect with human support
491
  """
492
 
493
-
494
  prompt = ChatPromptTemplate.from_messages([
495
  ("system", system_prompt),
496
  MessagesPlaceholder(variable_name="chat_history"),
@@ -503,35 +421,20 @@ def main():
503
  agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
504
 
505
  if prompt_input := st.chat_input("Type your message here..."):
506
- # Log inventory access attempt
507
- log_inventory_access(
508
- product_name=prompt_input,
509
- found=validate_product_in_inventory(prompt_input),
510
- username=st.session_state.user.username
511
- )
512
-
513
  with st.chat_message("user"):
514
  st.write(prompt_input)
515
 
516
  with st.chat_message("assistant"):
517
  response = agent_executor.invoke({
518
  "input": prompt_input,
519
- "chat_history": [HumanMessage(content=msg["content"]) if msg["type"] == "human"
520
- else AIMessage(content=msg["content"])
521
  for msg in st.session_state.chat_history]
522
  })["output"]
523
-
524
- # Post-process response to ensure inventory compliance
525
- if "last_retrieved_docs" in st.session_state:
526
- if st.session_state.last_retrieved_docs[0].metadata.get("id") == -1:
527
- response = f"I apologize, we don't currently carry that product in our inventory."
528
- elif not st.session_state.last_retrieved_docs:
529
- response = f"I couldn't find that product in our current inventory."
530
-
531
  st.write(response)
532
 
533
  # Handle inventory update on purchase
534
  if "https://www.example.com/payment" in response:
 
535
  product_name = None
536
  if "proceed with the purchase of" in response:
537
  start = response.find("proceed with the purchase of") + len("proceed with the purchase of")
@@ -539,16 +442,19 @@ def main():
539
  product_name = response[start:end].strip()
540
 
541
  if product_name:
 
542
  docs = retrieve_query(product_name)
543
- if docs and docs[0].metadata.get("id") != -1:
544
  product_doc = docs[0]
545
  product_id = product_doc.metadata.get("id")
546
  current_stock = product_doc.metadata.get("stock")
547
 
548
  if product_id and current_stock > 0:
549
  try:
550
- # Verify current stock
551
  c = conn.cursor()
 
 
552
  c.execute('SELECT stock FROM products WHERE id = ?', (product_id,))
553
  actual_stock = c.fetchone()[0]
554
 
@@ -558,23 +464,38 @@ def main():
558
  (actual_stock - 1, product_id))
559
 
560
  # Update user's purchase history
561
- st.session_state.user.products_bought.append(product_name)
562
- st.session_state.user.update_products_bought([product_name])
 
 
 
 
 
 
563
 
564
  conn.commit()
 
565
  st.success(f"Successfully purchased {product_name}! Stock updated.")
566
  load_data.clear()
567
 
568
  # Check for low stock
569
- if (actual_stock - 1) <= 3:
570
- st.warning(f"Low stock alert: Only {actual_stock - 1} units remaining!")
571
  else:
572
  st.error(f"Sorry, {product_name} is now out of stock!")
573
  except Exception as e:
574
  st.error(f"Error processing purchase: {str(e)}")
575
- conn.rollback()
 
576
  finally:
577
- c.close()
 
 
 
 
 
 
 
578
 
579
  new_messages = [
580
  {"type": "human", "content": prompt_input},
 
2
  import sqlite3
3
  import streamlit as st
4
  from werkzeug.security import generate_password_hash, check_password_hash
 
5
 
6
  from langchain_groq import ChatGroq
7
  from langchain_huggingface import HuggingFaceEmbeddings
 
46
  features TEXT NOT NULL,
47
  stock INTEGER NOT NULL DEFAULT 0)''')
48
 
49
+ # Check and update schema if needed
50
+ c.execute("PRAGMA table_info(products)")
51
+ columns = [column[1] for column in c.fetchall()]
52
+ if 'stock' not in columns:
53
+ c.execute('ALTER TABLE products ADD COLUMN stock INTEGER NOT NULL DEFAULT 0')
 
 
54
 
55
  # Insert default company settings if empty
56
  c.execute('SELECT COUNT(*) FROM company_settings')
 
68
 
69
  conn = init_db()
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  # --- Admin Classes ---
72
+
73
  class Company:
74
  @staticmethod
75
  def get_settings():
 
93
  c.execute('SELECT * FROM products')
94
  return c.fetchall()
95
 
 
 
 
 
 
 
 
 
 
96
  @staticmethod
97
  def get_by_id(product_id):
98
  c = conn.cursor()
 
132
  @classmethod
133
  def create(cls, username, password):
134
  hashed_pw = generate_password_hash(password)
135
+ conn = sqlite3.connect('users.db')
136
  c = conn.cursor()
137
  c.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, hashed_pw))
138
  user_id = c.lastrowid
139
  conn.commit()
140
+ conn.close()
141
  return cls(user_id, username, hashed_pw)
142
 
143
  @classmethod
144
  def get_by_username(cls, username):
145
+ conn = sqlite3.connect('users.db')
146
  c = conn.cursor()
147
  c.execute('SELECT * FROM users WHERE username = ?', (username,))
148
  user = c.fetchone()
149
+ conn.close()
150
  if user:
151
  return cls(user[0], user[1], user[2],
152
  eval(user[3]) if user[3] else [],
 
155
 
156
  def update_chat_history(self, new_messages):
157
  updated_history = self.chat_history + new_messages
158
+ conn = sqlite3.connect('users.db')
159
  c = conn.cursor()
160
  c.execute('UPDATE users SET previous_chat_history = ? WHERE id = ?',
161
  (str(updated_history), self.id))
162
  conn.commit()
163
+ conn.close()
164
+ self.chat_history = updated_history # Update in-memory
165
 
166
  def update_products_bought(self, new_products):
167
  updated_products = self.products_bought + new_products
168
+ conn = sqlite3.connect('users.db')
169
  c = conn.cursor()
170
  c.execute('UPDATE users SET previous_products_bought = ? WHERE id = ?',
171
  (str(updated_products), self.id))
172
  conn.commit()
173
+ conn.close()
174
+ self.products_bought = updated_products # Update in-memory
175
 
176
  # --- AI Setup ---
177
  os.environ["GROQ_API_KEY"] = st.secrets["GROQ_API_KEY"]
 
200
  retriever = load_data()
201
 
202
  def retrieve_query(query: str):
 
203
  docs = retriever.get_relevant_documents(query)
204
+ st.session_state.last_retrieved_docs = docs
205
+ return docs
 
 
 
 
 
 
 
 
206
 
207
  tool = Tool(
208
  name="product_retriever",
209
  func=retrieve_query,
210
+ description="Useful for retrieving product information including current stock levels"
211
  )
212
 
213
  # --- Admin Dashboard ---
 
266
  st.rerun()
267
  else:
268
  st.info("No products found in database")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
  # --- Main App ---
271
  def main():
272
  company_settings = Company.get_settings()
273
  company_name = company_settings[1]
274
 
275
+ st.title("AI Sales Assistant 🤖")
276
 
277
  if 'user' not in st.session_state:
278
  st.session_state.user = None
 
282
 
283
  # Authentication
284
  if not st.session_state.user and not st.session_state.admin_mode:
285
+ st.header("Login/Register Admin")
286
  tab1, tab2, tab3 = st.tabs(["Login", "Register", "Admin"])
287
 
288
  with tab1:
 
329
 
330
  else:
331
  # Chat Interface
332
+ st.header(f"Welcome to {company_name}, {st.session_state.user.username}!")
333
+ st.subheader("Chat with our AI Sales Assistant")
334
 
335
  # Display Chat History
336
  for msg in st.session_state.chat_history:
 
354
  - Key Features: {company_settings[4]}
355
  - Agent Name: {company_settings[3]}
356
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  ## Inventory Management Policy
358
  1. **Stock Verification**:
359
  - ALWAYS check current stock before recommending any product
 
398
  - Emojis: Use sparingly (1-2 per message max)
399
  - Branding: Consistently reference {company_settings[1]} when appropriate
400
 
401
+ ## Current Inventory Status
402
+ {current_products}
403
+
404
  ## Important Reminders
405
  1. NEVER recommend products not in our inventory
406
  2. ALWAYS verify stock before discussing any product
 
409
  5. For complex queries, offer to connect with human support
410
  """
411
 
 
412
  prompt = ChatPromptTemplate.from_messages([
413
  ("system", system_prompt),
414
  MessagesPlaceholder(variable_name="chat_history"),
 
421
  agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
422
 
423
  if prompt_input := st.chat_input("Type your message here..."):
 
 
 
 
 
 
 
424
  with st.chat_message("user"):
425
  st.write(prompt_input)
426
 
427
  with st.chat_message("assistant"):
428
  response = agent_executor.invoke({
429
  "input": prompt_input,
430
+ "chat_history": [HumanMessage(content=msg["content"]) if msg["type"] == "human" else AIMessage(content=msg["content"])
 
431
  for msg in st.session_state.chat_history]
432
  })["output"]
 
 
 
 
 
 
 
 
433
  st.write(response)
434
 
435
  # Handle inventory update on purchase
436
  if "https://www.example.com/payment" in response:
437
+ # Extract product name from the agent's response
438
  product_name = None
439
  if "proceed with the purchase of" in response:
440
  start = response.find("proceed with the purchase of") + len("proceed with the purchase of")
 
442
  product_name = response[start:end].strip()
443
 
444
  if product_name:
445
+ # Retrieve current product details
446
  docs = retrieve_query(product_name)
447
+ if docs:
448
  product_doc = docs[0]
449
  product_id = product_doc.metadata.get("id")
450
  current_stock = product_doc.metadata.get("stock")
451
 
452
  if product_id and current_stock > 0:
453
  try:
454
+ conn = sqlite3.connect('users.db')
455
  c = conn.cursor()
456
+
457
+ # Verify current stock
458
  c.execute('SELECT stock FROM products WHERE id = ?', (product_id,))
459
  actual_stock = c.fetchone()[0]
460
 
 
464
  (actual_stock - 1, product_id))
465
 
466
  # Update user's purchase history
467
+ c.execute('SELECT previous_products_bought FROM users WHERE id = ?',
468
+ (st.session_state.user.id,))
469
+ current_purchases = c.fetchone()[0]
470
+
471
+ updated_purchases = eval(current_purchases) + [product_name] if current_purchases else [product_name]
472
+
473
+ c.execute('UPDATE users SET previous_products_bought = ? WHERE id = ?',
474
+ (str(updated_purchases), st.session_state.user.id))
475
 
476
  conn.commit()
477
+ st.session_state.user.products_bought.append(product_name)
478
  st.success(f"Successfully purchased {product_name}! Stock updated.")
479
  load_data.clear()
480
 
481
  # Check for low stock
482
+ if (actual_stock - 1) <= 3 and (actual_stock - 1) > 0:
483
+ st.warning(f"Low stock alert: Only {actual_stock - 1} units of {product_name} remaining!")
484
  else:
485
  st.error(f"Sorry, {product_name} is now out of stock!")
486
  except Exception as e:
487
  st.error(f"Error processing purchase: {str(e)}")
488
+ if conn:
489
+ conn.rollback()
490
  finally:
491
+ if conn:
492
+ conn.close()
493
+ else:
494
+ st.error("Product is out of stock or not found in the inventory.")
495
+ else:
496
+ st.error("Could not retrieve product details. Please try again.")
497
+ else:
498
+ st.error("Please specify which product you'd like to purchase.")
499
 
500
  new_messages = [
501
  {"type": "human", "content": prompt_input},