zach commited on
Commit
6506ee8
·
1 Parent(s): 2369fc5

Refactors app.py to progressively generate and cleans up code

Browse files
Files changed (1) hide show
  1. src/app.py +70 -95
src/app.py CHANGED
@@ -34,27 +34,31 @@ from src.theme import CustomTheme
34
  from src.utils import truncate_text, validate_prompt_length
35
 
36
 
37
- def process_prompt(prompt: str):
38
  """
39
- Generates text from Claude API and converts it to speech using Hume and ElevenLabs.
40
 
41
  Args:
42
  prompt (str): User-provided text prompt.
43
-
44
- Returns:
45
- tuple: Generated text, two audio file paths (Hume & ElevenLabs), and
46
- a dictionary mapping audio options to providers.
47
  """
48
- logger.info(f'Processing prompt: {truncate_text(prompt, max_length=100)}')
49
-
50
  try:
51
  # Validate prompt length
52
  validate_prompt_length(prompt, PROMPT_MAX_LENGTH, PROMPT_MIN_LENGTH)
53
 
 
54
  # Generate text
55
  generated_text = generate_text_with_claude(prompt)
56
  logger.info(f'Generated text ({len(generated_text)} characters).')
57
 
 
 
 
 
 
 
 
 
58
  # Generate TTS output in parallel
59
  with ThreadPoolExecutor(max_workers=2) as executor:
60
  hume_audio, elevenlabs_audio = executor.map(
@@ -71,61 +75,24 @@ def process_prompt(prompt: str):
71
  options = [(hume_audio, 'Hume AI'), (elevenlabs_audio, 'ElevenLabs')]
72
  random.shuffle(options)
73
 
 
 
 
 
 
 
 
 
74
  return (
75
- generated_text,
76
- options[0][0], # Option 1 audio
77
- options[1][0], # Option 2 audio
78
- {OPTION_ONE: options[0][1], OPTION_TWO: options[1][1]}, # Option mapping
79
  )
80
 
81
- except ValueError as ve:
82
- logger.warning(f'Validation error: {ve}')
83
- return str(ve), None, None, {}
84
-
85
  except Exception as e:
86
  logger.error(f'Unexpected error: {e}')
87
- return 'An error occurred. Please try again.', None, None, {}
88
-
89
-
90
- def run_process_prompt(prompt: str):
91
- """
92
- Manages UI state while processing a prompt.
93
-
94
- Args:
95
- prompt (str): User input prompt.
96
-
97
- Yields:
98
- tuple: UI state updates in three stages:
99
- 1. Disables UI and clears previous outputs.
100
- 2. Displays generated content.
101
- 3. Re-enables UI after processing.
102
- """
103
- # Disable UI, clear previous outputs
104
- yield (
105
- gr.update(interactive=False, variant='secondary'), # Disable Generate Button
106
- gr.update(value="Generating..."), # Clear generated text
107
- gr.update(value=None), # Clear Option 1 audio
108
- gr.update(value=None), # Clear Option 2 audio
109
- None, # Clear option mapping
110
- gr.update(interactive=False, value=VOTE_FOR_OPTION_ONE, variant='secondary'), # Reset vote button 1
111
- gr.update(interactive=False, value=VOTE_FOR_OPTION_TWO, variant='secondary'), # Reset vote button 2
112
- None, # Reset Option 2 audio state
113
- )
114
-
115
- # Process the prompt
116
- generated_text, option1_audio, option2_audio, option_mapping = process_prompt(prompt)
117
-
118
- # Display generated text and audio
119
- yield (
120
- gr.update(interactive=True), # Re-enable Generate Button
121
- gr.update(value=generated_text), # Show generated text
122
- gr.update(value=option1_audio, autoplay=True), # Set Option 1 audio
123
- gr.update(value=option2_audio), # Set Option 2 audio
124
- option_mapping, # Store option mapping
125
- gr.update(), # Keep Vote button 1 disabled
126
- gr.update(), # Keep Vote button 2 disabled
127
- option2_audio, # Store Option 2 audio
128
- )
129
 
130
 
131
  def vote(option_mapping: dict, selected_button: str):
@@ -155,7 +122,7 @@ def vote(option_mapping: dict, selected_button: str):
155
  return (
156
  gr.update(value=f'{selected_provider} ✔', interactive=False, variant='primary') if is_option_1 else gr.update(value=other_provider, interactive=False, variant='secondary'),
157
  gr.update(value=other_provider, interactive=False, variant='secondary') if is_option_1 else gr.update(value=f'{selected_provider} ✔', interactive=False, variant='primary'),
158
- gr.update(variant='primary')
159
  )
160
 
161
 
@@ -167,7 +134,7 @@ def build_gradio_interface() -> gr.Blocks:
167
  gr.Blocks: The Gradio UI layout.
168
  """
169
  custom_theme = CustomTheme()
170
- with gr.Blocks(title="Expressive TTS Arena", theme=custom_theme) as demo:
171
  # Title
172
  gr.Markdown('# Expressive TTS Arena')
173
 
@@ -179,30 +146,28 @@ def build_gradio_interface() -> gr.Blocks:
179
  )
180
 
181
  # Sample prompt select
182
- with gr.Row():
183
- sample_prompt_dropdown = gr.Dropdown(
184
- choices=list(SAMPLE_PROMPTS.keys()),
185
- label='Choose a sample prompt (or enter your own)',
186
- value=None,
187
- interactive=True,
188
- )
189
 
190
  # Prompt input
191
- with gr.Row():
192
- prompt_input = gr.Textbox(
193
- label='Enter your prompt',
194
- placeholder='Or type your own...',
195
- lines=2,
196
- max_lines=2,
197
- show_copy_button=True,
198
- )
199
 
200
  # Generate Button
201
  generate_button = gr.Button('Generate', variant='primary')
202
 
203
  with gr.Column(variant='compact'):
204
- # Output text
205
- output_text = gr.Textbox(
206
  label='Generated Text',
207
  interactive=False,
208
  autoscroll=False,
@@ -211,19 +176,20 @@ def build_gradio_interface() -> gr.Blocks:
211
  show_copy_button=True,
212
  )
213
 
214
- # Output audio
215
  with gr.Row():
216
- with gr.Column():
217
- option1_audio_player = gr.Audio(label=OPTION_ONE, type='filepath', interactive=False)
218
- vote_button_1 = gr.Button(VOTE_FOR_OPTION_ONE, interactive=False)
219
 
220
- with gr.Column():
221
- option2_audio_player = gr.Audio(label=OPTION_TWO, type='filepath', interactive=False)
222
- vote_button_2 = gr.Button(VOTE_FOR_OPTION_TWO, interactive=False)
 
223
 
224
  # UI state components
225
  option_mapping_state = gr.State()
226
  option2_audio_state = gr.State()
 
227
 
228
  # Event handlers
229
  sample_prompt_dropdown.change(
@@ -233,18 +199,27 @@ def build_gradio_interface() -> gr.Blocks:
233
  )
234
 
235
  generate_button.click(
236
- fn=run_process_prompt,
 
 
 
 
 
 
 
 
 
 
237
  inputs=[prompt_input],
238
- outputs=[
239
- generate_button,
240
- output_text,
241
- option1_audio_player,
242
- option2_audio_player,
243
- option_mapping_state,
244
- vote_button_1,
245
- vote_button_2,
246
- option2_audio_state,
247
- ],
248
  )
249
 
250
  vote_button_1.click(
 
34
  from src.utils import truncate_text, validate_prompt_length
35
 
36
 
37
+ def generate_text(prompt: str):
38
  """
39
+ Generates text from Claude API.
40
 
41
  Args:
42
  prompt (str): User-provided text prompt.
 
 
 
 
43
  """
44
+ logger.info(f'Generating text with prompt: {truncate_text(prompt, max_length=100)}')
 
45
  try:
46
  # Validate prompt length
47
  validate_prompt_length(prompt, PROMPT_MAX_LENGTH, PROMPT_MIN_LENGTH)
48
 
49
+
50
  # Generate text
51
  generated_text = generate_text_with_claude(prompt)
52
  logger.info(f'Generated text ({len(generated_text)} characters).')
53
 
54
+ return gr.update(value=generated_text)
55
+
56
+ except ValueError as ve:
57
+ logger.warning(f'Validation error: {ve}')
58
+ return str(ve)
59
+
60
+ def text_to_speech(prompt: str, generated_text: str):
61
+ try:
62
  # Generate TTS output in parallel
63
  with ThreadPoolExecutor(max_workers=2) as executor:
64
  hume_audio, elevenlabs_audio = executor.map(
 
75
  options = [(hume_audio, 'Hume AI'), (elevenlabs_audio, 'ElevenLabs')]
76
  random.shuffle(options)
77
 
78
+ option_1_audio = options[0][0]
79
+ option_2_audio = options[1][0]
80
+
81
+ option_1_provider = options[0][1]
82
+ option_2_provider = options[1][1]
83
+
84
+ options_map = { OPTION_ONE: option_1_provider, OPTION_TWO: option_2_provider }
85
+
86
  return (
87
+ gr.update(value=option_1_audio, autoplay=True), # Set option 1 audio
88
+ gr.update(value=option_2_audio), # Option 2 audio
89
+ options_map, # Set option mapping state
90
+ option_2_audio # Set option 2 audio state
91
  )
92
 
 
 
 
 
93
  except Exception as e:
94
  logger.error(f'Unexpected error: {e}')
95
+ return None, None, {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
 
98
  def vote(option_mapping: dict, selected_button: str):
 
122
  return (
123
  gr.update(value=f'{selected_provider} ✔', interactive=False, variant='primary') if is_option_1 else gr.update(value=other_provider, interactive=False, variant='secondary'),
124
  gr.update(value=other_provider, interactive=False, variant='secondary') if is_option_1 else gr.update(value=f'{selected_provider} ✔', interactive=False, variant='primary'),
125
+ gr.update(interactive=True, variant='primary')
126
  )
127
 
128
 
 
134
  gr.Blocks: The Gradio UI layout.
135
  """
136
  custom_theme = CustomTheme()
137
+ with gr.Blocks(title='Expressive TTS Arena', theme=custom_theme) as demo:
138
  # Title
139
  gr.Markdown('# Expressive TTS Arena')
140
 
 
146
  )
147
 
148
  # Sample prompt select
149
+ sample_prompt_dropdown = gr.Dropdown(
150
+ choices=list(SAMPLE_PROMPTS.keys()),
151
+ label='Choose a sample prompt (or enter your own)',
152
+ value=None,
153
+ interactive=True,
154
+ )
 
155
 
156
  # Prompt input
157
+ prompt_input = gr.Textbox(
158
+ label='Enter your prompt',
159
+ placeholder='Or type your own...',
160
+ lines=2,
161
+ max_lines=2,
162
+ show_copy_button=True,
163
+ )
 
164
 
165
  # Generate Button
166
  generate_button = gr.Button('Generate', variant='primary')
167
 
168
  with gr.Column(variant='compact'):
169
+ # Generated text
170
+ generated_text = gr.Textbox(
171
  label='Generated Text',
172
  interactive=False,
173
  autoscroll=False,
 
176
  show_copy_button=True,
177
  )
178
 
179
+ # Audio players
180
  with gr.Row():
181
+ option1_audio_player = gr.Audio(label=OPTION_ONE, type='filepath', interactive=False)
182
+ option2_audio_player = gr.Audio(label=OPTION_TWO, type='filepath', interactive=False)
 
183
 
184
+ # Vote buttons
185
+ with gr.Row():
186
+ vote_button_1 = gr.Button(VOTE_FOR_OPTION_ONE, interactive=False)
187
+ vote_button_2 = gr.Button(VOTE_FOR_OPTION_TWO, interactive=False)
188
 
189
  # UI state components
190
  option_mapping_state = gr.State()
191
  option2_audio_state = gr.State()
192
+ generated_text_state = gr.State()
193
 
194
  # Event handlers
195
  sample_prompt_dropdown.change(
 
199
  )
200
 
201
  generate_button.click(
202
+ fn=lambda _: (
203
+ gr.update(interactive=False),
204
+ gr.update(interactive=False, value=VOTE_FOR_OPTION_ONE, variant='secondary'),
205
+ gr.update(interactive=False, value=VOTE_FOR_OPTION_TWO, variant='secondary'),
206
+ None,
207
+ None,
208
+ ),
209
+ inputs=[],
210
+ outputs=[generate_button, vote_button_1, vote_button_2, option_mapping_state, option2_audio_state]
211
+ ).then(
212
+ fn=generate_text,
213
  inputs=[prompt_input],
214
+ outputs=[generated_text]
215
+ ).then(
216
+ fn=text_to_speech,
217
+ inputs=[prompt_input, generated_text],
218
+ outputs=[option1_audio_player, option2_audio_player, option_mapping_state, option2_audio_state]
219
+ ).then(
220
+ fn=lambda _: gr.update(interactive=True, variation='primary'),
221
+ inputs=[],
222
+ outputs=[generate_button]
 
223
  )
224
 
225
  vote_button_1.click(