|
import streamlit as st |
|
import torch |
|
from pinecone import Pinecone |
|
from transformers import AutoTokenizer, AutoModel |
|
import time |
|
import requests |
|
import json |
|
|
|
|
|
st.set_page_config( |
|
page_title="Hukuki Döküman Arama (Detaylı Özet)", |
|
page_icon="⚖️", |
|
layout="wide", |
|
initial_sidebar_state="expanded" |
|
) |
|
|
|
|
|
st.title("⚖️ Hukuki Döküman Semantik Arama Detaylı Özet") |
|
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] + "..." |
|
|
|
|
|
def process_with_dify(query): |
|
|
|
dify_api_key = "app-0UV1vRHHnChGssQ2Kc5UK9gg" |
|
dify_api_endpoint = "https://api.dify.ai/v1/chat-messages" |
|
|
|
headers = { |
|
"Authorization": f"Bearer {dify_api_key}", |
|
"Content-Type": "application/json" |
|
} |
|
|
|
payload = { |
|
"inputs": {}, |
|
"query": f"{query}", |
|
"response_mode": "blocking", |
|
"user": "user" |
|
} |
|
|
|
try: |
|
response = requests.post(dify_api_endpoint, headers=headers, json=payload) |
|
if response.status_code == 200: |
|
data = response.json() |
|
return data.get("answer", "") |
|
else: |
|
st.warning(f"Dify AI ile iletişim kurulurken hata oluştu: {response.status_code}") |
|
return "" |
|
except Exception as e: |
|
st.warning(f"Dify AI işlemi sırasında hata: {str(e)}") |
|
return "" |
|
|
|
|
|
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("Sorgunuz Dify AI ile analiz ediliyor..."): |
|
dify_output = process_with_dify(query) |
|
|
|
if not dify_output: |
|
st.error("Dify AI'dan yanıt alınamadı. Lütfen tekrar deneyin.") |
|
st.stop() |
|
|
|
|
|
with st.expander("Sorgu Dönüşümü", expanded=True): |
|
st.write("Orijinal sorgu:") |
|
st.info(query) |
|
st.write("Dify AI çıktısı (arama için kullanılacak):") |
|
st.success(dify_output) |
|
|
|
|
|
with st.spinner("Arama yapılıyor..."): |
|
try: |
|
|
|
start_time = time.time() |
|
query_embedding = get_query_embedding(dify_output, 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("©2025 Etikos AI") |