BFyedek / app.py
SamiKoen's picture
Update app.py
db17c0f verified
raw
history blame
10.9 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
# 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; [ {role, content}, ... ]
def run_scheduler(chat_history_ref):
def scheduled_save_and_upload():
if chat_history_ref:
print(f"Zamanlayıcı tetiklendi: {time.strftime('%Y-%m-%d %H:%M:%S')}")
try:
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")))
for msg in chat_history_ref:
if msg["role"] in ["user", "assistant"]:
f.write(f"{msg['role'].capitalize()}: {msg['content']}\n")
print(f"Sohbet dosyaya kaydedildi: {os.path.abspath(LOG_FILE)}")
except Exception as e:
print(f"Kayıt hatası: {e}")
HF_REPO_ID = "SamiKoen/BF"
api = HfApi(token=hfapi)
try:
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 dosyası güncellemesi - {}".format(time.strftime("%Y-%m-%d %H:%M:%S"))
)
print(f"Log dosyası HF'ye yüklendi: {LOG_FILE}")
except Exception as e:
print(f"HF yükleme hatası: {e}")
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()
print(f"Zamanlayıcı çalışıyor, bekliyor: {time.strftime('%H:%M:%S')}")
time.sleep(60)
def chatbot_fn(user_message, history):
"""
ChatInterface fonksiyonu; yalnızca mevcut kullanıcı mesajı ve asistan cevabını döndürür.
Önceki geçmişin birleşmesini önlemek için her turda sadece yeni mesaj çifti yield edilir.
"""
if history is None:
history = []
# Log: Kullanıcı mesajını ekle
try:
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ı (örnek)
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."}
]
# Eğer 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})
# Tam geçmiş: sistem mesajları + önceki history + yeni kullanıcı mesajı
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 = ""
# Streaming: sadece mevcut turun mesaj çifti yield edilecek
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 # Sadece asistan cevabını güncelle
yield current_pair # Her adımda yalnızca mevcut mesaj çifti
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 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 (isteğe bağlı)
global_chat_history.append({"role": "user", "content": user_message})
global_chat_history.append({"role": "assistant", "content": partial_response})
# Son hali döndür
yield current_pair
# Ek: slow_echo fonksiyonu (basit streaming örneği)
def slow_echo(message, history):
for i in range(len(message)):
time.sleep(0.05)
yield [{"role": "user", "content": message}, {"role": "assistant", "content": + message[: i + 1]}]
# Kullanmak istediğiniz mod:
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=["Like", "Spam", "Inappropriate", "Other"],
save_history=True
)
if __name__ == "__main__":
demo.launch(debug=True, share=True)