Spaces:
Sleeping
Sleeping
| 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() | |