import gradio as gr import os from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import FAISS from langchain_community.embeddings import HuggingFaceEmbeddings from groq import Groq from dotenv import load_dotenv from faster_whisper import WhisperModel from elevenlabs.client import ElevenLabs from elevenlabs import play import tempfile # Load environment variables load_dotenv() # Initialize APIs GROQ_API_KEY = "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" ELEVENLABS_API_KEY = "ap2_69e1e821-6ea7-4fa0-88dc-ba54f2ac246c" # Initialize clients groq_client = Groq(api_key=GROQ_API_KEY) elevenlabs_client = ElevenLabs(api_key=ELEVENLABS_API_KEY) # Initialize Whisper model whisper_model = WhisperModel("small", device="cpu", compute_type="int8") def summarize_resume(resume_text): """Generate a concise summary of key resume points""" prompt = f"""Create a concise summary of this resume highlighting: 1. Professional title/role 2. Years of experience 3. Core skills/competencies 4. Education background 5. Notable achievements Resume: {resume_text[:3000]}... [truncated]""" response = groq_client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model="llama3-70b-8192", temperature=0.3, ) return response.choices[0].message.content def calculate_ats_score(resume_text): """Calculate ATS score based on resume content""" prompt = f"""Analyze this resume and calculate an ATS score (0-100) considering: 1. Keyword optimization (20 pts) 2. Section organization (20 pts) 3. Experience quality (20 pts) 4. Education completeness (20 pts) 5. Readability (20 pts) Return ONLY the numerical score and nothing else. Resume: {resume_text[:3000]}... [truncated]""" response = groq_client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model="llama3-70b-8192", temperature=0, ) try: return int(response.choices[0].message.content.strip()) except: return 50 # Default if parsing fails def process_resume(file): """Process uploaded resume PDF""" try: # Load and process PDF loader = PyPDFLoader(file.name) docs = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200, separators=["\n\n", "\n", " ", ""] ).split_documents(loader.load()) # Create vector store embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2") FAISS.from_documents(docs, embeddings).save_local("resume_index") # Generate outputs full_text = "\n".join([doc.page_content for doc in docs]) gr.Info("✅ Resume processed successfully!") return summarize_resume(full_text), f"ATS Score: {calculate_ats_score(full_text)}/100" except Exception as e: gr.Warning(f"❌ Error: {str(e)}") return f"Error: {str(e)}", "ATS Score: N/A" def transcribe_audio(audio_path): """Convert speech to text using Whisper""" segments, _ = whisper_model.transcribe(audio_path) return " ".join([segment.text for segment in segments]) def generate_question(resume_text): """Generate general interview questions based on resume""" prompt = f"""Generate one general interview question focusing on: - Teamwork experiences - Challenges overcome - Learning experiences - Career motivations - Problem-solving examples Make it conversational and open-ended. Resume Excerpt: {resume_text[:2000]}... [truncated]""" response = groq_client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model="llama3-70b-8192", temperature=0.7, ) return response.choices[0].message.content def evaluate_response(question, response_text): """Evaluate interview response""" prompt = f"""Evaluate this interview response on: 1. Clarity (1-5) 2. Confidence (1-5) 3. Relevance (1-5) 4. Suggested improvements Question: {question} Response: {response_text}""" evaluation = groq_client.chat.completions.create( messages=[{"role": "user", "content": prompt}], model="llama3-70b-8192", temperature=0.2, ) return evaluation.choices[0].message.content def speak_feedback(text): """Convert text feedback to speech""" try: if not text.strip(): raise ValueError("Empty feedback text") audio = elevenlabs_client.generate( text=text, voice="Rachel", model="eleven_monolingual_v2" ) # Create a temporary file with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as tmp: for chunk in audio: if chunk: tmp.write(chunk) tmp_path = tmp.name return tmp_path except Exception as e: gr.Warning(f"TTS Error: {str(e)}") return None # Gradio Interface with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("## Ready Set Hire") gr.Markdown("Upload your resume and practice general interview questions with AI feedback") with gr.Tab("📄 Resume Analysis"): with gr.Row(): with gr.Column(): resume_upload = gr.File( label="Upload Resume (PDF)", file_types=[".pdf"], elem_id="resume-upload" ) process_btn = gr.Button("Analyze Resume", variant="primary") with gr.Column(): resume_summary = gr.Textbox(label="Resume Summary", lines=10) ats_score = gr.Textbox( label="ATS Compatibility Score", interactive=False, elem_classes=["ats-score"] ) process_btn.click( fn=process_resume, inputs=resume_upload, outputs=[resume_summary, ats_score] ) with gr.Tab("🎤 Mock Interview"): with gr.Row(): with gr.Column(): audio_input = gr.Audio(sources=["microphone"], type="filepath") transcribe_btn = gr.Button("Transcribe Response", variant="primary") question_box = gr.Textbox(label="Current Question") generate_btn = gr.Button("Generate New Question") with gr.Column(): transcription = gr.Textbox(label="Your Response") evaluation = gr.Textbox(label="Feedback", lines=8) feedback_audio = gr.Audio(label="Feedback Audio", visible=False) # Event handlers transcribe_btn.click( fn=transcribe_audio, inputs=audio_input, outputs=transcription ) generate_btn.click( fn=generate_question, inputs=resume_summary, outputs=question_box ) gr.on( triggers=[transcription.change], fn=evaluate_response, inputs=[question_box, transcription], outputs=evaluation ) if __name__ == "__main__": demo.launch()