Spaces:
Running
Running
zach
commited on
Commit
·
6506ee8
1
Parent(s):
2369fc5
Refactors app.py to progressively generate and cleans up code
Browse files- 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
|
38 |
"""
|
39 |
-
Generates text from Claude API
|
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'
|
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 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
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
|
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=
|
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 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
)
|
189 |
|
190 |
# Prompt input
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
)
|
199 |
|
200 |
# Generate Button
|
201 |
generate_button = gr.Button('Generate', variant='primary')
|
202 |
|
203 |
with gr.Column(variant='compact'):
|
204 |
-
#
|
205 |
-
|
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 |
-
#
|
215 |
with gr.Row():
|
216 |
-
|
217 |
-
|
218 |
-
vote_button_1 = gr.Button(VOTE_FOR_OPTION_ONE, interactive=False)
|
219 |
|
220 |
-
|
221 |
-
|
222 |
-
|
|
|
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=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
237 |
inputs=[prompt_input],
|
238 |
-
outputs=[
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
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(
|