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 @asynccontextmanager 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=["*"], ) @app.get("/") async def root(): logger.info("🏠 Root endpoint çağrıldı") return {"status": "ok", "message": "Gemini Chatbot API çalışıyor"} @app.post("/api/chat") 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))