File size: 21,498 Bytes
5334f44
 
 
 
2b35763
5334f44
40f29ea
 
 
 
5334f44
 
40f29ea
2b35763
 
 
 
 
 
 
5334f44
 
40f29ea
5334f44
 
2b35763
 
5334f44
 
 
40f29ea
5334f44
40f29ea
2b35763
 
 
 
 
 
 
 
 
 
40f29ea
2b35763
 
40f29ea
5334f44
40f29ea
5334f44
 
 
 
2b35763
5334f44
 
 
2b35763
 
 
 
 
 
 
40f29ea
5334f44
 
 
 
2b35763
 
 
 
5334f44
1dc478a
5334f44
 
2b35763
eed7a19
2b35763
5334f44
 
eed7a19
8b4bb8f
 
5334f44
eed7a19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40f29ea
 
8b4bb8f
 
 
faaf135
5334f44
eed7a19
2b35763
eed7a19
8b4bb8f
 
eed7a19
 
 
5334f44
 
 
 
2b35763
5334f44
 
 
 
 
 
8b4bb8f
 
 
2b35763
5334f44
2b35763
 
 
 
eed7a19
2b35763
 
 
 
 
5334f44
 
2b35763
8b4bb8f
 
 
5334f44
8b4bb8f
eed7a19
8b4bb8f
 
2b35763
8b4bb8f
 
 
 
 
 
 
 
2b35763
eed7a19
5334f44
 
8b4bb8f
 
 
 
5334f44
8b4bb8f
5334f44
2b35763
8b4bb8f
5334f44
2b35763
7c14329
2b35763
 
 
 
40f29ea
 
2b35763
 
 
 
 
 
 
 
5334f44
7c14329
2b35763
 
 
 
40f29ea
2b35763
7c14329
 
5334f44
 
7c14329
2b35763
7c14329
2b35763
7c14329
5334f44
 
 
 
 
8b4bb8f
 
5334f44
2b35763
 
 
5334f44
2b35763
 
 
5334f44
2b35763
5334f44
7c14329
2b35763
 
8b4bb8f
 
2b35763
5334f44
8b4bb8f
 
 
7c14329
8b4bb8f
 
2b35763
7c14329
 
2b35763
7c14329
 
2b35763
 
7c14329
2b35763
 
 
 
5334f44
40f29ea
5334f44
40f29ea
 
 
 
 
 
 
 
 
 
 
 
 
 
faaf135
 
40f29ea
 
 
 
 
 
 
 
 
 
5334f44
 
2b35763
 
 
 
 
 
7c14329
 
2b35763
 
 
7c14329
2b35763
 
 
 
 
7c14329
2b35763
 
 
d9c31cf
2b35763
 
 
7c14329
2b35763
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b4bb8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2b35763
 
 
 
 
8b4bb8f
2b35763
d9c31cf
7c14329
40f29ea
2b35763
40f29ea
 
 
 
 
 
 
 
 
2b35763
 
 
8b4bb8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2b35763
 
 
8b4bb8f
2b35763
d9c31cf
 
 
2b35763
 
2dc408f
 
 
 
2b35763
 
 
40f29ea
 
13b6cde
faaf135
40f29ea
 
 
2b35763
 
2dc408f
 
 
 
2b35763
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
import torch
import wikipedia
from transformers import pipeline
import gradio as gr
import os

# --- Global Log Listesini Tanımla ---
# Başlangıçtaki model yükleme mesajlarını Gradio UI'a aktarmak için bu listeyi kullanıyoruz.
_initial_logs = []

# --- 1. İlk Tarama Modeli (Hızlı NER) ---
# XLM-Roberta çok dillidir ve standart olarak PER, LOC, ORG, MISC etiketleri üretir.
_initial_logs.append("1. Standart NER Modeli yükleniyor...")
# NER modelini yüklerken, cihazı açıkça belirterek Gradio'nun başlangıç aşamalarında oluşabilecek hataları azaltıyoruz.
ner_pipe = pipeline(
    "ner", 
    model="xlm-roberta-large-finetuned-conll03-english", 
    aggregation_strategy="simple",
    device=0 if torch.cuda.is_available() else -1
)

# --- 2. Akıl Yürütme Modeli (LLM - RAG Karar Verici) ---
_initial_logs.append("2. LLM (Karar Verici) yükleniyor...")
model_id = "Qwen/Qwen2.5-1.5B-Instruct" # Türkçe yeteneği iyi ve hızlı
llm_model_kwargs = {}

# Cihaz ayarlarını yapma
if torch.cuda.is_available():
    llm_model_kwargs["torch_dtype"] = torch.bfloat16
    llm_device_map = "auto"
    _initial_logs.append("CUDA desteği bulundu, model GPU üzerinde bfloat16 ile yüklenecek.")
else:
    _initial_logs.append("CUDA desteği bulunamadı, model CPU üzerinde float32 ile yüklenecek.")
    llm_device_map = "cpu" # Açıkça CPU'ya ayarlandı

try:
    gen_pipe = pipeline(
        "text-generation",
        model=model_id,
        model_kwargs=llm_model_kwargs,
        device_map=llm_device_map
    )
except Exception as e:
    _initial_logs.append(f"❌ LLM yüklenirken bir hata oluştu (Muhtemelen Bellek Yetersizliği/OOM): {e}")
    # Olası bir çökme durumunu önlemek için alternatif bir çözüm ekleyin
    gen_pipe = None
    _initial_logs.append("LLM boru hattı devre dışı bırakıldı. Uygulama sadece temel NER yapacaktır.")

_initial_logs.append("✅ Modeller Hazır!")

# --- Wikipedia Fonksiyonu ---
wikipedia.set_lang("tr")
def get_wiki_summary(term):
    """Wikipedia'dan bir terim için özet bilgi çeker."""
    try:
        results = wikipedia.search(term)
        if not results: return None
        # İlk sonucu alır, ilk 3 cümleyi özetler
        return wikipedia.summary(results[0], sentences=3, auto_suggest=False) 
    except wikipedia.exceptions.PageError:
        return None
    except wikipedia.exceptions.RedirectError:
        return None
    except Exception as e:
        # Bu loglar konsola düşer, ancak UI log akışına dahil edilmez.
        return None

# --- LLM ile Etiket Rafine Etme Fonksiyonu ---
def refine_label_with_llm(entity_text, wiki_context, custom_label_definitions):
    """LLM kullanarak MISC etiketini özel etiketlerden biriyle rafine eder."""
    if gen_pipe is None:
        return "MISC" # LLM devre dışıysa rafine etme

    labels_str = ", ".join(custom_label_definitions.keys())
    label_definitions_str = "\n".join(
        [f"- {k}: {v}" for k, v in custom_label_definitions.items()]
    )
    
    # LLM için örnekler (Few-shot learning) - İyileştirilmiş ve daha çeşitli
    few_shot_examples = """ÖRNEK 1 (NORP İÇİN):
VARLIK: Türk
BAĞLAM: Türkler, Türkiye Cumhuriyeti'nde yaşayan ve Türkçe konuşan büyük bir etnik gruptur. (NORP tanımı ile eşleşir.)
GÖZLEM: 'Türk' varlığı bir etnik grubu veya milliyeti ifade ediyor. Bağlamda 'etnik grup' ve 'Türkiye Cumhuriyeti' geçiyor.
AKIL YÜRÜTME: Etnik veya dini grupları tanımlayan etiket 'NORP'dur. 'Türk' varlığı bu tanıma uymaktadır.
CEVAP: NORP

ÖRNEK 2 (LANGUAGE İÇİN):
VARLIK: Türkçe
BAĞLAM: Türkçenin ilk yazılı örnekleri Orhun Yazıtları'dır. Türk dilleri koluna ait bir dildir. Dünya çapında en çok konuşulan 20. dildir. (LANGUAGE tanımı ile eşleşir.)
GÖZLEM: 'Türkçe', bir iletişim aracı olan dili ifade ediyor. Bağlamda 'Türk dilleri koluna ait bir dildir' ifadesi geçiyor.
AKIL YÜRÜTME: İnsan dillerinin adlarını tanımlayan etiket 'LANGUAGE'dir. Varlık tanımıyla tam olarak eşleşmektedir.
CEVAP: LANGUAGE

ÖRNEK 3 (BOOK İÇİN):
VARLIK: Sinekli Bakkal
BAĞLAM: Halide Edib Adıvar'ın 1935'te yayınlanan ve popüler kültürde önemli yer tutan romanıdır. Eser, II. Dünya Savaşı öncesi İstanbul'u anlatır. (BOOK tanımı ile eşleşir.)
GÖZLEM: 'Sinekli Bakkal' yazar adı ve yayın tarihi ile birlikte bir 'roman' olarak anılıyor. Bağlamda 'romanıdır' ifadesi geçiyor.
AKIL YÜRÜTME: Yazılı veya basılı eserleri, romanları ve yayınları tanımlayan etiket 'BOOK'tur. Bu tanıma uymaktadır.
CEVAP: BOOK

ÖRNEK 4 (TITLE İÇİN):
VARLIK: General
BAĞLAM: Birçok orduda yüksek rütbeli bir subay unvanıdır. Türkiye'de en yüksek rütbelerden biridir. (TITLE tanımı ile eşleşir.)
GÖZLEM: 'General' bir rütbe, unvan veya pozisyon belirtiyor. Bağlamda 'yüksek rütbeli bir subay unvanı' ifadesi geçiyor.
AKIL YÜRÜTME: Kişinin unvanını, rütbesini veya pozisyonunu tanımlayan etiket 'TITLE'dır. 'General' bu tanıma uymaktadır.
CEVAP: TITLE
    """ 

    # --- Chain of Thought (CoT) Prompt Yapısı --- İyileştirilmiş ve daha kuralcı
    prompt = f"""Sen uzman bir veri sınıflandırma sistemisin.
Görevin, aşağıdaki varlığı ve bağlamı analiz ederek, **YALNIZCA** ETİKET TANIMLARI'ndan birini (veya uymuyorsa MISC'i) seçmektir.

AKIL YÜRÜTME ZİNCİRİNİ (Chain of Thought) takip ederek adım adım karar ver:
1. GÖZLEM: Varlık ve Wiki Bağlamı arasındaki anahtar eşleşmeleri (isim, tarih, unvan, tür, yayıncı vb.) listele.
2. AKIL YÜRÜTME: Bu gözlemlerin, **ETİKET TANIMLARI**'ndan hangisine **EN YAKIN** olduğunu gerekçesiyle açıkla.
3. KARAR: Yalnızca uygun olan ETİKET'i (veya MISC'i) ÇIKTI FORMATI'na uygun olarak belirt. **MISC'i sadece hiçbir tanıma uymuyorsa kullan.**

{few_shot_examples}

---
ETİKET TANIMLARI:{label_definitions_str}
---
GÖREV VARLIĞI:
VARLIK: {entity_text}
BAĞLAM (Wikipedia): {wiki_context}

ÇIKTI FORMATI:
GÖZLEM: [Tespit edilen anahtar özellikler]
AKIL YÜRÜTME: [Gözlemlerin hangi tanıma uyduğu ve neden]
CEVAP: [SEÇİLEN ETİKET]
"""
    messages = [{"role": "user", "content": prompt}]
    
    # LLM çağrısı
    outputs = gen_pipe(
        messages, 
        max_new_tokens=150, # CoT için token sayısını koruduk
        do_sample=False, 
        temperature=0.1
    )
    
    # LLM çıktısını analiz etme
    full_output = outputs[0]["generated_text"][-1]["content"].strip()
    final_label = "MISC"
    
    # CoT'yi log'a kaydetmek için parse edelim
    cog_output = ""
    
    try:
        # Cevap satırını bulur
        # Regex ile daha sağlam arama yapılabilir, ancak şimdilik listenin sonuncusunu alıyoruz.
        cevap_satiri = [line for line in full_output.split('\n') if 'CEVAP:' in line][-1]
        raw_label = cevap_satiri.split('CEVAP:')[-1].strip().upper()
        
        # GÖZLEM ve AKIL YÜRÜTME satırlarını bul
        gözlem = [line for line in full_output.split('\n') if 'GÖZLEM:' in line]
        akil_yurutme = [line for line in full_output.split('\n') if 'AKIL YÜRÜTME:' in line]

        if gözlem and akil_yurutme:
            # Sadece akıl yürütme kısmını log'a eklemek için alalım
            cog_output = akil_yurutme[-1].replace('AKIL YÜRÜTME:', '').strip()
            
        # Geçerli etiketleri kontrol et
        valid_labels = list(custom_label_definitions.keys()) # UNKNOWN'ı çıkarttık, zaten tanımlı değil.
        if raw_label in valid_labels:
            final_label = raw_label
            # CoT çıktısını etikete ekleyerek sonraki aşamada kullanmak için
            if cog_output:
                 return (final_label, cog_output) 
        
    except IndexError:
        # LLM çıktısı beklenmedik bir formattaysa MISC döner
        final_label = "MISC"
        
    return (final_label, cog_output) # (Etiket, CoT Gerekçesi)

# --- NER Pipeline Fonksiyonu (YALNIZCA 2 DEĞER ÜRETİYOR) ---
def advanced_ner_pipeline(text, target_labels, progress=gr.Progress()):
    """
    Standart NER modelini çalıştırır, MISC etiketli varlıklar için 
    Wikipedia ve LLM kullanarak zenginleştirme (RAG) yapar.
    """
    # İlk adımda, global başlangıç loglarını log_messages'a ekle.
    log_messages = list(_initial_logs) 
    
    try:
        initial_results = ner_pipe(text)
    except Exception as e:
        log_messages.append(f"❌ Hata: Temel NER modelinde sorun oluştu: {e}")
        yield log_messages, []
        return
        
    final_results = []
    total_entities = len(initial_results)
    
    log_messages.append(f"🕵️‍♀️ Toplam {total_entities} varlık inceleniyor...")
    
    # İlk yield (2 değer)
    # Bu, model yükleme loglarını Gradio arayüzüne ilk kez gönderir.
    yield log_messages, None 

    for i, entity in enumerate(initial_results):
        word = entity['word']
        label = entity['entity_group']
        
        # Gradio ilerleme çubuğunu güncelle
        current_progress = (i + 1) / total_entities
        # İlerlemeyi, işlenen varlık sayısıyla güncelleyin.
        progress(current_progress, desc=f"İşleniyor: {word}")

        result_obj = {
            "entity": word,
            "initial_label": label,
            "final_label": label,
            "source": "Model",
            "reasoning": "" # CoT gerekçesi için yeni alan
        }
        
        # Eğer etiket MISC ise RAG sürecini başlat
        if label == "MISC" and gen_pipe is not None:
            standardized_word = word.title()
            log_messages.append(f"   ⚠️ MISC tespit edildi: '{word}'. Wikipedia'ya soruluyor...")
            yield log_messages, None # Log mesajını gönder (2 değer)
            
            wiki_context = get_wiki_summary(standardized_word)
            
            if wiki_context:
                log_messages.append(f"   📄 Wiki Bağlamı bulundu: {wiki_context[:50]}...")
                yield log_messages, None # Log mesajını gönder (2 değer)
                
                # LLM'den hem etiketi hem de gerekçeyi al
                new_label, reasoning = refine_label_with_llm(standardized_word, wiki_context, target_labels)
                
                result_obj["final_label"] = new_label
                result_obj["source"] = "RAG+LLM (CoT)"
                result_obj["reasoning"] = reasoning # Gerekçeyi kaydet
                
                log_messages.append(f"   🔄 Etiket Güncellendi: MISC -> {new_label}")
                if reasoning:
                     log_messages.append(f"   💡 Akıl Yürütme: {reasoning[:70]}...")
                yield log_messages, None # Log mesajını gönder (2 değer)
            else:
                log_messages.append("   ❌ Wiki'de bilgi bulunamadı, MISC olarak kalıyor.")
                yield log_messages, None # Log mesajını gönder (2 değer)
        else:
            log_messages.append(f"   ✅ {word} için etiket: {label}")
            yield log_messages, None # Log mesajını gönder (2 değer)
            
        final_results.append(result_obj)
        
    log_messages.append("✅ İşlem tamamlandı! Nihai sonuçlar tabloda.")
    # Sonuçlar hazır olduğunda final_results'ı gönder
    yield log_messages, final_results # Nihai loglar ve sonuçlar (2 değer)

# --- Özel Etiket Tanımları (23 Etikete Genişletildi) ---
custom_label_definitions = {
    # Standart CoNLL-2003 etiketleri (LLM tarafından rafine edilmez, ancak sonuçta görünür)
    "PER": "Kişi adları, takma adlar, ünlüler.",
    "ORG": "Şirketler, kurumlar, hükümet kuruluşları.",
    "LOC": "Coğrafi yerler, siyasi bölgeler, binalar.",
    # MISC içinden rafine edilecek ince taneli etiketler (Toplam 19 adet)
    "DATE": "Mutlak veya göreceli tarih ifadeleri (yıl, ay, gün).",
    "TIME": "Günün saati, zaman aralığı.",
    "MONEY": "Parasal değerler, para birimleri.",
    "QUANTITY": "Ağırlık, uzunluk, hacim gibi ölçü birimleri ve sayısal değerler.",
    "PERCENT": "Yüzdelik ifadeler.",
    "NORP": "Milliyetler, etnik, dini veya politik gruplar.",
    "LAW": "Resmi kanun, yasa, yönetmelik veya hukuki belge adı.",
    "EVENT": "Savaşlar, festivaller, spor turnuvaları, doğal afetler veya kurumsal etkinlikler.",
    "BOOK": "Yazılı veya basılı bir eser, yayınlanmış roman, ders kitabı.",
    "MOVIE": "Sinema filmi, dizi, belgesel gibi görsel-işitsel yapıt.",
    "SONG": "Müzik eseri, şarkı, beste veya albüm adı.",
    "ART": "Resim, heykel, spesifik mimari eser adı (Örn: Mona Lisa).",
    "AWARD": "Ödül, madalya veya nişan adı (Örn: Nobel, Oscar).",
    "PRODUCT": "Ticari olarak satılan somut bir eşya, model, cihaz veya marka serisi (Örn: iPhone 15 Pro, Mercedes C200).",
    "SOFTWARE": "Bilgisayar programları, mobil uygulamalar, yapay zeka sistemleri, işletim sistemleri.",
    "ORG_SUB": "Şirket birimleri, üniversite fakülteleri, dernek şubeleri gibi büyük bir kurumun alt birimleri.",
    "LANGUAGE": "Dillerin adı (Örn: İngilizce, Arapça).",
    "TITLE": "Kişinin unvanı, rütbesi veya pozisyonu (Örn: Profesör, General, Başkan).",
    "CYBER": "URL, E-posta adresi, IP adresi, hashtag veya kullanıcı adı.",
    # MISC: Kalan her şey
    "MISC": "Diğer adlandırılmış varlıklar (LLM tarafından rafine edilemeyen veya uymayanlar)."
}

# --- Gradio Arayüzü Fonksiyonu (Düzeltilmiş ve Birleştirilmiş) ---
def process_ner_request(text, progress=gr.Progress()): 
    """
    Gradio arayüzünden çağrılan ana fonksiyondur. 
    advanced_ner_pipeline'dan gelen logları ve sonuçları HTML olarak biçimlendirir.
    """
    all_logs = []
    final_results = None

    for logs_step, results_step in advanced_ner_pipeline(text, custom_label_definitions, progress=progress):
        all_logs = logs_step # Her adımda güncel logları al
        
        if results_step is not None:
            final_results = results_step 

        # --- Geçici Log Çıktısı Oluşturma ---
        log_output_html = "<div style='max-height: 200px; overflow-y: scroll; border: 1px solid #eee; padding: 10px; margin-bottom: 10px; background-color: #f9f9f9; border-radius: 8px; font-family: monospace; font-size: 12px;'>"
        for log in all_logs:
            # Stil iyileştirmeleri
            color = 'blue' if '✅' in log else ('orange' if '⚠️' in log else ('red' if '❌' in log else 'black'))
            log_output_html += f"<p style='margin: 0; color: {color};'>{log.replace('   ', '&nbsp;&nbsp;&nbsp;')}</p>"
        log_output_html += "</div>"

        # --- Geçici/Nihai Sonuç Tablosu Oluşturma ---
        current_results_html = "" 
        
        if final_results: # Eğer sonuç listesi boş değilse tabloyu oluştur
            # Tailwind benzeri sınıflar kullanarak basit bir tablo oluştur
            current_results_html = """
            <style>
                .ner-table {
                    width:100%; 
                    border-collapse: collapse;
                    font-family: Arial, sans-serif;
                    border-radius: 8px;
                    overflow: hidden;
                    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
                }
                .ner-table th, .ner-table td {
                    padding: 12px 15px; 
                    text-align: left; 
                    border-bottom: 1px solid #ddd;
                }
                .ner-table th {
                    background-color: #3b82f6; /* Tailwind blue-500 */
                    color: white;
                    font-weight: bold;
                    text-transform: uppercase;
                }
                .ner-table tr:nth-child(even) {
                    background-color: #f3f4f6; /* Tailwind gray-100 */
                }
                .ner-table tr:hover {
                    background-color: #e5e7eb; /* Tailwind gray-200 */
                }
                .tooltip-text {
                    visibility: hidden;
                    background-color: #333;
                    color: #fff;
                    text-align: center;
                    border-radius: 6px;
                    padding: 5px 10px;
                    position: absolute;
                    z-index: 1;
                    bottom: 125%; /* Üstte göster */
                    left: 50%;
                    transform: translateX(-50%);
                    opacity: 0;
                    transition: opacity 0.3s;
                    width: 300px;
                    font-size: 11px;
                    line-height: 1.4;
                }
                .tooltip-container:hover .tooltip-text {
                    visibility: visible;
                    opacity: 1;
                }
                .tooltip-container {
                    position: relative;
                    display: inline-block;
                }
            </style>
            <table class='ner-table'>\n"""
            current_results_html += "  <tr>\n"
            current_results_html += "    <th>VARLIK</th>\n"
            current_results_html += "    <th>İLK ETİKET</th>\n"
            current_results_html += "    <th>RAG SONRASI ETİKETİ (GEREKÇE)</th>\n"
            current_results_html += "    <th>KAYNAK</th>\n"
            current_results_html += "  </tr>\n"
            for item in final_results:
                # Etiket renklendirmesi için basit bir mantık (23 etiket için rastgele renkler atandı)
                color_map = {
                    "PER": "background-color: #f8c291;", "LOC": "background-color: #a2c4c9;", "ORG": "background-color: #b3c99f;",
                    "MISC": "background-color: #fef08a;", 
                    "DATE": "background-color: #e5ccff;", "TIME": "background-color: #d1d5db;", "MONEY": "background-color: #fcd34d;",
                    "QUANTITY": "background-color: #bfdbfe;", "PERCENT": "background-color: #99f6e4;", "NORP": "background-color: #fbcfe8;",
                    "LAW": "background-color: #f0abfc;", "EVENT": "background-color: #a7f3d0;", "BOOK": "background-color: #ffedd5;",
                    "MOVIE": "background-color: #c7d2fe;", "SONG": "background-color: #e9d5ff;", "ART": "background-color: #bae6fd;",
                    "AWARD": "background-color: #fee2e2;", "PRODUCT": "background-color: #ffc999;", "SOFTWARE": "background-color: #d1fae5;",
                    "ORG_SUB": "background-color: #ccfbf1;", "LANGUAGE": "background-color: #fef9c3;", "TITLE": "background-color: #fecaca;",
                    "CYBER": "background-color: #dbeafe;"
                }
                final_label_style = color_map.get(item['final_label'], "")
                
                # CoT gerekçesini bir tooltip olarak ekleme
                reasoning_tooltip = ""
                if item.get("reasoning"):
                    # Gerekçe varsa tooltip'i oluştur
                    reasoning_tooltip = f"""
                        <div class='tooltip-container'>
                            {item['final_label']}
                            <span class='tooltip-text'>
                                CoT Akıl Yürütme: {item['reasoning']}
                            </span>
                        </div>
                    """
                else:
                    reasoning_tooltip = item['final_label']


                current_results_html += "  <tr>\n"
                current_results_html += f"    <td>{item['entity']}</td>\n"
                current_results_html += f"    <td>{item['initial_label']}</td>\n"
                current_results_html += f"    <td style='{final_label_style}'>{reasoning_tooltip}</td>\n"
                current_results_html += f"    <td>{item['source']}</td>\n"
                current_results_html += "  </tr>\n"
            current_results_html += "</table>"
        
        # Gradio'ya logları ve sonuç tablosunu gönder (her adımda güncelleyin)
        yield log_output_html, current_results_html 

# --- Gradio Arayüzünü Tanımla ---
iface = gr.Interface(
    fn=process_ner_request,
    inputs=gr.Textbox(lines=5, placeholder="Metin giriniz...", label="Giriş Metni"),
    # Çıktı olarak 2 ayrı HTML bileşeni döndürülmeli.
    outputs=[gr.HTML(label="İşlem Logları"), gr.HTML(label="Zenginleştirilmiş NER Sonuçları")], 
    title="Gelişmiş İnce Taneli NER (23 Etiket - RAG/LLM Destekli)",
    description="Metindeki varlıkları tanır ve MISC etiketlileri, 23 ince taneli kategoriye dönüştürmek için Wikipedia (RAG) ve LLM (Qwen2.5-1.5B) kullanılmaktadır. Etiket seti, akademik çalışmalardan esinlenerek 23 özel kategoriye genişletilmiştir.",
    examples=[ 
        # Yeni etiketleri test eden örnekler eklendi
        ["Milli Eğitim Bakanlığı'na bağlı Lise Birimleri, 2024 Türkiye Kupası etkinliğine katılacak ve %15 indirim uygulayacak."],
        ["General Vural, Türkçe dilini kullanan Türk askerlerini, https://example.com üzerinden uyardı. 'Hürriyet Kasidesi' eserini okudu."],
        ["Windows 11 işletim sistemi, 1000 TL karşılığında satışa sunulmuştur. 'Cumhurbaşkanı' unvanına sahip kişi, Kanun maddesini değiştirdi."],
    ],
    analytics_enabled=False
)

# --- Gradio Uygulamasını Başlat ---
if __name__ == "__main__":
    # Eğer port belirlenmemişse, varsayılan 7860 kullanılır.
    port = int(os.environ.get('PORT', 7860)) 
    iface.launch(share=False, server_port=port)