Spaces:
Running
Running
zach
commited on
Commit
·
ec0cbf8
1
Parent(s):
fcb34bb
Add additional validation logic for prompt and generated text to prevent calling APIs unless valid and displaying the error for user feedback.
Browse files- src/app.py +37 -21
- src/utils.py +2 -2
src/app.py
CHANGED
@@ -37,35 +37,46 @@ from src.theme import CustomTheme
|
|
37 |
from src.utils import truncate_text, validate_prompt_length
|
38 |
|
39 |
|
40 |
-
def
|
41 |
"""
|
42 |
-
|
|
|
|
|
43 |
|
44 |
Args:
|
45 |
-
prompt (str):
|
46 |
|
47 |
Returns:
|
48 |
-
Union[str, gr.update]
|
49 |
-
|
|
|
50 |
"""
|
51 |
-
logger.info(f'Generating text with prompt: {truncate_text(prompt, max_length=100)}')
|
52 |
try:
|
53 |
-
#
|
54 |
-
|
|
|
|
|
55 |
|
56 |
-
|
57 |
generated_text = generate_text_with_claude(prompt)
|
|
|
|
|
|
|
58 |
logger.info(f'Generated text ({len(generated_text)} characters).')
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
-
return gr.update(value=generated_text)
|
61 |
-
|
62 |
-
except ValueError as ve:
|
63 |
-
logger.warning(f'Validation error: {ve}')
|
64 |
-
return str(ve)
|
65 |
|
66 |
def text_to_speech(prompt: str, generated_text: str) -> tuple[gr.update, gr.update, dict, str | None]:
|
67 |
"""
|
68 |
Converts generated text to speech using Hume AI and ElevenLabs APIs.
|
|
|
|
|
|
|
69 |
|
70 |
Args:
|
71 |
prompt (str): The original user-provided prompt.
|
@@ -78,6 +89,10 @@ def text_to_speech(prompt: str, generated_text: str) -> tuple[gr.update, gr.upda
|
|
78 |
- `options_map`: A dictionary mapping OPTION_ONE and OPTION_TWO to their providers.
|
79 |
- `option_2_audio`: The second audio file path or `None` if an error occurs.
|
80 |
"""
|
|
|
|
|
|
|
|
|
81 |
try:
|
82 |
# Generate TTS output in parallel
|
83 |
with ThreadPoolExecutor(max_workers=2) as executor:
|
@@ -111,8 +126,8 @@ def text_to_speech(prompt: str, generated_text: str) -> tuple[gr.update, gr.upda
|
|
111 |
)
|
112 |
|
113 |
except Exception as e:
|
114 |
-
logger.error(f'Unexpected error: {e}')
|
115 |
-
return
|
116 |
|
117 |
|
118 |
def vote(
|
@@ -202,6 +217,7 @@ def build_gradio_interface() -> gr.Blocks:
|
|
202 |
autoscroll=False,
|
203 |
lines=5,
|
204 |
max_lines=5,
|
|
|
205 |
show_copy_button=True,
|
206 |
)
|
207 |
|
@@ -256,14 +272,14 @@ def build_gradio_interface() -> gr.Blocks:
|
|
256 |
vote_submitted_state
|
257 |
]
|
258 |
).then(
|
259 |
-
#
|
260 |
-
fn=
|
261 |
inputs=[prompt_input],
|
262 |
-
outputs=[generated_text]
|
263 |
).then(
|
264 |
-
#
|
265 |
fn=text_to_speech,
|
266 |
-
inputs=[prompt_input, generated_text],
|
267 |
outputs=[
|
268 |
option1_audio_player,
|
269 |
option2_audio_player,
|
|
|
37 |
from src.utils import truncate_text, validate_prompt_length
|
38 |
|
39 |
|
40 |
+
def validate_and_generate_text(prompt: str) -> tuple[Union[str, gr.update], gr.update]:
|
41 |
"""
|
42 |
+
Validates the prompt before generating text.
|
43 |
+
- If valid, returns the generated text and keeps the button disabled.
|
44 |
+
- If invalid, returns an error message and re-enables the button.
|
45 |
|
46 |
Args:
|
47 |
+
prompt (str): The user-provided text prompt.
|
48 |
|
49 |
Returns:
|
50 |
+
tuple[Union[str, gr.update], gr.update]:
|
51 |
+
- The generated text or an error message.
|
52 |
+
- The updated state of the "Generate" button.
|
53 |
"""
|
|
|
54 |
try:
|
55 |
+
validate_prompt_length(prompt, PROMPT_MAX_LENGTH, PROMPT_MIN_LENGTH) # Raises error if invalid
|
56 |
+
except ValueError as ve:
|
57 |
+
logger.warning(f'Validation error: {ve}')
|
58 |
+
return str(ve), gr.update(interactive=True) # Show error, re-enable button
|
59 |
|
60 |
+
try:
|
61 |
generated_text = generate_text_with_claude(prompt)
|
62 |
+
if not generated_text:
|
63 |
+
raise ValueError("Claude API returned an empty response.")
|
64 |
+
|
65 |
logger.info(f'Generated text ({len(generated_text)} characters).')
|
66 |
+
return gr.update(value=generated_text), gr.update(interactive=False) # Keep button disabled
|
67 |
+
|
68 |
+
except Exception as e:
|
69 |
+
logger.error(f'Error while generating text with Claude API: {e}')
|
70 |
+
return "Error: Failed to generate text. Please try again.", gr.update(interactive=True) # Re-enable button
|
71 |
+
|
72 |
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
def text_to_speech(prompt: str, generated_text: str) -> tuple[gr.update, gr.update, dict, str | None]:
|
75 |
"""
|
76 |
Converts generated text to speech using Hume AI and ElevenLabs APIs.
|
77 |
+
|
78 |
+
If the generated text is invalid (empty or an error message), this function
|
79 |
+
does nothing and returns `None` values to prevent TTS from running.
|
80 |
|
81 |
Args:
|
82 |
prompt (str): The original user-provided prompt.
|
|
|
89 |
- `options_map`: A dictionary mapping OPTION_ONE and OPTION_TWO to their providers.
|
90 |
- `option_2_audio`: The second audio file path or `None` if an error occurs.
|
91 |
"""
|
92 |
+
if not generated_text or generated_text.startswith("Error:"):
|
93 |
+
logger.warning("Skipping TTS generation due to invalid text.")
|
94 |
+
return gr.update(value=None), gr.update(value=None), {}, None # Return empty updates
|
95 |
+
|
96 |
try:
|
97 |
# Generate TTS output in parallel
|
98 |
with ThreadPoolExecutor(max_workers=2) as executor:
|
|
|
126 |
)
|
127 |
|
128 |
except Exception as e:
|
129 |
+
logger.error(f'Unexpected error during TTS generation: {e}')
|
130 |
+
return gr.update(), gr.update(), {}, None
|
131 |
|
132 |
|
133 |
def vote(
|
|
|
217 |
autoscroll=False,
|
218 |
lines=5,
|
219 |
max_lines=5,
|
220 |
+
max_length=PROMPT_MAX_LENGTH,
|
221 |
show_copy_button=True,
|
222 |
)
|
223 |
|
|
|
272 |
vote_submitted_state
|
273 |
]
|
274 |
).then(
|
275 |
+
# Validate and prompt and generate text
|
276 |
+
fn=validate_and_generate_text,
|
277 |
inputs=[prompt_input],
|
278 |
+
outputs=[generated_text, generate_button] # Ensure button gets re-enabled on failure
|
279 |
).then(
|
280 |
+
# Validate generated text and synthesize text-to-speech
|
281 |
fn=text_to_speech,
|
282 |
+
inputs=[prompt_input, generated_text], # Pass prompt & generated text
|
283 |
outputs=[
|
284 |
option1_audio_player,
|
285 |
option2_audio_player,
|
src/utils.py
CHANGED
@@ -107,14 +107,14 @@ def validate_prompt_length(prompt: str, max_length: int, min_length: int) -> Non
|
|
107 |
# Check if prompt is too short
|
108 |
if prompt_length < min_length:
|
109 |
raise ValueError(
|
110 |
-
f'
|
111 |
f'Received only {prompt_length}.'
|
112 |
)
|
113 |
|
114 |
# Check if prompt exceeds max length
|
115 |
if prompt_length > max_length:
|
116 |
raise ValueError(
|
117 |
-
f'
|
118 |
f'Your prompt contains {prompt_length} characters.'
|
119 |
)
|
120 |
|
|
|
107 |
# Check if prompt is too short
|
108 |
if prompt_length < min_length:
|
109 |
raise ValueError(
|
110 |
+
f'Error: prompt must be at least {min_length} character(s) long. '
|
111 |
f'Received only {prompt_length}.'
|
112 |
)
|
113 |
|
114 |
# Check if prompt exceeds max length
|
115 |
if prompt_length > max_length:
|
116 |
raise ValueError(
|
117 |
+
f'Error: the prompt exceeds the maximum allowed length of {max_length} characters. '
|
118 |
f'Your prompt contains {prompt_length} characters.'
|
119 |
)
|
120 |
|