import os from fastapi import FastAPI from unsloth import FastLanguageModel from transformers import pipeline from pydantic import BaseModel from datetime import datetime app = FastAPI() model = None tokenizer = None pipe = None # === Log fonksiyonu def log(message): timestamp = datetime.now().strftime("%H:%M:%S") print(f"[{timestamp}] {message}", flush=True) # === System prompt (intent yapısı) SYSTEM_PROMPT = """ Siz bir görev tabanlı asistan botsunuz. Kullanıcının doğal dildeki mesajlarını anlayabilir, niyetlerini (intent) tespit edebilir, eksik bilgileri sorabilir ve backend API'lerine tetikleme hazırlığı yapabilirsiniz. ❗ Cevaplarınızda mutlaka aşağıdaki formatlı blokları döndürmelisiniz ve bunların dışında hiçbir metin, açıklama veya selamlama eklememelisiniz. ✅ Format: #ANSWER: #INTENT: (veya NONE) #PARAMS: {parametre_adı: değer, ...} #MISSING: [eksik_parametre_adı, ...] #ACTION_JSON: {api için gönderilecek json, eksikse boş bırak} ✅ Desteklenen intent'ler ve ACTION_JSON formatları: 1️⃣ doviz-kuru-intent → Döviz kuru sorgusu Parametreler: - currency (dolar, euro, TL) ACTION_JSON formatı: { "currency": "" } 2️⃣ yol-durumu-intent → Yol durumu sorgusu Parametreler: - from_location (Ankara, İstanbul, İzmir) - to_location (Ankara, İstanbul, İzmir) ACTION_JSON formatı: { "from_location": "", "to_location": "" } 3️⃣ hava-durumu-intent → Hava durumu sorgusu Parametreler: - city (Ankara, İstanbul, İzmir) ACTION_JSON formatı: { "city": "" } ❗ Eksik parametre varsa, sadece eksik olanları #MISSING listesine ekleyiniz ve #ACTION_JSON boş döndürünüz. ❗ Parametreler tamamsa, ilgili ACTION_JSON formatına uygun json hazırlayınız. ✅ Örnekler: Kullanıcı: "Dolar kuru nedir?" #ANSWER: NONE #INTENT: doviz-kuru-intent #PARAMS: {"currency": "dolar"} #MISSING: [] #ACTION_JSON: {"currency": "dolar"} Kullanıcı: "Yol durumu" #ANSWER: Lütfen from_location ve to_location bilgisini belirtir misiniz? #INTENT: yol-durumu-intent #PARAMS: {} #MISSING: ["from_location", "to_location"] #ACTION_JSON: {} ❗ Kullanıcıya hitap ederken formal bir dil kullanınız, sadece bu formatlı blokları döndürünüz. """ class ChatRequest(BaseModel): prompt: str @app.on_event("startup") def load_model(): global model, tokenizer, pipe # Ortam değişkenleri os.environ["HF_HOME"] = "/app/.cache" os.environ["HF_DATASETS_CACHE"] = "/app/.cache" os.environ["HF_HUB_CACHE"] = "/app/.cache" os.environ["TRITON_CACHE_DIR"] = "/tmp/.triton" model_name = "atasoglu/Turkish-Llama-3-8B-function-calling" hf_token = os.getenv("HF_TOKEN") log("🚀 Model yüklemesi başlatılıyor...") model, tokenizer = FastLanguageModel.from_pretrained( model_name=model_name, load_in_4bit=True, token=hf_token, cache_dir="/app/.cache" ) FastLanguageModel.for_inference(model) pipe = pipeline( "text-generation", model=model, tokenizer=tokenizer, device_map="auto" ) log("✅ Model başarıyla yüklendi ve cache’e alındı.") @app.post("/chat") def chat(req: ChatRequest): try: log(f"💬 Yeni istek alındı: '{req.prompt}'") full_prompt = f"{SYSTEM_PROMPT}\n\nKullanıcı: {req.prompt}\nAsistan:" log("🧠 LLM çağrısı başlatılıyor...") outputs = pipe( full_prompt, max_new_tokens=256, temperature=0.2, top_p=0.95, repetition_penalty=1.1 ) answer = outputs[0]["generated_text"].replace(full_prompt, "").strip() log("✅ LLM cevabı başarıyla alındı.") return {"response": answer} except Exception as e: log(f"❌ /chat sırasında hata oluştu: {e}") return {"error": f"Hata: {str(e)}"} @app.get("/") def health(): return {"status": "ok"}