import re import google.generativeai as genai import PyPDF2 import streamlit as st from docx import Document from langchain.memory import ConversationBufferMemory from langchain.prompts import (ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder) from langchain.schema.output_parser import StrOutputParser from langchain.schema.runnable import RunnableLambda, RunnablePassthrough from langchain_core.messages import SystemMessage # Updated import from langchain_google_genai import ChatGoogleGenerativeAI # Function to extract text from PDF def extract_text_from_pdf(uploaded_file): text = "" reader = PyPDF2.PdfReader(uploaded_file) for page in reader.pages: text += page.extract_text() return text # Function to extract text from Word document def extract_text_from_word(uploaded_file): text = "" doc = Document(uploaded_file) for paragraph in doc.paragraphs: text += paragraph.text + "\n" return text def parse_mcq_questions(mcq_list): # Split the string into individual questions questions = re.split(r'\d+\.\s+', mcq_list)[1:] # Skip the empty first element parsed_questions = [] for q in questions: # Split into question and options parts = q.strip().split(' - ') question = parts[0].strip() options = { opt[0]: opt[2:].strip() for opt in parts[1:] } parsed_questions.append({ 'question': question, 'options': options }) return parsed_questions # Function to generate MCQs using LLM def generate_mcqs(keywords): # Construct the query query = {"human_input": f""" You are an advanced AI model trained to generate high-quality multiple-choice questions (MCQs). Based on the provided list of skills: {keywords}, create **exactly 10 MCQs**. Each MCQ should focus on most important concepts related to the internal topics of each skill. For example, if the keyword is "Python," the questions should be derived from core Python concepts, like data structures, syntax, or libraries. The MCQs should follow this structure: 1. A clear and concise important question based on a topic within the skill. 2. Four options (labeled as A, B, C, and D). 3. Only one correct answer per question, with the other options serving as plausible distractors. Do not provide any other information, explanations, or extra text. Output **only** the 10 MCQs in proper structure, like this: 1. Question text... - A) Option 1 - B) Option 2 - C) Option 3 - D) Option 4 2. Question text... - A) Option 1 - B) Option 2 - C) Option 3 - D) Option 4 Continue this format for all 10 questions. """} # Invoke the language model to generate MCQs response = chain.invoke(query) memory.save_context(query, {"output": response}) # Return the generated MCQs as a string return response # Function to evaluate MCQ answers def evaluate_mcqs(mcq_list, answers): query = {"human_input": f""" You are an advanced AI model trained to evaluate answers for high-quality multiple-choice questions (MCQs). Act as an expert professional in all relevant skills and concepts, analyzing the user's answers in detail. Follow these instructions: 1. Evaluate the provided answers {answers} against the correct answers for the MCQs. 2. Award 1 mark for each correct answer. Determine if each answer is correct or incorrect. 3. For incorrect answers: - Analyze deeply to identify the specific concepts or subtopics within the skill where the user is struggling. - Provide a focused list of concepts the user needs to improve on, derived from the incorrect answers. 4. At the end of the evaluation, output: - Total marks scored (out of 10). - A detailed and analyzed one by one list of concepts to focus on, ensuring they address the root areas of misunderstanding or lack of knowledge. Output **only** the following information: - Total marks scored: X/10 - Concepts to focus on: [Provide an analyzed and specific list of concepts derived from incorrect answers] """} response = chain.invoke(query) memory.save_context(query, {"output": response}) return response # Function to generate Questions using LLM def generate_questions(keywords): # Construct the query query = {"human_input": f""" You are a highly advanced AI trained to act as a real-time interview expert. Based on the provided keywords {keywords}, identify the most relevant skills and generate exactly two coding interview questions. These questions should adhere to the professional structure used in coding platforms like LeetCode or HackerRank. Follow these instructions: 1. Analyze the provided keywords to identify key skills and concepts. 2. Generate two easy to medium-level coding questions that align with these skills. 3. Ensure the questions are well-structured, with a clear problem statement, input format, output format, and example(s) for clarity. 4. Output the questions in the following format: Question 1: [Title of the Question] Problem Statement: [Provide a clear description of the problem.] Input Format: [Specify the format of input(s).] Output Format: [Specify the format of output(s).] Constraints: [Mention constraints, if applicable.] Example(s): - Input: [Provide sample input] - Output: [Provide corresponding output] Question 2: [Title of the Question] Problem Statement: [Provide a clear description of the problem.] Input Format: [Specify the format of input(s).] Output Format: [Specify the format of output(s).] Constraints: [Mention constraints, if applicable.] Example(s): - Input: [Provide sample input] - Output: [Provide corresponding output] """} # Invoke the language model to generate MCQs response = chain.invoke(query) memory.save_context(query, {"output": response}) # Return the generated MCQs as a string return response # Function to Interview start using LLM def interview(job_description_keywords): # Construct the query query = {"human_input": f""" You are a real-time expert interviewer with in-depth knowledge of various industries, job roles, and market trends. Your task is to conduct an interview for a specific job role based on the given keywords: {job_description_keywords}. Analyze the keywords to fully understand the role's responsibilities, required skills, and challenges. Use this understanding to ask relevant and impactful interview questions. Rules: 1. Begin the interview with a self-introduction question to ease the candidate into the process. 2. Ask 10 highly effective, real-world interview questions tailored to the role, progressing from general to more specific and challenging. 3. Ensure the questions focus on assessing the candidate’s practical knowledge, problem-solving skills, and ability to handle real-world scenarios. 4. Incorporate situational and behavioral questions to evaluate how the candidate handles challenges and decision-making. 5. The last two questions must delve into the candidate’s past projects, focusing on: - The project's purpose and goals. - Challenges faced and how they were addressed. - Impact and measurable outcomes. 6. Provide one question at a time, without additional context, explanations, or formatting. 7. Questions must be clear, concise, and aligned with the job role, ensuring they reflect real-time industry expectations. Start the interview with the first question. """} # Invoke the language model to generate MCQs response = chain.invoke(query) memory.save_context(query, {"output": response}) # Return the generated MCQs as a string return response # Initialize Google Generative AI chat model def initialize_chat_model(): with open("key.txt", "r") as f: GOOGLE_API_KEY = f.read().strip() chat_model = ChatGoogleGenerativeAI( google_api_key=GOOGLE_API_KEY, model="gemini-1.5-pro-latest", temperature=0.4, max_tokens=2000, timeout=120, max_retries=5, top_p=0.9, top_k=40, presence_penalty=0.6, frequency_penalty=0.3 ) return chat_model chat_model = initialize_chat_model() # Create Chat Template chat_prompt_template = ChatPromptTemplate.from_messages( [ SystemMessage( content=""" You are a language model designed to follow user instructions exactly as given. Do not take any actions or provide any information unless specifically directed by the user. Your role is to fulfill the user's requests precisely without deviating from the instructions provided.""" ), MessagesPlaceholder(variable_name="chat_history"), HumanMessagePromptTemplate.from_template("{human_input}") ] ) # Initialize the Memory memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) # Create an Output Parser output_parser = StrOutputParser() # Define a chain chain = RunnablePassthrough.assign( chat_history=RunnableLambda(lambda human_input: memory.load_memory_variables(human_input)['chat_history']) ) | chat_prompt_template | chat_model | output_parser # Streamlit App st.title("Interview Preparation with AI") st.markdown("## Part-1: Upload Files, Summarize, and Extract Keywords") # File upload section file1 = st.file_uploader("Upload your resume (PDF or DOCX):", type=["pdf", "docx"]) file2 = st.file_uploader("Upload the job description (PDF or DOCX):", type=["pdf", "docx"]) if file1 and file2: try: # Detect file type and extract text for file 1 if file1.name.endswith('.pdf'): text1 = extract_text_from_pdf(file1) elif file1.name.endswith('.docx'): text1 = extract_text_from_word(file1) else: st.error("Unsupported file type for file 1") # Detect file type and extract text for file 2 if file2.name.endswith('.pdf'): text2 = extract_text_from_pdf(file2) elif file2.name.endswith('.docx'): text2 = extract_text_from_word(file2) else: st.error("Unsupported file type for file 2") # Ensure session state variables are initialized # if "ats_score_calculated" not in st.session_state: # st.session_state.ats_score_calculated = False if 'resume_keywords' not in st.session_state: st.session_state.resume_keywords = text1 if 'job_description_keywords' not in st.session_state: st.session_state.job_description_keywords = text2 # Button to Calculate ATS Score if st.button("ATS Score"): #or st.session_state.ats_score_calculated: #st.session_state.ats_score_calculated = True st.markdown("### ATS Score Calculation") query = {"human_input": f""" Act as an expert ATS (Applicant Tracking System) analyst with 15+ years of experience in HR tech. Perform deep analysis of this resume and job description with military precision: Job Description: {job_description_keywords} Resume: {resume_keywords} Execute these steps systematically: 1. INITIAL ANALYSIS PHASE a. Extract ALL requirements from job description with priority levels (mandatory/nice-to-have) b. Parse resume with entity recognition (skills, dates, roles, certifications) c. Create skill ontology mapping between job requirements and resume content 2. REQUIREMENTS BREAKDOWN ENGINE For EACH job requirement: i. Check exact match in resume - calculate total duration across positions ii. If no direct match: - Split requirement into 3-5 sub-skills using competency framework - For each sub-skill: * Search related terms in resume * Calculate cumulative experience duration * Apply 30% experience decay for indirect matches iii. Apply scoring: - 0.5 pts per year of direct experience (max 4pts/requirement) - 0.3 pts per year of indirect sub-skill experience (max 2pts/sub-skill) - -1.5 pts for missing mandatory requirements 3. EXPERIENCE CALCULATION MATRIX a. Parse employment dates with month/year precision b. For overlapping positions: apply parallel experience weighting (x1.2) c. Convert all experience to decimal years (e.g., 1y 6m = 1.5) d. For management roles: add 0.5y virtual experience per subordinate 4. SECTION OPTIMIZATION SCORECARD Analyze and score (0-100) with weighted impact: - Technical Skills Match (35% weight) - Experience Duration (25%) - Education/Certifications (15%) - Keyword Density (10%) - Project Relevance (10%) - Leadership Keywords (5%) 5. REAL-TIME ATS SCORE REPORT Generate structured output with: A. Requirement Analysis Matrix: | Requirement | Type | Direct Exp | Sub-Skills Matched | Sub-Skill Exp | Score | Hit/Miss | B. Experience Calculation Ledger: | Skill Cluster | Resume Terms | Positions Matched | Raw Duration | Weighted Duration | C. Gap Analysis: - Top 3 missing requirements - Under-qualified areas with improvement roadmap D. Final ATS Score Breakdown: - Base Score (0-100) - Bonus Points (certifications, premium education) - Penalties (missing mandatory) - Final Adjusted Score E. Optimization Recommendations: - Exact terminology to add - Strategic placement suggestions - Skill emphasis ratios F. Give the final overal ATS score FORMAT REQUIREMENTS: - Use markdown tables with exact duration calculations - Show intermediate scoring computations - Highlight critical matches/misses with icons (✅/⚠️/❌) - Include confidence intervals for experience calculations - Add time-adjusted scoring (older experience = 0.8 decay/year) - Normalize scores against industry benchmarks Deliver professional-grade analysis suitable for enterprise HR decisions. """} response = chain.invoke(query) memory.save_context(query, {"output": response}) st.write(response) if 'questions' not in st.session_state: # Your MCQ string goes here mcq_list = generate_mcqs(st.session_state.job_description_keywords) st.session_state.questions = parse_mcq_questions(mcq_list) if 'current_question' not in st.session_state: st.session_state.current_question = 0 if 'answers' not in st.session_state: st.session_state.answers = [] if "mcq_button" not in st.session_state: st.session_state.mcq_button = False if st.button("MCQ Test") or st.session_state.mcq_button: st.session_state.mcq_button = True # Display current question number and total questions st.write(f"Question {st.session_state.current_question + 1} of {len(st.session_state.questions)}") # Display current question current_q = st.session_state.questions[st.session_state.current_question] st.write(current_q['question']) # Create radio buttons for options with the corrected format_func answer = st.radio( "Select your answer:", options=['A', 'B', 'C', 'D'], # List of option keys format_func=lambda x: f"{x}) {current_q['options'].get(x, ' ')}", key=f"question_{st.session_state.current_question}" # Unique key per question ) # Navigation buttons in columns col1, col2 = st.columns(2) if st.session_state.current_question > 0: with col1: if st.button("Previous"): st.session_state.current_question -= 1 st.rerun() if st.session_state.current_question < len(st.session_state.questions) - 1: with col2: if st.button("Next"): st.session_state.answers.append(f"{st.session_state.current_question + 1}-{answer}") st.session_state.current_question += 1 st.rerun() else: with col2: if st.button("Submit"): st.session_state.answers.append(f"{st.session_state.current_question + 1}-{answer}") st.write("Quiz completed! Your answers:") query = {"human_input": f""" You are an advanced AI model trained to evaluate answers for high-quality multiple-choice questions (MCQs). Act as an expert professional in all relevant skills and concepts, analyzing the user's answers in detail. Follow these instructions: 1. Evaluate the provided answers : {st.session_state.answers} against the correct answers for the MCQs. 2. Award 1 mark for each correct answer. Determine if each answer is correct or incorrect. 3. For incorrect answers: - Analyze deeply to identify the specific concepts or subtopics within the skill where the user is struggling. - Provide a focused list of concepts the user needs to improve on, derived from the incorrect answers. 4. At the end of the evaluation, output: - Total marks scored (out of 10). - A detailed and analyzed one by one list of concepts to focus on, ensuring they address the root areas of misunderstanding or lack of knowledge. Output **only** the following information: - Total marks scored: X/10 - Concepts to focus on: [Provide an analyzed and specific list of concepts derived from incorrect answers] """} response = chain.invoke(query) memory.save_context(query, {"output": response}) st.session_state.mcq_button = False #st.write(response) #st.write(st.session_state.answers) if "generate_questions_button" not in st.session_state: st.session_state.generate_questions_button = False if st.button("Generate Questions") or st.session_state.generate_questions_button: st.session_state.generate_questions_button = True # Generate questions if 'questions_response' not in st.session_state: st.session_state.questions_response = generate_questions(st.session_state.job_description_keywords) # Split questions code_questions = [q.strip() for q in st.session_state.questions_response.split("Question")[1:]] code_questions = [f"Question{q}" for q in code_questions] # Display questions and collect answers st.session_state.code_questions = code_questions st.session_state.coding_answers = [""] * len(code_questions) # Display each question with a text area for answers for i, question in enumerate(code_questions): st.markdown(f"### {question}") cod_answer = st.text_area(f"Your Answer for Question {i+1}", key=f"answer_{i}") st.session_state.coding_answers[i] = cod_answer if st.button("Submit Answers"): st.write("### Submitted Answers:") #st.write(st.session_state.coding_answers) query = {"human_input": f""" Evaluate the following user responses to two coding questions: **User Responses:** {st.session_state.coding_answers} **Evaluation Criteria:** * **Perfection:** Each question carries 10 marks. * **Assess:** * Correctness of the code logic and implementation. * Efficiency of the solution (time and space complexity). * Code readability, maintainability, and adherence to best practices. * Handling of edge cases and potential errors. **Output:** * **Marks:** * **Question 1:** [Out of 10 marks] * **Question 2:** [Out of 10 marks] * **Analysis:** * Identify areas where the user needs to improve. * Suggest specific topics or concepts for further study and practice. * Provide constructive feedback on the user's approach and coding style. **Note:** * Provide a concise and informative evaluation. * Avoid vague or generic feedback. """} response = chain.invoke(query) memory.save_context(query, {"output": response}) st.session_state.generate_questions_button = False st.write(response) if "Interview_questions_button" not in st.session_state: st.session_state.Interview_questions_button = False if st.button("Interview Questions") or st.session_state.Interview_questions_button: st.session_state.Interview_questions_button = True if 'flag' not in st.session_state: st.session_state.flag = 1 if st.session_state.flag <= 10 : if 'interview_questions' not in st.session_state: st.session_state.interview_questions = interview(st.session_state.job_description_keywords) st.write(st.session_state.interview_questions) #Input from the user using chat_input human_prompt = st.chat_input(" Message Pega ...") if True : query = {"human_input": "I am Bhanu prasad, I have completed my graduation in 2024"} query1 = {"human_input": human_prompt} st.write(query) st.write(query1) response = chain.invoke(query1) memory.save_context(query, {"output": response}) st.write(response) st.session_state.flag += 1 except Exception as e: st.error(f"An error occurred: {e}") else: st.info("Please upload both files to proceed.")