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 |
|