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