# Standard Library Imports from typing import Tuple, Union # Third-Party Library Imports import gradio as gr # Local Application Imports from src.common import Config, logger from src.core import TTSService, VotingService from src.database import AsyncDBSessionMaker from .components import Arena, Leaderboard class Frontend: """ Main frontend class orchestrating the Gradio UI application. Initializes and manages the Arena and Leaderboard components, builds the overall UI structure (Tabs, HTML), and handles top-level events like tab selection. """ def __init__(self, config: Config, db_session_maker: AsyncDBSessionMaker): """ Initializes the Frontend application controller. Args: config: The application configuration object. db_session_maker: An asynchronous database session factory. """ self.config = config # Instantiate services self.tts_service: TTSService = TTSService(config) self.voting_service: VotingService = VotingService(db_session_maker) logger.debug("Frontend initialized with TTSService and VotingService.") # Initialize components with dependencies self.arena = Arena(config, self.tts_service, self.voting_service) self.leaderboard = Leaderboard(self.voting_service) logger.debug("Frontend initialized with Arena and Leaderboard components.") async def _handle_tab_select(self, evt: gr.SelectData) -> Tuple[ Union[dict, gr.skip], Union[dict, gr.skip], Union[dict, gr.skip], ]: """ Handles tab selection events. Refreshes leaderboard if its tab is selected. Args: evt: Gradio SelectData event, containing the selected tab's value (label). Returns: A tuple of Gradio update dictionaries for the leaderboard tables if the Leaderboard tab was selected and data needed refreshing, otherwise a tuple of gr.skip() objects. """ selected_tab = evt.value if selected_tab == "Leaderboard": # Refresh leaderboard, but don't force it (allow cache/throttle) return await self.leaderboard.refresh_leaderboard(force=False) # Return skip updates for other tabs return gr.skip(), gr.skip(), gr.skip() async def build_gradio_interface(self) -> gr.Blocks: """ Builds and configures the complete Gradio Blocks UI. Pre-loads initial leaderboard data, defines layout (HTML, Tabs), integrates Arena and Leaderboard sections, and sets up tab selection handler. Returns: The fully constructed Gradio Blocks application instance. """ logger.info("Building Gradio interface...") with gr.Blocks(title="Expressive TTS Arena", css_paths="static/css/styles.css") as demo: # --- Header HTML --- gr.HTML( value="""

Expressive TTS Arena

Join the community in evaluating text-to-speech models, and vote for the AI voice that best captures the emotion, nuance, and expressiveness of human speech.

""" ) # --- Tabs --- with gr.Tabs() as tabs: with gr.TabItem("Arena"): self.arena.build_arena_section() with gr.TabItem("Leaderboard"): ( leaderboard_table, battle_counts_table, win_rates_table ) = await self.leaderboard.build_leaderboard_section() # --- Top-level Event Handlers --- tabs.select( fn=self._handle_tab_select, inputs=[], outputs=[leaderboard_table, battle_counts_table, win_rates_table], ) logger.debug("Gradio interface built successfully") return demo