Spaces:
Running
Running
Commit ·
9fde5bb
1
Parent(s): 74d8cf6
Refactor sample sentences into a new examples.py file for better organization
Browse files- app.py +46 -39
- examples.py +72 -0
app.py
CHANGED
|
@@ -6,25 +6,12 @@ import json
|
|
| 6 |
import uuid
|
| 7 |
from pathlib import Path
|
| 8 |
from typing import Optional
|
|
|
|
| 9 |
|
| 10 |
APP_NAME = "arabic-tts-arena"
|
| 11 |
|
| 12 |
LEADERBOARD_FILE = Path(__file__).parent / "leaderboard.json"
|
| 13 |
|
| 14 |
-
SAMPLE_SENTENCES = [
|
| 15 |
-
"مرحباً بكم في ساحة تقييم نماذج تحويل النص إلى كلام العربية",
|
| 16 |
-
"الذكاء الاصطناعي يغير طريقة تفاعلنا مع التكنولوجيا",
|
| 17 |
-
"اللغة العربية من أجمل اللغات وأكثرها ثراءً",
|
| 18 |
-
"التعلم الآلي يفتح آفاقاً جديدة في معالجة اللغات الطبيعية",
|
| 19 |
-
"صوت الإنسان هو أداة التواصل الأكثر تعبيراً",
|
| 20 |
-
"الشمس تشرق من الشرق وتغرب في الغرب",
|
| 21 |
-
"القراءة غذاء العقل والروح",
|
| 22 |
-
"العلم نور والجهل ظلام",
|
| 23 |
-
"الصبر مفتاح الفرج",
|
| 24 |
-
"من جد وجد ومن زرع حصد",
|
| 25 |
-
]
|
| 26 |
-
|
| 27 |
-
|
| 28 |
MAX_SYNTHESIS_RETRIES = 2 # per-model retry cap before giving up
|
| 29 |
|
| 30 |
|
|
@@ -54,6 +41,7 @@ def _get_available_models() -> dict[str, dict[str, str]]:
|
|
| 54 |
print(f"✅ Available models: {', '.join(_AVAILABLE_MODELS_CACHE.keys())}")
|
| 55 |
return _AVAILABLE_MODELS_CACHE
|
| 56 |
|
|
|
|
| 57 |
def get_model_cls(model_id: str):
|
| 58 |
"""Get a Modal class by model_id using the registered class name."""
|
| 59 |
available = _get_available_models()
|
|
@@ -269,6 +257,7 @@ def decode_audio_to_file(audio_base64: str) -> Optional[str]:
|
|
| 269 |
Returns the file path (Gradio gr.Audio accepts file paths).
|
| 270 |
"""
|
| 271 |
import tempfile
|
|
|
|
| 272 |
try:
|
| 273 |
wav_bytes = base64.b64decode(audio_base64)
|
| 274 |
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
|
|
@@ -301,7 +290,11 @@ def synthesize_audio(text: str, model_id: str) -> dict:
|
|
| 301 |
last_error = str(e)
|
| 302 |
print(f"⚠️ {model_id} attempt {attempt} exception: {last_error}")
|
| 303 |
# All retries exhausted
|
| 304 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
|
| 306 |
|
| 307 |
def _get_model_battle_counts() -> dict[str, int]:
|
|
@@ -409,12 +402,19 @@ def generate_comparison(text: str):
|
|
| 409 |
|
| 410 |
# — Model A —
|
| 411 |
yield (
|
| 412 |
-
None,
|
| 413 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
gr.update(visible=False),
|
| 415 |
gr.update(value="⏳ Synthesizing…", interactive=False),
|
| 416 |
gr.update(value="⏳ Generating audio with Model A…", visible=True),
|
| 417 |
-
"🔒 Hidden",
|
|
|
|
| 418 |
gr.update(visible=False), # hide next_round_btn
|
| 419 |
gr.update(interactive=True), # vote_a_btn
|
| 420 |
gr.update(interactive=True), # vote_b_btn
|
|
@@ -436,12 +436,19 @@ def generate_comparison(text: str):
|
|
| 436 |
|
| 437 |
# — Model B —
|
| 438 |
yield (
|
| 439 |
-
None,
|
| 440 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
gr.update(visible=False),
|
| 442 |
gr.update(value="⏳ Synthesizing…", interactive=False),
|
| 443 |
gr.update(value="⏳ Generating audio with Model B…", visible=True),
|
| 444 |
-
"🔒 Hidden",
|
|
|
|
| 445 |
gr.update(visible=False), # hide next_round_btn
|
| 446 |
gr.update(interactive=True), # vote_a_btn
|
| 447 |
gr.update(interactive=True), # vote_b_btn
|
|
@@ -476,8 +483,8 @@ def generate_comparison(text: str):
|
|
| 476 |
model_b_id,
|
| 477 |
result_a["audio_base64"],
|
| 478 |
result_b["audio_base64"],
|
| 479 |
-
gr.update(visible=True),
|
| 480 |
-
gr.update(visible=True),
|
| 481 |
gr.update(visible=False), # result_display
|
| 482 |
gr.update(value="🔊 Synthesize", interactive=True),
|
| 483 |
gr.update(value="", visible=False), # hide status
|
|
@@ -721,20 +728,16 @@ def refresh_leaderboard():
|
|
| 721 |
"""
|
| 722 |
|
| 723 |
# Metadata line
|
| 724 |
-
meta_html =
|
| 725 |
-
f'<div class="lb-meta">'
|
| 726 |
-
f'Last updated {updated_str}'
|
| 727 |
-
f'</div>'
|
| 728 |
-
)
|
| 729 |
|
| 730 |
# Column labels
|
| 731 |
col_header = (
|
| 732 |
'<div class="lb-colheader">'
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
)
|
| 739 |
|
| 740 |
# Build rows
|
|
@@ -753,7 +756,9 @@ def refresh_leaderboard():
|
|
| 753 |
rank_cls = tier_rank.get(rank, "")
|
| 754 |
|
| 755 |
if model_url:
|
| 756 |
-
name_el =
|
|
|
|
|
|
|
| 757 |
else:
|
| 758 |
name_el = f'<span class="lb-name">{name}</span>'
|
| 759 |
|
|
@@ -762,17 +767,17 @@ def refresh_leaderboard():
|
|
| 762 |
items_html += (
|
| 763 |
f'<div class="lb-item {row_cls}">'
|
| 764 |
f'<div class="lb-rank {rank_cls}">{rank}</div>'
|
| 765 |
-
f
|
| 766 |
f'<div class="lb-score">{elo:.0f}</div>'
|
| 767 |
f'<div class="lb-votes">{votes_text}</div>'
|
| 768 |
-
f
|
| 769 |
)
|
| 770 |
|
| 771 |
return (
|
| 772 |
f'<div class="lb-container">'
|
| 773 |
-
f
|
| 774 |
f'<div class="lb-list">{items_html}</div>'
|
| 775 |
-
f
|
| 776 |
)
|
| 777 |
|
| 778 |
except Exception as e:
|
|
@@ -956,7 +961,9 @@ def create_demo():
|
|
| 956 |
)
|
| 957 |
|
| 958 |
# — Status indicator (shown during synthesis) —
|
| 959 |
-
status_display = gr.HTML(
|
|
|
|
|
|
|
| 960 |
|
| 961 |
# — Audio players (hidden until synthesis) —
|
| 962 |
with gr.Row(visible=False, equal_height=True) as audio_row:
|
|
|
|
| 6 |
import uuid
|
| 7 |
from pathlib import Path
|
| 8 |
from typing import Optional
|
| 9 |
+
from examples import SAMPLE_SENTENCES
|
| 10 |
|
| 11 |
APP_NAME = "arabic-tts-arena"
|
| 12 |
|
| 13 |
LEADERBOARD_FILE = Path(__file__).parent / "leaderboard.json"
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
MAX_SYNTHESIS_RETRIES = 2 # per-model retry cap before giving up
|
| 16 |
|
| 17 |
|
|
|
|
| 41 |
print(f"✅ Available models: {', '.join(_AVAILABLE_MODELS_CACHE.keys())}")
|
| 42 |
return _AVAILABLE_MODELS_CACHE
|
| 43 |
|
| 44 |
+
|
| 45 |
def get_model_cls(model_id: str):
|
| 46 |
"""Get a Modal class by model_id using the registered class name."""
|
| 47 |
available = _get_available_models()
|
|
|
|
| 257 |
Returns the file path (Gradio gr.Audio accepts file paths).
|
| 258 |
"""
|
| 259 |
import tempfile
|
| 260 |
+
|
| 261 |
try:
|
| 262 |
wav_bytes = base64.b64decode(audio_base64)
|
| 263 |
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
|
|
|
|
| 290 |
last_error = str(e)
|
| 291 |
print(f"⚠️ {model_id} attempt {attempt} exception: {last_error}")
|
| 292 |
# All retries exhausted
|
| 293 |
+
return {
|
| 294 |
+
"success": False,
|
| 295 |
+
"error": f"{model_id} failed after {MAX_SYNTHESIS_RETRIES} attempts: {last_error}",
|
| 296 |
+
"model_id": model_id,
|
| 297 |
+
}
|
| 298 |
|
| 299 |
|
| 300 |
def _get_model_battle_counts() -> dict[str, int]:
|
|
|
|
| 402 |
|
| 403 |
# — Model A —
|
| 404 |
yield (
|
| 405 |
+
None,
|
| 406 |
+
None,
|
| 407 |
+
None,
|
| 408 |
+
None,
|
| 409 |
+
None,
|
| 410 |
+
None,
|
| 411 |
+
gr.update(visible=False),
|
| 412 |
+
gr.update(visible=False),
|
| 413 |
gr.update(visible=False),
|
| 414 |
gr.update(value="⏳ Synthesizing…", interactive=False),
|
| 415 |
gr.update(value="⏳ Generating audio with Model A…", visible=True),
|
| 416 |
+
"🔒 Hidden",
|
| 417 |
+
"🔒 Hidden", # reset model labels
|
| 418 |
gr.update(visible=False), # hide next_round_btn
|
| 419 |
gr.update(interactive=True), # vote_a_btn
|
| 420 |
gr.update(interactive=True), # vote_b_btn
|
|
|
|
| 436 |
|
| 437 |
# — Model B —
|
| 438 |
yield (
|
| 439 |
+
None,
|
| 440 |
+
None,
|
| 441 |
+
None,
|
| 442 |
+
None,
|
| 443 |
+
None,
|
| 444 |
+
None,
|
| 445 |
+
gr.update(visible=False),
|
| 446 |
+
gr.update(visible=False),
|
| 447 |
gr.update(visible=False),
|
| 448 |
gr.update(value="⏳ Synthesizing…", interactive=False),
|
| 449 |
gr.update(value="⏳ Generating audio with Model B…", visible=True),
|
| 450 |
+
"🔒 Hidden",
|
| 451 |
+
"🔒 Hidden", # reset model labels
|
| 452 |
gr.update(visible=False), # hide next_round_btn
|
| 453 |
gr.update(interactive=True), # vote_a_btn
|
| 454 |
gr.update(interactive=True), # vote_b_btn
|
|
|
|
| 483 |
model_b_id,
|
| 484 |
result_a["audio_base64"],
|
| 485 |
result_b["audio_base64"],
|
| 486 |
+
gr.update(visible=True), # audio_row
|
| 487 |
+
gr.update(visible=True), # vote_row
|
| 488 |
gr.update(visible=False), # result_display
|
| 489 |
gr.update(value="🔊 Synthesize", interactive=True),
|
| 490 |
gr.update(value="", visible=False), # hide status
|
|
|
|
| 728 |
"""
|
| 729 |
|
| 730 |
# Metadata line
|
| 731 |
+
meta_html = f'<div class="lb-meta">Last updated {updated_str}</div>'
|
|
|
|
|
|
|
|
|
|
|
|
|
| 732 |
|
| 733 |
# Column labels
|
| 734 |
col_header = (
|
| 735 |
'<div class="lb-colheader">'
|
| 736 |
+
"<span>Rank</span>"
|
| 737 |
+
"<span>Model</span>"
|
| 738 |
+
"<span>Score</span>"
|
| 739 |
+
"<span>Votes</span>"
|
| 740 |
+
"</div>"
|
| 741 |
)
|
| 742 |
|
| 743 |
# Build rows
|
|
|
|
| 756 |
rank_cls = tier_rank.get(rank, "")
|
| 757 |
|
| 758 |
if model_url:
|
| 759 |
+
name_el = (
|
| 760 |
+
f'<a class="lb-name" href="{model_url}" target="_blank">{name}</a>'
|
| 761 |
+
)
|
| 762 |
else:
|
| 763 |
name_el = f'<span class="lb-name">{name}</span>'
|
| 764 |
|
|
|
|
| 767 |
items_html += (
|
| 768 |
f'<div class="lb-item {row_cls}">'
|
| 769 |
f'<div class="lb-rank {rank_cls}">{rank}</div>'
|
| 770 |
+
f"<div>{name_el}</div>"
|
| 771 |
f'<div class="lb-score">{elo:.0f}</div>'
|
| 772 |
f'<div class="lb-votes">{votes_text}</div>'
|
| 773 |
+
f"</div>"
|
| 774 |
)
|
| 775 |
|
| 776 |
return (
|
| 777 |
f'<div class="lb-container">'
|
| 778 |
+
f"{style_block}{meta_html}{col_header}"
|
| 779 |
f'<div class="lb-list">{items_html}</div>'
|
| 780 |
+
f"</div>"
|
| 781 |
)
|
| 782 |
|
| 783 |
except Exception as e:
|
|
|
|
| 961 |
)
|
| 962 |
|
| 963 |
# — Status indicator (shown during synthesis) —
|
| 964 |
+
status_display = gr.HTML(
|
| 965 |
+
value="", visible=False, elem_classes=["status-msg"]
|
| 966 |
+
)
|
| 967 |
|
| 968 |
# — Audio players (hidden until synthesis) —
|
| 969 |
with gr.Row(visible=False, equal_height=True) as audio_row:
|
examples.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SAMPLE_SENTENCES = [
|
| 2 |
+
# --- Greetings & Welcome ---
|
| 3 |
+
"مرحباً بكم في ساحة تقييم نماذج تحويل النص إلى كلام العربية",
|
| 4 |
+
"أهلاً وسهلاً بكم في هذا التطبيق التفاعلي",
|
| 5 |
+
"السلام عليكم ورحمة الله وبركاته",
|
| 6 |
+
|
| 7 |
+
# --- Technology & AI ---
|
| 8 |
+
"الذكاء الاصطناعي يغير طريقة تفاعلنا مع التكنولوجيا",
|
| 9 |
+
"التعلم الآلي يفتح آفاقاً جديدة في معالجة اللغات الطبيعية",
|
| 10 |
+
"تطوير تقنيات الكلام العربي يشهد تقدماً ملحوظاً في السنوات الأخيرة",
|
| 11 |
+
"الحوسبة السحابية غيرت مفهوم تخزين البيانات ومعالجتها",
|
| 12 |
+
"الروبوتات الذكية أصبحت جزءاً من حياتنا اليومية",
|
| 13 |
+
"تطبيقات الهاتف الذكي سهلت كثيراً من المهام اليومية",
|
| 14 |
+
"البيانات الضخمة تساعد الشركات على اتخاذ قرارات أفضل",
|
| 15 |
+
"الأمن السيبراني أصبح ضرورة لا رفاهية في العصر الرقمي",
|
| 16 |
+
|
| 17 |
+
# --- Arabic Language & Culture ---
|
| 18 |
+
"اللغة العربية من أجمل اللغات وأكثرها ثراءً",
|
| 19 |
+
"صوت الإنسان هو أداة التواصل الأكثر تعبيراً",
|
| 20 |
+
"الخط العربي فن عريق يجمع بين الجمال والإبداع",
|
| 21 |
+
"الشعر العربي ديوان العرب وسجل تاريخهم",
|
| 22 |
+
"الأدب العربي يزخر بروائع خالدة عبر العصور",
|
| 23 |
+
"اللهجات العربية المتنوعة تعكس غنى الثقافة العربية",
|
| 24 |
+
"القرآن الكريم حفظ اللغة العربية على مر العصور",
|
| 25 |
+
"المعلقات السبع من أعظم ما أنتجه الشعر العربي القديم",
|
| 26 |
+
|
| 27 |
+
# --- Proverbs & Wisdom ---
|
| 28 |
+
"القراءة غذاء العقل والروح",
|
| 29 |
+
"العلم نور والجهل ظلام",
|
| 30 |
+
"الصبر مفتاح الفرج",
|
| 31 |
+
"من جد وجد ومن زرع حصد",
|
| 32 |
+
"العقل السليم في الجسم السليم",
|
| 33 |
+
"أطلبوا العلم من المهد إلى اللحد",
|
| 34 |
+
"خير الكلام ما قل ودل",
|
| 35 |
+
"إذا هبت رياحك فاغتنمها",
|
| 36 |
+
"رحلة الألف ميل تبدأ بخطوة واحدة",
|
| 37 |
+
"لا تؤجل عمل اليوم إلى الغد",
|
| 38 |
+
"الوقت كالسيف إن لم تقطعه قطعك",
|
| 39 |
+
"من لم يتعلم من التاريخ محكوم عليه بتكراره",
|
| 40 |
+
|
| 41 |
+
# --- Nature & Science ---
|
| 42 |
+
"الشمس تشرق من الشرق وتغرب في الغرب",
|
| 43 |
+
"الماء أساس الحياة على كوكب الأرض",
|
| 44 |
+
"الفضاء الخارجي مليء بالأسرار التي لم نكتشفها بعد",
|
| 45 |
+
"المحيطات تغطي أكثر من سبعين بالمئة من سطح الأرض",
|
| 46 |
+
"التنوع البيولوجي ضروري لاستمرار الحياة على كوكبنا",
|
| 47 |
+
"الطاقة المتجددة هي مستقبل العالم في مواجهة التغير المناخي",
|
| 48 |
+
"النجوم التي نراها في الليل قد تكون أبعد مما نتخيل",
|
| 49 |
+
"الجبال أوتاد الأرض وتلعب دوراً مهماً في توازن المناخ",
|
| 50 |
+
|
| 51 |
+
# --- Daily Life & Society ---
|
| 52 |
+
"التعليم هو السلاح الأقوى لتغيير العالم",
|
| 53 |
+
"الأسرة هي اللبنة الأولى في بناء المجتمع",
|
| 54 |
+
"السفر يوسع المدارك ويفتح الآفاق",
|
| 55 |
+
"الرياضة تقوي الجسم وتنشط العقل",
|
| 56 |
+
"الموسيقى لغة عالمية يفهمها الجميع",
|
| 57 |
+
"الطبخ العربي يتميز بنكهاته الغنية وتوابله الفريدة",
|
| 58 |
+
"الضيافة العربية معروفة بكرمها وحفاوتها",
|
| 59 |
+
"القهوة العربية رمز للكرم والأصالة",
|
| 60 |
+
"الأسواق الشعبية تحكي قصص المدن وتاريخها",
|
| 61 |
+
"شهر رمضان المبارك يجمع العائلات على مائدة واحدة",
|
| 62 |
+
|
| 63 |
+
# --- Longer / More Complex ---
|
| 64 |
+
"يقول المثل العربي إن الكتاب يُقرأ من عنوانه، لكن الحقيقة أن المحتوى هو الأهم",
|
| 65 |
+
"في عالم سريع التغير، يبقى التعلم المستمر هو المفتاح للنجاح والتميز",
|
| 66 |
+
"تحويل النص إلى كلام هو أحد أهم تطبيقات الذكاء الاصطناعي في مجال اللغويات",
|
| 67 |
+
"المدن العربية القديمة مثل القاهرة وبغداد ودمشق شهدت حضارات عريقة",
|
| 68 |
+
"التواصل الفعال يتطلب الاستماع الجيد قبل التحدث",
|
| 69 |
+
"الابتكار والإبداع هما أساس التقدم في أي مجتمع يسعى للتطور",
|
| 70 |
+
"اللغة العربية تحتوي على أكثر من اثني عشر مليون كلمة بدون تكرار",
|
| 71 |
+
"الترجمة الآلية تطورت كثيراً لكنها لا تزال بحاجة إلى تحسين في اللغة العربية",
|
| 72 |
+
]
|