|
|
from fastapi import FastAPI, HTTPException |
|
|
from pydantic import BaseModel |
|
|
import joblib |
|
|
import re |
|
|
from sentence_transformers import SentenceTransformer |
|
|
from threading import Lock |
|
|
import os |
|
|
import sys |
|
|
|
|
|
|
|
|
class MakaleBasligi(BaseModel): |
|
|
"""API'ye gelen JSON gövdesinin yapısını tanımlar ve veri doğrulamasını yapar.""" |
|
|
baslik: str |
|
|
|
|
|
|
|
|
app = FastAPI( |
|
|
title="Makale Kategorize Etme API", |
|
|
description="Hugging Face üzerinde barındırılan metin gömme ve SVC ile kategori tahmin hizmeti.", |
|
|
version="1.0.0" |
|
|
) |
|
|
|
|
|
|
|
|
MODEL_LOADED = False |
|
|
MODEL_LOCK = Lock() |
|
|
classifier = None |
|
|
embedding_model = None |
|
|
|
|
|
|
|
|
|
|
|
def metin_temizle(metin: str) -> str: |
|
|
"""Metni küçük harfe dönüştürür, noktalama işaretlerini ve sayıları kaldırır.""" |
|
|
if not isinstance(metin, str): |
|
|
return "" |
|
|
metin = metin.lower() |
|
|
metin = re.sub(r'[^\w\s]', '', metin) |
|
|
metin = re.sub(r'\d+', '', metin) |
|
|
return metin |
|
|
|
|
|
|
|
|
|
|
|
def load_models(): |
|
|
"""Modelleri ilk çalıştırmada yükler ve kilit mekanizması kullanır.""" |
|
|
global classifier, embedding_model, MODEL_LOADED |
|
|
|
|
|
with MODEL_LOCK: |
|
|
if MODEL_LOADED: |
|
|
return |
|
|
|
|
|
print("Modeller ilk kez yükleniyor...") |
|
|
try: |
|
|
|
|
|
classifier_path = 'siniflandirma_modeli.pkl' |
|
|
if not os.path.exists(classifier_path): |
|
|
print(f"HATA: '{classifier_path}' bulunamadı. Lütfen Spaces dosya yapısını kontrol edin.", file=sys.stderr) |
|
|
raise FileNotFoundError(f"Model dosyası '{classifier_path}' bulunamadı.") |
|
|
|
|
|
classifier = joblib.load(classifier_path) |
|
|
print("SVC modeli başarıyla yüklendi.") |
|
|
|
|
|
|
|
|
embedding_model = SentenceTransformer('distiluse-base-multilingual-cased-v2') |
|
|
print("Sentence-Transformer modeli başarıyla indirildi/yüklendi.") |
|
|
|
|
|
MODEL_LOADED = True |
|
|
print("Model yükleme tamamlandı. API hazır.") |
|
|
except Exception as e: |
|
|
|
|
|
print(f"KRİTİK HATA: Model yüklenirken bir sorun oluştu: {e}", file=sys.stderr) |
|
|
raise e |
|
|
|
|
|
|
|
|
|
|
|
@app.on_event("startup") |
|
|
async def startup_event(): |
|
|
"""Uygulama başladığında modelleri yükler.""" |
|
|
try: |
|
|
load_models() |
|
|
except Exception: |
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
@app.post('/kategorize-et') |
|
|
async def kategorize_et_api(data: MakaleBasligi): |
|
|
""" |
|
|
Gelen makale başlığını kategorize eder ve sonucu döndürür. |
|
|
|
|
|
Beklenen JSON gövdesi: {"baslik": "makale başlığı metni"} |
|
|
""" |
|
|
if not MODEL_LOADED: |
|
|
raise HTTPException(status_code=503, detail="Hizmet kullanılamıyor: Modeller henüz yüklenmedi veya yüklenirken hata oluştu.") |
|
|
|
|
|
try: |
|
|
makale_basligi = data.baslik |
|
|
|
|
|
|
|
|
if not makale_basligi or len(makale_basligi.strip()) == 0: |
|
|
raise HTTPException(status_code=400, detail="Makale başlığı boş veya geçersiz.") |
|
|
|
|
|
|
|
|
temizlenmis_baslik = metin_temizle(makale_basligi) |
|
|
|
|
|
|
|
|
tahmin_vektoru = embedding_model.encode([temizlenmis_baslik]) |
|
|
|
|
|
|
|
|
tahmin_sonucu = classifier.predict(tahmin_vektoru)[0] |
|
|
|
|
|
|
|
|
return {"kategori": tahmin_sonucu} |
|
|
|
|
|
except HTTPException as http_e: |
|
|
|
|
|
raise http_e |
|
|
except Exception as e: |
|
|
|
|
|
print(f"TAHMİN SIRASINDA BEKLENMEYEN HATA: {e}", file=sys.stderr) |
|
|
raise HTTPException(status_code=500, detail=f"Tahmin işlemi sırasında beklenmeyen bir hata oluştu: {e}") |