mozed54 commited on
Commit
3927902
Β·
verified Β·
1 Parent(s): 9a47423

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -35
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
- if np.abs(data).max() > 1.0:
94
- data = data / 32768.0
 
 
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(feedback_file, entry, repo_id):
 
 
 
 
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 non-arabes ──────────────────────────────────
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
- nl = re.sub(r'[*_`#*]+', '', nl).strip()
 
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, # flag succès → 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, # flag succès → 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, # flag succès → False
303
  )
304
 
305
 
306
  # ── Γ‰TAPE 2 : RΓ©ponse Gemma ───────────────────────────────────────
307
  def respond_gemma(ok, progress=gr.Progress()):
308
- # FIX : ne rien faire si la transcription a Γ©chouΓ© ou Γ©tait vide
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 "⏳ جاري Ψ§Ω„ΨͺΩΩƒΩŠΨ±...", gr.update(visible=False)
 
 
 
 
 
 
374
 
375
  current_exchange["reponse"] = last_partial
376
  progress(1.0, desc="βœ… PrΓͺt")
377
- yield last_partial, gr.update(visible=True)
 
 
 
 
 
378
 
379
  except Exception as e:
380
- yield f"Erreur : {str(e)}", gr.update(visible=False)
 
 
 
 
 
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, # reset du state flag
396
  )
397
 
398
 
@@ -410,7 +447,8 @@ def save_feedback_whisper(transcription, note):
410
  "transcription": transcription,
411
  "note": int(note),
412
  }
413
- append_and_upload("feedback.jsonl", entry, FEEDBACK_REPO)
 
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
- append_and_upload("feedback.jsonl", entry, FEEDBACK2_REPO)
 
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], # FIX : conditionne Gemma au succès Whisper
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], # FIX : conditionne Gemma au succès Whisper
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, # FIX : reset du flag aussi
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