Product-ai / app.py
pk75's picture
Update app.py
b97d99f verified
# app.py
import gradio as gr
import os
import time
import datetime
import random
import config
from modules.tts_handler import text_to_speech_file
from modules.stt_handler import transcribe_audio
from modules.doc_processor import extract_text_from_document
from modules.llm_handler import generate_coaching_question, evaluate_response, generate_coaching_feedback, get_overall_score, parse_scores_from_evaluation, get_score_interpretation
from modules.report_generator import generate_pdf_report
def test_recording():
"""Simple test to enable recording"""
return gr.update(interactive=True), "βœ… Recording enabled! Try recording something now."
def start_coaching_session(coaching_type, doc_file, name, num_questions):
if not coaching_type:
return (
{}, # state
[{"role": "assistant", "content": "Please select a product management focus area to begin your coaching session."}], # chatbot
None, # audio_out
gr.update(interactive=False), # audio_in
gr.update(interactive=True) # start_btn
)
doc_text = ""
if doc_file:
doc_text = extract_text_from_document(doc_file.name)
if "Error" in doc_text or "Unsupported" in doc_text:
return (
{}, # state
[{"role": "assistant", "content": f"Error processing document: {doc_text}. Continuing without document context."}], # chatbot
None, # audio_out
gr.update(interactive=False), # audio_in
gr.update(interactive=True) # start_btn
)
initial_state = {
"coaching_type": coaching_type,
"doc_text": doc_text,
"name": name if name else "Product Manager",
"question_count": int(num_questions),
"current_question_num": 1,
"coaching_log": []
}
first_question = generate_coaching_question(coaching_type, doc_text, 1)
initial_state["current_question_text"] = first_question
greeting = f"Hello {initial_state['name']}! Welcome to your personal AI product coaching session on {coaching_type}. We'll explore {int(num_questions)} scenarios together. Let's start with the first one:"
full_message = f"{greeting}\n\n{first_question}\n\nπŸŽ™οΈ Click the microphone button above to record your response!"
tts_prompt = f"{greeting} {first_question}"
ai_voice_path = text_to_speech_file(tts_prompt)
return (
initial_state, # state
[{"role": "assistant", "content": full_message}], # chatbot
ai_voice_path, # audio_out
gr.update(interactive=True), # audio_in - ENABLE recording
gr.update(interactive=False) # start_btn - disable after starting
)
def handle_coaching_turn(user_audio, chatbot_history, current_state):
"""Handle each coaching turn - one question at a time with immediate feedback"""
print(f"πŸŽ™οΈ Audio recording received: {user_audio}")
print(f"πŸ“ Current state: {current_state is not None}")
print(f"πŸ’¬ Chat history length: {len(chatbot_history) if chatbot_history else 0}")
# Check if we have valid inputs
if not user_audio:
print("❌ No audio received - returning early")
return chatbot_history, current_state, gr.update(interactive=True), gr.update(visible=False), None
if not current_state or "current_question_text" not in current_state:
print("❌ Invalid session state - returning early")
return chatbot_history, current_state, gr.update(interactive=True), gr.update(visible=False), None
print(f"βœ… Processing audio file: {user_audio}")
# Transcribe user response
try:
user_response_text = transcribe_audio(user_audio)
print(f"βœ… Transcribed: {user_response_text[:100]}...")
if user_response_text.startswith("Sorry, I") or "error" in user_response_text.lower():
# Transcription failed, return error message
if not chatbot_history:
chatbot_history = []
chatbot_history.append({"role": "assistant", "content": user_response_text})
return chatbot_history, current_state, gr.update(interactive=True), gr.update(visible=False), None
except Exception as e:
print(f"❌ Transcription error: {e}")
error_msg = "Sorry, I couldn't process your audio. Please try recording again."
if not chatbot_history:
chatbot_history = []
chatbot_history.append({"role": "assistant", "content": error_msg})
return chatbot_history, current_state, gr.update(interactive=True), gr.update(visible=False), None
# Initialize chat history if empty
if not chatbot_history:
chatbot_history = []
# Add user response to chat
chatbot_history.append({"role": "user", "content": user_response_text})
# Get feedback from AI
try:
feedback_text = evaluate_response(
current_state["current_question_text"],
user_response_text,
current_state["coaching_type"]
)
# Parse scores
scores = parse_scores_from_evaluation(feedback_text)
overall_score = get_overall_score(scores)
score_interpretation = get_score_interpretation(overall_score)
# Add score to feedback
if overall_score > 0:
score_summary = f"πŸ“Š SCORE: {overall_score}/10 - {score_interpretation}\n\n"
feedback_with_score = score_summary + feedback_text
else:
feedback_with_score = feedback_text
except Exception as e:
print(f"❌ Evaluation error: {e}")
feedback_with_score = "Thank you for your response. Let me give you some feedback on your approach."
overall_score = 7 # Default score
scores = {}
# Store in coaching log
current_state["coaching_log"].append({
"question": current_state["current_question_text"],
"response": user_response_text,
"feedback": feedback_with_score,
"scores": scores,
"overall_score": overall_score
})
# Add AI feedback to chat
chatbot_history.append({"role": "assistant", "content": feedback_with_score})
# Generate voice feedback (with error handling)
try:
ai_feedback_voice = text_to_speech_file(f"Your score is {overall_score} out of 10. {feedback_text[:100]}...")
except Exception as e:
print(f"❌ TTS error: {e}")
ai_feedback_voice = None
# Check if session is complete
if current_state["current_question_num"] >= current_state["question_count"]:
# Session complete - generate report
session_scores = [item.get("overall_score", 0) for item in current_state["coaching_log"]]
avg_score = sum(session_scores) / len(session_scores) if session_scores else 0
session_interpretation = get_score_interpretation(avg_score)
end_message = f"πŸŽ‰ Session Complete!\n\nπŸ“ˆ FINAL AVERAGE: {avg_score:.1f}/10 - {session_interpretation}\n\nGenerating your report..."
chatbot_history.append({"role": "assistant", "content": end_message})
# Generate PDF report
try:
pdf_path = generate_pdf_file(current_state)
print(f"βœ… Report generated: {pdf_path}")
except Exception as e:
print(f"❌ Report generation error: {e}")
pdf_path = None
try:
end_voice = text_to_speech_file("Congratulations! Your coaching session is complete.")
except Exception as e:
print(f"❌ TTS error: {e}")
end_voice = ai_feedback_voice
return (
chatbot_history,
current_state,
gr.update(interactive=False), # Disable recording
gr.update(value=pdf_path, visible=True) if pdf_path else gr.update(visible=False), # Show download button
end_voice
)
else:
# Move to next question
current_state["current_question_num"] += 1
next_question = generate_coaching_question(
current_state["coaching_type"],
current_state["doc_text"],
current_state["current_question_num"]
)
current_state["current_question_text"] = next_question
q_num = current_state["current_question_num"]
next_message = f"🎯 Question {q_num}/{current_state['question_count']}:\n\n{next_question}"
chatbot_history.append({"role": "assistant", "content": next_message})
# Generate voice for next question
try:
next_voice = text_to_speech_file(f"Question {q_num}. {next_question}")
except Exception as e:
print(f"❌ TTS error: {e}")
next_voice = ai_feedback_voice
return (
chatbot_history,
current_state,
gr.update(interactive=True), # Keep recording enabled
gr.update(visible=False), # Keep download hidden
next_voice
)
def generate_pdf_file(state):
final_data = {
"name": state["name"],
"type": state["coaching_type"],
"q_and_a": state["coaching_log"]
}
file_name = f"Product_Coaching_Report_{state['name']}_{datetime.datetime.now().strftime('%Y-%m-%d')}.pdf"
file_path = os.path.join(config.REPORT_FOLDER, file_name)
generate_pdf_report(final_data, file_path)
return file_path
with gr.Blocks(theme=gr.themes.Default()) as app:
state = gr.State({})
gr.Markdown("# πŸš€ Personal AI Product Coach")
gr.Markdown("### Elevate your product management skills with personalized coaching sessions")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Setup Your Coaching Session")
user_name = gr.Textbox(label="Your Name", placeholder="Enter your name")
coaching_type_dd = gr.Dropdown(
config.COACHING_TYPES,
label="Product Management Focus Area"
)
gr.Markdown("*Choose the area you want to improve*")
num_questions_slider = gr.Slider(
minimum=1, maximum=10, value=5, step=1,
label="Number of Scenarios"
)
gr.Markdown("*How many coaching scenarios do you want to practice?*")
doc_uploader = gr.File(
label="Upload Your Resume/Portfolio (Optional)",
file_types=[".pdf", ".docx", ".txt"]
)
gr.Markdown("*Upload your resume or product portfolio for personalized coaching*")
start_btn = gr.Button("🎯 Start Coaching Session", variant="primary")
with gr.Column(scale=2):
chatbot = gr.Chatbot(
label="AI Product Coach",
height=500,
type="messages"
)
with gr.Row():
# Use a simpler audio input approach
audio_in = gr.Audio(
sources=["microphone"],
type="filepath",
label="πŸŽ™οΈ Record Your Response",
interactive=False,
show_download_button=False
)
# Add manual submit button for reliability
submit_audio_btn = gr.Button("πŸ“€ Submit Audio", variant="secondary", visible=False)
# Add a test button to check if recording works
test_btn = gr.Button("πŸ§ͺ Test Recording", variant="secondary", visible=True)
status_text = gr.Textbox(label="Status", value="Click 'Start Coaching Session' to begin", interactive=False)
download_pdf_btn = gr.File(label="πŸ“Š Download Coaching Report", visible=False)
audio_out = gr.Audio(visible=False, autoplay=True)
start_btn.click(
fn=start_coaching_session,
inputs=[coaching_type_dd, doc_uploader, user_name, num_questions_slider],
outputs=[state, chatbot, audio_out, audio_in, start_btn]
)
test_btn.click(
fn=test_recording,
outputs=[audio_in, status_text]
)
# Multiple event handlers for better reliability
audio_in.change(
fn=handle_coaching_turn,
inputs=[audio_in, chatbot, state],
outputs=[chatbot, state, audio_in, download_pdf_btn, audio_out]
)
# Also try upload event
audio_in.upload(
fn=handle_coaching_turn,
inputs=[audio_in, chatbot, state],
outputs=[chatbot, state, audio_in, download_pdf_btn, audio_out]
)
# Manual submit button as backup
submit_audio_btn.click(
fn=handle_coaching_turn,
inputs=[audio_in, chatbot, state],
outputs=[chatbot, state, audio_in, download_pdf_btn, audio_out]
)
# Add footer with helpful information
gr.Markdown("""
---
### πŸ’‘ Coaching Tips
- **Speak naturally** - Think out loud and explain your reasoning
- **Use frameworks** - Apply PM frameworks like RICE, Jobs-to-be-Done, etc.
- **Be specific** - Provide concrete examples and metrics when possible
- **Ask questions** - Great PMs ask clarifying questions about requirements
### 🎯 What Makes a Great Response?
βœ… Clear problem identification
βœ… Structured thinking approach
βœ… Data-driven reasoning
βœ… Stakeholder consideration
βœ… Action-oriented next steps
""")
if __name__ == "__main__":
# Ensure required directories exist
os.makedirs(config.UPLOAD_FOLDER, exist_ok=True)
os.makedirs(config.REPORT_FOLDER, exist_ok=True)
# Print startup message
print("πŸš€ Personal AI Product Coach Starting...")
print("πŸ“š Focus Areas Available:", ", ".join(config.COACHING_TYPES))
print("🎯 Ready to help you improve your product management skills!")
print("=" * 60)
# Check for API key
if not config.GROQ_API_KEY:
print("⚠️ WARNING: GROQ_API_KEY not found!")
print("Please set your Groq API key as an environment variable.")
print("Get your free key from: https://console.groq.com/keys")
# Launch the app - Configure for both local and HuggingFace deployment
app.launch(
server_name="0.0.0.0" if os.getenv("SPACE_ID") else "127.0.0.1",
server_port=None if os.getenv("SPACE_ID") else 7861,
share=True,
show_error=True
)