CR7CAD commited on
Commit
11cb389
·
verified ·
1 Parent(s): 2924dc7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -50
app.py CHANGED
@@ -4,7 +4,6 @@ import streamlit as st
4
  import docx
5
  import docx2txt
6
  import tempfile
7
- import numpy as np
8
  import time
9
  import re
10
  import concurrent.futures
@@ -36,8 +35,14 @@ def load_models():
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="TinyLlama/TinyLlama-1.1B-Chat-v1.0", max_new_tokens=150)
 
 
 
 
 
 
41
 
42
  return models
43
 
@@ -296,64 +301,86 @@ def summarize_resume_text(resume_text):
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,7 +391,7 @@ st.markdown(
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,9 +429,9 @@ if uploaded_file is not None and company_prompt and st.button("Analyze Resume"):
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)
@@ -414,16 +441,20 @@ if uploaded_file is not None and company_prompt and st.button("Analyze Resume"):
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")
 
4
  import docx
5
  import docx2txt
6
  import tempfile
 
7
  import time
8
  import re
9
  import concurrent.futures
 
35
  # Load smaller summarization model for speed
36
  models['summarizer'] = pipeline("summarization", model="facebook/bart-large-cnn", max_length=130)
37
 
38
+ # Load TinyLlama model for evaluation
39
+ models['evaluator'] = pipeline(
40
+ "text-generation",
41
+ model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
42
+ max_new_tokens=200,
43
+ do_sample=True,
44
+ temperature=0.7
45
+ )
46
 
47
  return models
48
 
 
301
  return formatted_summary, execution_time
302
 
303
  #####################################
304
+ # Function: Evaluate with TinyLlama
305
  #####################################
306
  @st.cache_data(show_spinner=False)
307
+ def evaluate_with_tiny_llama(candidate_summary, company_info, _evaluator=None):
308
  """
309
+ Use TinyLlama to evaluate the match between a candidate's resume and company requirements.
 
310
  """
311
  start_time = time.time()
312
 
313
  evaluator = _evaluator or models['evaluator']
314
 
315
+ # Format the chat prompt for TinyLlama's chat format
316
+ prompt = f"""<|im_start|>system
317
+ You are an expert HR recruiter. Your task is to evaluate how well a candidate's profile matches with a company's requirements. Be concise but thorough in your evaluation.
318
+ <|im_end|>
319
+
320
+ <|im_start|>user
321
+ I need to evaluate a job candidate against company requirements. Please:
322
+ 1. Analyze the match between the candidate and the position
323
+ 2. Give a suitability score from 0-100
324
+ 3. Provide 2-3 sentences explaining your evaluation
325
+ 4. List the top 3 strengths of the candidate for this role
326
+ 5. List 1-2 potential gaps if any
327
 
328
  Candidate Profile:
329
  {candidate_summary}
330
 
331
+ Company Requirements:
332
+ {company_info}
333
+ <|im_end|>
334
 
335
+ <|im_start|>assistant
 
 
336
  """
337
 
338
+ # Generate the response
339
+ response = evaluator(prompt)[0]['generated_text']
340
 
341
+ # Extract just the assistant's response after the prompt
342
+ assistant_response_start = response.find("<|im_start|>assistant") + len("<|im_start|>assistant")
343
+ assistant_response = response[assistant_response_start:].strip()
 
 
 
 
 
 
344
 
345
+ # Remove any trailing tag if present
346
+ if "<|im_end|>" in assistant_response:
347
+ assistant_response = assistant_response.split("<|im_end|>")[0].strip()
348
+
349
+ # Try to extract the score from the response
350
+ score_match = re.search(r'(\d{1,3})/100|score:?\s*(\d{1,3})|rating:?\s*(\d{1,3})|suitability:?\s*(\d{1,3})',
351
+ assistant_response.lower())
352
+
353
+ if score_match:
354
+ # Find the first group that matched and isn't None
355
+ for group in score_match.groups():
356
+ if group is not None:
357
+ score = int(group)
358
+ normalized_score = min(100, max(0, score)) / 100 # Ensure it's in 0-1 range
359
  break
360
  else:
361
+ normalized_score = 0.5 # Default if no group was extracted
362
+ else:
363
+ # If no explicit score, try to infer from sentiments
364
+ positive_words = ['excellent', 'perfect', 'outstanding', 'ideal', 'great']
365
+ negative_words = ['poor', 'inadequate', 'insufficient', 'lacks', 'mismatch']
366
+
367
+ positive_count = sum(assistant_response.lower().count(word) for word in positive_words)
368
+ negative_count = sum(assistant_response.lower().count(word) for word in negative_words)
369
+
370
+ if positive_count > negative_count * 2:
371
+ normalized_score = 0.85
372
+ elif positive_count > negative_count:
373
+ normalized_score = 0.7
374
+ elif negative_count > positive_count * 2:
375
+ normalized_score = 0.3
376
+ elif negative_count > positive_count:
377
+ normalized_score = 0.4
378
+ else:
379
+ normalized_score = 0.5
380
 
381
  execution_time = time.time() - start_time
382
 
383
+ return normalized_score, assistant_response, execution_time
384
 
385
  #####################################
386
  # Main Streamlit Interface - with Progress Reporting
 
391
  Upload your resume file in **.docx**, **.doc**, or **.txt** format. The app performs the following tasks:
392
  1. Extracts text from the resume.
393
  2. Uses AI to generate a structured candidate summary with name, age, expected job industry, previous work experience, and skills.
394
+ 3. Uses TinyLlama AI to evaluate the candidate's suitability for the company and provide detailed feedback.
395
  """
396
  )
397
 
 
429
  st.markdown(summary)
430
  st.info(f"Summary generated in {summarization_time:.2f} seconds")
431
 
432
+ # Step 3: Evaluate with TinyLlama
433
+ status_text.text("Step 3/3: Evaluating candidate with TinyLlama...")
434
+ suitability_score, evaluation, evaluation_time = evaluate_with_tiny_llama(
435
  summary, company_prompt, _evaluator=models['evaluator']
436
  )
437
  progress_bar.progress(100)
 
441
 
442
  # Display suitability results
443
  st.subheader("Suitability Assessment")
 
444
 
445
+ # Display score with appropriate color
446
+ score_percent = int(suitability_score * 100)
447
  if suitability_score >= 0.85:
448
+ st.success(f"**Matching Score:** {score_percent}%")
449
  elif suitability_score >= 0.70:
450
+ st.success(f"**Matching Score:** {score_percent}%")
451
  elif suitability_score >= 0.50:
452
+ st.warning(f"**Matching Score:** {score_percent}%")
453
  else:
454
+ st.error(f"**Matching Score:** {score_percent}%")
455
+
456
+ # Display the full evaluation
457
+ st.markdown("### Detailed Evaluation")
458
+ st.markdown(evaluation)
459
 
460
+ st.info(f"Evaluation completed in {evaluation_time:.2f} seconds using TinyLlama")