shahad altamimi
جعل الواجهة والأصول ديناميكية (تدعم أي عدد نماذج) — تمهيد لإضافة نماذج
0704195
Raw
History Blame Contribute Delete
3.83 kB
# ============================================================
# 📄 الملف: app/assets.py
# 🎯 الغرض: تنزيل الملفات الكبيرة تلقائيًا عند النشر (Streamlit Cloud).
#
# الملفات الكبيرة (النماذج المُكمَّمة + الـ embeddings + البيانات) لا تُرفع
# على GitHub (تتجاوز حد 1GB). نخزّنها في مستودع Hugging Face Hub عام،
# والتطبيق ينزّلها أول تشغيل فقط إن لم تكن موجودة محليًا.
#
# اسم مستودع Hugging Face يُقرأ من الأسرار: st.secrets["HF_DATA_REPO"]
# (مثال: "username/reverse-dictionary-assets"). المستودع عام فلا يحتاج توكن.
#
# محليًا (الملفات موجودة) لا يحدث أي تنزيل — الدالة ترجع فورًا.
# ============================================================
import os
from pathlib import Path
import streamlit as st
import config
# ما الذي نُنزّله (أنماط المسارات داخل مستودع HF):
# - data/embeddings/ : المتجهات المحفوظة (.npy)
# - data/processed/ : البيانات المنظّفة (data.csv)
# - models/** : كل النماذج المحلية (الثالث وأي نموذج محلي إضافي).
# النماذج من HuggingFace (الأول/الثاني) تُنزَّل تلقائيًا.
DOWNLOAD_PATTERNS = [
"data/embeddings/**",
"data/processed/data.csv",
"models/**",
]
def _setting(key: str) -> str | None:
"""يقرأ إعدادًا من متغيّرات البيئة أولًا (HF Spaces)، ثم من st.secrets
فقط إن وُجد ملف secrets.toml فعلًا — لتفادي تحذير «No secrets found»."""
val = os.getenv(key)
if val:
return val
secret_paths = (
"/root/.streamlit/secrets.toml",
"/app/.streamlit/secrets.toml",
os.path.expanduser("~/.streamlit/secrets.toml"),
".streamlit/secrets.toml",
)
if any(os.path.exists(p) for p in secret_paths):
try:
return st.secrets[key]
except Exception:
return None
return None
def _repo_id() -> str | None:
"""اسم مستودع HF الذي يحوي الملفات الكبيرة."""
return _setting("HF_DATA_REPO")
def assets_ready() -> bool:
"""هل كل الملفات الكبيرة موجودة محليًا؟"""
if not config.PROCESSED_DATA_PATH.exists():
return False
for m in config.MODELS:
# ملف الـ embeddings مطلوب لكل نموذج
if not config.embedding_path(m["key"]).exists():
return False
# النماذج المحلية (مسار مطلق) لازم يكون مجلدها موجودًا.
# نماذج HuggingFace (مثل "org/name") تُنزَّل تلقائيًا فلا نفحصها.
if os.path.isabs(m["hf_id"]) and not Path(m["hf_id"]).exists():
return False
return True
def ensure_assets() -> None:
"""يُنزّل الملفات الكبيرة من Hugging Face Hub إن لم تكن موجودة."""
if assets_ready():
return
repo = _repo_id()
if not repo:
raise RuntimeError(
"الملفات الكبيرة (النماذج/الـ embeddings) غير موجودة، و HF_DATA_REPO "
"غير مضبوط في الأسرار. راجع DEPLOY.md."
)
from huggingface_hub import snapshot_download
# تُنزَّل مباشرة إلى جذر المشروع بنفس البنية (data/، models/...)
snapshot_download(
repo_id=repo,
repo_type="model",
local_dir=str(config.BASE_DIR),
allow_patterns=DOWNLOAD_PATTERNS,
)