Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,9 +3,9 @@ import gradio as gr
|
|
| 3 |
import faiss
|
| 4 |
import numpy as np
|
| 5 |
from sentence_transformers import SentenceTransformer
|
| 6 |
-
from transformers import AutoTokenizer, AutoModelForCausalLM,
|
| 7 |
-
from peft import get_peft_model
|
| 8 |
-
from datasets import Dataset
|
| 9 |
import json
|
| 10 |
import os
|
| 11 |
from typing import List, Tuple
|
|
@@ -13,9 +13,9 @@ from functools import partial
|
|
| 13 |
import random
|
| 14 |
from datetime import datetime
|
| 15 |
from collections import deque
|
| 16 |
-
import requests
|
| 17 |
-
from huggingface_hub import hf_hub_download
|
| 18 |
-
import tempfile
|
| 19 |
|
| 20 |
# === CSS ve Emoji Fonksiyonu ===
|
| 21 |
current_css = """
|
|
@@ -71,11 +71,10 @@ def add_emojis(text: str) -> str:
|
|
| 71 |
}
|
| 72 |
|
| 73 |
found_emojis = []
|
| 74 |
-
words = text.split()
|
| 75 |
for word in words:
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
found_emojis.append(emoji_mapping[clean_word])
|
| 79 |
|
| 80 |
unique_emojis = list(set(found_emojis))
|
| 81 |
if unique_emojis:
|
|
@@ -85,31 +84,84 @@ def add_emojis(text: str) -> str:
|
|
| 85 |
# === SABİTLER ===
|
| 86 |
EMBEDDER_NAME = "paraphrase-multilingual-MiniLM-L12-v2"
|
| 87 |
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
| 88 |
-
FEEDBACK_FILE = "chatbot_feedback.jsonl"
|
| 89 |
-
QA_PATH = "qa_dataset.jsonl"
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
if tokenizer.pad_token is None:
|
| 101 |
tokenizer.pad_token = tokenizer.eos_token
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
# === Fahrenheit 451 Metni (Örnek paragraflar) ===
|
|
|
|
| 113 |
FAHRENHEIT_451_TEXT = """
|
| 114 |
Montag mutlu değildi. Fark etmesi ancak kapısının önüne kadar gelmesi ile oldu. Gece yarısı vakti, sokağın sonunda çevresine yayılan karanlık içinde duran ev, rüzgarın yaprak hışırtısı ve soluğunu görebilir kılacak kadar soğuk havada ev ona garip bir görüntü sunuyordu.
|
| 115 |
|
|
@@ -145,8 +197,6 @@ Montag şaşkınlıkla kızın yüzüne baktı. Karanlık gecenin ortasında, so
|
|
| 145 |
|
| 146 |
"Niye yaptı bunu?" diye sordu Montag. "Bilmiyoruz," dedi teknisyen. "Belki çok mutsuzdu. Belki de kazayla oldu. Çok yaygın bir durum." "Yaygın mı?" "Evet. İnsanlar çok bunalıyor. Televizyonda her şey çok hızlı. Radyoda çok gürültü. Herkes koşuyor. Kimse durmuyor."
|
| 147 |
|
| 148 |
-
Montag teknisyenlerin çalışmasını izledi. Karısının yüzü çok solgundu. Sanki ölmüş gibiydi. Ama nefes alıyordu. Teknisyenler işlerini bitirdiler. "Sabaha kadar uyuyacak," dediler. "Yarın normal olacak. Hiçbir şey hatırlamayacak."
|
| 149 |
-
|
| 150 |
Montag yalnız kaldı. Karısına baktı. Çok solgun görünüyordu. Montag Clarisse'i düşündü. "Mutlu musun?" sorusu kafasına takıldı. Gerçekten mutlu muydu? Karısı niye böyle bir şey yapmıştı?
|
| 151 |
|
| 152 |
Montag pencereye gitti. Dışarı baktı. Sokak çok sessizdi. Kimse yoktu. Sadece sokak lambaları yanıyordu. Montag Clarisse'in yüzünü düşündü. Kızın gözleri çok parlaktı. Çok canlıydı. Sanki her şeyi görüyordu.
|
|
@@ -198,6 +248,7 @@ Ve o gece, Montag çok önemli bir karar verdi. Bir kitap okumaya karar verdi. Y
|
|
| 198 |
|
| 199 |
# === DOSYA KAYIT ===
|
| 200 |
def save_feedback(user_question: str, answer: str, liked: bool, filepath: str = FEEDBACK_FILE):
|
|
|
|
| 201 |
feedback_entry = {
|
| 202 |
"input": user_question,
|
| 203 |
"output": answer,
|
|
@@ -225,8 +276,12 @@ class RLAgent:
|
|
| 225 |
def record_experience(self, user_question: str, generated_answer: str, liked: bool):
|
| 226 |
try:
|
| 227 |
combined_text = f"Soru: {user_question} Cevap: {generated_answer}"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
embedding = embedder.encode([combined_text], convert_to_tensor=True).squeeze(0).to(self.device)
|
| 229 |
-
reward = 1.0 if liked else -1.5
|
| 230 |
self.experience_buffer.append((embedding, torch.tensor([reward], dtype=torch.float32).to(self.device)))
|
| 231 |
self.train_reward_model()
|
| 232 |
except Exception as e:
|
|
@@ -251,15 +306,18 @@ class RLAgent:
|
|
| 251 |
|
| 252 |
avg_reward = rewards.mean().item() if rewards.numel() > 0 else 0
|
| 253 |
|
|
|
|
| 254 |
if avg_reward > 0.5:
|
| 255 |
-
self.current_temp = max(
|
| 256 |
-
self.current_rep_penalty = max(1.
|
| 257 |
elif avg_reward < -0.5:
|
| 258 |
-
self.current_temp = min(1.5, self.current_temp + self.learning_rate_reward * 0.
|
| 259 |
-
self.current_rep_penalty = min(2.0, self.current_rep_penalty + self.learning_rate_reward * 0.
|
| 260 |
|
| 261 |
self.current_temp = float(np.clip(self.current_temp, 0.8, 1.8))
|
| 262 |
-
self.current_rep_penalty = float(np.clip(self.current_rep_penalty, 1.
|
|
|
|
|
|
|
| 263 |
except Exception as e:
|
| 264 |
print(f"Error training reward model: {e}")
|
| 265 |
|
|
@@ -269,51 +327,20 @@ class RLAgent:
|
|
| 269 |
"repetition_penalty": self.current_rep_penalty
|
| 270 |
}
|
| 271 |
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
embedder
|
| 276 |
-
|
| 277 |
-
index = None
|
| 278 |
-
rl_agent = None
|
| 279 |
-
|
| 280 |
-
def initialize_components():
|
| 281 |
-
global model, tokenizer, embedder, paragraphs, index, rl_agent
|
| 282 |
-
|
| 283 |
try:
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
tokenizer = AutoTokenizer.from_pretrained("distilgpt2")
|
| 287 |
-
model = AutoModelForCausalLM.from_pretrained("distilgpt2").to(DEVICE)
|
| 288 |
-
|
| 289 |
-
if tokenizer.pad_token is None:
|
| 290 |
-
tokenizer.pad_token = tokenizer.eos_token
|
| 291 |
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
print("Embedder yükleniyor...")
|
| 296 |
-
embedder = SentenceTransformer(EMBEDDER_NAME)
|
| 297 |
-
print("Embedder yüklendi.")
|
| 298 |
-
|
| 299 |
-
print("Metin işleniyor...")
|
| 300 |
-
paragraphs = [p.strip() for p in FAHRENHEIT_451_TEXT.split("\n") if len(p.strip()) > 50]
|
| 301 |
-
|
| 302 |
-
print("Embeddingler hesaplanıyor...")
|
| 303 |
-
paragraph_embeddings = embedder.encode(paragraphs, convert_to_numpy=True)
|
| 304 |
-
index = faiss.IndexFlatL2(paragraph_embeddings.shape[1])
|
| 305 |
-
index.add(paragraph_embeddings)
|
| 306 |
-
print("Embeddingler hazır.")
|
| 307 |
-
|
| 308 |
-
print("RL Agent başlatılıyor...")
|
| 309 |
-
rl_agent = RLAgent(embedder.get_sentence_embedding_dimension())
|
| 310 |
-
print("RL Agent hazır.")
|
| 311 |
-
|
| 312 |
-
print("Tüm bileşenler başarıyla yüklendi!")
|
| 313 |
-
return True
|
| 314 |
except Exception as e:
|
| 315 |
-
print(f"
|
| 316 |
-
return
|
| 317 |
|
| 318 |
# === MONTAG PERSONA ===
|
| 319 |
MONTAG_PERSONA = (
|
|
@@ -323,11 +350,10 @@ MONTAG_PERSONA = (
|
|
| 323 |
f"- Derin, felsefi ve düşündürücü cevaplar verirsin.\n"
|
| 324 |
f"- Kitapların değerini çok iyi bilirsin ve onlara karşı gizli bir sevgi beslersin.\n"
|
| 325 |
f"- Toplumun baskıcı yapısına karşı içten içe isyankarsın ve arayış içindesin.\n"
|
| 326 |
-
f"- Cevapların 2
|
| 327 |
f"- Asla kendini tekrarlamazsın ve genel geçer ifadelerden kaçınırsın.\n"
|
| 328 |
)
|
| 329 |
|
| 330 |
-
|
| 331 |
# === ALTERNATİF CEVAPLAR ===
|
| 332 |
alternative_responses = [
|
| 333 |
"Bu soru bana Clarisse'i hatırlattı... Onun da sorgulayan bir ruhu vardı, tıpkı şimdi senin sorduğun gibi.",
|
|
@@ -346,30 +372,36 @@ alternative_responses = [
|
|
| 346 |
"Bir itfaiyeci olarak benim görevim yakmak... Ama bazen bir soru, yaktığım bin kitaptan daha çok aydınlatır.",
|
| 347 |
"Clarisse'i ilk gördüğümde bana sorduğu o soruyu hatırladım. Senin bu sorun da o kadar masum ama yıkıcı."
|
| 348 |
]
|
|
|
|
| 349 |
def generate_alternative_response(user_question: str) -> str:
|
| 350 |
"""Modelin cevap üretemediği veya yetersiz cevap verdiği durumlarda alternatif bir yanıt döndürür."""
|
| 351 |
-
# Montag persona'sına uygun, rastgele bir alternatif cevap seçelim
|
| 352 |
-
# user_question'ı doğrudan kullanmasak da, signature'da tutmak uygun olabilir
|
| 353 |
-
|
| 354 |
response = random.choice(alternative_responses)
|
| 355 |
-
|
| 356 |
-
# Cevaba emoji ekleme mantığını da burada kullanabiliriz
|
| 357 |
final_response = add_emojis(response)
|
| 358 |
return final_response
|
| 359 |
|
| 360 |
def generate_answer(question: str, chatbot_history: List[List[str]]) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
try:
|
| 362 |
gen_params = rl_agent.get_generation_params()
|
| 363 |
-
context = retrieve_context(question)
|
| 364 |
|
| 365 |
history_text = ""
|
| 366 |
if chatbot_history:
|
|
|
|
| 367 |
recent_dialogue = []
|
| 368 |
-
for user_msg, assistant_msg in chatbot_history[-3:]:
|
| 369 |
if user_msg:
|
| 370 |
-
|
|
|
|
|
|
|
|
|
|
| 371 |
if assistant_msg:
|
| 372 |
-
|
|
|
|
|
|
|
| 373 |
history_text = "\n".join(recent_dialogue) + "\n"
|
| 374 |
|
| 375 |
prompt = (
|
|
@@ -377,75 +409,71 @@ def generate_answer(question: str, chatbot_history: List[List[str]]) -> str:
|
|
| 377 |
f"Bağlam:\n{context}\n\n"
|
| 378 |
f"Önceki Sohbet:\n{history_text}\n"
|
| 379 |
f"Soru: {question}\n"
|
| 380 |
-
f"Montag (
|
| 381 |
)
|
| 382 |
|
| 383 |
inputs = tokenizer.encode(prompt, return_tensors="pt", truncation=True, max_length=512).to(DEVICE)
|
| 384 |
|
| 385 |
outputs = model.generate(
|
| 386 |
inputs,
|
| 387 |
-
max_new_tokens=
|
| 388 |
do_sample=True,
|
| 389 |
top_p=0.9,
|
| 390 |
temperature=gen_params["temperature"],
|
| 391 |
repetition_penalty=gen_params["repetition_penalty"],
|
| 392 |
-
no_repeat_ngram_size=3,
|
| 393 |
num_beams=1,
|
| 394 |
pad_token_id=tokenizer.eos_token_id,
|
| 395 |
eos_token_id=tokenizer.eos_token_id,
|
| 396 |
-
early_stopping=True
|
| 397 |
)
|
| 398 |
-
|
| 399 |
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 400 |
|
| 401 |
-
|
| 402 |
-
|
|
|
|
| 403 |
else:
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
"ne düşünmem gerektiğini bilmiyorum", "anlamadım", "tekrar eder misin",
|
| 411 |
-
"evet", "hayır", "hmm", "sanırım", "bunu hiç düşünmemiştim", "düşünmem gerekiyor"
|
| 412 |
-
]
|
| 413 |
-
|
| 414 |
-
is_generic_or_too_short = (
|
| 415 |
-
any(phrase in response.lower() for phrase in generic_phrases) or
|
| 416 |
-
len(response.split()) < 10 or
|
| 417 |
-
response.count('.') + response.count('?') + response.count('!') < 1 or
|
| 418 |
-
(response.count('.') + response.count('?') + response.count('!') == 1 and len(response.split()) < 15)
|
| 419 |
-
)
|
| 420 |
|
| 421 |
-
if is_generic_or_too_short:
|
| 422 |
-
print("INFO: Generic/Too short response detected, generating alternative.")
|
| 423 |
-
return generate_alternative_response(question)
|
| 424 |
|
|
|
|
| 425 |
sentences = []
|
| 426 |
current_sentence_parts = []
|
| 427 |
sentence_count = 0
|
| 428 |
-
|
|
|
|
| 429 |
for char in response:
|
| 430 |
current_sentence_parts.append(char)
|
| 431 |
if char in ['.', '!', '?']:
|
| 432 |
sentence = "".join(current_sentence_parts).strip()
|
| 433 |
-
if sentence:
|
| 434 |
sentences.append(sentence)
|
| 435 |
sentence_count += 1
|
| 436 |
current_sentence_parts = []
|
| 437 |
-
if sentence_count >= 5:
|
| 438 |
break
|
| 439 |
-
|
|
|
|
| 440 |
if current_sentence_parts and "".join(current_sentence_parts).strip():
|
| 441 |
final_part = "".join(current_sentence_parts).strip()
|
| 442 |
-
if final_part and
|
| 443 |
-
|
|
|
|
|
|
|
|
|
|
| 444 |
|
| 445 |
response_cleaned = ' '.join(sentences).strip()
|
| 446 |
|
| 447 |
-
|
| 448 |
-
|
|
|
|
|
|
|
| 449 |
return generate_alternative_response(question)
|
| 450 |
|
| 451 |
final_response = add_emojis(response_cleaned)
|
|
@@ -454,12 +482,6 @@ def generate_answer(question: str, chatbot_history: List[List[str]]) -> str:
|
|
| 454 |
except Exception as e:
|
| 455 |
print(f"Error generating answer: {e}")
|
| 456 |
return generate_alternative_response(question)
|
| 457 |
-
|
| 458 |
-
def retrieve_context(question: str, top_k=3) -> str:
|
| 459 |
-
question_embedding = embedder.encode([question], convert_to_numpy=True)
|
| 460 |
-
D, I = index.search(question_embedding, top_k)
|
| 461 |
-
retrieved_paragraphs = [paragraphs[i] for i in I[0] if i < len(paragraphs)]
|
| 462 |
-
return "\n".join(retrieved_paragraphs)
|
| 463 |
|
| 464 |
|
| 465 |
# === Gradio callback fonksiyonları ===
|
|
@@ -467,83 +489,66 @@ def respond(msg: str, chatbot_history: List[List[str]]) -> Tuple[str, List[List[
|
|
| 467 |
if not msg.strip():
|
| 468 |
return "", chatbot_history
|
| 469 |
|
| 470 |
-
#
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
# Modelden cevap üret
|
| 474 |
-
output = model.generate(
|
| 475 |
-
input_ids=input_ids,
|
| 476 |
-
max_new_tokens=150,
|
| 477 |
-
temperature=0.8,
|
| 478 |
-
top_p=0.95,
|
| 479 |
-
top_k=50,
|
| 480 |
-
do_sample=True,
|
| 481 |
-
repetition_penalty=1.1,
|
| 482 |
-
pad_token_id=tokenizer.eos_token_id,
|
| 483 |
-
eos_token_id=tokenizer.eos_token_id
|
| 484 |
-
)
|
| 485 |
|
| 486 |
-
#
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
# Sohbet geçmişine ekle
|
| 490 |
-
chatbot_history.append([msg, answer])
|
| 491 |
|
| 492 |
return "", chatbot_history
|
| 493 |
|
| 494 |
-
|
| 495 |
def regenerate_answer(chatbot_history: List[List[str]]) -> Tuple[str, List[List[str]]]:
|
|
|
|
| 496 |
if not chatbot_history:
|
| 497 |
return "", []
|
| 498 |
|
| 499 |
-
last_user_question = chatbot_history[-1][0]
|
| 500 |
|
| 501 |
if last_user_question:
|
| 502 |
-
|
|
|
|
| 503 |
|
| 504 |
-
new_answer = generate_answer(last_user_question, chatbot_history)
|
| 505 |
-
chatbot_history.append([last_user_question, new_answer])
|
| 506 |
return "", chatbot_history
|
| 507 |
return "", chatbot_history
|
| 508 |
|
| 509 |
|
| 510 |
-
def feedback_callback(chatbot_history: List[List[str]], liked: bool) ->
|
| 511 |
if not chatbot_history:
|
| 512 |
-
return
|
| 513 |
-
|
| 514 |
-
last_pair = chatbot_history[-1]
|
| 515 |
-
if len(last_pair) != 2:
|
| 516 |
-
return
|
| 517 |
-
|
| 518 |
-
user_question, answer = last_pair
|
| 519 |
-
if user_question and answer:
|
| 520 |
-
save_feedback(user_question, answer, liked)
|
| 521 |
-
rl_agent.record_experience(user_question, answer, liked)
|
| 522 |
-
|
| 523 |
|
| 524 |
last_user_question = chatbot_history[-1][0]
|
| 525 |
last_assistant_answer = chatbot_history[-1][1]
|
| 526 |
|
| 527 |
if last_user_question and last_assistant_answer:
|
| 528 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 529 |
|
| 530 |
rl_agent.record_experience(
|
| 531 |
-
|
| 532 |
-
|
| 533 |
liked
|
| 534 |
)
|
| 535 |
|
| 536 |
if liked:
|
| 537 |
-
|
|
|
|
|
|
|
| 538 |
with open(QA_PATH, "a", encoding="utf-8") as f:
|
| 539 |
f.write(json.dumps(qa_pair, ensure_ascii=False) + "\n")
|
| 540 |
-
|
| 541 |
-
|
|
|
|
| 542 |
return "Geri bildirim kaydedilemedi. Geçmişte yeterli sohbet bulunmuyor."
|
| 543 |
|
| 544 |
# === Gradio arayüzü == #
|
| 545 |
-
current_css
|
| 546 |
-
|
| 547 |
def create_chat_interface():
|
| 548 |
with gr.Blocks(theme=gr.themes.Soft(), css=current_css) as demo:
|
| 549 |
gr.Markdown("""
|
|
@@ -560,17 +565,25 @@ def create_chat_interface():
|
|
| 560 |
|
| 561 |
with gr.Row():
|
| 562 |
like_btn = gr.Button("👍 Beğendim")
|
| 563 |
-
dislike_btn = gr.Button("👎 Beğenmedim (
|
| 564 |
|
| 565 |
feedback_status_output = gr.Textbox(label="Geri Bildirim Durumu", interactive=False, max_lines=1)
|
| 566 |
|
| 567 |
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
| 568 |
submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
|
| 569 |
-
clear_btn.click(lambda: [], None, chatbot, queue=False)
|
| 570 |
|
|
|
|
| 571 |
like_btn.click(partial(feedback_callback, liked=True), [chatbot], [feedback_status_output])
|
|
|
|
|
|
|
| 572 |
dislike_btn.click(partial(feedback_callback, liked=False), [chatbot], [feedback_status_output])
|
| 573 |
-
dislike_btn.click(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
return demo
|
| 575 |
|
| 576 |
|
|
@@ -580,4 +593,4 @@ if __name__ == "__main__":
|
|
| 580 |
demo = create_chat_interface()
|
| 581 |
demo.launch()
|
| 582 |
else:
|
| 583 |
-
print("Uygulama başlatılamadı: Bileşenler yüklenirken hata oluştu.")
|
|
|
|
| 3 |
import faiss
|
| 4 |
import numpy as np
|
| 5 |
from sentence_transformers import SentenceTransformer
|
| 6 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM # TrainingArguments, LoraConfig, TaskType removed as not directly used for inference
|
| 7 |
+
from peft import PeftModel # get_peft_model removed as not directly used for inference
|
| 8 |
+
from datasets import Dataset # Not directly used in the provided snippet but kept for completeness
|
| 9 |
import json
|
| 10 |
import os
|
| 11 |
from typing import List, Tuple
|
|
|
|
| 13 |
import random
|
| 14 |
from datetime import datetime
|
| 15 |
from collections import deque
|
| 16 |
+
# import requests # Not directly used but kept for completeness
|
| 17 |
+
# from huggingface_hub import hf_hub_download # Not directly used but kept for completeness
|
| 18 |
+
# import tempfile # Not directly used but kept for completeness
|
| 19 |
|
| 20 |
# === CSS ve Emoji Fonksiyonu ===
|
| 21 |
current_css = """
|
|
|
|
| 71 |
}
|
| 72 |
|
| 73 |
found_emojis = []
|
| 74 |
+
words = text.lower().replace('.', '').replace(',', '').replace('!', '').replace('?', '').split()
|
| 75 |
for word in words:
|
| 76 |
+
if word in emoji_mapping:
|
| 77 |
+
found_emojis.append(emoji_mapping[word])
|
|
|
|
| 78 |
|
| 79 |
unique_emojis = list(set(found_emojis))
|
| 80 |
if unique_emojis:
|
|
|
|
| 84 |
# === SABİTLER ===
|
| 85 |
EMBEDDER_NAME = "paraphrase-multilingual-MiniLM-L12-v2"
|
| 86 |
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
| 87 |
+
FEEDBACK_FILE = "data/chatbot_feedback.jsonl" # Changed to a data directory
|
| 88 |
+
QA_PATH = "data/qa_dataset.jsonl" # Changed to a data directory
|
| 89 |
+
|
| 90 |
+
# Your original base model and fine-tuned model path (for reference)
|
| 91 |
+
# BASE_MODEL = "ytu-ce-cosmos/turkish-gpt2-large"
|
| 92 |
+
# MODEL_PATH = "lordzukoiroh/montaggppt2lora"
|
| 93 |
|
| 94 |
+
# Using a smaller model for broader compatibility (e.g., Hugging Face Spaces free tier)
|
| 95 |
+
BASE_MODEL_FOR_DEMO = "distilgpt2"
|
| 96 |
+
|
| 97 |
+
# === GLOBAL DEĞİŞKENLER ===
|
| 98 |
+
model = None
|
| 99 |
+
tokenizer = None
|
| 100 |
+
embedder = None
|
| 101 |
+
paragraphs = []
|
| 102 |
+
paragraph_embeddings = None # Store embeddings to avoid recomputing
|
| 103 |
+
index = None
|
| 104 |
+
rl_agent = None
|
| 105 |
+
|
| 106 |
+
def load_model_and_tokenizer_func(base_model_name: str):
|
| 107 |
+
"""Loads the base model and tokenizer."""
|
| 108 |
+
print(f"Model yükleniyor: {base_model_name}...")
|
| 109 |
+
tokenizer = AutoTokenizer.from_pretrained(base_model_name)
|
| 110 |
+
base_model = AutoModelForCausalLM.from_pretrained(base_model_name).to(DEVICE)
|
| 111 |
+
|
| 112 |
+
# If you were loading a fine-tuned LoRA model, you would uncomment and adjust this:
|
| 113 |
+
# model = PeftModel.from_pretrained(base_model, MODEL_PATH).to(DEVICE)
|
| 114 |
+
model_to_return = base_model # For this demo, we use the base model
|
| 115 |
|
| 116 |
if tokenizer.pad_token is None:
|
| 117 |
tokenizer.pad_token = tokenizer.eos_token
|
| 118 |
+
|
| 119 |
+
model_to_return.eval()
|
| 120 |
+
print(f"Model {base_model_name} yüklendi.")
|
| 121 |
+
return model_to_return, tokenizer
|
| 122 |
|
| 123 |
+
def initialize_components():
|
| 124 |
+
global model, tokenizer, embedder, paragraphs, paragraph_embeddings, index, rl_agent
|
| 125 |
+
|
| 126 |
+
try:
|
| 127 |
+
print("Model ve tokenizer yükleniyor...")
|
| 128 |
+
model, tokenizer = load_model_and_tokenizer_func(BASE_MODEL_FOR_DEMO)
|
| 129 |
+
|
| 130 |
+
print("Embedder yükleniyor...")
|
| 131 |
+
embedder = SentenceTransformer(EMBEDDER_NAME)
|
| 132 |
+
print("Embedder yüklendi.")
|
| 133 |
|
| 134 |
+
print("Metin işleniyor...")
|
| 135 |
+
# Clean and filter paragraphs, ensure they are substantial enough for embedding
|
| 136 |
+
paragraphs = [p.strip() for p in FAHRENHEIT_451_TEXT.split("\n") if len(p.strip().split()) > 10]
|
| 137 |
+
|
| 138 |
+
print(f"Toplam {len(paragraphs)} paragraf işlendi.")
|
| 139 |
+
if not paragraphs:
|
| 140 |
+
raise ValueError("No valid paragraphs extracted from the text.")
|
| 141 |
|
| 142 |
+
print("Embeddingler hesaplanıyor ve FAISS indeksi oluşturuluyor...")
|
| 143 |
+
paragraph_embeddings = embedder.encode(paragraphs, convert_to_numpy=True)
|
| 144 |
+
|
| 145 |
+
# Ensure embeddings are float32 for FAISS
|
| 146 |
+
if paragraph_embeddings.dtype != np.float32:
|
| 147 |
+
paragraph_embeddings = paragraph_embeddings.astype(np.float32)
|
| 148 |
|
| 149 |
+
index = faiss.IndexFlatL2(paragraph_embeddings.shape[1])
|
| 150 |
+
index.add(paragraph_embeddings)
|
| 151 |
+
print("Embeddingler ve FAISS indeksi hazır.")
|
| 152 |
+
|
| 153 |
+
print("RL Agent başlatılıyor...")
|
| 154 |
+
rl_agent = RLAgent(embedder.get_sentence_embedding_dimension())
|
| 155 |
+
print("RL Agent hazır.")
|
| 156 |
|
| 157 |
+
print("Tüm bileşenler başarıyla yüklendi!")
|
| 158 |
+
return True
|
| 159 |
+
except Exception as e:
|
| 160 |
+
print(f"Bileşenler yüklenirken hata: {e}")
|
| 161 |
+
return False
|
| 162 |
|
| 163 |
# === Fahrenheit 451 Metni (Örnek paragraflar) ===
|
| 164 |
+
# Moved up for clarity and scope if needed elsewhere, though it's already a global string.
|
| 165 |
FAHRENHEIT_451_TEXT = """
|
| 166 |
Montag mutlu değildi. Fark etmesi ancak kapısının önüne kadar gelmesi ile oldu. Gece yarısı vakti, sokağın sonunda çevresine yayılan karanlık içinde duran ev, rüzgarın yaprak hışırtısı ve soluğunu görebilir kılacak kadar soğuk havada ev ona garip bir görüntü sunuyordu.
|
| 167 |
|
|
|
|
| 197 |
|
| 198 |
"Niye yaptı bunu?" diye sordu Montag. "Bilmiyoruz," dedi teknisyen. "Belki çok mutsuzdu. Belki de kazayla oldu. Çok yaygın bir durum." "Yaygın mı?" "Evet. İnsanlar çok bunalıyor. Televizyonda her şey çok hızlı. Radyoda çok gürültü. Herkes koşuyor. Kimse durmuyor."
|
| 199 |
|
|
|
|
|
|
|
| 200 |
Montag yalnız kaldı. Karısına baktı. Çok solgun görünüyordu. Montag Clarisse'i düşündü. "Mutlu musun?" sorusu kafasına takıldı. Gerçekten mutlu muydu? Karısı niye böyle bir şey yapmıştı?
|
| 201 |
|
| 202 |
Montag pencereye gitti. Dışarı baktı. Sokak çok sessizdi. Kimse yoktu. Sadece sokak lambaları yanıyordu. Montag Clarisse'in yüzünü düşündü. Kızın gözleri çok parlaktı. Çok canlıydı. Sanki her şeyi görüyordu.
|
|
|
|
| 248 |
|
| 249 |
# === DOSYA KAYIT ===
|
| 250 |
def save_feedback(user_question: str, answer: str, liked: bool, filepath: str = FEEDBACK_FILE):
|
| 251 |
+
os.makedirs(os.path.dirname(filepath), exist_ok=True) # Ensure directory exists
|
| 252 |
feedback_entry = {
|
| 253 |
"input": user_question,
|
| 254 |
"output": answer,
|
|
|
|
| 276 |
def record_experience(self, user_question: str, generated_answer: str, liked: bool):
|
| 277 |
try:
|
| 278 |
combined_text = f"Soru: {user_question} Cevap: {generated_answer}"
|
| 279 |
+
# Ensure embedder is available
|
| 280 |
+
if embedder is None:
|
| 281 |
+
print("Embedder not initialized, cannot record experience.")
|
| 282 |
+
return
|
| 283 |
embedding = embedder.encode([combined_text], convert_to_tensor=True).squeeze(0).to(self.device)
|
| 284 |
+
reward = 1.0 if liked else -1.5 # Adjusted negative reward for stronger signal
|
| 285 |
self.experience_buffer.append((embedding, torch.tensor([reward], dtype=torch.float32).to(self.device)))
|
| 286 |
self.train_reward_model()
|
| 287 |
except Exception as e:
|
|
|
|
| 306 |
|
| 307 |
avg_reward = rewards.mean().item() if rewards.numel() > 0 else 0
|
| 308 |
|
| 309 |
+
# Adjust parameters more subtly
|
| 310 |
if avg_reward > 0.5:
|
| 311 |
+
self.current_temp = max(0.9, self.current_temp - self.learning_rate_reward * 0.05) # Lower temp for more deterministic good answers
|
| 312 |
+
self.current_rep_penalty = max(1.0, self.current_rep_penalty - self.learning_rate_reward * 0.02)
|
| 313 |
elif avg_reward < -0.5:
|
| 314 |
+
self.current_temp = min(1.5, self.current_temp + self.learning_rate_reward * 0.05) # Higher temp for more exploration if bad
|
| 315 |
+
self.current_rep_penalty = min(2.0, self.current_rep_penalty + self.learning_rate_reward * 0.02)
|
| 316 |
|
| 317 |
self.current_temp = float(np.clip(self.current_temp, 0.8, 1.8))
|
| 318 |
+
self.current_rep_penalty = float(np.clip(self.current_rep_penalty, 1.0, 2.5)) # Allow lower rep penalty
|
| 319 |
+
# print(f"INFO: Updated generation params - Temp: {self.current_temp:.2f}, Rep_Penalty: {self.current_rep_penalty:.2f}")
|
| 320 |
+
|
| 321 |
except Exception as e:
|
| 322 |
print(f"Error training reward model: {e}")
|
| 323 |
|
|
|
|
| 327 |
"repetition_penalty": self.current_rep_penalty
|
| 328 |
}
|
| 329 |
|
| 330 |
+
def retrieve_context(query: str, k: int = 2) -> str:
|
| 331 |
+
"""FAISS indeksini kullanarak sorguya en uygun paragrafları getirir."""
|
| 332 |
+
if index is None or embedder is None or not paragraphs:
|
| 333 |
+
print("WARNING: FAISS index, embedder or paragraphs not initialized for context retrieval.")
|
| 334 |
+
return "Bağlam bulunamadı."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
try:
|
| 336 |
+
query_embedding = embedder.encode([query], convert_to_numpy=True).astype(np.float32)
|
| 337 |
+
D, I = index.search(query_embedding, k) # D: distances, I: indices
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 338 |
|
| 339 |
+
retrieved_texts = [paragraphs[i] for i in I[0] if i < len(paragraphs)]
|
| 340 |
+
return "\n".join(retrieved_texts)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
except Exception as e:
|
| 342 |
+
print(f"Bağlam alınırken hata: {e}")
|
| 343 |
+
return "Bağlam alınırken bir sorun oluştu."
|
| 344 |
|
| 345 |
# === MONTAG PERSONA ===
|
| 346 |
MONTAG_PERSONA = (
|
|
|
|
| 350 |
f"- Derin, felsefi ve düşündürücü cevaplar verirsin.\n"
|
| 351 |
f"- Kitapların değerini çok iyi bilirsin ve onlara karşı gizli bir sevgi beslersin.\n"
|
| 352 |
f"- Toplumun baskıcı yapısına karşı içten içe isyankarsın ve arayış içindesin.\n"
|
| 353 |
+
f"- Cevapların **2 ila 5 cümle** arasında, net, özgün ve akıcıdır. Bazen alaycı, sorgulayıcı veya melankolik bir ton kullanırsın.\n"
|
| 354 |
f"- Asla kendini tekrarlamazsın ve genel geçer ifadelerden kaçınırsın.\n"
|
| 355 |
)
|
| 356 |
|
|
|
|
| 357 |
# === ALTERNATİF CEVAPLAR ===
|
| 358 |
alternative_responses = [
|
| 359 |
"Bu soru bana Clarisse'i hatırlattı... Onun da sorgulayan bir ruhu vardı, tıpkı şimdi senin sorduğun gibi.",
|
|
|
|
| 372 |
"Bir itfaiyeci olarak benim görevim yakmak... Ama bazen bir soru, yaktığım bin kitaptan daha çok aydınlatır.",
|
| 373 |
"Clarisse'i ilk gördüğümde bana sorduğu o soruyu hatırladım. Senin bu sorun da o kadar masum ama yıkıcı."
|
| 374 |
]
|
| 375 |
+
|
| 376 |
def generate_alternative_response(user_question: str) -> str:
|
| 377 |
"""Modelin cevap üretemediği veya yetersiz cevap verdiği durumlarda alternatif bir yanıt döndürür."""
|
|
|
|
|
|
|
|
|
|
| 378 |
response = random.choice(alternative_responses)
|
|
|
|
|
|
|
| 379 |
final_response = add_emojis(response)
|
| 380 |
return final_response
|
| 381 |
|
| 382 |
def generate_answer(question: str, chatbot_history: List[List[str]]) -> str:
|
| 383 |
+
if model is None or tokenizer is None or rl_agent is None:
|
| 384 |
+
print("ERROR: Model, tokenizer veya RL Agent başlatılmamış.")
|
| 385 |
+
return generate_alternative_response(question)
|
| 386 |
+
|
| 387 |
try:
|
| 388 |
gen_params = rl_agent.get_generation_params()
|
| 389 |
+
context = retrieve_context(question) # Call the correct retrieve_context
|
| 390 |
|
| 391 |
history_text = ""
|
| 392 |
if chatbot_history:
|
| 393 |
+
# Get last 3 turns to maintain short-term context
|
| 394 |
recent_dialogue = []
|
| 395 |
+
for user_msg, assistant_msg in chatbot_history[-3:]:
|
| 396 |
if user_msg:
|
| 397 |
+
# Clean emojis from history before passing to model
|
| 398 |
+
cleaned_user_msg = user_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
|
| 399 |
+
if cleaned_user_msg:
|
| 400 |
+
recent_dialogue.append(f"Kullanıcı: {cleaned_user_msg}")
|
| 401 |
if assistant_msg:
|
| 402 |
+
cleaned_assistant_msg = assistant_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
|
| 403 |
+
if cleaned_assistant_msg:
|
| 404 |
+
recent_dialogue.append(f"Montag: {cleaned_assistant_msg}")
|
| 405 |
history_text = "\n".join(recent_dialogue) + "\n"
|
| 406 |
|
| 407 |
prompt = (
|
|
|
|
| 409 |
f"Bağlam:\n{context}\n\n"
|
| 410 |
f"Önceki Sohbet:\n{history_text}\n"
|
| 411 |
f"Soru: {question}\n"
|
| 412 |
+
f"Montag (cevap 2 ila 5 cümle arasında olmalı):" # Explicitly tell model the sentence limit
|
| 413 |
)
|
| 414 |
|
| 415 |
inputs = tokenizer.encode(prompt, return_tensors="pt", truncation=True, max_length=512).to(DEVICE)
|
| 416 |
|
| 417 |
outputs = model.generate(
|
| 418 |
inputs,
|
| 419 |
+
max_new_tokens=100, # Increased max_new_tokens to allow for 2-5 sentences
|
| 420 |
do_sample=True,
|
| 421 |
top_p=0.9,
|
| 422 |
temperature=gen_params["temperature"],
|
| 423 |
repetition_penalty=gen_params["repetition_penalty"],
|
| 424 |
+
no_repeat_ngram_size=3, # Helps prevent direct repetition of phrases
|
| 425 |
num_beams=1,
|
| 426 |
pad_token_id=tokenizer.eos_token_id,
|
| 427 |
eos_token_id=tokenizer.eos_token_id,
|
| 428 |
+
early_stopping=True # Stop when EOS token is generated
|
| 429 |
)
|
| 430 |
+
|
| 431 |
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 432 |
|
| 433 |
+
# Post-processing to extract Montag's specific response and trim prefix
|
| 434 |
+
if "Montag (cevap 2 ila 5 cümle arasında olmalı):" in response:
|
| 435 |
+
response = response.split("Montag (cevap 2 ila 5 cümle arasında olmalı):")[-1].strip()
|
| 436 |
else:
|
| 437 |
+
# Fallback if the marker isn't found (e.g., model truncated or ignored)
|
| 438 |
+
# Try to remove the prompt part from the beginning of the response
|
| 439 |
+
prompt_end_marker_len = len(f"Soru: {question}\nMontag (cevap 2 ila 5 cümle arasında olmalı):")
|
| 440 |
+
# This check is a bit tricky, but aims to remove the input prompt if it's echoed
|
| 441 |
+
if len(response) > len(prompt) and response.startswith(prompt[:-min(prompt_end_marker_len, len(prompt))]):
|
| 442 |
+
response = response[len(prompt):].strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
|
|
|
|
|
|
|
|
|
|
| 444 |
|
| 445 |
+
# Ensure response adheres to 2-5 sentence structure and isn't too generic
|
| 446 |
sentences = []
|
| 447 |
current_sentence_parts = []
|
| 448 |
sentence_count = 0
|
| 449 |
+
|
| 450 |
+
# Simple sentence tokenization based on punctuation
|
| 451 |
for char in response:
|
| 452 |
current_sentence_parts.append(char)
|
| 453 |
if char in ['.', '!', '?']:
|
| 454 |
sentence = "".join(current_sentence_parts).strip()
|
| 455 |
+
if sentence: # Add sentence if not empty
|
| 456 |
sentences.append(sentence)
|
| 457 |
sentence_count += 1
|
| 458 |
current_sentence_parts = []
|
| 459 |
+
if sentence_count >= 5: # Stop after 5 sentences
|
| 460 |
break
|
| 461 |
+
|
| 462 |
+
# Add any remaining part as a sentence if it's not just whitespace
|
| 463 |
if current_sentence_parts and "".join(current_sentence_parts).strip():
|
| 464 |
final_part = "".join(current_sentence_parts).strip()
|
| 465 |
+
if final_part and sentence_count < 5: # Only add if we haven't reached 5 sentences yet
|
| 466 |
+
sentences.append(final_part)
|
| 467 |
+
elif final_part and sentence_count == 5 and len(sentences) < 5: # Edge case: ensure 5th sentence is added if still building
|
| 468 |
+
sentences.append(final_part)
|
| 469 |
+
|
| 470 |
|
| 471 |
response_cleaned = ' '.join(sentences).strip()
|
| 472 |
|
| 473 |
+
# Final check for quality and length
|
| 474 |
+
# A good Montag response should be at least 15 words and have at least 2 sentences
|
| 475 |
+
if not response_cleaned or len(response_cleaned.split()) < 15 or sentence_count < 2:
|
| 476 |
+
print("INFO: Generated response is too short or malformed after processing. Generating alternative.")
|
| 477 |
return generate_alternative_response(question)
|
| 478 |
|
| 479 |
final_response = add_emojis(response_cleaned)
|
|
|
|
| 482 |
except Exception as e:
|
| 483 |
print(f"Error generating answer: {e}")
|
| 484 |
return generate_alternative_response(question)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
|
| 486 |
|
| 487 |
# === Gradio callback fonksiyonları ===
|
|
|
|
| 489 |
if not msg.strip():
|
| 490 |
return "", chatbot_history
|
| 491 |
|
| 492 |
+
# This now correctly calls generate_answer, which handles RAG and RL parameters
|
| 493 |
+
answer = generate_answer(msg, chatbot_history)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
|
| 495 |
+
# Ensure no duplicate responses are appended (if user clicks send multiple times)
|
| 496 |
+
if not chatbot_history or chatbot_history[-1] != [msg, answer]:
|
| 497 |
+
chatbot_history.append([msg, answer])
|
|
|
|
|
|
|
| 498 |
|
| 499 |
return "", chatbot_history
|
| 500 |
|
|
|
|
| 501 |
def regenerate_answer(chatbot_history: List[List[str]]) -> Tuple[str, List[List[str]]]:
|
| 502 |
+
"""Generates a new answer for the last user question using the main generation logic."""
|
| 503 |
if not chatbot_history:
|
| 504 |
return "", []
|
| 505 |
|
| 506 |
+
last_user_question = chatbot_history[-1][0] # Get the last user question
|
| 507 |
|
| 508 |
if last_user_question:
|
| 509 |
+
# Remove the previous assistant answer to replace it
|
| 510 |
+
chatbot_history.pop()
|
| 511 |
|
| 512 |
+
new_answer = generate_answer(last_user_question, chatbot_history) # Generate new answer
|
| 513 |
+
chatbot_history.append([last_user_question, new_answer]) # Add new answer
|
| 514 |
return "", chatbot_history
|
| 515 |
return "", chatbot_history
|
| 516 |
|
| 517 |
|
| 518 |
+
def feedback_callback(chatbot_history: List[List[str]], liked: bool) -> str:
|
| 519 |
if not chatbot_history:
|
| 520 |
+
return "Önce bir sohbet gerçekleştirin."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
|
| 522 |
last_user_question = chatbot_history[-1][0]
|
| 523 |
last_assistant_answer = chatbot_history[-1][1]
|
| 524 |
|
| 525 |
if last_user_question and last_assistant_answer:
|
| 526 |
+
# Clean emojis before saving feedback or passing to RL agent
|
| 527 |
+
cleaned_user_question = last_user_question.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
|
| 528 |
+
cleaned_assistant_answer = last_assistant_answer.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
|
| 529 |
+
|
| 530 |
+
# ONLY ONE CALL FOR SAVE AND RECORD
|
| 531 |
+
save_feedback(cleaned_user_question, cleaned_assistant_answer, liked, FEEDBACK_FILE)
|
| 532 |
|
| 533 |
rl_agent.record_experience(
|
| 534 |
+
cleaned_user_question,
|
| 535 |
+
cleaned_assistant_answer,
|
| 536 |
liked
|
| 537 |
)
|
| 538 |
|
| 539 |
if liked:
|
| 540 |
+
# Save liked QA pairs for potential future fine-tuning or analysis
|
| 541 |
+
qa_pair = {"question": cleaned_user_question, "answer": cleaned_assistant_answer, "liked": True}
|
| 542 |
+
os.makedirs(os.path.dirname(QA_PATH), exist_ok=True)
|
| 543 |
with open(QA_PATH, "a", encoding="utf-8") as f:
|
| 544 |
f.write(json.dumps(qa_pair, ensure_ascii=False) + "\n")
|
| 545 |
+
return "Geri bildiriminiz kaydedildi. Teşekkürler! 👍"
|
| 546 |
+
else:
|
| 547 |
+
return "Geri bildiriminiz kaydedildi. Yeni bir yanıt denenecek. 👎"
|
| 548 |
return "Geri bildirim kaydedilemedi. Geçmişte yeterli sohbet bulunmuyor."
|
| 549 |
|
| 550 |
# === Gradio arayüzü == #
|
| 551 |
+
# current_css defined at the top of the file
|
|
|
|
| 552 |
def create_chat_interface():
|
| 553 |
with gr.Blocks(theme=gr.themes.Soft(), css=current_css) as demo:
|
| 554 |
gr.Markdown("""
|
|
|
|
| 565 |
|
| 566 |
with gr.Row():
|
| 567 |
like_btn = gr.Button("👍 Beğendim")
|
| 568 |
+
dislike_btn = gr.Button("👎 Beğenmedim (Yeni Cevap Dene)")
|
| 569 |
|
| 570 |
feedback_status_output = gr.Textbox(label="Geri Bildirim Durumu", interactive=False, max_lines=1)
|
| 571 |
|
| 572 |
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
| 573 |
submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
|
| 574 |
+
clear_btn.click(lambda: [], None, chatbot, queue=False) # Clear button resets the chat
|
| 575 |
|
| 576 |
+
# When "Like" is clicked, record feedback.
|
| 577 |
like_btn.click(partial(feedback_callback, liked=True), [chatbot], [feedback_status_output])
|
| 578 |
+
|
| 579 |
+
# When "Dislike" is clicked, record feedback AND regenerate the answer.
|
| 580 |
dislike_btn.click(partial(feedback_callback, liked=False), [chatbot], [feedback_status_output])
|
| 581 |
+
dislike_btn.click(
|
| 582 |
+
regenerate_answer,
|
| 583 |
+
[chatbot],
|
| 584 |
+
[msg, chatbot], # msg for clearing input, chatbot for updating chat
|
| 585 |
+
queue=False # Important for immediate regeneration
|
| 586 |
+
)
|
| 587 |
return demo
|
| 588 |
|
| 589 |
|
|
|
|
| 593 |
demo = create_chat_interface()
|
| 594 |
demo.launch()
|
| 595 |
else:
|
| 596 |
+
print("Uygulama başlatılamadı: Bileşenler yüklenirken hata oluştu.")
|