Kunjan Shah commited on
Commit
a1b40b1
Β·
1 Parent(s): 5f7f0b8

Added Question Generation Part

Browse files
Files changed (1) hide show
  1. core/input_comp_gen.py +190 -44
core/input_comp_gen.py CHANGED
@@ -1,21 +1,118 @@
1
  import streamlit as st
2
  import PyPDF2
3
- #import io
 
 
 
 
 
 
 
 
 
 
4
 
5
  def read_resume(file):
6
- reader=PyPDF2.PdfReader(file)
7
- text= ""
 
 
 
 
 
 
 
 
8
  for page in reader.pages:
9
  text += page.extract_text() + "\n"
10
  return text.strip()
11
 
12
- def read_job_description(file):
 
13
  return file.read().decode("utf-8").strip()
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  #=== Sidebar: User Inputs ===#
17
  with st.sidebar:
18
- st.title("AI_Job_Matcher – HiredGPT Duel")
19
 
20
  #1 Resume Upload
21
  st.header("1. Upload Your Resume")
@@ -44,11 +141,13 @@ with st.sidebar:
44
  #5 Submit Button
45
  submit = st.button("Submit - (Next: generate rival resume and interview questions)")
46
 
 
 
 
47
 
48
  #=== Main Area: Q&A and Duel ===#
49
  if submit:
50
-
51
- # Input Validation
52
  errors = []
53
  if not user_resume:
54
  errors.append("Please upload your resume (PDF).")
@@ -64,72 +163,119 @@ if submit:
64
  st.toast(error, icon="⚠️")
65
  st.stop()
66
 
67
-
68
- # Resume and Job Description Processing
69
  resume_text = ""
70
  if user_resume:
71
  try:
72
  resume_text = read_resume(user_resume)
73
- st.success("Resume uploaded successfully!")
 
74
  except Exception as e:
75
- st.error(f"Error reading resume: {e}")
76
- else:
77
- st.error("Please upload your resume.")
78
 
79
- jd_text=''
80
  if job_desc_file:
81
  try:
82
  if job_desc_file.type == "application/pdf":
83
- jd_text = read_job_description(job_desc_file)
84
  elif job_desc_file.type == "text/plain":
85
- jd_text = job_desc_file.read().decode("utf-8").strip()
86
  else:
87
  st.error("Unsupported file type for job description.")
 
 
 
88
  except Exception as e:
89
- st.error(f"Error reading job description: {e}")
 
 
90
  elif job_desc_text:
91
  jd_text = job_desc_text.strip()
92
- st.success("Job description successfully read.")
 
93
  else:
94
  st.error("Please provide the job description to continue.")
 
95
 
96
  if resume_text and jd_text:
97
- st.success("Resume & JD Preview")
98
- with st.expander("View Resume", expanded=False):
99
- st.write(resume_text[:3000])
100
- with st.expander("View Job Description", expanded=False):
101
- st.write(jd_text[:3000])
 
 
102
 
103
- st.header("Interview Duel")
 
 
 
 
 
 
104
 
105
- questions = ["Tell me about yourself.", "Describe a challenge at work and how you solved it."]
106
- if "q_num" not in st.session_state:
107
- st.session_state.q_num = 0
108
-
109
- question = questions[st.session_state.q_num]
110
- st.subheader(f"Question {st.session_state.q_num+1}: {question}")
 
 
 
 
111
 
112
  # Duel columns: User vs LLM
113
  col1, col2 = st.columns(2)
114
  with col1:
115
- st.markdown("**Your Answer:**")
116
- user_answer = st.text_area("Type your answer here", key=f"user_ans_{st.session_state.q_num}")
117
 
118
  with col2:
119
- st.markdown("**Star Applicant's Answer:**")
120
- if st.button("Generate Rival's Answer"):
121
  # Place your LLM call here to get the rival's answer
122
  llm_answer = "This is how the star applicant would answer."
123
  st.write(llm_answer)
124
 
125
  # Scoring/Feedback
126
- if st.button("Score & Feedback"):
127
- # Call LLM to compare and score
128
- st.success("Your Score: 7/10\nStar Applicant's Score: 9/10")
129
- st.info("Tip: Give more specific examples to boost your answer.")
130
-
131
- # Next question navigation (simple MVP)
132
- if st.session_state.q_num < len(questions) - 1:
133
- if st.button("Next Question"):
134
- st.session_state.q_num += 1
135
- st.experimental_rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import PyPDF2
3
+ import os
4
+ import json # Added import for JSON parsing
5
+ from dotenv import load_dotenv
6
+ from openai import OpenAI
7
+
8
+ load_dotenv()
9
+ api_key = os.getenv("GEMINI_API_KEY")
10
+ client = OpenAI(
11
+ api_key=api_key,
12
+ base_url="https://generativelanguage.googleapis.com/v1beta/openai/"
13
+ )
14
 
15
  def read_resume(file):
16
+ reader = PyPDF2.PdfReader(file)
17
+ text = ""
18
+ for page in reader.pages:
19
+ text += page.extract_text() + "\n"
20
+ return text.strip()
21
+
22
+ def read_job_description_pdf(file):
23
+ """Read PDF job description"""
24
+ reader = PyPDF2.PdfReader(file)
25
+ text = ""
26
  for page in reader.pages:
27
  text += page.extract_text() + "\n"
28
  return text.strip()
29
 
30
+ def read_job_description_txt(file):
31
+ """Read TXT job description"""
32
  return file.read().decode("utf-8").strip()
33
 
34
+ def generate_question(resume_text, job_desc_text, job_role):
35
+ prompt = f"""
36
+ You are an interview coach. Based on this resume and job description, generate exactly 5 interview questions for the role "{job_role}".
37
+
38
+ Resume:
39
+ {resume_text}
40
+
41
+ Job Description:
42
+ {job_desc_text}
43
+
44
+ Generate 5 relevant interview questions. Include follow-up prompts in parentheses if needed.
45
+ You must respond with ONLY a valid JSON array of strings. No explanations, no markdown, just the JSON array.
46
+
47
+ Example format: ["Question 1 here", "Question 2 here", "Question 3 here", "Question 4 here", "Question 5 here"]
48
+ """
49
+ response = client.chat.completions.create(
50
+ model="gemini-2.0-flash-lite",
51
+ messages=[
52
+ {"role": "system", "content": "You are a JSON generator. You only respond with valid JSON arrays. Never include explanations or markdown formatting."},
53
+ {"role": "user", "content": prompt}
54
+ ],
55
+ temperature=0.7,
56
+
57
+ )
58
+ text = response.choices[0].message.content.strip()
59
+ #st.write("Raw API Response:", text) # Debug line
60
+
61
+ try:
62
+ # Debug: Check if response is empty
63
+ if not text:
64
+ st.error("Empty response from API")
65
+ return default_questions()
66
+
67
+ # Debug: Try to clean the response
68
+ cleaned_text = text
69
+ if not text.startswith('['):
70
+ #st.warning("Response not in JSON format, attempting to clean...")
71
+ # Find JSON array markers
72
+ start_idx = text.find('[')
73
+ end_idx = text.rfind(']')
74
+
75
+ if start_idx != -1 and end_idx != -1:
76
+ cleaned_text = text[start_idx:end_idx + 1]
77
+ #st.write("Cleaned text:", cleaned_text) # Debug line
78
+ else:
79
+ #st.error("Could not find JSON array markers")
80
+ return default_questions()
81
+
82
+ # Try parsing the cleaned JSON
83
+ questions = json.loads(cleaned_text)
84
+ return questions
85
+
86
+ except json.JSONDecodeError as e:
87
+ st.error(f"JSON Parse Error: {str(e)}")
88
+ st.error(f"Problem at position: {e.pos}")
89
+ st.error(f"Problem line: {e.lineno}, col: {e.colno}")
90
+ return default_questions()
91
+ except Exception as e:
92
+ st.error(f"Other error: {str(e)}")
93
+ return default_questions()
94
+
95
+ def default_questions():
96
+ """Fallback questions if API fails"""
97
+ return [
98
+ "Tell me about your relevant experience for this role.",
99
+ "What are your key strengths and weaknesses?",
100
+ "Why are you interested in this position?",
101
+ "Describe a challenging project you've worked on.",
102
+ "What questions do you have about the role?"
103
+ ]
104
+
105
+ # Initialize session state variables
106
+ if 'q_num' not in st.session_state:
107
+ st.session_state.q_num = 0
108
+ if 'questions' not in st.session_state:
109
+ st.session_state.questions = []
110
+ if 'processing_complete' not in st.session_state:
111
+ st.session_state.processing_complete = False
112
 
113
  #=== Sidebar: User Inputs ===#
114
  with st.sidebar:
115
+ st.title("AI-Job_Matcher – HiredGPT Duel")
116
 
117
  #1 Resume Upload
118
  st.header("1. Upload Your Resume")
 
141
  #5 Submit Button
142
  submit = st.button("Submit - (Next: generate rival resume and interview questions)")
143
 
144
+ # Status messages in sidebar
145
+ if submit or st.session_state.processing_complete:
146
+ st.subheader("πŸ“‹ Processing Status")
147
 
148
  #=== Main Area: Q&A and Duel ===#
149
  if submit:
150
+ #1 Input Validation
 
151
  errors = []
152
  if not user_resume:
153
  errors.append("Please upload your resume (PDF).")
 
163
  st.toast(error, icon="⚠️")
164
  st.stop()
165
 
166
+ #2 Resume and Job Description Processing
 
167
  resume_text = ""
168
  if user_resume:
169
  try:
170
  resume_text = read_resume(user_resume)
171
+ with st.sidebar:
172
+ st.success("βœ… Resume uploaded successfully!")
173
  except Exception as e:
174
+ with st.sidebar:
175
+ st.error(f"❌ Error reading resume: {e}")
176
+ st.stop()
177
 
178
+ jd_text = ''
179
  if job_desc_file:
180
  try:
181
  if job_desc_file.type == "application/pdf":
182
+ jd_text = read_job_description_pdf(job_desc_file)
183
  elif job_desc_file.type == "text/plain":
184
+ jd_text = read_job_description_txt(job_desc_file)
185
  else:
186
  st.error("Unsupported file type for job description.")
187
+ st.stop()
188
+ with st.sidebar:
189
+ st.success("βœ… Job description uploaded successfully!")
190
  except Exception as e:
191
+ with st.sidebar:
192
+ st.error(f"❌ Error reading job description: {e}")
193
+ st.stop()
194
  elif job_desc_text:
195
  jd_text = job_desc_text.strip()
196
+ with st.sidebar:
197
+ st.success("βœ… Job description text processed!")
198
  else:
199
  st.error("Please provide the job description to continue.")
200
+ st.stop()
201
 
202
  if resume_text and jd_text:
203
+ # Show preview in main area
204
+ st.success("πŸŽ‰ All files processed successfully!")
205
+
206
+ with st.expander("πŸ“„ View Resume Preview", expanded=False):
207
+ st.write(resume_text[:3000] + "..." if len(resume_text) > 3000 else resume_text)
208
+ with st.expander("πŸ“‹ View Job Description Preview", expanded=False):
209
+ st.write(jd_text[:3000] + "..." if len(jd_text) > 3000 else jd_text)
210
 
211
+ #3 Generate Interview Questions
212
+ with st.spinner("πŸ€– Generating interview questions..."):
213
+ st.session_state.questions = generate_question(resume_text, jd_text, job_role)
214
+ st.session_state.processing_complete = True
215
+
216
+ with st.sidebar:
217
+ st.success("βœ… Interview questions generated!")
218
 
219
+ # Show Interview Duel if processing is complete
220
+ if st.session_state.processing_complete and st.session_state.questions:
221
+ st.header("πŸ₯Š Interview Duel")
222
+
223
+ qn = st.session_state.q_num
224
+ questions = st.session_state.questions
225
+
226
+ if qn < len(questions):
227
+ st.subheader(f"Question {qn+1}/{len(questions)}:")
228
+ st.info(questions[qn])
229
 
230
  # Duel columns: User vs LLM
231
  col1, col2 = st.columns(2)
232
  with col1:
233
+ st.markdown("**πŸ‘€ Your Answer:**")
234
+ user_answer = st.text_area("Type your answer here", key=f"user_ans_{qn}", height=150)
235
 
236
  with col2:
237
+ st.markdown("**πŸ€– Rival's Answer:**")
238
+ if st.button("Generate Rival's Answer", key=f"rival_ans_{qn}"):
239
  # Place your LLM call here to get the rival's answer
240
  llm_answer = "This is how the star applicant would answer."
241
  st.write(llm_answer)
242
 
243
  # Scoring/Feedback
244
+ col3, col4, col5 = st.columns([1, 1, 1])
245
+ with col3:
246
+ if st.button("πŸ“Š Score & Feedback", key=f"score_{qn}"):
247
+ # Call LLM to compare and score
248
+ st.success("Your Score: 7/10\nStar Applicant's Score: 9/10")
249
+ st.info("πŸ’‘ Tip: Give more specific examples to boost your answer.")
250
+
251
+ # Next question navigation
252
+ with col4:
253
+ if st.session_state.q_num < len(questions) - 1:
254
+ if st.button("➑️ Next Question", key=f"next_{qn}"):
255
+ st.session_state.q_num += 1
256
+ st.rerun()
257
+ else:
258
+ st.success("πŸŽ‰ Interview Complete!")
259
+
260
+ with col5:
261
+ if st.session_state.q_num > 0:
262
+ if st.button("⬅️ Previous Question", key=f"prev_{qn}"):
263
+ st.session_state.q_num -= 1
264
+ st.rerun()
265
+
266
+ # Progress bar
267
+ progress = (qn + 1) / len(questions)
268
+ st.progress(progress, text=f"Progress: {qn + 1}/{len(questions)} questions")
269
+
270
+ elif st.session_state.processing_complete and not st.session_state.questions:
271
+ st.error("❌ Failed to generate questions. Please try again.")
272
+
273
+ # Reset button in sidebar
274
+ if st.session_state.processing_complete:
275
+ with st.sidebar:
276
+ st.divider()
277
+ if st.button("πŸ”„ Start New Interview", type="secondary"):
278
+ for key in ['q_num', 'questions', 'processing_complete']:
279
+ if key in st.session_state:
280
+ del st.session_state[key]
281
+ st.rerun()