BF / app.py
SamiKoen's picture
Update app.py
95cf217 verified
raw
history blame
11.5 kB
import gradio as gr
import os
import json
import requests
import xml.etree.ElementTree as ET
import schedule
import time
import threading
from huggingface_hub import HfApi, create_repo
import warnings
# Gradio uyarılarını bastır
warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
# Log dosyası adı ve yolu
LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}")
# API ayarları
API_URL = "https://api.openai.com/v1/chat/completions"
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
# Trek bisiklet ürünlerini çekme
url = 'https://www.trekbisiklet.com.tr/output/8582384479'
response = requests.get(url)
root = ET.fromstring(response.content)
products = []
for item in root.findall('item'):
if item.find('isOptionOfAProduct').text == '1' and item.find('stockAmount').text > '0':
name_words = item.find('rootlabel').text.lower().split()
name = name_words[0]
full_name = ' '.join(name_words)
stock_amount = "stokta"
price = item.find('priceWithTax').text
item_info = (stock_amount, price)
products.append((name, item_info, full_name))
# Hugging Face token
hfapi = os.getenv("hfapi")
if not hfapi:
raise ValueError("hfapi ortam değişkeni ayarlanmamış!")
create_repo("BF", token=hfapi, repo_type="space", space_sdk="gradio", exist_ok=True)
global_chat_history = [] # Tüm sohbet geçmişi
history_lock = threading.Lock() # Global geçmiş için kilit
file_lock = threading.Lock() # Dosya yazma için kilit
last_logged_index = 0 # Son kaydedilen mesaj indeksi
def run_scheduler(chat_history_ref):
def scheduled_save_and_upload():
global last_logged_index
if chat_history_ref:
print(f"Zamanlayıcı tetiklendi: {time.strftime('%Y-%m-%d %H:%M:%S')}")
try:
with file_lock: # Dosya yazma kilidi
with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write("\n--- Zamanlanmış Kayıt: {} ---\n".format(time.strftime("%Y-%m-%d %H:%M:%S")))
with history_lock: # Geçmiş kilidi
new_messages = chat_history_ref[last_logged_index:]
for msg in new_messages:
if msg["role"] in ["user", "assistant"]:
f.write(f"{msg['role'].capitalize()}: {msg['content']}\n")
last_logged_index = len(chat_history_ref) # Son indeksi güncelle
print(f"Sohbet dosyaya kaydedildi: {os.path.abspath(LOG_FILE)}")
except Exception as e:
print(f"Kayıt hatası: {e}")
time.sleep(5) # Hata sonrası tekrar denemeden önce bekle
return # Tekrar deneme için erken çıkış
HF_REPO_ID = "SamiKoen/BF"
api = HfApi(token=hfapi)
for attempt in range(3): # 3 kez tekrar deneme
try:
with file_lock:
api.upload_file(
path_or_fileobj=LOG_FILE,
path_in_repo="chat_logs.txt",
repo_id=HF_REPO_ID,
repo_type="space",
commit_message="Otomatik log güncellemesi - {}".format(time.strftime("%Y-%m-%d %H:%M:%S"))
)
print(f"Log dosyası HF'ye yüklendi: {LOG_FILE}")
break
except Exception as e:
print(f"HF yükleme hatası (deneme {attempt+1}/3): {e}")
time.sleep(5)
else:
print("HF yükleme başarısız, tüm denemeler tamamlandı.")
print(f"Zamanlanmış işlem tamamlandı: {time.strftime('%H:%M:%S')}")
schedule.every().day.at("11:32").do(scheduled_save_and_upload)
schedule.every().day.at("15:15").do(scheduled_save_and_upload)
schedule.every().day.at("15:30").do(scheduled_save_and_upload)
schedule.every().day.at("17:32").do(scheduled_save_and_upload)
schedule.every().day.at("19:15").do(scheduled_save_and_upload)
schedule.every().day.at("21:30").do(scheduled_save_and_upload)
print("Zamanlayıcı başlatıldı")
while True:
schedule.run_pending()
time.sleep(60)
def chatbot_fn(user_message, history):
if history is None:
history = []
# Log: Kullanıcı mesajını ekle
try:
with file_lock:
with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(f"User: {user_message}\n")
except Exception as e:
print(f"Dosya yazma hatası (Kullanıcı): {e}")
# Sistem mesajları
system_messages = [
{"role": "system", "content": "Sen bir Trek bisiklet satış ve danışman asistanısın; Trek, Electra bisikletler, Bontrager aksesuarlar, Bryton yol bilgisayarları ve Trieye gözlükler konusunda uzmanım."},
{"role": "system", "content": "Başka markalar (ör. Specialized, Giant) hakkında bilgi vermem ve yorum yapmam."},
{"role": "system", "content": "Bir önceki sohbeti unuturum ve yalnızca aşağıda yan yana yazılan bilgilerden cevap veririm."},
{"role": "system", "content": "Stok kontrolüm gerçek zamanlıdır; stokta yoksa 'yok' derim."},
{"role": "system", "content": "Model adı rakamsız gelirse (ör. Madone SL), rakam eklenmesini rica ederim (ör. Madone SL 7)."},
{"role": "system", "content": "Madone SLR sorulursa Gen 7 kabul ederim."},
{"role": "system", "content": "Yol bisikletleri (Madone, Émonda, Domane, Checkpoint, Speed Concept) boyları 47-64 cm'dir."},
{"role": "system", "content": "Dağ bisikletleri (Marlin, Roscoe, Procaliber, Supercaliber, Fuel EX) boyları XXS-XL'dir."},
{"role": "system", "content": "Şehir bisikletleri FX ve DS’dir; elektrikli modeller Powerfly, Rail, Fuel EXe, Domane+ SLR, Verve+’dır; gravel için Checkpoint vardır."},
{"role": "system", "content": "Yeni Madone Gen 8 (27 Haziran 2024): Émonda kadar hafif (900 Serisi OCLV Karbon, 320g hafif) ve Madone Gen 7 kadar hızlıdır."},
{"role": "system", "content": "IsoFlow ile %80 konforludur; SL ekonomik, SLR üst seviyedir."},
{"role": "system", "content": "Stok ve fiyatlar için www.trekbisiklet.com.tr’ye bakarım; farklı boy/renk varsa söylerim, yoksa başka model öneririm."},
{"role": "system", "content": "En büyük/küçük boy sorusuna stoktan cevap veririm; üyelere özel fiyatlar için siteye üye olun derim."},
{"role": "system", "content": "İstanbul’da Caddebostan mağazamız (0216 6292432, 10:00-19:00, Bike Fit 3500 TL) ve Ortaköy mağazamız (0212 2271015, 10:00-19:00) bulunmaktadır."},
{"role": "system", "content": "Sarıyer mağazamız (0542 1371080, 10:00-19:00) elektrikli bisiklet odaklıdır."},
{"role": "system", "content": "İzmir Alsancak mağazası Nisan 2025’te açılacak ve Pazar günleri kapalı olacak."},
{"role": "system", "content": "Bontrager aksesuarlar, Bryton Rider S800 stokta ve Trieye gözlükler Norveç menşeli geri görüş aynalıdır."},
{"role": "system", "content": "Bike Finder olarak adım adım sorarım: 1) kategori (yol, dağ, hibrit, gravel, elektrikli), 2) amaç (ulaşım, spor, yarış), 3) özellik (performans, konfor)."},
{"role": "system", "content": "4) zemin (asfalt, off-road), 5) boy ve iç bacak ölçüsü, 6) ek tercih (renk, bütçe) sorularını sorarım; sonra stoktan öneri yapar www.trekbisiklet.com.tr’ye yönlendiririm."},
{"role": "system", "content": "Sipariş süreci: sepete ekle, bilgi gir, ödeme yap, tamamla; Trek kadroları ömür boyu garantilidir; 2000’den beri Alatin Bisiklet dağıtıyor; ASLA DURMA ve TREK RMK DYNAMIS’e sponsoruz; takas için www.bikeexchangehub.com, canlı sohbet için sitedeki yeşil düğme, bayiler için www.alatin.com.tr/sayfa/bayilerimiz/ kullanılır."}
]
# Kullanıcı mesajında ürün ismi geçiyorsa ekle
input_words = user_message.lower().split()
for product_info in products:
if product_info[0] in input_words:
new_msg = f"{product_info[2]} {product_info[1][0]} ve fiyatı EURO {product_info[1][1]}"
system_messages.append({"role": "system", "content": new_msg})
messages = system_messages + history + [{"role": "user", "content": user_message}]
payload = {
"model": "gpt-4o",
"messages": messages,
"temperature": 0.7,
"top_p": 0.9,
"n": 1,
"stream": True,
"presence_penalty": 0,
"frequency_penalty": 0,
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {OPENAI_API_KEY}"
}
response = requests.post(API_URL, headers=headers, json=payload, stream=True)
if response.status_code != 200:
yield [{"role": "user", "content": user_message}, {"role": "assistant", "content": "Bir hata oluştu."}]
return
partial_response = ""
current_pair = [{"role": "user", "content": user_message}, {"role": "assistant", "content": ""}]
for chunk in response.iter_lines():
if not chunk:
continue
chunk_str = chunk.decode('utf-8')
if chunk_str.startswith("data: ") and chunk_str != "data: [DONE]":
try:
chunk_data = json.loads(chunk_str[6:])
delta = chunk_data['choices'][0]['delta']
if 'content' in delta:
partial_response += delta['content']
current_pair[1]["content"] = partial_response
yield current_pair
except json.JSONDecodeError as e:
print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
elif chunk_str == "data: [DONE]":
break
# Log: Asistan cevabını ekle
try:
with file_lock:
with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(f"Bot: {partial_response}\n")
except Exception as e:
print(f"Dosya yazma hatası (Bot): {e}")
# Global geçmişi güncelle
with history_lock:
global_chat_history.append({"role": "user", "content": user_message})
global_chat_history.append({"role": "assistant", "content": partial_response})
yield current_pair
# Slow echo (test için)
def slow_echo(message, history):
for i in range(len(message)):
time.sleep(0.05)
yield [{"role": "user", "content": message}, {"role": "assistant", "content": "You typed: " + message[: i + 1]}]
# Kullanım modu
USE_SLOW_ECHO = False
chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn
if not USE_SLOW_ECHO:
scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
scheduler_thread.start()
demo = gr.ChatInterface(
fn=chat_fn,
title="Trek Asistanı",
#description="Hoş geldiniz! Trek ile ilgili sorularınızı yanıtlıyorum.",
theme="default",
type="messages",
flagging_mode="manual",
flagging_options=["Doğru", "Yanlış", "Emin değilim", "Diğer"],
save_history=True
)
if __name__ == "__main__":
demo.launch(debug=True, share=True)