Spaces:
Sleeping
Sleeping
| import os | |
| import logging | |
| import json | |
| from typing import List, Dict | |
| from fastapi import FastAPI, Request, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from contextlib import asynccontextmanager | |
| from langchain_core.prompts import ChatPromptTemplate | |
| from langchain_core.documents import Document | |
| from langchain_core.output_parsers import StrOutputParser | |
| from langchain_core.runnables import RunnablePassthrough | |
| from langchain_text_splitters import RecursiveCharacterTextSplitter | |
| from langchain_community.vectorstores import Chroma | |
| from langchain_community.embeddings import HuggingFaceEmbeddings | |
| from langchain_google_genai import ChatGoogleGenerativeAI | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") | |
| logger = logging.getLogger(__name__) | |
| GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") | |
| # ========================================================= | |
| # GEMINI RAG CHATBOT | |
| # ========================================================= | |
| class GeminiRAGChatbotCPU: | |
| def __init__(self, model_variant="gemini-2.0-flash-lite"): | |
| self.model_variant = model_variant | |
| self.device = "cpu" | |
| self.vectordb = None | |
| self.rag_chain = None | |
| self.llm = None | |
| self.embeddings = None | |
| self.retriever = None | |
| logger.info(f"🤖 GeminiRAGChatbotCPU başlatıldı - Model: {model_variant}") | |
| def gemini_yukle(self): | |
| logger.info("📡 Gemini API yükleniyor...") | |
| self.llm = ChatGoogleGenerativeAI( | |
| model=self.model_variant, | |
| google_api_key=GEMINI_API_KEY, | |
| temperature=0.2, | |
| max_output_tokens=200, | |
| ) | |
| logger.info("✅ Gemini API yüklendi") | |
| return self.llm | |
| def embedding_yukle(self): | |
| model = "emrecan/bert-base-turkish-cased-mean-nli-stsb-tr" | |
| logger.info(f"📚 Embedding modeli yükleniyor: {model}") | |
| self.embeddings = HuggingFaceEmbeddings( | |
| model_name=model, | |
| model_kwargs={'device': self.device}, | |
| encode_kwargs={'normalize_embeddings': True} | |
| ) | |
| logger.info("✅ Embedding modeli yüklendi") | |
| return self.embeddings | |
| def dokumanlari_yukle(self, klasor_yolu: str): | |
| logger.info(f"📁 Dokümanlar yükleniyor: {klasor_yolu}") | |
| if not os.path.exists(klasor_yolu): | |
| logger.warning(f"⚠️ Klasör bulunamadı, oluşturuluyor: {klasor_yolu}") | |
| os.makedirs(klasor_yolu) | |
| return [] | |
| documents = [] | |
| file_count = 0 | |
| for root, _, files in os.walk(klasor_yolu): | |
| for f in files: | |
| path = os.path.join(root, f) | |
| if f.endswith(".txt"): | |
| try: | |
| with open(path, "r", encoding="utf-8") as t: | |
| text = t.read() | |
| documents.append(Document(page_content=text, metadata={"source": f})) | |
| file_count += 1 | |
| logger.info(f" ✓ Yüklendi: {f} ({len(text)} karakter)") | |
| except Exception as e: | |
| logger.error(f" ✗ Yüklenemedi: {f} - Hata: {e}") | |
| logger.info(f"✅ Toplam {file_count} doküman yüklendi") | |
| if file_count == 0: | |
| logger.warning("⚠️ Hiç doküman bulunamadı! Varsayılan içerik ekleniyor...") | |
| documents = [Document( | |
| page_content="Global AI Hub, yapay zeka eğitimleri sunan bir platformdur.", | |
| metadata={"source": "default.txt"} | |
| )] | |
| return documents | |
| def metni_parcala(self, documents: List): | |
| logger.info(f"✂️ Dokümanlar parçalanıyor ({len(documents)} doküman)...") | |
| text_splitter = RecursiveCharacterTextSplitter( | |
| chunk_size=450, | |
| chunk_overlap=50, | |
| length_function=len, | |
| separators=["\n\n", "\n", ".", "!", "?", ";", ":", " ", ""] | |
| ) | |
| chunks = text_splitter.split_documents(documents) | |
| logger.info(f"✅ {len(chunks)} chunk oluşturuldu") | |
| return chunks | |
| def vektor_db_olustur(self, chunks: List, db_yolu): | |
| logger.info(f"🗄️ Vector DB oluşturuluyor: {db_yolu}") | |
| if not chunks or len(chunks) == 0: | |
| logger.error("❌ HATA: Chunks listesi boş!") | |
| raise ValueError("Chunks listesi boş, vector DB oluşturulamıyor!") | |
| if os.path.exists(db_yolu): | |
| logger.info(f" ⚠️ Mevcut DB siliniyor: {db_yolu}") | |
| import shutil | |
| shutil.rmtree(db_yolu) | |
| logger.info(f" 📊 {len(chunks)} chunk ile DB oluşturuluyor...") | |
| self.vectordb = Chroma.from_documents(chunks, self.embeddings, persist_directory=db_yolu) | |
| logger.info("✅ Vector DB oluşturuldu") | |
| return self.vectordb | |
| def format_docs(self, docs): | |
| return "\n\n".join(doc.page_content for doc in docs) | |
| def chatbot_olustur(self, k=5): | |
| logger.info(f"💬 Chatbot oluşturuluyor (k={k})...") | |
| self.retriever = self.vectordb.as_retriever(search_type="similarity", search_kwargs={"k": k}) | |
| template = """Sen Gemini, Türkçe konuşan bir süpermarket asistanısın. | |
| Bağlam: | |
| {context} | |
| Soru: {question} | |
| Kurallar: | |
| - Sadece bağlamdaki bilgilerle cevap ver | |
| - Kısa ve net ol | |
| - Bilgi yoksa "Bu konuda bilgim yok" de | |
| Cevap:""" | |
| prompt = ChatPromptTemplate.from_template(template) | |
| self.rag_chain = ( | |
| {"context": self.retriever | self.format_docs, "question": RunnablePassthrough()} | |
| | prompt | |
| | self.llm | |
| | StrOutputParser() | |
| ) | |
| logger.info("✅ Chatbot oluşturuldu") | |
| return self.rag_chain | |
| def soru_sor(self, soru: str): | |
| logger.info(f"❓ Soru alındı: {soru[:50]}...") | |
| try: | |
| cevap = self.rag_chain.invoke(soru) | |
| logger.info(f"💬 Cevap üretildi: {cevap[:50]}...") | |
| return {"cevap": cevap.strip()} | |
| except Exception as e: | |
| logger.error(f"❌ Cevap üretme hatası: {e}") | |
| raise | |
| def setup(self, init=True, db_yolu="./chroma_db", dokuman_klasoru="./documents", k=5): | |
| logger.info("=" * 60) | |
| logger.info("🚀 SETUP BAŞLATILIYOR") | |
| logger.info("=" * 60) | |
| try: | |
| self.embedding_yukle() | |
| self.gemini_yukle() | |
| if init: | |
| docs = self.dokumanlari_yukle(dokuman_klasoru) | |
| chunks = self.metni_parcala(docs) | |
| self.vektor_db_olustur(chunks, db_yolu) | |
| self.chatbot_olustur(k) | |
| logger.info("=" * 60) | |
| logger.info("✅ SETUP TAMAMLANDI") | |
| logger.info("=" * 60) | |
| except Exception as e: | |
| logger.error("=" * 60) | |
| logger.error(f"❌ SETUP HATASI: {e}") | |
| logger.error("=" * 60) | |
| import traceback | |
| traceback.print_exc() | |
| raise | |
| # ========================================================= | |
| # FASTAPI APP | |
| # ========================================================= | |
| gemini = None | |
| async def lifespan(app: FastAPI): | |
| global gemini | |
| logger.info("🌟 FastAPI Uygulama Başlatılıyor...") | |
| try: | |
| gemini = GeminiRAGChatbotCPU() | |
| gemini.setup(init=True) | |
| logger.info("🎉 Uygulama başarıyla başlatıldı!") | |
| except Exception as e: | |
| logger.error(f"💥 Uygulama başlatma hatası: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| yield | |
| logger.info("👋 Uygulama kapatılıyor...") | |
| app = FastAPI(lifespan=lifespan, title="Gemini Chatbot API") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| async def root(): | |
| logger.info("🏠 Root endpoint çağrıldı") | |
| return {"status": "ok", "message": "Gemini Chatbot API çalışıyor"} | |
| async def chat(request: Request): | |
| try: | |
| data = await request.json() | |
| soru = data.get("message", "") | |
| logger.info(f"📨 API çağrısı alındı - Message: {soru[:50]}...") | |
| if not soru: | |
| logger.warning("⚠️ Boş mesaj gönderildi") | |
| raise HTTPException(status_code=400, detail="Message required") | |
| sonuc = gemini.soru_sor(soru) | |
| logger.info(f"✅ Başarılı cevap döndürüldü") | |
| return sonuc | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"❌ API hatası: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| raise HTTPException(status_code=500, detail=str(e)) |