itsdevruba/Medad

TypeScript

0

25 commits

updated Sep 25, 2026

See the code

README

مداد — Medad

منصة تعليمية تفاعلية تحوّل دروس الأطفال إلى قصص ومغامرات، أحداثها في مناطق المملكة العربية السعودية.

🔗 المنصة: https://medad-five.vercel.app

في ميزة "موضوع خاص"، الطفل يكتب عنوان أي درس ويختار صفه الدراسي، فيكتب له نموذج علام (ALLaM-7B) المدرّب على داتا مداد قصة تعليمية جديدة. القصة تنعرض مقسّمة أجزاء، وبينها أسئلة فهم قصيرة، ومعه البطل اللي اختاره. وفي النهاية تطلع له نفس شاشة النتيجة حقت القصص الجاهزة.


المحتويات


المميزات

  • قصص جاهزة تفاعلية، مثل دورة الماء والسلاسل الغذائية، فيها أسئلة واختيارات ونقاط وإنجازات.
  • موضوع خاص بالذكاء الاصطناعي: قصة جديدة لأي درس يكتبه الطفل، يولّدها نموذج علام المدرّب، وبين أجزائها سؤال أو سؤالين اختيار من متعدد.
  • شاشة نتيجة موحّدة: القصص الجاهزة و"موضوع خاص" تنتهي بنفس الشاشة (نسبة، نجوم، إنجازات)، وتنحفظ في لوحة التقدم.
  • أربعة أبطال مرافقين (ملك، فهد، سالم، دانة). البطل اللي يختاره الطفل يرحّب فيه ويرافقه خلال القصة.
  • لمسة سعودية: أحداث القصص في مدن ومناطق سعودية حقيقية.

طريقة العمل

الطفل يكتب عنوان الدرس + الصف
          │
          ▼
واجهة React (Vite)  ──POST /generate──►  سيرفر FastAPI على Modal (GPU L4)
          ▲                                       │
          │                                       ▼
          │                         نموذج علام المدرّب (merged, float16)
          │                                       │
          │                                       ▼
          │                         تنظيف النص + تقسيمه لأجزاء
          │                                       │
          │                                       ▼
          │                  توليد أسئلة الفهم (نفس النموذج، prompt ثاني)
          │                                       │
          └──── Story JSON (أجزاء + أسئلة) ◄──────┘
  1. الواجهة ترسل { lesson, grade } للسيرفر.
  2. السيرفر يبني الطلب بنفس الـ system prompt وإعدادات التوليد المستخدمة في نوتبوك الاستدلال (midad-allam-merged-inference).
  3. ينظّف النص من الرموز الزايدة، ويقسّمه من جزأين إلى خمسة أجزاء، ويستخرج المدينة المذكورة في القصة.
  4. يولّد سؤال اختيار من متعدد (٣ خيارات) بعد جزء أو جزئين، بـ prompt مختلف. إذا رد النموذج بصيغة غلط، السؤال ينشال والقصة تكمل بدونه. الخيارات تنخلط عشوائياً.
  5. يرجع كائن Story بنفس الهيكل اللي تستخدمه القصص الجاهزة في src/data/stories.ts، فالتطبيق يعرضه على طول.

النقاط: المجموع ٤٠٠ مثل القصص الجاهزة. كل سؤال صحيح = ١٠٠ نقطة (والغلط ١٠)، والباقي يتوزع على أجزاء القصة. يعني كل الأسئلة صح = ١٠٠٪، وكلها غلط ≈ ٥٥٪.

للتحكم في الأسئلة من server/modal_app.py: ENABLE_QUESTIONS = False يطفيها، و MAX_QUESTIONS يحدد عددها. كل سؤال يزيد تقريباً ١٠–١٥ ثانية على وقت التوليد.

السيرفر يشتغل لحاله وقت الطلب، ويطفي نفسه بعد ٥ دقائق بدون استخدام. أول طلب بعد فترة توقف ياخذ تقريباً دقيقة إلى دقيقتين عشان يحمّل النموذج، وبعدها كل قصة تاخذ تقريباً ٢٠ إلى ٤٠ ثانية. التطبيق يرسل طلب "تصحية" للسيرفر أول ما يفتح، عشان يكون جاهز غالباً لما يوصل الطفل لـ"موضوع خاص".

هيكل المشروع

Medad-main/
├── server/
│   └── modal_app.py            # سيرفر علام على Modal (تنزيل النموذج + الـ API)
├── src/
│   ├── api/
│   │   └── storyApi.ts         # طلب القصة من السيرفر + تصحية السيرفر
│   ├── assets/characters/      # صور الأبطال الأربعة (Git LFS)
│   ├── components/
│   │   ├── GeneratingScreen.tsx     # شاشة انتظار توليد القصة (مع حالة الخطأ وإعادة المحاولة)
│   │   ├── AdventureScreen.tsx      # شاشة القصة والأسئلة (لقصص علام: البطل المرافق)
│   │   ├── CompanionSpeech.tsx      # فقاعة كلام البطل المرافق
│   │   ├── ResultsScreen.tsx        # شاشة النتيجة (لكل القصص)
│   │   └── ...
│   ├── data/
│   │   ├── stories.ts          # القصص الجاهزة + هيكل Story
│   │   └── characters.ts       # بيانات الأبطال
│   └── App.tsx
├── AI-work/                    # داتا التدريب ونوتبوك التدريب
├── docs/                       # التقرير (المنهجية) ومخطط سير العمل والبوستر
├── legacy/                     # نسخ قديمة ما عاد تُستخدم (سيرفر Kaggle + tunnel، نوتبوك قديم)
├── .env.example                # مثال لرابط السيرفر
└── README.md

تشغيل الواجهة

المتطلبات: Node.js 18 أو أحدث.

npm install

سوّوا ملف .env في مجلد المشروع الرئيسي، فيه رابط السيرفر. تقدرون تنسخون .env.example:

VITE_API_URL=https://<workspace>--medad-medad-web.modal.run

رابط السيرفر الحالي عند صاحبة البرانش. اطلبوه منها، لأن ملف .env ما يترفع على GitHub.

npm run dev

بعدها افتحوا http://localhost:8443.

تشغيل السيرفر على Modal

هذا القسم تحتاجونه بس إذا بتنشرون السيرفر من حسابكم. إذا بتجربون الواجهة بس، استخدموا الرابط الموجود.

المتطلبات: Python 3.10 أو أحدث، وحساب على Modal مضاف له بطاقة. التكلفة تنخصم من الرصيد المجاني، ويُفضّل تحطون حد صرف من إعدادات Billing.

pip install modal
python -m modal setup

١. احفظوا مفتاح Kaggle في Modal. تجيبونه من kaggle.com: Settings ثم API ثم Create New Token.

python -m modal secret create medad-secrets KAGGLE_USERNAME=... KAGGLE_KEY=...

الحساب لازم يقدر يوصل للنموذج lama377/midad-allam-7b على Kaggle.

٢. نزّلوا النموذج. هذي الخطوة مرة وحدة بس، والنموذج تقريباً 13GB.

cd server
python -m modal run modal_app.py::download_model

٣. انشروا السيرفر:

python -m modal deploy modal_app.py

الرابط اللي يطلع لكم هو قيمة VITE_API_URL.

مستخدمي Windows: إذا طلع خطأ ترميز بسبب الحروف العربية، شغّلوا هذا الأمر قبل أوامر Modal:

$env:PYTHONIOENCODING="utf-8"

الـ API

الطريقةالمسارالوصف
GET/healthفحص إن السيرفر شغال، ويستخدم لتصحيته. يرجع {"status":"ok"}
POST/generateيولّد قصة

مثال طلب:

{ "lesson": "دورة الماء", "grade": "الصف الرابع الابتدائي", "subject": "" }

الرد: كائن Story، والأجزاء فيه عقد من نوع narrative، وبينها أسئلة من نوع question، وآخرها ending:

{
  "id": "custom-1a2b3c4d",
  "title": "دورة الماء",
  "region": "الدمام",
  "grade": "الصف الرابع الابتدائي",
  "startNode": "part_1",
  "nodes": {
    "part_1": { "type": "narrative", "title": "الجزء الأول", "text": "...", "next": "question_1" },
    "question_1": {
      "type": "question", "title": "سؤال سريع", "text": "...؟",
      "choices": [
        { "text": "...", "correct": true,  "points": 100, "feedback": "...", "next": "part_2" },
        { "text": "...", "correct": false, "points": 10,  "feedback": "الإجابة الصحيحة: ...", "next": "part_2" },
        { "text": "...", "correct": false, "points": 10,  "feedback": "الإجابة الصحيحة: ...", "next": "part_2" }
      ]
    },
    "part_2": { "type": "ending", "title": "الخاتمة", "text": "..." }
  }
}

النشر على Vercel

الواجهة منشورة على Vercel من الـ fork: FajerAlanazi/Medad. أي push على main ينشر الموقع تلقائياً، وأي برانش ثاني يطلع له رابط Preview مستقل.

  • Environment Variables: VITE_API_URL لازم ينضاف في Vercel (Settings → Environment Variables)، لأن ملف .env ما ينرفع. تأكدوا إن الرابط ما فيه مسافات ولا / في آخره.
  • Git LFS: فعّلوا Git Large File Storage (LFS) من Settings → Git، وإلا صور الأبطال ما تطلع. بعد التفعيل سوّوا Redeploy.

تجربة تعديل على السيرفر بدون ما يتأثر الموقع

  1. سوّوا برانش جديد، وفي server/modal_app.py غيّروا APP_NAME = "medad" إلى "medad-dev".
  2. modal deploy modal_app.py: يطلع سيرفر تجريبي برابط فيه medad-dev. النموذج ما يحتاج تنزيل ثاني، لأن النسختين يستخدمون نفس الـ Volume.
  3. في Vercel حطوا رابط medad-dev في VITE_API_URL لبيئة Preview بس، وارفعوا البرانش وجربوا رابط الـ Preview.
  4. إذا نجحت التجربة: رجّعوا APP_NAME = "medad"، و modal deploy، وادمجوا في main، وبعدها modal app stop medad-dev.

لمتابعة ردود النموذج على الأسئلة: modal app logs medad (أو medad-dev). كل سؤال يطلع جنبه ✅ إذا انقبل، أو ❌ إذا صيغته غلط.

ملاحظات مهمة

  • صور الأبطال محفوظة بـ Git LFS. قبل ما تسوّون clone أو push، شغّلوا git lfs install. إذا حمّلتوا المشروع كملف ZIP من GitHub، الصور بتنزل فاضية، حجم كل وحدة تقريباً 131 بايت.
  • إصدار transformers: النموذج انحفظ على Kaggle بإصدار 5، فالسيرفر لازم يستخدم transformers>=5. الإصدار 4 يعطي الخطأ 'list' object has no attribute 'keys'.
  • جودة الأسئلة: النموذج متدرّب على كتابة القصص وليس الأسئلة، فممكن أحياناً ما يطلع سؤال (إذا الصيغة غلط)، أو يطلع سؤال بسيط. الكود يتحقق من الصيغة قبل ما يعرض السؤال.
  • رابط السيرفر عام: أي أحد معه الرابط يقدر يستخدمه، والتكلفة على حساب Modal. عشان كذا لا تنشرونه للعامة، وحطوا حد صرف في الحساب.

آخر التحديثات

الملفالتغيير
server/modal_app.pyسيرفر علام على Modal + توليد أسئلة الفهم بين الأجزاء + توزيع النقاط (المجموع ٤٠٠)
src/App.tsxربط "موضوع خاص" بالسيرفر، وشاشة النتيجة نفسها لكل القصص
src/components/AdventureScreen.tsxالنقاط تظهر لكل القصص، وزر "اعرض نتيجتي" في النهاية. قصص علام معها البطل المرافق
src/components/ResultsScreen.tsxتشتغل لقصص "موضوع خاص" بعد (تستخدم عنوان الدرس في التقييم)
src/api/storyApi.tsطلب القصة وتصحية السيرفر. يشيل المسافات من الرابط تلقائياً
src/components/GeneratingScreen.tsxتنتظر رد السيرفر الحقيقي، وفيها حالة خطأ وإعادة محاولة
src/components/CompanionSpeech.tsxفقاعة كلام البطل المرافق
src/data/stories.tsشلنا صور الخلفية اللي ما لها علاقة بالقصص الجاهزة
src/data/characters.tsتصحيح ترحيب ملك: "ومتحمسة أتعلم وياك اليوم"
src/assets/characters/*.pngصور الأبطال (Git LFS)
.env.example، .gitignoreمثال الرابط، والسماح برفع .env.example

itsdevruba/Medad

TypeScript

0

25 commits

updated Sep 25, 2026

See the code

README

مداد — Medad

منصة تعليمية تفاعلية تحوّل دروس الأطفال إلى قصص ومغامرات، أحداثها في مناطق المملكة العربية السعودية.

🔗 المنصة: https://medad-five.vercel.app

في ميزة "موضوع خاص"، الطفل يكتب عنوان أي درس ويختار صفه الدراسي، فيكتب له نموذج علام (ALLaM-7B) المدرّب على داتا مداد قصة تعليمية جديدة. القصة تنعرض مقسّمة أجزاء، وبينها أسئلة فهم قصيرة، ومعه البطل اللي اختاره. وفي النهاية تطلع له نفس شاشة النتيجة حقت القصص الجاهزة.


المحتويات


المميزات

  • قصص جاهزة تفاعلية، مثل دورة الماء والسلاسل الغذائية، فيها أسئلة واختيارات ونقاط وإنجازات.
  • موضوع خاص بالذكاء الاصطناعي: قصة جديدة لأي درس يكتبه الطفل، يولّدها نموذج علام المدرّب، وبين أجزائها سؤال أو سؤالين اختيار من متعدد.
  • شاشة نتيجة موحّدة: القصص الجاهزة و"موضوع خاص" تنتهي بنفس الشاشة (نسبة، نجوم، إنجازات)، وتنحفظ في لوحة التقدم.
  • أربعة أبطال مرافقين (ملك، فهد، سالم، دانة). البطل اللي يختاره الطفل يرحّب فيه ويرافقه خلال القصة.
  • لمسة سعودية: أحداث القصص في مدن ومناطق سعودية حقيقية.

طريقة العمل

الطفل يكتب عنوان الدرس + الصف
          │
          ▼
واجهة React (Vite)  ──POST /generate──►  سيرفر FastAPI على Modal (GPU L4)
          ▲                                       │
          │                                       ▼
          │                         نموذج علام المدرّب (merged, float16)
          │                                       │
          │                                       ▼
          │                         تنظيف النص + تقسيمه لأجزاء
          │                                       │
          │                                       ▼
          │                  توليد أسئلة الفهم (نفس النموذج، prompt ثاني)
          │                                       │
          └──── Story JSON (أجزاء + أسئلة) ◄──────┘
  1. الواجهة ترسل { lesson, grade } للسيرفر.
  2. السيرفر يبني الطلب بنفس الـ system prompt وإعدادات التوليد المستخدمة في نوتبوك الاستدلال (midad-allam-merged-inference).
  3. ينظّف النص من الرموز الزايدة، ويقسّمه من جزأين إلى خمسة أجزاء، ويستخرج المدينة المذكورة في القصة.
  4. يولّد سؤال اختيار من متعدد (٣ خيارات) بعد جزء أو جزئين، بـ prompt مختلف. إذا رد النموذج بصيغة غلط، السؤال ينشال والقصة تكمل بدونه. الخيارات تنخلط عشوائياً.
  5. يرجع كائن Story بنفس الهيكل اللي تستخدمه القصص الجاهزة في src/data/stories.ts، فالتطبيق يعرضه على طول.

النقاط: المجموع ٤٠٠ مثل القصص الجاهزة. كل سؤال صحيح = ١٠٠ نقطة (والغلط ١٠)، والباقي يتوزع على أجزاء القصة. يعني كل الأسئلة صح = ١٠٠٪، وكلها غلط ≈ ٥٥٪.

للتحكم في الأسئلة من server/modal_app.py: ENABLE_QUESTIONS = False يطفيها، و MAX_QUESTIONS يحدد عددها. كل سؤال يزيد تقريباً ١٠–١٥ ثانية على وقت التوليد.

السيرفر يشتغل لحاله وقت الطلب، ويطفي نفسه بعد ٥ دقائق بدون استخدام. أول طلب بعد فترة توقف ياخذ تقريباً دقيقة إلى دقيقتين عشان يحمّل النموذج، وبعدها كل قصة تاخذ تقريباً ٢٠ إلى ٤٠ ثانية. التطبيق يرسل طلب "تصحية" للسيرفر أول ما يفتح، عشان يكون جاهز غالباً لما يوصل الطفل لـ"موضوع خاص".

هيكل المشروع

Medad-main/
├── server/
│   └── modal_app.py            # سيرفر علام على Modal (تنزيل النموذج + الـ API)
├── src/
│   ├── api/
│   │   └── storyApi.ts         # طلب القصة من السيرفر + تصحية السيرفر
│   ├── assets/characters/      # صور الأبطال الأربعة (Git LFS)
│   ├── components/
│   │   ├── GeneratingScreen.tsx     # شاشة انتظار توليد القصة (مع حالة الخطأ وإعادة المحاولة)
│   │   ├── AdventureScreen.tsx      # شاشة القصة والأسئلة (لقصص علام: البطل المرافق)
│   │   ├── CompanionSpeech.tsx      # فقاعة كلام البطل المرافق
│   │   ├── ResultsScreen.tsx        # شاشة النتيجة (لكل القصص)
│   │   └── ...
│   ├── data/
│   │   ├── stories.ts          # القصص الجاهزة + هيكل Story
│   │   └── characters.ts       # بيانات الأبطال
│   └── App.tsx
├── AI-work/                    # داتا التدريب ونوتبوك التدريب
├── docs/                       # التقرير (المنهجية) ومخطط سير العمل والبوستر
├── legacy/                     # نسخ قديمة ما عاد تُستخدم (سيرفر Kaggle + tunnel، نوتبوك قديم)
├── .env.example                # مثال لرابط السيرفر
└── README.md

تشغيل الواجهة

المتطلبات: Node.js 18 أو أحدث.

npm install

سوّوا ملف .env في مجلد المشروع الرئيسي، فيه رابط السيرفر. تقدرون تنسخون .env.example:

VITE_API_URL=https://<workspace>--medad-medad-web.modal.run

رابط السيرفر الحالي عند صاحبة البرانش. اطلبوه منها، لأن ملف .env ما يترفع على GitHub.

npm run dev

بعدها افتحوا http://localhost:8443.

تشغيل السيرفر على Modal

هذا القسم تحتاجونه بس إذا بتنشرون السيرفر من حسابكم. إذا بتجربون الواجهة بس، استخدموا الرابط الموجود.

المتطلبات: Python 3.10 أو أحدث، وحساب على Modal مضاف له بطاقة. التكلفة تنخصم من الرصيد المجاني، ويُفضّل تحطون حد صرف من إعدادات Billing.

pip install modal
python -m modal setup

١. احفظوا مفتاح Kaggle في Modal. تجيبونه من kaggle.com: Settings ثم API ثم Create New Token.

python -m modal secret create medad-secrets KAGGLE_USERNAME=... KAGGLE_KEY=...

الحساب لازم يقدر يوصل للنموذج lama377/midad-allam-7b على Kaggle.

٢. نزّلوا النموذج. هذي الخطوة مرة وحدة بس، والنموذج تقريباً 13GB.

cd server
python -m modal run modal_app.py::download_model

٣. انشروا السيرفر:

python -m modal deploy modal_app.py

الرابط اللي يطلع لكم هو قيمة VITE_API_URL.

مستخدمي Windows: إذا طلع خطأ ترميز بسبب الحروف العربية، شغّلوا هذا الأمر قبل أوامر Modal:

$env:PYTHONIOENCODING="utf-8"

الـ API

الطريقةالمسارالوصف
GET/healthفحص إن السيرفر شغال، ويستخدم لتصحيته. يرجع {"status":"ok"}
POST/generateيولّد قصة

مثال طلب:

{ "lesson": "دورة الماء", "grade": "الصف الرابع الابتدائي", "subject": "" }

الرد: كائن Story، والأجزاء فيه عقد من نوع narrative، وبينها أسئلة من نوع question، وآخرها ending:

{
  "id": "custom-1a2b3c4d",
  "title": "دورة الماء",
  "region": "الدمام",
  "grade": "الصف الرابع الابتدائي",
  "startNode": "part_1",
  "nodes": {
    "part_1": { "type": "narrative", "title": "الجزء الأول", "text": "...", "next": "question_1" },
    "question_1": {
      "type": "question", "title": "سؤال سريع", "text": "...؟",
      "choices": [
        { "text": "...", "correct": true,  "points": 100, "feedback": "...", "next": "part_2" },
        { "text": "...", "correct": false, "points": 10,  "feedback": "الإجابة الصحيحة: ...", "next": "part_2" },
        { "text": "...", "correct": false, "points": 10,  "feedback": "الإجابة الصحيحة: ...", "next": "part_2" }
      ]
    },
    "part_2": { "type": "ending", "title": "الخاتمة", "text": "..." }
  }
}

النشر على Vercel

الواجهة منشورة على Vercel من الـ fork: FajerAlanazi/Medad. أي push على main ينشر الموقع تلقائياً، وأي برانش ثاني يطلع له رابط Preview مستقل.

  • Environment Variables: VITE_API_URL لازم ينضاف في Vercel (Settings → Environment Variables)، لأن ملف .env ما ينرفع. تأكدوا إن الرابط ما فيه مسافات ولا / في آخره.
  • Git LFS: فعّلوا Git Large File Storage (LFS) من Settings → Git، وإلا صور الأبطال ما تطلع. بعد التفعيل سوّوا Redeploy.

تجربة تعديل على السيرفر بدون ما يتأثر الموقع

  1. سوّوا برانش جديد، وفي server/modal_app.py غيّروا APP_NAME = "medad" إلى "medad-dev".
  2. modal deploy modal_app.py: يطلع سيرفر تجريبي برابط فيه medad-dev. النموذج ما يحتاج تنزيل ثاني، لأن النسختين يستخدمون نفس الـ Volume.
  3. في Vercel حطوا رابط medad-dev في VITE_API_URL لبيئة Preview بس، وارفعوا البرانش وجربوا رابط الـ Preview.
  4. إذا نجحت التجربة: رجّعوا APP_NAME = "medad"، و modal deploy، وادمجوا في main، وبعدها modal app stop medad-dev.

لمتابعة ردود النموذج على الأسئلة: modal app logs medad (أو medad-dev). كل سؤال يطلع جنبه ✅ إذا انقبل، أو ❌ إذا صيغته غلط.

ملاحظات مهمة

  • صور الأبطال محفوظة بـ Git LFS. قبل ما تسوّون clone أو push، شغّلوا git lfs install. إذا حمّلتوا المشروع كملف ZIP من GitHub، الصور بتنزل فاضية، حجم كل وحدة تقريباً 131 بايت.
  • إصدار transformers: النموذج انحفظ على Kaggle بإصدار 5، فالسيرفر لازم يستخدم transformers>=5. الإصدار 4 يعطي الخطأ 'list' object has no attribute 'keys'.
  • جودة الأسئلة: النموذج متدرّب على كتابة القصص وليس الأسئلة، فممكن أحياناً ما يطلع سؤال (إذا الصيغة غلط)، أو يطلع سؤال بسيط. الكود يتحقق من الصيغة قبل ما يعرض السؤال.
  • رابط السيرفر عام: أي أحد معه الرابط يقدر يستخدمه، والتكلفة على حساب Modal. عشان كذا لا تنشرونه للعامة، وحطوا حد صرف في الحساب.

آخر التحديثات

الملفالتغيير
server/modal_app.pyسيرفر علام على Modal + توليد أسئلة الفهم بين الأجزاء + توزيع النقاط (المجموع ٤٠٠)
src/App.tsxربط "موضوع خاص" بالسيرفر، وشاشة النتيجة نفسها لكل القصص
src/components/AdventureScreen.tsxالنقاط تظهر لكل القصص، وزر "اعرض نتيجتي" في النهاية. قصص علام معها البطل المرافق
src/components/ResultsScreen.tsxتشتغل لقصص "موضوع خاص" بعد (تستخدم عنوان الدرس في التقييم)
src/api/storyApi.tsطلب القصة وتصحية السيرفر. يشيل المسافات من الرابط تلقائياً
src/components/GeneratingScreen.tsxتنتظر رد السيرفر الحقيقي، وفيها حالة خطأ وإعادة محاولة
src/components/CompanionSpeech.tsxفقاعة كلام البطل المرافق
src/data/stories.tsشلنا صور الخلفية اللي ما لها علاقة بالقصص الجاهزة
src/data/characters.tsتصحيح ترحيب ملك: "ومتحمسة أتعلم وياك اليوم"
src/assets/characters/*.pngصور الأبطال (Git LFS)
.env.example، .gitignoreمثال الرابط، والسماح برفع .env.example