mramirez2001 commited on
Commit
fd30b83
verified
1 Parent(s): 396f257

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -124
app.py CHANGED
@@ -18,175 +18,136 @@ except TypeError:
18
  api_key_found = False
19
 
20
  print("Loading Whisper for transcription...")
21
- # Usamos el modelo 'base' que es un buen compromiso entre velocidad y precisi贸n
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
 
28
- # Prompt para mantener la conversaci贸n
29
- CONVERSATION_SYSTEM_PROMPT = """
30
- You are a friendly and encouraging English language tutor named Alex.
31
- A student will speak to you. Your task is to keep a natural, simple conversation going.
32
- 1. Briefly analyze the user's previous response to estimate their CEFR level (A1, A2, B1, etc.).
33
- 2. Formulate a simple, open-ended follow-up question that is appropriate for THAT estimated level.
34
- 3. Your entire response must be a single, short paragraph in natural, conversational English. DO NOT use JSON.
35
- """
36
-
37
- # Prompt para la evaluaci贸n final
38
- FINAL_EVALUATION_SYSTEM_PROMPT = """
39
- You are an expert English language examiner providing a final report. Analyze the entire conversation history provided.
40
 
41
- Your task is to return a single, valid JSON object with the following structure. Do not include any text outside this JSON object.
 
42
 
 
 
 
43
  JSON Output Structure:
44
  {
45
- "cefr_level": "string (e.g., A2, B1)",
46
- "feedback_en": {
47
- "strengths": "string (A paragraph summarizing the student's strong points in pronunciation, vocabulary, and fluency.)",
48
- "areas_for_improvement": "string (A paragraph detailing the main patterns of error and what to focus on.)",
49
- "word_by_word_feedback": [
50
- {"word": "string", "feedback": "string (Specific phonetic or usage feedback.)"}
51
- ]
52
  },
53
- "feedback_es": {
54
- "fortalezas": "string (Un p谩rrafo resumiendo los puntos fuertes del estudiante en pronunciaci贸n, vocabulario y fluidez.)",
55
- "areas_a_mejorar": "string (Un p谩rrafo detallando los patrones de error principales y en qu茅 enfocarse.)",
56
- "feedback_por_palabra": [
57
- {"palabra": "string", "feedback": "string (Retroalimentaci贸n fon茅tica o de uso espec铆fica.)"}
58
- ]
59
- }
 
 
60
  }
61
  """
62
 
63
 
64
  # --- 2. FUNCIONES L脫GICAS ---
65
 
66
- def transcribe_audio(audio_input):
67
- """Transcribe el audio usando la API de Whisper de OpenAI."""
68
- if audio_input is None:
69
- return ""
 
 
 
 
 
 
 
 
70
  sr, y = audio_input
71
  temp_audio_path = "temp_audio.wav"
72
  sf.write(temp_audio_path, y, sr)
73
- with open(temp_audio_path, "rb") as audio_file:
74
- transcript = client.audio.transcriptions.create(
75
- model="whisper-1",
76
- file=audio_file
77
- ).text
78
- return transcript
79
-
80
- def chat_interaction(audio_input, history_state):
81
- """
82
- Gestiona una vuelta de la conversaci贸n.
83
- """
84
- if not api_key_found: raise gr.Error("OpenAI API key not found.")
85
- if audio_input is None: return history_state, history_state, "", ""
86
 
87
- # 1. Transcribir el audio del usuario
88
- user_text = transcribe_audio(audio_input)
89
 
90
- # 2. Actualizar el historial con el mensaje del usuario
91
- history_state.append({"role": "user", "content": user_text})
 
 
 
 
 
 
92
 
93
- # Formatear para el chatbot de Gradio
94
- chat_display = []
95
- for i, msg in enumerate(history_state):
96
- if msg['role'] == 'user':
97
- chat_display.append((msg['content'], None))
98
- elif msg['role'] == 'assistant':
99
- if chat_display and chat_display[-1][1] is None:
100
- chat_display[-1] = (chat_display[-1][0], msg['content'])
101
-
102
- # 3. Decidir si continuar la conversaci贸n o dar el reporte final
103
- if len(history_state) < 9: # 1 system + 4 pares de user/assistant
104
- # --- Continuar conversaci贸n ---
105
- messages_to_send = [{"role": "system", "content": CONVERSATION_SYSTEM_PROMPT}] + history_state
106
-
107
- response = client.chat.completions.create(
108
- model="gpt-4o",
109
- messages=messages_to_send,
110
- temperature=0.7
111
- )
112
- ai_response = response.choices[0].message.content
113
- history_state.append({"role": "assistant", "content": ai_response})
114
- chat_display[-1] = (chat_display[-1][0], ai_response)
115
-
116
- return chat_display, history_state, gr.Markdown(visible=False), gr.Markdown(visible=False)
117
 
118
- else:
119
- # --- Generar evaluaci贸n final ---
120
- print("Generating final evaluation...")
121
- messages_to_send = [{"role": "system", "content": FINAL_EVALUATION_SYSTEM_PROMPT}] + history_state
122
-
123
- response = client.chat.completions.create(
124
- model="gpt-4o",
125
- response_format={"type": "json_object"},
126
- messages=messages_to_send
127
- )
128
-
129
- try:
130
- result = json.loads(response.choices[0].message.content)
131
-
132
- # Formatear el feedback en Ingl茅s
133
- fb_en = result.get('feedback_en', {})
134
- md_en = f"## Final Report (CEFR Level: {result.get('cefr_level', 'N/A')})\n"
135
- md_en += f"### Strengths\n{fb_en.get('strengths', '')}\n"
136
- md_en += f"### Areas for Improvement\n{fb_en.get('areas_for_improvement', '')}\n"
137
- md_en += "### Word-by-Word Feedback\n"
138
- for item in fb_en.get('word_by_word_feedback', []):
139
- md_en += f"- **{item['word']}**: {item['feedback']}\n"
140
-
141
- # Formatear el feedback en Espa帽ol
142
- fb_es = result.get('feedback_es', {})
143
- md_es = f"## Reporte Final (Nivel MCERL: {result.get('cefr_level', 'N/A')})\n"
144
- md_es += f"### Fortalezas\n{fb_es.get('fortalezas', '')}\n"
145
- md_es += f"### 脕reas a Mejorar\n{fb_es.get('areas_a_mejorar', '')}\n"
146
- md_es += "### Retroalimentaci贸n por Palabra\n"
147
- for item in fb_es.get('feedback_por_palabra', []):
148
- md_es += f"- **{item['palabra']}**: {item['feedback']}\n"
149
-
150
- # Mensaje final para el chat
151
- final_message = "Thank you for the conversation! Here is your final report."
152
- chat_display[-1] = (chat_display[-1][0], final_message)
153
-
154
- return chat_display, history_state, gr.Markdown(value=md_en, visible=True), gr.Markdown(value=md_es, visible=True)
155
 
156
- except (json.JSONDecodeError, KeyError) as e:
157
- print(f"Error parsing final report: {e}")
158
- return chat_display, history_state, gr.Markdown(value="Error generating report.", visible=True), gr.Markdown(visible=False)
159
-
160
- # --- 3. INTERFAZ DE GRADIO ---
161
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
162
  gr.Markdown("# 馃嚞馃嚙 AI English Speaking Practice & Assessment")
163
 
164
  with gr.Tabs():
 
165
  with gr.TabItem("Pr谩ctica Conversacional (Chat AI)"):
166
  with gr.Row():
167
  with gr.Column(scale=2):
168
- chatbot = gr.Chatbot(
169
- value=[(None, "Hi there! I'm Alex. How are you doing today?")],
170
- label="Conversation with your AI Tutor"
171
- )
172
  audio_in_chat = gr.Audio(sources=["microphone"], type="numpy", label="Record your response")
173
  with gr.Column(scale=1):
174
  gr.Markdown("### Final Report")
175
  feedback_en_out = gr.Markdown(label="English Feedback", visible=False)
176
  feedback_es_out = gr.Markdown(label="Retroalimentaci贸n en Espa帽ol", visible=False)
177
-
178
- # Estado para guardar el historial de la conversaci贸n
179
  history = gr.State([])
180
-
181
  audio_in_chat.stop_recording(
182
  fn=chat_interaction,
183
  inputs=[audio_in_chat, history],
184
  outputs=[chatbot, history, feedback_en_out, feedback_es_out]
185
  )
186
 
 
187
  with gr.TabItem("Evaluaci贸n por Frase"):
188
- gr.Markdown("This is a placeholder for the original sentence evaluation tool.")
189
- # Aqu铆 pegar铆as la interfaz de la herramienta anterior si quisieras combinar ambas.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
  if __name__ == "__main__":
192
  if not api_key_found:
 
18
  api_key_found = False
19
 
20
  print("Loading Whisper for transcription...")
 
21
  whisper_model = whisper.load_model("base", device="cpu")
22
  print("Whisper model loaded.")
23
 
24
 
25
  # --- 1. DEFINICI脫N DE PROMPTS PARA LA IA ---
26
 
27
+ # Prompt para la conversaci贸n
28
+ CONVERSATION_SYSTEM_PROMPT = "..." # (Mantener el prompt de conversaci贸n que ya tienes)
 
 
 
 
 
 
 
 
 
 
29
 
30
+ # Prompt para la evaluaci贸n final de la conversaci贸n
31
+ FINAL_EVALUATION_SYSTEM_PROMPT = "..." # (Mantener el prompt de evaluaci贸n final que ya tienes)
32
 
33
+ # Prompt para la evaluaci贸n de una sola frase
34
+ SENTENCE_EVALUATION_SYSTEM_PROMPT = """
35
+ You are an expert English language examiner specializing in phonetics. Your task is to provide a detailed, diagnostic assessment of a student's spoken English based on a reference sentence and detailed word-level audio analysis. Your entire response MUST be in English. You must return a single, valid JSON object with the following structure. Do not include any text outside of this JSON object.
36
  JSON Output Structure:
37
  {
38
+ "overall_score_100": integer,
39
+ "cefr_level": "string (A1, A2, B1, B2, C1, or C2)",
40
+ "holistic_feedback": {
41
+ "strengths": "string (A paragraph in English summarizing strong points.)",
42
+ "areas_for_improvement": "string (A paragraph in English detailing main error patterns.)"
 
 
43
  },
44
+ "word_by_word_analysis": [
45
+ {
46
+ "reference_word": "string",
47
+ "spoken_word": "string",
48
+ "word_score_100": integer,
49
+ "correct_ipa": "string",
50
+ "feedback": "string"
51
+ }
52
+ ]
53
  }
54
  """
55
 
56
 
57
  # --- 2. FUNCIONES L脫GICAS ---
58
 
59
+ # Funci贸n para la Pesta帽a "Pr谩ctica Conversacional"
60
+ def chat_interaction(audio_input, history_state):
61
+ # ... (Mantener la funci贸n 'chat_interaction' completa que ya tienes)
62
+ pass # Placeholder for your existing chat_interaction function
63
+
64
+ # Funci贸n para la Pesta帽a "Evaluaci贸n por Frase"
65
+ def run_sentence_evaluation(audio_input, reference_transcript):
66
+ # This is the 'run_evaluation' function from your previous, single-tab app
67
+ if not api_key_found: raise gr.Error("OpenAI API key not found.")
68
+ if audio_input is None or not reference_transcript:
69
+ return 0, "N/A", "Please provide both an audio file and the reference text.", None
70
+
71
  sr, y = audio_input
72
  temp_audio_path = "temp_audio.wav"
73
  sf.write(temp_audio_path, y, sr)
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ # (La l贸gica de 'extract_word_level_features' y la llamada a la API va aqu铆)
76
+ # For brevity, this part is condensed. Use the full function from your previous script.
77
 
78
+ # Placeholder for the actual API call logic
79
+ word_analysis_df = pd.DataFrame({
80
+ "Reference Word": reference_transcript.split(),
81
+ "Spoken Word": reference_transcript.split(),
82
+ "Score": [np.random.randint(80, 100) for _ in reference_transcript.split()],
83
+ "Correct IPA": ["..."], "Feedback": ["..."]
84
+ })
85
+ holistic_feedback_md = "### Strengths\nExcellent clarity.\n\n### Areas for Improvement\nWork on sentence intonation."
86
 
87
+ return 92, "B2", holistic_feedback_md, gr.DataFrame(value=word_analysis_df)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ # --- 3. INTERFAZ DE GRADIO CON PESTA脩AS ---
 
 
 
 
91
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
92
  gr.Markdown("# 馃嚞馃嚙 AI English Speaking Practice & Assessment")
93
 
94
  with gr.Tabs():
95
+ # --- Pesta帽a 1: Chat AI ---
96
  with gr.TabItem("Pr谩ctica Conversacional (Chat AI)"):
97
  with gr.Row():
98
  with gr.Column(scale=2):
99
+ chatbot = gr.Chatbot(value=[(None, "Hi there! I'm Alex. How are you doing today?")], label="Conversation with your AI Tutor")
 
 
 
100
  audio_in_chat = gr.Audio(sources=["microphone"], type="numpy", label="Record your response")
101
  with gr.Column(scale=1):
102
  gr.Markdown("### Final Report")
103
  feedback_en_out = gr.Markdown(label="English Feedback", visible=False)
104
  feedback_es_out = gr.Markdown(label="Retroalimentaci贸n en Espa帽ol", visible=False)
 
 
105
  history = gr.State([])
 
106
  audio_in_chat.stop_recording(
107
  fn=chat_interaction,
108
  inputs=[audio_in_chat, history],
109
  outputs=[chatbot, history, feedback_en_out, feedback_es_out]
110
  )
111
 
112
+ # --- Pesta帽a 2: Evaluaci贸n por Frase ---
113
  with gr.TabItem("Evaluaci贸n por Frase"):
114
+ TONGUE_TWISTERS = [
115
+ "Peter Piper picked a peck of pickled peppers.",
116
+ "She sells seashells by the seashore.",
117
+ "How much wood would a woodchuck chuck if a woodchuck could chuck wood?",
118
+ "Betty Botter bought some butter but she said the butter鈥檚 bitter.",
119
+ "A proper copper coffee pot."
120
+ ]
121
+
122
+ gr.Markdown("Choose a tongue twister or write your own sentence. Record yourself, and our AI examiner will provide a detailed diagnostic report.")
123
+
124
+ tongue_twister_selector = gr.Dropdown(choices=TONGUE_TWISTERS, label="Or Choose a Tongue Twister to Practice")
125
+
126
+ with gr.Row():
127
+ with gr.Column(scale=1):
128
+ audio_in_sentence = gr.Audio(sources=["microphone"], type="numpy", label="1. Record Your Voice")
129
+ text_in_sentence = gr.Textbox(lines=3, label="2. Reference Sentence", value=TONGUE_TWISTERS[0])
130
+ submit_btn_sentence = gr.Button("Get Assessment", variant="primary")
131
+
132
+ with gr.Column(scale=2):
133
+ gr.Markdown("### Assessment Summary")
134
+ with gr.Row():
135
+ score_out_sentence = gr.Number(label="Overall Score (0-100)", interactive=False)
136
+ level_out_sentence = gr.Textbox(label="Estimated CEFR Level", interactive=False)
137
+ holistic_feedback_out_sentence = gr.Markdown(label="Examiner's Feedback")
138
+
139
+ gr.Markdown("--- \n ### Detailed Word-by-Word Analysis")
140
+ word_analysis_out_sentence = gr.DataFrame(headers=["Reference Word", "Spoken Word", "Score", "Correct IPA", "Feedback"], label="Phonetic Breakdown", wrap=True)
141
+
142
+ def update_text(choice):
143
+ return gr.Textbox(value=choice)
144
+ tongue_twister_selector.change(fn=update_text, inputs=tongue_twister_selector, outputs=text_in_sentence)
145
+
146
+ submit_btn_sentence.click(
147
+ fn=run_sentence_evaluation,
148
+ inputs=[audio_in_sentence, text_in_sentence],
149
+ outputs=[score_out_sentence, level_out_sentence, holistic_feedback_out_sentence, word_analysis_out_sentence]
150
+ )
151
 
152
  if __name__ == "__main__":
153
  if not api_key_found: