SamiKoen commited on
Commit
d2ecabf
·
verified ·
1 Parent(s): 0d3317e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +404 -303
app.py CHANGED
@@ -2,195 +2,380 @@ import gradio as gr
2
  import os
3
  import json
4
  import requests
5
- import warnings
 
6
  import time
7
  import threading
8
- import tempfile
9
- import base64
10
- from huggingface_hub import HfApi, create_repo
11
- import subprocess
 
 
 
 
 
12
 
13
- # Gradio uyarılarını bastır
14
  warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
15
 
16
- # Log dosyası adı ve yolu
17
  LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
18
- print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}")
19
 
20
- # API ayarları
21
  API_URL = "https://api.openai.com/v1/chat/completions"
22
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
23
  if not OPENAI_API_KEY:
24
- print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
25
-
26
- # Azure Speech API ayarları
27
- SPEECH_KEY = os.getenv("AZURE_SPEECH_KEY")
28
- SPEECH_REGION = os.getenv("AZURE_SPEECH_REGION", "westeurope")
29
 
30
- if not SPEECH_KEY:
31
- print("Hata: AZURE_SPEECH_KEY çevre değişkeni ayarlanmamış!")
 
 
32
 
33
- # Hugging Face token
34
- hfapi = os.getenv("hfapi")
35
- if not hfapi:
36
- print("Uyarı: hfapi ortam değişkeni ayarlanmamış! Log dosyası yüklenemeyecek.")
37
-
38
- if hfapi:
39
- try:
40
- create_repo("BF", token=hfapi, repo_type="space", space_sdk="gradio", exist_ok=True)
41
- except Exception as e:
42
- print(f"HF repo oluşturma hatası: {e}")
43
-
44
- global_chat_history = [] # Tüm sohbet geçmişi
45
- history_lock = threading.Lock() # Global geçmiş için kilit
46
- file_lock = threading.Lock() # Dosya yazma için kilit
47
- last_logged_index = 0 # Son kaydedilen mesaj indeksi
48
-
49
- # Azure Speech REST API ile metin-konuşma dönüşümü
50
- def text_to_speech_azure_rest(text, output_file="response.mp3"):
51
- try:
52
- url = f"https://{SPEECH_REGION}.tts.speech.microsoft.com/cognitiveservices/v1"
53
- headers = {
54
- "Ocp-Apim-Subscription-Key": SPEECH_KEY,
55
- "Content-Type": "application/ssml+xml",
56
- "X-Microsoft-OutputFormat": "audio-16khz-128kbitrate-mono-mp3"
57
- }
58
 
59
- ssml = f"""
60
- <speak version='1.0' xml:lang='tr-TR'>
61
- <voice xml:lang='tr-TR' name='tr-TR-EmelNeural'>
62
- {text}
63
- </voice>
64
- </speak>
65
- """
66
 
67
- response = requests.post(url, headers=headers, data=ssml.encode('utf-8'))
 
68
 
69
- if response.status_code == 200:
70
- with open(output_file, "wb") as file:
71
- file.write(response.content)
72
-
73
- # Dosyayı binary olarak oku
74
- with open(output_file, "rb") as file:
75
- audio_data = file.read()
76
-
77
- # Gradio için dönüş formatı
78
- return (16000, audio_data)
79
- else:
80
- print(f"Azure TTS hatası: {response.status_code} - {response.text}")
81
- return None
82
- except Exception as e:
83
- print(f"Ses sentezleme hatası: {e}")
84
- return None
85
-
86
- # Azure Speech REST API ile konuşma-metin dönüşümü
87
- def speech_to_text_azure_rest(audio_data):
88
- try:
89
- # Geçici ses dosyası oluştur
90
- with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
91
- temp_file_path = temp_file.name
92
-
93
- if isinstance(audio_data, tuple):
94
- # Gradio'dan gelen (sample_rate, data) formatı
95
- sample_rate, data = audio_data
96
- with open(temp_file_path, 'wb') as f:
97
- f.write(data)
98
- else:
99
- # Doğrudan binary veri
100
- with open(temp_file_path, 'wb') as f:
101
- f.write(audio_data)
102
 
103
- url = f"https://{SPEECH_REGION}.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1"
104
- params = {
105
- "language": "tr-TR",
106
- "format": "detailed"
107
- }
108
- headers = {
109
- "Ocp-Apim-Subscription-Key": SPEECH_KEY,
110
- "Content-Type": "audio/wav"
111
- }
 
 
 
 
 
 
 
 
112
 
113
- with open(temp_file_path, 'rb') as audio_file:
114
- response = requests.post(url, params=params, headers=headers, data=audio_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- # Geçici dosyayı temizle
117
  try:
118
- os.unlink(temp_file_path)
119
- except:
120
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
- if response.status_code == 200:
123
- result = response.json()
124
- if result.get("RecognitionStatus") == "Success":
125
- return result.get("DisplayText", "")
 
 
 
 
 
 
 
 
 
 
 
 
126
  else:
127
- print(f"Konuşma tanıma başarısız: {result.get('RecognitionStatus')}")
128
- return "Konuşma tanınamadı. Lütfen tekrar deneyin veya yazarak iletişim kurun."
129
- else:
130
- print(f"Azure STT hatası: {response.status_code} - {response.text}")
131
- return "Ses tanıma servisi hatası. Lütfen yazarak iletişim kurun."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  except Exception as e:
133
- print(f"Konuşma tanıma hatası: {e}")
134
- return "Ses dönüştürme sırasında bir hata oluştu. Lütfen yazarak iletişim kurun."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- # Zamanlanmış log kaydı ve yükleme
137
  def run_scheduler(chat_history_ref):
138
- import schedule
139
-
140
  def scheduled_save_and_upload():
141
  global last_logged_index
142
  if chat_history_ref:
143
- print(f"Zamanlayıcı tetiklendi: {time.strftime('%Y-%m-%d %H:%M:%S')}")
144
  try:
145
- with file_lock: # Dosya yazma kilidi
146
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
147
- f.write("\n--- Zamanlanmış Kayıt: {} ---\n".format(time.strftime("%Y-%m-%d %H:%M:%S")))
148
- with history_lock: # Geçmiş kilidi
149
  new_messages = chat_history_ref[last_logged_index:]
150
  for msg in new_messages:
151
  if msg["role"] in ["user", "assistant"]:
152
  f.write(f"{msg['role'].capitalize()}: {msg['content']}\n")
153
- last_logged_index = len(chat_history_ref) # Son indeksi güncelle
154
- print(f"Sohbet dosyaya kaydedildi: {os.path.abspath(LOG_FILE)}")
155
  except Exception as e:
156
- print(f"Kayıt hatası: {e}")
157
- return
 
158
 
159
- if hfapi:
160
- HF_REPO_ID = "SamiKoen/BF"
161
- api = HfApi(token=hfapi)
162
- for attempt in range(3): # 3 kez tekrar deneme
163
- try:
164
- with file_lock:
165
- api.upload_file(
166
- path_or_fileobj=LOG_FILE,
167
- path_in_repo="chat_logs.txt",
168
- repo_id=HF_REPO_ID,
169
- repo_type="space",
170
- commit_message="Otomatik log güncellemesi - {}".format(time.strftime("%Y-%m-%d %H:%M:%S"))
171
- )
172
- print(f"Log dosyası HF'ye yüklendi: {LOG_FILE}")
173
- break
174
- except Exception as e:
175
- print(f"HF yükleme hatası (deneme {attempt+1}/3): {e}")
176
- time.sleep(5)
177
- else:
178
- print("HF yükleme başarısız, tüm denemeler tamamlandı.")
179
- print(f"Zamanlanmış işlem tamamlandı: {time.strftime('%H:%M:%S')}")
180
 
181
- # Günlük zamanlanmış görevler
182
  schedule.every().day.at("11:32").do(scheduled_save_and_upload)
183
  schedule.every().day.at("15:15").do(scheduled_save_and_upload)
 
184
  schedule.every().day.at("17:32").do(scheduled_save_and_upload)
 
185
  schedule.every().day.at("21:30").do(scheduled_save_and_upload)
186
- print("Zamanlayıcı başlatıldı")
187
-
188
  while True:
189
  schedule.run_pending()
190
  time.sleep(60)
191
 
192
- # GPT API kullanarak chatbot yanıtı oluşturma
193
- def get_chatbot_response(messages):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  payload = {
195
  "model": "gpt-4.5-preview",
196
  "messages": messages,
@@ -208,8 +393,9 @@ def get_chatbot_response(messages):
208
 
209
  response = requests.post(API_URL, headers=headers, json=payload, stream=True)
210
  if response.status_code != 200:
211
- return "API hatası: Yanıt alınamadı."
212
-
 
213
  partial_response = ""
214
 
215
  for chunk in response.iter_lines():
@@ -224,181 +410,96 @@ def get_chatbot_response(messages):
224
  partial_response += delta['content']
225
  yield partial_response
226
  except json.JSONDecodeError as e:
227
- print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
228
  elif chunk_str == "data: [DONE]":
229
  break
230
-
231
- return partial_response
232
-
233
- # Soru gönderildikten sonra metin kutusunu temizleme
234
- def clear_textbox():
235
- return ""
236
 
237
- # Ses girişi işleme
238
- def process_audio_input(audio):
239
- if audio is None:
240
- return "Ses kaydedilemedi. Lütfen tekrar deneyin veya yazarak iletişim kurun."
241
-
242
- user_message = speech_to_text_azure_rest(audio)
243
- return user_message if user_message else "Ses tanınamadı. Lütfen tekrar deneyin veya yazarak iletişim kurun."
244
-
245
- # Ana chatbot fonksiyonu
246
- def chatbot_fn(user_message, history):
247
- if history is None:
248
- history = []
249
-
250
- # Eğer hiç mesaj yoksa hata döndür
251
- if not user_message:
252
- return history, None
253
-
254
- # Log: Kullanıcı mesajın�� ekle
255
  try:
256
  with file_lock:
257
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
258
- f.write(f"User: {user_message}\n")
259
  except Exception as e:
260
- print(f"Dosya yazma hatası (Kullanıcı): {e}")
261
-
262
- # Sistem mesajları
263
- system_messages = [
264
- {"role": "system", "content": "Sen bir AI Trek marka bisiklet uzmanı, bilir kişisi ve asistanısın. Trek ve Electra bisikletler konusunda uzmanım. Kullanıcılara nazik ve yardımcı şekilde cevap ver."},
265
- {"role": "system", "content": "Türkiye'deki Trek mağazaları: İstanbul'da üç Trek mağazamız var: Caddebostan, Ortaköy ve Sarıyer. İzmir'de yeni bir mağazamız Nisan 2025'te açılacak."}
266
- ]
267
-
268
- # Chabot geçmişi için doğru formatta dönüşüm yap
269
- formatted_history = []
270
- for i in range(0, len(history), 2):
271
- if i+1 < len(history):
272
- formatted_history.append({"role": "user", "content": history[i][0]})
273
- formatted_history.append({"role": "assistant", "content": history[i][1]})
274
-
275
- # Mesaj dizisini oluştur
276
- messages = system_messages + formatted_history + [{"role": "user", "content": user_message}]
277
-
278
- # Chatbot yanıtını al
279
- response_text = ""
280
  try:
281
- for partial_response in get_chatbot_response(messages):
282
- response_text = partial_response
 
 
283
  except Exception as e:
284
- print(f"Yanıt alma hatası: {e}")
285
- response_text = "Üzgünüm, yanıt alınırken bir hata oluştu. Lütfen tekrar deneyin."
 
 
 
 
 
 
 
286
 
287
- # Geçmişi güncelle
288
- history.append((user_message, response_text))
289
 
290
- # Yanıtı ses dosyasına dönüştür
291
- audio_response = None
292
- try:
293
- if SPEECH_KEY:
294
- audio_response = text_to_speech_azure_rest(response_text)
295
- except Exception as e:
296
- print(f"Ses oluşturma hatası: {e}")
297
 
298
- # Log: Asistan cevabını ekle
299
- try:
300
- with file_lock:
301
- with open(LOG_FILE, 'a', encoding='utf-8') as f:
302
- f.write(f"Bot: {response_text}\n")
303
- except Exception as e:
304
- print(f"Dosya yazma hatası (Bot): {e}")
 
 
305
 
306
- # Global geçmişi güncelle
307
- with history_lock:
308
- global_chat_history.append({"role": "user", "content": user_message})
309
- global_chat_history.append({"role": "assistant", "content": response_text})
 
 
 
 
 
 
310
 
311
- return history, audio_response
312
-
313
- # Gradio arayüzü
314
- def create_interface():
315
- with gr.Blocks(theme="default") as demo:
316
- gr.Markdown("# Trek Sesli Asistanı")
317
- gr.Markdown("Mikrofonla konuşmak için ses kaydedebilir veya metin mesajı gönderebilirsiniz.")
318
-
319
- chatbot = gr.Chatbot(height=600)
320
 
321
  with gr.Row():
322
- msg = gr.Textbox(
323
- placeholder="Mesajınızı yazın...",
324
- lines=2,
325
- interactive=True,
326
- scale=4
327
- )
328
- send_btn = gr.Button("Gönder", scale=1)
329
-
330
  with gr.Row():
331
- audio_input = gr.Audio(
332
- sources=["microphone"],
333
- type="blob",
334
- label="Sesli Soru Kaydet"
335
- )
336
- clear = gr.Button("Temizle")
337
-
338
- audio_output = gr.Audio(label="Sesli Yanıt", autoplay=True)
339
-
340
- status_text = gr.Markdown("Sistem hazır. Mesajınızı yazabilir veya sesli soru kaydedebilirsiniz.")
341
-
342
- # Event handlers
343
- msg.submit(chatbot_fn, [msg, chatbot], [chatbot, audio_output]).then(
344
- clear_textbox, None, [msg]
345
- ).then(
346
- lambda: "Mesajınız gönderildi ve yanıtlandı.",
347
- None,
348
- [status_text]
349
- )
350
-
351
- send_btn.click(chatbot_fn, [msg, chatbot], [chatbot, audio_output]).then(
352
- clear_textbox, None, [msg]
353
- ).then(
354
- lambda: "Mesajınız gönderildi ve yanıtlandı.",
355
- None,
356
- [status_text]
357
- )
358
-
359
- # Ses girişi olduğunda
360
  audio_input.change(
361
- process_audio_input,
362
- [audio_input],
363
- [msg]
364
- ).then(
365
- lambda: "Ses tanıma tamamlandı. 'Gönder' butonuna basın veya Enter tuşuna basın.",
366
- None,
367
- [status_text]
368
- )
369
-
370
- clear.click(lambda: None, None, chatbot, queue=False).then(
371
- lambda: "Sohbet geçmişi temizlendi.",
372
- None,
373
- [status_text]
374
  )
375
 
376
- gr.Markdown("### Trek Bisiklet Asistanı - Sesli İletişim")
377
- gr.Markdown("Trek bisikletler hakkında sorularınızı sesli veya yazılı olarak sorabilirsiniz.")
378
-
379
- return demo
380
-
381
- # Ortam değişkenlerini kontrol et
382
- def check_environment():
383
- missing_vars = []
384
- if not OPENAI_API_KEY:
385
- missing_vars.append("OPENAI_API_KEY")
386
- if not SPEECH_KEY:
387
- missing_vars.append("AZURE_SPEECH_KEY")
388
-
389
- if missing_vars:
390
- print(f"Uyarı: Aşağıdaki ortam değişkenleri eksik: {', '.join(missing_vars)}")
391
- print("Eksik değişkenler ilgili özelliklerin çalışmamasına neden olabilir.")
392
-
393
- # Çevre değişkenlerini kontrol et
394
- check_environment()
395
-
396
- # Scheduler thread'i başlat
397
- scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
398
- scheduler_thread.start()
399
-
400
- # Arayüzü oluştur ve başlat
401
- demo = create_interface()
402
 
403
  if __name__ == "__main__":
404
- demo.launch(debug=True)
 
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
 
19
+ # Suppress Gradio warnings
20
  warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
21
 
22
+ # Log file name and path
23
  LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
24
+ print(f"File path: {os.path.abspath(LOG_FILE)}")
25
 
26
+ # API settings
27
  API_URL = "https://api.openai.com/v1/chat/completions"
28
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
29
  if not OPENAI_API_KEY:
30
+ print("Error: OPENAI_API_KEY environment variable not set!")
 
 
 
 
31
 
32
+ # Fetch Trek bicycle products
33
+ url = 'https://www.trekbisiklet.com.tr/output/8582384479'
34
+ response = requests.get(url)
35
+ root = ET.fromstring(response.content)
36
 
37
+ products = []
38
+ for item in root.findall('item'):
39
+ # Get all products, then check stock status
40
+ name_words = item.find('rootlabel').text.lower().split()
41
+ name = name_words[0]
42
+ full_name = ' '.join(name_words)
43
+
44
+ stock_amount = "stokta" if item.find('stockAmount').text > '0' else "stokta değil"
45
+
46
+ # We don't add price/link info for out-of-stock products
47
+ if stock_amount == "stokta":
48
+ # Get normal price
49
+ price_str = item.find('priceTaxWithCur').text if item.find('priceTaxWithCur') is not None else "Fiyat bilgisi yok"
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
+ # Get EFT price (wire transfer discounted original price)
52
+ price_eft_str = item.find('priceEft').text if item.find('priceEft') is not None else ""
 
 
 
 
 
53
 
54
+ # Get discounted price (promotional price)
55
+ price_rebate_str = item.find('priceRebateWithTax').text if item.find('priceRebateWithTax') is not None else ""
56
 
57
+ # Get wire transfer discounted promotional price
58
+ price_rebate_money_order_str = item.find('priceRebateWithMoneyOrderWithTax').text if item.find('priceRebateWithMoneyOrderWithTax') is not None else ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ # Round normal price
61
+ try:
62
+ price_float = float(price_str)
63
+ # If price is above 200000, round to nearest 5000
64
+ if price_float > 200000:
65
+ price = str(round(price_float / 5000) * 5000)
66
+ # If price is above 30000, round to nearest 1000
67
+ elif price_float > 30000:
68
+ price = str(round(price_float / 1000) * 1000)
69
+ # If price is above 10000, round to nearest 100
70
+ elif price_float > 10000:
71
+ price = str(round(price_float / 100) * 100)
72
+ # Otherwise round to nearest 10
73
+ else:
74
+ price = str(round(price_float / 10) * 10)
75
+ except (ValueError, TypeError):
76
+ price = price_str # Keep as is if can't be converted to number
77
 
78
+ # Round wire transfer discounted original price (if any)
79
+ if price_eft_str:
80
+ try:
81
+ price_eft_float = float(price_eft_str)
82
+ # If price is above 200000, round to nearest 5000
83
+ if price_eft_float > 200000:
84
+ price_eft = str(round(price_eft_float / 5000) * 5000)
85
+ # If price is above 30000, round to nearest 1000
86
+ elif price_eft_float > 30000:
87
+ price_eft = str(round(price_eft_float / 1000) * 1000)
88
+ # If price is above 10000, round to nearest 100
89
+ elif price_eft_float > 10000:
90
+ price_eft = str(round(price_eft_float / 100) * 100)
91
+ # Otherwise round to nearest 10
92
+ else:
93
+ price_eft = str(round(price_eft_float / 10) * 10)
94
+ except (ValueError, TypeError):
95
+ price_eft = price_eft_str
96
+ else:
97
+ # If wire transfer discounted price not provided, calculate 2.5% discount from original price
98
+ try:
99
+ price_eft_float = price_float * 0.975 # 2.5% discount
100
+ # If price is above 200000, round to nearest 5000
101
+ if price_eft_float > 200000:
102
+ price_eft = str(round(price_eft_float / 5000) * 5000)
103
+ # If price is above 30000, round to nearest 1000
104
+ elif price_eft_float > 30000:
105
+ price_eft = str(round(price_eft_float / 1000) * 1000)
106
+ # If price is above 10000, round to nearest 100
107
+ elif price_eft_float > 10000:
108
+ price_eft = str(round(price_eft_float / 100) * 100)
109
+ # Otherwise round to nearest 10
110
+ else:
111
+ price_eft = str(round(price_eft_float / 10) * 10)
112
+ except (ValueError, TypeError):
113
+ price_eft = ""
114
 
115
+ # Round discounted price
116
  try:
117
+ if price_rebate_str:
118
+ price_rebate_float = float(price_rebate_str)
119
+ # If price is above 200000, round to nearest 5000
120
+ if price_rebate_float > 200000:
121
+ price_rebate = str(round(price_rebate_float / 5000) * 5000)
122
+ # If price is above 30000, round to nearest 1000
123
+ elif price_rebate_float > 30000:
124
+ price_rebate = str(round(price_rebate_float / 1000) * 1000)
125
+ # If price is above 10000, round to nearest 100
126
+ elif price_rebate_float > 10000:
127
+ price_rebate = str(round(price_rebate_float / 100) * 100)
128
+ # Otherwise round to nearest 10
129
+ else:
130
+ price_rebate = str(round(price_rebate_float / 10) * 10)
131
+ else:
132
+ price_rebate = ""
133
+ except (ValueError, TypeError):
134
+ price_rebate = price_rebate_str
135
 
136
+ # Round wire transfer discounted promotional price
137
+ try:
138
+ if price_rebate_money_order_str:
139
+ price_rebate_money_order_float = float(price_rebate_money_order_str)
140
+ # If price is above 200000, round to nearest 5000
141
+ if price_rebate_money_order_float > 200000:
142
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
143
+ # If price is above 30000, round to nearest 1000
144
+ elif price_rebate_money_order_float > 30000:
145
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
146
+ # If price is above 10000, round to nearest 100
147
+ elif price_rebate_money_order_float > 10000:
148
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
149
+ # Otherwise round to nearest 10
150
+ else:
151
+ price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
152
  else:
153
+ price_rebate_money_order = ""
154
+ except (ValueError, TypeError):
155
+ price_rebate_money_order = price_rebate_money_order_str
156
+
157
+ # Get only product link, not image link
158
+ product_link = item.find('productLink').text if item.find('productLink') is not None else ""
159
+
160
+ # Combine all price information
161
+ item_info = (stock_amount, price, product_link, price_eft, price_rebate, price_rebate_money_order)
162
+ else:
163
+ # For out-of-stock products, only indicate stock status, don't provide price and link info
164
+ item_info = (stock_amount, "", "", "", "", "")
165
+
166
+ products.append((name, item_info, full_name))
167
+
168
+ # Hugging Face token
169
+ hfapi = os.getenv("hfapi")
170
+ if not hfapi:
171
+ raise ValueError("hfapi environment variable not set!")
172
+
173
+ create_repo("BF", token=hfapi, repo_type="space", space_sdk="gradio", exist_ok=True)
174
+
175
+ global_chat_history = [] # All chat history
176
+ history_lock = threading.Lock() # Lock for global history
177
+ file_lock = threading.Lock() # Lock for file writing
178
+ last_logged_index = 0 # Last saved message index
179
+
180
+ # Google Drive authentication (Service Account from Secrets)
181
+ SCOPES = ['https://www.googleapis.com/auth/drive.readonly']
182
+
183
+ def authenticate_google_drive():
184
+ service_account_json = os.getenv("SERVICE_ACCOUNT_JSON")
185
+ if not service_account_json:
186
+ raise ValueError("SERVICE_ACCOUNT_JSON environment variable not found!")
187
+
188
+ try:
189
+ json_data = json.loads(service_account_json)
190
+ print("JSON successfully parsed:", json_data.keys())
191
+ print("Private key content:", json_data.get("private_key"))
192
+ creds = Credentials.from_service_account_info(json_data, scopes=SCOPES)
193
+ except json.JSONDecodeError as e:
194
+ raise ValueError(f"Invalid JSON from Secrets: {e}")
195
  except Exception as e:
196
+ raise ValueError(f"Authentication error: {e}")
197
+
198
+ return build('drive', 'v3', credentials=creds)
199
+
200
+ def download_file_from_drive(service, file_id, file_name):
201
+ request = service.files().get_media(fileId=file_id)
202
+ fh = io.BytesIO()
203
+ downloader = MediaIoBaseDownload(fh, request)
204
+ done = False
205
+ while done is False:
206
+ status, done = downloader.next_chunk()
207
+ fh.seek(0)
208
+ with open(file_name, 'wb') as f:
209
+ f.write(fh.read())
210
+ return file_name
211
+
212
+ # Pull documents from Google Drive and process
213
+ document_content = ""
214
+ service = authenticate_google_drive()
215
+
216
+ try:
217
+ # Excel file from Google Drive
218
+ excel_file_id = "test10rgiGp5y5ZYU0dpRvps2t0t1dEzjW8LK" # Example Excel file ID, add your own ID
219
+ excel_file = download_file_from_drive(service, excel_file_id, "veriler.xlsx")
220
+ df = pd.read_excel(excel_file)
221
+ excel_text = df.to_string()
222
+ document_content += excel_text + "\n"
223
+ except Exception as e:
224
+ print(f"Google Drive Excel reading error: {e}")
225
+
226
+ try:
227
+ # Word file from Google Drive
228
+ word_file_id = "9I8H7G6F5E4D3C2B1A" # Example Word file ID, add your own ID
229
+ word_file = download_file_from_drive(service, word_file_id, "aciklamalar.docx")
230
+ doc = Document(word_file)
231
+ word_text = "\n".join([para.text for para in doc.paragraphs])
232
+ document_content += word_text
233
+ except Exception as e:
234
+ print(f"Google Drive Word reading error: {e}")
235
 
 
236
  def run_scheduler(chat_history_ref):
 
 
237
  def scheduled_save_and_upload():
238
  global last_logged_index
239
  if chat_history_ref:
240
+ print(f"Timer triggered: {time.strftime('%Y-%m-%d %H:%M:%S')}")
241
  try:
242
+ with file_lock: # File writing lock
243
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
244
+ f.write("\n--- Scheduled Save: {} ---\n".format(time.strftime("%Y-%m-%d %H:%M:%S")))
245
+ with history_lock: # History lock
246
  new_messages = chat_history_ref[last_logged_index:]
247
  for msg in new_messages:
248
  if msg["role"] in ["user", "assistant"]:
249
  f.write(f"{msg['role'].capitalize()}: {msg['content']}\n")
250
+ last_logged_index = len(chat_history_ref) # Update last index
251
+ print(f"Chat saved to file: {os.path.abspath(LOG_FILE)}")
252
  except Exception as e:
253
+ print(f"Save error: {e}")
254
+ time.sleep(5) # Wait before retrying
255
+ return # Early exit for retry
256
 
257
+ HF_REPO_ID = "SamiKoen/BF"
258
+ api = HfApi(token=hfapi)
259
+ for attempt in range(3): # Try 3 times
260
+ try:
261
+ with file_lock:
262
+ api.upload_file(
263
+ path_or_fileobj=LOG_FILE,
264
+ path_in_repo="chat_logs.txt",
265
+ repo_id=HF_REPO_ID,
266
+ repo_type="space",
267
+ commit_message="Automatic log update - {}".format(time.strftime("%Y-%m-%d %H:%M:%S"))
268
+ )
269
+ print(f"Log file uploaded to HF: {LOG_FILE}")
270
+ break
271
+ except Exception as e:
272
+ print(f"HF upload error (attempt {attempt+1}/3): {e}")
273
+ time.sleep(5)
274
+ else:
275
+ print("HF upload failed, all attempts completed.")
276
+ print(f"Scheduled task completed: {time.strftime('%H:%M:%S')}")
 
277
 
 
278
  schedule.every().day.at("11:32").do(scheduled_save_and_upload)
279
  schedule.every().day.at("15:15").do(scheduled_save_and_upload)
280
+ schedule.every().day.at("15:30").do(scheduled_save_and_upload)
281
  schedule.every().day.at("17:32").do(scheduled_save_and_upload)
282
+ schedule.every().day.at("19:15").do(scheduled_save_and_upload)
283
  schedule.every().day.at("21:30").do(scheduled_save_and_upload)
284
+ print("Timer started")
 
285
  while True:
286
  schedule.run_pending()
287
  time.sleep(60)
288
 
289
+ # Define system messages for the chatbot
290
+ def get_system_messages():
291
+ return [
292
+ {"role": "system", "content": "Sen bir AI Trek marka bisiklet uzmanı, bilir kişisi ve asistanısın."},
293
+ # Additional system messages would go here
294
+ ]
295
+
296
+ @spaces.GPU(duration=1200)
297
+ def chatbot_fn(user_message, history):
298
+ if history is None:
299
+ history = []
300
+
301
+ # Log: Add user message
302
+ try:
303
+ with file_lock:
304
+ with open(LOG_FILE, 'a', encoding='utf-8') as f:
305
+ f.write(f"User: {user_message}\n")
306
+ except Exception as e:
307
+ print(f"File writing error (User): {e}")
308
+
309
+ # System messages
310
+ system_messages = get_system_messages()
311
+
312
+ # Add document data to system messages
313
+ if document_content:
314
+ system_messages.append({"role": "system", "content": f"Document information: {document_content}"})
315
+
316
+ # Add product info if mentioned in user message
317
+ input_words = user_message.lower().split()
318
+ for product_info in products:
319
+ if product_info[0] in input_words:
320
+ if product_info[1][0] == "stokta":
321
+ # Check for promotional price
322
+ has_campaign = product_info[1][4] and product_info[1][4] != ""
323
+
324
+ # Original price is always shown
325
+ normal_price = f"Original price: {product_info[1][1]} TL"
326
+
327
+ # Promotional price info
328
+ rebate_price = ""
329
+ discount_info = ""
330
+ eft_price = ""
331
+ rebate_money_order_price = ""
332
+
333
+ if has_campaign:
334
+ # If product is on sale, show promotional price
335
+ rebate_price = f"\nPromotional price: {product_info[1][4]} TL"
336
+
337
+ # Calculate discount amount
338
+ try:
339
+ original_price = float(product_info[1][1].replace(',', '.'))
340
+ campaign_price = float(product_info[1][4].replace(',', '.'))
341
+ discount_amount = original_price - campaign_price
342
+
343
+ # Show discount amount if greater than 0
344
+ if discount_amount > 0:
345
+ # Apply rounding rules for discount amount
346
+ if discount_amount > 200000:
347
+ discount_amount_rounded = round(discount_amount / 5000) * 5000
348
+ elif discount_amount > 30000:
349
+ discount_amount_rounded = round(discount_amount / 1000) * 1000
350
+ elif discount_amount > 10000:
351
+ discount_amount_rounded = round(discount_amount / 100) * 100
352
+ else:
353
+ discount_amount_rounded = round(discount_amount / 10) * 10
354
+
355
+ # Discount info
356
+ discount_info = f"\nDiscount amount: {discount_amount_rounded:.0f} TL"
357
+ except (ValueError, TypeError):
358
+ discount_info = ""
359
+
360
+ # Show wire transfer discounted promotional price
361
+ rebate_money_order_price = ""
362
+ else:
363
+ # If not on sale, show wire transfer discounted normal price
364
+ if product_info[1][3] and product_info[1][3] != "":
365
+ eft_price = f"\nWire transfer price: {product_info[1][3]} TL"
366
+
367
+ # Product link
368
+ product_link = f"\nProduct link: {product_info[1][2]}"
369
+
370
+ # Combine all information
371
+ 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}"
372
+ else:
373
+ # If product is out of stock, only indicate stock status
374
+ new_msg = f"{product_info[2]} {product_info[1][0]}"
375
+ system_messages.append({"role": "system", "content": new_msg})
376
+
377
+ messages = system_messages + history + [{"role": "user", "content": user_message}]
378
+
379
  payload = {
380
  "model": "gpt-4.5-preview",
381
  "messages": messages,
 
393
 
394
  response = requests.post(API_URL, headers=headers, json=payload, stream=True)
395
  if response.status_code != 200:
396
+ yield "An error occurred."
397
+ return
398
+
399
  partial_response = ""
400
 
401
  for chunk in response.iter_lines():
 
410
  partial_response += delta['content']
411
  yield partial_response
412
  except json.JSONDecodeError as e:
413
+ print(f"JSON parse error: {e} - Chunk: {chunk_str}")
414
  elif chunk_str == "data: [DONE]":
415
  break
 
 
 
 
 
 
416
 
417
+ # Log: Add assistant response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  try:
419
  with file_lock:
420
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
421
+ f.write(f"Bot: {partial_response}\n")
422
  except Exception as e:
423
+ print(f"File writing error (Bot): {e}")
424
+
425
+ # Update global history
426
+ with history_lock:
427
+ global_chat_history.append({"role": "user", "content": user_message})
428
+ global_chat_history.append({"role": "assistant", "content": partial_response})
429
+
430
+ # Text-to-speech function
431
+ def text_to_speech(text):
432
+ # You can implement TTS functionality here
433
+ # This is a placeholder for actual TTS implementation
434
+ return None
435
+
436
+ # Speech-to-text function
437
+ def speech_to_text(audio):
438
+ # You can implement STT functionality here
439
+ # This is a placeholder for actual STT implementation
 
 
 
440
  try:
441
+ # Use an STT model to convert audio to text
442
+ # This is a placeholder
443
+ transcription = "Placeholder transcription"
444
+ return transcription
445
  except Exception as e:
446
+ print(f"Speech recognition error: {e}")
447
+ return ""
448
+
449
+ # Function to handle voice input
450
+ def voice_input_handler(audio, history):
451
+ # Convert speech to text
452
+ text = speech_to_text(audio)
453
+ if not text:
454
+ return "I couldn't understand that. Please try again.", history
455
 
456
+ # Get chatbot response using the text
457
+ response_generator = chatbot_fn(text, history)
458
 
459
+ # Get the final response
460
+ response = None
461
+ for r in response_generator:
462
+ response = r
 
 
 
463
 
464
+ return text, history + [{"role": "user", "content": text}, {"role": "assistant", "content": response}]
465
+
466
+ # Initialize scheduler thread
467
+ scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
468
+ scheduler_thread.start()
469
+
470
+ # Create Gradio interface with voice capabilities
471
+ with gr.Blocks() as demo:
472
+ gr.Markdown("# Trek Voice Assistant")
473
 
474
+ with gr.Tab("Text Chat"):
475
+ chatbot = gr.ChatInterface(
476
+ fn=chatbot_fn,
477
+ title="Trek Assistant",
478
+ theme="default",
479
+ type="messages",
480
+ flagging_mode="manual",
481
+ flagging_options=["Correct", "Incorrect", "Not sure", "Other"],
482
+ save_history=True
483
+ )
484
 
485
+ with gr.Tab("Voice Chat"):
486
+ chat_history = gr.State([])
 
 
 
 
 
 
 
487
 
488
  with gr.Row():
489
+ with gr.Column(scale=4):
490
+ display = gr.Chatbot(height=400)
491
+
 
 
 
 
 
492
  with gr.Row():
493
+ audio_input = gr.Audio(source="microphone", type="filepath", label="Speak")
494
+ clear_btn = gr.Button("Clear")
495
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
  audio_input.change(
497
+ fn=voice_input_handler,
498
+ inputs=[audio_input, chat_history],
499
+ outputs=[display, chat_history]
 
 
 
 
 
 
 
 
 
 
500
  )
501
 
502
+ clear_btn.click(lambda: ([], []), outputs=[display, chat_history])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
 
504
  if __name__ == "__main__":
505
+ demo.launch(debug=True, enable_queue=True)