Spaces:
Runtime error
Runtime error
Upload app.py
Browse files
app.py
CHANGED
|
@@ -217,6 +217,7 @@ def ensure_db_ready():
|
|
| 217 |
time.sleep(1)
|
| 218 |
load_vectordb.clear()
|
| 219 |
load_all_chunks.clear()
|
|
|
|
| 220 |
st.rerun()
|
| 221 |
except Exception as e:
|
| 222 |
st.error(f"❌ فشل البناء: {e}")
|
|
@@ -285,8 +286,8 @@ def hybrid_search(query: str, k: int = TOP_K_SEARCH) -> tuple:
|
|
| 285 |
# Category-Aware Search (Soft Boosting)
|
| 286 |
qn_low = qn.lower()
|
| 287 |
target_cat = None
|
| 288 |
-
if any(
|
| 289 |
-
elif any(
|
| 290 |
|
| 291 |
results = []
|
| 292 |
for t in ranked:
|
|
@@ -310,21 +311,29 @@ def expand_query(q: str):
|
|
| 310 |
|
| 311 |
# Ordered: more specific triggers first (substring match on question)
|
| 312 |
PAGE_URL_TRIGGERS = [
|
| 313 |
-
|
| 314 |
-
(PAGE_URLS["
|
| 315 |
-
(PAGE_URLS["ar_concours_a2"],
|
| 316 |
-
(PAGE_URLS["
|
| 317 |
-
(PAGE_URLS["
|
| 318 |
-
|
| 319 |
-
(PAGE_URLS["
|
| 320 |
-
(PAGE_URLS["
|
| 321 |
-
(PAGE_URLS["
|
|
|
|
|
|
|
| 322 |
]
|
| 323 |
|
| 324 |
def get_best_url(question: str):
|
| 325 |
q = question.lower()
|
|
|
|
| 326 |
for url, keys in PAGE_URL_TRIGGERS:
|
| 327 |
if any(k in q for k in keys):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
return url
|
| 329 |
return None
|
| 330 |
|
|
@@ -353,7 +362,7 @@ def rerank_results(query: str, results: list):
|
|
| 353 |
raw = float(s)
|
| 354 |
conf = sigmoid(raw)
|
| 355 |
bonus = 0.05 if any(kw in r["content"] for kw in KWS) else 0.0
|
| 356 |
-
scored.append({**r, "raw_score": raw, "confidence": conf+bonus})
|
| 357 |
|
| 358 |
return sorted(scored, key=lambda x:x["confidence"], reverse=True)[:TOP_K_RERANK], t_ms
|
| 359 |
|
|
@@ -375,7 +384,9 @@ def build_conversation_summary(messages: list, max_chars: int = 1200) -> str:
|
|
| 375 |
parts = []
|
| 376 |
for m in messages[-8:]:
|
| 377 |
role = "user" if m.get("role") == "user" else "assistant"
|
| 378 |
-
|
|
|
|
|
|
|
| 379 |
summary = "\n".join(parts)
|
| 380 |
return summary[:max_chars]
|
| 381 |
|
|
|
|
| 217 |
time.sleep(1)
|
| 218 |
load_vectordb.clear()
|
| 219 |
load_all_chunks.clear()
|
| 220 |
+
load_bm25.clear()
|
| 221 |
st.rerun()
|
| 222 |
except Exception as e:
|
| 223 |
st.error(f"❌ فشل البناء: {e}")
|
|
|
|
| 286 |
# Category-Aware Search (Soft Boosting)
|
| 287 |
qn_low = qn.lower()
|
| 288 |
target_cat = None
|
| 289 |
+
if any(kw in qn_low for kw in ["مناظرة", "مناظرات", "concours"]): target_cat = "concours_ar" if detect_lang(qn)=="ar" else "concours_fr"
|
| 290 |
+
elif any(kw in qn_low for kw in ["تكوين", "تكوينات", "formation"]): target_cat = "formation_ar" if detect_lang(qn)=="ar" else "formation_fr"
|
| 291 |
|
| 292 |
results = []
|
| 293 |
for t in ranked:
|
|
|
|
| 311 |
|
| 312 |
# Ordered: more specific triggers first (substring match on question)
|
| 313 |
PAGE_URL_TRIGGERS = [
|
| 314 |
+
# Arabic triggers — specific Arabic keywords only
|
| 315 |
+
(PAGE_URLS["ar_concours_superieur"], ["المرحلة العليا", "مناظرة عليا", "concours-entree-cycle-superieur-ar"]),
|
| 316 |
+
(PAGE_URLS["ar_concours_a2"], ["أ2", "entree-au-cycle-de-formation-des-cadres-moyens-ar"]),
|
| 317 |
+
(PAGE_URLS["ar_concours_a3"], ["أ3", "agents-categorie-a3-ar"]),
|
| 318 |
+
(PAGE_URLS["ar_concours_general"], ["معلومات عامة", "informations-generales-ar"]),
|
| 319 |
+
# French triggers — specific French keywords only
|
| 320 |
+
(PAGE_URLS["concours_superieur"], ["cycle supérieur", "cycle-superieur", "le-concours-dentree-au-cycle-superieur"]),
|
| 321 |
+
(PAGE_URLS["concours_a2"], ["cadres-moyens-de-la-sous-categorie-a2", "sous-categorie a2"]),
|
| 322 |
+
(PAGE_URLS["concours_a3"], ["agents-de-la-sous-categorie-a3", "sous-categorie a3"]),
|
| 323 |
+
(PAGE_URLS["concours_general"], ["informations-generales"]),
|
| 324 |
+
(PAGE_URLS["formation_continue"], ["formation-continue-a-distance", "formation continue", "تكوين مستمر"]),
|
| 325 |
]
|
| 326 |
|
| 327 |
def get_best_url(question: str):
|
| 328 |
q = question.lower()
|
| 329 |
+
lang = detect_lang(question)
|
| 330 |
for url, keys in PAGE_URL_TRIGGERS:
|
| 331 |
if any(k in q for k in keys):
|
| 332 |
+
# If question is Arabic, skip French-only URLs and vice versa
|
| 333 |
+
if lang == "ar" and "/fr/" in url:
|
| 334 |
+
continue
|
| 335 |
+
if lang == "fr" and "/ar/" in url:
|
| 336 |
+
continue
|
| 337 |
return url
|
| 338 |
return None
|
| 339 |
|
|
|
|
| 362 |
raw = float(s)
|
| 363 |
conf = sigmoid(raw)
|
| 364 |
bonus = 0.05 if any(kw in r["content"] for kw in KWS) else 0.0
|
| 365 |
+
scored.append({**r, "raw_score": raw, "confidence": min(1.0, conf+bonus)})
|
| 366 |
|
| 367 |
return sorted(scored, key=lambda x:x["confidence"], reverse=True)[:TOP_K_RERANK], t_ms
|
| 368 |
|
|
|
|
| 384 |
parts = []
|
| 385 |
for m in messages[-8:]:
|
| 386 |
role = "user" if m.get("role") == "user" else "assistant"
|
| 387 |
+
# Strip HTML tags before summarizing
|
| 388 |
+
content = re.sub(r"<[^>]+>", "", m['content'])
|
| 389 |
+
parts.append(f"{role}: {content[:400]}")
|
| 390 |
summary = "\n".join(parts)
|
| 391 |
return summary[:max_chars]
|
| 392 |
|