Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
CHANGED
|
@@ -10,6 +10,7 @@ import soundfile as sf
|
|
| 10 |
import whisper
|
| 11 |
import pandas as pd
|
| 12 |
from gtts import gTTS
|
|
|
|
| 13 |
|
| 14 |
# --- 0. CONFIGURACI脫N INICIAL ---
|
| 15 |
try:
|
|
@@ -19,12 +20,12 @@ except TypeError:
|
|
| 19 |
api_key_found = False
|
| 20 |
|
| 21 |
print("Loading Whisper for transcription...")
|
|
|
|
| 22 |
whisper_model = whisper.load_model("base", device="cpu")
|
| 23 |
print("Whisper model loaded.")
|
| 24 |
|
| 25 |
|
| 26 |
# --- 1. DEFINICI脫N DE PROMPTS PARA LA IA ---
|
| 27 |
-
# (Estos son los prompts completos y correctos para cada funci贸n)
|
| 28 |
|
| 29 |
CONVERSATION_SYSTEM_PROMPT = """
|
| 30 |
You are a friendly and encouraging English language tutor named Alex.
|
|
@@ -122,30 +123,29 @@ def run_sentence_evaluation(audio_input, reference_transcript):
|
|
| 122 |
result = json.loads(response.choices[0].message.content)
|
| 123 |
holistic_feedback_md = f"### Strengths\n{result['holistic_feedback']['strengths']}\n\n### Areas for Improvement\n{result['holistic_feedback']['areas_for_improvement']}"
|
| 124 |
word_analysis_df = pd.DataFrame(result['word_by_word_analysis'])
|
| 125 |
-
|
| 126 |
reference_audio_paths = []
|
|
|
|
| 127 |
for index, row in word_analysis_df.iterrows():
|
| 128 |
-
word_to_speak = row['reference_word']; audio_path = f"reference_audio/{index}_{
|
| 129 |
try:
|
| 130 |
tts = gTTS(text=word_to_speak, lang='en'); tts.save(audio_path); reference_audio_paths.append(audio_path)
|
| 131 |
-
except Exception:
|
|
|
|
| 132 |
word_analysis_df['reference_audio'] = reference_audio_paths
|
| 133 |
df_for_display = word_analysis_df[['reference_word', 'spoken_word', 'word_score_100', 'feedback_en', 'feedback_es', 'reference_audio']]
|
| 134 |
-
return (result.get("overall_score_100", 0), result.get("cefr_level", "N/A"), holistic_feedback_md,
|
| 135 |
except (json.JSONDecodeError, KeyError) as e:
|
| 136 |
print(f"Error processing API response: {e}"); error_msg = "The API response was not in the expected format."
|
| 137 |
return 0, "Error", error_msg, None
|
| 138 |
|
| 139 |
|
| 140 |
-
# --- 3. INTERFAZ DE GRADIO CON PESTA脩AS
|
| 141 |
-
|
| 142 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 143 |
gr.Markdown("# 馃嚞馃嚙 AI English Speaking Practice & Assessment")
|
| 144 |
-
|
| 145 |
with gr.Tabs():
|
| 146 |
-
# --- PESTA脩A 1: CHAT AI
|
| 147 |
with gr.TabItem("Pr谩ctica Conversacional (Chat AI)"):
|
| 148 |
-
# ... (todo el c贸digo de la interfaz del chatbot se mantiene igual)
|
| 149 |
with gr.Row():
|
| 150 |
with gr.Column(scale=2):
|
| 151 |
chatbot = gr.Chatbot(value=[(None, "Hi there! I'm Alex. How are you doing today?")], label="Conversation with your AI Tutor")
|
|
@@ -155,12 +155,11 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 155 |
history = gr.State([])
|
| 156 |
audio_in_chat.stop_recording(fn=chat_interaction, inputs=[audio_in_chat, history], outputs=[chatbot, history, feedback_en_out, feedback_es_out])
|
| 157 |
|
| 158 |
-
# --- PESTA脩A 2: EVALUACI脫N POR FRASE
|
| 159 |
with gr.TabItem("Evaluaci贸n por Frase"):
|
| 160 |
TONGUE_TWISTERS = ["Peter Piper picked a peck of pickled peppers.", "She sells seashells by the seashore.", "How much wood would a woodchuck chuck if a woodchuck could chuck wood?", "Betty Botter bought some butter but she said the butter鈥檚 bitter.", "A proper copper coffee pot."]
|
| 161 |
gr.Markdown("Choose a tongue twister or write your own sentence. Record yourself, and our AI examiner will provide a detailed diagnostic report.")
|
| 162 |
tongue_twister_selector = gr.Dropdown(choices=TONGUE_TWISTERS, label="Or Choose a Tongue Twister to Practice")
|
| 163 |
-
|
| 164 |
with gr.Row():
|
| 165 |
with gr.Column(scale=1):
|
| 166 |
audio_in_sentence = gr.Audio(sources=["microphone"], type="numpy", label="1. Record Your Voice")
|
|
@@ -172,26 +171,11 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
|
| 172 |
score_out_sentence = gr.Number(label="Overall Score (0-100)", interactive=False)
|
| 173 |
level_out_sentence = gr.Textbox(label="Estimated CEFR Level", interactive=False)
|
| 174 |
holistic_feedback_out_sentence = gr.Markdown(label="Examiner's Feedback")
|
| 175 |
-
|
| 176 |
gr.Markdown("--- \n ### Detailed Word-by-Word Analysis")
|
| 177 |
-
|
| 178 |
-
# --- AJUSTE CLAVE: Especificamos el tipo de dato para cada columna ---
|
| 179 |
-
word_analysis_out_sentence = gr.DataFrame(
|
| 180 |
-
headers=["Reference Word", "Spoken Word", "Score (0-100)", "Feedback (English)", "Feedback (Espa帽ol)", "Reference Audio"],
|
| 181 |
-
# Le decimos a Gradio que la 煤ltima columna es de tipo 'audio'
|
| 182 |
-
datatype=["str", "str", "number", "str", "str", "audio"],
|
| 183 |
-
label="Phonetic Breakdown",
|
| 184 |
-
wrap=True
|
| 185 |
-
)
|
| 186 |
-
|
| 187 |
def update_text(choice): return gr.Textbox(value=choice)
|
| 188 |
tongue_twister_selector.change(fn=update_text, inputs=tongue_twister_selector, outputs=text_in_sentence)
|
| 189 |
-
|
| 190 |
-
submit_btn_sentence.click(
|
| 191 |
-
fn=run_sentence_evaluation,
|
| 192 |
-
inputs=[audio_in_sentence, text_in_sentence],
|
| 193 |
-
outputs=[score_out_sentence, level_out_sentence, holistic_feedback_out_sentence, word_analysis_out_sentence]
|
| 194 |
-
)
|
| 195 |
|
| 196 |
if __name__ == "__main__":
|
| 197 |
if not api_key_found: print("\nFATAL: OpenAI API key not found.")
|
|
|
|
| 10 |
import whisper
|
| 11 |
import pandas as pd
|
| 12 |
from gtts import gTTS
|
| 13 |
+
import re # Necesario para limpiar nombres de archivo
|
| 14 |
|
| 15 |
# --- 0. CONFIGURACI脫N INICIAL ---
|
| 16 |
try:
|
|
|
|
| 20 |
api_key_found = False
|
| 21 |
|
| 22 |
print("Loading Whisper for transcription...")
|
| 23 |
+
# Usamos el modelo 'base' que es un buen compromiso entre velocidad y precisi贸n
|
| 24 |
whisper_model = whisper.load_model("base", device="cpu")
|
| 25 |
print("Whisper model loaded.")
|
| 26 |
|
| 27 |
|
| 28 |
# --- 1. DEFINICI脫N DE PROMPTS PARA LA IA ---
|
|
|
|
| 29 |
|
| 30 |
CONVERSATION_SYSTEM_PROMPT = """
|
| 31 |
You are a friendly and encouraging English language tutor named Alex.
|
|
|
|
| 123 |
result = json.loads(response.choices[0].message.content)
|
| 124 |
holistic_feedback_md = f"### Strengths\n{result['holistic_feedback']['strengths']}\n\n### Areas for Improvement\n{result['holistic_feedback']['areas_for_improvement']}"
|
| 125 |
word_analysis_df = pd.DataFrame(result['word_by_word_analysis'])
|
| 126 |
+
print("Generando audios de referencia...")
|
| 127 |
reference_audio_paths = []
|
| 128 |
+
os.makedirs("reference_audio", exist_ok=True)
|
| 129 |
for index, row in word_analysis_df.iterrows():
|
| 130 |
+
word_to_speak = row['reference_word']; safe_filename = re.sub(r'\W+', '', word_to_speak.lower()); audio_path = f"reference_audio/{index}_{safe_filename}.mp3"
|
| 131 |
try:
|
| 132 |
tts = gTTS(text=word_to_speak, lang='en'); tts.save(audio_path); reference_audio_paths.append(audio_path)
|
| 133 |
+
except Exception as e:
|
| 134 |
+
print(f"Error al generar TTS para '{word_to_speak}': {e}"); reference_audio_paths.append(None)
|
| 135 |
word_analysis_df['reference_audio'] = reference_audio_paths
|
| 136 |
df_for_display = word_analysis_df[['reference_word', 'spoken_word', 'word_score_100', 'feedback_en', 'feedback_es', 'reference_audio']]
|
| 137 |
+
return (result.get("overall_score_100", 0), result.get("cefr_level", "N/A"), holistic_feedback_md, df_for_display)
|
| 138 |
except (json.JSONDecodeError, KeyError) as e:
|
| 139 |
print(f"Error processing API response: {e}"); error_msg = "The API response was not in the expected format."
|
| 140 |
return 0, "Error", error_msg, None
|
| 141 |
|
| 142 |
|
| 143 |
+
# --- 3. INTERFAZ DE GRADIO CON PESTA脩AS ---
|
|
|
|
| 144 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 145 |
gr.Markdown("# 馃嚞馃嚙 AI English Speaking Practice & Assessment")
|
|
|
|
| 146 |
with gr.Tabs():
|
| 147 |
+
# --- PESTA脩A 1: CHAT AI ---
|
| 148 |
with gr.TabItem("Pr谩ctica Conversacional (Chat AI)"):
|
|
|
|
| 149 |
with gr.Row():
|
| 150 |
with gr.Column(scale=2):
|
| 151 |
chatbot = gr.Chatbot(value=[(None, "Hi there! I'm Alex. How are you doing today?")], label="Conversation with your AI Tutor")
|
|
|
|
| 155 |
history = gr.State([])
|
| 156 |
audio_in_chat.stop_recording(fn=chat_interaction, inputs=[audio_in_chat, history], outputs=[chatbot, history, feedback_en_out, feedback_es_out])
|
| 157 |
|
| 158 |
+
# --- PESTA脩A 2: EVALUACI脫N POR FRASE ---
|
| 159 |
with gr.TabItem("Evaluaci贸n por Frase"):
|
| 160 |
TONGUE_TWISTERS = ["Peter Piper picked a peck of pickled peppers.", "She sells seashells by the seashore.", "How much wood would a woodchuck chuck if a woodchuck could chuck wood?", "Betty Botter bought some butter but she said the butter鈥檚 bitter.", "A proper copper coffee pot."]
|
| 161 |
gr.Markdown("Choose a tongue twister or write your own sentence. Record yourself, and our AI examiner will provide a detailed diagnostic report.")
|
| 162 |
tongue_twister_selector = gr.Dropdown(choices=TONGUE_TWISTERS, label="Or Choose a Tongue Twister to Practice")
|
|
|
|
| 163 |
with gr.Row():
|
| 164 |
with gr.Column(scale=1):
|
| 165 |
audio_in_sentence = gr.Audio(sources=["microphone"], type="numpy", label="1. Record Your Voice")
|
|
|
|
| 171 |
score_out_sentence = gr.Number(label="Overall Score (0-100)", interactive=False)
|
| 172 |
level_out_sentence = gr.Textbox(label="Estimated CEFR Level", interactive=False)
|
| 173 |
holistic_feedback_out_sentence = gr.Markdown(label="Examiner's Feedback")
|
|
|
|
| 174 |
gr.Markdown("--- \n ### Detailed Word-by-Word Analysis")
|
| 175 |
+
word_analysis_out_sentence = gr.DataFrame(headers=["Reference Word", "Spoken Word", "Score (0-100)", "Feedback (English)", "Feedback (Espa帽ol)", "Reference Audio"], datatype=["str", "str", "number", "str", "str", "filepath"], label="Phonetic Breakdown", wrap=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
def update_text(choice): return gr.Textbox(value=choice)
|
| 177 |
tongue_twister_selector.change(fn=update_text, inputs=tongue_twister_selector, outputs=text_in_sentence)
|
| 178 |
+
submit_btn_sentence.click(fn=run_sentence_evaluation, inputs=[audio_in_sentence, text_in_sentence], outputs=[score_out_sentence, level_out_sentence, holistic_feedback_out_sentence, word_analysis_out_sentence])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
if __name__ == "__main__":
|
| 181 |
if not api_key_found: print("\nFATAL: OpenAI API key not found.")
|