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("""
# 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.
#Select the type of interview you want to practice.