File size: 6,715 Bytes
3e66fd0
 
7460785
 
2f0b1a7
8ab22e1
3e66fd0
 
7460785
 
 
3e66fd0
d2ee4b2
 
 
 
 
 
 
47ff742
 
 
 
3e66fd0
d2ee4b2
2f0b1a7
41098c3
 
 
 
 
 
 
104b35b
2f0b1a7
104b35b
2f0b1a7
ec0184d
2f0b1a7
 
 
 
104b35b
eeb69f8
104b35b
eeb69f8
 
d2ee4b2
104b35b
 
 
 
 
eeb69f8
 
 
d2ee4b2
104b35b
 
 
 
 
 
eeb69f8
 
d2ee4b2
104b35b
 
 
 
eeb69f8
41098c3
 
eeb69f8
 
 
 
 
 
41098c3
eeb69f8
 
 
 
 
 
41098c3
eeb69f8
 
 
 
 
 
 
 
41098c3
eeb69f8
 
 
 
 
 
41098c3
eeb69f8
 
 
 
 
 
41098c3
eeb69f8
 
 
 
41098c3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eeb69f8
 
 
2f0b1a7
 
 
 
 
3e66fd0
 
7460785
47ff742
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3e66fd0
2f0b1a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a0585f
 
 
 
 
 
 
 
ec0184d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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

# === Ortam değişkenlerini ayarla (fallback)
os.environ.setdefault("HF_HOME", "/app/.cache")
os.environ.setdefault("HF_DATASETS_CACHE", "/app/.cache")
os.environ.setdefault("HF_HUB_CACHE", "/app/.cache")
os.environ.setdefault("TRITON_CACHE_DIR", "/tmp/.triton")
os.environ.setdefault("TORCHINDUCTOR_CACHE_DIR", "/tmp/torchinductor_cache")

# === Log fonksiyonu
def log(message):
    timestamp = datetime.now().strftime("%H:%M:%S")
    print(f"[{timestamp}] {message}", flush=True)

# === System prompt (intent yapısı ve ACTION_JSON formatları)
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.

Eğer kullanıcının söylediğinden bir intent yakalarsanız, ama intent için gerekli parametre söylenmemişse, parametre değerini kullanıcıya sorun.
Kullanıcı parametre değerini söylerse bununla birlikte intent bilgisini ve oluşan ACTION_JSON'ı döndürün. Fakat kullanıcı eksik parametrenin
değerini söylemek yerine konuyu değiştirir ve farklı bir şey söylerse, ve eğer söylediğinde farklı bir intent varsa, bu intent akışını işletmelisiniz. 
Eğer intent yoksa kullanıcı konuyu değiştirip normal sohbet ediyor anlamına gelir, sohbeti kullanıcıya cevap vererek sürdürünüz.

❗ 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: <cevap metni veya NONE>
#INTENT: <intent_adı> (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:
1️⃣ doviz-kuru-intent → Döviz kuru sorgusu
Parametre:
- currency: döviz cinsi (örn. dolar, euro, TL)
ACTION_JSON:
{
  "currency": "<currency>"
}

2️⃣ yol-durumu-intent → Yol durumu sorgusu
Parametreler:
- from_location: başlangıç noktası (herhangi bir şehir)
- to_location: varış noktası (herhangi bir şehir)
ACTION_JSON:
{
  "from_location": "<from_location>",
  "to_location": "<to_location>"
}

3️⃣ hava-durumu-intent → Hava durumu sorgusu
Parametre:
- city: herhangi bir şehir
ACTION_JSON:
{
  "city": "<city>"
}

✅ Kurallar:
- Eğer intent bulunamazsa, kullanıcının sohbet amaçlı söylediği ifadeye cevap vermelisiniz:
#ANSWER: <cevap metni>
#INTENT: NONE
#PARAMS: {}
#MISSING: []
#ACTION_JSON: {}

- Eğer intent bulunur ama eksik parametre varsa:
#ANSWER: NONE
#INTENT: <intent_adı>
#PARAMS: {elde edilen parametreler}
#MISSING: [eksik_parametreler]
#ACTION_JSON: {}

- Eğer tüm parametreler tamamlanmışsa:
#ANSWER: NONE
#INTENT: <intent_adı>
#PARAMS: {tüm parametreler}
#MISSING: []
#ACTION_JSON: {tamamlanmış json}

✅ Örnekler:
Kullanıcı: "Dolar kuru nedir?"
#ANSWER: NONE
#INTENT: "doviz-kuru-intent"
#PARAMS: {"currency": "dolar"}
#MISSING: []
#ACTION_JSON: {"currency": "dolar"}

Kullanıcı: "Ankara'dan Çorum'a yol durumu nasıl?"
#ANSWER: NONE
#INTENT: "yol-durumu-intent"
#PARAMS: {"from_location": "Ankara", "to_location": "Çorum"}
#MISSING: []
#ACTION_JSON: {"from_location": "Ankara", "to_location": "Çorum"}

Kullanıcı: "Bolu'da hava nasıl?"
#ANSWER: NONE
#INTENT: "hava-durumu-intent"
#PARAMS: {"city": "Bolu"}
#MISSING: []
#ACTION_JSON: {"city": "Bolu"}

✅ Yol Durumu Eksik Parametre Tamamlama Akışı:
Kullanıcı: "Yol durumu"
#ANSWER: "Nereden nereye gitmek istiyorsunuz?"
#INTENT: "yol-durumu-intent"
#PARAMS: {}
#MISSING: ["from_location", "to_location"]
#ACTION_JSON: {}

Kullanıcı: "Ankara'dan İstanbul'a"
#ANSWER: NONE
#INTENT: "yol-durumu-intent"
#PARAMS: { "from_location": "Ankara", "to_location": "İstanbul"}
#MISSING: []
#ACTION_JSON: { "from_location": "Ankara", "to_location": "İstanbul" }

✅ Hava Durumu Eksik Parametre Tamamlama Akışı:
Kullanıcı: "Hava nasıl olacak, yağmur var mı?"
#ANSWER: "Hangi şehirdeki hava durumunu öğrenmek istiyorsunuz?"
#INTENT: hava-durumu-intent
#PARAMS: {}
#MISSING: ["city"]
#ACTION_JSON: {}

Kullanıcı: "İstanbul'u öğrenmek istiyorum"
#ANSWER: NONE
#INTENT: hava-durumu-intent
#PARAMS: { "city": "İstanbul" }
#MISSING: []
#ACTION_JSON: { "city": "İstanbul" }

✅ Eksik Parametre Tamamlama Akışında Kullanıcının Konuyu Değiştirmesi:
Kullanıcı: "Hava nasıl olacak, yağmur var mı?"
#ANSWER: "Hangi şehirdeki hava durumunu öğrenmek istiyorsunuz?"
#INTENT: hava-durumu-intent
#PARAMS: {}
#MISSING: ["city"]
#ACTION_JSON: {}

Kullanıcı: "Sence en şekerli meyve hangisidir?"
#ANSWER: "En şekerli meyve hurmadır."
#INTENT: NONE
#PARAMS: {}
#MISSING: []
#ACTION_JSON: {}

❗ Eksik parametre varsa mutlaka #MISSING listesine ekleyin ve #ACTION_JSON'ı boş bırakın.
❗ Parametreler tamamsa #ACTION_JSON eksiksiz doldurulmuş olmalı.
❗ Cevaplarda kesinlikle bu formatın dışına çıkmayın.

"""

class ChatRequest(BaseModel):
    prompt: str

@app.on_event("startup")
def load_model():
    global model, tokenizer, pipe
    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"}