Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -5,7 +5,6 @@ import docx
|
|
5 |
import docx2txt
|
6 |
import tempfile
|
7 |
import numpy as np
|
8 |
-
from scipy.spatial.distance import cosine
|
9 |
import time
|
10 |
import re
|
11 |
import concurrent.futures
|
@@ -37,8 +36,8 @@ def load_models():
|
|
37 |
# Load smaller summarization model for speed
|
38 |
models['summarizer'] = pipeline("summarization", model="facebook/bart-large-cnn", max_length=130)
|
39 |
|
40 |
-
# Load
|
41 |
-
models['
|
42 |
|
43 |
return models
|
44 |
|
@@ -297,33 +296,64 @@ def summarize_resume_text(resume_text):
|
|
297 |
return formatted_summary, execution_time
|
298 |
|
299 |
#####################################
|
300 |
-
# Function:
|
301 |
#####################################
|
302 |
-
# Fixed: Use underscore prefix for non-hashable arguments to tell Streamlit not to hash them
|
303 |
@st.cache_data(show_spinner=False)
|
304 |
-
def
|
305 |
"""
|
306 |
-
|
307 |
-
|
308 |
"""
|
309 |
start_time = time.time()
|
310 |
|
311 |
-
|
312 |
|
313 |
-
#
|
314 |
-
|
315 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
316 |
|
317 |
-
#
|
318 |
-
|
319 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
|
321 |
-
#
|
322 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
|
324 |
execution_time = time.time() - start_time
|
325 |
|
326 |
-
return
|
327 |
|
328 |
#####################################
|
329 |
# Main Streamlit Interface - with Progress Reporting
|
@@ -334,7 +364,7 @@ st.markdown(
|
|
334 |
Upload your resume file in **.docx**, **.doc**, or **.txt** format. The app performs the following tasks:
|
335 |
1. Extracts text from the resume.
|
336 |
2. Uses AI to generate a structured candidate summary with name, age, expected job industry, previous work experience, and skills.
|
337 |
-
3.
|
338 |
"""
|
339 |
)
|
340 |
|
@@ -372,26 +402,28 @@ if uploaded_file is not None and company_prompt and st.button("Analyze Resume"):
|
|
372 |
st.markdown(summary)
|
373 |
st.info(f"Summary generated in {summarization_time:.2f} seconds")
|
374 |
|
375 |
-
# Step 3:
|
376 |
-
status_text.text("Step 3/3:
|
377 |
-
|
378 |
-
|
|
|
379 |
progress_bar.progress(100)
|
380 |
|
381 |
# Clear status messages
|
382 |
status_text.empty()
|
383 |
|
384 |
-
# Display
|
385 |
st.subheader("Suitability Assessment")
|
386 |
-
st.markdown(f"**Matching Score:** {
|
387 |
-
st.info(f"Compatibility assessment completed in {similarity_time:.2f} seconds")
|
388 |
|
389 |
-
#
|
390 |
-
if
|
391 |
-
st.success("
|
392 |
-
elif
|
393 |
-
st.success("
|
394 |
-
elif
|
395 |
-
st.warning("
|
396 |
else:
|
397 |
-
st.error("
|
|
|
|
|
|
5 |
import docx2txt
|
6 |
import tempfile
|
7 |
import numpy as np
|
|
|
8 |
import time
|
9 |
import re
|
10 |
import concurrent.futures
|
|
|
36 |
# Load smaller summarization model for speed
|
37 |
models['summarizer'] = pipeline("summarization", model="facebook/bart-large-cnn", max_length=130)
|
38 |
|
39 |
+
# Load Phi-4 model for evaluation
|
40 |
+
models['evaluator'] = pipeline("text-generation", model="microsoft/Phi-4-mini-instruct", max_new_tokens=150)
|
41 |
|
42 |
return models
|
43 |
|
|
|
296 |
return formatted_summary, execution_time
|
297 |
|
298 |
#####################################
|
299 |
+
# Function: Evaluate Candidate with Phi-4
|
300 |
#####################################
|
|
|
301 |
@st.cache_data(show_spinner=False)
|
302 |
+
def evaluate_suitability(candidate_summary, company_prompt, _evaluator=None):
|
303 |
"""
|
304 |
+
Use the Phi-4 model to evaluate the suitability of a candidate
|
305 |
+
based on their resume summary and the company requirements.
|
306 |
"""
|
307 |
start_time = time.time()
|
308 |
|
309 |
+
evaluator = _evaluator or models['evaluator']
|
310 |
|
311 |
+
# Craft a prompt for the model
|
312 |
+
prompt = f"""You are an expert HR recruiter. Analyze the candidate's profile and the job requirements to provide:
|
313 |
+
1. A suitability score from 0 to 100
|
314 |
+
2. A brief evaluation explaining why the candidate is or isn't suitable
|
315 |
+
|
316 |
+
Candidate Profile:
|
317 |
+
{candidate_summary}
|
318 |
+
|
319 |
+
Job Requirements:
|
320 |
+
{company_prompt}
|
321 |
+
|
322 |
+
Give your assessment in this format:
|
323 |
+
Score: [0-100]
|
324 |
+
Evaluation: [Your brief evaluation]
|
325 |
+
"""
|
326 |
+
|
327 |
+
# Generate the evaluation with Phi-4
|
328 |
+
result = evaluator(prompt, do_sample=True, temperature=0.3)[0]['generated_text']
|
329 |
|
330 |
+
# Extract the score and evaluation from the result
|
331 |
+
score_match = re.search(r'Score:\s*(\d+)', result)
|
332 |
+
if score_match:
|
333 |
+
score = int(score_match.group(1))
|
334 |
+
# Normalize to 0-1 range
|
335 |
+
normalized_score = score / 100
|
336 |
+
else:
|
337 |
+
# Default score if extraction fails
|
338 |
+
normalized_score = 0.5
|
339 |
|
340 |
+
# Extract the evaluation text
|
341 |
+
evaluation_match = re.search(r'Evaluation:(.*?)($|\n\n)', result, re.DOTALL)
|
342 |
+
if evaluation_match:
|
343 |
+
evaluation = evaluation_match.group(1).strip()
|
344 |
+
else:
|
345 |
+
# Extract text after "Score:" line if specific evaluation format is not found
|
346 |
+
lines = result.split('\n')
|
347 |
+
for i, line in enumerate(lines):
|
348 |
+
if 'Score:' in line and i+1 < len(lines):
|
349 |
+
evaluation = '\n'.join(lines[i+1:]).strip()
|
350 |
+
break
|
351 |
+
else:
|
352 |
+
evaluation = "The candidate's profile has been evaluated based on the job requirements."
|
353 |
|
354 |
execution_time = time.time() - start_time
|
355 |
|
356 |
+
return normalized_score, evaluation, execution_time
|
357 |
|
358 |
#####################################
|
359 |
# Main Streamlit Interface - with Progress Reporting
|
|
|
364 |
Upload your resume file in **.docx**, **.doc**, or **.txt** format. The app performs the following tasks:
|
365 |
1. Extracts text from the resume.
|
366 |
2. Uses AI to generate a structured candidate summary with name, age, expected job industry, previous work experience, and skills.
|
367 |
+
3. Uses Phi-4 AI to evaluate the candidate's suitability for the company and provide feedback.
|
368 |
"""
|
369 |
)
|
370 |
|
|
|
402 |
st.markdown(summary)
|
403 |
st.info(f"Summary generated in {summarization_time:.2f} seconds")
|
404 |
|
405 |
+
# Step 3: Evaluate candidate with Phi-4
|
406 |
+
status_text.text("Step 3/3: Evaluating candidate suitability with Phi-4...")
|
407 |
+
suitability_score, evaluation, evaluation_time = evaluate_suitability(
|
408 |
+
summary, company_prompt, _evaluator=models['evaluator']
|
409 |
+
)
|
410 |
progress_bar.progress(100)
|
411 |
|
412 |
# Clear status messages
|
413 |
status_text.empty()
|
414 |
|
415 |
+
# Display suitability results
|
416 |
st.subheader("Suitability Assessment")
|
417 |
+
st.markdown(f"**Matching Score:** {suitability_score:.0%}")
|
|
|
418 |
|
419 |
+
# Display colored evaluation box based on score
|
420 |
+
if suitability_score >= 0.85:
|
421 |
+
st.success(f"**Evaluation:** {evaluation}")
|
422 |
+
elif suitability_score >= 0.70:
|
423 |
+
st.success(f"**Evaluation:** {evaluation}")
|
424 |
+
elif suitability_score >= 0.50:
|
425 |
+
st.warning(f"**Evaluation:** {evaluation}")
|
426 |
else:
|
427 |
+
st.error(f"**Evaluation:** {evaluation}")
|
428 |
+
|
429 |
+
st.info(f"Evaluation completed in {evaluation_time:.2f} seconds")
|