import gradio as gr import uuid import os import matplotlib.pyplot as plt from gtts import gTTS from openai import OpenAI from app_utils import ( LANG_CODES, save_to_db, fetch_user_sessions, convert_to_wav, transcribe_audio, parse_scores_from_feedback, generate_progress_summary, build_score_comparison_data, render_score_chart, build_trend_data, render_trend_chart ) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) def generate_feedback(transcript, language, goal="general improvement", focus_areas=None, previous_transcript=None): focus_str = ", ".join(focus_areas) if focus_areas else "Clarity, Structure, Fluency, Content Relevance, and Tone" history_section = f"\n\nFor reference, their previous transcript was:\n{previous_transcript}" if previous_transcript else "" prompt = f""" You are a supportive communication coach helping a learner whose goal is: **{goal}**. First, return a JSON object of scores (0–10) for these areas: {focus_str} Then provide a clear and friendly evaluation: For each area: - Repeat the score (0–10) - Explain why the user got that score Then provide: - A detailed summary of strengths and improvement areas. - One motivational line to end with. Do not specifically add motivational keyword, just add the line. Transcript: {history_section} """.strip() response = client.chat.completions.create( model="gpt-4", messages=[ {"role": "system", "content": f"You are a warm and constructive communication coach responding in {language}."}, {"role": "user", "content": prompt} ], temperature=0.7 ) feedback = response.choices[0].message.content try: split_idx = feedback.index('}') + 1 feedback_clean = feedback[split_idx:].strip() except: feedback_clean = feedback return feedback, feedback_clean def generate_example_response(transcript, language): prompt = f"""Rewrite this speech to make it more polished, fluent, and confident. Keep the meaning and tone the same, but improve clarity and structure. Transcript: {transcript} """ response = client.chat.completions.create( model="gpt-4", messages=[ {"role": "system", "content": f"Reply in {language}. Provide only the improved version of the speech."}, {"role": "user", "content": prompt} ] ) return response.choices[0].message.content def render_empty_chart(title): fig, ax = plt.subplots() ax.set_title(title) ax.text(0.5, 0.5, "No scores yet.\nSpeak again to generate progress!", ha='center', va='center', fontsize=12) ax.axis('off') return fig def spoken_dashboard(nickname_input): with gr.Column() as spoken_panel: gr.Markdown(""" """) with gr.Row(): language_dropdown = gr.Dropdown(label="🌍 Language", choices=list(LANG_CODES.keys()), value="English") goal_dropdown = gr.Dropdown(label="🎯 Goal", choices=["Interview preparation", "Public speaking", "Class presentation", "General improvement"], value="General improvement") focus_checkboxes = gr.CheckboxGroup(label="🧠 Focus Areas", choices=["Clarity", "Structure", "Fluency", "Tone", "Content Relevance"], value=["Clarity", "Structure", "Fluency", "Tone", "Content Relevance"]) with gr.Row(): audio_input = gr.Audio(type="filepath", label="🎙 Speak or Upload Audio") audio_output = gr.Audio(label="🔊 Chatter's Response", type="filepath") transcript_box = gr.Textbox(label="📖 Transcript", interactive=False) feedback_box = gr.Textbox(label="💡 Feedback", interactive=False) hidden_transcript = gr.Textbox(visible=False) with gr.Row(): try_again = gr.Button("🔁 Try Again") show_example = gr.Button("🎯 Show Me an Example") example_box = gr.Textbox(label="🗣 Suggested Improvement", visible=True) history_table = gr.Dataframe(headers=["🕒 Timestamp", "🌐 Language", "📄 Transcript", "💬 Feedback"]) with gr.Row(): with gr.Column(scale=1): gr.Dropdown( choices=[""], label="📊 Score Comparison", interactive=False, show_label=True ) score_chart = gr.Plot() with gr.Column(scale=1): trend_category_dropdown = gr.Dropdown( label="📈 Track Progress In", choices=["Clarity", "Structure", "Fluency", "Tone", "Content Relevance"], value="Tone" ) trend_chart = gr.Plot(label="Progress Over Time") milestone_box = gr.Markdown(visible=False) def tutor_feedback(audio_file, language, goal, focus_areas, trend_category, nickname): if not audio_file: return "", "No audio received.", None, "", [], render_empty_chart("📊 Score Comparison"), render_empty_chart("📈 Progress Over Time"), gr.update(visible=False) if hasattr(nickname, "value"): nickname = nickname.value wav_path = convert_to_wav(audio_file) transcript = transcribe_audio(wav_path) previous_sessions = fetch_user_sessions(nickname) previous_transcript = previous_sessions[-1].transcript if previous_sessions else None previous_feedback = previous_sessions[-1].feedback if previous_sessions else None full_feedback, feedback_clean = generate_feedback(transcript, language, goal, focus_areas, previous_transcript) if previous_feedback: feedback_clean += generate_progress_summary(full_feedback, previous_feedback) milestone = "" session_count = len(previous_sessions) + 1 if session_count in [3, 5, 10]: milestone = f"🎉 Congrats on completing **{session_count} sessions**!" feedback_clean += f"\n\n{milestone}" lang_code = LANG_CODES.get(language, "en") tts = gTTS(feedback_clean, lang=lang_code) mp3_path = f"/tmp/{uuid.uuid4()}.mp3" tts.save(mp3_path) save_to_db(nickname, transcript, full_feedback, language) sessions = fetch_user_sessions(nickname) session_table = [[s.timestamp, s.language, s.transcript[:40], s.feedback[:40]] for s in sessions] score_plot = render_score_chart(build_score_comparison_data(full_feedback, previous_feedback)) if previous_feedback else render_empty_chart("📊 Score Comparison") dates, trend_scores = build_trend_data(sessions, trend_category) trend_plot = render_trend_chart(dates, trend_scores, trend_category) if trend_scores else render_empty_chart(f"📈 {trend_category} Progress") return transcript, feedback_clean, mp3_path, transcript, session_table, score_plot, trend_plot, gr.update(visible=bool(milestone), value=milestone) audio_input.change( fn=tutor_feedback, inputs=[audio_input, language_dropdown, goal_dropdown, focus_checkboxes, trend_category_dropdown, nickname_input], outputs=[transcript_box, feedback_box, audio_output, hidden_transcript, history_table, score_chart, trend_chart, milestone_box], show_progress="minimal" ) try_again.click(fn=lambda: ("", "", None, "", "", render_empty_chart("📊 Score Comparison"), render_empty_chart("📈 Progress Over Time"), gr.update(visible=False)), inputs=None, outputs=[transcript_box, feedback_box, audio_output, hidden_transcript, example_box, score_chart, trend_chart, milestone_box]) show_example.click(fn=generate_example_response, inputs=[hidden_transcript, language_dropdown], outputs=example_box) def update_trend_chart(trend_category, nickname): if hasattr(nickname, "value"): nickname = nickname.value sessions = fetch_user_sessions(nickname) dates, trend_scores = build_trend_data(sessions, trend_category) if trend_scores: return render_trend_chart(dates, trend_scores, trend_category) return render_empty_chart(f"📈 {trend_category} Progress") trend_category_dropdown.change( fn=update_trend_chart, inputs=[trend_category_dropdown, nickname_input], outputs=[trend_chart] ) return spoken_panel