CR7CAD commited on
Commit
c9f8450
·
verified ·
1 Parent(s): adf6e8f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -34
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 smaller feature extraction model for speed
41
- models['feature_extractor'] = pipeline("feature-extraction", model="roberta-base")
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: Compare Candidate Summary to Company Prompt - Optimized
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 compute_suitability(candidate_summary, company_prompt, _feature_extractor=None):
305
  """
306
- Compute the similarity between candidate summary and company prompt.
307
- Returns a score in the range [0, 1] and execution time.
308
  """
309
  start_time = time.time()
310
 
311
- feature_extractor = _feature_extractor or models['feature_extractor']
312
 
313
- # Extract features (embeddings)
314
- candidate_features = feature_extractor(candidate_summary)
315
- company_features = feature_extractor(company_prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
- # Convert to numpy arrays and flatten if needed
318
- candidate_vec = np.mean(np.array(candidate_features[0]), axis=0)
319
- company_vec = np.mean(np.array(company_features[0]), axis=0)
 
 
 
 
 
 
320
 
321
- # Compute cosine similarity (1 - cosine distance)
322
- similarity = 1 - cosine(candidate_vec, company_vec)
 
 
 
 
 
 
 
 
 
 
 
323
 
324
  execution_time = time.time() - start_time
325
 
326
- return similarity, execution_time
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. Compares the candidate summary with a company profile to produce a suitability score.
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: Compute similarity
376
- status_text.text("Step 3/3: Calculating compatibility with company profile...")
377
- # Pass the feature extractor with an underscore prefix to avoid hashing issues
378
- similarity_score, similarity_time = compute_suitability(summary, company_prompt, _feature_extractor=models['feature_extractor'])
 
379
  progress_bar.progress(100)
380
 
381
  # Clear status messages
382
  status_text.empty()
383
 
384
- # Display similarity score
385
  st.subheader("Suitability Assessment")
386
- st.markdown(f"**Matching Score:** {similarity_score:.2%}")
387
- st.info(f"Compatibility assessment completed in {similarity_time:.2f} seconds")
388
 
389
- # Provide interpretation
390
- if similarity_score >= 0.85:
391
- st.success("Excellent match! This candidate's profile is strongly aligned with the company requirements.")
392
- elif similarity_score >= 0.70:
393
- st.success("Good match! This candidate shows strong potential for the position.")
394
- elif similarity_score >= 0.50:
395
- st.warning("Moderate match. The candidate meets some requirements but there may be gaps.")
396
  else:
397
- st.error("Low match. The candidate's profile may not align well with the requirements.")
 
 
 
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")