Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -37,8 +37,9 @@ SUPABASE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJ
|
|
37 |
model = SentenceTransformer("sentence-transformers/paraphrase-MiniLM-L3-v2")
|
38 |
|
39 |
# ๐ Ambil FAQ berdasarkan UID
|
40 |
-
def get_faq_from_supabase(
|
41 |
-
|
|
|
42 |
headers = {
|
43 |
"apikey": SUPABASE_KEY,
|
44 |
"Authorization": f"Bearer {SUPABASE_KEY}",
|
@@ -47,10 +48,9 @@ def get_faq_from_supabase(uid):
|
|
47 |
try:
|
48 |
r = requests.get(url, headers=headers)
|
49 |
r.raise_for_status()
|
50 |
-
|
51 |
-
return [{"q": d["question"], "a": d["answer"]} for d in data]
|
52 |
except Exception as e:
|
53 |
-
print("โ
|
54 |
return []
|
55 |
|
56 |
# ๐ฎ Endpoint prediksi jawaban dari pertanyaan user
|
@@ -58,26 +58,94 @@ def get_faq_from_supabase(uid):
|
|
58 |
@limiter.limit("5/minute")
|
59 |
async def predict(request: Request):
|
60 |
body = await request.json()
|
61 |
-
|
62 |
|
63 |
-
if not
|
64 |
-
return {"data": ["
|
65 |
|
66 |
-
|
|
|
67 |
if not faqs:
|
68 |
-
return {"data": ["
|
69 |
|
70 |
-
|
71 |
-
|
|
|
72 |
|
73 |
-
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
-
|
77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
# ๐งน Hapus satu pesan berdasarkan ID
|
82 |
@app.post("/delete_chat")
|
83 |
async def delete_chat(request: Request):
|
|
|
37 |
model = SentenceTransformer("sentence-transformers/paraphrase-MiniLM-L3-v2")
|
38 |
|
39 |
# ๐ Ambil FAQ berdasarkan UID
|
40 |
+
def get_faq_from_supabase(admin_id):
|
41 |
+
"""Ambil FAQ khusus untuk admin tertentu"""
|
42 |
+
url = f"{SUPABASE_URL}/rest/v1/faq_items?admin_id=eq.{admin_id}"
|
43 |
headers = {
|
44 |
"apikey": SUPABASE_KEY,
|
45 |
"Authorization": f"Bearer {SUPABASE_KEY}",
|
|
|
48 |
try:
|
49 |
r = requests.get(url, headers=headers)
|
50 |
r.raise_for_status()
|
51 |
+
return r.json()
|
|
|
52 |
except Exception as e:
|
53 |
+
print(f"โ Error mengambil FAQ untuk admin {admin_id}:", e)
|
54 |
return []
|
55 |
|
56 |
# ๐ฎ Endpoint prediksi jawaban dari pertanyaan user
|
|
|
58 |
@limiter.limit("5/minute")
|
59 |
async def predict(request: Request):
|
60 |
body = await request.json()
|
61 |
+
admin_id, question = body.get("data", [None, None]) # admin_id dari UID di link
|
62 |
|
63 |
+
if not admin_id or not question:
|
64 |
+
return {"data": ["Admin ID atau pertanyaan tidak valid."]}
|
65 |
|
66 |
+
# Ambil FAQ khusus admin ini saja
|
67 |
+
faqs = get_faq_from_supabase(admin_id)
|
68 |
if not faqs:
|
69 |
+
return {"data": ["Maaf, belum ada FAQ yang tersedia."]}
|
70 |
|
71 |
+
# Proses pencarian jawaban
|
72 |
+
questions = [f["question"] for f in faqs]
|
73 |
+
answers = [f["answer"] for f in faqs]
|
74 |
|
75 |
+
try:
|
76 |
+
embeddings = model.encode(questions, convert_to_tensor=True)
|
77 |
+
query_embedding = model.encode(question, convert_to_tensor=True)
|
78 |
+
similarity = util.pytorch_cos_sim(query_embedding, embeddings)
|
79 |
+
best_idx = torch.argmax(similarity).item()
|
80 |
+
|
81 |
+
# Threshold similarity (minimal 0.5)
|
82 |
+
if similarity[0][best_idx] < 0.5:
|
83 |
+
return {"data": ["Maaf, saya tidak mengerti pertanyaan Anda."]}
|
84 |
+
|
85 |
+
return {"data": [answers[best_idx]]}
|
86 |
+
except Exception as e:
|
87 |
+
print("โ Error processing question:", e)
|
88 |
+
return {"data": ["Terjadi kesalahan saat memproses pertanyaan."]}
|
89 |
+
|
90 |
+
# Save chat
|
91 |
+
@app.post("/save_chat")
|
92 |
+
async def save_chat(request: Request):
|
93 |
+
body = await request.json()
|
94 |
+
admin_id = body.get("admin_id")
|
95 |
+
session_id = body.get("session_id")
|
96 |
+
is_bot = body.get("is_bot", False)
|
97 |
+
is_admin = body.get("is_admin", False)
|
98 |
+
message = body.get("message")
|
99 |
+
|
100 |
+
if not all([admin_id, session_id, message]):
|
101 |
+
return JSONResponse({"error": "Data tidak lengkap"}, status_code=400)
|
102 |
|
103 |
+
url = f"{SUPABASE_URL}/rest/v1/chat_logs"
|
104 |
+
headers = {
|
105 |
+
"apikey": SUPABASE_KEY,
|
106 |
+
"Authorization": f"Bearer {SUPABASE_KEY}",
|
107 |
+
"Content-Type": "application/json",
|
108 |
+
"Prefer": "return=representation"
|
109 |
+
}
|
110 |
+
payload = {
|
111 |
+
"admin_id": admin_id,
|
112 |
+
"session_id": session_id,
|
113 |
+
"is_bot": is_bot,
|
114 |
+
"is_admin": is_admin,
|
115 |
+
"message": message
|
116 |
+
}
|
117 |
|
118 |
+
try:
|
119 |
+
r = requests.post(url, json=payload, headers=headers)
|
120 |
+
r.raise_for_status()
|
121 |
+
return {"message": "Pesan berhasil disimpan", "id": r.json()[0]["id"]}
|
122 |
+
except Exception as e:
|
123 |
+
print("โ Gagal menyimpan pesan:", e)
|
124 |
+
return JSONResponse({"error": "Gagal menyimpan pesan"}, status_code=500)
|
125 |
+
|
126 |
+
# Chat history
|
127 |
+
@app.get("/chat_history")
|
128 |
+
async def get_chat_history(request: Request, admin_id: str, session_id: str):
|
129 |
+
"""Ambil history chat spesifik untuk sesi ini"""
|
130 |
+
url = f"{SUPABASE_URL}/rest/v1/chat_logs"
|
131 |
+
params = {
|
132 |
+
"admin_id": f"eq.{admin_id}",
|
133 |
+
"or": f"(session_id.eq.{session_id},is_bot.eq.true)",
|
134 |
+
"order": "created_at.asc"
|
135 |
+
}
|
136 |
+
headers = {
|
137 |
+
"apikey": SUPABASE_KEY,
|
138 |
+
"Authorization": f"Bearer {SUPABASE_KEY}"
|
139 |
+
}
|
140 |
|
141 |
+
try:
|
142 |
+
r = requests.get(url, params=params, headers=headers)
|
143 |
+
r.raise_for_status()
|
144 |
+
return r.json()
|
145 |
+
except Exception as e:
|
146 |
+
print(f"โ Error mengambil history untuk {session_id}:", e)
|
147 |
+
return JSONResponse({"error": "Gagal mengambil history"}, status_code=500)
|
148 |
+
|
149 |
# ๐งน Hapus satu pesan berdasarkan ID
|
150 |
@app.post("/delete_chat")
|
151 |
async def delete_chat(request: Request):
|