canary_aed_streaming / app /ui_utils.py
Archime's picture
impl walkthrough
6f523af
raw
history blame
7.42 kB
from app.logger_config import logger as logging
import gradio as gr
from pathlib import Path
import os
DEFAULT_CONFIG = {
"task_type": "Transcription",
"lang_source": "French",
"lang_target": "English",
"chunk_secs": 1.0,
"left_context_secs": 20.0,
"right_context_secs": 0.5,
"streaming_policy": "waitk",
"alignatt_thr": 8,
"waitk_lagging": 2,
"exclude_sink_frames": 8,
"xatt_scores_layer": -2,
"hallucinations_detector": True,
}
EXAMPLE_CONFIGS = {
"data/english_meeting.wav": {
"task_type": "Transcription", "lang_source": "English", "lang_target": "English",
"chunk_secs": 1.0, "left_context_secs": 20.0, "right_context_secs": 0.5,
"streaming_policy": "waitk", "alignatt_thr": 8, "waitk_lagging": 2,
"exclude_sink_frames": 8, "xatt_scores_layer": -2, "hallucinations_detector": True
},
"data/french_news.wav": {
"task_type": "Transcription", "lang_source": "French", "lang_target": "English",
"chunk_secs": 1.0, "left_context_secs": 15.0, "right_context_secs": 0.3,
"streaming_policy": "alignatt", "alignatt_thr": 10, "waitk_lagging": 3,
"exclude_sink_frames": 6, "xatt_scores_layer": -1, "hallucinations_detector": True
},
"data/spanish_podcast.wav": {
"task_type": "Translation", "lang_source": "Spanish", "lang_target": "English",
"chunk_secs": 1.5, "left_context_secs": 25.0, "right_context_secs": 0.4,
"streaming_policy": "waitk", "alignatt_thr": 7, "waitk_lagging": 1,
"exclude_sink_frames": 8, "xatt_scores_layer": -2, "hallucinations_detector": False
}
}
SUPPORTED_LANGS_MAP = {
"Bulgarian": "bg", "Croatian": "hr", "Czech": "cs", "Danish": "da",
"Dutch": "nl", "English": "en", "Estonian": "et", "Finnish": "fi",
"French": "fr", "German": "de", "Greek": "el", "Hungarian": "hu",
"Italian": "it", "Latvian": "lv", "Lithuanian": "lt", "Maltese": "mt",
"Polish": "pl", "Portuguese": "pt", "Romanian": "ro", "Slovak": "sk",
"Slovenian": "sl", "Spanish": "es", "Swedish": "sv", "Russian": "ru", "Ukrainian": "uk"
}
# ========== FONCTIONS UTILITAIRES ==========
def to_updates(cfg):
"""Map dict -> gr.update list dans l'ordre des sorties."""
return [
gr.update(value=cfg["task_type"]),
gr.update(value=cfg["lang_source"]),
gr.update(
value=cfg["lang_target"],
visible=(cfg["task_type"] == "Translation")
),
gr.update(value=cfg["chunk_secs"]),
gr.update(value=cfg["left_context_secs"]),
gr.update(value=cfg["right_context_secs"]),
gr.update(value=cfg["streaming_policy"]),
gr.update(value=cfg["alignatt_thr"]),
gr.update(value=cfg["waitk_lagging"]),
gr.update(value=cfg["exclude_sink_frames"]),
gr.update(value=cfg["xatt_scores_layer"]),
gr.update(value=cfg["hallucinations_detector"]),
]
def apply_preset_if_example(filepath, auto_apply):
"""Si fichier = exemple ET auto_apply=True -> applique preset. Sinon, ne rien changer."""
logging.info(f"apply_preset_if_example {filepath} {auto_apply} ")
if not filepath or not auto_apply:
updates = [gr.update() for _ in range(12)]
updates.append(gr.update())
return tuple(updates)
# On compare uniquement le nom de fichier, pas le chemin complet
file_name = Path(filepath).name
# Recherche dans EXAMPLE_CONFIGS par nom de fichier
cfg = next(
(config for path, config in EXAMPLE_CONFIGS.items() if Path(path).name == file_name),
None
)
if not cfg:
updates = [gr.update() for _ in range(12)]
updates.append(gr.update())
return tuple(updates)
updates = to_updates(cfg)
updates.append(gr.update(value=f"Preset applied for: {file_name}"))
return tuple(updates)
def reset_to_defaults():
"""Réinitialise tous les champs aux valeurs par défaut."""
updates = to_updates(DEFAULT_CONFIG) # 12 champs
# Ajout du résumé (13e sortie)
updates.append(gr.update(value="Defaults restored."))
return tuple(updates)
def summarize_config(
task, src, tgt,
chunk, left, right,
policy, thr, lag, sink, xatt, halluc
):
txt = f"🧠 **Task:** {task}\n🌐 **Source language:** {src}"
if task == "Translation":
txt += f"\n🎯 **Target language:** {tgt}"
txt += (
f"\n\n### ⚙️ Advanced Parameters:\n"
f"- chunk_secs = {chunk}\n"
f"- left_context_secs = {left}\n"
f"- right_context_secs = {right}\n"
f"- decoding.streaming_policy = {policy}\n"
f"- decoding.alignatt_thr = {thr}\n"
f"- decoding.waitk_lagging = {lag}\n"
f"- decoding.exclude_sink_frames = {sink}\n"
f"- decoding.xatt_scores_layer = {xatt}\n"
f"- decoding.hallucinations_detector = {halluc}"
)
return txt
def handle_additional_outputs( progress_value):
"""
Update UI elements based on streaming progress or errors.
Controls button states, audio visibility, and progress slider.
"""
logging.debug(f"Additional output received: {progress_value}")
# ui_components = [start_button, stop_button,go_to_task, audio_source_step, status_slider]
# Handle structured error message
non_ok= (
gr.update(visible=True), # start_button enabled
gr.update(visible=False), # stop_button disabled
gr.update(visible=False), # go_to_task disabled
gr.update(interactive=True), # audio_source_step re-shown
gr.update(visible=False, value=0), # slider hidden
)
if isinstance(progress_value, dict) and progress_value.get("error"):
msg = progress_value.get("message", "Unknown error.")
logging.error(f"[stream_ui] Client-side error: {msg}")
return non_ok
try:
progress = float(progress_value)
except (ValueError, TypeError):
progress = 0
# --- Stream not started ---
if progress <= 0:
return non_ok
# --- Stream finished ---
if progress >= 100:
return non_ok
# --- Stream in progress ---
return (
gr.update(visible=False), # start_button disabled
gr.update(visible=True), # stop_button enabled
gr.update(visible=True), # go_to_task enabled
gr.update(interactive=False), # hide audio_source_step
gr.update(visible=True, value=progress), # show progress
)
def on_file_load(filepath):
"""
Update active audio path or reset".
"""
# Si un fichier est chargé (upload, micro, ou exemple),
# audio_path ne sera pas None.
is_visible = filepath is not None
return filepath, gr.update(visible=is_visible)
def get_custom_theme() :
# === Thème personnalisé (studio néon) ===
theme = gr.themes.Base(
primary_hue="blue",
secondary_hue="indigo",
).set(
body_background_fill="#F7F8FA",
body_text_color="#222222",
block_border_color="#D0D3D9",
button_primary_background_fill="#3B82F6",
button_primary_background_fill_hover="#2563EB",
button_primary_text_color="#FFFFFF",
)
css_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "assets", "custom_style.css")
with open(css_path, encoding="utf-8") as f:
css_style = f.read()
return theme, css_style