shahad altamimi commited on
Commit
f53136a
·
1 Parent(s): 1c48a5d
.gitignore CHANGED
@@ -225,3 +225,7 @@ __marimo__/
225
  **/.streamlit/secrets.toml
226
  # قاعدة بيانات SQLite المحلية للتجربة
227
  **/data/eval_local.db
 
 
 
 
 
225
  **/.streamlit/secrets.toml
226
  # قاعدة بيانات SQLite المحلية للتجربة
227
  **/data/eval_local.db
228
+
229
+ # النماذج المُكمَّمة (ONNX) والنسخة الاحتياطية للـ embeddings — كبيرة، تُبنى محليًا
230
+ **/models_onnx/
231
+ **/data/embeddings_backup_fp32/
README.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: القاموس العكسي للعربية
3
+ emoji: 🔍
4
+ colorFrom: green
5
+ colorTo: gray
6
+ sdk: streamlit
7
+ sdk_version: 1.40.0
8
+ app_file: reverse-dictionary/frontend/streamlit_app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # 🔍 القاموس العكسي للعربية — تقييم البحث الدلالي
13
+
14
+ تطبيق Streamlit يقارن 3 نماذج Embedding عربية: تُدخل تعريفًا، فتظهر أقرب
15
+ الكلمات في كل نموذج، ويقيّمها المستخدمون (صح/خطأ لكل كلمة)، وتُجمَّع
16
+ التقييمات في قاعدة Supabase.
17
+
18
+ - النماذج مُكمَّمة (ONNX int8) لتسريع التشغيل وتقليل الذاكرة على CPU.
19
+ - الملفات الكبيرة (النماذج + الـ embeddings) تُنزَّل تلقائيًا من Hugging Face Hub.
20
+
21
+ ## النشر
22
+ راجع [README_STREAMLIT_CLOUD.md](reverse-dictionary/README_STREAMLIT_CLOUD.md)
23
+ و [README_DEPLOY.md](reverse-dictionary/README_DEPLOY.md).
24
+
25
+ ## الأسرار المطلوبة (Space settings → Secrets)
26
+ - `HF_DATA_REPO` — مستودع HF الذي يحوي النماذج والـ embeddings.
27
+ - `DATABASE_URL` — رابط Supabase (Postgres pooler).
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # 📄 requirements.txt (جذر المستودع) — يقرأه Streamlit Cloud / HF Spaces.
3
+ # نسخة مطابقة لـ reverse-dictionary/backend/requirements.txt
4
+ # ============================================================
5
+
6
+ streamlit
7
+ python-dotenv
8
+ pandas
9
+ numpy
10
+ scikit-learn
11
+ sentence-transformers
12
+ torch
13
+ sqlalchemy
14
+ psycopg[binary]
15
+ huggingface_hub
reverse-dictionary/README_STREAMLIT_CLOUD.md ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 النشر على Streamlit Community Cloud (مجاني)
2
+
3
+ التطبيق ينشر برابط عام. الملفات الكبيرة (النماذج + الـ embeddings) تُخزَّن على
4
+ **Hugging Face Hub** (لأن GitHub المجاني يسمح بـ 1GB فقط)، والكود على **GitHub**،
5
+ والتقييمات في **Supabase**.
6
+
7
+ ```
8
+ GitHub (الكود) → Streamlit Cloud (يشغّل التطبيق)
9
+
10
+ ├─ ينزّل النماذج/الـ embeddings من Hugging Face Hub
11
+ └─ يحفظ التقييمات في Supabase
12
+ ```
13
+
14
+ ---
15
+
16
+ ## 1) رفع الملفات الكبيرة إلى Hugging Face Hub
17
+
18
+ 1. أنشئي حساب على <https://huggingface.co> (مجاني).
19
+ 2. أنشئي **Access Token**: الصورة الشخصية → Settings → Access Tokens → New token
20
+ (نوع **Write**). انسخيه.
21
+ 3. في الطرفية (من مجلد `reverse-dictionary/backend`):
22
+ ```bash
23
+ export HF_TOKEN=hf_xxxxxxxx # التوكن من الخطوة 2
24
+ export HF_DATA_REPO="USERNAME/reverse-dictionary-assets" # بدّلي USERNAME
25
+ .venv/bin/python scripts/upload_assets.py
26
+ ```
27
+ 4. بعد الرفع: افتحي المستودع على HF → **Settings** → اجعليه **Public**.
28
+
29
+ > هذا يرفع ~1.7GB مرة واحدة. المستودع العام لا يحتاج توكن عند التنزيل.
30
+
31
+ ---
32
+
33
+ ## 2) إعداد Supabase (لتخزين التقييمات)
34
+
35
+ اتبعي الخطوات في [README_DEPLOY.md](README_DEPLOY.md) قسم «إعداد Supabase»
36
+ للحصول على `DATABASE_URL` (نوع pooler، يبدأ بـ `postgresql://`).
37
+
38
+ ---
39
+
40
+ ## 3) رفع الكود على GitHub
41
+
42
+ من جذر المشروع (`semantic-search-system`):
43
+ ```bash
44
+ git add -A
45
+ git commit -m "تجهيز للنشر على Streamlit Cloud"
46
+ git branch -M main
47
+ git remote add origin https://github.com/USERNAME/REPO.git # بدّلي
48
+ git push -u origin main
49
+ ```
50
+ > الملفات الكبيرة مُستثناة من Git (في `.gitignore`) — لا تُرفع على GitHub.
51
+
52
+ ---
53
+
54
+ ## 4) النشر على Streamlit Cloud
55
+
56
+ 1. ادخلي <https://share.streamlit.io> وسجّلي دخول بحساب GitHub.
57
+ 2. **Create app** → **Deploy a public app from GitHub**.
58
+ 3. عبّئي:
59
+ - **Repository:** مستودعك على GitHub.
60
+ - **Branch:** `main`.
61
+ - **Main file path:** `reverse-dictionary/frontend/streamlit_app.py`
62
+ - **Advanced settings → Python version:** `3.11`
63
+ 4. في **Advanced settings → Secrets**، الصقي:
64
+ ```toml
65
+ HF_DATA_REPO = "USERNAME/reverse-dictionary-assets"
66
+ DATABASE_URL = "postgresql://postgres.xxxx:PASSWORD@...pooler.supabase.com:6543/postgres"
67
+ ```
68
+ 5. **Deploy!**
69
+
70
+ > أول تشغيل بطيء: يثبّت المكتبات ثم ينزّل ~1.7GB من HF Hub. الإقلاعات اللاحقة أسرع.
71
+
72
+ ---
73
+
74
+ ## 5) متابعة التقييمات
75
+
76
+ من لوحة **Supabase → Table Editor → ratings** تشوفين كل التقييمات وتصدّرينها CSV.
77
+
78
+ ---
79
+
80
+ ## ⚠️ ملاحظات
81
+ - **الذاكرة:** Streamlit Cloud المجاني محدود الذاكرة. خفّفنا الاستهلاك (نماذج
82
+ مُكمَّمة + `mmap` للـ embeddings). لو ظهر خطأ ذاكرة (الرسالة "Oh no"/إعادة تشغيل
83
+ متكررة)، الحل الأأمن هو **Hugging Face Spaces** (ذاكرته 16GB) بنفس هذي الملفات.
84
+ - **عند تحديث الكود:** ادفعي على GitHub، و Streamlit Cloud يعيد النشر تلقائيًا.
85
+ - **عند تحديث النماذج/الـ embeddings:** أعيدي تشغيل `upload_assets.py`، ثم
86
+ أعيدي تشغيل التطبيق من لوحة Streamlit Cloud (Reboot) لإعادة التنزيل.
reverse-dictionary/backend/app/assets.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # 📄 الملف: app/assets.py
3
+ # 🎯 الغرض: تنزيل الملفات الكبيرة تلقائيًا عند النشر (Streamlit Cloud).
4
+ #
5
+ # الملفات الكبيرة (النماذج المُكمَّمة + الـ embeddings + البيانات) لا تُرفع
6
+ # على GitHub (تتجاوز حد 1GB). نخزّنها في مستودع Hugging Face Hub عام،
7
+ # والتطبيق ينزّلها أول تشغيل فقط إن لم تكن موجودة محليًا.
8
+ #
9
+ # اسم مستودع Hugging Face يُقرأ من الأسرار: st.secrets["HF_DATA_REPO"]
10
+ # (مثال: "username/reverse-dictionary-assets"). المستودع عام فلا يحتاج توكن.
11
+ #
12
+ # محليًا (الملفات موجودة) لا يحدث أي تنزيل — الدالة ترجع فورًا.
13
+ # ============================================================
14
+
15
+ import os
16
+ from pathlib import Path
17
+
18
+ import streamlit as st
19
+
20
+ import config
21
+
22
+ # ما الذي نُنزّله (أنماط المسارات داخل مستودع HF):
23
+ # - data/embeddings/ : المتجهات المحفوظة (.npy)
24
+ # - data/processed/ : البيانات المنظّفة (data.csv)
25
+ # - models/ksaa_custom : النموذج الثالث المحلي (الأول والثاني يُنزَّلان تلقائيًا من HF)
26
+ DOWNLOAD_PATTERNS = [
27
+ "data/embeddings/**",
28
+ "data/processed/data.csv",
29
+ "models/ksaa_custom/**",
30
+ ]
31
+
32
+
33
+ def _repo_id() -> str | None:
34
+ """اسم مستودع HF الذي يحوي الملفات الكبيرة (من الأسرار أو البيئة)."""
35
+ try:
36
+ return st.secrets["HF_DATA_REPO"]
37
+ except Exception:
38
+ return os.getenv("HF_DATA_REPO")
39
+
40
+
41
+ def assets_ready() -> bool:
42
+ """هل كل الملفات الكبيرة موجودة محليًا؟"""
43
+ if not config.PROCESSED_DATA_PATH.exists():
44
+ return False
45
+ if not Path(config.MODEL_3_PATH).exists():
46
+ return False
47
+ for m in config.MODELS:
48
+ if not config.embedding_path(m["key"]).exists():
49
+ return False
50
+ return True
51
+
52
+
53
+ def ensure_assets() -> None:
54
+ """يُنزّل الملفات الكبيرة من Hugging Face Hub إن لم تكن موجودة."""
55
+ if assets_ready():
56
+ return
57
+
58
+ repo = _repo_id()
59
+ if not repo:
60
+ raise RuntimeError(
61
+ "الملفات الكبيرة (النماذج/الـ embeddings) غير موجودة، و HF_DATA_REPO "
62
+ "غير مضبوط في الأسرار. راجع README_STREAMLIT_CLOUD.md."
63
+ )
64
+
65
+ from huggingface_hub import snapshot_download
66
+
67
+ # تُنزَّل مباشرة إلى جذر المشروع بنفس البنية (data/، models/...)
68
+ snapshot_download(
69
+ repo_id=repo,
70
+ repo_type="model",
71
+ local_dir=str(config.BASE_DIR),
72
+ allow_patterns=DOWNLOAD_PATTERNS,
73
+ )
reverse-dictionary/backend/app/models_loader.py CHANGED
@@ -52,7 +52,9 @@ class ResourceManager:
52
  f"ملف الـ embeddings غير موجود: {path}\n"
53
  "شغّل أولًا: python scripts/build_embeddings.py"
54
  )
55
- self.embeddings[model_key] = np.load(path)
 
 
56
  return self.embeddings[model_key]
57
 
58
  # --------------------------------------------------------
 
52
  f"ملف الـ embeddings غير موجود: {path}\n"
53
  "شغّل أولًا: python scripts/build_embeddings.py"
54
  )
55
+ # mmap_mode="r": تبقى المصفوفة على القرص ولا تُحمّل كاملة في
56
+ # الذاكرة — يوفّر ~مئات الميغابايت (مهم للنشر على ذاكرة محدودة).
57
+ self.embeddings[model_key] = np.load(path, mmap_mode="r")
58
  return self.embeddings[model_key]
59
 
60
  # --------------------------------------------------------
reverse-dictionary/backend/requirements.txt CHANGED
@@ -13,3 +13,4 @@ sentence-transformers # نماذج الـ embedding + Cross-Encoder (reranker)
13
  torch # محرّك التعلّم العميق (يأتي مع sentence-transformers)
14
  sqlalchemy # التعامل مع قاعدة البيانات (Supabase / SQLite)
15
  psycopg[binary] # موصّل Postgres لـ Supabase
 
 
13
  torch # محرّك التعلّم العميق (يأتي مع sentence-transformers)
14
  sqlalchemy # التعامل مع قاعدة البيانات (Supabase / SQLite)
15
  psycopg[binary] # موصّل Postgres لـ Supabase
16
+ huggingface_hub # تنزيل الملفات الكبيرة من HF Hub عند النشر
reverse-dictionary/backend/scripts/upload_assets.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # 📄 الملف: scripts/upload_assets.py
3
+ # 🎯 الغرض: رفع الملفات الكبيرة إلى مستودع Hugging Face Hub (مرة واحدة).
4
+ #
5
+ # يرفع: data/embeddings/ (المتجهات) + data/processed/data.csv (البيانات)
6
+ # + models/ksaa_custom/ (النموذج الثالث المحلي).
7
+ # النموذجان الأول والثاني يُنزَّلان تلقائيًا من HuggingFace فلا يُرفعان.
8
+ # بعدها يقدر التطبيق المنشور ينزّل هذي الملفات تلقائيًا.
9
+ #
10
+ # قبل التشغيل:
11
+ # 1) سجّلي دخول HF مرة واحدة: huggingface-cli login
12
+ # (أو صدّري التوكن: export HF_TOKEN=hf_xxx)
13
+ # 2) حدّدي اسم المستودع: export HF_DATA_REPO="username/repo-name"
14
+ #
15
+ # ثم (من داخل مجلد backend):
16
+ # python scripts/upload_assets.py
17
+ # ============================================================
18
+
19
+ import os
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ sys.path.append(str(Path(__file__).resolve().parent.parent))
24
+ import config
25
+
26
+ from huggingface_hub import HfApi, create_repo
27
+
28
+
29
+ def main():
30
+ repo = os.getenv("HF_DATA_REPO")
31
+ if not repo:
32
+ raise SystemExit("❌ حدّدي المستودع أولًا: export HF_DATA_REPO=\"username/repo\"")
33
+
34
+ print(f"📦 المستودع: {repo}")
35
+ create_repo(repo, repo_type="model", exist_ok=True)
36
+ api = HfApi()
37
+
38
+ # 1) الـ embeddings
39
+ print("⏫ رفع data/embeddings/ ...")
40
+ api.upload_folder(folder_path=str(config.EMBEDDINGS_DIR),
41
+ path_in_repo="data/embeddings",
42
+ repo_id=repo, repo_type="model")
43
+
44
+ # 2) البيانات المنظّفة
45
+ print("⏫ رفع data/processed/data.csv ...")
46
+ api.upload_file(path_or_fileobj=str(config.PROCESSED_DATA_PATH),
47
+ path_in_repo="data/processed/data.csv",
48
+ repo_id=repo, repo_type="model")
49
+
50
+ # 3) النموذج الثالث المحلي (ksaa)
51
+ print("⏫ رفع models/ksaa_custom/ ...")
52
+ api.upload_folder(folder_path=str(config.MODEL_3_PATH),
53
+ path_in_repo="models/ksaa_custom",
54
+ repo_id=repo, repo_type="model")
55
+
56
+ print(f"✅ تم الرفع. اجعلي المستودع عامًّا (Public) من إعداداته على HF.")
57
+ print(f" ثم في أسرار Streamlit Cloud: HF_DATA_REPO = \"{repo}\"")
58
+
59
+
60
+ if __name__ == "__main__":
61
+ main()
reverse-dictionary/frontend/streamlit_app.py CHANGED
@@ -31,12 +31,26 @@ import streamlit as st
31
  import config
32
  from app.search import search_one_model
33
  from app import database as db
 
34
 
35
  # عدد الكلمات المعروضة لكل نموذج
36
  TOP_N = 5
37
 
38
  st.set_page_config(page_title="البحث الدلالي", layout="wide")
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  # ============================================================
41
  # 🎨 التصميم — لوحة هادئة: رمادي محايد + نعناعي
42
  # ============================================================
 
31
  import config
32
  from app.search import search_one_model
33
  from app import database as db
34
+ from app import assets
35
 
36
  # عدد الكلمات المعروضة لكل نموذج
37
  TOP_N = 5
38
 
39
  st.set_page_config(page_title="البحث الدلالي", layout="wide")
40
 
41
+
42
+ # ============================================================
43
+ # 📥 تجهيز الملفات الكبيرة (تنزيل من HF Hub أول تشغيل على السحابة)
44
+ # محليًا: الملفات موجودة فترجع فورًا بلا تنزيل.
45
+ # ============================================================
46
+ @st.cache_resource(show_spinner="⏳ تجهيز النماذج لأول مرة (تنزيل)… قد يأخذ دقائق")
47
+ def _bootstrap_assets():
48
+ assets.ensure_assets()
49
+ return True
50
+
51
+
52
+ _bootstrap_assets()
53
+
54
  # ============================================================
55
  # 🎨 التصميم — لوحة هادئة: رمادي محايد + نعناعي
56
  # ============================================================