Vartex39 commited on
Commit
4c198b1
·
1 Parent(s): e724b91

🔥 Yeni UI entegrasyonu: RAG + geçmiş + özetleme

Browse files
Files changed (11) hide show
  1. app.py +4 -2
  2. core/rag/rag_engine.py +30 -0
  3. docs/tablo.txt +6 -0
  4. docs/test_data.py +10 -0
  5. docs/test_data.txt +7 -0
  6. main_test.py +11 -0
  7. rag_ui_template.py +47 -0
  8. requirements.txt +8 -4
  9. summarizer.py +62 -20
  10. test.py +13 -0
  11. ui.py +157 -109
app.py CHANGED
@@ -1,2 +1,4 @@
1
- import ui
2
- ui.demo.launch()
 
 
 
1
+ from ui import demo
2
+
3
+ if __name__ == "__main__":
4
+ demo.launch()
core/rag/rag_engine.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sentence_transformers import SentenceTransformer
2
+ import faiss
3
+ import numpy as np
4
+ import os
5
+
6
+ class SimpleRAG:
7
+ def __init__(self, model_name="sentence-transformers/all-MiniLM-L6-v2"):
8
+ self.model = SentenceTransformer(model_name)
9
+ self.index = None
10
+ self.docs = []
11
+ self.embeddings = []
12
+
13
+ def load_docs(self, folder_path):
14
+ for file in os.listdir(folder_path):
15
+ if file.endswith(".txt"):
16
+ with open(os.path.join(folder_path, file), "r", encoding="utf-8") as f:
17
+ text = f.read()
18
+ self.docs.append((file, text))
19
+
20
+ def build_index(self):
21
+ texts = [doc[1] for doc in self.docs]
22
+ embeddings = self.model.encode(texts, convert_to_numpy=True)
23
+ self.embeddings = embeddings
24
+ self.index = faiss.IndexFlatL2(embeddings.shape[1])
25
+ self.index.add(embeddings)
26
+
27
+ def search(self, query, top_k=3):
28
+ query_embedding = self.model.encode([query], convert_to_numpy=True)
29
+ distances, indices = self.index.search(query_embedding, top_k)
30
+ return [(self.docs[i][0], self.docs[i][1]) for i in indices[0]]
docs/tablo.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Ürün, Satış Miktarı, Fiyat (TL)
2
+ Laptop, 25, 15000
3
+ Telefon, 40, 10000
4
+ Tablet, 15, 7000
5
+ Monitör, 30, 4000
6
+ Kulaklık, 50, 1500
docs/test_data.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ TEXT = """
2
+
3
+ Veri madenciliği, büyük miktarda veriden anlamlı bilgiler elde etmeye yönelik süreçtir. Bu süreçte çeşitli algoritmalar kullanılarak veri içerisindeki gizli kalıplar ve ilişkiler ortaya çıkarılır.
4
+
5
+ Makine öğrenmesi bu sürecin önemli bir parçasıdır. Gözetimli öğrenme, gözetimsiz öğrenme ve pekiştirmeli öğrenme gibi farklı türleri vardır.
6
+
7
+ Bir diğer önemli konu ise doğal dil işleme (NLP)’dir. NLP, metin verisi üzerinde çalışarak insan dilini bilgisayarların anlayabileceği biçime çevirir. Örnek uygulamaları arasında metin sınıflandırma, duygu analizi ve özetleme bulunur.
8
+
9
+ Son olarak, bu tekniklerin birleşimiyle güçlü yapay zeka sistemleri oluşturulabilir. Geliştirilen sistemler; öneri motorları, sohbet botları ve karar destek sistemleri gibi alanlarda yaygın olarak kullanılmaktadır.
10
+ """
docs/test_data.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Veri madenciliği, büyük miktarda veriden anlamlı bilgiler elde etmeye yönelik süreçtir. Bu süreçte çeşitli algoritmalar kullanılarak veri içerisindeki gizli kalıplar ve ilişkiler ortaya çıkarılır.
2
+
3
+ Makine öğrenmesi bu sürecin önemli bir parçasıdır. Gözetimli öğrenme, gözetimsiz öğrenme ve pekiştirmeli öğrenme gibi farklı türleri vardır.
4
+
5
+ Bir diğer önemli konu ise doğal dil işleme (NLP)’dir. NLP, metin verisi üzerinde çalışarak insan dilini bilgisayarların anlayabileceği biçime çevirir. Örnek uygulamaları arasında metin sınıflandırma, duygu analizi ve özetleme bulunur.
6
+
7
+ Son olarak, bu tekniklerin birleşimiyle güçlü yapay zeka sistemleri oluşturulabilir. Geliştirilen sistemler; öneri motorları, sohbet botları ve karar destek sistemleri gibi alanlarda yaygın olarak kullanılmaktadır.
main_test.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from rag_engine import SimpleRAG
2
+
3
+ rag = SimpleRAG()
4
+ rag.load_docs("docs")
5
+ rag.build_index()
6
+
7
+ query = "FPGA ile yapılan veri gizleme uygulamaları neler?"
8
+ results = rag.search(query)
9
+
10
+ for fname, content in results:
11
+ print(f"\n--- {fname} ---\n{content[:300]}...\n")
rag_ui_template.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ import os
4
+ from rag_engine import SimpleRAG
5
+ from summarizer import generate_answer
6
+
7
+ def process_uploaded_files(files):
8
+ docs = []
9
+ for file in files:
10
+ with open(file.name, "r", encoding="utf-8") as f:
11
+ content = f.read()
12
+ docs.append((os.path.basename(file.name), content))
13
+ return docs
14
+
15
+ def answer_with_rag(query, files, user_api_key):
16
+ if not query or not files:
17
+ return "⚠️ Soru ya da dosya eksik.", "Hata"
18
+
19
+ docs = process_uploaded_files(files)
20
+ rag = SimpleRAG()
21
+ rag.docs = docs
22
+ rag.build_index()
23
+
24
+ results = rag.search(query, top_k=3)
25
+ contexts = [r[1] for r in results]
26
+ try:
27
+ answer = generate_answer(query, contexts, model_name="anthropic/claude-3-haiku", api_key=user_api_key)
28
+ sources = "\n\n---\n".join([f"Kaynak: {r[0]}" for r in results])
29
+ return answer, sources
30
+ except Exception as e:
31
+ return f"Hata: {str(e)}", "Hata"
32
+
33
+ with gr.Blocks() as demo:
34
+ gr.Markdown("## VizSum Pro - RAG Demo")
35
+
36
+ api_key_input = gr.Textbox(label="OpenRouter API Key", type="password")
37
+ file_input = gr.File(file_types=[".txt"], file_count="multiple", label="Metin Dosyası Yükle (.txt)")
38
+ query_input = gr.Textbox(label="Soru", placeholder="Claude'a sor...")
39
+
40
+ submit_btn = gr.Button("Yanıtla")
41
+ answer_output = gr.Textbox(label="Claude Yanıtı", lines=10)
42
+ source_output = gr.Textbox(label="Kullanılan Kaynaklar", lines=4, visible=True)
43
+
44
+ submit_btn.click(fn=answer_with_rag, inputs=[query_input, file_input, api_key_input], outputs=[answer_output, source_output])
45
+
46
+ if __name__ == "__main__":
47
+ demo.launch()
requirements.txt CHANGED
@@ -1,6 +1,10 @@
1
  gradio
2
- pytesseract
3
- python-dotenv
4
  requests
5
- PyMuPDF
6
- Pillow
 
 
 
 
 
 
 
1
  gradio
 
 
2
  requests
3
+ faiss-cpu
4
+ sentence-transformers
5
+ python-dotenv
6
+ python-docx
7
+ pytesseract
8
+ pdfminer.six
9
+ pdfplumber
10
+ Pillow
summarizer.py CHANGED
@@ -1,25 +1,24 @@
1
  import os
2
  import requests
3
  from dotenv import load_dotenv
4
- from utils import chunk_text_by_tokens # type: ignore
5
 
6
  load_dotenv()
7
- print("KEY IN SUMMARIZER:", os.getenv("OPENROUTER_API_KEY")) # <<< bunu ekle
8
  api_key = os.getenv("OPENROUTER_API_KEY")
9
 
10
- api_key = os.getenv("OPENROUTER_API_KEY")
11
  if not api_key or not api_key.strip():
12
  raise RuntimeError("❌ OPENROUTER_API_KEY bulunamadı. Hugging Face Secrets kısmına eklenmeli.")
13
 
14
- def build_prompt(text, mode, lang_mode="Otomatik", is_table=False):
 
 
 
15
  lang_instruction = ""
16
- if "Çevir" in lang_mode:
17
- if "Türkçeye" in lang_mode:
18
- lang_instruction = "\n\nSonuç Türkçeye çevrilsin."
19
- elif "İngilizceye" in lang_mode:
20
- lang_instruction = "\n\nSonuç İngilizceye çevrilsin."
21
- elif lang_mode == "Otomatik":
22
- lang_instruction = "\n\nMetnin dilini algıla ve uygun dilde özetle."
23
 
24
  if is_table:
25
  instruction = "Aşağıdaki tabloyu analiz et ve teknik bir şekilde özetle."
@@ -43,7 +42,7 @@ Aşağıdaki metni 3 ayrı biçimde özetle:
43
  instruction = "Bu metinden önemli notlar çıkar."
44
  elif "Chat" in mode:
45
  instruction = """
46
- Aşağıdaki yazışmalıarı veya serbest notları oku ve şunları çıkar:
47
 
48
  - Ana konuşma başlıkları
49
  - Varsa karar verilen noktalar
@@ -63,31 +62,74 @@ def summarize_text(text, mode, model_name="anthropic/claude-3-haiku", lang_mode=
63
  "Content-Type": "application/json"
64
  }
65
 
 
66
  payload = {
67
  "model": model_name,
68
  "messages": [
69
- {"role": "user", "content": build_prompt(text, mode, lang_mode, is_table)}
70
  ],
71
- "max_tokens": 1000
72
  }
73
 
74
  try:
75
  response = requests.post(url, headers=headers, json=payload)
76
  response.raise_for_status()
77
  result = response.json()
78
-
79
  return result['choices'][0]['message']['content'].strip()
80
  except requests.exceptions.HTTPError as e:
81
- return f"❌ HTTP Hatası: {e} | Yanıt: {response.text}"
82
  except Exception as e:
83
- return f"❌ Sistemsel Hata: {str(e)}"
84
 
85
  def summarize_long_text(text, mode, model_name="anthropic/claude-3-haiku", lang_mode="Otomatik", is_table=False):
86
- chunks = chunk_text_by_tokens(text, max_tokens=1300)
87
 
88
  summaries = []
89
  for chunk in chunks:
90
- summary = summarize_text(chunk, mode, model_name, lang_mode, is_table)
91
- summaries.append(summary)
 
 
 
92
 
93
  return "\n\n".join(summaries)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import requests
3
  from dotenv import load_dotenv
4
+ from utils import chunk_text_by_tokens
5
 
6
  load_dotenv()
 
7
  api_key = os.getenv("OPENROUTER_API_KEY")
8
 
 
9
  if not api_key or not api_key.strip():
10
  raise RuntimeError("❌ OPENROUTER_API_KEY bulunamadı. Hugging Face Secrets kısmına eklenmeli.")
11
 
12
+ class SummaryException(Exception):
13
+ pass
14
+
15
+ def build_prompt(text, mode, lang_mode="Otomatik", is_table=False, model_name=""):
16
  lang_instruction = ""
17
+
18
+ if lang_mode == "Türkçeye Çevir":
19
+ lang_instruction = "\n\nSonuç Türkçeye çevrilsin."
20
+ elif lang_mode == "İngilizceye Çevir":
21
+ lang_instruction = "\n\nSonuç İngilizceye çevrilsin."
 
 
22
 
23
  if is_table:
24
  instruction = "Aşağıdaki tabloyu analiz et ve teknik bir şekilde özetle."
 
42
  instruction = "Bu metinden önemli notlar çıkar."
43
  elif "Chat" in mode:
44
  instruction = """
45
+ Aşağıdaki yazışmaları veya serbest notları oku ve şunları çıkar:
46
 
47
  - Ana konuşma başlıkları
48
  - Varsa karar verilen noktalar
 
62
  "Content-Type": "application/json"
63
  }
64
 
65
+ prompt = build_prompt(text, mode, lang_mode, is_table, model_name)
66
  payload = {
67
  "model": model_name,
68
  "messages": [
69
+ {"role": "user", "content": prompt}
70
  ],
71
+ "max_tokens": 500
72
  }
73
 
74
  try:
75
  response = requests.post(url, headers=headers, json=payload)
76
  response.raise_for_status()
77
  result = response.json()
 
78
  return result['choices'][0]['message']['content'].strip()
79
  except requests.exceptions.HTTPError as e:
80
+ raise SummaryException(f"❌ HTTP Hatası: {e} | Yanıt: {response.text}")
81
  except Exception as e:
82
+ raise SummaryException(f"❌ Sistemsel Hata: {str(e)}")
83
 
84
  def summarize_long_text(text, mode, model_name="anthropic/claude-3-haiku", lang_mode="Otomatik", is_table=False):
85
+ chunks = chunk_text_by_tokens(text, max_tokens=800)
86
 
87
  summaries = []
88
  for chunk in chunks:
89
+ try:
90
+ summary = summarize_text(chunk, mode, model_name, lang_mode, is_table)
91
+ summaries.append(summary)
92
+ except SummaryException as e:
93
+ summaries.append(str(e))
94
 
95
  return "\n\n".join(summaries)
96
+
97
+ def generate_answer(query, source_tuples, chat_history=None, model_name="anthropic/claude-3-haiku"):
98
+ url = "https://openrouter.ai/api/v1/chat/completions"
99
+ headers = {
100
+ "Authorization": f"Bearer {api_key.strip()}",
101
+ "Content-Type": "application/json"
102
+ }
103
+
104
+ context_block = "\n\n".join([f"({i+1}) {txt}" for i, (_, txt) in enumerate(source_tuples)])
105
+
106
+ history_prompt = ""
107
+ if chat_history:
108
+ for q, a in chat_history:
109
+ history_prompt += f"Q: {q}\nA: {a}\n\n"
110
+
111
+ prompt = f"""{history_prompt}
112
+ Aşağıdaki kaynak metinlere dayanarak kullanıcıdan gelen soruyu yanıtla:
113
+
114
+ {context_block}
115
+
116
+ Soru: {query}
117
+ Yanıt:"""
118
+
119
+ payload = {
120
+ "model": model_name,
121
+ "messages": [
122
+ {"role": "user", "content": prompt.strip()}
123
+ ],
124
+ "max_tokens": 1500
125
+ }
126
+
127
+ try:
128
+ response = requests.post(url, headers=headers, json=payload)
129
+ response.raise_for_status()
130
+ result = response.json()
131
+ return result['choices'][0]['message']['content'].strip()
132
+ except requests.exceptions.HTTPError as e:
133
+ raise SummaryException(f"❌ HTTP Hatası: {e} | Yanıt: {response.text}")
134
+ except Exception as e:
135
+ raise SummaryException(f"❌ Sistemsel Hata: {str(e)}")
test.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from docs.test_data import TEXT
2
+ from summarizer import summarize_long_text
3
+
4
+ summary = summarize_long_text(
5
+ TEXT,
6
+ mode="Sade",
7
+ model_name="mistralai/mistral-7b-instruct",
8
+ lang_mode="Otomatik",
9
+ is_table=False
10
+ )
11
+
12
+ print("\n--- AI Özeti ---\n")
13
+ print(summary)
ui.py CHANGED
@@ -1,136 +1,184 @@
1
  import gradio as gr
2
- import tempfile
 
3
  from ocr_engine import extract_text_from_image
4
  from pdf_reader import extract_text_chunks_from_pdf
5
- from summarizer import summarize_long_text
6
- from utils import chunk_text_by_tokens
7
-
8
- def process_input(pdf, image, manual_text, mode, model_name, start_page, end_page, lang_mode, is_table):
9
- if is_table and model_name != "anthropic/claude-3-haiku":
10
- return "Tablo içeriği için yalnızca Claude önerilir.", "", None
11
-
12
- info_block = ""
13
-
14
- if pdf is not None:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  text_chunks = extract_text_chunks_from_pdf(pdf, start=int(start_page), end=int(end_page))
16
  if any("[ERROR]" in chunk for chunk in text_chunks):
17
- return text_chunks[0], "", None
18
-
19
  all_text = "\n\n".join(text_chunks)
20
  chunk_count = len(chunk_text_by_tokens(all_text, max_tokens=1000))
21
- info_block = f"""
22
- Sayfa Aralığı: {start_page}–{end_page}
23
- Model: {model_name}
24
- Chunk Sayısı: {chunk_count}
25
- """.strip()
26
-
27
- elif image is not None:
28
  text = extract_text_from_image(image)
29
  if "[ERROR]" in text:
30
- return text, "", None
31
- text_chunks = [text]
32
- all_text = text
 
 
 
33
 
34
- elif manual_text.strip() != "":
35
- text_chunks = [manual_text]
36
- all_text = manual_text
37
 
38
- else:
39
- return "Lütfen bir giriş türü seçin.", "", None
 
40
 
41
  full_summary = summarize_long_text(all_text, mode, model_name, lang_mode, is_table)
42
-
43
  if info_block:
44
- full_summary = f"{info_block}\n\n{full_summary}"
45
 
46
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode='w', encoding='utf-8')
47
  temp_file.write(full_summary)
48
  temp_file.close()
49
-
50
  return all_text, full_summary, temp_file.name
51
 
52
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  with gr.Blocks() as demo:
55
  gr.Markdown("## VizSum")
56
 
57
- with gr.Row():
58
- pdf_input = gr.File(label="PDF Yükle", file_types=[".pdf"])
59
- image_input = gr.Image(type="filepath", label="Görsel Yükle")
60
-
61
- manual_input = gr.Textbox(lines=5, label="Metni Manuel Gir")
62
-
63
- with gr.Row(visible=False) as page_controls:
64
- start_page = gr.Number(label="Başlangıç Sayfası", value=1, minimum=1, precision=0)
65
- end_page = gr.Number(label="Bitiş Sayfası", value=5, minimum=1, precision=0)
66
-
67
- def show_page_controls(pdf):
68
- return gr.update(visible=True)
69
-
70
- pdf_input.change(
71
- fn=show_page_controls,
72
- inputs=[pdf_input],
73
- outputs=[page_controls]
74
- )
75
-
76
-
77
-
78
- def enable_page_inputs(pdf):
79
- return gr.update(interactive=True), gr.update(interactive=True)
80
-
81
- pdf_input.change(
82
- fn=enable_page_inputs,
83
- inputs=[pdf_input],
84
- outputs=[start_page, end_page]
85
- )
86
-
87
- mode_selector = gr.Dropdown(
88
- choices=[
89
- "Teknik Özet",
90
- "Sade Anlatım",
91
- "Eleştir ve Değerlendir",
92
- "Başlık Çıkar",
93
- "Not Formatı",
94
- "Karma Özet",
95
- "Chat Özeti (Yazışma/Not)"
96
- ],
97
- label="Özetleme Modu",
98
- value="Teknik Özet"
99
- )
100
-
101
- model_selector = gr.Dropdown(
102
- choices=[
103
- "anthropic/claude-3-haiku",
104
- "openai/gpt-3.5-turbo",
105
- "mistralai/mistral-7b-instruct"
106
- ],
107
- label="Dil Modeli",
108
- value="anthropic/claude-3-haiku"
109
- )
110
-
111
- with gr.Row():
112
- submit_btn = gr.Button("Özetle")
113
-
114
- with gr.Row():
115
- text_output = gr.Textbox(label="Giriş Metni")
116
- summary_output = gr.Textbox(label="AI Özeti", lines=10, show_copy_button=True)
117
- summary_file = gr.File(label="Özeti İndir", interactive=True)
118
-
119
- lang_mode = gr.Radio(
120
- choices=["Otomatik", "Sadece Türkçe", "Sadece İngilizce", "Türkçeye Çevir", "İngilizceye Çevir"],
121
- label="Dil Algılama / Çeviri Modu",
122
- value="Otomatik"
123
- )
124
-
125
- is_table = gr.Checkbox(label="Tablo içeriyor (Claude tablo gibi özetlesin)", value=False)
126
-
127
- submit_btn.click(
128
- fn=process_input,
129
- inputs=[pdf_input, image_input, manual_input, mode_selector, model_selector, start_page, end_page, lang_mode, is_table],
130
- outputs=[text_output, summary_output, summary_file]
131
- )
132
-
133
-
134
 
135
  if __name__ == "__main__":
136
  demo.launch(share=True)
 
1
  import gradio as gr
2
+ import tempfile
3
+ import os
4
  from ocr_engine import extract_text_from_image
5
  from pdf_reader import extract_text_chunks_from_pdf
6
+ from summarizer import summarize_long_text, generate_answer
7
+ from utils import chunk_text_by_tokens
8
+ from core.rag.rag_engine import SimpleRAG
9
+ from docx import Document
10
+
11
+ chat_history = []
12
+ rag_engine = None
13
+
14
+ def load_uploaded_docs(files):
15
+ global rag_engine
16
+ if not files:
17
+ return "Dosya yüklenmedi."
18
+ rag_engine = SimpleRAG()
19
+ docs = []
20
+
21
+ for file in files:
22
+ ext = os.path.splitext(file.name)[-1].lower()
23
+ try:
24
+ if ext == ".txt":
25
+ with open(file.name, "r", encoding="utf-8") as f:
26
+ content = f.read()
27
+ elif ext == ".pdf":
28
+ chunks = extract_text_chunks_from_pdf(file.name)
29
+ content = "\n".join(chunks)
30
+ elif ext == ".docx":
31
+ doc = Document(file.name)
32
+ content = "\n".join([p.text for p in doc.paragraphs])
33
+ elif ext in [".jpg", ".jpeg", ".png"]:
34
+ content = extract_text_from_image(file.name)
35
+ else:
36
+ content = ""
37
+ if content.strip():
38
+ docs.append((os.path.basename(file.name), content))
39
+ except Exception as e:
40
+ print("Dosya okuma hatası:", e)
41
+
42
+ if not docs:
43
+ return "Dosyalar boş veya okunamadı."
44
+
45
+ rag_engine.docs = docs
46
+ rag_engine.build_index()
47
+ return f"{len(docs)} dosya başarıyla yüklendi."
48
+
49
+ def extract_input_text(pdf, image, manual_text, start_page, end_page):
50
+ if pdf:
51
  text_chunks = extract_text_chunks_from_pdf(pdf, start=int(start_page), end=int(end_page))
52
  if any("[ERROR]" in chunk for chunk in text_chunks):
53
+ return text_chunks[0], None, None
 
54
  all_text = "\n\n".join(text_chunks)
55
  chunk_count = len(chunk_text_by_tokens(all_text, max_tokens=1000))
56
+ info_block = f"Sayfa Aralığı: {start_page}–{end_page}\nChunk Sayısı: {chunk_count}"
57
+ return all_text, info_block, "pdf"
58
+ elif image:
 
 
 
 
59
  text = extract_text_from_image(image)
60
  if "[ERROR]" in text:
61
+ return text, None, None
62
+ return text, None, "image"
63
+ elif manual_text.strip():
64
+ return manual_text, None, "manual"
65
+ else:
66
+ return "Lütfen bir giriş türü seçin.", None, None
67
 
68
+ def process_input(pdf, image, manual_text, mode, model_name, start_page, end_page, lang_mode, is_table):
69
+ if is_table and model_name != "anthropic/claude-3-haiku":
70
+ return "Tablo içeriği için yalnızca Claude önerilir.", "", None
71
 
72
+ all_text, info_block, input_type = extract_input_text(pdf, image, manual_text, start_page, end_page)
73
+ if input_type is None:
74
+ return all_text, "", None
75
 
76
  full_summary = summarize_long_text(all_text, mode, model_name, lang_mode, is_table)
 
77
  if info_block:
78
+ full_summary = f"{info_block}\n\nModel: {model_name}\n\n{full_summary}"
79
 
80
  temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode='w', encoding='utf-8')
81
  temp_file.write(full_summary)
82
  temp_file.close()
 
83
  return all_text, full_summary, temp_file.name
84
 
85
+ def format_chat_history():
86
+ return "\n\n".join([f"Soru: {q}\nYanıt: {a}" for q, a in chat_history])
87
+
88
+ def toggle_history_display(show):
89
+ return gr.update(visible=show, value=format_chat_history() if show else "")
90
+
91
+ def process_rag_query(query):
92
+ if rag_engine is None:
93
+ return "Önce metin dosyası yükleyin.", "", ""
94
+
95
+ results = rag_engine.search(query, top_k=3)
96
+ try:
97
+ answer = generate_answer(query, results, chat_history=chat_history)
98
+ chat_history.append((query, answer))
99
+ sources = "\n\n---\n\n".join([f"Kaynak: {fname}" for fname, _ in results])
100
+ history = format_chat_history()
101
+ except Exception as e:
102
+ answer = str(e)
103
+ sources = "Hata oluştu."
104
+ history = ""
105
+ return answer, sources, history
106
+
107
+ def reset_history():
108
+ global chat_history
109
+ chat_history = []
110
+ return "", "", ""
111
 
112
  with gr.Blocks() as demo:
113
  gr.Markdown("## VizSum")
114
 
115
+ with gr.Tabs():
116
+ with gr.TabItem("Özetleme"):
117
+ with gr.Row():
118
+ pdf_input = gr.File(label="PDF Yükle", file_types=[".pdf"])
119
+ image_input = gr.Image(type="filepath", label="Görsel Yükle")
120
+
121
+ manual_input = gr.Textbox(lines=5, label="Metni Manuel Gir")
122
+
123
+ with gr.Row(visible=False) as page_controls:
124
+ start_page = gr.Number(label="Başlangıç Sayfası", value=1, minimum=1, precision=0)
125
+ end_page = gr.Number(label="Bitiş Sayfası", value=5, minimum=1, precision=0)
126
+
127
+ pdf_input.change(fn=lambda pdf: gr.update(visible=True), inputs=[pdf_input], outputs=[page_controls])
128
+ pdf_input.change(fn=lambda pdf: (gr.update(interactive=True), gr.update(interactive=True)), inputs=[pdf_input], outputs=[start_page, end_page])
129
+
130
+ mode_selector = gr.Dropdown(
131
+ choices=["Teknik Özet", "Sade Anlatım", "Eleştir ve Değerlendir", "Başlık Çıkar", "Not Formatı", "Karma Özet", "Chat Özeti (Yazışma/Not)"],
132
+ label="Özetleme Modu",
133
+ value="Teknik Özet"
134
+ )
135
+
136
+ model_selector = gr.Dropdown(
137
+ choices=["anthropic/claude-3-haiku", "openai/gpt-3.5-turbo", "mistralai/mistral-7b-instruct"],
138
+ label="Dil Modeli",
139
+ value="anthropic/claude-3-haiku"
140
+ )
141
+
142
+ lang_mode = gr.Radio(
143
+ choices=["Otomatik", "Sadece Türkçe", "Sadece İngilizce", "Türkçeye Çevir", "İngilizceye Çevir"],
144
+ label="Dil Algılama / Çeviri Modu",
145
+ value="Otomatik"
146
+ )
147
+
148
+ is_table = gr.Checkbox(label="Tablo içeriyor (Claude tablo gibi özetlesin)", value=False)
149
+
150
+ submit_btn = gr.Button("Özetle")
151
+
152
+ text_output = gr.Textbox(label="Giriş Metni")
153
+ summary_output = gr.Textbox(label="AI Özeti", lines=10, show_copy_button=True)
154
+ summary_file = gr.File(label="Özeti İndir", interactive=False)
155
+
156
+ submit_btn.click(
157
+ fn=process_input,
158
+ inputs=[pdf_input, image_input, manual_input, mode_selector, model_selector, start_page, end_page, lang_mode, is_table],
159
+ outputs=[text_output, summary_output, summary_file]
160
+ )
161
+
162
+ with gr.TabItem("Soru-Cevap (RAG)"):
163
+ doc_upload = gr.File(label="Dosya Yükle (.txt, .pdf, .docx, .jpg, .png)", file_types=[".txt", ".pdf", ".docx", ".jpg", ".png"], file_count="multiple")
164
+ upload_status = gr.Textbox(label="Yükleme Durumu")
165
+ doc_upload.change(fn=load_uploaded_docs, inputs=[doc_upload], outputs=[upload_status])
166
+
167
+ query_input = gr.Textbox(label="Soru", placeholder="Belgelerden bir şey sor...")
168
+ answer_output = gr.Textbox(label="Claude Yanıtı", lines=10)
169
+ source_output = gr.Textbox(label="Kaynaklar", lines=5, visible=False)
170
+
171
+ history_toggle = gr.Checkbox(label="Geçmişi Göster", value=False)
172
+ history_output = gr.Textbox(label="Soru-Cevap Geçmişi", lines=10, visible=False, interactive=False)
173
+
174
+ show_sources = gr.Checkbox(label="Kaynakları Göster", value=False)
175
+ rag_btn = gr.Button("Cevapla")
176
+ reset_btn = gr.Button("Geçmişi Sıfırla")
177
+
178
+ rag_btn.click(fn=process_rag_query, inputs=[query_input], outputs=[answer_output, source_output, history_output])
179
+ history_toggle.change(fn=toggle_history_display, inputs=[history_toggle], outputs=[history_output])
180
+ show_sources.change(fn=lambda visible: gr.update(visible=visible), inputs=[show_sources], outputs=[source_output])
181
+ reset_btn.click(fn=reset_history, inputs=[], outputs=[answer_output, source_output, history_output])
 
 
 
 
 
 
 
 
 
 
182
 
183
  if __name__ == "__main__":
184
  demo.launch(share=True)