intervu_final / app.py
maahikachitagi's picture
Update app.py
bad29ea verified
import gradio as gr
from huggingface_hub import InferenceClient
import random
import whisper
from pydub import AudioSegment
import torch
import torchvision.transforms as transforms
import torchvision.models as models
from PIL import Image
# Load mode
client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
whisper_model = whisper.load_model("base")
def transcribe_audio(file_path):
if file_path is None:
return "❌ No audio file provided."
try:
audio = AudioSegment.from_file(file_path)
converted_path = "converted.wav"
audio.export(converted_path, format="wav")
result = whisper_model.transcribe(converted_path, fp16=False)
return result["text"]
except Exception as e:
return f"❌ ERROR: {str(e)}"
# Step 1: Set interview type
def set_type(choice, user_profile):
user_profile["interview_type"] = choice
return "Great! What’s your background and what field/role are you aiming for?", user_profile
# Step 2: Save background
def save_background(info, user_profile):
user_profile["field"] = info
return "Awesome! Type 'start' below to begin your interview.", user_profile
# Generate interview question
def generate_question(user_profile):
system_prompt = f"You are a professional interviewer conducting a {user_profile['interview_type']} interview for a candidate in {user_profile['field']}. Generate one thoughtful, clear, and concise interview question."
messages = [{"role": "system", "content": system_prompt}]
response = client.chat_completion(messages, max_tokens=100, stream=False)
return response.choices[0].message.content.strip()
# Generate feedback using LLM
def generate_feedback_llm(user_profile):
feedback = []
for i, (question, answer) in enumerate(zip(user_profile.get("questions", []), user_profile.get("user_answers", []))):
messages = [
{"role": "system", "content": f"You are a professional interviewer providing feedback for a candidate's response in a {user_profile['interview_type']} interview for a {user_profile['field']} role. DO NOT include any candidate responses or dialogue. DO NOT include any 'Interviewer:', 'Candidate:' prefixes"},
{"role": "user", "content": f"Question: {question}\nAnswer: {answer}\nPlease give specific, constructive feedback."}
]
response = client.chat_completion(messages, max_tokens=150, stream=False)
feedback.append(f"Question {i+1}: {response.choices[0].message.content.strip()}")
return "\n\n".join(feedback)
# Chat lo
def respond(message, chat_history, user_profile):
message_lower = message.strip().lower()
if not user_profile.get("interview_type") or not user_profile.get("field"):
bot_msg = "Please finish steps 1 and 2 before starting the interview."
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": bot_msg})
return chat_history
if message_lower == 'start':
user_profile['questions'] = []
user_profile['user_answers'] = []
user_profile['current_q'] = 0
user_profile['interview_in_progress'] = True
intro = f"Welcome to your {user_profile['interview_type']} interview for a {user_profile['field']} position. I will ask you up to 10 questions. Type 'stop' anytime to end."
first_q = generate_question(user_profile)
user_profile['questions'].append(first_q)
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": intro})
chat_history.append({"role": "assistant", "content": f"First question: {first_q}"})
return chat_history
if message_lower == 'stop' and user_profile.get("interview_in_progress"):
user_profile['interview_in_progress'] = False
bot_msg = "Interview stopped. Type 'feedback' if you'd like me to analyze your answers. Thanks for interviewing with Intervu!"
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": bot_msg})
return chat_history
if message_lower == 'feedback':
feedback = generate_feedback_llm(user_profile)
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": feedback})
return chat_history
if user_profile.get("interview_in_progress"):
user_profile['user_answers'].append(message)
user_profile['current_q'] += 1
if user_profile['current_q'] < 10:
next_q = generate_question(user_profile)
user_profile['questions'].append(next_q)
bot_msg = f"Next question: {next_q}"
else:
user_profile['interview_in_progress'] = False
bot_msg = "Interview complete! Type 'feedback' if you'd like me to analyze your answers. Thanks for interviewing with Intervu!"
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": bot_msg})
return chat_history
# fallback LLM response
messages = [
{"role": "system", "content": f"You are a professional interviewer conducting a {user_profile['interview_type']} interview for a candidate in {user_profile['field']}."},
{"role": "user", "content": message}
]
response = client.chat_completion(messages, max_tokens=150, stream=False)
bot_msg = response.choices[0].message.content.strip()
chat_history.append({"role": "user", "content": message})
chat_history.append({"role": "assistant", "content": bot_msg})
return chat_history
# Handle audio input
def handle_audio(audio_file, chat_history, user_profile):
transcribed = transcribe_audio(audio_file)
if transcribed.startswith("❌"):
chat_history.append({"role": "assistant", "content": transcribed})
return chat_history
return respond(transcribed, chat_history, user_profile)
# Load ResNet18 model
model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)
model.fc = torch.nn.Linear(model.fc.in_features, 2) # Adjust for two classes
model.eval()
# Define image transformation
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor()
])
# Function to classify posture images
def classify_image(image):
if image is None:
return "No image provided!"
image = transform(image).unsqueeze(0)
output = model(image)
_, predicted = torch.max(output, 1)
return "You have good posture! Keep it up!" if predicted.item() == 0 else "I suggest sitting straighter or getting more into frame. It will help for your future interviews."
# Full UI
with gr.Blocks(css="""
body { background-color: #161b24; font-family: 'Nato', sans-serif !important; }
h1 { text-align: center; color: #2c3e50; }
img { display: block; margin: auto; width: 100px; border-radius: 20px; }
button {
font-size: 16px;
padding: 10px 20px;
border-radius: 10px;
border: 2px solid rgba(124, 248, 255, 0.4);
background-color: rgba(124, 248, 255, 0.4);
color: #fafdff;
transition: all 0.2s ease;
}
button:hover {
background-color: #49888f;
border-color: #7cf8ff;
transform: scale(1.05);
}
.gr-chatbot { background-color: white; border-radius: 15px; padding: 20px; }
.tab-container {
height: auto !important;
min-height: 0 !important;
flex: unset !important;
}
.tab-container[style] {
height: auto !important;
min-height: 0 !important;
flex: unset !important;
padding-bottom: 25px;
margin-top: 40px;
}
.custom-tabs .tab-nav {
display: flex;
align-items: center;
width: 100%;
position: relative;
overflow: hidden;
background-color: white;
padding: 8px;
border-radius: 30px;
height: auto !important;
min-height: 0 !important
padding-bottom: 25px !important;
margin-top: 40px !important;
gap: 16px;
}
.custom-tabs button[role="tab"] {
border-radius: 50px !important;
padding: 10 !important;
font-weight: 500;
background-color: transparent;
border:none;
justify-content: center;
width: 40%;
color: #fafdff !important;
}
.custom-tabs button[role="tab"][aria-selected="true"] {
border: 2px solid #7cf8ff !important;
color: #7cf8ff !important;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
transform: none !important;
}
.custom-tabs button[role="tab"]:hover {
background-color: #2e323a;
transform: scale(1.05);
transform: none !important;
}
/* REMOVE THE UGLY ORANGE UNDERLINE LINE */
.custom-tabs button[role="tab"] {
border-bottom: none !important;
box-shadow: none !important;
}
.custom-tabs button[role="tab"]::after {
display: none !important;
border-bottom: none !important;
}
/*:root {
--trim-region-color: rgba(255, 153, 0, 0.5);*/
.custom-tabs button[role="tab"]:nth-child(1)::before {
content: "";
background: url("https://cdn-uploads.huggingface.co/production/uploads/6841b10b397a67a7c7a39b89/6gGwgTv14woJpgh1G59Ad.png") no-repeat center center;
background-size: contain;
width: 20px;
height: 20px;
display: inline-block;
margin-right: 8px;
vertical-align: middle;
}
.custom-tabs button[role="tab"]:nth-child(2)::before {
content: "";
background: url("https://cdn-uploads.huggingface.co/production/uploads/6841b10b397a67a7c7a39b89/F0fJhrP6CtFFqhQWKb9dF.png") no-repeat center center;
background-size: contain;
width: 20px;
height: 20px;
display: inline-block;
margin-right: 8px;
vertical-align: middle;
}
.custom-tabs button[role="tab"]:nth-child(3)::before {
content: "";
background: url("https://cdn-uploads.huggingface.co/production/uploads/6841b10b397a67a7c7a39b89/tfjs2xtx5g61oqfcchHpd.png") no-repeat center center;
background-size: contain;
width: 20px;
height: 20px;
display: inline-block;
margin-right: 8px;
vertical-align: middle;
}
.bruh button{
font-size: 0px !important;
padding: 0px !important;
border-radius: 0px !important;
border: none !important;
background-color: #27272a !important;
color: #fafdff !important;
transition: none !important;
}
""") as demo:
gr.Markdown("""
<div style='width: 100%; text-align: center;'>
<img src="https://cdn-uploads.huggingface.co/production/uploads/6841b10b397a67a7c7a39b89/iudiYIVkr1ZcAqpEWaW3Z.png" style="width: 100%; height: auto; object-fit: contain;">
</div>
""")
#https://cdn-uploads.huggingface.co/production/uploads/6841b10b397a67a7c7a39b89/MTR_dMHte-RCIbyujLW83.png
# gr.Markdown("""
# <div style='width: 90%; text-align: center;'>
# <img src="https://cdn-uploads.huggingface.co/production/uploads/6841b10b397a67a7c7a39b89/H6VqF7bDhPQ9k8lQo7fqO.png" style="width: 100%; height: auto; object-fit: contain;">
# </div>
# """)
user_profile = gr.State({"interview_type": "", "field": "", "interview_in_progress": False})
chat_history = gr.State([])
# gr.Markdown("""<div style='
# font-family: 'Lato', sans-serif;
# text-align: center;
# padding: 30px;
# box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
# margin-bottom: 20px;
# '>
# <h1 style='font-size: 89.5px; color: #fafdff;'>Welcome to <b><span style='color: #7cf8ff;'>Intervu</span></b></h1>
# <p style='font-size: 22.1px; color: #fafdff; margin-top: 30px; text-align: center; margin-bottom: 30px;'>Before you begin, complete Step 1 to select your interview type and Step 2 to enter your background. Practice is available through text, speech, or webcam.</p>
# </div>
# """)
# Step 1: Interview Type
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("""
<div style="
background: rgba(255, 255, 255, 0.1);
padding: 20px;
border-radius: 15px;
backdrop-filter: blur(5px);
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
">
<h3>Step 1: Choose Interview Type</h3>
<p>Select the type of interview you want to practice.</p>
</div>
""")
btn1 = gr.Button("Technical")
btn2 = gr.Button("Competency-Based Interview")
btn3 = gr.Button("Case")
with gr.Column(scale=2):
type_output = gr.Textbox(label="Bot Response", interactive=False)
btn1.click(set_type, inputs=[gr.Textbox(value="Technical", visible=False), user_profile], outputs=[type_output, user_profile])
btn2.click(set_type, inputs=[gr.Textbox(value="Competency-Based Interview", visible=False), user_profile], outputs=[type_output, user_profile])
btn3.click(set_type, inputs=[gr.Textbox(value="Case", visible=False), user_profile], outputs=[type_output, user_profile])
# Step 2: Background
gr.Markdown("### Step 2: Enter Your Background")
background = gr.Textbox(label="Your background and field/goal")
background_btn = gr.Button("Submit")
background_output = gr.Textbox(label="Bot response", interactive=False)
background_btn.click(save_background, inputs=[background, user_profile], outputs=[background_output, user_profile])
# Step 3: Chat Mode Tabs
gr.Markdown("### Step 3: Choose Chat Mode")
with gr.Row():
with gr.Column(elem_classes=["custom-tabs"]):
with gr.Tabs():
with gr.Tab('Text Mode'):
chatbot_text = gr.Chatbot(label="Interview Chat (Text Mode)", type="messages")
msg = gr.Textbox(label="Type 'start' to begin")
send_btn = gr.Button("Send")
send_btn.click(respond, inputs=[msg, chat_history, user_profile], outputs=[chatbot_text], queue=False)
send_btn.click(lambda: "", None, msg, queue=False)
with gr.Tab("Audio Mode"):
chatbot_audio = gr.Chatbot(label="Interview Chat (Audio Mode)", type="messages")
audio_input = gr.Audio(type="filepath", label="Record Your Answer", elem_classes=["bruh"])
audio_btn = gr.Button("Send Audio")
audio_btn.click(handle_audio, inputs=[audio_input, chat_history, user_profile], outputs=[chatbot_audio], queue=False, )
with gr.Tab("Webcam Mode"):
img_upload = gr.Image(type="pil", label="Upload an Image", elem_classes=["bruh"])
posture_output = gr.Textbox(label="Posture Feedback", elem_classes=["bruh"])
posture_btn = gr.Button("Analyze Posture")
posture_btn.click(classify_image, inputs=[img_upload], outputs=[posture_output])
demo.launch()