Update app.py
Browse files
app.py
CHANGED
|
@@ -83,6 +83,11 @@ def generate_audio_name():
|
|
| 83 |
|
| 84 |
|
| 85 |
def process_audio(audio):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
if audio is None:
|
| 87 |
return None
|
| 88 |
if isinstance(audio, tuple):
|
|
@@ -90,15 +95,21 @@ def process_audio(audio):
|
|
| 90 |
if data.ndim > 1:
|
| 91 |
data = data.mean(axis=1)
|
| 92 |
data = data.astype(np.float32)
|
| 93 |
-
|
| 94 |
-
|
|
|
|
|
|
|
| 95 |
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
|
| 96 |
sf.write(tmp.name, data, sample_rate)
|
| 97 |
return tmp.name
|
| 98 |
return audio
|
| 99 |
|
| 100 |
|
| 101 |
-
def append_and_upload(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
api = HfApi(token=HF_TOKEN)
|
| 103 |
local = f"/tmp/{repo_id.replace('/', '')}.jsonl"
|
| 104 |
try:
|
|
@@ -146,8 +157,14 @@ def build_prompt(transcription: str) -> str:
|
|
| 146 |
)
|
| 147 |
|
| 148 |
|
| 149 |
-
# ββ Filtre caractΓ¨res
|
| 150 |
def filter_arabic_only(text: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
allowed = re.compile(
|
| 152 |
r'[^\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF'
|
| 153 |
r'\uFB50-\uFDFF\uFE70-\uFEFF'
|
|
@@ -155,6 +172,8 @@ def filter_arabic_only(text: str) -> str:
|
|
| 155 |
r'\u060C\u061B\u061F\u06D4'
|
| 156 |
r'\u0021-\u002F\u003A-\u0040'
|
| 157 |
r'\u005B-\u0060\u007B-\u007E'
|
|
|
|
|
|
|
| 158 |
r'\n]'
|
| 159 |
)
|
| 160 |
return allowed.sub('', text).strip()
|
|
@@ -177,7 +196,8 @@ def extract_reply(text: str) -> str:
|
|
| 177 |
next_parts = []
|
| 178 |
for j in range(i + 1, len(lines)):
|
| 179 |
nl = lines[j].strip().strip('"\'\u201c\u201d\u2018\u2019\u00ab\u00bb')
|
| 180 |
-
|
|
|
|
| 181 |
if nl:
|
| 182 |
next_parts.append(nl)
|
| 183 |
if next_parts:
|
|
@@ -259,7 +279,6 @@ def generate_gemma_response(transcription: str, progress=None):
|
|
| 259 |
|
| 260 |
# ββ ΓTAPE 1 : Transcription βββββββββββββββββββββββββββββββββββββββ
|
| 261 |
def transcribe_only(audio, progress=gr.Progress()):
|
| 262 |
-
# FIX : reset TOUJOURS en premier β Γ©vite d'utiliser une transcription prΓ©cΓ©dente
|
| 263 |
current_exchange["transcription"] = None
|
| 264 |
current_exchange["reponse"] = None
|
| 265 |
stop_generation["value"] = False
|
|
@@ -271,7 +290,7 @@ def transcribe_only(audio, progress=gr.Progress()):
|
|
| 271 |
gr.update(visible=False),
|
| 272 |
gr.update(visible=False),
|
| 273 |
gr.update(interactive=True),
|
| 274 |
-
False,
|
| 275 |
)
|
| 276 |
|
| 277 |
try:
|
|
@@ -290,7 +309,7 @@ def transcribe_only(audio, progress=gr.Progress()):
|
|
| 290 |
gr.update(visible=False),
|
| 291 |
gr.update(visible=False),
|
| 292 |
gr.update(interactive=False),
|
| 293 |
-
True,
|
| 294 |
)
|
| 295 |
|
| 296 |
except Exception as e:
|
|
@@ -299,13 +318,13 @@ def transcribe_only(audio, progress=gr.Progress()):
|
|
| 299 |
gr.update(visible=False),
|
| 300 |
gr.update(visible=False),
|
| 301 |
gr.update(interactive=True),
|
| 302 |
-
False,
|
| 303 |
)
|
| 304 |
|
| 305 |
|
| 306 |
# ββ ΓTAPE 2 : RΓ©ponse Gemma βββββββββββββββββββββββββββββββββββββββ
|
| 307 |
def respond_gemma(ok, progress=gr.Progress()):
|
| 308 |
-
|
| 309 |
if not ok:
|
| 310 |
yield (
|
| 311 |
"",
|
|
@@ -331,8 +350,9 @@ def respond_gemma(ok, progress=gr.Progress()):
|
|
| 331 |
last_partial = ""
|
| 332 |
for partial in generate_gemma_response(transcription, progress=progress):
|
| 333 |
last_partial = partial
|
|
|
|
| 334 |
yield (
|
| 335 |
-
"β³ Ψ¬Ψ§Ψ±Ω Ψ§ΩΨͺΩΩΩΨ±...",
|
| 336 |
gr.update(visible=False),
|
| 337 |
gr.update(visible=False),
|
| 338 |
gr.update(interactive=False),
|
|
@@ -359,9 +379,10 @@ def respond_gemma(ok, progress=gr.Progress()):
|
|
| 359 |
|
| 360 |
# ββ RΓ©gΓ©nΓ©ration Gemma seule ββββββββββββββββββββββββββββββββββββββ
|
| 361 |
def regenerate_gemma_only(progress=gr.Progress()):
|
|
|
|
| 362 |
transcription = current_exchange.get("transcription")
|
| 363 |
if not transcription:
|
| 364 |
-
yield "", gr.update(visible=False)
|
| 365 |
return
|
| 366 |
|
| 367 |
try:
|
|
@@ -370,14 +391,30 @@ def regenerate_gemma_only(progress=gr.Progress()):
|
|
| 370 |
last_partial = ""
|
| 371 |
for partial in generate_gemma_response(transcription, progress=progress):
|
| 372 |
last_partial = partial
|
| 373 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
|
| 375 |
current_exchange["reponse"] = last_partial
|
| 376 |
progress(1.0, desc="β
PrΓͺt")
|
| 377 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
|
| 379 |
except Exception as e:
|
| 380 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
|
| 382 |
|
| 383 |
# ββ Reset βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -392,7 +429,7 @@ def reset_all():
|
|
| 392 |
gr.update(visible=False), gr.update(visible=False),
|
| 393 |
None, None, "", "",
|
| 394 |
gr.update(interactive=True), gr.update(interactive=True),
|
| 395 |
-
False,
|
| 396 |
)
|
| 397 |
|
| 398 |
|
|
@@ -410,7 +447,8 @@ def save_feedback_whisper(transcription, note):
|
|
| 410 |
"transcription": transcription,
|
| 411 |
"note": int(note),
|
| 412 |
}
|
| 413 |
-
|
|
|
|
| 414 |
if current_audio["path"] and os.path.exists(current_audio["path"]):
|
| 415 |
api.upload_file(
|
| 416 |
path_or_fileobj=current_audio["path"],
|
|
@@ -436,7 +474,8 @@ def save_feedback_gemma(note):
|
|
| 436 |
"reponse": r,
|
| 437 |
"note": int(note),
|
| 438 |
}
|
| 439 |
-
|
|
|
|
| 440 |
return f"β
Feedback enregistrΓ© β {'β' * int(note)}"
|
| 441 |
except Exception as e:
|
| 442 |
return f"β Erreur : {str(e)}"
|
|
@@ -456,7 +495,6 @@ body, .gradio-container {
|
|
| 456 |
padding: 2rem 1.5rem !important;
|
| 457 |
}
|
| 458 |
|
| 459 |
-
/* ββ Header ββ */
|
| 460 |
#app-header {
|
| 461 |
text-align: center;
|
| 462 |
margin-bottom: 2rem;
|
|
@@ -466,7 +504,6 @@ body, .gradio-container {
|
|
| 466 |
#app-header h1 { font-size: 2rem; font-weight: 600; color: #f0f0f5; margin: 0 0 0.4rem; }
|
| 467 |
#app-header p { font-size: 0.9rem; color: #6b6b80; margin: 0; }
|
| 468 |
|
| 469 |
-
/* ββ Section labels ββ */
|
| 470 |
.section-label {
|
| 471 |
font-size: 0.72rem;
|
| 472 |
font-weight: 600;
|
|
@@ -476,7 +513,6 @@ body, .gradio-container {
|
|
| 476 |
margin-bottom: 0.6rem;
|
| 477 |
}
|
| 478 |
|
| 479 |
-
/* ββ Bouton Transcrire voix (mic) ββ */
|
| 480 |
#mic-btn {
|
| 481 |
background: linear-gradient(135deg, #22c97a 0%, #16a85f 100%) !important;
|
| 482 |
color: #fff !important;
|
|
@@ -500,7 +536,6 @@ body, .gradio-container {
|
|
| 500 |
box-shadow: none !important;
|
| 501 |
}
|
| 502 |
|
| 503 |
-
/* ββ Bouton Transcrire fichier ββ */
|
| 504 |
#file-btn {
|
| 505 |
background: linear-gradient(135deg, #5a5aff 0%, #4040d9 100%) !important;
|
| 506 |
color: #fff !important;
|
|
@@ -524,7 +559,6 @@ body, .gradio-container {
|
|
| 524 |
box-shadow: none !important;
|
| 525 |
}
|
| 526 |
|
| 527 |
-
/* ββ Bouton Reset ββ */
|
| 528 |
button.danger {
|
| 529 |
background: #dc2626 !important;
|
| 530 |
color: #fff !important;
|
|
@@ -535,7 +569,6 @@ button.danger {
|
|
| 535 |
}
|
| 536 |
button.danger:hover { background: #b91c1c !important; }
|
| 537 |
|
| 538 |
-
/* ββ Bouton RΓ©gΓ©nΓ©rer ββ */
|
| 539 |
button.regenerate {
|
| 540 |
background: #7c3aed !important;
|
| 541 |
color: #fff !important;
|
|
@@ -546,7 +579,6 @@ button.regenerate {
|
|
| 546 |
}
|
| 547 |
button.regenerate:hover { background: #6d28d9 !important; }
|
| 548 |
|
| 549 |
-
/* ββ Textboxes ββ */
|
| 550 |
#transcription-box textarea {
|
| 551 |
background: #0f0f13 !important;
|
| 552 |
border: 1.5px solid #1e1e2a !important;
|
|
@@ -580,7 +612,6 @@ button.regenerate:hover { background: #6d28d9 !important; }
|
|
| 580 |
box-shadow: 0 0 0 3px rgba(168,85,247,0.12) !important;
|
| 581 |
}
|
| 582 |
|
| 583 |
-
/* ββ Panneau feedback ββ */
|
| 584 |
.feedback-panel {
|
| 585 |
background: #13131c;
|
| 586 |
border: 1px solid #222230;
|
|
@@ -589,7 +620,6 @@ button.regenerate:hover { background: #6d28d9 !important; }
|
|
| 589 |
margin-top: 0.8rem;
|
| 590 |
}
|
| 591 |
|
| 592 |
-
/* ββ Stars radio (feedback) ββ */
|
| 593 |
.feedback-panel .wrap {
|
| 594 |
gap: 6px !important;
|
| 595 |
}
|
|
@@ -624,7 +654,6 @@ button.regenerate:hover { background: #6d28d9 !important; }
|
|
| 624 |
color: #f59e0b !important;
|
| 625 |
}
|
| 626 |
|
| 627 |
-
/* ββ Bouton Envoyer feedback ββ */
|
| 628 |
.feedback-panel button {
|
| 629 |
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important;
|
| 630 |
color: #1a1000 !important;
|
|
@@ -649,10 +678,8 @@ button.regenerate:hover { background: #6d28d9 !important; }
|
|
| 649 |
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3) !important;
|
| 650 |
}
|
| 651 |
|
| 652 |
-
/* ββ Status feedback ββ */
|
| 653 |
.feedback-panel .prose { color: #6b6b80 !important; font-size: 0.85rem !important; }
|
| 654 |
|
| 655 |
-
/* ββ Mobile fixes ββ */
|
| 656 |
@media (max-width: 768px) {
|
| 657 |
.gr-audio canvas,
|
| 658 |
[data-testid="audio"] canvas,
|
|
@@ -694,7 +721,6 @@ function() {
|
|
| 694 |
# ββ Interface βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 695 |
with gr.Blocks(title="Whisper + Gemma Β· Darija", css=css, js=js) as demo:
|
| 696 |
|
| 697 |
-
# FIX : state qui transmet le rΓ©sultat de transcription Γ respond_gemma
|
| 698 |
transcription_ok = gr.State(False)
|
| 699 |
|
| 700 |
gr.HTML(
|
|
@@ -760,7 +786,7 @@ with gr.Blocks(title="Whisper + Gemma Β· Darija", css=css, js=js) as demo:
|
|
| 760 |
outputs=[whisper_out, fb_whisper, fb_gemma, mic_btn, transcription_ok],
|
| 761 |
).then(
|
| 762 |
fn=respond_gemma,
|
| 763 |
-
inputs=[transcription_ok],
|
| 764 |
outputs=[gemma_out, fb_whisper, fb_gemma, mic_btn],
|
| 765 |
)
|
| 766 |
|
|
@@ -770,14 +796,15 @@ with gr.Blocks(title="Whisper + Gemma Β· Darija", css=css, js=js) as demo:
|
|
| 770 |
outputs=[whisper_out, fb_whisper, fb_gemma, file_btn, transcription_ok],
|
| 771 |
).then(
|
| 772 |
fn=respond_gemma,
|
| 773 |
-
inputs=[transcription_ok],
|
| 774 |
outputs=[gemma_out, fb_whisper, fb_gemma, file_btn],
|
| 775 |
)
|
| 776 |
|
|
|
|
| 777 |
regenerate_btn.click(
|
| 778 |
fn=regenerate_gemma_only,
|
| 779 |
inputs=[],
|
| 780 |
-
outputs=[gemma_out, fb_gemma],
|
| 781 |
)
|
| 782 |
|
| 783 |
btn_whisper.click(
|
|
@@ -798,7 +825,7 @@ with gr.Blocks(title="Whisper + Gemma Β· Darija", css=css, js=js) as demo:
|
|
| 798 |
fb_whisper, fb_gemma,
|
| 799 |
note_whisper, note_gemma, status_whisper, status_gemma,
|
| 800 |
mic_btn, file_btn,
|
| 801 |
-
transcription_ok,
|
| 802 |
],
|
| 803 |
)
|
| 804 |
|
|
|
|
| 83 |
|
| 84 |
|
| 85 |
def process_audio(audio):
|
| 86 |
+
"""Convertit un audio numpy en fichier WAV temporaire.
|
| 87 |
+
|
| 88 |
+
FIX : Normalisation correcte β si le pic est > 1.0, on divise par le pic
|
| 89 |
+
(et non par 32768.0) pour Γ©viter de rendre l'audio quasi-silencieux.
|
| 90 |
+
"""
|
| 91 |
if audio is None:
|
| 92 |
return None
|
| 93 |
if isinstance(audio, tuple):
|
|
|
|
| 95 |
if data.ndim > 1:
|
| 96 |
data = data.mean(axis=1)
|
| 97 |
data = data.astype(np.float32)
|
| 98 |
+
# FIX : vraie normalisation par le pic rΓ©el
|
| 99 |
+
peak = np.abs(data).max()
|
| 100 |
+
if peak > 1.0:
|
| 101 |
+
data = data / peak
|
| 102 |
tmp = tempfile.NamedTemporaryFile(suffix=".wav", delete=False)
|
| 103 |
sf.write(tmp.name, data, sample_rate)
|
| 104 |
return tmp.name
|
| 105 |
return audio
|
| 106 |
|
| 107 |
|
| 108 |
+
def append_and_upload(entry, repo_id):
|
| 109 |
+
"""Ajoute une entrΓ©e au feedback.jsonl local puis l'upload vers HF.
|
| 110 |
+
|
| 111 |
+
FIX : suppression du paramètre feedback_file inutilisé.
|
| 112 |
+
"""
|
| 113 |
api = HfApi(token=HF_TOKEN)
|
| 114 |
local = f"/tmp/{repo_id.replace('/', '')}.jsonl"
|
| 115 |
try:
|
|
|
|
| 157 |
)
|
| 158 |
|
| 159 |
|
| 160 |
+
# ββ Filtre caractΓ¨res βββββββββββββββββββββββββββββββββββββββββββββ
|
| 161 |
def filter_arabic_only(text: str) -> str:
|
| 162 |
+
"""Garde les caractères arabes + ponctuation + chiffres + lettres latines.
|
| 163 |
+
|
| 164 |
+
FIX : ajout des plages \u0030-\u0039 (chiffres 0-9) et
|
| 165 |
+
\u0041-\u005A / \u0061-\u007A (lettres latines) pour prΓ©server
|
| 166 |
+
les numΓ©ros de tΓ©lΓ©phone, codes, et mots techniques (modem, fiber...).
|
| 167 |
+
"""
|
| 168 |
allowed = re.compile(
|
| 169 |
r'[^\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF'
|
| 170 |
r'\uFB50-\uFDFF\uFE70-\uFEFF'
|
|
|
|
| 172 |
r'\u060C\u061B\u061F\u06D4'
|
| 173 |
r'\u0021-\u002F\u003A-\u0040'
|
| 174 |
r'\u005B-\u0060\u007B-\u007E'
|
| 175 |
+
r'\u0030-\u0039' # Chiffres 0-9
|
| 176 |
+
r'\u0041-\u005A\u0061-\u007A' # Lettres latines A-Z a-z
|
| 177 |
r'\n]'
|
| 178 |
)
|
| 179 |
return allowed.sub('', text).strip()
|
|
|
|
| 196 |
next_parts = []
|
| 197 |
for j in range(i + 1, len(lines)):
|
| 198 |
nl = lines[j].strip().strip('"\'\u201c\u201d\u2018\u2019\u00ab\u00bb')
|
| 199 |
+
# FIX : regex nettoyΓ©e (le * Γ©tait en double)
|
| 200 |
+
nl = re.sub(r'[*_`#]+', '', nl).strip()
|
| 201 |
if nl:
|
| 202 |
next_parts.append(nl)
|
| 203 |
if next_parts:
|
|
|
|
| 279 |
|
| 280 |
# ββ ΓTAPE 1 : Transcription βββββββββββββββββββββββββββββββββββββββ
|
| 281 |
def transcribe_only(audio, progress=gr.Progress()):
|
|
|
|
| 282 |
current_exchange["transcription"] = None
|
| 283 |
current_exchange["reponse"] = None
|
| 284 |
stop_generation["value"] = False
|
|
|
|
| 290 |
gr.update(visible=False),
|
| 291 |
gr.update(visible=False),
|
| 292 |
gr.update(interactive=True),
|
| 293 |
+
False,
|
| 294 |
)
|
| 295 |
|
| 296 |
try:
|
|
|
|
| 309 |
gr.update(visible=False),
|
| 310 |
gr.update(visible=False),
|
| 311 |
gr.update(interactive=False),
|
| 312 |
+
True,
|
| 313 |
)
|
| 314 |
|
| 315 |
except Exception as e:
|
|
|
|
| 318 |
gr.update(visible=False),
|
| 319 |
gr.update(visible=False),
|
| 320 |
gr.update(interactive=True),
|
| 321 |
+
False,
|
| 322 |
)
|
| 323 |
|
| 324 |
|
| 325 |
# ββ ΓTAPE 2 : RΓ©ponse Gemma βββββββββββββββββββββββββββββββββββββββ
|
| 326 |
def respond_gemma(ok, progress=gr.Progress()):
|
| 327 |
+
"""FIX : yield le texte PARTIEL Γ chaque itΓ©ration pour un vrai streaming."""
|
| 328 |
if not ok:
|
| 329 |
yield (
|
| 330 |
"",
|
|
|
|
| 350 |
last_partial = ""
|
| 351 |
for partial in generate_gemma_response(transcription, progress=progress):
|
| 352 |
last_partial = partial
|
| 353 |
+
# FIX : on yield le texte partiel (pas un message figΓ©)
|
| 354 |
yield (
|
| 355 |
+
partial if partial else "β³ Ψ¬Ψ§Ψ±Ω Ψ§ΩΨͺΩΩΩΨ±...",
|
| 356 |
gr.update(visible=False),
|
| 357 |
gr.update(visible=False),
|
| 358 |
gr.update(interactive=False),
|
|
|
|
| 379 |
|
| 380 |
# ββ RΓ©gΓ©nΓ©ration Gemma seule ββββββββββββββββββββββββββββββββββββββ
|
| 381 |
def regenerate_gemma_only(progress=gr.Progress()):
|
| 382 |
+
"""FIX : yield le streaming ET gère les boutons pour éviter les conflits."""
|
| 383 |
transcription = current_exchange.get("transcription")
|
| 384 |
if not transcription:
|
| 385 |
+
yield "", gr.update(visible=False), gr.update(interactive=True), gr.update(interactive=True)
|
| 386 |
return
|
| 387 |
|
| 388 |
try:
|
|
|
|
| 391 |
last_partial = ""
|
| 392 |
for partial in generate_gemma_response(transcription, progress=progress):
|
| 393 |
last_partial = partial
|
| 394 |
+
# FIX : yield le texte partiel + boutons dΓ©sactivΓ©s
|
| 395 |
+
yield (
|
| 396 |
+
partial if partial else "β³ Ψ¬Ψ§Ψ±Ω Ψ§ΩΨͺΩΩΩΨ±...",
|
| 397 |
+
gr.update(visible=False),
|
| 398 |
+
gr.update(interactive=False),
|
| 399 |
+
gr.update(interactive=False),
|
| 400 |
+
)
|
| 401 |
|
| 402 |
current_exchange["reponse"] = last_partial
|
| 403 |
progress(1.0, desc="β
PrΓͺt")
|
| 404 |
+
yield (
|
| 405 |
+
last_partial,
|
| 406 |
+
gr.update(visible=True),
|
| 407 |
+
gr.update(interactive=True),
|
| 408 |
+
gr.update(interactive=True),
|
| 409 |
+
)
|
| 410 |
|
| 411 |
except Exception as e:
|
| 412 |
+
yield (
|
| 413 |
+
f"Erreur : {str(e)}",
|
| 414 |
+
gr.update(visible=False),
|
| 415 |
+
gr.update(interactive=True),
|
| 416 |
+
gr.update(interactive=True),
|
| 417 |
+
)
|
| 418 |
|
| 419 |
|
| 420 |
# ββ Reset βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 429 |
gr.update(visible=False), gr.update(visible=False),
|
| 430 |
None, None, "", "",
|
| 431 |
gr.update(interactive=True), gr.update(interactive=True),
|
| 432 |
+
False,
|
| 433 |
)
|
| 434 |
|
| 435 |
|
|
|
|
| 447 |
"transcription": transcription,
|
| 448 |
"note": int(note),
|
| 449 |
}
|
| 450 |
+
# FIX : appel sans le paramètre feedback_file fantôme
|
| 451 |
+
append_and_upload(entry, FEEDBACK_REPO)
|
| 452 |
if current_audio["path"] and os.path.exists(current_audio["path"]):
|
| 453 |
api.upload_file(
|
| 454 |
path_or_fileobj=current_audio["path"],
|
|
|
|
| 474 |
"reponse": r,
|
| 475 |
"note": int(note),
|
| 476 |
}
|
| 477 |
+
# FIX : appel sans le paramètre feedback_file fantôme
|
| 478 |
+
append_and_upload(entry, FEEDBACK2_REPO)
|
| 479 |
return f"β
Feedback enregistrΓ© β {'β' * int(note)}"
|
| 480 |
except Exception as e:
|
| 481 |
return f"β Erreur : {str(e)}"
|
|
|
|
| 495 |
padding: 2rem 1.5rem !important;
|
| 496 |
}
|
| 497 |
|
|
|
|
| 498 |
#app-header {
|
| 499 |
text-align: center;
|
| 500 |
margin-bottom: 2rem;
|
|
|
|
| 504 |
#app-header h1 { font-size: 2rem; font-weight: 600; color: #f0f0f5; margin: 0 0 0.4rem; }
|
| 505 |
#app-header p { font-size: 0.9rem; color: #6b6b80; margin: 0; }
|
| 506 |
|
|
|
|
| 507 |
.section-label {
|
| 508 |
font-size: 0.72rem;
|
| 509 |
font-weight: 600;
|
|
|
|
| 513 |
margin-bottom: 0.6rem;
|
| 514 |
}
|
| 515 |
|
|
|
|
| 516 |
#mic-btn {
|
| 517 |
background: linear-gradient(135deg, #22c97a 0%, #16a85f 100%) !important;
|
| 518 |
color: #fff !important;
|
|
|
|
| 536 |
box-shadow: none !important;
|
| 537 |
}
|
| 538 |
|
|
|
|
| 539 |
#file-btn {
|
| 540 |
background: linear-gradient(135deg, #5a5aff 0%, #4040d9 100%) !important;
|
| 541 |
color: #fff !important;
|
|
|
|
| 559 |
box-shadow: none !important;
|
| 560 |
}
|
| 561 |
|
|
|
|
| 562 |
button.danger {
|
| 563 |
background: #dc2626 !important;
|
| 564 |
color: #fff !important;
|
|
|
|
| 569 |
}
|
| 570 |
button.danger:hover { background: #b91c1c !important; }
|
| 571 |
|
|
|
|
| 572 |
button.regenerate {
|
| 573 |
background: #7c3aed !important;
|
| 574 |
color: #fff !important;
|
|
|
|
| 579 |
}
|
| 580 |
button.regenerate:hover { background: #6d28d9 !important; }
|
| 581 |
|
|
|
|
| 582 |
#transcription-box textarea {
|
| 583 |
background: #0f0f13 !important;
|
| 584 |
border: 1.5px solid #1e1e2a !important;
|
|
|
|
| 612 |
box-shadow: 0 0 0 3px rgba(168,85,247,0.12) !important;
|
| 613 |
}
|
| 614 |
|
|
|
|
| 615 |
.feedback-panel {
|
| 616 |
background: #13131c;
|
| 617 |
border: 1px solid #222230;
|
|
|
|
| 620 |
margin-top: 0.8rem;
|
| 621 |
}
|
| 622 |
|
|
|
|
| 623 |
.feedback-panel .wrap {
|
| 624 |
gap: 6px !important;
|
| 625 |
}
|
|
|
|
| 654 |
color: #f59e0b !important;
|
| 655 |
}
|
| 656 |
|
|
|
|
| 657 |
.feedback-panel button {
|
| 658 |
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important;
|
| 659 |
color: #1a1000 !important;
|
|
|
|
| 678 |
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3) !important;
|
| 679 |
}
|
| 680 |
|
|
|
|
| 681 |
.feedback-panel .prose { color: #6b6b80 !important; font-size: 0.85rem !important; }
|
| 682 |
|
|
|
|
| 683 |
@media (max-width: 768px) {
|
| 684 |
.gr-audio canvas,
|
| 685 |
[data-testid="audio"] canvas,
|
|
|
|
| 721 |
# ββ Interface βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 722 |
with gr.Blocks(title="Whisper + Gemma Β· Darija", css=css, js=js) as demo:
|
| 723 |
|
|
|
|
| 724 |
transcription_ok = gr.State(False)
|
| 725 |
|
| 726 |
gr.HTML(
|
|
|
|
| 786 |
outputs=[whisper_out, fb_whisper, fb_gemma, mic_btn, transcription_ok],
|
| 787 |
).then(
|
| 788 |
fn=respond_gemma,
|
| 789 |
+
inputs=[transcription_ok],
|
| 790 |
outputs=[gemma_out, fb_whisper, fb_gemma, mic_btn],
|
| 791 |
)
|
| 792 |
|
|
|
|
| 796 |
outputs=[whisper_out, fb_whisper, fb_gemma, file_btn, transcription_ok],
|
| 797 |
).then(
|
| 798 |
fn=respond_gemma,
|
| 799 |
+
inputs=[transcription_ok],
|
| 800 |
outputs=[gemma_out, fb_whisper, fb_gemma, file_btn],
|
| 801 |
)
|
| 802 |
|
| 803 |
+
# FIX : la régénération gère maintenant aussi mic_btn et file_btn
|
| 804 |
regenerate_btn.click(
|
| 805 |
fn=regenerate_gemma_only,
|
| 806 |
inputs=[],
|
| 807 |
+
outputs=[gemma_out, fb_gemma, mic_btn, file_btn],
|
| 808 |
)
|
| 809 |
|
| 810 |
btn_whisper.click(
|
|
|
|
| 825 |
fb_whisper, fb_gemma,
|
| 826 |
note_whisper, note_gemma, status_whisper, status_gemma,
|
| 827 |
mic_btn, file_btn,
|
| 828 |
+
transcription_ok,
|
| 829 |
],
|
| 830 |
)
|
| 831 |
|