SamiKoen commited on
Commit
5d95d68
·
verified ·
1 Parent(s): 3162098

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +241 -85
app.py CHANGED
@@ -1,100 +1,256 @@
1
- import gradio as gr
2
  import os
3
- import json
4
- import requests
5
- from openai import OpenAI # OpenAI Python istemcisini kullanacağız
6
- import io
 
 
 
7
 
8
- # OpenAI istemcisini başlat
9
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
10
- client = OpenAI(api_key=OPENAI_API_KEY)
11
 
12
- # Mevcut chatbot_fn fonksiyonunu sesli hale getir
13
- @spaces.GPU(duration=1200)
14
- def chatbot_fn(user_message, history):
15
- if history is None:
16
- history = []
17
 
18
- # Log: Kullanıcı mesajını ekle (mevcut kodun aynısı)
19
- try:
20
- with file_lock:
21
- with open(LOG_FILE, 'a', encoding='utf-8') as f:
22
- f.write(f"User: {user_message}\n")
23
- except Exception as e:
24
- print(f"Dosya yazma hatası (Kullanıcı): {e}")
25
 
26
- # Sistem mesajları ve ürün bilgisi (mevcut kodun aynısı)
27
- system_messages = [...] # Mevcut sistem mesajlarını buraya koy
28
- messages = system_messages + history + [{"role": "user", "content": user_message}]
29
-
30
- # OpenAI Chat Completions isteği
31
- payload = {
32
- "model": "gpt-4.5-preview", # Model adını kontrol et, doğru olduğundan emin ol
33
- "messages": messages,
34
- "temperature": 0.2,
35
- "stream": True,
36
- }
37
- headers = {
38
- "Content-Type": "application/json",
39
- "Authorization": f"Bearer {OPENAI_API_KEY}"
40
- }
41
-
42
- response = requests.post(API_URL, headers=headers, json=payload, stream=True)
43
- if response.status_code != 200:
44
- yield "Bir hata oluştu.", None
45
- return
46
 
47
- partial_response = ""
48
- for chunk in response.iter_lines():
49
- if not chunk:
50
- continue
51
- chunk_str = chunk.decode('utf-8')
52
- if chunk_str.startswith("data: ") and chunk_str != "data: [DONE]":
53
- try:
54
- chunk_data = json.loads(chunk_str[6:])
55
- delta = chunk_data['choices'][0]['delta']
56
- if 'content' in delta:
57
- partial_response += delta['content']
58
- yield partial_response, None # Metni gerçek zamanlı göster
59
- except json.JSONDecodeError as e:
60
- print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
61
- elif chunk_str == "data: [DONE]":
 
 
 
62
  break
 
63
 
64
- # TTS ile ses oluştur
65
- audio_response = client.audio.speech.create(
66
- model="tts-1",
67
- voice="alloy", # Ses seçeneği: alloy, echo, fable, onyx, nova, shimmer
68
- input=partial_response
69
- )
70
- audio_buffer = io.BytesIO(audio_response.content) # Ses dosyasını bellekte tut
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- # Log: Asistan cevabını ekle
 
 
 
 
73
  try:
74
- with file_lock:
75
- with open(LOG_FILE, 'a', encoding='utf-8') as f:
76
- f.write(f"Bot: {partial_response}\n")
77
  except Exception as e:
78
- print(f"Dosya yazma hatası (Bot): {e}")
79
 
80
- # Global geçmişi güncelle
81
- with history_lock:
82
- global_chat_history.append({"role": "user", "content": user_message})
83
- global_chat_history.append({"role": "assistant", "content": partial_response})
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
- yield partial_response, audio_buffer # Hem metin hem ses döndür
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- # Gradio arayüzü
88
- demo = gr.ChatInterface(
89
- fn=chatbot_fn,
90
- title="Trek Asistanı",
91
- theme="default",
92
- type="messages",
93
- additional_outputs=[gr.Audio(label="Sesli Yanıt", type="file")], # Ses çıkışı ekle
94
- flagging_mode="manual",
95
- flagging_options=["Doğru", "Yanlış", "Emin değilim", "Diğer"],
96
- save_history=True
97
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
- if __name__ == "__main__":
100
- demo.launch(debug=True)
 
 
1
  import os
2
+ import openai
3
+ import gradio as gr
4
+ import xml.etree.ElementTree as ET
5
+ import pandas as pd # Excel okuma için pandas
6
+ import docx # Word (.docx) dosyalarını okuma
7
+ import threading
8
+ import time
9
 
10
+ # OpenAI API anahtarını ortam değişkeninden al
11
+ openai.api_key = os.getenv("OPENAI_API_KEY")
 
12
 
13
+ # OpenAI modeli ayarları
14
+ OPENAI_CHAT_MODEL = "gpt-4" # Metin tabanlı GPT modeli
15
+ TTS_MODEL = "tts-1" # OpenAI TTS modeli
16
+ TTS_VOICE = "nova" # Tercih edilen ses
 
17
 
18
+ # Trek ürün bilgileri XML dosyasını yükle (stok ve fiyat kontrolü için)
19
+ try:
20
+ trek_tree = ET.parse("trek_products.xml")
21
+ trek_root = trek_tree.getroot()
22
+ except Exception as e:
23
+ trek_root = None
24
+ print(f"XML yüklenirken hata oluştu: {e}")
25
 
26
+ # Sistem mesajı ve özel kurallar (sohbetin her başında yer alacak temel yönergeler)
27
+ SYSTEM_PROMPT = (
28
+ "You are Trek Chatbot, an AI assistant that provides information about Trek products and performs various tasks. "
29
+ "Follow the given rules and guidelines. If the user asks for Trek product info, provide stock and price from the XML database. "
30
+ "If the user requests to read a file, fetch its content from Google Drive. "
31
+ "Always respond helpfully in the same language as the user's question, and keep a friendly tone."
32
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ def check_trek_product_info(product_id):
35
+ """
36
+ Verilen ürün kodu/ID için Trek XML veritabanından fiyat ve stok bilgisini döndürür.
37
+ Bulunamazsa (None, None) döndürür.
38
+ """
39
+ price = None
40
+ stock = None
41
+ if trek_root is None:
42
+ return price, stock
43
+ # XML yapısında <product><id>...</id><price>...</price><stock>...</stock></product> şeklinde olduğunu varsayıyoruz
44
+ for product in trek_root.findall("product"):
45
+ pid = product.find("id").text if product.find("id") is not None else ""
46
+ if pid and product_id.lower() == pid.lower():
47
+ # Ürün eşleşti, fiyat ve stok bilgilerini al
48
+ price_elem = product.find("price")
49
+ stock_elem = product.find("stock")
50
+ price = price_elem.text if price_elem is not None else None
51
+ stock = stock_elem.text if stock_elem is not None else None
52
  break
53
+ return price, stock
54
 
55
+ def read_file_from_drive(file_path):
56
+ """
57
+ Google Drive üzerinde bulunan bir Excel (.xlsx) veya Word (.docx) dosyasını okuyup içeriğini döndürür.
58
+ """
59
+ content = ""
60
+ try:
61
+ if file_path.lower().endswith((".xlsx", ".xls")):
62
+ # Excel dosyasını oku
63
+ df = pd.read_excel(file_path)
64
+ # Excel içeriğini CSV formatında metne dönüştür (alternatif: istenen şekilde işlenebilir)
65
+ content = df.to_csv(index=False)
66
+ elif file_path.lower().endswith(".docx"):
67
+ # Word dosyasını oku
68
+ doc = docx.Document(file_path)
69
+ full_text = [para.text for para in doc.paragraphs]
70
+ content = "\n".join(full_text).strip()
71
+ else:
72
+ # Metin dosyası olarak oku (Excel/Word dışındaki dosyalar için genel durum)
73
+ with open(file_path, 'r', encoding='utf-8') as f:
74
+ content = f.read()
75
+ except Exception as e:
76
+ content = f"Hata: Dosya okunurken sorun oluştu - {e}"
77
+ return content
78
 
79
+ def log_to_huggingface(log_content):
80
+ """
81
+ Konuşma geçmişini veya belirli bir log bilgisini Hugging Face'e yükler.
82
+ Bu örnekte işlev gövdesi boş bırakılmıştır; gerçek uygulamada API çağrısı yapılmalıdır.
83
+ """
84
  try:
85
+ # TODO: Hugging Face'e yükleme kodunu buraya entegre edin (örneğin huggingface_hub kullanarak).
86
+ # Örnek: HfApi().upload_file(...) ile log_content içeriğini bir depo/dosya olarak yükleyebilirsiniz.
87
+ pass
88
  except Exception as e:
89
+ print(f"Hugging Face log yüklemede hata: {e}")
90
 
91
+ def periodic_log(interval=300):
92
+ """
93
+ Sohbet geçmişini belirli aralıklarla (interval saniye) Hugging Face'e gönderen arkaplan görevi.
94
+ """
95
+ while True:
96
+ time.sleep(interval)
97
+ try:
98
+ # Sistem mesajı haricindeki konuşmaları derle
99
+ conversation_text = "\n".join(
100
+ [f"{m['role'].capitalize()}: {m['content']}"
101
+ for m in conversation_log if m.get('role') != 'system']
102
+ )
103
+ if conversation_text:
104
+ log_to_huggingface(conversation_text)
105
+ except Exception as e:
106
+ print(f"Periyodik loglama hatası: {e}")
107
 
108
+ def generate_text_response(conversation):
109
+ """
110
+ OpenAI ChatCompletion API ile güncel konuşma geçmişine göre metin yanıtı üretir.
111
+ """
112
+ try:
113
+ response = openai.ChatCompletion.create(
114
+ model=OPENAI_CHAT_MODEL,
115
+ messages=conversation
116
+ )
117
+ answer = response['choices'][0]['message']['content'].strip()
118
+ except Exception as e:
119
+ # Hata durumunda özür dileyen bir mesaj döndür (hata mesajını da içerebilir)
120
+ answer = f"Üzgünüm, yanıt üretirken bir hata oluştu: {e}"
121
+ return answer
122
 
123
+ def generate_audio_from_text(text):
124
+ """
125
+ Verilen metni OpenAI TTS modeliyle sese dönüştürür ve MP3 dosya yolunu döndürür.
126
+ TTS başarısız olursa None döndürür.
127
+ """
128
+ try:
129
+ audio_response = openai.Audio.create(
130
+ model=TTS_MODEL,
131
+ voice=TTS_VOICE,
132
+ text=text,
133
+ response_format="audio/mp3"
134
+ )
135
+ # audio_response içeriğinden ses verisini al (varsayılan anahtar adı 'audio' olarak düşünülmüştür)
136
+ audio_content = audio_response.get('audio')
137
+ if audio_content is None:
138
+ # Bazı durumlarda audio_response doğrudan binary içerebilir, o durumda audio_content None olacaktır.
139
+ audio_content = audio_response # Mümkünse direkt kullan
140
+
141
+ # Elde edilen ses verisini bir MP3 dosyasına kaydet
142
+ filename = f"response_{int(time.time())}.mp3"
143
+ with open(filename, "wb") as f:
144
+ f.write(audio_content)
145
+ return filename
146
+ except Exception as e:
147
+ # Hata durumunda konsola logla ve None döndür
148
+ print(f"TTS generation failed: {e}")
149
+ return None
150
+
151
+ # Gradio arayüzünü tanımla
152
+ with gr.Blocks() as demo:
153
+ gr.Markdown("## Trek Chatbot (Metin + Sesli Yanıt)") # Başlık
154
+
155
+ chatbot = gr.Chatbot(label="Sohbet Geçmişi") # Sohbet geçmişini gösterecek bileşen
156
+ user_input = gr.Textbox(label="Mesajınız") # Kullanıcıdan metin girişi
157
+ send_btn = gr.Button("Gönder") # Mesaj gönderme butonu
158
+ clear_btn = gr.Button("Konuşmayı Sıfırla") # Sohbeti temizleme butonu
159
+
160
+ # Durum (state) değişkenleri: tam konuşma geçmişi (sistem mesajı dahil) ve sadece kullanıcı-asistan çiftleri (görüntüleme için)
161
+ conversation_state = gr.State([{"role": "system", "content": SYSTEM_PROMPT}]) # Sistem mesajıyla başlat
162
+ chat_history_state = gr.State([]) # Kullanıcı ve asistan mesaj çiftlerini tutacak (görüntüleme amacıyla)
163
+
164
+ audio_output = gr.Audio(label="Sesli Yanıt", elem_id="audio_out") # Sesli yanıt için çıktı bileşeni
165
+
166
+ # Global değişken (referans): konuşma geçmişi listesi (sistem dahil) - sohbet geçmişini kilitleyerek yönetiyoruz
167
+ conversation_log = conversation_state.value # conversation_state içindeki liste nesnesine referans
168
+
169
+ def respond(user_message, conversation_state_value, chat_history_value):
170
+ """
171
+ Kullanıcının mesajına yanıt üretir (metin ve ses).
172
+ conversation_state_value: tam geçmiş (sistem mesajı dahil),
173
+ chat_history_value: sohbet geçmişi (görüntüleme için kullanıcı-bot çiftleri).
174
+ """
175
+ # Kullanıcı mesajını konuşma geçmişine ekle (OpenAI modeline tam geçmişi vereceğiz)
176
+ conversation_state_value.append({"role": "user", "content": user_message})
177
+
178
+ bot_answer = None # Asistan yanıtı (metin)
179
+
180
+ # Özel kurallar: Trek ürün stok/fiyat sorgusu
181
+ if "stok" in user_message.lower() or "fiyat" in user_message.lower():
182
+ # Mesajda stok veya fiyat kelimesi geçiyorsa, ürünü bulmaya çalış
183
+ words = user_message.split()
184
+ product_id = None
185
+ for w in words:
186
+ # Basit yaklaşım: mesajdaki ilk alfasayısal kelimeyi ürün kodu varsayıyoruz
187
+ if w.isalnum():
188
+ product_id = w
189
+ break
190
+ if product_id:
191
+ price, stock = check_trek_product_info(product_id)
192
+ if price is not None:
193
+ # Ürün bulundu, stok ve fiyat bilgisi ile yanıt oluştur
194
+ stock_status = "stokta var" if stock and stock != "0" else "stokta yok"
195
+ bot_answer = f"{product_id} ürünü {stock_status}, fiyatı {price}."
196
+ else:
197
+ # Ürün bulunamadı
198
+ bot_answer = "Üzgünüm, bu ürünün bilgisine ulaşılamadı."
199
+
200
+ # Özel kurallar: Google Drive dosya okuma isteği
201
+ if bot_answer is None and "drive" in user_message.lower():
202
+ # Mesajda 'drive' geçiyorsa, son kelimeyi dosya yolu olarak al (örnek basit yaklaşım)
203
+ file_path = user_message.split()[-1]
204
+ file_content = read_file_from_drive(file_path)
205
+ bot_answer = f"{file_path} dosyasının içeriği:\n{file_content}"
206
+
207
+ # Genel durum: Özel bir durum yoksa, GPT modelinden yanıt al
208
+ if bot_answer is None:
209
+ bot_answer = generate_text_response(conversation_state_value)
210
+
211
+ # Asistanın metin yanıtını konuşma geçmişine ekle (model bağlamı için)
212
+ conversation_state_value.append({"role": "assistant", "content": bot_answer})
213
+
214
+ # Hugging Face'e log kaydet (arkaplanda, asenkron şekilde)
215
+ log_text = f"User: {user_message}\nAssistant: {bot_answer}\n"
216
+ threading.Thread(target=log_to_huggingface, args=(log_text,)).start()
217
+
218
+ # Metin yanıtını TTS ile sese dönüştür
219
+ audio_file = generate_audio_from_text(bot_answer)
220
+
221
+ # Sohbet geçmişini güncelle (görsel için kullanıcı-bot çiftini ekle)
222
+ chat_history_value = chat_history_value or [] # None ise boş liste yap
223
+ chat_history_value.append((user_message, bot_answer))
224
+
225
+ # conversation_state_value ve chat_history_value, Gradio state'lerini güncellemek için döndürülür
226
+ # audio_file ise ses dosyasının yolu ya da None (Gradio audio bileşenini güncellemek için)
227
+ return conversation_state_value, chat_history_value, audio_file
228
+
229
+ # Gönder butonuna tıklandığında veya Enter'a basıldığında respond fonksiyonunu çağır
230
+ send_btn.click(
231
+ respond,
232
+ inputs=[user_input, conversation_state, chat_history_state],
233
+ outputs=[conversation_state, chatbot, audio_output]
234
+ )
235
+ user_input.submit(
236
+ respond,
237
+ inputs=[user_input, conversation_state, chat_history_state],
238
+ outputs=[conversation_state, chatbot, audio_output]
239
+ )
240
+
241
+ # Konuşmayı sıfırla (temizle) butonu işlevi
242
+ def clear_history():
243
+ """Sohbet geçmişini ve durumu sıfırlar."""
244
+ return [{"role": "system", "content": SYSTEM_PROMPT}], [], None
245
+ clear_btn.click(
246
+ clear_history,
247
+ inputs=None,
248
+ outputs=[conversation_state, chatbot, audio_output],
249
+ queue=False
250
+ )
251
+
252
+ # Arkaplanda periyodik loglama işlemini başlat (daemon thread, uygulamayı engellemez)
253
+ threading.Thread(target=periodic_log, args=(300,), daemon=True).start()
254
 
255
+ # Gradio arayüzünü başlat
256
+ demo.launch()