Instructions to use anwar-dev/Bayan-Arabic-TTS-v1.0 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- NeMo
How to use anwar-dev/Bayan-Arabic-TTS-v1.0 with NeMo:
# tag did not correspond to a valid NeMo domain.
- Notebooks
- Google Colab
- Kaggle
Download compare_models/scripts/make_page.py from anwar-dev/Bayan-Arabic-TTS-v1.0: direct link, hf CLI and curl.
- Browser
- Download file 20.2 kB
-
https://huggingface.co/anwar-dev/Bayan-Arabic-TTS-v1.0/resolve/main/compare_models/scripts/make_page.py
- Command line
-
hf download hf://anwar-dev/Bayan-Arabic-TTS-v1.0/compare_models/scripts/make_page.py
-
curl -L -o make_page.py https://huggingface.co/anwar-dev/Bayan-Arabic-TTS-v1.0/resolve/main/compare_models/scripts/make_page.py
20.2 kB
| #!/usr/bin/env python3 | |
| """Build compare_models/index.html from metrics.json and the audio folders.""" | |
| from __future__ import annotations | |
| import html | |
| import json | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parent.parent | |
| METRICS = json.loads((ROOT / "metrics.json").read_text(encoding="utf-8")) | |
| TEXTS = [ln.strip() for ln in (ROOT / "texts.txt").read_text(encoding="utf-8").splitlines() if ln.strip()] | |
| CATEGORIES = ["أخبار", "علوم", "سؤال", "أرقام", "فصحى طويلة"] | |
| MODELS = [ | |
| dict(key="bayan", name="Bayan (صوت مراد)", short="Bayan", params="357M", arabic="فصحى + مصرية", | |
| clone="مدمج (تدريب)", lic="NVIDIA Open", lic_url="../NOTICE.md", rtf=0.99, | |
| hf="", tested=True, ours=True), | |
| dict(key="chatterbox", name="Chatterbox Multilingual v2", short="Chatterbox", params="0.5B", arabic="23 لغة", | |
| clone="zero-shot", lic="MIT", lic_url="https://huggingface.co/ResembleAI/chatterbox", rtf=1.06, | |
| hf="https://huggingface.co/ResembleAI/chatterbox", tested=True), | |
| dict(key="xtts", name="XTTS-v2", short="XTTS-v2", params="~467M", arabic="17 لغة", | |
| clone="zero-shot", lic="CPML (غير تجاري)", lic_url="https://coqui.ai/cpml", rtf=0.31, | |
| hf="https://huggingface.co/coqui/XTTS-v2", tested=True), | |
| dict(key="mms", name="MMS-TTS Arabic", short="MMS", params="~36M", arabic="فصحى", | |
| clone="لا", lic="CC-BY-NC 4.0", lic_url="https://huggingface.co/facebook/mms-tts-ara", rtf=0.03, | |
| hf="https://huggingface.co/facebook/mms-tts-ara", tested=True), | |
| dict(key="magpie", name="MagpieTTS الأصلي (Jason)", short="MagpieTTS", params="357M", arabic="فصحى + مصرية", | |
| clone="أصوات مدمجة", lic="NVIDIA Open", lic_url="https://huggingface.co/nvidia/magpie_tts_multilingual_357m", rtf=1.03, | |
| hf="https://huggingface.co/nvidia/magpie_tts_multilingual_357m", tested=True), | |
| ] | |
| BY_KEY = {m["key"]: m for m in MODELS} | |
| ROW_ORDER = ["bayan", "chatterbox", "xtts", "mms", "magpie"] | |
| POPULAR = [ | |
| ("hexgrad/Kokoro-82M", "Kokoro-82M", "82M", "10.5M", "7,243", "لا — 8 لغات", "Apache-2.0", False), | |
| ("coqui/XTTS-v2", "XTTS-v2", "~467M", "6.3M", "3,901", "نعم — 17 لغة", "CPML", True), | |
| ("Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice", "Qwen3-TTS 1.7B", "1.7B", "2.0M", "2,154", "لا — 10 لغات", "Apache-2.0", False), | |
| ("ResembleAI/chatterbox", "Chatterbox Multilingual", "0.5B", "1.77M", "1,858", "نعم — 23 لغة", "MIT", True), | |
| ("openbmb/VoxCPM2", "VoxCPM2", "2B", "902k", "1,705", "نعم — 30 لغة", "Apache-2.0", False), | |
| ("SWivid/F5-TTS", "F5-TTS", "336M", "887k", "1,219", "لا رسمياً — en/zh", "CC-BY-NC 4.0", False), | |
| ("microsoft/VibeVoice-1.5B", "VibeVoice-1.5B", "1.5B", "483k", "2,502", "لا — en/zh", "MIT", False), | |
| ("sesame/csm-1b", "CSM-1B", "1B", "129k", "2,481", "لا — إنجليزي", "Apache-2.0", False), | |
| ("nvidia/magpie_tts_multilingual_357m", "MagpieTTS 357M", "357M", "10.7k", "231", "نعم", "NVIDIA Open", True), | |
| ("facebook/mms-tts-ara", "MMS-TTS Arabic", "~36M", "6.5k", "24", "نعم", "CC-BY-NC 4.0", True), | |
| ("fishaudio/fish-speech-1.5", "Fish Speech 1.5", "~0.5B", "3.9k", "779", "نعم — 13 لغة", "CC-BY-NC-SA", False), | |
| ] | |
| def pct(x: float) -> str: | |
| return f"{x * 100:.2f}%" | |
| def badge_class(cer_norm: float) -> str: | |
| if cer_norm < 0.005: | |
| return "ok" | |
| if cer_norm < 0.03: | |
| return "warn" | |
| return "bad" | |
| def badge_text(cer_norm: float) -> str: | |
| if cer_norm < 0.005: | |
| return "مطابق" | |
| return f"CER* {cer_norm * 100:.1f}%" | |
| def aggregate_table() -> str: | |
| rows = [] | |
| for key in ROW_ORDER: | |
| m = BY_KEY[key] | |
| r = METRICS[key] | |
| name = f'<a href="{m["hf"]}" target="_blank" rel="noopener">{html.escape(m["name"])}</a>' if m["hf"] else f'<b>{html.escape(m["name"])}</b>' | |
| ours = " class=\"ours\"" if m.get("ours") else "" | |
| rows.append( | |
| f"<tr{ours}><td dir=\"auto\">{name}</td><td>{m['params']}</td>" | |
| f"<td class=\"main\">{pct(r['mean_cer_norm'])}</td><td>{pct(r['mean_cer'])}</td>" | |
| f"<td>{pct(r['mean_wer'])}</td><td>{r['mean_sim']:.3f}</td><td>{m['rtf']:.2f}</td>" | |
| f"<td dir=\"auto\"><a href=\"{m['lic_url']}\" target=\"_blank\" rel=\"noopener\">{html.escape(m['lic'])}</a></td></tr>" | |
| ) | |
| return "\n".join(rows) | |
| def sentence_cards() -> str: | |
| cards = [] | |
| for i, text in enumerate(TEXTS): | |
| players = [] | |
| for key in ROW_ORDER: | |
| m = BY_KEY[key] | |
| r = METRICS[key]["samples"][i] | |
| players.append( | |
| f"""<div class="player"> | |
| <div class="ph"><span class="pn">{html.escape(m['short'])}</span><span class="tag {badge_class(r['cer_norm'])}">{badge_text(r['cer_norm'])}</span></div> | |
| <audio controls preload="none" src="audio/{key}/sample_{i:02d}.wav"></audio> | |
| </div>""" | |
| ) | |
| hyp_rows = "\n".join( | |
| f"<tr><td>{html.escape(BY_KEY[k]['short'])}</td><td dir=\"rtl\">{html.escape(METRICS[k]['samples'][i]['hyp'])}</td></tr>" | |
| for k in ROW_ORDER | |
| ) | |
| cards.append( | |
| f"""<article class="sentence"> | |
| <div class="sh"><span class="num">{i + 1:02d}</span><span class="cat">{CATEGORIES[i]}</span></div> | |
| <p class="st">{html.escape(text)}</p> | |
| <div class="players">{''.join(players)}</div> | |
| <details class="hyp"><summary>ما سمعه Whisper من كل نظام</summary> | |
| <table><thead><tr><th>النظام</th><th>النص المُلتقط</th></tr></thead><tbody>{hyp_rows}</tbody></table> | |
| </details> | |
| </article>""" | |
| ) | |
| return "\n".join(cards) | |
| def popular_table() -> str: | |
| rows = [] | |
| for repo, name, params, dl, likes, ar, lic, tested in POPULAR: | |
| mark = '<span class="yes">✓</span>' if tested else '<span class="no">—</span>' | |
| rows.append( | |
| f"<tr><td dir=\"auto\"><a href=\"https://huggingface.co/{repo}\" target=\"_blank\" rel=\"noopener\">{html.escape(name)}</a></td>" | |
| f"<td>{params}</td><td dir=\"ltr\">{dl}</td><td dir=\"ltr\">{likes}</td><td>{ar}</td><td dir=\"auto\">{html.escape(lic)}</td><td>{mark}</td></tr>" | |
| ) | |
| return "\n".join(rows) | |
| CSS = """ | |
| :root{--bg:#0b1020;--panel:#141b31;--panel2:#1b2440;--text:#e8ecf7;--muted:#9aa7c7; | |
| --accent:#5ee0b5;--accent2:#7aa2ff;--line:#26304d;--warn:#ffcf6e;--bad:#ff7a90} | |
| *{box-sizing:border-box} | |
| body{margin:0;background:radial-gradient(1200px 600px at 80% -10%,#1d2a55 0%,var(--bg) 55%); | |
| color:var(--text);font-family:"Noto Naskh Arabic","Segoe UI",Tahoma,Arial,sans-serif;line-height:1.8} | |
| a,a:visited{color:var(--accent2);text-decoration:none}a:hover{text-decoration:underline} | |
| .wrap{max-width:1100px;margin:0 auto;padding:40px 22px 70px} | |
| header{border:1px solid var(--line);background:linear-gradient(160deg,#182242,#101830 70%); | |
| border-radius:22px;padding:34px 30px;position:relative;overflow:hidden} | |
| header:before{content:"";position:absolute;inset:auto -60px -120px auto;width:320px;height:320px; | |
| background:radial-gradient(circle,#5ee0b544,transparent 65%);filter:blur(10px)} | |
| .eyebrow{color:var(--accent);font-size:13px;letter-spacing:.4px;margin:0 0 8px} | |
| h1{margin:0 0 8px;font-size:clamp(27px,4vw,42px);letter-spacing:-.5px} | |
| .sub{color:var(--muted);margin:0 0 18px;font-size:17.5px;max-width:850px} | |
| .badges{display:flex;flex-wrap:wrap;gap:8px;position:relative;z-index:1} | |
| .badge{border:1px solid var(--line);background:#0f1730;color:var(--muted);padding:5px 12px;border-radius:999px;font-size:13px} | |
| .badge b{color:var(--text);font-weight:600} | |
| section{margin-top:34px} | |
| h2{font-size:22px;margin:0 0 14px;display:flex;align-items:center;gap:10px} | |
| h2:before{content:"";width:7px;height:22px;border-radius:4px;background:linear-gradient(180deg,var(--accent),var(--accent2))} | |
| .grid{display:grid;gap:14px} | |
| .g4{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))} | |
| .card{background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:18px} | |
| .metric{font-size:30px;font-weight:700;color:var(--accent);letter-spacing:-.5px} | |
| .metric.blue{color:var(--accent2)} | |
| .label{color:var(--muted);font-size:14px} | |
| .note{color:var(--muted);font-size:13.5px;margin-top:8px} | |
| .scroll{overflow-x:auto;border:1px solid var(--line);border-radius:16px;background:var(--panel)} | |
| table{width:100%;border-collapse:collapse;font-size:14.5px;min-width:760px} | |
| th,td{padding:11px 12px;border-bottom:1px solid var(--line);text-align:right;white-space:normal} | |
| th{color:var(--muted);font-weight:600;font-size:12.5px;background:#111a33} | |
| tr:last-child td{border-bottom:none} | |
| td.main{color:var(--accent);font-weight:700;white-space:nowrap} | |
| tr.ours td{background:#123a3055} | |
| .sentence{background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:16px 18px;margin:14px 0} | |
| .sh{display:flex;align-items:center;gap:10px;margin-bottom:4px} | |
| .num{color:var(--muted);font-size:13px;font-variant-numeric:tabular-nums} | |
| .cat{font-size:12px;color:var(--accent2);border:1px solid #2a3a66;background:#151f3a;border-radius:999px;padding:1px 10px} | |
| .st{font-size:16px;margin:4px 0 12px} | |
| .players{display:grid;grid-template-columns:repeat(auto-fit,minmax(185px,1fr));gap:10px} | |
| .player{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:10px 11px} | |
| .ph{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-bottom:2px} | |
| .pn{font-size:13.5px;font-weight:600} | |
| .tag{font-size:11px;border-radius:999px;padding:1px 8px;border:1px solid var(--line);color:var(--muted)} | |
| .tag.ok{color:var(--accent);border-color:#1f5a4a;background:#123a30} | |
| .tag.warn{color:var(--warn);border-color:#5a4a1f;background:#3a3012} | |
| .tag.bad{color:var(--bad);border-color:#5a1f2a;background:#3a121a} | |
| audio{width:100%;height:34px;margin-top:4px;filter:invert(.92) hue-rotate(180deg)} | |
| details.hyp{margin-top:10px;color:var(--muted);font-size:13px} | |
| details.hyp summary{cursor:pointer;color:var(--accent2)} | |
| details.hyp table{min-width:0;margin-top:8px;font-size:13px} | |
| details.hyp th,details.hyp td{white-space:normal;padding:6px 8px} | |
| .yes{color:var(--accent);font-weight:700}.no{color:var(--muted)} | |
| .callout{background:var(--panel2);border:1px solid var(--line);border-radius:14px;padding:16px 18px;font-size:14.5px;color:var(--muted)} | |
| .callout b{color:var(--text)} | |
| ul{margin:8px 0 0;padding-right:20px;color:var(--muted)}li{margin:6px 0} | |
| footer{margin-top:44px;color:var(--muted);font-size:13px;border-top:1px solid var(--line);padding-top:18px} | |
| .links{display:flex;gap:16px;flex-wrap:wrap;margin-top:8px} | |
| @media(max-width:640px){.players{grid-template-columns:1fr 1fr}.st{font-size:15px}} | |
| @media(max-width:420px){.players{grid-template-columns:1fr}} | |
| """ | |
| def main() -> int: | |
| bayan = METRICS["bayan"] | |
| chatter = METRICS["chatterbox"] | |
| xtts = METRICS["xtts"] | |
| html_doc = f"""<!DOCTYPE html> | |
| <html lang="ar" dir="rtl"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Bayan (صوت مراد) مقابل النماذج المشهورة على Hugging Face</title> | |
| <style>{CSS}</style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <header> | |
| <p class="eyebrow">مقارنة مفتوحة · 5 جمل فصحى · 5 أنظمة</p> | |
| <h1>Bayan (صوت مراد) مقابل النماذج المشهورة على Hugging Face</h1> | |
| <p class="sub">نفس النصوص، نفس البطاقة الصوتية، ونفس أدوات القياس. لا نختار الجمل التي ننجح فيها فقط، | |
| ونعرض أيضاً ما لم يكن في صالحنا: التعادل النصي مع Chatterbox، وتفوق XTTS في السرعة.</p> | |
| <div class="badges"> | |
| <span class="badge"><b>5</b> جمل من اختبار الـ32</span> | |
| <span class="badge"><b>Whisper</b> large-v3</span> | |
| <span class="badge"><b>TitaNet</b> لتشابه الصوت</span> | |
| <span class="badge"><b>RX 6700 XT</b> · ROCm</span> | |
| <span class="badge">آخر تحديث: <b>21 أكتوبر 2026</b></span> | |
| </div> | |
| </header> | |
| <section> | |
| <h2>الخلاصة في أربع بطاقات</h2> | |
| <div class="grid g4"> | |
| <div class="card"><div class="metric">{pct(bayan['mean_cer_norm'])}</div><div class="label">CER* لـBayan — 5/5 بلا خطأ نطقي</div><div class="note">بعد تطبيع الأرقام والوحدات</div></div> | |
| <div class="card"><div class="metric">{bayan['mean_sim']:.3f}</div><div class="label">تشابه الصوت مع مراد — الأعلى</div><div class="note">Chatterbox 0.830 · XTTS 0.718</div></div> | |
| <div class="card"><div class="metric blue">{pct(chatter['mean_cer_norm'])}</div><div class="label">CER* لـChatterbox — تعادل نصي</div><div class="note">أقوى منافس مفتوح في هذه العينة</div></div> | |
| <div class="card"><div class="metric blue">0.31</div><div class="label">RTF لـXTTS — الأسرع بين الجودة العالية</div><div class="note">Bayan 0.99 · Chatterbox 1.06</div></div> | |
| </div> | |
| </section> | |
| <section> | |
| <h2>النتائج المجمّعة</h2> | |
| <div class="scroll"> | |
| <table> | |
| <thead><tr><th>النظام</th><th>المعاملات</th><th>CER* ↓</th><th>CER خام (سياقي)</th><th>WER ↓</th><th>التشابه ↑</th><th>RTF ↓</th><th>الترخيص</th></tr></thead> | |
| <tbody> | |
| {aggregate_table()} | |
| </tbody> | |
| </table> | |
| </div> | |
| <p class="note"><b>CER*</b> هو المقياس العادل للنطق: يستبدل الأرقام والوحدات برمز واحد في الطرفين. السبب أن Whisper يكتب «110 كم» بينما النص «مئة وعشرة كيلومترات»؛ لذلك يبدو CER الخام أسوأ مما هو. Bayan وChatterbox كلاهما 0.00% بعد التطبيع.</p> | |
| </section> | |
| <section> | |
| <h2>استمع وقارن — الجمل الخمس</h2> | |
| <p class="note" style="margin-top:-4px">الترتيب: Bayan، Chatterbox، XTTS-v2، MMS، ثم MagpieTTS الأصلي بصوت Jason. كل المشغلات موحّدة الجهارة (−20 LUFS) لتحكيم عادل؛ القياسات على الملفات الخام.</p> | |
| {sentence_cards()} | |
| </section> | |
| <section> | |
| <h2>خريطة النماذج المشهورة على Hugging Face</h2> | |
| <div class="scroll"> | |
| <table> | |
| <thead><tr><th>النموذج</th><th>المعاملات</th><th>التنزيلات</th><th>الإعجابات</th><th>العربية</th><th>الترخيص</th><th>جُرّب؟</th></tr></thead> | |
| <tbody> | |
| {popular_table()} | |
| </tbody> | |
| </table> | |
| </div> | |
| <p class="note">الأرقام لحظة 21 أكتوبر 2026 وقد تتغير. «جُرّب هنا» تعني أننا ولّدنا به نفس الجمل الخمس؛ غيره مذكور للسياق فقط. Kokoro وQwen3-TTS وVibeVoice وCSM لا تدعم العربية، وF5-TTS أساسه إنجليزي/صيني.</p> | |
| </section> | |
| <section> | |
| <h2>لماذا هذه النماذج تحديداً؟</h2> | |
| <div class="grid" style="grid-template-columns:repeat(auto-fit,minmax(240px,1fr))"> | |
| <div class="card"><b>Chatterbox</b><div class="note">0.5B، MIT، 23 لغة، استنساخ فوري. أوزان v2 المتاحة عبر الحزمة 0.1.7. أقوى منافس في هذه العينة: 0.00% CER* وتشابه 0.830.</div></div> | |
| <div class="card"><b>XTTS-v2</b><div class="note">~467M، 17 لغة، استنساخ من 6 ثوانٍ. الأسرع بين النماذج عالية الجودة (RTF 0.31)، لكنه غير تجاري (CPML) وارتكب خطأين بسيطين.</div></div> | |
| <div class="card"><b>MMS-TTS Arabic</b><div class="note">~36M فقط من Meta، بلا استنساخ، وأسرع نظام (RTF 0.03). جودته أقل بوضوح: WER 15.8% وتشابه 0.407.</div></div> | |
| <div class="card"><b>MagpieTTS الأصلي</b><div class="note">النموذج الأساسي بصوت Jason. يوضح أن الفرق ليس في النص فقط: التشابه مع مراد 0.299 مقابل 0.867 لنسختنا.</div></div> | |
| </div> | |
| </section> | |
| <section> | |
| <h2>المنهجية والعدالة</h2> | |
| <div class="callout"> | |
| <ul> | |
| <li><b>النصوص:</b> خمس جمل من اختبار الـ32 الحصري (فهارس 0، 1، 8، 19، 30): أخبار، علوم، سؤال، أرقام، وجملة فصحى طويلة. مشكّلة بالكامل.</li> | |
| <li><b>Bayan:</b> 357M إجمالاً، دُرّب منها 13.9M فقط (LoRA + صف صوت) ثم دُمجت في الملف المنشور.</li> | |
| <li><b>الاستنساخ:</b> XTTS وChatterbox استلما نفس المقطع المرجعي (9.04 ثانية) من بيانات مراد. Bayan نفسه تدرب على 71.6 دقيقة — عدم تماثل واضح، وليس في مصلحة المنافسين.</li> | |
| <li><b>الإعدادات:</b> XTTS وChatterbox وMMS على إعداداتها الافتراضية؛ Bayan على cfg 2.5 / temperature 0.6 / top-k 80 / seed 1234. لم نُعد ضبط أي نموذج خصيصاً لهذه الجمل.</li> | |
| <li><b>القياس:</b> Whisper large-v3 للـCER/WER بعد تطبيع عربي (إزالة التشكيل والترقيم وتوحيد الألف والتاء المربوطة). التشابه = cosine بين TitaNet ومتوسط بصمة مراد من 12 مقطعاً تدريبياً.</li> | |
| <li><b>RTF:</b> زمن التوليد ÷ زمن الصوت، بدون تحميل النموذج، على RX 6700 XT عبر ROCm 6.3. الأرقام في الجدول من تشغيل الـ5 جمل (وBayan/Jason من تشغيل الـ32).</li> | |
| <li><b>العينات:</b> XTTS وChatterbox يخرجان 24 kHz، وMMS 16 kHz، وBayan 22.05 kHz. وُحّدت كلها إلى 22.05 kHz في مشغلات الصفحة فقط؛ القياسات على الملفات الأصلية.</li> | |
| <li><b>الشفافية:</b> أخطاء XTTS: «للمتجددته» و«الميتوكندرية». MMS: «ألن»، «الميت كوندريا»، «بدقا»، «الساعد»، وجملة طويلة مشوّشة. Chatterbox: لا أخطاء نصية. Bayan: لا أخطاء بعد تطبيع الأرقام.</li> | |
| </ul> | |
| </div> | |
| </section> | |
| <section> | |
| <h2>الحكم النهائي</h2> | |
| <div class="callout"> | |
| <b>Bayan ليس الأفضل في كل شيء، لكنه الأفضل في ما صُنع من أجله:</b> بصمة صوت «مراد» المصرية | |
| (0.867)، وفصحى سليمة 5/5 بعد تطبيع الأرقام، بحجم 357M يعمل محلياً داخل NeMo. في المقابل، | |
| <b>Chatterbox</b> منافس قوي جداً للفصحى (0.00% CER*، تشابه 0.830، رخصة MIT)، و<b>XTTS-v2</b> | |
| أسرع (0.31) لكنه غير تجاري وأقل دقة قليلاً. <b>MMS</b> خفيف وسريع لكنه ليس في نفس المستوى، | |
| و<b>Jason</b> يثبت أن الصوت الأصلي بعيد عن هدفنا. | |
| </div> | |
| </section> | |
| <footer> | |
| <div>القياسات الخام: <a href="metrics.json" style="color:var(--accent2)">metrics.json</a> · <a href="results.csv" style="color:var(--accent2)">results.csv</a> · النصوص: <a href="texts.txt" style="color:var(--accent2)">texts.txt</a></div> | |
| <div class="links"> | |
| <a href="../model_card.html" style="color:var(--accent2)">بطاقة النموذج</a> | |
| <a href="../README.md" style="color:var(--accent2)">README</a> | |
| <a href="../eval/msa_32/report.html" style="color:var(--accent2)">اختبار الـ32 جملة</a> | |
| <a href="../compare/index.html" style="color:var(--accent2)">مقارنة النص الطويل</a> | |
| </div> | |
| <div style="margin-top:10px">الصفحة مولّدة آلياً من <code>scripts/make_page.py</code>. لا تُستخدم عينات النماذج الأخرى خارج نطاق المقارنة دون مراجعة تراخيصها.</div> | |
| </footer> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| (ROOT / "index.html").write_text(html_doc, encoding="utf-8") | |
| print("wrote", ROOT / "index.html", len(html_doc)) | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |