SamiKoen commited on
Commit
8b6b7a8
·
verified ·
1 Parent(s): 00c226f

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +695 -894
app.py CHANGED
@@ -1,895 +1,696 @@
1
- import gradio as gr
2
- import os
3
- import json
4
- import requests
5
- import xml.etree.ElementTree as ET
6
- import schedule
7
- import time
8
- import threading
9
- import uuid
10
- import hashlib
11
- from huggingface_hub import HfApi, create_repo, hf_hub_download
12
- import warnings
13
- import pandas as pd
14
- from docx import Document
15
- import spaces
16
- from google.oauth2.service_account import Credentials
17
- from googleapiclient.discovery import build
18
- from googleapiclient.http import MediaIoBaseDownload
19
- import io
20
- import warnings
21
- from requests.packages.urllib3.exceptions import InsecureRequestWarning
22
- warnings.simplefilter('ignore', InsecureRequestWarning)
23
-
24
- # Prompt dosyasını import et
25
- from prompts import get_prompt_content_only
26
-
27
- # Enhanced features import et
28
- from enhanced_features import (
29
- initialize_enhanced_features, process_image_message,
30
- handle_comparison_request, get_user_recommendations, profile_manager,
31
- get_user_profile_summary, get_user_chat_context
32
- )
33
- from image_renderer import extract_product_info_for_gallery, format_message_with_images
34
-
35
- # Gradio uyarılarını bastır
36
- warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
37
-
38
- # Log dosyası adı ve yolu
39
- LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
40
- print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}")
41
-
42
- # API ayarları
43
- API_URL = "https://api.openai.com/v1/chat/completions"
44
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
45
- if not OPENAI_API_KEY:
46
- print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
47
-
48
- # Trek bisiklet ürünlerini çekme
49
- url = 'https://www.trekbisiklet.com.tr/output/8582384479'
50
- response = requests.get(url, verify=False, timeout=30)
51
- if response.status_code == 200 and response.content:
52
- root = ET.fromstring(response.content)
53
- else:
54
- print(f"HTTP hatası: {response.status_code}")
55
- root = None
56
-
57
- products = []
58
- if root is not None:
59
- for item in root.findall('item'):
60
- # Tüm ürünleri al, sonra stokta olma durumunu kontrol et
61
- name_words = item.find('rootlabel').text.lower().split()
62
- name = name_words[0]
63
- full_name = ' '.join(name_words)
64
-
65
- stock_amount = "stokta" if item.find('stockAmount').text > '0' else "stokta değil"
66
-
67
- # Stokta olmayan ürünler için fiyat/link bilgisi eklemiyoruz
68
- if stock_amount == "stokta":
69
- # Normal fiyat bilgisini al
70
- price_str = item.find('priceTaxWithCur').text if item.find('priceTaxWithCur') is not None else "Fiyat bilgisi yok"
71
-
72
- # EFT fiyatını al (havale indirimli orijinal fiyat)
73
- price_eft_str = item.find('priceEft').text if item.find('priceEft') is not None else ""
74
-
75
- # İndirimli fiyatı al (kampanyalı fiyat)
76
- price_rebate_str = item.find('priceRebateWithTax').text if item.find('priceRebateWithTax') is not None else ""
77
-
78
- # Havale indirimi fiyatını al (havale indirimli kampanyalı fiyat)
79
- price_rebate_money_order_str = item.find('priceRebateWithMoneyOrderWithTax').text if item.find('priceRebateWithMoneyOrderWithTax') is not None else ""
80
-
81
- # Normal fiyatı yuvarla
82
- try:
83
- price_float = float(price_str)
84
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
85
- if price_float > 200000:
86
- price = str(round(price_float / 5000) * 5000) # En yakın 5000'e yuvarla
87
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
88
- elif price_float > 30000:
89
- price = str(round(price_float / 1000) * 1000) # En yakın 1000'e yuvarla
90
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
91
- elif price_float > 10000:
92
- price = str(round(price_float / 100) * 100) # En yakın 100'e yuvarla
93
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
94
- else:
95
- price = str(round(price_float / 10) * 10) # En yakın 10'a yuvarla
96
- except (ValueError, TypeError):
97
- price = price_str # Sayıya dönüştürülemezse olduğu gibi bırak
98
-
99
- # Havale indirimli orijinal fiyatı yuvarla (varsa)
100
- if price_eft_str:
101
- try:
102
- price_eft_float = float(price_eft_str)
103
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
104
- if price_eft_float > 200000:
105
- price_eft = str(round(price_eft_float / 5000) * 5000)
106
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
107
- elif price_eft_float > 30000:
108
- price_eft = str(round(price_eft_float / 1000) * 1000)
109
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
110
- elif price_eft_float > 10000:
111
- price_eft = str(round(price_eft_float / 100) * 100)
112
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
113
- else:
114
- price_eft = str(round(price_eft_float / 10) * 10)
115
- except (ValueError, TypeError):
116
- price_eft = price_eft_str
117
- else:
118
- # Havale indirimli fiyat verilmemişse, orijinal fiyattan %2.5 indirim hesapla
119
- try:
120
- price_eft_float = price_float * 0.975 # %2.5 indirim
121
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
122
- if price_eft_float > 200000:
123
- price_eft = str(round(price_eft_float / 5000) * 5000)
124
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
125
- elif price_eft_float > 30000:
126
- price_eft = str(round(price_eft_float / 1000) * 1000)
127
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
128
- elif price_eft_float > 10000:
129
- price_eft = str(round(price_eft_float / 100) * 100)
130
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
131
- else:
132
- price_eft = str(round(price_eft_float / 10) * 10)
133
- except (ValueError, TypeError):
134
- price_eft = ""
135
-
136
- # İndirimli fiyatı yuvarla
137
- try:
138
- if price_rebate_str:
139
- price_rebate_float = float(price_rebate_str)
140
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
141
- if price_rebate_float > 200000:
142
- price_rebate = str(round(price_rebate_float / 5000) * 5000)
143
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
144
- elif price_rebate_float > 30000:
145
- price_rebate = str(round(price_rebate_float / 1000) * 1000)
146
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
147
- elif price_rebate_float > 10000:
148
- price_rebate = str(round(price_rebate_float / 100) * 100)
149
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
150
- else:
151
- price_rebate = str(round(price_rebate_float / 10) * 10)
152
- else:
153
- price_rebate = ""
154
- except (ValueError, TypeError):
155
- price_rebate = price_rebate_str
156
-
157
- # Havale indirimi kampanyalı fiyatı yuvarla
158
- try:
159
- if price_rebate_money_order_str:
160
- price_rebate_money_order_float = float(price_rebate_money_order_str)
161
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
162
- if price_rebate_money_order_float > 200000:
163
- price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
164
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
165
- elif price_rebate_money_order_float > 30000:
166
- price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
167
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
168
- elif price_rebate_money_order_float > 10000:
169
- price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
170
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
171
- else:
172
- price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
173
- else:
174
- price_rebate_money_order = ""
175
- except (ValueError, TypeError):
176
- price_rebate_money_order = price_rebate_money_order_str
177
-
178
- # Ürün linkini ve resim URL'sini al
179
- product_link = item.find('productLink').text if item.find('productLink') is not None else ""
180
- image_url = item.find('picture1Path').text if item.find('picture1Path') is not None else ""
181
-
182
- # Tüm fiyat bilgilerini birleştir (resim URL'si eklendi)
183
- item_info = (stock_amount, price, product_link, price_eft, price_rebate, price_rebate_money_order, image_url)
184
- else:
185
- # Stokta olmayan ürünler için sadece stok durumunu belirt, fiyat ve link bilgisi verme
186
- item_info = (stock_amount, "", "", "", "", "", "")
187
-
188
- products.append((name, item_info, full_name))
189
-
190
- # Debug: Ürün sayısını yazdır
191
- print(f"Toplam {len(products)} ürün yüklendi")
192
- if products:
193
- print(f"İlk ürün örneği: {products[0]}")
194
-
195
- # Enhanced features'ları başlat
196
- initialize_enhanced_features(OPENAI_API_KEY, products)
197
-
198
- # Hugging Face token
199
- hfapi = os.getenv("hfapi")
200
- if not hfapi:
201
- raise ValueError("hfapi ortam değişkeni ayarlanmamış!")
202
-
203
- create_repo("BF", token=hfapi, repo_type="space", space_sdk="gradio", exist_ok=True)
204
-
205
- global_chat_history = [] # Tüm sohbet geçmişi
206
- history_lock = threading.Lock() # Global geçmiş için kilit
207
- file_lock = threading.Lock() # Dosya yazma için kilit
208
- last_logged_index = 0 # Son kaydedilen mesaj indeksi
209
-
210
- # Google Drive kimlik doğrulama (Hizmet Hesabı Secrets'tan)
211
- SCOPES = ['https://www.googleapis.com/auth/drive.readonly']
212
-
213
- def authenticate_google_drive():
214
- service_account_json = os.getenv("SERVICE_ACCOUNT_JSON")
215
- if not service_account_json:
216
- raise ValueError("SERVICE_ACCOUNT_JSON ortam değişkeni bulunamadı!")
217
-
218
- try:
219
- json_data = json.loads(service_account_json)
220
- print("JSON başarıyla ayrıştırıldı:", json_data.keys())
221
- print("Private key içeriği:", json_data.get("private_key"))
222
- creds = Credentials.from_service_account_info(json_data, scopes=SCOPES)
223
- except json.JSONDecodeError as e:
224
- raise ValueError(f"Secrets'tan alınan JSON geçersiz: {e}")
225
- except Exception as e:
226
- raise ValueError(f"Kimlik doğrulama hatası: {e}")
227
-
228
- return build('drive', 'v3', credentials=creds)
229
-
230
- def download_file_from_drive(service, file_id, file_name):
231
- request = service.files().get_media(fileId=file_id)
232
- fh = io.BytesIO()
233
- downloader = MediaIoBaseDownload(fh, request)
234
- done = False
235
- while done is False:
236
- status, done = downloader.next_chunk()
237
- fh.seek(0)
238
- with open(file_name, 'wb') as f:
239
- f.write(fh.read())
240
- return file_name
241
-
242
- # Dökümanları Google Drive'dan çekme ve işleme
243
- document_content = ""
244
- service = authenticate_google_drive()
245
-
246
- try:
247
- # Google Drive'dan Excel dosyası
248
- excel_file_id = "test10rgiGp5y5ZYU0dpRvps2t0t1dEzjW8LK" # Örnek Excel dosya ID'si, kendi ID'nizi ekleyin
249
- excel_file = download_file_from_drive(service, excel_file_id, "veriler.xlsx")
250
- df = pd.read_excel(excel_file)
251
- excel_text = df.to_string()
252
- document_content += excel_text + "\n"
253
- except Exception as e:
254
- print(f"Google Drive Excel okuma hatası: {e}")
255
-
256
- try:
257
- # Google Drive'dan Word dosyası
258
- word_file_id = "9I8H7G6F5E4D3C2B1A" # Örnek Word dosya ID'si, kendi ID'nizi ekleyin
259
- word_file = download_file_from_drive(service, word_file_id, "aciklamalar.docx")
260
- doc = Document(word_file)
261
- word_text = "\n".join([para.text for para in doc.paragraphs])
262
- document_content += word_text
263
- except Exception as e:
264
- print(f"Google Drive Word okuma hatası: {e}")
265
-
266
- def run_scheduler(chat_history_ref):
267
- def scheduled_save_and_upload():
268
- global last_logged_index
269
- if chat_history_ref:
270
- print(f"Zamanlayıcı tetiklendi: {time.strftime('%Y-%m-%d %H:%M:%S')}")
271
- try:
272
- with file_lock: # Dosya yazma kilidi
273
- with open(LOG_FILE, 'a', encoding='utf-8') as f:
274
- f.write("\n--- Zamanlanmış Kayıt: {} ---\n".format(time.strftime("%Y-%m-%d %H:%M:%S")))
275
- with history_lock: # Geçmiş kilidi
276
- new_messages = chat_history_ref[last_logged_index:]
277
- for msg in new_messages:
278
- if msg["role"] in ["user", "assistant"]:
279
- f.write(f"{msg['role'].capitalize()}: {msg['content']}\n")
280
- last_logged_index = len(chat_history_ref) # Son indeksi güncelle
281
- print(f"Sohbet dosyaya kaydedildi: {os.path.abspath(LOG_FILE)}")
282
- except Exception as e:
283
- print(f"Kayıt hatası: {e}")
284
- time.sleep(5) # Hata sonrası tekrar denemeden önce bekle
285
- return # Tekrar deneme için erken çıkış
286
-
287
- HF_REPO_ID = "SamiKoen/BF"
288
- api = HfApi(token=hfapi)
289
- for attempt in range(3): # 3 kez tekrar deneme
290
- try:
291
- with file_lock:
292
- api.upload_file(
293
- path_or_fileobj=LOG_FILE,
294
- path_in_repo="chat_logs.txt",
295
- repo_id=HF_REPO_ID,
296
- repo_type="space",
297
- commit_message="Otomatik log güncellemesi - {}".format(time.strftime("%Y-%m-%d %H:%M:%S"))
298
- )
299
- print(f"Log dosyası HF'ye yüklendi: {LOG_FILE}")
300
- break
301
- except Exception as e:
302
- print(f"HF yükleme hatası (deneme {attempt+1}/3): {e}")
303
- time.sleep(5)
304
- else:
305
- print("HF yükleme başarısız, tüm denemeler tamamlandı.")
306
- print(f"Zamanlanmış işlem tamamlandı: {time.strftime('%H:%M:%S')}")
307
-
308
- schedule.every().day.at("11:32").do(scheduled_save_and_upload)
309
- schedule.every().day.at("15:15").do(scheduled_save_and_upload)
310
- schedule.every().day.at("15:30").do(scheduled_save_and_upload)
311
- schedule.every().day.at("17:32").do(scheduled_save_and_upload)
312
- schedule.every().day.at("19:15").do(scheduled_save_and_upload)
313
- schedule.every().day.at("21:30").do(scheduled_save_and_upload)
314
- print("Zamanlayıcı başlatıldı")
315
- while True:
316
- schedule.run_pending()
317
- time.sleep(60)
318
-
319
- @spaces.GPU(duration=1200)
320
- def chatbot_fn(user_message, history, image=None):
321
- if history is None:
322
- history = []
323
-
324
- try:
325
- # Enhanced features - Görsel işleme
326
- if image is not None:
327
- user_message = process_image_message(image, user_message)
328
-
329
- # Enhanced features - Karşılaştırma kontrolü
330
- comparison_result = handle_comparison_request(user_message)
331
- if comparison_result:
332
- yield comparison_result
333
- return
334
-
335
- # Enhanced features - Kişisel öneriler
336
- user_id = getattr(chatbot_fn, '_current_session_id', 'default_user')
337
-
338
- # Profil sorguları kontrol et
339
- if any(keyword in user_message.lower() for keyword in ["profil", "tercih", "geçmiş", "hatırla", "kaydet"]):
340
- profile_summary = get_user_profile_summary(user_id)
341
- yield profile_summary
342
- return
343
-
344
- recommendation_result = get_user_recommendations(user_id, user_message)
345
- if recommendation_result:
346
- # Kullanıcı etkileşimini kaydet (artık get_user_recommendations içinde)
347
- yield recommendation_result
348
- return
349
-
350
- except Exception as e:
351
- print(f"Enhanced features error: {e}")
352
- # Enhanced features hata verirse normal chatbot'a devam et
353
-
354
- # Log: Kullanıcı mesajını ekle
355
- try:
356
- with file_lock:
357
- with open(LOG_FILE, 'a', encoding='utf-8') as f:
358
- f.write(f"User: {user_message}\n")
359
- except Exception as e:
360
- print(f"Dosya yazma hatası (Kullanıcı): {e}")
361
-
362
- # Sistem mesajlarını external dosyadan yükle
363
- system_messages = get_prompt_content_only()
364
-
365
- # Döküman verilerini sistem mesajlarına ekle
366
- if document_content:
367
- system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
368
-
369
- # Kullanıcı profil bilgilerini ve geçmiş kontekstini ekle
370
- try:
371
- user_id = getattr(chatbot_fn, '_current_session_id', 'default_user')
372
- profile = profile_manager.get_or_create_profile(user_id)
373
- preferences = profile.get("preferences", {})
374
-
375
- # Kullanıcı tercihlerini sistem mesajı olarak ekle
376
- if any(preferences.values()):
377
- pref_info = []
378
- if preferences.get("bike_category"):
379
- pref_info.append(f"Kategori: {preferences['bike_category']}")
380
- if preferences.get("budget_range"):
381
- pref_info.append(f"Bütçe: {preferences['budget_range'][0]:,.0f}-{preferences['budget_range'][1]:,.0f} TL")
382
- if preferences.get("usage_purpose"):
383
- pref_info.append(f"Kullanım amacı: {preferences['usage_purpose']}")
384
-
385
- if pref_info:
386
- system_messages.append({
387
- "role": "system",
388
- "content": f"KULLANICI TERÇİHLERİ: {', '.join(pref_info)}. Bu bilgileri dikkate alarak daha kişiselleştirilmiş öneriler sun."
389
- })
390
- except Exception as e:
391
- print(f"Profile context error: {e}")
392
-
393
- # Kullanıcı mesajında ürün ismi geçiyorsa ekle
394
- input_words = user_message.lower().split()
395
- user_message_lower = user_message.lower()
396
-
397
- for product_info in products:
398
- # Hem ilk kelimeyi hem de tam ürün adını kontrol et
399
- product_first_word = product_info[0] # İlk kelime
400
- product_full_name = product_info[2].lower() # Tam ürün adı
401
-
402
- # Eşleşme kontrolü: ilk kelime veya tam ürün adında geçen kelimeler
403
- if (product_first_word in input_words or
404
- any(word in user_message_lower for word in product_full_name.split() if len(word) > 2)):
405
- if product_info[1][0] == "stokta":
406
- # Kampanyalı fiyat kontrolü
407
- has_campaign = product_info[1][4] and product_info[1][4] != ""
408
-
409
- # Orijinal fiyat her zaman gösterilir
410
- normal_price = f"Orijinal fiyat: {product_info[1][1]} TL"
411
-
412
- # Kampanyalı fiyat bilgisi
413
- rebate_price = ""
414
- discount_info = ""
415
- eft_price = ""
416
- rebate_money_order_price = ""
417
-
418
- if has_campaign:
419
- # Ürün kampanyalıysa, kampanyalı fiyat gösterilir
420
- rebate_price = f"\nKampanyalı fiyat: {product_info[1][4]} TL"
421
-
422
- # İndirim miktarı hesaplanır
423
- try:
424
- original_price = float(product_info[1][1].replace(',', '.'))
425
- campaign_price = float(product_info[1][4].replace(',', '.'))
426
- discount_amount = original_price - campaign_price
427
-
428
- # İndirim miktarı 0'dan büyükse göster
429
- if discount_amount > 0:
430
- # İndirim miktarı için yuvarlama kurallarını uygula
431
- if discount_amount > 200000:
432
- discount_amount_rounded = round(discount_amount / 5000) * 5000
433
- elif discount_amount > 30000:
434
- discount_amount_rounded = round(discount_amount / 1000) * 1000
435
- elif discount_amount > 10000:
436
- discount_amount_rounded = round(discount_amount / 100) * 100
437
- else:
438
- discount_amount_rounded = round(discount_amount / 10) * 10
439
-
440
- # İndirim bilgisi
441
- discount_info = f"\nYapılan indirim: {discount_amount_rounded:.0f} TL"
442
- except (ValueError, TypeError):
443
- discount_info = ""
444
-
445
- # Havale indirimli kampanyalı fiyat bilgisini gösterme
446
- rebate_money_order_price = ""
447
- else:
448
- # Kampanyalı değilse, havale indirimli normal fiyatı göster
449
- if product_info[1][3] and product_info[1][3] != "":
450
- eft_price = f"\nHavale indirimli fiyat: {product_info[1][3]} TL"
451
-
452
- # Ürün linki ve resim
453
- product_link = f"\nÜrün linki: {product_info[1][2]}"
454
- product_image = ""
455
- if len(product_info[1]) > 6 and product_info[1][6]: # Resim URL'si varsa
456
- product_image = f"\nÜrün resmi: {product_info[1][6]}"
457
-
458
- # Tüm bilgileri birleştir
459
- 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}"
460
- else:
461
- # Ürün stokta yoksa sadece stok durumunu bildir
462
- new_msg = f"{product_info[2]} {product_info[1][0]}"
463
- system_messages.append({"role": "system", "content": new_msg})
464
-
465
- messages = system_messages + history + [{"role": "user", "content": user_message}]
466
-
467
- payload = {
468
- "model": "gpt-4.1",
469
- "messages": messages,
470
- "temperature": 0.2,
471
- "top_p": 1,
472
- "n": 1,
473
- "stream": True,
474
- "presence_penalty": 0,
475
- "frequency_penalty": 0,
476
- }
477
- headers = {
478
- "Content-Type": "application/json",
479
- "Authorization": f"Bearer {OPENAI_API_KEY}"
480
- }
481
-
482
- response = requests.post(API_URL, headers=headers, json=payload, stream=True)
483
- if response.status_code != 200:
484
- yield "Bir hata oluştu."
485
- return
486
-
487
- partial_response = ""
488
-
489
- for chunk in response.iter_lines():
490
- if not chunk:
491
- continue
492
- chunk_str = chunk.decode('utf-8')
493
- if chunk_str.startswith("data: ") and chunk_str != "data: [DONE]":
494
- try:
495
- chunk_data = json.loads(chunk_str[6:])
496
- delta = chunk_data['choices'][0]['delta']
497
- if 'content' in delta:
498
- partial_response += delta['content']
499
- # Resim formatlaması uygula
500
- formatted_response = extract_product_info_for_gallery(partial_response)
501
- yield formatted_response
502
- except json.JSONDecodeError as e:
503
- print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
504
- elif chunk_str == "data: [DONE]":
505
- break
506
-
507
- # Son resim formatlaması
508
- final_response = extract_product_info_for_gallery(partial_response)
509
- yield final_response
510
-
511
- # Kullanıcı profiline normal sohbet kaydet
512
- try:
513
- user_id = getattr(chatbot_fn, '_current_session_id', 'default_user')
514
- profile_manager.add_interaction(user_id, "chat_message", {
515
- "user_message": user_message,
516
- "bot_response": partial_response,
517
- "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
518
- })
519
- except Exception as e:
520
- print(f"Profile normal chat save error: {e}")
521
-
522
- # Log: Asistan cevabını ekle
523
- try:
524
- with file_lock:
525
- with open(LOG_FILE, 'a', encoding='utf-8') as f:
526
- f.write(f"Bot: {partial_response}\n")
527
- except Exception as e:
528
- print(f"Dosya yazma hatası (Bot): {e}")
529
-
530
- # Global geçmişi güncelle
531
- with history_lock:
532
- global_chat_history.append({"role": "user", "content": user_message})
533
- global_chat_history.append({"role": "assistant", "content": partial_response})
534
-
535
- # Slow echo (test için)
536
- def slow_echo(message, history):
537
- for i in range(len(message)):
538
- time.sleep(0.05)
539
- yield "You typed: " + message[: i + 1]
540
-
541
- # Kullanım modu
542
- USE_SLOW_ECHO = False
543
- chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn
544
-
545
- if not USE_SLOW_ECHO:
546
- scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
547
- scheduler_thread.start()
548
-
549
- # Trek markasına özel tema oluştur (düzeltilmiş sürüm)
550
- trek_theme = gr.themes.Base(
551
- primary_hue="red", # Trek kırmızısı için
552
- secondary_hue="slate", # Gri tonları için
553
- neutral_hue="slate",
554
- radius_size=gr.themes.sizes.radius_sm, # Köşe yuvarlatma değerleri
555
- spacing_size=gr.themes.sizes.spacing_md, # Aralık değerleri
556
- text_size=gr.themes.sizes.text_sm # Yazı boyutları (small)
557
- )
558
- # Chatbot kartları için arka plan renkleri değiştiren CSS
559
- custom_css = """
560
- /* Genel font ayarları */
561
- .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div,
562
- button, input, select, textarea {
563
- font-family: 'Segoe UI', 'SF Pro Text', 'Roboto', -apple-system, BlinkMacSystemFont,
564
- 'Helvetica Neue', Arial, sans-serif !important;
565
- font-size: 0.6rem !important;
566
- }
567
-
568
- /* Mobil responsive başlangıç */
569
- @media (max-width: 768px) {
570
- .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div {
571
- font-size: 0.8rem !important;
572
- }
573
-
574
- h1 {
575
- font-size: 1.8rem !important;
576
- text-align: center;
577
- margin: 10px 0;
578
- }
579
-
580
- h2 {
581
- font-size: 1.4rem !important;
582
- }
583
- }
584
-
585
- /* Input alanı için de aynı boyut */
586
- .message-textbox textarea {
587
- font-size: 0.6rem !important;
588
- }
589
-
590
- /* Başlıklar için özel boyutlar */
591
- h1 {
592
- font-size: 1.4rem !important;
593
- font-weight: 800 !important;
594
- }
595
-
596
- h2 {
597
- font-size: 1.2rem !important;
598
- font-weight: 600 !important;
599
- }
600
-
601
- h3 {
602
- font-size: 1rem !important;
603
- font-weight: 600 !important;
604
- }
605
-
606
- /* Kart arka plan renkleri - görseldeki gibi */
607
- /* Kullanıcı mesajları için mavi tonda arka plan */
608
- .user-message, .user-message-highlighted {
609
- background-color: #e9f5fe !important;
610
- border-bottom-right-radius: 0 !important;
611
- box-shadow: 0 1px 2px rgba(0,0,0,0.1) !important;
612
- }
613
-
614
- /* Bot mesajları için beyaz arka plan ve hafif kenarlık */
615
- .bot-message, .bot-message-highlighted {
616
- background-color: white !important;
617
- border: 1px solid #e0e0e0 !important;
618
- border-bottom-left-radius: 0 !important;
619
- box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
620
- }
621
-
622
- /* Mesaj baloncuklarının köşe yuvarlatma değerleri */
623
- .message-wrap {
624
- border-radius: 12px !important;
625
- margin: 0.5rem 0 !important;
626
- max-width: 90% !important;
627
- }
628
-
629
- /* Sohbet alanının genel arka planı */
630
- .chat-container, .gradio-container {
631
- background-color: #f7f7f7 !important;
632
- }
633
-
634
- /* Daha net yazılar için text rendering */
635
- * {
636
- -webkit-font-smoothing: antialiased;
637
- -moz-osx-font-smoothing: grayscale;
638
- text-rendering: optimizeLegibility;
639
- }
640
-
641
- /* Resim gösterimi için özel stiller */
642
- #chatbot .message img {
643
- max-width: 100%;
644
- height: auto;
645
- border-radius: 8px;
646
- margin: 8px 0;
647
- }
648
-
649
- #chatbot .message {
650
- word-wrap: break-word;
651
- }
652
-
653
- /* Ürün galeri stilleri */
654
- .product-gallery {
655
- display: flex;
656
- flex-wrap: wrap;
657
- gap: 10px;
658
- margin: 10px 0;
659
- }
660
-
661
- .product-card {
662
- border: 1px solid #ddd;
663
- border-radius: 8px;
664
- padding: 8px;
665
- max-width: 180px;
666
- text-align: center;
667
- background: white;
668
- }
669
-
670
- /* Mobil responsive - Chatbot */
671
- @media (max-width: 768px) {
672
- #chatbot {
673
- height: 60vh !important;
674
- margin: 10px 0;
675
- }
676
-
677
- .gradio-container {
678
- padding: 8px !important;
679
- margin: 0 !important;
680
- }
681
-
682
- /* Mesaj baloncukları mobilde daha küçük */
683
- .message-wrap {
684
- max-width: 95% !important;
685
- margin: 0.3rem 0 !important;
686
- padding: 8px 12px !important;
687
- border-radius: 18px !important;
688
- font-size: 0.9rem !important;
689
- }
690
-
691
- /* Kullanıcı mesajları */
692
- .user-message, .user-message-highlighted {
693
- background-color: #007AFF !important;
694
- color: white !important;
695
- margin-left: auto !important;
696
- margin-right: 8px !important;
697
- }
698
-
699
- /* Bot mesajları */
700
- .bot-message, .bot-message-highlighted {
701
- background-color: #f1f1f1 !important;
702
- color: #333 !important;
703
- margin-left: 8px !important;
704
- margin-right: auto !important;
705
- }
706
- }
707
-
708
- /* Input alanına uçan kağıt ikonu ekle */
709
- #msg-input {
710
- position: relative;
711
- }
712
-
713
- #msg-input textarea {
714
- padding-right: 40px !important;
715
- }
716
-
717
- .input-icon {
718
- position: absolute;
719
- right: 12px;
720
- top: 50%;
721
- transform: translateY(-50%);
722
- font-size: 16px;
723
- pointer-events: none;
724
- z-index: 10;
725
- color: #666;
726
- }
727
-
728
- /* Mobil responsive - Input alanı */
729
- @media (max-width: 768px) {
730
- #msg-input {
731
- margin: 10px 0;
732
- }
733
-
734
- #msg-input textarea {
735
- padding: 12px 45px 12px 15px !important;
736
- font-size: 1rem !important;
737
- border-radius: 20px !important;
738
- border: 2px solid #e0e0e0 !important;
739
- min-height: 50px !important;
740
- }
741
-
742
- #msg-input textarea:focus {
743
- border-color: #007AFF !important;
744
- box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1) !important;
745
- }
746
-
747
- .input-icon {
748
- right: 15px;
749
- font-size: 18px;
750
- }
751
-
752
- /* Ürün galeri mobil responsive */
753
- .product-gallery {
754
- gap: 8px !important;
755
- margin: 8px 0 !important;
756
- }
757
-
758
- .product-card {
759
- max-width: 140px !important;
760
- padding: 6px !important;
761
- font-size: 0.8rem !important;
762
- }
763
-
764
- .product-card img {
765
- max-width: 120px !important;
766
- max-height: 80px !important;
767
- }
768
-
769
- /* Resimler mobilde daha küçük */
770
- #chatbot .message img {
771
- max-width: 250px !important;
772
- max-height: 150px !important;
773
- border-radius: 12px !important;
774
- margin: 6px 0 !important;
775
- }
776
-
777
- /* Tablo responsive */
778
- table {
779
- font-size: 0.8rem !important;
780
- overflow-x: auto !important;
781
- display: block !important;
782
- white-space: nowrap !important;
783
- }
784
-
785
- table th, table td {
786
- padding: 4px 6px !important;
787
- min-width: 60px !important;
788
- }
789
- }
790
-
791
- /* JavaScript ile placeholder eklemek için */
792
- </style>
793
- <script>
794
- document.addEventListener('DOMContentLoaded', function() {
795
- // Viewport meta tag ekle (mobil için)
796
- if (!document.querySelector('meta[name="viewport"]')) {
797
- const viewport = document.createElement('meta');
798
- viewport.name = 'viewport';
799
- viewport.content = 'width=device-width, initial-scale=1.0, user-scalable=no';
800
- document.head.appendChild(viewport);
801
- }
802
-
803
- // Input alanına uçan kağıt ikonu ekle
804
- setTimeout(function() {
805
- const msgInput = document.querySelector('#msg-input');
806
- if (msgInput) {
807
- // Icon elementi oluştur
808
- const icon = document.createElement('div');
809
- icon.innerHTML = '✈️';
810
- icon.className = 'input-icon';
811
-
812
- // Input container'ına ekle
813
- msgInput.style.position = 'relative';
814
- msgInput.appendChild(icon);
815
-
816
- // Textarea'ya padding ekle
817
- const textarea = msgInput.querySelector('textarea');
818
- if (textarea) {
819
- textarea.style.paddingRight = '40px';
820
- }
821
- }
822
- }, 1000);
823
- });
824
- </script>
825
- <style>
826
- """
827
-
828
-
829
- # Enhanced chatbot fonksiyonu image destekli
830
- def enhanced_chatbot_fn(message, history, image):
831
- return chatbot_fn(message, history, image)
832
-
833
- # Demo arayüzü - Mobil responsive
834
- with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı") as demo:
835
- gr.Markdown("# 🚲 Trek Asistanı AI")
836
- gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması, kişisel öneriler ve detaylı ürün bilgileri sunuyorum.")
837
-
838
- chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False)
839
-
840
- msg = gr.Textbox(
841
- placeholder="Trek bisikletleri hakkında soru sorun...",
842
- show_label=False,
843
- elem_id="msg-input"
844
- )
845
-
846
- # Session state kaldırıldı - basit yöntem kullanılıyor
847
-
848
- def respond(message, chat_history):
849
- if not message.strip():
850
- return "", chat_history
851
-
852
- # Basit session ID sistemi - her kullanıcı için sabit ID
853
- if not hasattr(respond, '_user_session_id'):
854
- respond._user_session_id = str(uuid.uuid4())[:12] # Oturum boyunca sabit ID
855
-
856
- # Session ID'yi chatbot_fn'e geç
857
- chatbot_fn._current_session_id = respond._user_session_id
858
-
859
- # Chat history'yi chatbot_fn için uygun formata çevir
860
- formatted_history = []
861
- if chat_history:
862
- for user_msg, bot_msg in chat_history:
863
- formatted_history.append({"role": "user", "content": user_msg})
864
- formatted_history.append({"role": "assistant", "content": bot_msg})
865
-
866
- try:
867
- # Enhanced chatbot fonksiyonunu çağır (image=None)
868
- response_generator = chatbot_fn(message, formatted_history, None)
869
-
870
- # Generator'dan son cevabı al
871
- response = ""
872
- for partial in response_generator:
873
- response = partial
874
-
875
- # Chat history güncelle
876
- if chat_history is None:
877
- chat_history = []
878
- chat_history.append((message, response))
879
-
880
- # Profile kaydetme chatbot_fn içinde yapılıyor, burada duplikasyon yok
881
-
882
- return "", chat_history
883
-
884
- except Exception as e:
885
- error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
886
- print(f"Chat error: {e}")
887
- if chat_history is None:
888
- chat_history = []
889
- chat_history.append((message, error_msg))
890
- return "", chat_history
891
-
892
- msg.submit(respond, [msg, chatbot], [msg, chatbot])
893
-
894
- if __name__ == "__main__":
895
  demo.launch(debug=True)
 
1
+ import gradio as gr
2
+ import os
3
+ import json
4
+ import requests
5
+ import xml.etree.ElementTree as ET
6
+ import schedule
7
+ import time
8
+ import threading
9
+ from huggingface_hub import HfApi, create_repo, hf_hub_download
10
+ import warnings
11
+ import pandas as pd
12
+ from docx import Document
13
+ import spaces
14
+ from google.oauth2.service_account import Credentials
15
+ from googleapiclient.discovery import build
16
+ from googleapiclient.http import MediaIoBaseDownload
17
+ import io
18
+ import warnings
19
+ from requests.packages.urllib3.exceptions import InsecureRequestWarning
20
+ warnings.simplefilter('ignore', InsecureRequestWarning)
21
+
22
+ # Prompt dosyasını import et
23
+ from prompts import get_prompt_content_only
24
+
25
+ # Enhanced features import et (sadece temel özellikler)
26
+ from enhanced_features import (
27
+ initialize_enhanced_features, process_image_message,
28
+ handle_comparison_request
29
+ )
30
+ from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
+
32
+ # Gradio uyarılarını bastır
33
+ warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
34
+
35
+ # Log dosyası adı ve yolu
36
+ LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
37
+ print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}")
38
+
39
+ # API ayarları
40
+ API_URL = "https://api.openai.com/v1/chat/completions"
41
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
42
+ if not OPENAI_API_KEY:
43
+ print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
44
+
45
+ # Trek bisiklet ürünlerini çekme
46
+ url = 'https://www.trekbisiklet.com.tr/output/8582384479'
47
+ response = requests.get(url, verify=False, timeout=30)
48
+ if response.status_code == 200 and response.content:
49
+ root = ET.fromstring(response.content)
50
+ else:
51
+ print(f"HTTP hatası: {response.status_code}")
52
+ root = None
53
+
54
+ products = []
55
+ if root is not None:
56
+ for item in root.findall('item'):
57
+ # Tüm ürünleri al, sonra stokta olma durumunu kontrol et
58
+ name_words = item.find('rootlabel').text.lower().split()
59
+ name = name_words[0]
60
+ full_name = ' '.join(name_words)
61
+
62
+ stock_amount = "stokta" if item.find('stockAmount').text > '0' else "stokta değil"
63
+
64
+ # Stokta olmayan ürünler için fiyat/link bilgisi eklemiyoruz
65
+ if stock_amount == "stokta":
66
+ # Normal fiyat bilgisini al
67
+ price_str = item.find('priceTaxWithCur').text if item.find('priceTaxWithCur') is not None else "Fiyat bilgisi yok"
68
+
69
+ # EFT fiyatını al (havale indirimli orijinal fiyat)
70
+ price_eft_str = item.find('priceEft').text if item.find('priceEft') is not None else ""
71
+
72
+ # İndirimli fiyatı al (kampanyalı fiyat)
73
+ price_rebate_str = item.find('priceRebateWithTax').text if item.find('priceRebateWithTax') is not None else ""
74
+
75
+ # Havale indirimi fiyatını al (havale indirimli kampanyalı fiyat)
76
+ price_rebate_money_order_str = item.find('priceRebateWithMoneyOrderWithTax').text if item.find('priceRebateWithMoneyOrderWithTax') is not None else ""
77
+
78
+ # Normal fiyatı yuvarla
79
+ try:
80
+ price_float = float(price_str)
81
+ # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
82
+ if price_float > 200000:
83
+ price = str(round(price_float / 5000) * 5000) # En yakın 5000'e yuvarla
84
+ # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
85
+ elif price_float > 30000:
86
+ price = str(round(price_float / 1000) * 1000) # En yakın 1000'e yuvarla
87
+ # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
88
+ elif price_float > 10000:
89
+ price = str(round(price_float / 100) * 100) # En yakın 100'e yuvarla
90
+ # Diğer durumlarda en yakın 10'luk basamağa yuvarla
91
+ else:
92
+ price = str(round(price_float / 10) * 10) # En yakın 10'a yuvarla
93
+ except (ValueError, TypeError):
94
+ price = price_str # Sayıya dönüştürülemezse olduğu gibi bırak
95
+
96
+ # Havale indirimli orijinal fiyatı yuvarla (varsa)
97
+ if price_eft_str:
98
+ try:
99
+ price_eft_float = float(price_eft_str)
100
+ # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
101
+ if price_eft_float > 200000:
102
+ price_eft = str(round(price_eft_float / 5000) * 5000)
103
+ # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
104
+ elif price_eft_float > 30000:
105
+ price_eft = str(round(price_eft_float / 1000) * 1000)
106
+ # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
107
+ elif price_eft_float > 10000:
108
+ price_eft = str(round(price_eft_float / 100) * 100)
109
+ # Diğer durumlarda en yakın 10'luk basamağa yuvarla
110
+ else:
111
+ price_eft = str(round(price_eft_float / 10) * 10)
112
+ except (ValueError, TypeError):
113
+ price_eft = price_eft_str
114
+ else:
115
+ price_eft = ""
116
+
117
+ # İndirimli fiyatı yuvarla (varsa)
118
+ if price_rebate_str:
119
+ try:
120
+ price_rebate_float = float(price_rebate_str)
121
+ # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
122
+ if price_rebate_float > 200000:
123
+ price_rebate = str(round(price_rebate_float / 5000) * 5000)
124
+ # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
125
+ elif price_rebate_float > 30000:
126
+ price_rebate = str(round(price_rebate_float / 1000) * 1000)
127
+ # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
128
+ elif price_rebate_float > 10000:
129
+ price_rebate = str(round(price_rebate_float / 100) * 100)
130
+ # Diğer durumlarda en yakın 10'luk basamağa yuvarla
131
+ else:
132
+ price_rebate = str(round(price_rebate_float / 10) * 10)
133
+ except (ValueError, TypeError):
134
+ price_rebate = price_rebate_str
135
+ else:
136
+ price_rebate = ""
137
+
138
+ # Havale indirimli kampanyalı fiyatı yuvarla (varsa)
139
+ if price_rebate_money_order_str:
140
+ try:
141
+ price_rebate_money_order_float = float(price_rebate_money_order_str)
142
+ # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
143
+ if price_rebate_money_order_float > 200000:
144
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
145
+ # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
146
+ elif price_rebate_money_order_float > 30000:
147
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
148
+ # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
149
+ elif price_rebate_money_order_float > 10000:
150
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
151
+ # Diğer durumlarda en yakın 10'luk basamağa yuvarla
152
+ else:
153
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
154
+ except (ValueError, TypeError):
155
+ price_rebate_money_order = price_rebate_money_order_str
156
+ else:
157
+ price_rebate_money_order = ""
158
+
159
+ # Ürün bilgilerini al
160
+ product_info = [stock_amount, price, item.find('productDetailUrl').text, price_eft, price_rebate, price_rebate_money_order]
161
+
162
+ # Resim URL'si ekle (varsa)
163
+ if item.find('imageHighResUrl') is not None and item.find('imageHighResUrl').text:
164
+ product_info.append(item.find('imageHighResUrl').text)
165
+ else:
166
+ product_info.append("") # Boş resim URL'si
167
+
168
+ else:
169
+ # Stokta olmayan ürün için sadece stok durumu
170
+ product_info = [stock_amount]
171
+
172
+ products.append((name, product_info, full_name))
173
+
174
+ print(f"Toplam {len(products)} ürün yüklendi.")
175
+
176
+ # Initialize enhanced features
177
+ initialize_enhanced_features(OPENAI_API_KEY, products)
178
+
179
+ # Google Drive API ayarları
180
+ GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH")
181
+ GOOGLE_FOLDER_ID = "1bE8aMj8-eFGftjMPOF8bKQJAhfHa0BN8"
182
+
183
+ # Global değişkenler
184
+ file_lock = threading.Lock()
185
+ history_lock = threading.Lock()
186
+ global_chat_history = []
187
+ document_content = ""
188
+
189
+ # Google Drive'dan döküman indirme fonksiyonu
190
+ def download_documents_from_drive():
191
+ global document_content
192
+
193
+ if not GOOGLE_CREDENTIALS_PATH:
194
+ print("Google credentials dosyası bulunamadı.")
195
+ return
196
+
197
+ try:
198
+ credentials = Credentials.from_service_account_file(GOOGLE_CREDENTIALS_PATH)
199
+ service = build('drive', 'v3', credentials=credentials)
200
+
201
+ # Klasördeki dosyaları listele
202
+ results = service.files().list(
203
+ q=f"'{GOOGLE_FOLDER_ID}' in parents",
204
+ fields="files(id, name, mimeType)"
205
+ ).execute()
206
+
207
+ files = results.get('files', [])
208
+ all_content = []
209
+
210
+ for file in files:
211
+ print(f"İndiriliyor: {file['name']}")
212
+
213
+ # DOCX dosyaları için
214
+ if file['mimeType'] == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
215
+ request = service.files().get_media(fileId=file['id'])
216
+ file_io = io.BytesIO()
217
+ downloader = MediaIoBaseDownload(file_io, request)
218
+
219
+ done = False
220
+ while done is False:
221
+ status, done = downloader.next_chunk()
222
+
223
+ file_io.seek(0)
224
+ doc = Document(file_io)
225
+
226
+ content = f"\\n=== {file['name']} ===\\n"
227
+ for paragraph in doc.paragraphs:
228
+ if paragraph.text.strip():
229
+ content += paragraph.text + "\\n"
230
+
231
+ all_content.append(content)
232
+
233
+ document_content = "\\n".join(all_content)
234
+ print(f"Toplam {len(files)} döküman yüklendi.")
235
+
236
+ except Exception as e:
237
+ print(f"Google Drive'dan döküman indirme hatası: {e}")
238
+
239
+ # Döküman indirme işlemini arka planda çalıştır
240
+ document_thread = threading.Thread(target=download_documents_from_drive, daemon=True)
241
+ document_thread.start()
242
+
243
+ # Log dosyasını zamanla temizleme fonksiyonu
244
+ def clear_log_file():
245
+ try:
246
+ if os.path.exists(LOG_FILE):
247
+ with file_lock:
248
+ with open(LOG_FILE, 'w', encoding='utf-8') as f:
249
+ f.write("Log dosyası temizlendi.\\n")
250
+ print("Log dosyası temizlendi.")
251
+ except Exception as e:
252
+ print(f"Log dosyası temizleme hatası: {e}")
253
+
254
+ # Zamanlanmış görevleri çalıştırma fonksiyonu
255
+ def run_scheduler(chat_history):
256
+ schedule.every().day.at("03:00").do(clear_log_file)
257
+
258
+ while True:
259
+ schedule.run_pending()
260
+ time.sleep(60)
261
+
262
+ # Chatbot fonksiyonu
263
+ def chatbot_fn(user_message, history, image=None):
264
+ if history is None:
265
+ history = []
266
+
267
+ try:
268
+ # Enhanced features - Görsel işleme
269
+ if image is not None:
270
+ user_message = process_image_message(image, user_message)
271
+
272
+ # Enhanced features - Karşılaştırma kontrolü
273
+ comparison_result = handle_comparison_request(user_message)
274
+ if comparison_result:
275
+ yield comparison_result
276
+ return
277
+
278
+ # Enhanced features - Basit karşılaştırma ve görsel işleme
279
+ # Profil sistemi kaldırıldı - daha hızlı çalışma için
280
+
281
+ except Exception as e:
282
+ print(f"Enhanced features error: {e}")
283
+ # Enhanced features hata verirse normal chatbot'a devam et
284
+
285
+ # Log: Kullanıcı mesajını ekle
286
+ try:
287
+ with file_lock:
288
+ with open(LOG_FILE, 'a', encoding='utf-8') as f:
289
+ f.write(f"User: {user_message}\\n")
290
+ except Exception as e:
291
+ print(f"Dosya yazma hatası (Kullanıcı): {e}")
292
+
293
+ # Sistem mesajlarını external dosyadan yükle
294
+ system_messages = get_prompt_content_only()
295
+
296
+ # Döküman verilerini sistem mesajlarına ekle
297
+ if document_content:
298
+ system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
299
+
300
+ # Profil sistemi kaldırıldı - daha hızlı çalışma için
301
+
302
+ # Kullanıcı mesajında ürün ismi geçiyorsa ekle
303
+ input_words = user_message.lower().split()
304
+ for word in input_words:
305
+ for product_info in products:
306
+ if word in product_info[0] or word in product_info[2].lower():
307
+ # Stokta olup olmadığını kontrol et
308
+ if product_info[1][0] == "stokta":
309
+ # Normal fiyat
310
+ normal_price = f"\\nFiyat: {product_info[1][1]} TL"
311
+
312
+ # Kampanyalı fiyat kontrolü
313
+ rebate_price = ""
314
+ discount_info = ""
315
+ eft_price = ""
316
+ rebate_money_order_price = ""
317
+
318
+ # Kampanyalı fiyat var mı kontrol et
319
+ if len(product_info[1]) > 4 and product_info[1][4] and product_info[1][4] != "":
320
+ rebate_price = f"\\nKampanyalı fiyat: {product_info[1][4]} TL"
321
+
322
+ # İndirim miktarını hesapla
323
+ try:
324
+ normal_price_float = float(product_info[1][1])
325
+ rebate_price_float = float(product_info[1][4])
326
+ discount_amount = normal_price_float - rebate_price_float
327
+
328
+ # İndirim miktarı 0'dan büyükse göster
329
+ if discount_amount > 0:
330
+ # İndirim miktarı için yuvarlama kurallarını uygula
331
+ if discount_amount > 200000:
332
+ discount_amount_rounded = round(discount_amount / 5000) * 5000
333
+ elif discount_amount > 30000:
334
+ discount_amount_rounded = round(discount_amount / 1000) * 1000
335
+ elif discount_amount > 10000:
336
+ discount_amount_rounded = round(discount_amount / 100) * 100
337
+ else:
338
+ discount_amount_rounded = round(discount_amount / 10) * 10
339
+
340
+ # İndirim bilgisi
341
+ discount_info = f"\\nYapılan indirim: {discount_amount_rounded:.0f} TL"
342
+ except (ValueError, TypeError):
343
+ discount_info = ""
344
+
345
+ # Havale indirimli kampanyalı fiyat bilgisini gösterme
346
+ rebate_money_order_price = ""
347
+ else:
348
+ # Kampanyalı değilse, havale indirimli normal fiyatı göster
349
+ if product_info[1][3] and product_info[1][3] != "":
350
+ eft_price = f"\\nHavale indirimli fiyat: {product_info[1][3]} TL"
351
+
352
+ # Ürün linki ve resim
353
+ product_link = f"\\nÜrün linki: {product_info[1][2]}"
354
+ product_image = ""
355
+ if len(product_info[1]) > 6 and product_info[1][6]: # Resim URL'si varsa
356
+ product_image = f"\\nÜrün resmi: {product_info[1][6]}"
357
+
358
+ # Tüm bilgileri birleştir
359
+ 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}"
360
+ else:
361
+ # Ürün stokta yoksa sadece stok durumunu bildir
362
+ new_msg = f"{product_info[2]} {product_info[1][0]}"
363
+ system_messages.append({"role": "system", "content": new_msg})
364
+
365
+ messages = system_messages + history + [{"role": "user", "content": user_message}]
366
+
367
+ payload = {
368
+ "model": "gpt-4.1",
369
+ "messages": messages,
370
+ "temperature": 0.2,
371
+ "top_p": 1,
372
+ "n": 1,
373
+ "stream": True,
374
+ "presence_penalty": 0,
375
+ "frequency_penalty": 0,
376
+ }
377
+ headers = {
378
+ "Content-Type": "application/json",
379
+ "Authorization": f"Bearer {OPENAI_API_KEY}"
380
+ }
381
+
382
+ response = requests.post(API_URL, headers=headers, json=payload, stream=True)
383
+ if response.status_code != 200:
384
+ yield "Bir hata oluştu."
385
+ return
386
+
387
+ partial_response = ""
388
+
389
+ for chunk in response.iter_lines():
390
+ if not chunk:
391
+ continue
392
+ chunk_str = chunk.decode('utf-8')
393
+ if chunk_str.startswith("data: ") and chunk_str != "data: [DONE]":
394
+ try:
395
+ chunk_data = json.loads(chunk_str[6:])
396
+ delta = chunk_data['choices'][0]['delta']
397
+ if 'content' in delta:
398
+ partial_response += delta['content']
399
+ # Resim formatlaması uygula
400
+ formatted_response = extract_product_info_for_gallery(partial_response)
401
+ yield formatted_response
402
+ except json.JSONDecodeError as e:
403
+ print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
404
+ elif chunk_str == "data: [DONE]":
405
+ break
406
+
407
+ # Son resim formatlaması
408
+ final_response = extract_product_info_for_gallery(partial_response)
409
+ yield final_response
410
+
411
+ # Profil kaydetme kaldırıldı - daha hızlı çalışma için
412
+
413
+ # Log: Asistan cevabını ekle
414
+ try:
415
+ with file_lock:
416
+ with open(LOG_FILE, 'a', encoding='utf-8') as f:
417
+ f.write(f"Bot: {partial_response}\\n")
418
+ except Exception as e:
419
+ print(f"Dosya yazma hatası (Bot): {e}")
420
+
421
+ # Global geçmişi güncelle
422
+ with history_lock:
423
+ global_chat_history.append({"role": "user", "content": user_message})
424
+ global_chat_history.append({"role": "assistant", "content": partial_response})
425
+
426
+ # Slow echo (test için)
427
+ def slow_echo(message, history):
428
+ for i in range(len(message)):
429
+ time.sleep(0.05)
430
+ yield "You typed: " + message[: i + 1]
431
+
432
+ # Kullanım modu
433
+ USE_SLOW_ECHO = False
434
+ chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn
435
+
436
+ if not USE_SLOW_ECHO:
437
+ scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
438
+ scheduler_thread.start()
439
+
440
+ # Trek markasına özel tema oluştur (düzeltilmiş sürüm)
441
+ trek_theme = gr.themes.Base(
442
+ primary_hue="red", # Trek kırmızısı için
443
+ secondary_hue="slate", # Gri tonları için
444
+ neutral_hue="slate",
445
+ radius_size=gr.themes.sizes.radius_sm, # Köşe yuvarlatma değerleri
446
+ spacing_size=gr.themes.sizes.spacing_md, # Aralık değerleri
447
+ text_size=gr.themes.sizes.text_sm # Yazı boyutları (small)
448
+ )
449
+ # Chatbot kartları için arka plan renkleri değiştiren CSS
450
+ custom_css = """
451
+ /* Genel font ayarları */
452
+ .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div,
453
+ button, input, select, textarea {
454
+ font-family: 'Segoe UI', 'SF Pro Text', 'Roboto', -apple-system, BlinkMacSystemFont,
455
+ 'Helvetica Neue', Arial, sans-serif !important;
456
+ font-size: 0.6rem !important;
457
+ }
458
+
459
+ /* Mobil responsive başlangıç */
460
+ @media (max-width: 768px) {
461
+ .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div {
462
+ font-size: 0.8rem !important;
463
+ }
464
+
465
+ h1 {
466
+ font-size: 1.8rem !important;
467
+ text-align: center;
468
+ margin: 10px 0;
469
+ }
470
+
471
+ h2 {
472
+ font-size: 1.4rem !important;
473
+ }
474
+ }
475
+
476
+ /* Input alanı için de aynı boyut */
477
+ .message-textbox textarea {
478
+ font-size: 0.6rem !important;
479
+ }
480
+
481
+ /* Başlıklar için özel boyutlar */
482
+ h1 {
483
+ font-size: 1.4rem !important;
484
+ font-weight: 800 !important;
485
+ }
486
+
487
+ h2 {
488
+ font-size: 1.2rem !important;
489
+ font-weight: 600 !important;
490
+ }
491
+
492
+ h3 {
493
+ font-size: 1rem !important;
494
+ font-weight: 600 !important;
495
+ }
496
+
497
+ /* Kart arka plan renkleri - görseldeki gibi */
498
+ /* Kullanıcı mesajları için mavi tonda arka plan */
499
+ .user-message, .user-message-highlighted {
500
+ background-color: #e9f5fe !important;
501
+ border-bottom-right-radius: 0 !important;
502
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1) !important;
503
+ }
504
+
505
+ /* Bot mesajları için beyaz arka plan ve hafif kenarlık */
506
+ .bot-message, .bot-message-highlighted {
507
+ background-color: white !important;
508
+ border: 1px solid #e0e0e0 !important;
509
+ border-bottom-left-radius: 0 !important;
510
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
511
+ }
512
+
513
+ /* Mesaj baloncuklarının köşe yuvarlatma değerleri */
514
+ .message-wrap {
515
+ border-radius: 12px !important;
516
+ margin: 0.5rem 0 !important;
517
+ max-width: 90% !important;
518
+ }
519
+
520
+ /* Sohbet alanının genel arka planı */
521
+ .chat-container, .gradio-container {
522
+ background-color: #f7f7f7 !important;
523
+ }
524
+
525
+ /* Daha net yazılar için text rendering */
526
+ * {
527
+ -webkit-font-smoothing: antialiased;
528
+ -moz-osx-font-smoothing: grayscale;
529
+ text-rendering: optimizeLegibility;
530
+ }
531
+
532
+ /* Responsive mobil tasarım - iOS benzeri */
533
+ @media (max-width: 768px) {
534
+ /* Daha büyük ve dokunmatik dostu boyutlar */
535
+ .gradio-container {
536
+ padding: 0 !important;
537
+ margin: 0 !important;
538
+ }
539
+
540
+ /* Mesaj baloncukları iOS tarzı */
541
+ .message-wrap {
542
+ margin: 0.3rem 0.5rem !important;
543
+ max-width: 85% !important;
544
+ border-radius: 18px !important;
545
+ padding: 10px 15px !important;
546
+ font-size: 0.9rem !important;
547
+ }
548
+
549
+ /* Kullanıcı mesajları */
550
+ .user-message, .user-message-highlighted {
551
+ background-color: #007AFF !important;
552
+ color: white !important;
553
+ margin-left: auto !important;
554
+ margin-right: 8px !important;
555
+ }
556
+
557
+ /* Bot mesajları */
558
+ .bot-message, .bot-message-highlighted {
559
+ background-color: #f1f1f1 !important;
560
+ color: #333 !important;
561
+ margin-left: 8px !important;
562
+ margin-right: auto !important;
563
+ }
564
+ }
565
+
566
+ /* Input alanına uçan kağıt ikonu ekle */
567
+ #msg-input {
568
+ position: relative;
569
+ }
570
+
571
+ #msg-input textarea {
572
+ padding-right: 40px !important;
573
+ }
574
+
575
+ .input-icon {
576
+ position: absolute;
577
+ right: 12px;
578
+ top: 50%;
579
+ transform: translateY(-50%);
580
+ font-size: 16px;
581
+ pointer-events: none;
582
+ z-index: 10;
583
+ color: #666;
584
+ }
585
+
586
+ /* Mobil responsive - Input alanı */
587
+ @media (max-width: 768px) {
588
+ #msg-input {
589
+ margin: 10px 0;
590
+ }
591
+
592
+ #msg-input textarea {
593
+ padding: 12px 45px 12px 15px !important;
594
+ font-size: 1rem !important;
595
+ border-radius: 20px !important;
596
+ border: 1px solid #ddd !important;
597
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
598
+ }
599
+
600
+ .input-icon {
601
+ right: 15px;
602
+ font-size: 18px;
603
+ }
604
+ }
605
+
606
+ /* Genel mobil iyileştirmeler */
607
+ @media (max-width: 768px) {
608
+ /* Chatbot alanı tam ekran */
609
+ .gradio-container .main {
610
+ padding: 0 !important;
611
+ }
612
+
613
+ /* Başlık alanı küçült */
614
+ .gradio-container header {
615
+ padding: 8px !important;
616
+ }
617
+
618
+ /* Tab alanlarını küçült */
619
+ .tab-nav {
620
+ padding: 5px !important;
621
+ }
622
+
623
+ /* Scroll bar'ı gizle */
624
+ .scroll-hide {
625
+ scrollbar-width: none;
626
+ -ms-overflow-style: none;
627
+ }
628
+
629
+ .scroll-hide::-webkit-scrollbar {
630
+ display: none;
631
+ }
632
+ }
633
+ """
634
+
635
+ # Enhanced chatbot fonksiyonu image destekli
636
+ def enhanced_chatbot_fn(message, history, image):
637
+ return chatbot_fn(message, history, image)
638
+
639
+ # Demo arayüzü - Mobil responsive
640
+ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı") as demo:
641
+ gr.Markdown("# 🚲 Trek Asistanı AI")
642
+ gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması ve detaylı ürün bilgileri sunuyorum.")
643
+
644
+ chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False)
645
+
646
+ msg = gr.Textbox(
647
+ placeholder="Trek bisikletleri hakkında soru sorun...",
648
+ show_label=False,
649
+ elem_id="msg-input"
650
+ )
651
+
652
+ # Session ve profil sistemi tamamen kaldırıldı
653
+
654
+ def respond(message, chat_history):
655
+ if not message.strip():
656
+ return "", chat_history
657
+
658
+ # Session ID sistemi kaldırıldı - daha hızlı çalışma için
659
+
660
+ # Chat history'yi chatbot_fn için uygun formata çevir
661
+ formatted_history = []
662
+ if chat_history:
663
+ for user_msg, bot_msg in chat_history:
664
+ formatted_history.append({"role": "user", "content": user_msg})
665
+ formatted_history.append({"role": "assistant", "content": bot_msg})
666
+
667
+ try:
668
+ # Enhanced chatbot fonksiyonunu çağır (image=None)
669
+ response_generator = chatbot_fn(message, formatted_history, None)
670
+
671
+ # Generator'dan son cevabı al
672
+ response = ""
673
+ for partial in response_generator:
674
+ response = partial
675
+
676
+ # Chat history güncelle
677
+ if chat_history is None:
678
+ chat_history = []
679
+ chat_history.append((message, response))
680
+
681
+ # Profile kaydetme chatbot_fn içinde yapılıyor, burada duplikasyon yok
682
+
683
+ return "", chat_history
684
+
685
+ except Exception as e:
686
+ error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
687
+ print(f"Chat error: {e}")
688
+ if chat_history is None:
689
+ chat_history = []
690
+ chat_history.append((message, error_msg))
691
+ return "", chat_history
692
+
693
+ msg.submit(respond, [msg, chatbot], [msg, chatbot])
694
+
695
+ if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
696
  demo.launch(debug=True)