mramirez2001 commited on
Commit
efdc11e
verified
1 Parent(s): fb605fc

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -89
app.py CHANGED
@@ -11,57 +11,13 @@ import whisper
11
  import pandas as pd
12
  from gtts import gTTS
13
  import re
 
 
14
 
15
- # --- 0. CONFIGURACI脫N INICIAL ---
16
- try:
17
- client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
18
- api_key_found = True
19
- except TypeError:
20
- api_key_found = False
21
 
22
- print("Loading Whisper for transcription...")
23
- whisper_model = whisper.load_model("base", device="cpu")
24
- print("Whisper model loaded.")
25
-
26
-
27
- # --- 1. DEFINICI脫N DE PROMPTS PARA LA IA ---
28
- # (Tus prompts completos van aqu铆...)
29
- CONVERSATION_SYSTEM_PROMPT = """..."""
30
- FINAL_EVALUATION_SYSTEM_PROMPT = """..."""
31
- SENTENCE_EVALUATION_SYSTEM_PROMPT = """
32
- You are an expert English language examiner...
33
- JSON Output Structure:
34
- {
35
- "overall_score_100": integer,
36
- "cefr_level": "string",
37
- "holistic_feedback": { "strengths": "string", "areas_for_improvement": "string" },
38
- "word_by_word_analysis": [ { "reference_word": "string", "spoken_word": "string", "word_score_100": integer, "correct_ipa": "string", "feedback_en": "string", "feedback_es": "string" } ]
39
- }
40
- """
41
-
42
- # --- 2. FUNCIONES L脫GICAS ---
43
-
44
- def extract_word_level_features(audio_path):
45
- try:
46
- y, sr = librosa.load(audio_path, sr=16000)
47
- result = whisper_model.transcribe(audio_path, word_timestamps=True, fp16=False)
48
- if not result["segments"] or 'words' not in result["segments"][0]: return []
49
- word_segments = result["segments"][0]["words"]
50
- features_list = []
51
- for segment in word_segments:
52
- start_sample = int(segment['start'] * sr); end_sample = int(segment['end'] * sr)
53
- word_audio = y[start_sample:end_sample]
54
- rms_energy = np.mean(librosa.feature.rms(y=word_audio)) if len(word_audio) > 0 else 0
55
- features_list.append({"word": segment['word'].strip(), "start": round(segment['start'], 2), "end": round(segment['end'], 2), "energy": round(float(rms_energy), 4)})
56
- return features_list
57
- except Exception as e:
58
- print(f"Error during feature extraction: {e}"); return []
59
-
60
- def chat_interaction(audio_input, history_state):
61
- # (Tu funci贸n de chat sin cambios va aqu铆...)
62
- pass
63
-
64
- # --- CAMBIO: La funci贸n de evaluaci贸n de frase ahora devuelve MARKDOWN ---
65
  def run_sentence_evaluation(audio_input, reference_transcript):
66
  if not api_key_found: raise gr.Error("OpenAI API key not found.")
67
  if audio_input is None or not reference_transcript:
@@ -81,21 +37,26 @@ def run_sentence_evaluation(audio_input, reference_transcript):
81
  holistic_feedback_md = f"### Strengths\n{result['holistic_feedback']['strengths']}\n\n### Areas for Improvement\n{result['holistic_feedback']['areas_for_improvement']}"
82
  word_analysis_list = result['word_by_word_analysis']
83
 
84
- # --- NUEVA L脫GICA: Construir una tabla en Markdown ---
85
  md_table = "| Reference Word | Spoken Word | Score | Feedback (EN) | Feedback (ES) | Reference Audio |\n"
86
  md_table += "| :--- | :--- | :---: | :--- | :--- | :---: |\n"
87
 
88
- os.makedirs("reference_audio", exist_ok=True)
89
-
90
  for index, item in enumerate(word_analysis_list):
91
  word_to_speak = item['reference_word']
92
- safe_filename = re.sub(r'\W+', '', word_to_speak.lower())
93
- audio_path = f"reference_audio/{index}_{safe_filename}.mp3"
94
 
95
  try:
96
- tts = gTTS(text=word_to_speak, lang='en'); tts.save(audio_path)
97
- # Embeber el audio usando una etiqueta HTML <audio>
98
- audio_player = f'<audio src="file/{audio_path}" controls></audio>'
 
 
 
 
 
 
 
 
 
99
  except Exception as e:
100
  print(f"Error al generar TTS para '{word_to_speak}': {e}"); audio_player = "Error"
101
 
@@ -107,40 +68,20 @@ def run_sentence_evaluation(audio_input, reference_transcript):
107
  return 0, "Error", error_msg, ""
108
 
109
 
110
- # --- 3. INTERFAZ DE GRADIO CON PESTA脩AS (Con salida Markdown) ---
111
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
112
- gr.Markdown("# 馃嚞馃嚙 AI English Speaking Practice & Assessment")
113
- with gr.Tabs():
114
- # PESTA脩A 1: CHAT AI (sin cambios)
115
- with gr.TabItem("Pr谩ctica Conversacional (Chat AI)"):
116
- # ... (Aqu铆 va toda la definici贸n de la interfaz de tu chatbot, sin cambios)
117
- pass
118
-
119
- # PESTA脩A 2: EVALUACI脫N POR FRASE
120
  with gr.TabItem("Evaluaci贸n por Frase"):
121
- 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?"]
122
- gr.Markdown("Choose a tongue twister or write your own sentence...")
123
- tongue_twister_selector = gr.Dropdown(choices=TONGUE_TWISTERS, label="Or Choose a Tongue Twister to Practice")
124
- with gr.Row():
125
- with gr.Column(scale=1):
126
- audio_in_sentence = gr.Audio(sources=["microphone"], type="numpy", label="1. Record Your Voice")
127
- text_in_sentence = gr.Textbox(lines=3, label="2. Reference Sentence", value=TONGUE_TWISTERS[0])
128
- submit_btn_sentence = gr.Button("Get Assessment", variant="primary")
129
- with gr.Column(scale=2):
130
- gr.Markdown("### Assessment Summary")
131
- with gr.Row():
132
- score_out_sentence = gr.Number(label="Overall Score (0-100)", interactive=False)
133
- level_out_sentence = gr.Textbox(label="Estimated CEFR Level", interactive=False)
134
- holistic_feedback_out_sentence = gr.Markdown(label="Examiner's Feedback")
135
-
136
- gr.Markdown("--- \n ### Detailed Word-by-Word Analysis")
137
-
138
- # --- AJUSTE CLAVE: La salida ahora es un 煤nico componente Markdown ---
139
- word_analysis_out_sentence = gr.Markdown(label="Phonetic Breakdown")
140
-
141
- def update_text(choice): return gr.Textbox(value=choice)
142
- tongue_twister_selector.change(fn=update_text, inputs=tongue_twister_selector, outputs=text_in_sentence)
143
- 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])
144
 
145
  if __name__ == "__main__":
146
  if not api_key_found: print("\nFATAL: OpenAI API key not found.")
 
11
  import pandas as pd
12
  from gtts import gTTS
13
  import re
14
+ import io # Necesario para el buffer en memoria
15
+ import base64 # Necesario para la codificaci贸n
16
 
17
+ # --- (El resto de tu configuraci贸n y prompts se mantienen igual) ---
18
+ # ...
 
 
 
 
19
 
20
+ # --- CAMBIO: La funci贸n de evaluaci贸n ahora incrusta el audio en Base64 ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def run_sentence_evaluation(audio_input, reference_transcript):
22
  if not api_key_found: raise gr.Error("OpenAI API key not found.")
23
  if audio_input is None or not reference_transcript:
 
37
  holistic_feedback_md = f"### Strengths\n{result['holistic_feedback']['strengths']}\n\n### Areas for Improvement\n{result['holistic_feedback']['areas_for_improvement']}"
38
  word_analysis_list = result['word_by_word_analysis']
39
 
40
+ # --- NUEVA L脫GICA: Construir tabla en Markdown con audio Base64 ---
41
  md_table = "| Reference Word | Spoken Word | Score | Feedback (EN) | Feedback (ES) | Reference Audio |\n"
42
  md_table += "| :--- | :--- | :---: | :--- | :--- | :---: |\n"
43
 
 
 
44
  for index, item in enumerate(word_analysis_list):
45
  word_to_speak = item['reference_word']
 
 
46
 
47
  try:
48
+ # 1. Generar audio en un buffer en memoria (no en un archivo)
49
+ tts = gTTS(text=word_to_speak, lang='en')
50
+ mp3_fp = io.BytesIO()
51
+ tts.write_to_fp(mp3_fp)
52
+ mp3_fp.seek(0)
53
+
54
+ # 2. Codificar el audio en Base64
55
+ audio_base64 = base64.b64encode(mp3_fp.read()).decode('utf-8')
56
+
57
+ # 3. Crear una etiqueta de audio con los datos incrustados (Data URI)
58
+ audio_player = f'<audio src="data:audio/mpeg;base64,{audio_base64}" controls></audio>'
59
+
60
  except Exception as e:
61
  print(f"Error al generar TTS para '{word_to_speak}': {e}"); audio_player = "Error"
62
 
 
68
  return 0, "Error", error_msg, ""
69
 
70
 
71
+ # --- 3. INTERFAZ DE GRADIO CON PESTA脩AS (La definici贸n no cambia, pero ahora recibe Markdown) ---
72
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
73
+ # ... (Todo el resto de tu interfaz, incluyendo la Pesta帽a 1 y 2, se mantiene exactamente igual)
74
+ # ...
 
 
 
 
 
 
75
  with gr.TabItem("Evaluaci贸n por Frase"):
76
+ # ... (Toda la definici贸n de la interfaz de esta pesta帽a se mantiene igual)
77
+ # ...
78
+ word_analysis_out_sentence = gr.Markdown(label="Phonetic Breakdown") # <-- Esta salida ya est谩 lista para recibir la tabla Markdown
79
+ # ...
80
+ submit_btn_sentence.click(
81
+ fn=run_sentence_evaluation,
82
+ inputs=[audio_in_sentence, text_in_sentence],
83
+ outputs=[score_out_sentence, level_out_sentence, holistic_feedback_out_sentence, word_analysis_out_sentence]
84
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  if __name__ == "__main__":
87
  if not api_key_found: print("\nFATAL: OpenAI API key not found.")