SamiKoen commited on
Commit
8d2f4ad
·
verified ·
1 Parent(s): f6c512e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -53
app.py CHANGED
@@ -51,6 +51,17 @@ def normalize_turkish(text):
51
  text = text.replace(tr_char, eng_char)
52
  return text
53
 
 
 
 
 
 
 
 
 
 
 
 
54
  def fetch_warehouse_inventory(warehouse, product_name, search_terms):
55
  """Tek bir mağazanın stok bilgisini al"""
56
  try:
@@ -62,7 +73,8 @@ def fetch_warehouse_inventory(warehouse, product_name, search_terms):
62
 
63
  # Mağaza stoklarını al
64
  inventory_url = f"{STOCK_API_BASE}?action=inventory&warehouse={warehouse_id}&endpoint=inventory/{warehouse_id}"
65
- inventory_response = requests.get(inventory_url, timeout=3, verify=False)
 
66
 
67
  if inventory_response.status_code != 200:
68
  return None
@@ -102,7 +114,6 @@ def fetch_warehouse_inventory(warehouse, product_name, search_terms):
102
  # Eğer sadece beden sorgusu ise
103
  if is_only_size_query:
104
  # Beden terimini ürün başlığında ara (parantez içinde veya dışında)
105
- # Örnek: "Marlin 5 (M)" veya "Marlin 5 M" veya "Marlin 5 - M"
106
  if size_query in product_title.split() or f'({size_query})' in product_title or f' {size_query} ' in product_title or product_title.endswith(f' {size_query}'):
107
  qty = int(product.get('qty', 0))
108
  stock = int(product.get('stock', 0))
@@ -217,7 +228,8 @@ def get_realtime_stock_parallel(product_name):
217
 
218
  # Önce mağaza listesini al
219
  warehouses_url = f"{STOCK_API_BASE}?action=warehouses&endpoint=warehouses"
220
- warehouses_response = requests.get(warehouses_url, timeout=3, verify=False)
 
221
 
222
  if warehouses_response.status_code != 200:
223
  print(f"Mağaza listesi alınamadı: {warehouses_response.status_code}")
@@ -297,7 +309,8 @@ def get_realtime_stock(product_name):
297
  try:
298
  # Önce mağaza listesini al - endpoint parametresi eklendi
299
  warehouses_url = f"{STOCK_API_BASE}?action=warehouses&endpoint=warehouses"
300
- warehouses_response = requests.get(warehouses_url, timeout=3, verify=False) # Timeout 3 saniye
 
301
 
302
  if warehouses_response.status_code != 200:
303
  print(f"Mağaza listesi alınamadı: {warehouses_response.status_code}")
@@ -330,7 +343,8 @@ def get_realtime_stock(product_name):
330
 
331
  # Mağaza stoklarını al - endpoint parametresi eklendi
332
  inventory_url = f"{STOCK_API_BASE}?action=inventory&warehouse={warehouse_id}&endpoint=inventory/{warehouse_id}"
333
- inventory_response = requests.get(inventory_url, timeout=3, verify=False) # Timeout 3 saniye
 
334
 
335
  if inventory_response.status_code != 200:
336
  continue
@@ -453,13 +467,21 @@ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
453
  if not OPENAI_API_KEY:
454
  print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
455
 
 
 
 
456
  # Trek bisiklet ürünlerini çekme
457
  url = 'https://www.trekbisiklet.com.tr/output/8582384479'
458
- response = requests.get(url, verify=False, timeout=60)
459
- if response.status_code == 200 and response.content:
460
- root = ET.fromstring(response.content)
461
- else:
462
- print(f"HTTP hatası: {response.status_code}")
 
 
 
 
 
463
  root = None
464
 
465
  products = []
@@ -476,9 +498,15 @@ if root is not None:
476
  name = name_words[0]
477
  full_name = ' '.join(name_words)
478
 
479
- stock_amount = "stokta" if stock_elem.text and stock_elem.text > '0' else "stokta değil"
 
 
 
 
 
480
 
481
- # Stokta olmayan ürünler için fiyat/link bilgisi eklemiyoruz
 
482
  if stock_amount == "stokta":
483
  # Normal fiyat bilgisini al - Güvenli versiyon
484
  price_elem = item.find('priceTaxWithCur')
@@ -492,42 +520,34 @@ if root is not None:
492
  price_rebate_elem = item.find('priceRebateWithTax')
493
  price_rebate_str = price_rebate_elem.text if price_rebate_elem is not None and price_rebate_elem.text else ""
494
 
495
- # Havale indirimi fiyatını al (havale indirimli kampanyalı fiyat) - Güvenli versiyon
496
  price_rebate_money_order_elem = item.find('priceRebateWithMoneyOrderWithTax')
497
  price_rebate_money_order_str = price_rebate_money_order_elem.text if price_rebate_money_order_elem is not None and price_rebate_money_order_elem.text else ""
498
 
499
  # Normal fiyatı yuvarla
500
  try:
501
  price_float = float(price_str)
502
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
503
  if price_float > 200000:
504
- price = str(round(price_float / 5000) * 5000) # En yakın 5000'e yuvarla
505
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
506
  elif price_float > 30000:
507
- price = str(round(price_float / 1000) * 1000) # En yakın 1000'e yuvarla
508
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
509
  elif price_float > 10000:
510
- price = str(round(price_float / 100) * 100) # En yakın 100'e yuvarla
511
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
512
  else:
513
- price = str(round(price_float / 10) * 10) # En yakın 10'a yuvarla
514
  except (ValueError, TypeError):
515
- price = price_str # Sayıya dönüştürülemezse olduğu gibi bırak
516
 
517
  # Havale indirimli orijinal fiyatı yuvarla (varsa)
518
  if price_eft_str:
519
  try:
520
  price_eft_float = float(price_eft_str)
521
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
522
  if price_eft_float > 200000:
523
  price_eft = str(round(price_eft_float / 5000) * 5000)
524
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
525
  elif price_eft_float > 30000:
526
  price_eft = str(round(price_eft_float / 1000) * 1000)
527
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
528
  elif price_eft_float > 10000:
529
  price_eft = str(round(price_eft_float / 100) * 100)
530
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
531
  else:
532
  price_eft = str(round(price_eft_float / 10) * 10)
533
  except (ValueError, TypeError):
@@ -539,16 +559,12 @@ if root is not None:
539
  if price_rebate_str:
540
  try:
541
  price_rebate_float = float(price_rebate_str)
542
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
543
  if price_rebate_float > 200000:
544
  price_rebate = str(round(price_rebate_float / 5000) * 5000)
545
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
546
  elif price_rebate_float > 30000:
547
  price_rebate = str(round(price_rebate_float / 1000) * 1000)
548
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
549
  elif price_rebate_float > 10000:
550
  price_rebate = str(round(price_rebate_float / 100) * 100)
551
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
552
  else:
553
  price_rebate = str(round(price_rebate_float / 10) * 10)
554
  except (ValueError, TypeError):
@@ -556,20 +572,16 @@ if root is not None:
556
  else:
557
  price_rebate = ""
558
 
559
- # Havale indirimli kampanyalı fiyatı yuvarla (varsa)
560
  if price_rebate_money_order_str:
561
  try:
562
  price_rebate_money_order_float = float(price_rebate_money_order_str)
563
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
564
  if price_rebate_money_order_float > 200000:
565
  price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
566
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
567
  elif price_rebate_money_order_float > 30000:
568
  price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
569
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
570
  elif price_rebate_money_order_float > 10000:
571
  price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
572
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
573
  else:
574
  price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
575
  except (ValueError, TypeError):
@@ -660,14 +672,14 @@ def download_documents_from_drive():
660
  file_io.seek(0)
661
  doc = Document(file_io)
662
 
663
- content = f"\\n=== {file['name']} ===\\n"
664
  for paragraph in doc.paragraphs:
665
  if paragraph.text.strip():
666
- content += paragraph.text + "\\n"
667
 
668
  all_content.append(content)
669
 
670
- document_content = "\\n".join(all_content)
671
  print(f"Toplam {len(files)} döküman yüklendi.")
672
 
673
  except Exception as e:
@@ -683,7 +695,7 @@ def clear_log_file():
683
  if os.path.exists(LOG_FILE):
684
  with file_lock:
685
  with open(LOG_FILE, 'w', encoding='utf-8') as f:
686
- f.write("Log dosyası temizlendi.\\n")
687
  print("Log dosyası temizlendi.")
688
  except Exception as e:
689
  print(f"Log dosyası temizleme hatası: {e}")
@@ -696,11 +708,68 @@ def run_scheduler(chat_history):
696
  schedule.run_pending()
697
  time.sleep(60)
698
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
699
  # Chatbot fonksiyonu - STOK API ENTEGRASYONU EKLENDİ
700
  def chatbot_fn(user_message, history, image=None):
701
  if history is None:
702
  history = []
703
 
 
 
 
 
 
 
 
704
  try:
705
  # Enhanced features - Görsel işleme
706
  if image is not None:
@@ -719,7 +788,7 @@ def chatbot_fn(user_message, history, image=None):
719
  try:
720
  with file_lock:
721
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
722
- f.write(f"User: {user_message}\\n")
723
  except Exception as e:
724
  print(f"Dosya yazma hatası (Kullanıcı): {e}")
725
 
@@ -730,12 +799,11 @@ def chatbot_fn(user_message, history, image=None):
730
  if document_content:
731
  system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content[:3000]}"}) # Token limiti için kısalt
732
 
733
- # STOK SORGUSU KONTROLÜ - YENİ EKLENEN KISIM
734
- if is_stock_query(user_message):
735
  print("Stok sorgusu algılandı, API'den veri çekiliyor...")
736
 
737
  # Mesajdan ürün adını çıkarmaya çalış
738
- # Basit bir yaklaşım: stok kelimelerini temizleyip kalan kelimeleri ürün adı olarak kullan
739
  product_words = []
740
  skip_words = ['stok', 'stock', 'kaç', 'adet', 'tane', 'var', 'mı', 'mi',
741
  'mevcut', 'mu', 'bulunuyor', 'hangi', 'mağaza', 'nerede',
@@ -845,7 +913,7 @@ def chatbot_fn(user_message, history, image=None):
845
 
846
  # Fiyat bilgisi varsa al
847
  if len(product_info[1]) > 1 and product_info[1][1]:
848
- normal_price = f"\\nFiyat: {product_info[1][1]} TL"
849
  print(f"Fiyat bulundu: {product_info[1][1]}")
850
  else:
851
  normal_price = ""
@@ -857,7 +925,7 @@ def chatbot_fn(user_message, history, image=None):
857
  rebate_money_order_price = ""
858
 
859
  if len(product_info[1]) > 4 and product_info[1][4] and product_info[1][4] != "":
860
- rebate_price = f"\\nKampanyalı fiyat: {product_info[1][4]} TL"
861
 
862
  try:
863
  normal_price_float = float(product_info[1][1])
@@ -874,26 +942,26 @@ def chatbot_fn(user_message, history, image=None):
874
  else:
875
  discount_amount_rounded = round(discount_amount / 10) * 10
876
 
877
- discount_info = f"\\nYapılan indirim: {discount_amount_rounded:.0f} TL"
878
  except (ValueError, TypeError):
879
  discount_info = ""
880
 
881
  rebate_money_order_price = ""
882
  else:
883
  if product_info[1][3] and product_info[1][3] != "":
884
- eft_price = f"\\nHavale indirimli fiyat: {product_info[1][3]} TL"
885
 
886
- product_link = f"\\nÜrün linki: {product_info[1][2]}"
887
  product_image = ""
888
  if len(product_info[1]) > 6 and product_info[1][6]:
889
- product_image = f"\\nÜrün resmi: {product_info[1][6]}"
890
 
891
  # API'den stok alındıysa sadece fiyat/görsel/link ekle
892
  if has_stock_from_api:
893
- new_msg = f"ÜRÜN BİLGİLERİ (Fiyat/Link):\\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}{product_image}"
894
  else:
895
  # API'den stok alınmadıysa hem stok hem fiyat bilgisi ekle
896
- new_msg = f"{product_info[2]} {product_info[1][0]}\\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}{product_image}"
897
  else:
898
  # Stokta değilse
899
  if has_stock_from_api:
@@ -963,7 +1031,7 @@ def chatbot_fn(user_message, history, image=None):
963
  try:
964
  with file_lock:
965
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
966
- f.write(f"Bot: {partial_response}\\n")
967
  except Exception as e:
968
  print(f"Dosya yazma hatası (Bot): {e}")
969
 
@@ -1247,4 +1315,4 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1247
  msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True)
1248
 
1249
  if __name__ == "__main__":
1250
- demo.launch(debug=True)
 
51
  text = text.replace(tr_char, eng_char)
52
  return text
53
 
54
+ # NEW: Basit retry/backoff'lu güvenli GET wrapper
55
+ def safe_get(url, timeout=5, retries=2, verify=False, **kwargs):
56
+ last_exc = None
57
+ for i in range(retries + 1):
58
+ try:
59
+ return requests.get(url, timeout=timeout, verify=verify, **kwargs)
60
+ except requests.RequestException as e:
61
+ last_exc = e
62
+ time.sleep(0.3 * (2 ** i))
63
+ raise last_exc
64
+
65
  def fetch_warehouse_inventory(warehouse, product_name, search_terms):
66
  """Tek bir mağazanın stok bilgisini al"""
67
  try:
 
73
 
74
  # Mağaza stoklarını al
75
  inventory_url = f"{STOCK_API_BASE}?action=inventory&warehouse={warehouse_id}&endpoint=inventory/{warehouse_id}"
76
+ # CHANGED: requests.get -> safe_get
77
+ inventory_response = safe_get(inventory_url, timeout=5, verify=False)
78
 
79
  if inventory_response.status_code != 200:
80
  return None
 
114
  # Eğer sadece beden sorgusu ise
115
  if is_only_size_query:
116
  # Beden terimini ürün başlığında ara (parantez içinde veya dışında)
 
117
  if size_query in product_title.split() or f'({size_query})' in product_title or f' {size_query} ' in product_title or product_title.endswith(f' {size_query}'):
118
  qty = int(product.get('qty', 0))
119
  stock = int(product.get('stock', 0))
 
228
 
229
  # Önce mağaza listesini al
230
  warehouses_url = f"{STOCK_API_BASE}?action=warehouses&endpoint=warehouses"
231
+ # CHANGED: requests.get -> safe_get
232
+ warehouses_response = safe_get(warehouses_url, timeout=5, verify=False)
233
 
234
  if warehouses_response.status_code != 200:
235
  print(f"Mağaza listesi alınamadı: {warehouses_response.status_code}")
 
309
  try:
310
  # Önce mağaza listesini al - endpoint parametresi eklendi
311
  warehouses_url = f"{STOCK_API_BASE}?action=warehouses&endpoint=warehouses"
312
+ # CHANGED: requests.get -> safe_get
313
+ warehouses_response = safe_get(warehouses_url, timeout=5, verify=False) # Timeout 5 saniye
314
 
315
  if warehouses_response.status_code != 200:
316
  print(f"Mağaza listesi alınamadı: {warehouses_response.status_code}")
 
343
 
344
  # Mağaza stoklarını al - endpoint parametresi eklendi
345
  inventory_url = f"{STOCK_API_BASE}?action=inventory&warehouse={warehouse_id}&endpoint=inventory/{warehouse_id}"
346
+ # CHANGED: requests.get -> safe_get
347
+ inventory_response = safe_get(inventory_url, timeout=5, verify=False) # Timeout 5 saniye
348
 
349
  if inventory_response.status_code != 200:
350
  continue
 
467
  if not OPENAI_API_KEY:
468
  print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
469
 
470
+ # NEW: V1 bayrağı (stok sorularında LLM bypass)
471
+ GROUND_STOCK_ONLY = os.getenv("GROUND_STOCK_ONLY", "1") == "1"
472
+
473
  # Trek bisiklet ürünlerini çekme
474
  url = 'https://www.trekbisiklet.com.tr/output/8582384479'
475
+ # CHANGED: requests.get -> safe_get
476
+ try:
477
+ response = safe_get(url, verify=False, timeout=60)
478
+ if response.status_code == 200 and response.content:
479
+ root = ET.fromstring(response.content)
480
+ else:
481
+ print(f"HTTP hatası: {response.status_code}")
482
+ root = None
483
+ except Exception as e:
484
+ print(f"XML çekme hatası: {e}")
485
  root = None
486
 
487
  products = []
 
498
  name = name_words[0]
499
  full_name = ' '.join(name_words)
500
 
501
+ # CHANGED: stok kontrolü int'e çevrildi
502
+ try:
503
+ stock_val = int((stock_elem.text or "0").strip())
504
+ except (ValueError, TypeError):
505
+ stock_val = 0
506
+ stock_amount = "stokta" if stock_val > 0 else "stokta değil"
507
 
508
+ # Stokta olmayan ürünler için fiyat/link bilgisi yine saklanıyor (aşağıda)
509
+ # Stokta olanlar için detaylar
510
  if stock_amount == "stokta":
511
  # Normal fiyat bilgisini al - Güvenli versiyon
512
  price_elem = item.find('priceTaxWithCur')
 
520
  price_rebate_elem = item.find('priceRebateWithTax')
521
  price_rebate_str = price_rebate_elem.text if price_rebate_elem is not None and price_rebate_elem.text else ""
522
 
523
+ # Havale indirimli kampanyalı fiyatı - Güvenli versiyon
524
  price_rebate_money_order_elem = item.find('priceRebateWithMoneyOrderWithTax')
525
  price_rebate_money_order_str = price_rebate_money_order_elem.text if price_rebate_money_order_elem is not None and price_rebate_money_order_elem.text else ""
526
 
527
  # Normal fiyatı yuvarla
528
  try:
529
  price_float = float(price_str)
 
530
  if price_float > 200000:
531
+ price = str(round(price_float / 5000) * 5000)
 
532
  elif price_float > 30000:
533
+ price = str(round(price_float / 1000) * 1000)
 
534
  elif price_float > 10000:
535
+ price = str(round(price_float / 100) * 100)
 
536
  else:
537
+ price = str(round(price_float / 10) * 10)
538
  except (ValueError, TypeError):
539
+ price = price_str
540
 
541
  # Havale indirimli orijinal fiyatı yuvarla (varsa)
542
  if price_eft_str:
543
  try:
544
  price_eft_float = float(price_eft_str)
 
545
  if price_eft_float > 200000:
546
  price_eft = str(round(price_eft_float / 5000) * 5000)
 
547
  elif price_eft_float > 30000:
548
  price_eft = str(round(price_eft_float / 1000) * 1000)
 
549
  elif price_eft_float > 10000:
550
  price_eft = str(round(price_eft_float / 100) * 100)
 
551
  else:
552
  price_eft = str(round(price_eft_float / 10) * 10)
553
  except (ValueError, TypeError):
 
559
  if price_rebate_str:
560
  try:
561
  price_rebate_float = float(price_rebate_str)
 
562
  if price_rebate_float > 200000:
563
  price_rebate = str(round(price_rebate_float / 5000) * 5000)
 
564
  elif price_rebate_float > 30000:
565
  price_rebate = str(round(price_rebate_float / 1000) * 1000)
 
566
  elif price_rebate_float > 10000:
567
  price_rebate = str(round(price_rebate_float / 100) * 100)
 
568
  else:
569
  price_rebate = str(round(price_rebate_float / 10) * 10)
570
  except (ValueError, TypeError):
 
572
  else:
573
  price_rebate = ""
574
 
575
+ # Havale indirimli kampanyalı fiyatı (varsa)
576
  if price_rebate_money_order_str:
577
  try:
578
  price_rebate_money_order_float = float(price_rebate_money_order_str)
 
579
  if price_rebate_money_order_float > 200000:
580
  price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
 
581
  elif price_rebate_money_order_float > 30000:
582
  price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
 
583
  elif price_rebate_money_order_float > 10000:
584
  price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
 
585
  else:
586
  price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
587
  except (ValueError, TypeError):
 
672
  file_io.seek(0)
673
  doc = Document(file_io)
674
 
675
+ content = f"\n=== {file['name']} ===\n"
676
  for paragraph in doc.paragraphs:
677
  if paragraph.text.strip():
678
+ content += paragraph.text + "\n"
679
 
680
  all_content.append(content)
681
 
682
+ document_content = "\n".join(all_content)
683
  print(f"Toplam {len(files)} döküman yüklendi.")
684
 
685
  except Exception as e:
 
695
  if os.path.exists(LOG_FILE):
696
  with file_lock:
697
  with open(LOG_FILE, 'w', encoding='utf-8') as f:
698
+ f.write("Log dosyası temizlendi.\n")
699
  print("Log dosyası temizlendi.")
700
  except Exception as e:
701
  print(f"Log dosyası temizleme hatası: {e}")
 
708
  schedule.run_pending()
709
  time.sleep(60)
710
 
711
+ # NEW: Stok sorularına deterministik, tamamen veri tabanlı yanıt
712
+ def answer_stock_grounded(user_message):
713
+ """
714
+ Stok sorularına tamamen veri-tabanlı, deterministik yanıt üretir.
715
+ Yalnızca API + XML kullanır, LLM'ye gitmez.
716
+ """
717
+ # 1) Ürün adı çıkarma (mevcut mantığın güvenli versiyonu)
718
+ skip_words = ['stok','stock','kaç','adet','tane','var','mı','mi','mevcut','mu','bulunuyor',
719
+ 'hangi','mağaza','nerede','durumu','stoklarda','stokta',
720
+ 'için','ve','ile','toplam','toplamda','fiyat','fiyatı','ne','nedir','kadar',
721
+ 'beden','bedeni','bedenli']
722
+ words = [w for w in user_message.lower().split() if w not in skip_words]
723
+ if not words:
724
+ return "Hangi ürün için stok bakmamı istersiniz?"
725
+
726
+ # Sadece beden mi sorulmuş?
727
+ size_terms = {'xs','s','m','ml','l','xl','xxl','2xl','3xl','small','medium','large',
728
+ '44','46','48','50','52','54','56','58','60'}
729
+ is_only_size = (len(words) == 1 and words[0] in size_terms)
730
+ product_name = words[0] if is_only_size else ' '.join(words)
731
+
732
+ # 2) Gerçek zamanlı stok (paralel)
733
+ stock_text = get_realtime_stock_parallel(product_name)
734
+ if not stock_text:
735
+ return f"'{product_name}' için stok bilgisine şu an erişemedim."
736
+
737
+ # 3) XML’den (varsa) fiyat/link/resim ekle (sadece veride olanı)
738
+ norm_query = normalize_turkish(product_name).replace(" ", "")
739
+ extra_lines = []
740
+ added = 0
741
+ try:
742
+ for name, info, full_name in products:
743
+ if added >= 3: # Aşırı büyümeyi engelle
744
+ break
745
+ norm_full = normalize_turkish(full_name).replace(" ", "")
746
+ if norm_query and norm_query in norm_full:
747
+ normal_price = f"\nFiyat: {info[1]} TL" if len(info) > 1 and info[1] else ""
748
+ product_link = f"\nÜrün linki: {info[2]}" if len(info) > 2 and info[2] else ""
749
+ product_image = f"\nÜrün resmi: {info[6]}" if len(info) > 6 and info[6] else ""
750
+ eft = f"\nHavale indirimi: {info[3]} TL" if len(info) > 3 and info[3] else ""
751
+ rebate = f"\nKampanyalı fiyat: {info[4]} TL" if len(info) > 4 and info[4] else ""
752
+ rebate_mo = f"\nHavale kampanyalı: {info[5]} TL" if len(info) > 5 and info[5] else ""
753
+ extra_lines.append(f"{full_name} (veriden):{normal_price}{rebate}{eft}{rebate_mo}{product_link}{product_image}")
754
+ added += 1
755
+ except Exception:
756
+ pass
757
+
758
+ extra_block = ("\n\n" + "\n---\n".join(extra_lines)) if extra_lines else ""
759
+ return f"{stock_text}{extra_block}"
760
+
761
  # Chatbot fonksiyonu - STOK API ENTEGRASYONU EKLENDİ
762
  def chatbot_fn(user_message, history, image=None):
763
  if history is None:
764
  history = []
765
 
766
+ # NEW: Grounded stock only — stok sorgusuysa LLM'yi tamamen bypass
767
+ if is_stock_query(user_message) and GROUND_STOCK_ONLY:
768
+ print("Grounded stok modu: LLM devre dışı, veri tabanlı yanıt oluşturuluyor...")
769
+ deterministic_answer = answer_stock_grounded(user_message)
770
+ yield deterministic_answer
771
+ return
772
+
773
  try:
774
  # Enhanced features - Görsel işleme
775
  if image is not None:
 
788
  try:
789
  with file_lock:
790
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
791
+ f.write(f"User: {user_message}\n")
792
  except Exception as e:
793
  print(f"Dosya yazma hatası (Kullanıcı): {e}")
794
 
 
799
  if document_content:
800
  system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content[:3000]}"}) # Token limiti için kısalt
801
 
802
+ # STOK SORGUSU KONTROLÜ - (Eski yol) sadece GROUND_STOCK_ONLY kapalıysa
803
+ if is_stock_query(user_message) and not GROUND_STOCK_ONLY:
804
  print("Stok sorgusu algılandı, API'den veri çekiliyor...")
805
 
806
  # Mesajdan ürün adını çıkarmaya çalış
 
807
  product_words = []
808
  skip_words = ['stok', 'stock', 'kaç', 'adet', 'tane', 'var', 'mı', 'mi',
809
  'mevcut', 'mu', 'bulunuyor', 'hangi', 'mağaza', 'nerede',
 
913
 
914
  # Fiyat bilgisi varsa al
915
  if len(product_info[1]) > 1 and product_info[1][1]:
916
+ normal_price = f"\nFiyat: {product_info[1][1]} TL"
917
  print(f"Fiyat bulundu: {product_info[1][1]}")
918
  else:
919
  normal_price = ""
 
925
  rebate_money_order_price = ""
926
 
927
  if len(product_info[1]) > 4 and product_info[1][4] and product_info[1][4] != "":
928
+ rebate_price = f"\nKampanyalı fiyat: {product_info[1][4]} TL"
929
 
930
  try:
931
  normal_price_float = float(product_info[1][1])
 
942
  else:
943
  discount_amount_rounded = round(discount_amount / 10) * 10
944
 
945
+ discount_info = f"\nYapılan indirim: {discount_amount_rounded:.0f} TL"
946
  except (ValueError, TypeError):
947
  discount_info = ""
948
 
949
  rebate_money_order_price = ""
950
  else:
951
  if product_info[1][3] and product_info[1][3] != "":
952
+ eft_price = f"\nHavale indirimli fiyat: {product_info[1][3]} TL"
953
 
954
+ product_link = f"\nÜrün linki: {product_info[1][2]}"
955
  product_image = ""
956
  if len(product_info[1]) > 6 and product_info[1][6]:
957
+ product_image = f"\nÜrün resmi: {product_info[1][6]}"
958
 
959
  # API'den stok alındıysa sadece fiyat/görsel/link ekle
960
  if has_stock_from_api:
961
+ new_msg = f"ÜRÜN BİLGİLERİ (Fiyat/Link):\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}{product_image}"
962
  else:
963
  # API'den stok alınmadıysa hem stok hem fiyat bilgisi ekle
964
+ new_msg = f"{product_info[2]} {product_info[1][0]}\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}{product_image}"
965
  else:
966
  # Stokta değilse
967
  if has_stock_from_api:
 
1031
  try:
1032
  with file_lock:
1033
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
1034
+ f.write(f"Bot: {partial_response}\n")
1035
  except Exception as e:
1036
  print(f"Dosya yazma hatası (Bot): {e}")
1037
 
 
1315
  msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True)
1316
 
1317
  if __name__ == "__main__":
1318
+ demo.launch(debug=True)