Hothaifa commited on
Commit
bc12a37
·
verified ·
1 Parent(s): 092743e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +12 -41
app.py CHANGED
@@ -33,14 +33,7 @@ if GEMINI_API_KEY:
33
  except Exception as e:
34
  print(f"[ERROR] Failed to init Gemini Client: {e}")
35
 
36
- # ---------------------------------------------------------
37
- # 2. تعريف التطبيق
38
- # ---------------------------------------------------------
39
  app = FastAPI(title="Hajeen Islamic QA API")
40
-
41
- # ---------------------------------------------------------
42
- # 3. دوال مساعدة عامة
43
- # ---------------------------------------------------------
44
  DISCLAIMERS = {
45
  "ar": "",
46
  "de": "\n\n(Hinweis: Automatisch übersetzt. Konsultieren Sie das arabische Original.)",
@@ -126,9 +119,7 @@ def check_rate_limit(ip: str):
126
  raise HTTPException(status_code=429, detail="Rate limit exceeded.")
127
  dq.append(now)
128
 
129
- # ---------------------------------------------------------
130
- # 4. إعدادات النماذج والبيانات
131
- # ---------------------------------------------------------
132
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
133
 
134
  DATA_FILE_ID = "1GMG6fVxhUuBEAHP91c8RAUdUJh5TxY5O"
@@ -154,14 +145,10 @@ def safe_download(file_id, output_path):
154
  gdown.download(id=file_id, output=output_path, quiet=False)
155
  except Exception as e:
156
  print(f"[DOWNLOAD ERROR] {e}")
157
-
158
- # تهيئة النماذج
159
  model_name = 'aubmindlab/bert-base-arabertv2'
160
  tokenizer = AutoTokenizer.from_pretrained(model_name)
161
  model = AutoModel.from_pretrained(model_name).to(device)
162
  vectorizer = TfidfVectorizer(analyzer="char_wb", ngram_range=(3,5), min_df=1)
163
-
164
- # متغيرات جلوبال
165
  df_main = pd.DataFrame()
166
  df_learned = pd.DataFrame()
167
  df_all = pd.DataFrame()
@@ -180,14 +167,10 @@ def load_hadith_corpora(paths: dict) -> pd.DataFrame:
180
  df["matn_clean"] = df["matn_full"].fillna("").apply(normalize_ar)
181
  df["source"] = src
182
  df["hadith_number"] = df["hadith_number"].astype(str).str.replace(r"\D","",regex=True)
183
-
184
- # --- FIX: Ensure grading is string and no NaNs ---
185
  if "grading" not in df.columns:
186
  df["grading"] = ""
187
  else:
188
  df["grading"] = df["grading"].fillna("").astype(str)
189
- # -------------------------------------------------
190
-
191
  all_dfs.append(df[["source","hadith_number","matn_full","matn_clean","grading"]])
192
  except Exception as e:
193
  print(f"Error loading {src}: {e}")
@@ -206,8 +189,6 @@ async def startup_event():
206
  safe_download(ID_BUKHARI, PATHS["bukhari"])
207
  safe_download(ID_MUSLIM, PATHS["muslim"])
208
  safe_download(ID_MUSNAD, PATHS["musnad"])
209
-
210
- # تحميل الفتاوى
211
  if os.path.exists(data_path):
212
  df_main = pd.read_csv(data_path)
213
 
@@ -221,8 +202,6 @@ async def startup_event():
221
  question_embeddings = np.load(embeddings_path)
222
  index = faiss.IndexFlatL2(question_embeddings.shape[1])
223
  index.add(question_embeddings.astype('float32'))
224
-
225
- # تحميل الأحاديث
226
  df_all = load_hadith_corpora(PATHS)
227
  if not df_all.empty:
228
  tfidf_matrix = vectorizer.fit_transform(df_all["matn_clean"])
@@ -238,9 +217,6 @@ def get_embedding_for_query(text: str):
238
  outputs = model(**inputs)
239
  return outputs.last_hidden_state[:, 0, :].cpu().numpy()
240
 
241
- # ---------------------------------------------------------
242
- # 5. وظيفة Gemini للبحث
243
- # ---------------------------------------------------------
244
  def ask_gemini_with_search(query: str, lang: str = "ar"):
245
  if not gemini_client: return None
246
  model_id = "gemini-2.0-flash"
@@ -250,11 +226,17 @@ def ask_gemini_with_search(query: str, lang: str = "ar"):
250
  مهمتك: الإجابة على الأسئلة الشرعية والفتاوى بدقة بناءً على نتائج البحث الموثوقة.
251
 
252
  تعليمات هامة جداً:
253
- . أبدأ أجاباتك دائما بـ (الحمدلله والصلاة والسلام على رسول الله أما بعد :) واختم جوابك بـ (والله أعلم)
 
 
 
 
 
 
254
  . لغة الإجابة: المستخدم يسأل بلغة الكود ({lang}). يجب أن تكون إجابتك بالكامل بهذه اللغة ({lang}). لا تجب بالعربية إذا كان السؤال بغيرها.
255
  . استخدم "بحث Google" دائماً للتأكد من المعلومات من مصادر مثل (إسلام ويب، الإسلام سؤال وجواب، ابن باز).
256
  . الاختصار المفيد: لا تكثر من الحشو، وأعط الزبدة مع الدليل.
257
- . اذكر المصادر في نهاية إجابتك.
258
  """
259
 
260
  try:
@@ -281,10 +263,6 @@ def ask_gemini_with_search(query: str, lang: str = "ar"):
281
  print(f"[GEMINI ERROR] {e}")
282
  return None
283
 
284
- # ---------------------------------------------------------
285
- # 6. API Endpoints
286
- # ---------------------------------------------------------
287
-
288
  class SearchRequest(BaseModel):
289
  query: str
290
  top_k: int = 1
@@ -315,8 +293,6 @@ def search(request: SearchRequest):
315
  if guard:
316
  error_msg = translate_error_detail("عذراً، السؤال غير مناسب.", target_lang)
317
  raise HTTPException(status_code=400, detail=error_msg)
318
-
319
- # 1. التعلم الذاتي
320
  if not df_learned.empty:
321
  row = df_learned[df_learned["question"] == q]
322
  if not row.empty:
@@ -330,7 +306,7 @@ def search(request: SearchRequest):
330
  "score": 100
331
  }]}
332
 
333
- # 2. البحث المحلي (FAISS)
334
  is_arabic_query = any("\u0600" <= c <= "\u06FF" for c in q)
335
  if is_arabic_query and index is not None:
336
  query_emb = get_embedding_for_query(q)
@@ -350,7 +326,7 @@ def search(request: SearchRequest):
350
  "score": int(similarity)
351
  }]}
352
 
353
- # 3. Gemini Search
354
  print(f"[INFO] Asking Gemini: {q} (Lang: {target_lang})")
355
  gemini_answer = ask_gemini_with_search(q, lang=target_lang)
356
 
@@ -385,7 +361,7 @@ def feedback(req: FeedbackRequest):
385
  pd.DataFrame([req.dict()]).to_csv(FEEDBACK_FILE, mode='a', header=False, index=False)
386
  return {"message": "تم حفظ التقييم."}
387
 
388
- # --- Hadith Logic ---
389
  SOURCE_ALIAS = {
390
  "bukhari": "صحيح البخاري", "muslim": "صحيح مسلم", "musnad": "مسند أحمد",
391
  "صحيح البخاري": "صحيح البخاري", "صحيح مسلم": "صحيح مسلم", "مسند أحمد": "مسند أحمد"
@@ -472,13 +448,8 @@ def hadith_search(req: HadithSearchRequest, request: Request):
472
  base_score = sims[i] * 100
473
  fuzz_score = fuzz.token_set_ratio(q_norm, row["matn_clean"])
474
  source_bonus = 15 if row["source"] == "صحيح البخاري" else (10 if row["source"] == "صحيح مسلم" else 0)
475
-
476
- # --- FIX: Safe grading check for NaNs ---
477
- # Ensure grading value is converted to string before check
478
  grading_val = str(row.get("grading", "") or "")
479
  grading_bonus = 5 if "صحيح" in grading_val else 0
480
- # ----------------------------------------
481
-
482
  final_score = base_score * 0.5 + fuzz_score * 0.5 + source_bonus + grading_bonus
483
  candidates.append((row, final_score))
484
 
 
33
  except Exception as e:
34
  print(f"[ERROR] Failed to init Gemini Client: {e}")
35
 
 
 
 
36
  app = FastAPI(title="Hajeen Islamic QA API")
 
 
 
 
37
  DISCLAIMERS = {
38
  "ar": "",
39
  "de": "\n\n(Hinweis: Automatisch übersetzt. Konsultieren Sie das arabische Original.)",
 
119
  raise HTTPException(status_code=429, detail="Rate limit exceeded.")
120
  dq.append(now)
121
 
122
+
 
 
123
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
124
 
125
  DATA_FILE_ID = "1GMG6fVxhUuBEAHP91c8RAUdUJh5TxY5O"
 
145
  gdown.download(id=file_id, output=output_path, quiet=False)
146
  except Exception as e:
147
  print(f"[DOWNLOAD ERROR] {e}")
 
 
148
  model_name = 'aubmindlab/bert-base-arabertv2'
149
  tokenizer = AutoTokenizer.from_pretrained(model_name)
150
  model = AutoModel.from_pretrained(model_name).to(device)
151
  vectorizer = TfidfVectorizer(analyzer="char_wb", ngram_range=(3,5), min_df=1)
 
 
152
  df_main = pd.DataFrame()
153
  df_learned = pd.DataFrame()
154
  df_all = pd.DataFrame()
 
167
  df["matn_clean"] = df["matn_full"].fillna("").apply(normalize_ar)
168
  df["source"] = src
169
  df["hadith_number"] = df["hadith_number"].astype(str).str.replace(r"\D","",regex=True)
 
 
170
  if "grading" not in df.columns:
171
  df["grading"] = ""
172
  else:
173
  df["grading"] = df["grading"].fillna("").astype(str)
 
 
174
  all_dfs.append(df[["source","hadith_number","matn_full","matn_clean","grading"]])
175
  except Exception as e:
176
  print(f"Error loading {src}: {e}")
 
189
  safe_download(ID_BUKHARI, PATHS["bukhari"])
190
  safe_download(ID_MUSLIM, PATHS["muslim"])
191
  safe_download(ID_MUSNAD, PATHS["musnad"])
 
 
192
  if os.path.exists(data_path):
193
  df_main = pd.read_csv(data_path)
194
 
 
202
  question_embeddings = np.load(embeddings_path)
203
  index = faiss.IndexFlatL2(question_embeddings.shape[1])
204
  index.add(question_embeddings.astype('float32'))
 
 
205
  df_all = load_hadith_corpora(PATHS)
206
  if not df_all.empty:
207
  tfidf_matrix = vectorizer.fit_transform(df_all["matn_clean"])
 
217
  outputs = model(**inputs)
218
  return outputs.last_hidden_state[:, 0, :].cpu().numpy()
219
 
 
 
 
220
  def ask_gemini_with_search(query: str, lang: str = "ar"):
221
  if not gemini_client: return None
222
  model_id = "gemini-2.0-flash"
 
226
  مهمتك: الإجابة على الأسئلة الشرعية والفتاوى بدقة بناءً على نتائج البحث الموثوقة.
227
 
228
  تعليمات هامة جداً:
229
+ . أنت مختص في الفتاوي والأسألة الشرعية لا تدخل في أمور أخرى، إذا كان السؤال خارج نطاق الشريعة او أنه لا يبدو ك فتوى او سؤال ديني أو أستفسار ديني لا تجيب مثال إذا كان السؤال (ما هي عاصمة سوريا، كم سعر الدولار، هل أنت نموذج لغوي، هل يمكنك تعديل كود،) أعتذر بأدب وقول (أنا نموذج لغوي تعلمت على الأجابة على الأسألة الشرعية والدينية من مصادر أهل السنة والجماعة، أما سؤالك فيمكنك العثور على أجابة له في Google أو من Gemini أو أنماط أخرى).
230
+ . أذا تم الأستفسار عن أمر ديني أجب وأحرص على الشرح أذا كان أستفسار وليس سؤال، أما أذا سؤال لا تكثر من الحشو وأعط الزبدة مع الحفاظ على جمالية الجواب.
231
+ . المستخدم ليس دائماً على حق، ربما يسهو أو يخطأ، أذا اخطأ صحح له أتباعاً لمنهج أهل السنة والجماعة فقط، وحاول مساعدته في العثور على أجابة (دينية فقط) أما دون ذلك أجب بأعتذار كما ذكرت سابقاً.
232
+ . أحرص على عدم الأجابة على اسألة سياسية او علمية.
233
+ . أذا تم فتح نقاش معك قل أنا لا أصلح للنقاشات (أنا فقط أبحث وأجيب على اسألة فتوى او أستفسار) وشكراً
234
+ . ميولك يجب أن تكون لمنهج أهل السنة والجماعة فقط أما دون ذلك أعتذر بأدب وقل هداكم الله.
235
+ . أبدأ أجاباتك دائما بـ (الحمدلله والصلاة والسلام على رسول الله أما بعد : واختم جوابك بـ (والله أعلم).
236
  . لغة الإجابة: المستخدم يسأل بلغة الكود ({lang}). يجب أن تكون إجابتك بالكامل بهذه اللغة ({lang}). لا تجب بالعربية إذا كان السؤال بغيرها.
237
  . استخدم "بحث Google" دائماً للتأكد من المعلومات من مصادر مثل (إسلام ويب، الإسلام سؤال وجواب، ابن باز).
238
  . الاختصار المفيد: لا تكثر من الحشو، وأعط الزبدة مع الدليل.
239
+ . اذكر دائماً أن مصادرك هي محرك بحث Google وبالتحديد موقعين أسلام ويب وإسلام سؤال وجواب .
240
  """
241
 
242
  try:
 
263
  print(f"[GEMINI ERROR] {e}")
264
  return None
265
 
 
 
 
 
266
  class SearchRequest(BaseModel):
267
  query: str
268
  top_k: int = 1
 
293
  if guard:
294
  error_msg = translate_error_detail("عذراً، السؤال غير مناسب.", target_lang)
295
  raise HTTPException(status_code=400, detail=error_msg)
 
 
296
  if not df_learned.empty:
297
  row = df_learned[df_learned["question"] == q]
298
  if not row.empty:
 
306
  "score": 100
307
  }]}
308
 
309
+
310
  is_arabic_query = any("\u0600" <= c <= "\u06FF" for c in q)
311
  if is_arabic_query and index is not None:
312
  query_emb = get_embedding_for_query(q)
 
326
  "score": int(similarity)
327
  }]}
328
 
329
+
330
  print(f"[INFO] Asking Gemini: {q} (Lang: {target_lang})")
331
  gemini_answer = ask_gemini_with_search(q, lang=target_lang)
332
 
 
361
  pd.DataFrame([req.dict()]).to_csv(FEEDBACK_FILE, mode='a', header=False, index=False)
362
  return {"message": "تم حفظ التقييم."}
363
 
364
+
365
  SOURCE_ALIAS = {
366
  "bukhari": "صحيح البخاري", "muslim": "صحيح مسلم", "musnad": "مسند أحمد",
367
  "صحيح البخاري": "صحيح البخاري", "صحيح مسلم": "صحيح مسلم", "مسند أحمد": "مسند أحمد"
 
448
  base_score = sims[i] * 100
449
  fuzz_score = fuzz.token_set_ratio(q_norm, row["matn_clean"])
450
  source_bonus = 15 if row["source"] == "صحيح البخاري" else (10 if row["source"] == "صحيح مسلم" else 0)
 
 
 
451
  grading_val = str(row.get("grading", "") or "")
452
  grading_bonus = 5 if "صحيح" in grading_val else 0
 
 
453
  final_score = base_score * 0.5 + fuzz_score * 0.5 + source_bonus + grading_bonus
454
  candidates.append((row, final_score))
455