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 # Pydantic: Gelen JSON verisini doğrulamak için model class MakaleBasligi(BaseModel): """API'ye gelen JSON gövdesinin yapısını tanımlar ve veri doğrulamasını yapar.""" baslik: str # FastAPI uygulamasını başlat 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 yükleme işlemini sadece bir kez yapmak için global değişkenler MODEL_LOADED = False MODEL_LOCK = Lock() classifier = None embedding_model = None # Metin ön işleme fonksiyonu 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 # Modeli yükleme fonksiyonu (Sadece bir kez çalışacak) 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: # 1. SVC sınıflandırma modelini yükle 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.") # 2. Gömme modelini yükle 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: # Model yükleme hatası olursa, uygulamanın çökmesi ve Spaces'in yeniden başlatması beklenir print(f"KRİTİK HATA: Model yüklenirken bir sorun oluştu: {e}", file=sys.stderr) raise e # Uygulama başlatıldığında modelleri yükle @app.on_event("startup") async def startup_event(): """Uygulama başladığında modelleri yükler.""" try: load_models() except Exception: # Hata fırlatılmazsa uygulama başlatılamaz. pass # API'nin makale başlığını alıp kategoriyi tahmin edeceği uç nokta (endpoint) @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 # Basit bir kontrol ekleyelim 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) # Başlığı vektöre dönüştür tahmin_vektoru = embedding_model.encode([temizlenmis_baslik]) # Sınıflandırma modelini kullanarak kategoriyi tahmin et tahmin_sonucu = classifier.predict(tahmin_vektoru)[0] # Sonucu JSON formatında döndür return {"kategori": tahmin_sonucu} except HTTPException as http_e: # Pydantic veya kendi fırlattığımız HTTP hatalarını döndür raise http_e except Exception as e: # Tahmin sırasında oluşan diğer hatalar 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}")