aankitdas commited on
Commit
29ebcc1
·
1 Parent(s): e65252b

remove eval log from git - now stored in Supabase

Browse files
Files changed (8) hide show
  1. .gitignore +1 -1
  2. app/app.py +72 -2
  3. app/evaluator.py +7 -0
  4. app/results/eval_log.csv +0 -68
  5. app/storage.py +126 -0
  6. pyproject.toml +2 -1
  7. requirements.txt +0 -0
  8. uv.lock +0 -0
.gitignore CHANGED
@@ -19,7 +19,7 @@ voices/**/*.onnx
19
  voices/**/*.json
20
 
21
  # operational logs — machine specific
22
- # app/results/eval_log.csv
23
  app/session_export.csv
24
  app/results_export.csv
25
  app/parler_test.wav
 
19
  voices/**/*.json
20
 
21
  # operational logs — machine specific
22
+ app/results/eval_log.csv
23
  app/session_export.csv
24
  app/results_export.csv
25
  app/parler_test.wav
app/app.py CHANGED
@@ -16,6 +16,7 @@ import os
16
  import tempfile
17
  import pandas as pd
18
  import gradio as gr
 
19
 
20
  sys.path.insert(0, os.path.dirname(__file__))
21
 
@@ -26,7 +27,8 @@ load_dotenv(os.path.join(os.path.dirname(__file__), ".env"), override=False)
26
  from engines import ENGINES, ENGINE_MAP
27
  from engines.kokoro_engine import KOKORO_VOICES, KOKORO_DEFAULT_VOICE
28
  from evaluator import evaluate
29
-
 
30
  # ── constants ─────────────────────────────────────────────────────────────────
31
 
32
  BANDS = ["K-2", "3-5", "6-8", "9-12"]
@@ -83,6 +85,7 @@ def build_comparison_table(results: list[dict]) -> pd.DataFrame:
83
  "RTF ↓ (synth time / audio dur, <1.0 = fast)",
84
  "Latency (s)",
85
  "Cost",
 
86
  ]
87
  if not results:
88
  return pd.DataFrame(columns=columns)
@@ -99,6 +102,7 @@ def build_comparison_table(results: list[dict]) -> pd.DataFrame:
99
  "RTF ↓ (synth time / audio dur, <1.0 = fast)": format_rtf(r["rtf"]),
100
  "Latency (s)": r["latency_s"],
101
  "Cost": format_cost(r["engine_cost_usd"], r["chirp_equiv_usd"], r["engine"]),
 
102
  })
103
  return pd.DataFrame(rows)
104
 
@@ -293,8 +297,31 @@ def build_business_chart(results: list[dict]):
293
 
294
  return fig
295
 
 
 
 
 
 
 
 
 
296
  # ── event handlers ────────────────────────────────────────────────────────────
297
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  def on_engine_change(engine_name: str):
299
  """Show voice dropdown only for Kokoro."""
300
  is_kokoro = engine_name == "Kokoro (tuned)"
@@ -354,6 +381,19 @@ def run_synthesis(engine_name: str, band: str, text: str, voice: str):
354
  yield audio_path, f"✗ Eval failed: {e}", build_comparison_table(_session_results), build_business_chart(_session_results)
355
  return
356
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  _session_results.append(eval_result)
358
 
359
  status = (
@@ -385,15 +425,29 @@ def export_all():
385
  return gr.update(value=_EVAL_LOG_PATH, visible=True), "✓ Full history log ready to download."
386
 
387
  def load_history():
 
 
 
 
 
 
 
388
  if not os.path.exists(_EVAL_LOG_PATH):
389
  return build_comparison_table([]), build_business_chart([]), "⚠ No history found."
390
  try:
391
  df = pd.read_csv(_EVAL_LOG_PATH)
 
 
 
392
  records = df.to_dict(orient="records")
393
  return build_comparison_table(records), build_business_chart(records), f"✓ Loaded {len(records)} historical runs."
394
  except Exception as e:
395
  return build_comparison_table([]), build_business_chart([]), f"✗ Failed: {e}"
396
 
 
 
 
 
397
  # ── UI ───────────────────────────────���────────────────────────────────────────
398
 
399
  def build_ui():
@@ -444,10 +498,16 @@ def build_ui():
444
 
445
  comparison_table = gr.Dataframe(
446
  value=build_comparison_table([]),
447
- label="Eval Results",
448
  interactive=False,
449
  )
450
 
 
 
 
 
 
 
451
  business_chart = gr.Plot(
452
  value=build_business_chart([]),
453
  label="Business Decision Chart",
@@ -455,6 +515,7 @@ def build_ui():
455
 
456
  with gr.Row():
457
  clear_btn = gr.Button("🗑 Clear Session")
 
458
  load_history_btn = gr.Button("📂 Load History")
459
  export_session_btn = gr.Button("⬇ Export Session")
460
  export_all_btn = gr.Button("⬇ Export Full History")
@@ -487,6 +548,15 @@ def build_ui():
487
  fn=clear_results,
488
  outputs=[comparison_table, business_chart, export_status],
489
  )
 
 
 
 
 
 
 
 
 
490
 
491
  load_history_btn.click(
492
  fn=load_history,
 
16
  import tempfile
17
  import pandas as pd
18
  import gradio as gr
19
+ from storage import upload_audio_background, download_csv
20
 
21
  sys.path.insert(0, os.path.dirname(__file__))
22
 
 
27
  from engines import ENGINES, ENGINE_MAP
28
  from engines.kokoro_engine import KOKORO_VOICES, KOKORO_DEFAULT_VOICE
29
  from evaluator import evaluate
30
+ from storage import upload_audio_background
31
+ from pathlib import Path
32
  # ── constants ─────────────────────────────────────────────────────────────────
33
 
34
  BANDS = ["K-2", "3-5", "6-8", "9-12"]
 
85
  "RTF ↓ (synth time / audio dur, <1.0 = fast)",
86
  "Latency (s)",
87
  "Cost",
88
+ "Audio URL"
89
  ]
90
  if not results:
91
  return pd.DataFrame(columns=columns)
 
102
  "RTF ↓ (synth time / audio dur, <1.0 = fast)": format_rtf(r["rtf"]),
103
  "Latency (s)": r["latency_s"],
104
  "Cost": format_cost(r["engine_cost_usd"], r["chirp_equiv_usd"], r["engine"]),
105
+ "Audio URL": r.get("audio_url") or ""
106
  })
107
  return pd.DataFrame(rows)
108
 
 
297
 
298
  return fig
299
 
300
+ def _make_audio_filename(engine_name: str, band: str, ext: str) -> str:
301
+ """Generate a unique bucket filename for an audio file."""
302
+ from datetime import datetime
303
+ ts = datetime.now().strftime("%Y%m%d_%H%M%S")
304
+ safe_engine = engine_name.replace(" ", "_").replace("(", "").replace(")", "")
305
+ safe_band = band.replace("-", "")
306
+ return f"{ts}_{safe_engine}_{safe_band}{ext}"
307
+
308
  # ── event handlers ────────────────────────────────────────────────────────────
309
 
310
+ def on_row_select(evt: gr.SelectData, results_df: pd.DataFrame) -> tuple:
311
+ """
312
+ When a row is selected, pass the Supabase public URL directly
313
+ to gr.Audio value — Gradio fetches it internally.
314
+ """
315
+ try:
316
+ row_idx = evt.index[0]
317
+ url = results_df.iloc[row_idx]["Audio URL"]
318
+ if not url or not str(url).startswith("http"):
319
+ return gr.update(visible=False)
320
+ return gr.update(value=url, visible=True)
321
+ except Exception as e:
322
+ print(f"[Playback] Failed to load audio: {e}")
323
+ return gr.update(visible=False)
324
+
325
  def on_engine_change(engine_name: str):
326
  """Show voice dropdown only for Kokoro."""
327
  is_kokoro = engine_name == "Kokoro (tuned)"
 
381
  yield audio_path, f"✗ Eval failed: {e}", build_comparison_table(_session_results), build_business_chart(_session_results)
382
  return
383
 
384
+ # upload audio to Supabase in background — non-blocking
385
+ audio_ext = Path(audio_path).suffix
386
+ bucket_filename = _make_audio_filename(engine_name, band, audio_ext)
387
+
388
+ def _on_upload(url):
389
+ if url:
390
+ eval_result["audio_url"] = url
391
+ print(f"[Storage] Uploaded: {url}")
392
+ else:
393
+ eval_result["audio_url"] = None
394
+
395
+ upload_audio_background(audio_path, bucket_filename, callback=_on_upload)
396
+ eval_result["audio_url"] = None # placeholder until upload completes
397
  _session_results.append(eval_result)
398
 
399
  status = (
 
425
  return gr.update(value=_EVAL_LOG_PATH, visible=True), "✓ Full history log ready to download."
426
 
427
  def load_history():
428
+ # try Supabase first, fall back to local CSV
429
+ try:
430
+ from storage import download_csv
431
+ download_csv(_EVAL_LOG_PATH)
432
+ except Exception as e:
433
+ print(f"[Storage] Supabase download skipped, using local: {e}")
434
+
435
  if not os.path.exists(_EVAL_LOG_PATH):
436
  return build_comparison_table([]), build_business_chart([]), "⚠ No history found."
437
  try:
438
  df = pd.read_csv(_EVAL_LOG_PATH)
439
+ # fill missing audio_url column for old rows that predate storage
440
+ if "audio_url" not in df.columns:
441
+ df["audio_url"] = ""
442
  records = df.to_dict(orient="records")
443
  return build_comparison_table(records), build_business_chart(records), f"✓ Loaded {len(records)} historical runs."
444
  except Exception as e:
445
  return build_comparison_table([]), build_business_chart([]), f"✗ Failed: {e}"
446
 
447
+ def refresh_table():
448
+ """Rebuild comparison table from current session results — picks up audio URLs from completed uploads."""
449
+ return build_comparison_table(_session_results)
450
+
451
  # ── UI ───────────────────────────────���────────────────────────────────────────
452
 
453
  def build_ui():
 
498
 
499
  comparison_table = gr.Dataframe(
500
  value=build_comparison_table([]),
501
+ label="Eval Results — click a row to play audio",
502
  interactive=False,
503
  )
504
 
505
+ row_audio_player = gr.Audio(
506
+ label="Selected Row Audio",
507
+ visible=False,
508
+ type="filepath",
509
+ )
510
+
511
  business_chart = gr.Plot(
512
  value=build_business_chart([]),
513
  label="Business Decision Chart",
 
515
 
516
  with gr.Row():
517
  clear_btn = gr.Button("🗑 Clear Session")
518
+ refresh_btn = gr.Button("🔄 Refresh Table")
519
  load_history_btn = gr.Button("📂 Load History")
520
  export_session_btn = gr.Button("⬇ Export Session")
521
  export_all_btn = gr.Button("⬇ Export Full History")
 
548
  fn=clear_results,
549
  outputs=[comparison_table, business_chart, export_status],
550
  )
551
+ refresh_btn.click(
552
+ fn=refresh_table,
553
+ outputs=[comparison_table],
554
+ )
555
+ comparison_table.select(
556
+ fn=on_row_select,
557
+ inputs=[comparison_table],
558
+ outputs=[row_audio_player],
559
+ )
560
 
561
  load_history_btn.click(
562
  fn=load_history,
app/evaluator.py CHANGED
@@ -186,4 +186,11 @@ def evaluate(
186
  df = pd.DataFrame([result])
187
  df.to_csv(results_path, mode="a", header=not os.path.exists(results_path), index=False)
188
 
 
 
 
 
 
 
 
189
  return result
 
186
  df = pd.DataFrame([result])
187
  df.to_csv(results_path, mode="a", header=not os.path.exists(results_path), index=False)
188
 
189
+ # upload updated CSV to Supabase in background
190
+ try:
191
+ from storage import upload_csv_background
192
+ upload_csv_background(results_path)
193
+ except Exception as e:
194
+ print(f"[Storage] CSV background upload skipped: {e}")
195
+
196
  return result
app/results/eval_log.csv DELETED
@@ -1,68 +0,0 @@
1
- timestamp,engine,engine_type,production_ready,band,input_text,voice,wer,utmos,rtf,latency_s,engine_cost_usd,chirp_equiv_usd,chars
2
- 2026-04-10 14:00:29,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.515,0.506,3.064,0.0,0.001328,83
3
- 2026-04-10 14:01:08,Parler-TTS (mini),neural-local,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,described:K-2,0.0588,3.967,2.402,13.021,0.0,0.001328,83
4
- 2026-04-10 14:02:33,Parler-TTS (mini),neural-local,False,9-12,"""While your articulation was precise, the tone felt a bit overly formal for this specific prompt. Try to strike a balance between professional authority and conversational engagement.""",described:9-12,0.0741,4.269,1.923,19.376,0.0,0.002944,184
5
- 2026-04-10 14:02:53,Kokoro (tuned),neural-local,True,9-12,"""While your articulation was precise, the tone felt a bit overly formal for this specific prompt. Try to strike a balance between professional authority and conversational engagement.""",am_echo,0.0741,4.434,0.073,0.746,0.0,0.002944,184
6
- 2026-04-10 14:03:22,edge-tts (Microsoft),neural-cloud-free,False,9-12,"""While your articulation was precise, the tone felt a bit overly formal for this specific prompt. Try to strike a balance between professional authority and conversational engagement.""",en-US-JennyNeural,0.0741,,0.12,1.253,0.0,0.002944,184
7
- 2026-04-10 14:06:25,Parler-TTS (mini),neural-local,False,3-5,"""I am so impressed by your vocabulary today. Using words like 'energetic' and 'observant' really makes your speech stand out!""",described:3-5,0.3,4.355,1.94,13.742,0.0,0.002016,126
8
- 2026-04-10 14:06:53,Parler-TTS (mini),neural-local,False,9-12,"""I am so impressed by your vocabulary today. Using words like 'energetic' and 'observant' really makes your speech stand out!""",described:9-12,0.2,4.298,1.923,13.953,0.0,0.002016,126
9
- 2026-04-10 14:07:19,Parler-TTS (mini),neural-local,False,6-8,"""I am so impressed by your vocabulary today. Using words like 'energetic' and 'observant' really makes your speech stand out!""",described:6-8,0.25,4.421,1.849,14.361,0.0,0.002016,126
10
- 2026-04-10 14:07:56,Kokoro (tuned),neural-local,True,6-8,"""I am so impressed by your vocabulary today. Using words like 'energetic' and 'observant' really makes your speech stand out!""",af_heart,0.25,4.535,0.059,0.484,0.0,0.002016,126
11
- 2026-04-10 14:08:35,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.515,0.053,0.319,0.0,0.001328,83
12
- 2026-04-10 14:21:03,Piper (ONNX),neural-local,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,en_US-amy-medium,0.0588,4.511,0.052,0.297,0.0,0.001328,83
13
- 2026-04-10 14:21:26,Piper (ONNX),neural-local,False,9-12,Excellent work on your enunciation. Be mindful of your filler words—like 'um' and 'like'—as eliminating those will significantly increase the impact of your speech.,en_US-lessac-medium,0.25,4.256,0.037,0.332,0.0,0.002624,164
14
- 2026-04-10 14:31:59,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.515,0.217,1.312,0.0,0.001328,83
15
- 2026-04-10 14:46:40,Chatterbox Turbo (RunPod),neural-cloud-paid,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,lucy,0.0588,4.243,0.627,3.185,0.0034,0.001328,83
16
- 2026-04-10 14:46:54,Chatterbox Turbo (RunPod),neural-cloud-paid,True,6-8,You did such a great job speaking today! I loved how loud and clear your voice was.,laura,0.0588,4.003,0.539,2.438,0.0034,0.001328,83
17
- 2026-04-10 14:47:15,Chatterbox Turbo (RunPod),neural-cloud-paid,True,3-5,"""Let's try that one more time. Focus on making your voice go up and down so you don't sound like a robot. You've got this!""",chloe,0.08,4.11,0.431,3.279,0.005,0.001968,123
18
- 2026-04-10 14:47:40,Chatterbox Turbo (RunPod),neural-cloud-paid,True,9-12,"While your articulation was precise, the tone felt a bit overly formal for this specific prompt. Try to strike a balance between professional authority and conversational engagement",ethan,0.037,4.378,0.415,4.311,0.0054,0.002896,181
19
- 2026-04-10 14:48:28,Chatterbox Turbo (RunPod),neural-cloud-paid,True,K-2,That was a fantastic effort! I noticed how you paused at the periods to take a breath. It made your reading sound very natural and easy to follow.,lucy,0.1429,4.397,0.517,3.807,0.0056,0.002336,146
20
- 2026-04-10 14:53:17,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.517,0.188,1.137,0.0,0.001328,83
21
- 2026-04-10 14:54:04,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.518,0.067,0.403,0.0,0.001328,83
22
- 2026-04-10 14:54:28,Chatterbox Turbo (RunPod),neural-cloud-paid,True,6-8,"You are doing a great job. Next time, try to look at the ending of the words like 'ed' and 'ing' to make sure you’re saying the whole word clearly",madison,0.2333,4.314,0.53,4.093,0.00772,0.002336,146
23
- 2026-04-10 14:55:35,Parler-TTS (mini),neural-local,False,3-5,"""I am so impressed by your vocabulary today. Using words like 'energetic' and 'observant' really makes your speech stand out!",described:3-5,0.25,4.273,2.307,17.411,0.0,0.002,125
24
- 2026-04-10 14:56:02,Piper (ONNX),neural-local,False,9-12,"While your articulation was precise, the tone felt a bit overly formal for this specific prompt. Try to strike a balance between professional authority and conversational engagement.",en_US-lessac-medium,0.0741,3.901,0.04,0.377,0.0,0.002912,182
25
- 2026-04-10 14:56:23,Chatterbox Turbo (RunPod),neural-cloud-paid,True,9-12,"While your articulation was precise, the tone felt a bit overly formal for this specific prompt. Try to strike a balance between professional authority and conversational engagement.",ethan,0.0,4.403,0.403,3.962,0.00982,0.002912,182
26
- 2026-04-10 15:09:25,Chatterbox Turbo (RunPod),neural-cloud-paid,True,6-8,"Your ideas are very clear, but some of your words are blending together. Focus on 'crisp' consonants at the ends of your words—especially those 't' and 'd' sounds—to make sure everyone understands your points.",evelyn,0.2059,4.474,0.961,13.418,0.01396,0.003344,209
27
- 2026-04-10 15:56:11,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.515,0.247,1.493,0.0,0.001328,83
28
- 2026-04-10 15:58:27,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.515,0.153,0.925,0.0,0.001328,83
29
- 2026-04-10 16:07:16,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.515,0.199,1.202,0.0,0.001328,83
30
- 2026-04-10 16:14:10,Chatterbox Turbo (RunPod),neural-cloud-paid,True,9-12,"Your introduction really grabbed my attention. To make your middle section just as strong, try to use transitional words like 'furthermore' or 'on the other hand' to help your ideas flow together.",ethan,0.125,4.453,0.408,4.315,0.01058,0.003136,196
31
- 2026-04-10 16:16:20,pyttsx3 (baseline),rule-based-local,False,9-12,"Your introduction really grabbed my attention. To make your middle section just as strong, try to use transitional words like 'furthermore' or 'on the other hand' to help your ideas flow together.",SAPI5-default,0.125,3.843,0.018,0.22,0.0,0.003136,196
32
- 2026-04-10 16:17:06,Kokoro (tuned),neural-local,True,9-12,"Your introduction really grabbed my attention. To make your middle section just as strong, try to use transitional words like 'furthermore' or 'on the other hand' to help your ideas flow together.",af_heart,0.125,4.545,0.085,0.987,0.0,0.003136,196
33
- 2026-04-10 16:17:19,edge-tts (Microsoft),neural-cloud-free,False,9-12,"Your introduction really grabbed my attention. To make your middle section just as strong, try to use transitional words like 'furthermore' or 'on the other hand' to help your ideas flow together.",en-US-JennyNeural,0.0938,,0.076,0.803,0.0,0.003136,196
34
- 2026-04-10 16:42:29,edge-tts (Microsoft),neural-cloud-free,False,6-8,You did such a great job speaking today! I loved how loud and clear your voice was.,en-US-JennyNeural,0.0588,4.515,0.129,0.798,0.0,0.001328,83
35
- 2026-04-10 16:43:04,edge-tts (Microsoft),neural-cloud-free,False,6-8,Excellent work on your enunciation. Be mindful of your filler words—like 'um' and 'like'—as eliminating those will significantly increase the impact of your speech,en-US-JennyNeural,0.25,4.306,0.078,0.823,0.0,0.002608,163
36
- 2026-04-10 16:43:23,Kokoro (tuned),neural-local,True,6-8,Excellent work on your enunciation. Be mindful of your filler words—like 'um' and 'like'—as eliminating those will significantly increase the impact of your speech,af_heart,0.25,,0.122,1.302,0.0,0.002608,163
37
- 2026-04-10 16:43:54,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,,0.055,0.331,0.0,0.001328,83
38
- 2026-04-10 16:48:16,Chatterbox Turbo (RunPod),neural-cloud-paid,True,9-12,Excellent work on your enunciation. Be mindful of your filler words—like 'um' and 'like'—as eliminating those will significantly increase the impact of your speech,ethan,0.25,,2.544,24.774,0.00974,0.002608,163
39
- 2026-04-10 16:48:35,pyttsx3 (baseline),rule-based-local,False,9-12,Excellent work on your enunciation. Be mindful of your filler words—like 'um' and 'like'—as eliminating those will significantly increase the impact of your speech,SAPI5-default,0.25,,0.023,0.227,0.0,0.002608,163
40
- 2026-04-10 16:48:53,Piper (ONNX),neural-local,False,9-12,Excellent work on your enunciation. Be mindful of your filler words—like 'um' and 'like'—as eliminating those will significantly increase the impact of your speech,en_US-lessac-medium,0.2917,,0.042,0.376,0.0,0.002608,163
41
- 2026-04-10 16:49:48,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,,0.108,0.651,0.0,0.001328,83
42
- 2026-04-10 16:51:32,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.52,0.501,3.033,0.0,0.001328,83
43
- 2026-04-10 16:51:51,edge-tts (Microsoft),neural-cloud-free,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,en-US-JennyNeural,0.0588,4.515,0.108,0.665,0.0,0.001328,83
44
- 2026-04-10 16:52:35,edge-tts (Microsoft),neural-cloud-free,False,6-8,"""I noticed how you adjusted your pace during the presentation. That really helped the audience follow your main points. Great adjustment!""",en-US-JennyNeural,0.0952,4.504,0.151,1.398,0.0,0.002208,138
45
- 2026-04-10 16:53:37,Parler-TTS (mini),neural-local,False,9-12,"Your analysis of the character's motivation was quite sophisticated. To take this to the next level, consider how the historical context influenced those choices",described:9-12,0.0417,4.183,2.464,21.515,0.0,0.002576,161
46
- 2026-04-10 16:54:03,Piper (ONNX),neural-local,False,9-12,"Your analysis of the character's motivation was quite sophisticated. To take this to the next level, consider how the historical context influenced those choices",en_US-lessac-medium,0.0833,4.451,0.226,2.007,0.0,0.002576,161
47
- 2026-04-10 16:54:25,Chatterbox Turbo (RunPod),neural-cloud-paid,True,9-12,"Your analysis of the character's motivation was quite sophisticated. To take this to the next level, consider how the historical context influenced those choices",ethan,0.0417,4.429,0.414,3.601,0.0087,0.002576,161
48
- 2026-04-13 12:57:52,edge-tts (Microsoft),neural-cloud-free,False,6-8,You did such a great job speaking today! I loved how loud and clear your voice was.,en-US-JennyNeural,0.0588,4.515,0.247,1.526,0.0,0.001328,83
49
- 2026-04-13 12:58:46,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.515,0.492,2.978,0.0,0.001328,83
50
- 2026-04-13 15:55:10,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.516,0.509,3.08,0.0,0.001328,83
51
- 2026-04-13 15:55:17,edge-tts (Microsoft),neural-cloud-free,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,en-US-JennyNeural,0.0588,4.515,0.155,0.954,0.0,0.001328,83
52
- 2026-04-13 15:55:31,pyttsx3 (baseline),rule-based-local,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,SAPI5-default,0.0588,3.554,0.025,0.166,0.0,0.001328,83
53
- 2026-04-13 15:56:29,Parler-TTS (mini),neural-local,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,described:K-2,0.0588,4.209,4.379,23.997,0.0,0.001328,83
54
- 2026-04-13 15:56:50,Piper (ONNX),neural-local,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,en_US-amy-medium,0.0588,4.504,0.408,2.317,0.0,0.001328,83
55
- 2026-04-13 16:14:09,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.513,0.2,1.208,0.0,0.001328,83
56
- 2026-04-13 16:18:11,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.518,0.136,0.824,0.0,0.001328,83
57
- 2026-04-13 16:35:07,Parler-TTS (mini),neural-local,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,described:K-2,0.0588,4.301,44.085,237.486,0.0,0.001328,83
58
- 2026-04-13 18:21:33,ElevenLabs (Turbo),neural-cloud-paid,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,Jessica,0.0588,,,0.351,0.01245,0.001328,83
59
- 2026-04-13 18:22:04,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,,0.44,2.66,0.0,0.001328,83
60
- 2026-04-13 18:23:11,Piper (ONNX),neural-local,False,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,en_US-amy-medium,0.0588,,0.05,0.276,0.0,0.001328,83
61
- 2026-04-13 18:25:45,Kokoro (tuned),neural-local,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,af_heart,0.0588,4.517,0.328,1.986,0.0,0.001328,83
62
- 2026-04-13 18:25:58,ElevenLabs (Turbo),neural-cloud-paid,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,Jessica,0.0588,4.154,0.08,0.441,0.01245,0.001328,83
63
- 2026-04-13 18:26:18,ElevenLabs (Turbo),neural-cloud-paid,True,6-8,You did such a great job speaking today! I loved how loud and clear your voice was.,Sarah,0.0588,4.453,0.071,0.354,0.01245,0.001328,83
64
- 2026-04-13 18:26:29,ElevenLabs (Turbo),neural-cloud-paid,True,9-12,You did such a great job speaking today! I loved how loud and clear your voice was.,Brian,0.0588,3.848,0.075,0.351,0.01245,0.001328,83
65
- 2026-04-13 18:27:02,ElevenLabs (Turbo),neural-cloud-paid,True,9-12,"""Your analysis of the primary source was incredibly insightful today. You managed to connect the historical context to modern-day implications with real clarity. Next time, try to slow down during your transitions to let those big ideas really sink in for the audience.""",Brian,0.0465,4.151,0.052,0.746,0.0405,0.00432,270
66
- 2026-04-13 18:43:42,ElevenLabs (Turbo),neural-cloud-paid,True,K-2,You did such a great job speaking today! I loved how loud and clear your voice was.,Jessica,0.0588,4.197,0.084,0.461,0.01245,0.001328,83
67
- 2026-04-13 18:43:53,ElevenLabs (Turbo),neural-cloud-paid,True,9-12,"""You've made significant progress on your vocal projection. Your voice was steady and easy to follow throughout the entire presentation. To take this to the next level, experiment with varying your pitch to emphasize your most critical data points.""",Brian,0.0513,4.019,0.045,0.602,0.03735,0.003984,249
68
- 2026-04-13 18:45:27,ElevenLabs (Turbo),neural-cloud-paid,True,6-8,"""You've made significant progress on your vocal projection. Your voice was steady and easy to follow throughout the entire presentation. To take this to the next level, experiment with varying your pitch to emphasize your most critical data points.""",Sarah,0.0513,4.255,0.043,0.579,0.03735,0.003984,249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/storage.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app/storage.py
2
+ # Supabase Storage integration for persisting generated audio files.
3
+ # Uploads audio to the tts-audio public bucket and returns a public URL.
4
+ # Called as a background thread from run_synthesis — non-blocking.
5
+
6
+ import os
7
+ import threading
8
+ from pathlib import Path
9
+ from supabase import create_client, Client
10
+
11
+ # --- client setup ---
12
+ _client: Client | None = None
13
+
14
+ def _get_client() -> Client:
15
+ global _client
16
+ if _client is None:
17
+ url = os.getenv("SUPABASE_URL")
18
+ key = os.getenv("SUPABASE_ANON_KEY")
19
+ if not url or not key:
20
+ raise ValueError(
21
+ "SUPABASE_URL and SUPABASE_ANON_KEY must be set in .env"
22
+ )
23
+ _client = create_client(url, key)
24
+ return _client
25
+
26
+
27
+ def upload_audio(local_path: str, filename: str) -> str | None:
28
+ """
29
+ Upload an audio file to Supabase tts-audio bucket.
30
+ Returns the public URL on success, None on failure.
31
+
32
+ Args:
33
+ local_path: full path to local audio file
34
+ filename: destination filename in bucket (e.g. '2026-04-14_kokoro_K-2.wav')
35
+ """
36
+ try:
37
+ client = _get_client()
38
+ with open(local_path, "rb") as f:
39
+ data = f.read()
40
+
41
+ # detect content type
42
+ ext = Path(local_path).suffix.lower()
43
+ content_type = "audio/mpeg" if ext == ".mp3" else "audio/wav"
44
+
45
+ client.storage.from_("tts-audio").upload(
46
+ path=filename,
47
+ file=data,
48
+ file_options={"content-type": content_type, "upsert": "true"},
49
+ )
50
+
51
+ # build public URL
52
+ result = client.storage.from_("tts-audio").get_public_url(filename)
53
+ return result
54
+
55
+ except Exception as e:
56
+ print(f"[Storage] Upload failed for {filename}: {e}")
57
+ return None
58
+
59
+
60
+ def upload_audio_background(local_path: str, filename: str, callback=None) -> None:
61
+ """
62
+ Upload audio in a background thread — non-blocking.
63
+ Optionally calls callback(url) when done, where url is None on failure.
64
+
65
+ Args:
66
+ local_path: full path to local audio file
67
+ filename: destination filename in bucket
68
+ callback: optional function(url: str | None) called after upload
69
+ """
70
+ def _run():
71
+ url = upload_audio(local_path, filename)
72
+ if callback:
73
+ callback(url)
74
+
75
+ thread = threading.Thread(target=_run, daemon=True)
76
+ thread.start()
77
+
78
+ def upload_csv(local_path: str) -> bool:
79
+ """
80
+ Upload eval_log.csv to Supabase tts-audio bucket.
81
+ Uses upsert so it overwrites the existing file.
82
+ Returns True on success, False on failure.
83
+ """
84
+ try:
85
+ client = _get_client()
86
+ with open(local_path, "rb") as f:
87
+ data = f.read()
88
+
89
+ client.storage.from_("tts-audio").upload(
90
+ path="eval_log.csv",
91
+ file=data,
92
+ file_options={"content-type": "text/csv", "upsert": "true"},
93
+ )
94
+ print("[Storage] eval_log.csv uploaded to Supabase")
95
+ return True
96
+
97
+ except Exception as e:
98
+ print(f"[Storage] CSV upload failed: {e}")
99
+ return False
100
+
101
+
102
+ def download_csv(local_path: str) -> bool:
103
+ """
104
+ Download eval_log.csv from Supabase tts-audio bucket to local path.
105
+ Returns True on success, False on failure.
106
+ """
107
+ try:
108
+ client = _get_client()
109
+ response = client.storage.from_("tts-audio").download("eval_log.csv")
110
+
111
+ os.makedirs(os.path.dirname(local_path), exist_ok=True)
112
+ with open(local_path, "wb") as f:
113
+ f.write(response)
114
+
115
+ print("[Storage] eval_log.csv downloaded from Supabase")
116
+ return True
117
+
118
+ except Exception as e:
119
+ print(f"[Storage] CSV download failed (will use local fallback): {e}")
120
+ return False
121
+
122
+
123
+ def upload_csv_background(local_path: str) -> None:
124
+ """Upload CSV in background thread — non-blocking."""
125
+ thread = threading.Thread(target=upload_csv, args=(local_path,), daemon=True)
126
+ thread.start()
pyproject.toml CHANGED
@@ -16,7 +16,7 @@ dependencies = [
16
  "librosa",
17
  "ipywidgets",
18
  "nest-asyncio",
19
- "gradio",
20
  "jiwer",
21
  "faster-whisper",
22
  "torch>=2.6.0",
@@ -27,6 +27,7 @@ dependencies = [
27
  "python-dotenv>=1.2.2",
28
  "plotly>=6.7.0",
29
  "elevenlabs>=2.43.0",
 
30
  ]
31
 
32
  [tool.uv.sources]
 
16
  "librosa",
17
  "ipywidgets",
18
  "nest-asyncio",
19
+ "gradio>=6.12.0",
20
  "jiwer",
21
  "faster-whisper",
22
  "torch>=2.6.0",
 
27
  "python-dotenv>=1.2.2",
28
  "plotly>=6.7.0",
29
  "elevenlabs>=2.43.0",
30
+ "supabase>=2.28.3",
31
  ]
32
 
33
  [tool.uv.sources]
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
 
uv.lock CHANGED
The diff for this file is too large to render. See raw diff