rairo commited on
Commit
69406fb
·
verified ·
1 Parent(s): cb599e6

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +37 -34
main.py CHANGED
@@ -1,9 +1,9 @@
1
  """
2
- main.py — Pricelyst Shopping Advisor (Jessica Edition 2026 - Upgrade v2.4)
3
 
4
- ✅ Fixed: Vision Prompt now forces item extraction for Products/Meals.
5
- ✅ Fixed: "Nice Dog" Logic. Irrelevant images get a Persona response, not an error.
6
- Fixed: "Limbo State". Fallback logic ensures 'Pepsi' is searched even if items=[] initially.
7
  ✅ "Analyst Engine": Enhanced Basket Math, Category Context, ZESA Logic.
8
  ✅ "Visual Engine": Lists, Products, & Meal-to-Recipe recognition.
9
  ✅ Memory Logic: Short-Term Sliding Window (Last 6 messages).
@@ -303,6 +303,8 @@ def calculate_basket_optimization(item_names: List[str]) -> Dict[str, Any]:
303
  "product_id": int(best_prod['product_id']),
304
  "name": str(best_prod['product_name']),
305
  "category": str(best_prod['category']),
 
 
306
  "category_stats": cat_stats
307
  })
308
 
@@ -422,23 +424,21 @@ def gemini_detect_intent(transcript: str) -> Dict[str, Any]:
422
  Analyze transcript. Return STRICT JSON.
423
  Classify intent:
424
  - CASUAL_CHAT: Greetings, small talk, "hi", "thanks".
425
- - SHOPPING_BASKET: Looking for prices, creating a list.
426
  - UTILITY_CALC: Electricity/ZESA questions.
427
  - STORE_DECISION: "Where should I buy?", "Which store is cheapest?".
428
  - TRUST_CHECK: "Is this expensive?", "Is this a good deal?".
429
 
430
  Extract:
431
- - items: list of products
432
  - utility_amount: number
433
- - context_tag: "budget", "speed", "quality"
434
 
435
  JSON Schema:
436
  {
437
  "actionable": boolean,
438
  "intent": "string",
439
  "items": ["string"],
440
- "utility_amount": number,
441
- "context_tag": "string"
442
  }
443
  """
444
  try:
@@ -495,28 +495,34 @@ def gemini_chat_response(transcript: str, intent: Dict, analyst_data: Dict, chat
495
  context_str += f"ZIMBABWE CONTEXT: Fuel={ZIM_CONTEXT['fuel_petrol']}, ZESA Rate={ZIM_CONTEXT['zesa_step_1']['rate']}\n"
496
 
497
  if analyst_data:
498
- context_str += f"ANALYST DATA: {json.dumps(analyst_data, default=str)}\n"
499
 
500
  PROMPT = f"""
501
  You are Jessica, Pricelyst's Shopping Advisor (Zimbabwe).
502
- Role: Helpful, thrifty, intelligent companion. Not a robot.
 
503
 
504
  INPUT: "{transcript}"
505
  INTENT: {intent.get('intent')}
506
  CONTEXT:
507
  {context_str}
508
 
509
- CRITICAL RULES FOR HISTORY:
510
- 1. Use 'RECENT CHAT HISTORY' ONLY to resolve references (e.g. "how much is *it*?").
511
- 2. **TOPIC RESET**: If the user says "Hi", "Hello", or asks about a completely NEW topic, IGNORE the previous history items. Do not bring up old shopping lists unless asked.
 
 
 
512
 
513
- INSTRUCTIONS:
514
- - **Casual**: Warm greeting. If they just said "Hi", reply "Makadii! How can I help you save today?". Don't summarize past chats.
515
- - **Advice**: If data exists, guide them. "I found XYZ at Store A."
516
- - **Trust**: If user asks "Is this expensive?", check 'category_stats'.
517
- - **Irrelevant Images**: If user sent a photo of a dog/nature, compliment it warmly, then gently mention you specialize in shopping.
518
-
519
- TONE: Conversational, Zimbabwean English.
 
 
520
  """
521
 
522
  try:
@@ -527,7 +533,7 @@ def gemini_chat_response(transcript: str, intent: Dict, analyst_data: Dict, chat
527
  return resp.text
528
  except Exception as e:
529
  logger.error(f"Chat Gen Error: {e}")
530
- return "I found the data, but I'm struggling to summarize it. Please check the plan below."
531
 
532
  def gemini_generate_4step_plan(transcript: str, analyst_result: Dict) -> str:
533
  if not _gemini_client: return "# Error\nAI Offline."
@@ -563,7 +569,7 @@ def health():
563
  "ok": True,
564
  "offers_indexed": len(df),
565
  "api_source": PRICE_API_BASE,
566
- "persona": "Jessica v2.4"
567
  })
568
 
569
  @app.post("/chat")
@@ -604,7 +610,8 @@ def chat():
604
  analyst_data = {}
605
 
606
  # 3. Data Processing (The Analyst)
607
- if intent_type in ["SHOPPING_BASKET", "STORE_DECISION", "TRUST_CHECK"] and items:
 
608
  analyst_data = calculate_basket_optimization(items)
609
 
610
  elif intent_type == "UTILITY_CALC":
@@ -651,10 +658,7 @@ def analyze_image():
651
  description = vision_result.get("description", "an image")
652
 
653
  # Fallback: If type is PRODUCT/MEAL but items is empty, try to use description as search item
654
- # This catches the "Pepsi bottle" case where items was []
655
  if (img_type in ["PRODUCT", "MEAL"]) and not items and description:
656
- # Simple heuristic: treat the description as the item for fuzzy search
657
- # It won't be perfect, but it prevents the "silent failure"
658
  items = [description]
659
  logger.info(f"🔮 Fallback: Used description '{description}' as item.")
660
 
@@ -663,7 +667,7 @@ def analyze_image():
663
 
664
  # 2. Logic Branching
665
  if img_type == "IRRELEVANT" and not items:
666
- # Graceful Rejection / Compliment
667
  prompt = f"User uploaded a photo of: {description}. If it is a pet/flower/view, compliment it warmly! Then effectively explain you are a shopping bot and can't price check that."
668
  response_text = gemini_chat_response(prompt, {"intent": "CASUAL_CHAT"}, {}, "")
669
 
@@ -671,17 +675,17 @@ def analyze_image():
671
  # Run the Analyst Engine
672
  analyst_data = calculate_basket_optimization(items)
673
 
674
- # 3. DYNAMIC SIMULATED INTENT
675
  if img_type == "MEAL":
676
- simulated_user_msg = f"I want to cook {description}. I need these ingredients: {', '.join(items)}. How much will it cost?"
677
  intent_sim = {"intent": "SHOPPING_BASKET"}
678
 
679
  elif img_type == "LIST":
680
- simulated_user_msg = f"Here is my shopping list: {', '.join(items)}. Which store is cheapest for the whole basket?"
681
  intent_sim = {"intent": "STORE_DECISION"}
682
 
683
  else: # PRODUCT
684
- simulated_user_msg = f"I see {description}. Which store has the cheapest price for it right now?"
685
  intent_sim = {"intent": "STORE_DECISION"}
686
 
687
  # Generate Response
@@ -689,11 +693,10 @@ def analyze_image():
689
  simulated_user_msg,
690
  intent_sim,
691
  analyst_data,
692
- chat_history="" # Fresh context
693
  )
694
 
695
  else:
696
- # Catch-all if something really weird happens (Product type but no description/items)
697
  response_text = "I couldn't quite identify the product in that image. Could you type the name for me?"
698
 
699
  return jsonify({
 
1
  """
2
+ main.py — Pricelyst Shopping Advisor (Jessica Edition 2026 - Upgrade v2.5)
3
 
4
+ ✅ Fixed: "Basket Regression" - AI now returns prices IMMEDIATELY.
5
+ ✅ Fixed: "Bluffing" - AI explicitly states if item is found or missing.
6
+ Optimization: Removed "Add to list" chatter. Shortest path to value.
7
  ✅ "Analyst Engine": Enhanced Basket Math, Category Context, ZESA Logic.
8
  ✅ "Visual Engine": Lists, Products, & Meal-to-Recipe recognition.
9
  ✅ Memory Logic: Short-Term Sliding Window (Last 6 messages).
 
303
  "product_id": int(best_prod['product_id']),
304
  "name": str(best_prod['product_name']),
305
  "category": str(best_prod['category']),
306
+ "retailer": str(best_prod['retailer']), # Added explicitly for prompt access
307
+ "price": float(best_prod['price']), # Added explicitly for prompt access
308
  "category_stats": cat_stats
309
  })
310
 
 
424
  Analyze transcript. Return STRICT JSON.
425
  Classify intent:
426
  - CASUAL_CHAT: Greetings, small talk, "hi", "thanks".
427
+ - SHOPPING_BASKET: Looking for prices, products, lists, or "cheapest X".
428
  - UTILITY_CALC: Electricity/ZESA questions.
429
  - STORE_DECISION: "Where should I buy?", "Which store is cheapest?".
430
  - TRUST_CHECK: "Is this expensive?", "Is this a good deal?".
431
 
432
  Extract:
433
+ - items: list of products found in the text.
434
  - utility_amount: number
 
435
 
436
  JSON Schema:
437
  {
438
  "actionable": boolean,
439
  "intent": "string",
440
  "items": ["string"],
441
+ "utility_amount": number
 
442
  }
443
  """
444
  try:
 
495
  context_str += f"ZIMBABWE CONTEXT: Fuel={ZIM_CONTEXT['fuel_petrol']}, ZESA Rate={ZIM_CONTEXT['zesa_step_1']['rate']}\n"
496
 
497
  if analyst_data:
498
+ context_str += f"ANALYST DATA (Prices/Availability): {json.dumps(analyst_data, default=str)}\n"
499
 
500
  PROMPT = f"""
501
  You are Jessica, Pricelyst's Shopping Advisor (Zimbabwe).
502
+ Role: Intelligent Shopping Companion.
503
+ Goal: Shortest path to value. Give answers, not promises.
504
 
505
  INPUT: "{transcript}"
506
  INTENT: {intent.get('intent')}
507
  CONTEXT:
508
  {context_str}
509
 
510
+ CRITICAL INSTRUCTIONS (Shortest Path Rule):
511
+ 1. **CHECK ANALYST DATA FIRST**:
512
+ - If `ANALYST DATA` contains `found_items_details` or `split_strategy` with prices: **REPORT THEM IMMEDIATELY**.
513
+ - Say: "I found [Product] at [Retailer] for $[Price]."
514
+ - Do NOT say "I will add this to your list."
515
+ - Do NOT say "I will check for you." (You have already checked!)
516
 
517
+ 2. **MISSING ITEMS**:
518
+ - If `global_missing` has items: Say "I checked, but we don't have [Item] in our current catalogue."
519
+ - Don't fake it. Be honest about catalogue gaps.
520
+
521
+ 3. **CASUAL CHAT**:
522
+ - Only if no products are mentioned. "Makadii! How can I help?"
523
+ - Reset topic if user says "Hi" or changes subject.
524
+
525
+ TONE: Helpful, direct, Zimbabwean. Use Markdown for prices (e.g. **$3.50**).
526
  """
527
 
528
  try:
 
533
  return resp.text
534
  except Exception as e:
535
  logger.error(f"Chat Gen Error: {e}")
536
+ return "I checked the prices, but I'm having trouble displaying them right now."
537
 
538
  def gemini_generate_4step_plan(transcript: str, analyst_result: Dict) -> str:
539
  if not _gemini_client: return "# Error\nAI Offline."
 
569
  "ok": True,
570
  "offers_indexed": len(df),
571
  "api_source": PRICE_API_BASE,
572
+ "persona": "Jessica v2.5 (Immediate Price Check)"
573
  })
574
 
575
  @app.post("/chat")
 
610
  analyst_data = {}
611
 
612
  # 3. Data Processing (The Analyst)
613
+ # Trigger Analyst if Items exist OR intent is specifically about shopping/decisions
614
+ if items or intent_type in ["SHOPPING_BASKET", "STORE_DECISION", "TRUST_CHECK"]:
615
  analyst_data = calculate_basket_optimization(items)
616
 
617
  elif intent_type == "UTILITY_CALC":
 
658
  description = vision_result.get("description", "an image")
659
 
660
  # Fallback: If type is PRODUCT/MEAL but items is empty, try to use description as search item
 
661
  if (img_type in ["PRODUCT", "MEAL"]) and not items and description:
 
 
662
  items = [description]
663
  logger.info(f"🔮 Fallback: Used description '{description}' as item.")
664
 
 
667
 
668
  # 2. Logic Branching
669
  if img_type == "IRRELEVANT" and not items:
670
+ # Graceful Rejection
671
  prompt = f"User uploaded a photo of: {description}. If it is a pet/flower/view, compliment it warmly! Then effectively explain you are a shopping bot and can't price check that."
672
  response_text = gemini_chat_response(prompt, {"intent": "CASUAL_CHAT"}, {}, "")
673
 
 
675
  # Run the Analyst Engine
676
  analyst_data = calculate_basket_optimization(items)
677
 
678
+ # 3. DYNAMIC SIMULATED INTENT (Force immediate answer)
679
  if img_type == "MEAL":
680
+ simulated_user_msg = f"I want to cook {description}. I need {', '.join(items)}. How much does it cost?"
681
  intent_sim = {"intent": "SHOPPING_BASKET"}
682
 
683
  elif img_type == "LIST":
684
+ simulated_user_msg = f"Here is my list: {', '.join(items)}. What are the prices?"
685
  intent_sim = {"intent": "STORE_DECISION"}
686
 
687
  else: # PRODUCT
688
+ simulated_user_msg = f"I see {description}. What is the price for {', '.join(items)}?"
689
  intent_sim = {"intent": "STORE_DECISION"}
690
 
691
  # Generate Response
 
693
  simulated_user_msg,
694
  intent_sim,
695
  analyst_data,
696
+ chat_history=""
697
  )
698
 
699
  else:
 
700
  response_text = "I couldn't quite identify the product in that image. Could you type the name for me?"
701
 
702
  return jsonify({