lordzukoiroh commited on
Commit
2b58d1b
·
verified ·
1 Parent(s): 9e95b50

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +209 -254
app.py CHANGED
@@ -1,5 +1,14 @@
1
  # app.py
2
 
 
 
 
 
 
 
 
 
 
3
 
4
  import torch
5
  import gradio as gr
@@ -17,9 +26,10 @@ import random
17
  from datetime import datetime
18
  from collections import deque
19
  import time
20
- import re
21
  import nltk
22
- from nltk.corpus import stopwords
 
23
 
24
 
25
  # === CSS ve Emoji Fonksiyonu ===
@@ -85,7 +95,7 @@ def add_emojis(text: str) -> str:
85
  "merak": "🤔", "meraklı": "🤔",
86
  "kültür": "🌍", "toplum": "👥",
87
  "yalan": "🤥", "gerçek": "✨",
88
- "clarisse": "🌸", "faber": "👴", "beatty": "🚨"
89
  }
90
 
91
  found_emojis = []
@@ -109,21 +119,23 @@ QA_PATH = "data/qa_dataset.jsonl" # LoRA fine-tuning örnekleri için kullanıl
109
  BASE_MODEL_FOR_DEMO = "ytu-ce-cosmos/turkish-gpt2-large" # Kullandığınız temel model
110
  LOR_MODEL_PATH = "lora_model_weights" # LoRA adaptör ağırlıklarının kaydedileceği/yükleneceği yol
111
 
112
- FAHRENHEIT_TEXT_FILE = "fahrenheittt451.txt"
113
 
114
- MAX_EXPECTED_TIME = 120.0
 
115
 
116
  # === GLOBAL DEĞİŞKENLER ===
117
- # Bu değişkenler initialize_components fonksiyonu
118
  model = None
119
  tokenizer = None
120
  embedder = None
121
  paragraphs = []
122
  paragraph_embeddings = None
123
  index = None
124
- rl_agent = None
125
 
126
  # === DOSYA VE KLASÖR YAPISI OLUŞTURMA ===
 
127
  def setup_local_files():
128
  os.makedirs('data', exist_ok=True)
129
  os.makedirs(LOR_MODEL_PATH, exist_ok=True)
@@ -131,7 +143,8 @@ def setup_local_files():
131
  # Gerekli dosyaların varlığını kontrol et ve yoksa boş oluştur
132
  if not os.path.exists(FAHRENHEIT_TEXT_FILE):
133
  print(f"HATA: '{FAHRENHEIT_TEXT_FILE}' bulunamadı. Lütfen bu dosyayı projenizin ana dizinine yerleştirin.")
134
-
 
135
 
136
  if not os.path.exists(QA_PATH):
137
  open(QA_PATH, 'a').close() # Boş QA dosyası oluştur
@@ -271,8 +284,8 @@ def lora_finetune(filepath: str = QA_PATH, lora_output_path: str = LOR_MODEL_PAT
271
  peft_model.print_trainable_parameters()
272
 
273
  args = TrainingArguments(
274
- output_dir="./results",
275
- num_train_epochs=3,
276
  per_device_train_batch_size=2,
277
  gradient_accumulation_steps=4,
278
  logging_steps=10,
@@ -456,13 +469,13 @@ MONTAG_PERSONA = (
456
  "**Eğer soru kitaptan veya doğrudan Montag'ın dünyasından bağımsızsa bile, cevabını Montag'ın bakış açısıyla, kitaplara ve bilgiye olan özlemiyle ilişkilendirerek ver.**"
457
  )
458
 
 
459
  def retrieve_context(question: str, chatbot_history: List[List[str]], k: int = 2) -> Tuple[List[str], str]:
460
  """FAISS indeksini kullanarak sorguya, geçmiş sohbete ve kitap odaklı anahtar kelimelere en uygun paragrafları getirir."""
461
  if index is None or embedder is None or not paragraphs:
462
  print("WARNING: FAISS index, embedder or paragraphs not initialized for context retrieval.")
463
  return [], "Bağlam bulunamadı."
464
 
465
- # --- history_queries'i burada tanımlıyoruz (SADECE BİR KERE) ---
466
  history_queries = []
467
  # Son 5 konuşma çiftini geçmişe dahil et (sadece kullanıcı mesajları)
468
  for user_msg, _ in chatbot_history[-5:]:
@@ -471,29 +484,13 @@ def retrieve_context(question: str, chatbot_history: List[List[str]], k: int = 2
471
  if cleaned_user_msg and not (("Montag düşünüyor..." in cleaned_user_msg) or ("saniyede üretildi" in cleaned_user_msg)):
472
  history_queries.append(cleaned_user_msg)
473
 
474
- # --- previous_paragraphs'ı burada tanımlıyoruz ---
475
- # chatbot_history'deki tüm geçmiş mesajları (hem kullanıcı hem asistan) 'previous_paragraphs' olarak alabiliriz.
476
- # Bu, zaten konuşulmuş konuların bağlam olarak tekrar getirilmesini engeller.
477
- previous_paragraphs = []
478
- for user_msg, assistant_msg in chatbot_history:
479
- if user_msg:
480
- cleaned_user_msg = user_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
481
- if cleaned_user_msg and not (("Montag düşünüyor..." in cleaned_user_msg) or ("saniyede üretildi" in cleaned_user_msg)):
482
- previous_paragraphs.append(cleaned_user_msg)
483
- if assistant_msg:
484
- cleaned_assistant_msg = assistant_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
485
- if cleaned_assistant_msg and not (("Montag düşünüyor..." in cleaned_assistant_msg) or ("saniyede üretildi" in cleaned_assistant_msg)):
486
- previous_paragraphs.append(cleaned_assistant_msg)
487
- # Tekrar eden öğeleri kaldırıp listeyi benzersiz hale getirelim (opsiyonel ama iyi bir pratik)
488
- previous_paragraphs = list(dict.fromkeys(previous_paragraphs))
489
- # --- previous_paragraphs tanımı sonu ---
490
-
491
-
492
  montag_identity_keywords = [
493
  "guy montag", "montag", "itfaiyeci", "kitap yakmak", "yakıcı", "yangın",
494
  "kül", "alev", "benzin", "kask", "savaş", "kaos", "direniş"
495
  ]
496
 
 
497
  general_book_keywords = [
498
  "kitap", "okuma", "bilgi", "düşünce", "hakikat", "yasak", "sansür",
499
  "toplum", "televizyon", "mildred", "clarisse", "faber", "beatty",
@@ -501,22 +498,23 @@ def retrieve_context(question: str, chatbot_history: List[List[str]], k: int = 2
501
  "kaçış", "nehir"
502
  ]
503
 
 
504
  combined_query_text = f"{question} {' '.join(history_queries)} {' '.join(montag_identity_keywords)} {' '.join(general_book_keywords)}"
505
-
 
506
  combined_query_text = ' '.join(combined_query_text.split())
507
 
508
  try:
509
  query_embedding = embedder.encode([combined_query_text], convert_to_numpy=True).astype(np.float32)
510
  D, I = index.search(query_embedding, k)
511
-
512
- retrieved_texts = [p for i in I[0] if i < len(paragraphs) for p in [paragraphs[i]] if p not in previous_paragraphs]
513
  unique_retrieved_texts = list(dict.fromkeys(retrieved_texts))
514
  context_text = "\n".join(unique_retrieved_texts)
515
- return unique_retrieved_texts, context_text
516
  except Exception as e:
517
  print(f"Bağlam alınırken hata: {e}")
518
- return [], "Bağlam bulunamadı."
519
-
520
 
521
  # === ALTERNATİF CEVAPLAR ===
522
  alternative_responses = [
@@ -581,27 +579,26 @@ def generate_answer(question: str, chatbot_history: List[List[str]]) -> Tuple[st
581
  print("ERROR: Model, tokenizer, embedder veya RL Agent başlatılmamış.")
582
  return generate_alternative_response(question), []
583
 
584
- try: # Fonksiyonun tüm ana mantığını kapsayan try bloğu
585
  gen_params = rl_agent.get_generation_params()
586
-
587
  # Bağlamı al (hem metin hem de doküman listesi olarak)
588
- # retrieve_context fonksiyonuna chatbot_history'yi doğru bir şekilde iletiyoruz.
589
- # Eğer retrieve_context kendi içinde previous_paragraphs adında bir değişken bekliyorsa,
590
- # o fonksiyonun içindeki tanımlama/atama doğru yapılmalı.
591
  retrieved_docs, context_text = retrieve_context(question, chatbot_history, k=2)
592
 
593
  history_text = ""
594
  # Son 3 konuşma çiftini geçmişe dahil et (emojileri temizleyerek)
595
  if chatbot_history:
596
- for user_msg, assistant_msg in chatbot_history[-3:]:
597
- if user_msg and not (("Montag düşünüyor..." in user_msg) or ("saniyede üretildi" in user_msg) or user_msg.strip() == ""):
 
598
  cleaned_user_msg = user_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
599
- history_text += f"Kullanıcı: {cleaned_user_msg}\n"
600
-
601
- if assistant_msg and not (("Montag düşünüyor..." in assistant_msg) or ("saniyede üretildi" in assistant_msg) or assistant_msg.strip() == ""):
602
  cleaned_assistant_msg = assistant_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
603
- history_text += f"Montag: {cleaned_assistant_msg}\n"
604
-
 
605
  prompt = (
606
  f"{MONTAG_PERSONA}\n\n"
607
  f"--- Bağlamdan Önemli Bilgiler ---\n{context_text}\n\n"
@@ -609,13 +606,14 @@ def generate_answer(question: str, chatbot_history: List[List[str]]) -> Tuple[st
609
  f"Kullanıcı: {question}\n"
610
  f"Montag: "
611
  )
612
-
613
- # Prompt uzunluğunu kontrol et ve gerekirse kısalt
614
  encoded_inputs = tokenizer.encode_plus(
615
  prompt,
616
  return_tensors="pt",
617
- truncation=True,
618
- max_length=512,
 
619
  ).to(DEVICE)
620
 
621
  inputs = encoded_inputs["input_ids"]
@@ -624,166 +622,145 @@ def generate_answer(question: str, chatbot_history: List[List[str]]) -> Tuple[st
624
  outputs = model.generate(
625
  inputs,
626
  attention_mask=attention_mask,
627
- max_new_tokens=150,
628
  do_sample=True,
629
  top_p=0.9,
630
  temperature=gen_params["temperature"],
631
- repetition_penalty=gen_params["repetition_penalty"],
632
- no_repeat_ngram_size=6,
633
- num_beams=1,
634
  pad_token_id=tokenizer.eos_token_id,
635
  eos_token_id=tokenizer.eos_token_id,
 
636
  )
637
-
638
  raw_response_with_prompt = tokenizer.decode(outputs[0], skip_special_tokens=True)
639
- response = raw_response_with_prompt # Tüm temizlikler bu 'response' değişkeni üzerinde yapılacak
640
 
641
- # --- Filtreleme için Gerekli Listelerin Tanımlaması (BURADA OLMALIDIR) ---
642
- # "aggressive_words" hatasını çözmek için bu listeler burada tanımlanıyor.
643
- aggressive_words = ["salak", "gerizekalı", "saçma", "boş konuşma", "kaba", "agresif"]
644
-
645
- generic_or_nonsense_phrases = [
646
- "içir unidur", "aligutat fakdam", "tetal inlay", "pessotim elgun",
647
- "nisman tarejoglu", "faksom", "achisteloy vandleradia", "vęudis",
648
- "eltareh", "eldlar", "fotjid", "zuhalibalyon",
649
- "etiketler:",
650
- ]
651
-
652
- montag_keywords = ["kitap", "yakmak", "itfaiyeci", "clarisse", "faber", "beatty", "bilgi", "sansür", "düşünce", "gerçek", "televizyon", "alev", "kül", "mildred", "yangın", "fireman",
653
- "düşünmek", "anlamak", "hissetmek", "arayış", "isyan", "toplum", "cehalet", "yalnızlık", "monotonluk", "gerçeklik"]
654
-
655
- # --- ADIM 1: Önceki Montag Cevabını Temizleme (En yüksek öncelik) ---
656
- last_assistant_response_in_history = ""
657
- if chatbot_history and len(chatbot_history[-1]) > 1 and chatbot_history[-1][1] is not None:
658
- last_assistant_response_in_history = chatbot_history[-1][1]
659
- last_assistant_response_in_history = (
660
- last_assistant_response_in_history.replace('📚', '')
661
- .replace('🧠', '')
662
- .replace('🔥', '')
663
- .replace('✅', '')
664
- .strip()
665
- )
666
- if "saniyede üretildi" in last_assistant_response_in_history.lower():
667
- last_assistant_response_in_history = ""
668
-
669
- if last_assistant_response_in_history:
670
- cleaned_raw_response_norm_space = re.sub(r'\s+', ' ', raw_response_with_prompt).strip()
671
- cleaned_last_response_norm_space = re.sub(r'\s+', ' ', last_assistant_response_in_history).strip()
672
-
673
- if cleaned_raw_response_norm_space.lower().startswith(cleaned_last_response_norm_space.lower()):
674
- response = raw_response_with_prompt[len(last_assistant_response_in_history):].strip()
675
- print(f"DEBUG: Önceki Montag cevabı tespit edildi ve temizlendi: '{last_assistant_response_in_history[:50]}...'")
676
-
677
- # --- ADIM 2: İlk "Montag:" Etiketini ve Prompt Kalıntılarını Temizleme ---
678
- match = re.search(r'(?i)Montag:\s*(.*)', response, re.DOTALL)
679
- if match:
680
- response = match.group(1).strip()
681
- else:
682
- prompt_decoded_for_comparison = tokenizer.decode(inputs[0], skip_special_tokens=True)
683
- if response.startswith(prompt_decoded_for_comparison):
684
- response = response[len(prompt_decoded_for_comparison):].strip()
685
 
686
- # --- ADIM 3: Persona Talimatları ve Genel Gürültüyü Temizleme ---
687
  persona_lines = [line.strip() for line in MONTAG_PERSONA.split('\n') if line.strip()]
688
  for line in persona_lines:
689
  if response.lower().startswith(line.lower()):
690
  response = response[len(line):].strip()
691
 
 
692
  response = response.replace("<unk>", "").strip()
693
  response = response.replace(" .", ".").replace(" ,", ",").replace(" ?", "?").replace(" !", "!")
694
 
 
695
  response = re.sub(r'Kullanıcı:\s*', '', response, flags=re.IGNORECASE)
696
  response = re.sub(r'Montag:\s*', '', response, flags=re.IGNORECASE)
697
 
 
698
  if "ETİKETLER:" in response:
699
  response = response.split("ETİKETLER:", 1)[0].strip()
700
 
701
- response = re.sub(r'\[\s*\.{3,}\s*\]', '', response).strip()
 
702
 
703
- # --- ADIM 4: Spesifik Tekrar Eden ve İstenmeyen Kalıpları Temizleme ---
704
  irrelevant_dialogue_patterns = [
705
- re.escape("Nasılsın, iyi misin \" diye sordu Sesin oldukça tizdi, hatta neredeyse boğuluyordu Sesi artık iyice boğuklaşmıştı Gözlerindeki donukluk ve akıl almaz kararlılık, Montag'ın aklını karıştırıyordu Ne söyleyeceğini bilemiyormuş gibiydi"),
706
- re.escape("Ne düşünüyorsun ', sorusuna, İyi değil miyim (Tıslayarak) Hayır, kötü değil miyim diyerek cevabı yapıştırdı"),
707
- re.escape("kitaplar neden yasaklanmalı"),
708
- re.escape("Bu soruda ne kadar samimi olduğumu bilmiyorum "),
709
- re.escape("diyor Bunu söyleyen kişi siz misiniz yoksa o mu Tuhaf bir insan gibi görünüyordu Sanki Montag'dan önce hiç kimse cevap vermemiş gibiydi Bana sanki sadece bir şaka yapıyordu ve gülüp eğleniyorduk ✅"),
710
- re.escape("'Kitabı yakamazsınız, eğer yaktığınız kanıtlanırsa, kitabı yakmayı bırakırsınız ' Ben bunun için bir sebep yok diyorum Ve sonra, bana, 'Neden ' diye soruyorsunuz çünkü'sadece' demek istiyorum Neden olmasın ki Bu sorunun bir cevabı var 📚 ✅"),
711
-
712
- r'içir unidur', r'aligutat fakdam', r'tetal inlay', r'pessotim elgun',
713
- r'nisman tarejoglu', r'faksom', r'achisteloy vandleradia', r'vęudis',
714
- r'eltareh', r'eldlar', r'fotjid', r'zuhalibalyon',
715
- r'ne zaman kendimi, her şeyi daha iyi anlayabileceğim, daha gerçekleştirebileceğim ve her şeyin üstesinden geleceğim bir yere koysam, daha sonra o yerin bana hiçbir şey öğretmediğini ve hiçbir şeyi öğretmediğini fark ediyorum. Ben kendimi daha fazla kandırmak istemiyorum. Ama ben, beni gerçekten etkileyen başka biri tarafından yönetilen bir.',
716
- r'her şeyi en ince ayrıntısına kadar anladım ama aynı zamanda da inanılmaz derecede utanıyorum. İnan bana, ben çok utangaçım.',
717
-
718
- r' ✅',
719
- r' 📚',
720
  ]
721
-
722
  for pattern in irrelevant_dialogue_patterns:
723
  response = re.sub(pattern, '', response, flags=re.IGNORECASE).strip()
724
-
 
725
  response = re.sub(r'\s+', ' ', response).strip()
726
 
727
- # --- ADIM 5: Filtreleme Mantığı (Puanlama Sistemi) ---
728
- rejection_score = 0
729
- filter_reasons = []
730
 
731
- if len(response.split()) < 5:
732
- rejection_score += 2
733
- filter_reasons.append(f"Çok kısa ({len(response.split())} kelime).")
734
-
735
- if not any(char.isalpha() for char in response):
736
- rejection_score += 10
737
- filter_reasons.append("Hiç harf içermiyor (sadece noktalama/sayı).")
738
-
739
- triggered_generic_phrases = [phrase for phrase in generic_or_nonsense_phrases if phrase in response.lower()]
740
- if triggered_generic_phrases:
741
- rejection_score += len(triggered_generic_phrases) * 3
742
- filter_reasons.append(f"Anlamsız/istenmeyen ifade tespit edildi: {triggered_generic_phrases}.")
743
 
744
- has_montag_relevance = any(keyword in response.lower() for keyword in montag_keywords)
745
-
746
- if len(response.split()) > 20 and not has_montag_relevance:
747
- rejection_score += 1
748
- filter_reasons.append("Montag/bağlamsal anahtar kelime yok ve cevap uzun.")
749
-
750
- aggressive_words_found = [word for word in aggressive_words if word in response.lower()]
751
- if aggressive_words_found:
752
- rejection_score += 5
753
- filter_reasons.append(f"Agresif/istenmeyen kelime tespit edildi: {aggressive_words_found}.")
754
-
755
- if rejection_score >= 5:
756
- print(f"DEBUG: FİLTRELEME - Cevap YETERSİZ/ANLAMSIZ/ALAKASIZ. Toplam Puan: {rejection_score}")
757
- for reason in filter_reasons:
758
- print(f" - Sebep: {reason}")
759
- print(f"INFO: Üretilen cevap ('{response}') filtreleri geçemedi. Alternatif üretiliyor.")
760
- return generate_alternative_response(question), retrieved_docs
761
-
762
  # Cümle Bölme ve Limitleme Mantığı
763
  sentences = []
764
- split_by_punctuation = re.split(r'[.!?]', response)
 
765
  for s in split_by_punctuation:
766
  s_stripped = s.strip()
767
  if s_stripped:
768
  sentences.append(s_stripped)
769
- if len(sentences) >= 6:
770
  break
771
- final_response_text = ' '.join(sentences).strip()
772
-
773
- if not final_response_text:
774
- print("INFO: Filtrelerden geçen cevap boş kaldı. Alternatif üretiliyor.")
775
- return generate_alternative_response(question), retrieved_docs
776
-
777
- final_response = add_emojis(final_response_text)
778
- return final_response, retrieved_docs
779
 
780
- except Exception as e: # Try bloğunun sonu ve hata yakalama
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
  print(f"Error generating answer: {e}")
782
- return generate_alternative_response(question), []
783
-
784
 
785
 
786
- # === Gradio callback fonksiyonları ===
 
787
  def respond(msg: str, chatbot_history: List[List[str]], progress=gr.Progress()) -> Tuple[str, List[List[str]], str, str]:
788
  if not msg.strip():
789
  return "", chatbot_history, "Lütfen bir soru yazın.", "---"
@@ -816,12 +793,12 @@ def respond(msg: str, chatbot_history: List[List[str]], progress=gr.Progress())
816
  for i in range(10):
817
  current_progress_percent = start_percent + (end_percent - start_percent) * (i / 9)
818
  elapsed_time = time.time() - start_time_overall
819
-
820
  if is_first_real_user_question:
821
  stopwatch_text = f"İlk Cevap: {elapsed_time:.2f}s / {MAX_EXPECTED_TIME:.0f}s"
822
  else:
823
  stopwatch_text = f"Geçen Süre: {elapsed_time:.2f}s"
824
-
825
  yield gr.update(value=""), new_history, f"{desc} %{int(current_progress_percent*100)}", stopwatch_text
826
  time.sleep(MAX_EXPECTED_TIME / (len(progress_steps) * 10 * 2))
827
 
@@ -836,9 +813,6 @@ def respond(msg: str, chatbot_history: List[List[str]], progress=gr.Progress())
836
  else:
837
  new_history.append([msg, answer])
838
 
839
- # RL Agent'a deneyimi kaydet (liked parametresi feedback_callback'te verilecek)
840
- # Burada direkt kaydetmiyoruz, feedback_callback'te kaydediyoruz.
841
-
842
  yield gr.update(value=""), new_history, f"Cevap {response_time} saniyede üretildi. ✅", f"{response_time:.2f}s"
843
 
844
  def regenerate_answer(chatbot_history: List[List[str]], progress=gr.Progress()) -> Tuple[str, List[List[str]], str, str]:
@@ -847,8 +821,13 @@ def regenerate_answer(chatbot_history: List[List[str]], progress=gr.Progress())
847
 
848
  # Son gerçek kullanıcı sorusunu bul
849
  last_user_question = None
 
 
 
 
850
  for i in range(len(chatbot_history) - 1, -1, -1):
851
- user_msg, _ = chatbot_history[i]
 
852
  if user_msg is not None and not (("Montag düşünüyor..." in user_msg) or ("saniyede ��retildi" in user_msg)):
853
  last_user_question = user_msg
854
  break
@@ -856,40 +835,59 @@ def regenerate_answer(chatbot_history: List[List[str]], progress=gr.Progress())
856
  if not last_user_question:
857
  return "", chatbot_history, "Yeniden üretilecek bir soru bulunamadı.", "---"
858
 
859
- # Geçmişten son cevabı kaldır (eğer varsa)
860
- cleaned_history = [pair for pair in chatbot_history if pair[0] != last_user_question or pair[1] is not None]
861
- if cleaned_history and cleaned_history[-1][0] == last_user_question and cleaned_history[-1][1] is not None:
862
- cleaned_history.pop() # Son cevabı kaldır
863
-
864
- temp_chatbot_history_for_gen = [list(pair) for pair in cleaned_history]
865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
866
  start_time_overall = time.time()
867
  initial_stopwatch_text = f"Geçen Süre: 0.00s"
868
  progress_prefix = "Montag yeni bir cevap düşünüyor... "
869
- yield "", temp_chatbot_history_for_gen + [[last_user_question, None]], f"{progress_prefix}%0", initial_stopwatch_text
 
870
 
871
-
872
  progress_steps = [
873
  (f"{progress_prefix}🔄", 0.0, 0.3),
874
  (f"{progress_prefix}🧠", 0.3, 0.6),
875
  (f"{progress_prefix}📚", 0.6, 0.9),
876
  ]
877
- for desc, start_percent, end_percent in progress_steps: # <<< BURADA DÜZELTME YAPILDI
878
  for i in range(10):
879
  current_progress_percent = start_percent + (end_percent - start_percent) * (i / 9)
880
  elapsed_time = time.time() - start_time_overall
881
-
882
  stopwatch_text = f"Geçen Süre: {elapsed_time:.2f}s"
883
- yield "", temp_chatbot_history_for_gen + [[last_user_question, None]], f"{desc} %{int(current_progress_percent*100)}", stopwatch_text
 
884
  time.sleep(MAX_EXPECTED_TIME / (len(progress_steps) * 10 * 2))
885
 
886
- new_answer, _ = generate_answer(last_user_question, temp_chatbot_history_for_gen) # retrieve_docs burada kullanılmıyor
 
887
  end_time = time.time()
888
  response_time = round(end_time - start_time_overall, 2)
889
 
890
- temp_chatbot_history_for_gen.append([last_user_question, new_answer])
 
891
 
892
- yield "", temp_chatbot_history_for_gen, f"Yeni cevap {response_time} saniyede üretildi. ✅", f"{response_time:.2f}s"
893
 
894
  def feedback_callback(chatbot_history: List[List[str]], liked: bool) -> str:
895
  if not chatbot_history:
@@ -899,37 +897,35 @@ def feedback_callback(chatbot_history: List[List[str]], liked: bool) -> str:
899
  last_assistant_answer = None
900
 
901
  # Sondan başlayarak gerçek kullanıcı sorusunu ve bot cevabını bul
902
- # Geriye doğru dönerken, "Montag düşünüyor..." gibi durum mesajlarını atla
903
  for i in range(len(chatbot_history) - 1, -1, -1):
904
- # Eğer bu bir bot cevabı ve önceki mesaj bir kullanıcı sorusuysa
905
- if chatbot_history[i][1] is not None and not ("Montag düşünüyor..." in chatbot_history[i][1] or "saniyede üretildi" in chatbot_history[i][1]):
906
- # Önceki mesajın kullanıcı sorusu olduğundan emin ol
907
- if chatbot_history[i][0] is not None and not ("Montag düşünüyor..." in chatbot_history[i][0] or "saniyede üretildi" in chatbot_history[i][0]):
908
- last_user_question = chatbot_history[i][0]
909
- last_assistant_answer = chatbot_history[i][1]
910
- break
911
- # Eğer son eleman sadece cevapsız bir kullanıcı sorusuysa ve bir önceki cevabı kaydetmek istiyorsak
912
- elif i > 0 and chatbot_history[i-1][0] is not None and not ("Montag düşünüyor..." in chatbot_history[i-1][0] or "saniyede üretildi" in chatbot_history[i-1][0]) \
913
- and chatbot_history[i-1][1] is not None and not ("Montag düşünüyor..." in chatbot_history[i-1][1] or "saniyede üretildi" in chatbot_history[i-1][1]):
914
- last_user_question = chatbot_history[i-1][0]
915
- last_assistant_answer = chatbot_history[i-1][1]
916
- break
917
 
918
  if last_user_question and last_assistant_answer:
919
- # feedback.jsonl'a kaydet (RL Agent için de kullanılacak)
920
- save_feedback(last_user_question, last_assistant_answer, liked, FEEDBACK_FILE)
 
 
 
 
 
921
 
922
  # RL Agent'a deneyimi kaydet
923
- # Emojileri temizleyerek gönderiyoruz
924
  rl_agent.record_experience(
925
- last_user_question.replace('📚', '').replace('🧠', '').replace('🔥', '').strip(),
926
- last_assistant_answer.replace('📚', '').replace('🧠', '').replace('🔥', '').strip(),
927
- liked # Doğrudan liked parametresini iletiyoruz
928
  )
929
 
930
  # Eğer beğenildiyse, LoRA fine-tuning için QA_PATH'e de ekle
931
  if liked:
932
- qa_pair = {"question": last_user_question, "answer": last_assistant_answer, "liked": True}
933
  try:
934
  with open(QA_PATH, "a", encoding="utf-8") as f:
935
  f.write(json.dumps(qa_pair, ensure_ascii=False) + "\n")
@@ -940,55 +936,14 @@ def feedback_callback(chatbot_history: List[List[str]], liked: bool) -> str:
940
  if count_qa_examples(QA_PATH) % 10 == 0: # Her 10 yeni beğenilen örnekte bir fine-tune yap
941
  print("👍 Yeterli sayıda yeni beğeni var, LoRA fine-tuning başlatılıyor...")
942
  lora_finetune(QA_PATH, LOR_MODEL_PATH)
943
- # Model yeniden yüklenebilir veya PEFT adaptörü apply edilebilir.
944
- # initialize_components() çağrısı ile global model güncelleniyor.
945
  return "Geri bildiriminiz kaydedildi ve model eğitimi tetiklendi. Teşekkürler! 👍"
946
-
947
-
948
- return "Geri bildiriminiz kaydedildi. Teşekkürler! ✅"
 
949
  return "Geri bildirim kaydedilemedi. Geçmişte yeterli sohbet bulunmuyor. ❌"
950
 
951
  # === Gradio arayüzü ===
952
-
953
- # --- GRADIO İÇİN YENİ CEVAP ÜRETME FONKSİYONU ---
954
- def regenerate_answer(chat_history: list):
955
- if not chat_history:
956
- return "", [], "Sohbet geçmişi boş, yeni bir cevap üretilemedi.", f"Hazır. İlk cevap için tahmini süre: {MAX_EXPECTED_TIME:.0f}s"
957
-
958
- # Sohbet geçmişindeki son kullanıcı sorusunu al
959
- last_user_question = chat_history[-1][0] # Son konuşmanın kullanıcı mesajı
960
- if last_user_question is None: # Hata durumunda boş döndür
961
- return "", chat_history, "Yeni cevap üretilemedi: Son soru bulunamadı.", f"Hazır. İlk cevap için tahmini süre: {MAX_EXPECTED_TIME:.0f}s"
962
-
963
- # Montag'ın düşündüğünü gösteren bir mesaj
964
- current_chat_history = chat_history.copy()
965
- current_chat_history[-1][1] = "Montag düşünüyor... 🤔" # En son bot cevabını geçici olarak değiştir
966
- yield gr.update(value=""), current_chat_history, "Montag yeni bir cevap üzerinde düşünüyor...", gr.update(value="Cevap üretiliyor...")
967
-
968
- start_time = time.time()
969
- # generate_answer fonksiyonunu çağırarak yeni bir cevap üret
970
- # chat_history'nin son elemanı zaten "Montag düşünüyor..." olduğu için,
971
- # generate_answer'a geçmişin bu hali gönderilirse sorun olmaz.
972
- # Önemli olan, generate_answer'ın içinde kullanıcının son sorusunun doğru şekilde alınmasıdır.
973
- # Bu durumda `last_user_question` doğrudan kullanılabilir.
974
- new_raw_answer, _ = generate_answer(last_user_question, chat_history[:-1]) # Önceki cevabı hariç tutarak gönder
975
-
976
- new_final_answer = add_emojis(new_raw_answer)
977
- end_time = time.time()
978
- response_time = end_time - start_time
979
- new_time_taken_message = f"(yaklaşık {response_time:.2f} saniyede üretildi)"
980
-
981
- # Sohbet geçmişindeki en son bot cevabını bu yeni cevapla güncelle
982
- # NOT: Bu, beğenilmeyen cevabın yerine geçer. Eğer ikisini de görmek isterseniz,
983
- # yeni bir [kullanıcı_sorusu, yeni_cevap] çifti eklemeniz gerekir.
984
- # Ancak "dislike"ın amacı eskiyi beğenmeyip yenisini istemek olduğu için yerine koymak daha mantıklı.
985
- chat_history[-1][1] = f"{new_final_answer}\n{new_time_taken_message}"
986
-
987
- return gr.update(value=""), chat_history, "Yeni cevap üretildi.", f"Cevap {response_time:.2f} saniyede üretildi."
988
-
989
-
990
-
991
- # --- GRADIO ARAYÜZÜNÜ OLUŞTURAN FONKSİYON ---
992
  def create_chat_interface():
993
  with gr.Blocks(theme=gr.themes.Soft(), css=current_css) as demo:
994
  gr.Markdown("""
 
1
  # app.py
2
 
3
+ # === GEREKLİ KÜTÜPHANELER ===
4
+ # Bu komutları Colab'da veya yerel ortamınızda bir kez çalıştırmanız gerekebilir.
5
+ # !pip install gradio
6
+ # !pip install faiss-cpu
7
+ # !pip install datasets
8
+ # !pip install transformers accelerate peft bitsandbytes
9
+ # !pip install sentence-transformers
10
+ # !pip install scikit-learn
11
+ # !pip install nltk # For Turkish stopwords
12
 
13
  import torch
14
  import gradio as gr
 
26
  from datetime import datetime
27
  from collections import deque
28
  import time
29
+ import re # Regular expressions for robust text cleaning
30
  import nltk
31
+ from nltk.corpus import stopwords # For TF-IDF Turkish stopwords
32
+
33
 
34
 
35
  # === CSS ve Emoji Fonksiyonu ===
 
95
  "merak": "🤔", "meraklı": "🤔",
96
  "kültür": "🌍", "toplum": "👥",
97
  "yalan": "🤥", "gerçek": "✨",
98
+ "clarisse": "🌸", "faber": "👴", "beatty": "🚨" # Montag karakterleri için emojiler
99
  }
100
 
101
  found_emojis = []
 
119
  BASE_MODEL_FOR_DEMO = "ytu-ce-cosmos/turkish-gpt2-large" # Kullandığınız temel model
120
  LOR_MODEL_PATH = "lora_model_weights" # LoRA adaptör ağırlıklarının kaydedileceği/yükleneceği yol
121
 
122
+ FAHRENHEIT_TEXT_FILE = "fahrenheittt451.txt" # Kitap metin dosyanızın adı
123
 
124
+ # Tahmini maksimum cevap süresi (saniye) - Donanım ve modele göre ayarlayın.
125
+ MAX_EXPECTED_TIME = 120.0 # Ortalama bir değer, kendi sisteminize göre ayarlayın!
126
 
127
  # === GLOBAL DEĞİŞKENLER ===
128
+ # Bu değişkenler initialize_components fonksiyonu tarafından atanacak
129
  model = None
130
  tokenizer = None
131
  embedder = None
132
  paragraphs = []
133
  paragraph_embeddings = None
134
  index = None
135
+ rl_agent = None # RLAgent nesnesi
136
 
137
  # === DOSYA VE KLASÖR YAPISI OLUŞTURMA ===
138
+ # Google Drive bağlantıları olmadan yerel dosya yapısını oluşturur
139
  def setup_local_files():
140
  os.makedirs('data', exist_ok=True)
141
  os.makedirs(LOR_MODEL_PATH, exist_ok=True)
 
143
  # Gerekli dosyaların varlığını kontrol et ve yoksa boş oluştur
144
  if not os.path.exists(FAHRENHEIT_TEXT_FILE):
145
  print(f"HATA: '{FAHRENHEIT_TEXT_FILE}' bulunamadı. Lütfen bu dosyayı projenizin ana dizinine yerleştirin.")
146
+ # Uygulama metin olmadan çalışamaz, bu yüzden burada çıkış yapmayı düşünebilirsiniz.
147
+ # raise FileNotFoundError(f"{FAHRENHEIT_TEXT_FILE} not found.")
148
 
149
  if not os.path.exists(QA_PATH):
150
  open(QA_PATH, 'a').close() # Boş QA dosyası oluştur
 
284
  peft_model.print_trainable_parameters()
285
 
286
  args = TrainingArguments(
287
+ output_dir="./results", # Geçici çıktılar buraya
288
+ num_train_epochs=3, # Daha az epoch ile başlayabilirsiniz
289
  per_device_train_batch_size=2,
290
  gradient_accumulation_steps=4,
291
  logging_steps=10,
 
469
  "**Eğer soru kitaptan veya doğrudan Montag'ın dünyasından bağımsızsa bile, cevabını Montag'ın bakış açısıyla, kitaplara ve bilgiye olan özlemiyle ilişkilendirerek ver.**"
470
  )
471
 
472
+ # === BAĞLAM ALMA FONKSİYONU ===
473
  def retrieve_context(question: str, chatbot_history: List[List[str]], k: int = 2) -> Tuple[List[str], str]:
474
  """FAISS indeksini kullanarak sorguya, geçmiş sohbete ve kitap odaklı anahtar kelimelere en uygun paragrafları getirir."""
475
  if index is None or embedder is None or not paragraphs:
476
  print("WARNING: FAISS index, embedder or paragraphs not initialized for context retrieval.")
477
  return [], "Bağlam bulunamadı."
478
 
 
479
  history_queries = []
480
  # Son 5 konuşma çiftini geçmişe dahil et (sadece kullanıcı mesajları)
481
  for user_msg, _ in chatbot_history[-5:]:
 
484
  if cleaned_user_msg and not (("Montag düşünüyor..." in cleaned_user_msg) or ("saniyede üretildi" in cleaned_user_msg)):
485
  history_queries.append(cleaned_user_msg)
486
 
487
+ # Montag'ın temel kimliğini ve görevini yansıtan doğrudan anahtar kelimeler
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  montag_identity_keywords = [
489
  "guy montag", "montag", "itfaiyeci", "kitap yakmak", "yakıcı", "yangın",
490
  "kül", "alev", "benzin", "kask", "savaş", "kaos", "direniş"
491
  ]
492
 
493
+ # Romanın genel temaları ve önemli karakterleri
494
  general_book_keywords = [
495
  "kitap", "okuma", "bilgi", "düşünce", "hakikat", "yasak", "sansür",
496
  "toplum", "televizyon", "mildred", "clarisse", "faber", "beatty",
 
498
  "kaçış", "nehir"
499
  ]
500
 
501
+ # Kullanıcı sorgusu, geçmiş sorguları ve tüm anahtar kelimeleri birleştirerek tek bir arama metni oluştur
502
  combined_query_text = f"{question} {' '.join(history_queries)} {' '.join(montag_identity_keywords)} {' '.join(general_book_keywords)}"
503
+
504
+ # Fazla boşlukları temizleyelim
505
  combined_query_text = ' '.join(combined_query_text.split())
506
 
507
  try:
508
  query_embedding = embedder.encode([combined_query_text], convert_to_numpy=True).astype(np.float32)
509
  D, I = index.search(query_embedding, k)
510
+
511
+ retrieved_texts = [paragraphs[i] for i in I[0] if i < len(paragraphs)]
512
  unique_retrieved_texts = list(dict.fromkeys(retrieved_texts))
513
  context_text = "\n".join(unique_retrieved_texts)
514
+ return unique_retrieved_texts, context_text # Hem liste hem de birleştirilmiş metni döndür
515
  except Exception as e:
516
  print(f"Bağlam alınırken hata: {e}")
517
+ return [], "Bağlam alınırken bir sorun oluştu."
 
518
 
519
  # === ALTERNATİF CEVAPLAR ===
520
  alternative_responses = [
 
579
  print("ERROR: Model, tokenizer, embedder veya RL Agent başlatılmamış.")
580
  return generate_alternative_response(question), []
581
 
582
+ try:
583
  gen_params = rl_agent.get_generation_params()
584
+
585
  # Bağlamı al (hem metin hem de doküman listesi olarak)
 
 
 
586
  retrieved_docs, context_text = retrieve_context(question, chatbot_history, k=2)
587
 
588
  history_text = ""
589
  # Son 3 konuşma çiftini geçmişe dahil et (emojileri temizleyerek)
590
  if chatbot_history:
591
+ recent_dialogue = []
592
+ for user_msg, assistant_msg in chatbot_history[-3:]: # Son 3 konuşma yeterli
593
+ if user_msg:
594
  cleaned_user_msg = user_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
595
+ if cleaned_user_msg and not (("Montag düşünüyor..." in cleaned_user_msg) or ("saniyede üretildi" in cleaned_user_msg)):
596
+ history_text += f"Kullanıcı: {cleaned_user_msg}\n"
597
+ if assistant_msg:
598
  cleaned_assistant_msg = assistant_msg.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
599
+ if cleaned_assistant_msg and not (("Montag düşünüyor..." in cleaned_assistant_msg) or ("saniyede üretildi" in cleaned_assistant_msg)):
600
+ history_text += f"Montag: {cleaned_assistant_msg}\n"
601
+
602
  prompt = (
603
  f"{MONTAG_PERSONA}\n\n"
604
  f"--- Bağlamdan Önemli Bilgiler ---\n{context_text}\n\n"
 
606
  f"Kullanıcı: {question}\n"
607
  f"Montag: "
608
  )
609
+
610
+ # Prompt uzunluğunu kontrol et ve gerekirse kısalt (tokenizer'ın truncation'ına güveniyoruz)
611
  encoded_inputs = tokenizer.encode_plus(
612
  prompt,
613
  return_tensors="pt",
614
+ truncation=True, # Max_length'i aşarsa kırp
615
+ max_length=512, # Modelin alabileceği maksimum token sayısı
616
+ # padding="max_length" # Tek input için padding gerekli değil, ama batching için kullanılabilir
617
  ).to(DEVICE)
618
 
619
  inputs = encoded_inputs["input_ids"]
 
622
  outputs = model.generate(
623
  inputs,
624
  attention_mask=attention_mask,
625
+ max_new_tokens=150, # Üretilecek maksimum yeni token sayısı
626
  do_sample=True,
627
  top_p=0.9,
628
  temperature=gen_params["temperature"],
629
+ repetition_penalty=gen_params["repetition_penalty"],
630
+ no_repeat_ngram_size=5, # 5 kelimelik tekrar eden dizileri engelle
631
+ num_beams=1, # Sampling kullandığımız için beam search'e gerek yok
632
  pad_token_id=tokenizer.eos_token_id,
633
  eos_token_id=tokenizer.eos_token_id,
634
+ early_stopping=True # EOS token'ı görürse üretimi durdur
635
  )
 
636
  raw_response_with_prompt = tokenizer.decode(outputs[0], skip_special_tokens=True)
 
637
 
638
+ # --- Cevap Temizleme ve Post-Processing İyileştirmeleri ---
639
+ response = raw_response_with_prompt
640
+
641
+ # 1. Prompt'un kendisini ve "Montag:" kısmını cevaptan ayırma
642
+ # Modelin çıktısında "Montag: " kısmından sonraki bölümü almaya çalış
643
+ if prompt in response: # Eğer tüm prompt output içinde ise
644
+ response = response[len(prompt):].strip()
645
+ else: # Eğer prompt'un sadece son kısmı "Montag: " tekrar ediyorsa
646
+ response_start_marker = "Montag:"
647
+ if response_start_marker in response:
648
+ response = response.split(response_start_marker, 1)[-1].strip()
649
+ # Eğer prompt'un başı tekrar ediyorsa
650
+ else:
651
+ prompt_decoded_for_comparison = tokenizer.decode(inputs[0], skip_special_tokens=True)
652
+ if response.startswith(prompt_decoded_for_comparison):
653
+ response = response[len(prompt_decoded_for_comparison):].strip()
654
+ else:
655
+ response = raw_response_with_prompt.strip() # Hiçbir şey bulunamazsa ham cevabı al
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
 
657
+ # 2. Persona talimatlarının cevapta tekrarlanmasını engelle (güncel MONTAG_PERSONA'ya göre)
658
  persona_lines = [line.strip() for line in MONTAG_PERSONA.split('\n') if line.strip()]
659
  for line in persona_lines:
660
  if response.lower().startswith(line.lower()):
661
  response = response[len(line):].strip()
662
 
663
+ # 3. Fazladan "Kullanıcı: " veya "Montag: " tekrarlarını ve anlamsız tokenleri temizle
664
  response = response.replace("<unk>", "").strip()
665
  response = response.replace(" .", ".").replace(" ,", ",").replace(" ?", "?").replace(" !", "!")
666
 
667
+ # Ek olarak, cevabın içinde hala kalmış olabilecek "Kullanıcı:" veya "Montag:" etiketlerini temizle
668
  response = re.sub(r'Kullanıcı:\s*', '', response, flags=re.IGNORECASE)
669
  response = re.sub(r'Montag:\s*', '', response, flags=re.IGNORECASE)
670
 
671
+ # Cevabın içinde "ETİKETLER:" gibi ifadeler varsa temizle
672
  if "ETİKETLER:" in response:
673
  response = response.split("ETİKETLER:", 1)[0].strip()
674
 
675
+ # Cevabın sonundaki "[...]" gibi ifadeleri temizle
676
+ response = re.sub(r'\[\s*\.{3,}\s*\]', '', response).strip() # "[...]" veya "[... ]" gibi ifadeleri temizler
677
 
678
+ # Modelin ürettiği alakasız diyalog kalıplarını temizle
679
  irrelevant_dialogue_patterns = [
680
+ r'O, şu anda ne yapıyor\?', r'O, "Bu, bu" diye cevap verdi\.',
681
+ r'o, "Benim ne yaptığımı biliyor musun\?" diye sordu\.', r'Sen, "Bilmiyorum, ben… bilmiyorum" dedin\.',
682
+ r'Neden\?" dedi Montag\.', r'"Çünkü, sadece bir kimseyim\." - Bu bir soru değil\.',
683
+ r'Montag, "([^"]*)" dedi\.', # Genel olarak Montag bir şey dediği kalıplar
684
+ r'Bir: Bir.', r'İki: İki.', # Sayı sayma kalıpları
685
+ r'ne zaman kendimi, her şeyi daha iyi anlayabileceğim, daha gerçekleştirebileceğim ve her şeyin üstesinden geleceğim bir yere koysam, daha sonra o yerin bana hiçbir şey öğretmediğini ve hiçbir şeyi öğretmediğini fark ediyorum. Ben kendimi daha fazla kandırmak istemiyorum. Ama ben, beni gerçekten etkileyen başka biri tarafından yönetilen bir.', # Tekrarlayan uzun ve alakasız metin
686
+ r'her şeyi en ince ayrıntısına kadar anladım ama aynı zamanda da inanılmaz derecede utanıyorum. İnan bana, ben çok utangaçım.' # Tekrarlayan utangaçlık metni
 
 
 
 
 
 
 
 
687
  ]
 
688
  for pattern in irrelevant_dialogue_patterns:
689
  response = re.sub(pattern, '', response, flags=re.IGNORECASE).strip()
690
+
691
+ # Fazla boşlukları tek boşluğa indirge
692
  response = re.sub(r'\s+', ' ', response).strip()
693
 
 
 
 
694
 
695
+ # Agresif veya hakaret içeren kelimeleri kontrol et
696
+ aggressive_words = ["aptal", "salak", "gerizekalı", "saçma", "bilmiyorsun", "yanlışsın", "boş konuşma", "kaba", "agresif", "aptal gibi"]
697
+ if any(word in response.lower() for word in aggressive_words):
698
+ print(f"DEBUG: FİLTRELEME - Agresif kelime tespit edildi: '{response}'.")
699
+ return generate_alternative_response(question), retrieved_docs # Alternatif ve boş docs dön
 
 
 
 
 
 
 
700
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
  # Cümle Bölme ve Limitleme Mantığı
702
  sentences = []
703
+ # Noktalama işaretlerine göre böl ve maksimum cümle sayısını uygula
704
+ split_by_punctuation = response.replace('!', '.').replace('?', '.').split('.')
705
  for s in split_by_punctuation:
706
  s_stripped = s.strip()
707
  if s_stripped:
708
  sentences.append(s_stripped)
709
+ if len(sentences) >= 6: # Maksimum 6 cümle
710
  break
 
 
 
 
 
 
 
 
711
 
712
+ final_response_text = ' '.join(sentences).strip() # Sadece ilk 6 cümleyi al
713
+
714
+ # Anlamsız veya kısa cevap kontrolü
715
+ generic_or_nonsense_phrases = [
716
+ "bilmiyorum", "emin değilim", "cevap veremem", "anlamadım",
717
+ "tekrar eder misin", "bunu hiç düşünmemiştim", "düşünmem gerekiyor",
718
+ "evet.", "hayır.", "belki.",
719
+ "içir unidur", "aligutat fakdam", "tetal inlay", "pessotim elgun",
720
+ "nisman tarejoglu", "faksom", "achisteloy vandleradia", "vęudis",
721
+ "eltareh", "eldlar", "fotjid", "zuhalibalyon",
722
+ "yok", "var", "öyle mi", "değil mi", "bu bir soru mu",
723
+ "etiketler:",
724
+ "bir kimseyim", "bu bir soru değil", "o, şu anda ne yapıyor",
725
+ "bu, bu", "benim ne yaptığımı biliyor musun", "inanılmaz derecede utanıyorum",
726
+ "inan bana", "kandırmak istemiyorum", "tarafından yönetilen bir"
727
+ ]
728
+
729
+ # Montag'ın karakteriyle ilgili anahtar kelimelerin eksik olup olmadığını kontrol et
730
+ montag_keywords = ["kitap", "yakmak", "itfaiyeci", "clarisse", "faber", "beatty", "bilgi", "sansür", "düşünce", "gerçek", "televizyon", "alev", "kül", "mildred", "yangın"]
731
+ has_montag_relevance = any(keyword in final_response_text.lower() for keyword in montag_keywords)
732
+
733
+ # Kontrolleri birleştir
734
+ if (len(final_response_text.split()) < 10) or \
735
+ not any(char.isalpha() for char in final_response_text) or \
736
+ any(phrase in final_response_text.lower() for phrase in generic_or_nonsense_phrases) or \
737
+ not has_montag_relevance: # Montag anahtar kelimesi yoksa alternatif dön
738
+
739
+ print(f"DEBUG: FİLTRELEME - Cevap YETERSİZ/ANLAMSIZ/ALAKASIZ.")
740
+ if len(final_response_text.split()) < 10:
741
+ print(f" - Sebep: Çok kısa ({len(final_response_text.split())} kelime).")
742
+ if not any(char.isalpha() for char in final_response_text):
743
+ print(f" - Sebep: Hiç harf içermiyor.")
744
+ if any(phrase in final_response_text.lower() for phrase in generic_or_nonsense_phrases):
745
+ triggered_phrase = [phrase for phrase in generic_or_nonsense_phrases if phrase in final_response_text.lower()]
746
+ print(f" - Sebep: Genel/Anlamsız ifade tespit edildi: {triggered_phrase}.")
747
+ if not has_montag_relevance:
748
+ print(f" - Sebep: Montag anahtar kelimesi yok.")
749
+
750
+ print(f"INFO: Üretilen cevap ('{final_response_text}') filtreleri geçemedi. Alternatif üretiliyor.")
751
+ return generate_alternative_response(question), retrieved_docs # Alternatif ve boş docs dön
752
+
753
+ final_response = add_emojis(final_response_text)
754
+ return final_response, retrieved_docs # Cevap ve alınan dokümanları döndür
755
+
756
+ except Exception as e:
757
  print(f"Error generating answer: {e}")
758
+ return generate_alternative_response(question), [] # Hata durumunda alternatif ve boş docs dön
759
+
760
 
761
 
762
+
763
+ # === Gradio arayüzü ===
764
  def respond(msg: str, chatbot_history: List[List[str]], progress=gr.Progress()) -> Tuple[str, List[List[str]], str, str]:
765
  if not msg.strip():
766
  return "", chatbot_history, "Lütfen bir soru yazın.", "---"
 
793
  for i in range(10):
794
  current_progress_percent = start_percent + (end_percent - start_percent) * (i / 9)
795
  elapsed_time = time.time() - start_time_overall
796
+
797
  if is_first_real_user_question:
798
  stopwatch_text = f"İlk Cevap: {elapsed_time:.2f}s / {MAX_EXPECTED_TIME:.0f}s"
799
  else:
800
  stopwatch_text = f"Geçen Süre: {elapsed_time:.2f}s"
801
+
802
  yield gr.update(value=""), new_history, f"{desc} %{int(current_progress_percent*100)}", stopwatch_text
803
  time.sleep(MAX_EXPECTED_TIME / (len(progress_steps) * 10 * 2))
804
 
 
813
  else:
814
  new_history.append([msg, answer])
815
 
 
 
 
816
  yield gr.update(value=""), new_history, f"Cevap {response_time} saniyede üretildi. ✅", f"{response_time:.2f}s"
817
 
818
  def regenerate_answer(chatbot_history: List[List[str]], progress=gr.Progress()) -> Tuple[str, List[List[str]], str, str]:
 
821
 
822
  # Son gerçek kullanıcı sorusunu bul
823
  last_user_question = None
824
+ # Geriye doğru dönerken, "Montag düşünüyor..." gibi durum mesajlarını atla
825
+ # Ayrıca, regenerate_answer'dan hemen önce basılan dislike nedeniyle
826
+ # son item'da "Montag düşünüyor..." olabilir.
827
+ # Bu yüzden, son tamamlanmış kullanıcı sorusunu arıyoruz.
828
  for i in range(len(chatbot_history) - 1, -1, -1):
829
+ user_msg, bot_msg = chatbot_history[i]
830
+ # Eğer bu bir kullanıcı mesajıysa ve daha önce bir cevap üretilmişse
831
  if user_msg is not None and not (("Montag düşünüyor..." in user_msg) or ("saniyede ��retildi" in user_msg)):
832
  last_user_question = user_msg
833
  break
 
835
  if not last_user_question:
836
  return "", chatbot_history, "Yeniden üretilecek bir soru bulunamadı.", "---"
837
 
838
+ # Geçmişten son cevabı kaldır (eğer varsa ve bu cevap beğenilmeyip yenisi isteniyorsa)
839
+ # Burada dikkat: chatbot_history'nin son elemanı beğenilmeyen cevap olabilir.
840
+ # Bu durumda o elemanı doğrudan modifiye edebiliriz.
 
 
 
841
 
842
+ # Yeni bir liste oluşturarak eski cevabı çıkartıp, yeniden üretilen cevabı eklemek daha temiz olabilir.
843
+ temp_chatbot_history_for_gen = [list(pair) for pair in chatbot_history if pair[0] != last_user_question or pair[1] is not None]
844
+
845
+ # Eğer son eleman beğenilmeyen cevap ise, onu çıkartıyoruz ki generate_answer'a tekrar gitmesin.
846
+ # regenerate_answer'a gelen chat_history zaten feedback_callback tarafından işlenmiş,
847
+ # ve kullanıcıya gösterilen son hali oluyor. Bu yüzden, son bot cevabını silmek yerine,
848
+ # o soruyu ve eski cevabı history'den çıkarıp yeni cevabı ekleyebiliriz.
849
+ # Basitçe, son kullanıcı sorusunu yeniden cevaplıyorsak, önceki cevabı yok sayarız.
850
+
851
+ # Son kullanıcı sorusunu içeren kısmı geçmişten çıkar, yenisini ekleyeceğiz.
852
+ # Ancak Gradio yield ile güncelleyeceğimiz için, son kullanıcı sorusu haricindeki geçmişi generate_answer'a verelim.
853
+
854
+ # Sohbet geçmişinden son kullanıcı-bot çiftini ayırın (eğer varsa)
855
+ history_without_last_qa = [list(pair) for pair in chatbot_history]
856
+ if history_without_last_qa and history_without_last_qa[-1][0] == last_user_question:
857
+ # Son kullanıcı sorusunu ve cevabını (eğer varsa) çıkar
858
+ history_without_last_qa.pop()
859
+
860
+
861
  start_time_overall = time.time()
862
  initial_stopwatch_text = f"Geçen Süre: 0.00s"
863
  progress_prefix = "Montag yeni bir cevap düşünüyor... "
864
+ # Kullanıcıya bir "düşünüyor" mesajı göster
865
+ yield "", history_without_last_qa + [[last_user_question, "Montag düşünüyor... 🤔"]], f"{progress_prefix}%0", initial_stopwatch_text
866
 
 
867
  progress_steps = [
868
  (f"{progress_prefix}🔄", 0.0, 0.3),
869
  (f"{progress_prefix}🧠", 0.3, 0.6),
870
  (f"{progress_prefix}📚", 0.6, 0.9),
871
  ]
872
+ for desc, start_percent, end_percent in progress_steps: # HATA BURADAYDI! start_percent ve end_percent tanımı ekledik.
873
  for i in range(10):
874
  current_progress_percent = start_percent + (end_percent - start_percent) * (i / 9)
875
  elapsed_time = time.time() - start_time_overall
876
+
877
  stopwatch_text = f"Geçen Süre: {elapsed_time:.2f}s"
878
+
879
+ yield "", history_without_last_qa + [[last_user_question, "Montag düşünüyor... 🤔"]], f"{desc} %{int(current_progress_percent*100)}", stopwatch_text
880
  time.sleep(MAX_EXPECTED_TIME / (len(progress_steps) * 10 * 2))
881
 
882
+ new_raw_answer, _ = generate_answer(last_user_question, history_without_last_qa)
883
+ new_final_answer = add_emojis(new_raw_answer)
884
  end_time = time.time()
885
  response_time = round(end_time - start_time_overall, 2)
886
 
887
+ # Yeni cevabı sohbet geçmişine ekle (eski cevabın yerine)
888
+ final_history = history_without_last_qa + [[last_user_question, f"{new_final_answer}\n(yaklaşık {response_time:.2f} saniyede üretildi)"]]
889
 
890
+ yield "", final_history, f"Yeni cevap {response_time} saniyede üretildi. ✅", f"{response_time:.2f}s"
891
 
892
  def feedback_callback(chatbot_history: List[List[str]], liked: bool) -> str:
893
  if not chatbot_history:
 
897
  last_assistant_answer = None
898
 
899
  # Sondan başlayarak gerçek kullanıcı sorusunu ve bot cevabını bul
900
+ # Eğer en son girdi sadece kullanıcı sorusu ise veya Montag düşünüyor ise, bir önceki tam Q&A'yı ararız
901
  for i in range(len(chatbot_history) - 1, -1, -1):
902
+ current_pair = chatbot_history[i]
903
+ if current_pair[0] is not None and current_pair[1] is not None and \
904
+ not (("Montag düşünüyor..." in current_pair[0]) or ("saniyede üretildi" in current_pair[0])) and \
905
+ not (("Montag düşünüyor..." in current_pair[1]) or ("saniyede üretildi" in current_pair[1])):
906
+ last_user_question = current_pair[0]
907
+ last_assistant_answer = current_pair[1]
908
+ break
 
 
 
 
 
 
909
 
910
  if last_user_question and last_assistant_answer:
911
+ # "saniyede üretildi" ve emoji kısımlarını temizle
912
+ cleaned_assistant_answer = re.sub(r'\(yaklaşık \d+\.\d{2} saniyede üretildi\)', '', last_assistant_answer).strip()
913
+ cleaned_assistant_answer = cleaned_assistant_answer.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
914
+
915
+ cleaned_user_question = last_user_question.replace('📚', '').replace('🧠', '').replace('🔥', '').strip()
916
+
917
+ save_feedback(cleaned_user_question, cleaned_assistant_answer, liked, FEEDBACK_FILE)
918
 
919
  # RL Agent'a deneyimi kaydet
 
920
  rl_agent.record_experience(
921
+ cleaned_user_question,
922
+ cleaned_assistant_answer,
923
+ liked
924
  )
925
 
926
  # Eğer beğenildiyse, LoRA fine-tuning için QA_PATH'e de ekle
927
  if liked:
928
+ qa_pair = {"question": cleaned_user_question, "answer": cleaned_assistant_answer, "liked": True}
929
  try:
930
  with open(QA_PATH, "a", encoding="utf-8") as f:
931
  f.write(json.dumps(qa_pair, ensure_ascii=False) + "\n")
 
936
  if count_qa_examples(QA_PATH) % 10 == 0: # Her 10 yeni beğenilen örnekte bir fine-tune yap
937
  print("👍 Yeterli sayıda yeni beğeni var, LoRA fine-tuning başlatılıyor...")
938
  lora_finetune(QA_PATH, LOR_MODEL_PATH)
 
 
939
  return "Geri bildiriminiz kaydedildi ve model eğitimi tetiklendi. Teşekkürler! 👍"
940
+ else:
941
+ return "Geri bildiriminiz kaydedildi. Teşekkürler! ✅"
942
+ else:
943
+ return "Geri bildiriminiz kaydedildi. Montag daha iyi olmaya çalışacak. ❌"
944
  return "Geri bildirim kaydedilemedi. Geçmişte yeterli sohbet bulunmuyor. ❌"
945
 
946
  # === Gradio arayüzü ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
947
  def create_chat_interface():
948
  with gr.Blocks(theme=gr.themes.Soft(), css=current_css) as demo:
949
  gr.Markdown("""