zach commited on
Commit
afdac46
·
1 Parent(s): ca266e0

Add UI for voting

Browse files
Files changed (1) hide show
  1. src/app.py +125 -47
src/app.py CHANGED
@@ -16,8 +16,20 @@ import random
16
  import gradio as gr
17
  # Local Application Imports
18
  from src.config import logger
19
- from src.constants import PROMPT_MAX_LENGTH, PROMPT_MIN_LENGTH, SAMPLE_PROMPTS
20
- from src.integrations import generate_text_with_claude, text_to_speech_with_hume, text_to_speech_with_elevenlabs
 
 
 
 
 
 
 
 
 
 
 
 
21
  from src.utils import truncate_text, validate_prompt_length
22
 
23
 
@@ -51,19 +63,18 @@ def process_prompt(prompt: str):
51
  )
52
 
53
  logger.info(
54
- f'TTS generated: Hume={len(hume_audio)} bytes, '
55
- f'ElevenLabs={len(elevenlabs_audio)} bytes'
56
  )
57
 
58
  # Randomize audio order
59
- options = [(hume_audio, 'Hume TTS'), (elevenlabs_audio, 'ElevenLabs TTS')]
60
  random.shuffle(options)
61
 
62
  return (
63
  generated_text,
64
- options[0][0], # Option 1 audio
65
- options[1][0], # Option 2 audio
66
- {'Option 1': options[0][1], 'Option 2': options[1][1]}, # Mapping
67
  )
68
 
69
  except ValueError as ve:
@@ -90,12 +101,14 @@ def run_process_prompt(prompt: str):
90
  """
91
  # Disable UI, clear previous outputs
92
  yield (
93
- gr.update(interactive=False),
94
- gr.update(value=None),
95
- gr.update(value=None),
96
- gr.update(value=None),
97
- gr.update(value=None),
98
- None,
 
 
99
  )
100
 
101
  # Process the prompt
@@ -103,12 +116,45 @@ def run_process_prompt(prompt: str):
103
 
104
  # Display generated text and audio
105
  yield (
106
- gr.update(interactive=True),
107
- gr.update(value=generated_text),
108
- gr.update(value=option1_audio, autoplay=True),
109
- gr.update(value=option2_audio),
110
- gr.update(value=option_mapping),
111
- option2_audio,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  )
113
 
114
 
@@ -121,44 +167,55 @@ def build_gradio_interface() -> gr.Blocks:
121
  """
122
  with gr.Blocks() as demo:
123
  # Title and instructions
124
- gr.Markdown('# TTS Arena')
125
  gr.Markdown(
126
  'Generate text using **Claude by Anthropic**, then compare text-to-speech outputs '
127
- 'from **Hume TTS API** and **ElevenLabs TTS API**.'
128
  )
129
 
130
- # Input: Sample prompt selection & textbox
131
- with gr.Row():
132
- sample_prompt_dropdown = gr.Dropdown(
133
- choices=list(SAMPLE_PROMPTS.keys()),
134
- label='Choose a sample prompt (or enter your own)',
135
- value=None,
136
- interactive=True,
137
- )
138
-
139
- with gr.Row():
140
- prompt_input = gr.Textbox(
141
- label='Enter your prompt',
142
- placeholder='Or type your own...',
143
- lines=2,
144
- max_lines=2,
145
- )
 
 
 
146
 
147
  # Generate Button
148
- generate_button = gr.Button('Generate')
149
 
150
- # Output: Text & audio
151
- with gr.Column():
152
  output_text = gr.Textbox(
153
  label='Generated Text',
154
  interactive=False,
155
- lines=8,
156
- max_lines=12,
 
 
157
  )
158
 
 
159
  with gr.Row():
160
- option1_audio_player = gr.Audio(label='Option 1', type='filepath', interactive=False)
161
- option2_audio_player = gr.Audio(label='Option 2', type='filepath', interactive=False)
 
 
 
 
 
162
 
163
  # UI state components
164
  option_mapping_state = gr.State()
@@ -180,21 +237,42 @@ def build_gradio_interface() -> gr.Blocks:
180
  option1_audio_player,
181
  option2_audio_player,
182
  option_mapping_state,
 
 
183
  option2_audio_state,
184
  ],
185
  )
186
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  # Auto-play second audio after first finishes
188
  option1_audio_player.stop(
189
- fn=lambda _: gr.update(value=None), # Reset audio so Gradio autoplays it when set
190
  inputs=[],
191
  outputs=[option2_audio_player],
192
  ).then(
193
- fn=lambda audio: gr.update(value=audio, autoplay=True), # Set audio for playback
194
  inputs=[option2_audio_state],
195
  outputs=[option2_audio_player],
196
  )
197
 
 
 
 
 
 
 
 
198
  logger.debug('Gradio interface built successfully')
199
  return demo
200
 
 
16
  import gradio as gr
17
  # Local Application Imports
18
  from src.config import logger
19
+ from src.constants import (
20
+ OPTION_ONE,
21
+ OPTION_TWO,
22
+ VOTE_FOR_OPTION_ONE,
23
+ VOTE_FOR_OPTION_TWO,
24
+ PROMPT_MAX_LENGTH,
25
+ PROMPT_MIN_LENGTH,
26
+ SAMPLE_PROMPTS
27
+ )
28
+ from src.integrations import (
29
+ generate_text_with_claude,
30
+ text_to_speech_with_hume,
31
+ text_to_speech_with_elevenlabs
32
+ )
33
  from src.utils import truncate_text, validate_prompt_length
34
 
35
 
 
63
  )
64
 
65
  logger.info(
66
+ f'TTS generated: Hume={len(hume_audio)} bytes, ElevenLabs={len(elevenlabs_audio)} bytes'
 
67
  )
68
 
69
  # Randomize audio order
70
+ options = [(hume_audio, 'Hume AI'), (elevenlabs_audio, 'ElevenLabs')]
71
  random.shuffle(options)
72
 
73
  return (
74
  generated_text,
75
+ options[0][0], # Option 1 audio
76
+ options[1][0], # Option 2 audio
77
+ {OPTION_ONE: options[0][1], OPTION_TWO: options[1][1]}, # Option mapping
78
  )
79
 
80
  except ValueError as ve:
 
101
  """
102
  # Disable UI, clear previous outputs
103
  yield (
104
+ gr.update(interactive=False, variant='secondary'), # Disable Generate Button
105
+ gr.update(value=None), # Clear generated text
106
+ gr.update(value=None), # Clear Option 1 audio
107
+ gr.update(value=None), # Clear Option 2 audio
108
+ None, # Clear option mapping
109
+ gr.update(interactive=False, value=VOTE_FOR_OPTION_ONE, variant="secondary"), # Reset vote button 1
110
+ gr.update(interactive=False, value=VOTE_FOR_OPTION_TWO, variant="secondary"), # Reset vote button 2
111
+ None, # Reset Option 2 audio state
112
  )
113
 
114
  # Process the prompt
 
116
 
117
  # Display generated text and audio
118
  yield (
119
+ gr.update(interactive=True), # Re-enable Generate Button
120
+ gr.update(value=generated_text), # Show generated text
121
+ gr.update(value=option1_audio, autoplay=True), # Set Option 1 audio
122
+ gr.update(value=option2_audio), # Set Option 2 audio
123
+ option_mapping, # Store option mapping
124
+ gr.update(), # Keep Vote button 1 disabled
125
+ gr.update(), # Keep Vote button 2 disabled
126
+ option2_audio, # Store Option 2 audio
127
+ )
128
+
129
+
130
+ def vote(option_mapping: dict, selected_button: str):
131
+ """
132
+ Updates both vote buttons to reflect the user's choice.
133
+
134
+ Args:
135
+ option_mapping (dict): Maps "Option 1" and "Option 2" to their TTS providers.
136
+ selected_button (str): The label of the button that was clicked.
137
+
138
+ Returns:
139
+ tuple[gr.update, gr.update]: Updated properties for both vote buttons.
140
+ """
141
+ if not option_mapping:
142
+ return gr.update(), gr.update() # No updates if mapping is missing
143
+
144
+ # Determine which option was clicked
145
+ is_option_1 = selected_button == VOTE_FOR_OPTION_ONE
146
+ selected_option = OPTION_ONE if is_option_1 else OPTION_TWO
147
+ other_option = OPTION_TWO if is_option_1 else OPTION_ONE
148
+
149
+ # Get provider names
150
+ selected_provider = option_mapping.get(selected_option, 'Unknown')
151
+ other_provider = option_mapping.get(other_option, 'Unknown')
152
+
153
+ # Return updated button states
154
+ return (
155
+ gr.update(value=f'{selected_provider} ✔', interactive=False, variant='primary') if is_option_1 else gr.update(value=other_provider, interactive=False, variant='secondary'),
156
+ gr.update(value=other_provider, interactive=False, variant='secondary') if is_option_1 else gr.update(value=f'{selected_provider} ✔', interactive=False, variant='primary'),
157
+ gr.update(variant='primary')
158
  )
159
 
160
 
 
167
  """
168
  with gr.Blocks() as demo:
169
  # Title and instructions
170
+ gr.Markdown('# Expressive TTS Arena')
171
  gr.Markdown(
172
  'Generate text using **Claude by Anthropic**, then compare text-to-speech outputs '
173
+ 'from **Hume AI** and **ElevenLabs**. Listen to both samples and vote for your favorite!'
174
  )
175
 
176
+ with gr.Column(variant="compact"):
177
+ # Sample prompt select
178
+ with gr.Row():
179
+ sample_prompt_dropdown = gr.Dropdown(
180
+ choices=list(SAMPLE_PROMPTS.keys()),
181
+ label='Choose a sample prompt (or enter your own)',
182
+ value=None,
183
+ interactive=True,
184
+ )
185
+
186
+ # Prompt input
187
+ with gr.Row():
188
+ prompt_input = gr.Textbox(
189
+ label='Enter your prompt',
190
+ placeholder='Or type your own...',
191
+ lines=2,
192
+ max_lines=2,
193
+ show_copy_button=True,
194
+ )
195
 
196
  # Generate Button
197
+ generate_button = gr.Button('Generate', variant="primary")
198
 
199
+ with gr.Column(variant="compact"):
200
+ # Output text
201
  output_text = gr.Textbox(
202
  label='Generated Text',
203
  interactive=False,
204
+ autoscroll=False,
205
+ lines=5,
206
+ max_lines=5,
207
+ show_copy_button=True,
208
  )
209
 
210
+ # Output audio
211
  with gr.Row():
212
+ with gr.Column():
213
+ option1_audio_player = gr.Audio(label=OPTION_ONE, type='filepath', interactive=False)
214
+ vote_button_1 = gr.Button(VOTE_FOR_OPTION_ONE, interactive=False)
215
+
216
+ with gr.Column():
217
+ option2_audio_player = gr.Audio(label=OPTION_TWO, type='filepath', interactive=False)
218
+ vote_button_2 = gr.Button(VOTE_FOR_OPTION_TWO, interactive=False)
219
 
220
  # UI state components
221
  option_mapping_state = gr.State()
 
237
  option1_audio_player,
238
  option2_audio_player,
239
  option_mapping_state,
240
+ vote_button_1,
241
+ vote_button_2,
242
  option2_audio_state,
243
  ],
244
  )
245
 
246
+ vote_button_1.click(
247
+ fn=vote,
248
+ inputs=[option_mapping_state, vote_button_1],
249
+ outputs=[vote_button_1, vote_button_2, generate_button]
250
+ )
251
+
252
+ vote_button_2.click(
253
+ fn=vote,
254
+ inputs=[option_mapping_state, vote_button_2],
255
+ outputs=[vote_button_1, vote_button_2, generate_button]
256
+ )
257
+
258
  # Auto-play second audio after first finishes
259
  option1_audio_player.stop(
260
+ fn=lambda _: gr.update(value=None),
261
  inputs=[],
262
  outputs=[option2_audio_player],
263
  ).then(
264
+ fn=lambda audio: gr.update(value=audio, autoplay=True),
265
  inputs=[option2_audio_state],
266
  outputs=[option2_audio_player],
267
  )
268
 
269
+ # Enable voting after 2nd audio option playback finishes
270
+ option2_audio_player.stop(
271
+ fn=lambda _: (gr.update(interactive=True), gr.update(interactive=True)),
272
+ inputs=[],
273
+ outputs=[vote_button_1, vote_button_2],
274
+ )
275
+
276
  logger.debug('Gradio interface built successfully')
277
  return demo
278