|
import streamlit as st
|
|
import torch
|
|
from pinecone import Pinecone
|
|
from transformers import AutoTokenizer, AutoModel
|
|
import time
|
|
|
|
|
|
st.set_page_config(
|
|
page_title="Hukuki Döküman Arama (Metin Detay)",
|
|
page_icon="⚖️",
|
|
layout="wide",
|
|
initial_sidebar_state="expanded"
|
|
)
|
|
|
|
|
|
st.title("⚖️ Hukuki Döküman Semantik Arama Tam Metin")
|
|
st.markdown("Bu uygulama, 10.000 hukuki dökümanı içeren bir veritabanında semantik arama yapmanızı sağlar.")
|
|
|
|
|
|
@st.cache_resource
|
|
def initialize_pinecone():
|
|
pinecone_client = Pinecone(api_key="pcsk_5s8hcC_2zwJTQthP5PSWE992iXmbRx6ykNQbnEWLhj3fDuR1Cw9eKRn31i2zsRyyCxCmgW")
|
|
return pinecone_client.Index("etikos2")
|
|
|
|
|
|
@st.cache_resource
|
|
def load_model():
|
|
model_name = "intfloat/multilingual-e5-large"
|
|
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
|
model = AutoModel.from_pretrained(model_name)
|
|
|
|
|
|
device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
model = model.to(device)
|
|
|
|
return tokenizer, model, device
|
|
|
|
|
|
def get_query_embedding(query_text, tokenizer, model):
|
|
|
|
prefix = "query: "
|
|
query_text = prefix + query_text
|
|
|
|
|
|
inputs = tokenizer(
|
|
query_text,
|
|
padding=True,
|
|
truncation=True,
|
|
return_tensors="pt",
|
|
max_length=1024
|
|
).to(model.device)
|
|
|
|
|
|
with torch.no_grad():
|
|
model_output = model(**inputs)
|
|
|
|
|
|
attention_mask = inputs['attention_mask']
|
|
token_embeddings = model_output[0]
|
|
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
|
|
embeddings = torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
|
|
|
|
|
|
embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1)
|
|
|
|
|
|
embedding = embeddings[0].cpu().numpy().tolist()
|
|
return embedding
|
|
|
|
|
|
def get_text_preview(text, max_chars=1000):
|
|
if not text:
|
|
return "İçerik mevcut değil."
|
|
|
|
if len(text) <= max_chars:
|
|
return text
|
|
|
|
return text[:max_chars] + "..."
|
|
|
|
|
|
st.sidebar.header("Arama Ayarları")
|
|
top_k = st.sidebar.slider("Gösterilecek sonuç sayısı:", 1, 30, 5)
|
|
preview_length = st.sidebar.slider("Ön izleme uzunluğu (karakter):", 500, 3000, 1000)
|
|
|
|
|
|
with st.sidebar:
|
|
st.subheader("Sistem Durumu")
|
|
|
|
with st.status("Pinecone bağlantısı kuruluyor...", expanded=True) as status:
|
|
try:
|
|
index = initialize_pinecone()
|
|
status.update(label="Pinecone bağlantısı kuruldu ✅", state="complete", expanded=False)
|
|
except Exception as e:
|
|
status.update(label=f"Pinecone bağlantı hatası ❌: {str(e)}", state="error", expanded=True)
|
|
st.error("Veritabanına bağlanılamadı. Lütfen daha sonra tekrar deneyin.")
|
|
st.stop()
|
|
|
|
with st.status("Model yükleniyor...", expanded=True) as status:
|
|
try:
|
|
tokenizer, model, device = load_model()
|
|
status.update(label=f"Model yüklendi ✅ ({device.upper()} kullanılıyor)", state="complete", expanded=False)
|
|
except Exception as e:
|
|
status.update(label=f"Model yükleme hatası ❌: {str(e)}", state="error", expanded=True)
|
|
st.error("Model yüklenemedi. Lütfen daha sonra tekrar deneyin.")
|
|
st.stop()
|
|
|
|
|
|
query = st.text_area("Aramak istediğiniz konuyu yazın:", height=100,
|
|
placeholder="Örnek: Mülkiyet hakkı ile ilgili davalar")
|
|
|
|
|
|
search_button = st.button("🔍 Ara", type="primary", use_container_width=True)
|
|
|
|
|
|
if search_button and query:
|
|
with st.spinner("Arama yapılıyor..."):
|
|
try:
|
|
|
|
start_time = time.time()
|
|
query_embedding = get_query_embedding(query, tokenizer, model)
|
|
|
|
|
|
search_results = index.query(
|
|
vector=query_embedding,
|
|
top_k=top_k,
|
|
include_metadata=True
|
|
)
|
|
|
|
elapsed_time = time.time() - start_time
|
|
|
|
|
|
st.success(f"Arama tamamlandı! ({elapsed_time:.2f} saniye)")
|
|
|
|
if not search_results.matches:
|
|
st.info("Aramanıza uygun sonuç bulunamadı.")
|
|
else:
|
|
st.subheader(f"Arama Sonuçları ({len(search_results.matches)} döküman)")
|
|
|
|
|
|
for i, match in enumerate(search_results.matches):
|
|
with st.container():
|
|
col1, col2 = st.columns([4, 1])
|
|
|
|
with col1:
|
|
st.markdown(f"### {i+1}. {match.metadata.get('daire', 'Bilinmeyen Daire')}")
|
|
|
|
with col2:
|
|
st.metric(label="Benzerlik", value=f"{match.score*100:.1f}%")
|
|
|
|
st.markdown("**Döküman Bilgileri:**")
|
|
st.markdown(f"""
|
|
- **Karar No:** {match.metadata.get('karar_no', 'Belirtilmemiş')}
|
|
- **Esas No:** {match.metadata.get('esas_no', 'Belirtilmemiş')}
|
|
- **Tarih:** {match.metadata.get('tarih', 'Belirtilmemiş')}
|
|
""")
|
|
|
|
|
|
text_content = match.metadata.get('text', match.metadata.get('text_snippet', ''))
|
|
|
|
|
|
with st.expander("Döküman İçeriği", expanded=True):
|
|
st.markdown(get_text_preview(text_content, preview_length))
|
|
|
|
|
|
if text_content:
|
|
st.download_button(
|
|
label="Tam Metni İndir",
|
|
data=text_content,
|
|
file_name=f"karar_{match.metadata.get('karar_no', 'bilinmeyen')}.txt",
|
|
mime="text/plain"
|
|
)
|
|
|
|
st.divider()
|
|
|
|
except Exception as e:
|
|
st.error(f"Arama sırasında bir hata oluştu: {str(e)}")
|
|
|
|
|
|
st.sidebar.markdown("---")
|
|
st.sidebar.caption("© 2023 Hukuki Döküman Arama") |