Spaces:
Running
Running
zach
commited on
Commit
·
84c63d1
1
Parent(s):
db2bd16
Update option state to include the voice along with the provider, and update reporting of winner to include the voice name.
Browse files- src/app.py +52 -26
src/app.py
CHANGED
|
@@ -41,6 +41,7 @@ from src.integrations import (
|
|
| 41 |
text_to_speech_with_hume,
|
| 42 |
)
|
| 43 |
from src.theme import CustomTheme
|
|
|
|
| 44 |
from src.utils import truncate_text, validate_prompt_length
|
| 45 |
|
| 46 |
|
|
@@ -124,10 +125,16 @@ def text_to_speech(prompt: str, text: str, generated_text_state: str) -> Tuple[g
|
|
| 124 |
voice_b, audio_b = future_audio_b.result()
|
| 125 |
|
| 126 |
logger.info(f'TTS generated: {provider_a}={len(audio_a)} bytes, {provider_b}={len(audio_b)} bytes')
|
| 127 |
-
options = [
|
|
|
|
|
|
|
|
|
|
| 128 |
random.shuffle(options)
|
| 129 |
option_a_audio, option_b_audio = options[0][0], options[1][0]
|
| 130 |
-
options_map = {
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
return (
|
| 133 |
gr.update(value=option_a_audio, visible=True, autoplay=True),
|
|
@@ -146,38 +153,57 @@ def text_to_speech(prompt: str, text: str, generated_text_state: str) -> Tuple[g
|
|
| 146 |
raise gr.Error('An unexpected error ocurred. Please try again later.')
|
| 147 |
|
| 148 |
|
| 149 |
-
def vote(vote_submitted: bool,
|
| 150 |
"""
|
| 151 |
Handles user voting.
|
| 152 |
|
| 153 |
Args:
|
| 154 |
-
vote_submitted (bool): True if a vote was already submitted
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
Returns:
|
| 159 |
A tuple of:
|
| 160 |
-
-
|
| 161 |
-
-
|
| 162 |
-
-
|
|
|
|
| 163 |
"""
|
| 164 |
-
if not
|
| 165 |
-
return gr.skip(), gr.skip(), gr.skip()
|
| 166 |
|
| 167 |
-
|
| 168 |
-
selected_option, other_option = (OPTION_A, OPTION_B) if
|
| 169 |
-
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
return (
|
| 173 |
True,
|
| 174 |
-
gr.update(value=
|
| 175 |
-
else gr.update(value=
|
| 176 |
-
gr.update(value=
|
| 177 |
-
else gr.update(value=
|
| 178 |
gr.update(interactive=True)
|
| 179 |
)
|
| 180 |
|
|
|
|
| 181 |
def reset_ui() -> Tuple[gr.update, gr.update, gr.update, gr.update, None, None, bool]:
|
| 182 |
"""
|
| 183 |
Resets UI state before generating new text.
|
|
@@ -188,7 +214,7 @@ def reset_ui() -> Tuple[gr.update, gr.update, gr.update, gr.update, None, None,
|
|
| 188 |
- option_b_audio_player (clear audio)
|
| 189 |
- vote_button_a (disable and reset button text)
|
| 190 |
- vote_button_a (disable and reset button text)
|
| 191 |
-
-
|
| 192 |
- option_b_audio_state (reset option B audio state)
|
| 193 |
- vote_submitted_state (reset submitted vote state)
|
| 194 |
"""
|
|
@@ -297,8 +323,8 @@ def build_gradio_interface() -> gr.Blocks:
|
|
| 297 |
# UI state components
|
| 298 |
generated_text_state = gr.State('') # Track generated text state
|
| 299 |
option_b_audio_state = gr.State() # Track generated audio for option B for playing automatically after option 1 audio finishes
|
| 300 |
-
|
| 301 |
-
vote_submitted_state = gr.State(False) # Track whether the user has voted
|
| 302 |
|
| 303 |
# --- Register event handlers ---
|
| 304 |
|
|
@@ -344,7 +370,7 @@ def build_gradio_interface() -> gr.Blocks:
|
|
| 344 |
option_b_audio_player,
|
| 345 |
vote_button_a,
|
| 346 |
vote_button_b,
|
| 347 |
-
|
| 348 |
option_b_audio_state,
|
| 349 |
vote_submitted_state,
|
| 350 |
],
|
|
@@ -354,7 +380,7 @@ def build_gradio_interface() -> gr.Blocks:
|
|
| 354 |
outputs=[
|
| 355 |
option_a_audio_player,
|
| 356 |
option_b_audio_player,
|
| 357 |
-
|
| 358 |
option_b_audio_state,
|
| 359 |
],
|
| 360 |
).then(
|
|
@@ -370,12 +396,12 @@ def build_gradio_interface() -> gr.Blocks:
|
|
| 370 |
# Vote button click event handlers
|
| 371 |
vote_button_a.click(
|
| 372 |
fn=vote,
|
| 373 |
-
inputs=[vote_submitted_state,
|
| 374 |
outputs=[vote_submitted_state, vote_button_a, vote_button_b, synthesize_speech_button],
|
| 375 |
)
|
| 376 |
vote_button_b.click(
|
| 377 |
fn=vote,
|
| 378 |
-
inputs=[vote_submitted_state,
|
| 379 |
outputs=[vote_submitted_state, vote_button_a, vote_button_b, synthesize_speech_button],
|
| 380 |
)
|
| 381 |
|
|
|
|
| 41 |
text_to_speech_with_hume,
|
| 42 |
)
|
| 43 |
from src.theme import CustomTheme
|
| 44 |
+
from src.types import OptionMap
|
| 45 |
from src.utils import truncate_text, validate_prompt_length
|
| 46 |
|
| 47 |
|
|
|
|
| 125 |
voice_b, audio_b = future_audio_b.result()
|
| 126 |
|
| 127 |
logger.info(f'TTS generated: {provider_a}={len(audio_a)} bytes, {provider_b}={len(audio_b)} bytes')
|
| 128 |
+
options = [
|
| 129 |
+
(audio_a, {"provider": provider_a, "voice": voice_a}),
|
| 130 |
+
(audio_b, {"provider": provider_b, "voice": voice_b})
|
| 131 |
+
]
|
| 132 |
random.shuffle(options)
|
| 133 |
option_a_audio, option_b_audio = options[0][0], options[1][0]
|
| 134 |
+
options_map: OptionMap = {
|
| 135 |
+
OPTION_A: options[0][1],
|
| 136 |
+
OPTION_B: options[1][1]
|
| 137 |
+
}
|
| 138 |
|
| 139 |
return (
|
| 140 |
gr.update(value=option_a_audio, visible=True, autoplay=True),
|
|
|
|
| 153 |
raise gr.Error('An unexpected error ocurred. Please try again later.')
|
| 154 |
|
| 155 |
|
| 156 |
+
def vote(vote_submitted: bool, option_map: OptionMap, selected_button: str) -> Tuple[bool, gr.update, gr.update, gr.update]:
|
| 157 |
"""
|
| 158 |
Handles user voting.
|
| 159 |
|
| 160 |
Args:
|
| 161 |
+
vote_submitted (bool): True if a vote was already submitted.
|
| 162 |
+
option_map (OptionMap): A dictionary mapping option labels to their details.
|
| 163 |
+
Expected structure:
|
| 164 |
+
{
|
| 165 |
+
'Option A': '{"provider": "Hume AI", "voice": "<voice_name>"}',
|
| 166 |
+
'Option B': '{"provider": "ElevenLabs", "voice": "<voice_name>"}'
|
| 167 |
+
}
|
| 168 |
+
selected_button (str): The button that was clicked.
|
| 169 |
|
| 170 |
Returns:
|
| 171 |
A tuple of:
|
| 172 |
+
- A boolean indicating if the vote was accepted.
|
| 173 |
+
- An update for the selected vote button (showing provider, voice, and trophy emoji).
|
| 174 |
+
- An update for the unselected vote button (showing provider and voice).
|
| 175 |
+
- An update for enabling vote interactions.
|
| 176 |
"""
|
| 177 |
+
if not option_map or vote_submitted:
|
| 178 |
+
return gr.skip(), gr.skip(), gr.skip(), gr.skip()
|
| 179 |
|
| 180 |
+
option_a_selected = selected_button == VOTE_FOR_OPTION_A
|
| 181 |
+
selected_option, other_option = (OPTION_A, OPTION_B) if option_a_selected else (OPTION_B, OPTION_A)
|
| 182 |
+
|
| 183 |
+
# Parse selected option details from options map
|
| 184 |
+
selected_details = option_map.get(selected_option, {})
|
| 185 |
+
selected_provider = selected_details.get('provider', UNKNOWN_PROVIDER)
|
| 186 |
+
selected_voice = selected_details.get('voice', '')
|
| 187 |
+
|
| 188 |
+
# Parse other option details from options map
|
| 189 |
+
other_details = option_map.get(other_option, {})
|
| 190 |
+
other_provider = other_details.get('provider', UNKNOWN_PROVIDER)
|
| 191 |
+
other_voice = other_details.get('voice', '')
|
| 192 |
+
|
| 193 |
+
# Build button labels, displaying the provider and voice name, appending the trophy emoji to the selected option.
|
| 194 |
+
selected_label = f"{selected_provider} | Voice: {selected_voice} {TROPHY_EMOJI}"
|
| 195 |
+
other_label = f"{other_provider} | Voice: {other_voice}"
|
| 196 |
|
| 197 |
return (
|
| 198 |
True,
|
| 199 |
+
gr.update(value=selected_label, variant='primary', interactive=False) if option_a_selected
|
| 200 |
+
else gr.update(value=other_label, variant='secondary', interactive=False),
|
| 201 |
+
gr.update(value=other_label, variant='secondary', interactive=False) if option_a_selected
|
| 202 |
+
else gr.update(value=selected_label, variant='primary', interactive=False),
|
| 203 |
gr.update(interactive=True)
|
| 204 |
)
|
| 205 |
|
| 206 |
+
|
| 207 |
def reset_ui() -> Tuple[gr.update, gr.update, gr.update, gr.update, None, None, bool]:
|
| 208 |
"""
|
| 209 |
Resets UI state before generating new text.
|
|
|
|
| 214 |
- option_b_audio_player (clear audio)
|
| 215 |
- vote_button_a (disable and reset button text)
|
| 216 |
- vote_button_a (disable and reset button text)
|
| 217 |
+
- option_map_state (reset option map state)
|
| 218 |
- option_b_audio_state (reset option B audio state)
|
| 219 |
- vote_submitted_state (reset submitted vote state)
|
| 220 |
"""
|
|
|
|
| 323 |
# UI state components
|
| 324 |
generated_text_state = gr.State('') # Track generated text state
|
| 325 |
option_b_audio_state = gr.State() # Track generated audio for option B for playing automatically after option 1 audio finishes
|
| 326 |
+
option_map_state = gr.State() # Track option map (option A and option B are randomized)
|
| 327 |
+
vote_submitted_state = gr.State(False) # Track whether the user has voted for an option
|
| 328 |
|
| 329 |
# --- Register event handlers ---
|
| 330 |
|
|
|
|
| 370 |
option_b_audio_player,
|
| 371 |
vote_button_a,
|
| 372 |
vote_button_b,
|
| 373 |
+
option_map_state,
|
| 374 |
option_b_audio_state,
|
| 375 |
vote_submitted_state,
|
| 376 |
],
|
|
|
|
| 380 |
outputs=[
|
| 381 |
option_a_audio_player,
|
| 382 |
option_b_audio_player,
|
| 383 |
+
option_map_state,
|
| 384 |
option_b_audio_state,
|
| 385 |
],
|
| 386 |
).then(
|
|
|
|
| 396 |
# Vote button click event handlers
|
| 397 |
vote_button_a.click(
|
| 398 |
fn=vote,
|
| 399 |
+
inputs=[vote_submitted_state, option_map_state, vote_button_a],
|
| 400 |
outputs=[vote_submitted_state, vote_button_a, vote_button_b, synthesize_speech_button],
|
| 401 |
)
|
| 402 |
vote_button_b.click(
|
| 403 |
fn=vote,
|
| 404 |
+
inputs=[vote_submitted_state, option_map_state, vote_button_b],
|
| 405 |
outputs=[vote_submitted_state, vote_button_a, vote_button_b, synthesize_speech_button],
|
| 406 |
)
|
| 407 |
|