safiaa02 commited on
Commit
bfc8ca4
·
verified ·
1 Parent(s): 4ebc57a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +102 -121
app.py CHANGED
@@ -1,146 +1,127 @@
1
  import gradio as gr
2
  import os
3
- from openai import OpenAI
4
- import PyPDF2
5
  import docx2txt
 
6
  import json
 
7
 
8
- # Initialize OpenAI client (for AI/ML API)
9
  client = OpenAI(
10
- api_key=os.environ.get("OPENAI_API_KEY"),
11
- base_url="https://api.aimlapi.com/v1"
12
  )
13
 
14
- # Helper functions
15
- def read_pdf(file_obj):
16
  try:
17
- reader = PyPDF2.PdfReader(file_obj)
18
- text = ""
19
- for page in reader.pages:
20
- page_text = page.extract_text()
21
- if page_text:
22
- text += page_text + "\n"
23
- return text
24
- except Exception as e:
25
  return ""
26
 
27
- def read_docx(file_obj):
28
  try:
29
- return docx2txt.process(file_obj)
30
- except Exception as e:
31
  return ""
32
 
33
- # Core logic
34
- def analyze_resumes(resume_files, job_description, min_years_exp=0):
35
- if not resume_files or not job_description:
36
- return ["⚠️ Please upload resumes and provide a job description."] * 3
37
-
38
- resumes = []
39
- for file in resume_files:
40
- if file.name.endswith('.pdf'):
41
- text = read_pdf(file)
42
- elif file.name.endswith('.docx'):
43
- text = read_docx(file)
44
- else:
45
- text = ""
46
- resumes.append({
47
- "filename": file.name,
48
- "content": text
49
- })
50
-
51
- if not resumes:
52
- return ["⚠️ No valid resumes found."] * 3
53
-
54
- # Build a clear system prompt
55
- system_prompt = f"""
56
- You are SmartHire, an AI Job Screening Assistant.
57
- Tasks:
58
- - Analyze candidate resumes.
59
- - Match against the job description.
60
- - Rank candidates.
61
- - Filter out candidates below {min_years_exp} years of experience.
62
- Respond in JSON format:
63
- [
64
- {{
65
- "name": "Candidate Name (or filename)",
66
- "strengths": ["strength1", "strength2"],
67
- "risks": ["risk1", "risk2"],
68
- "score": number
69
- }},
70
- ...
71
- ]
72
  """
73
-
74
- # Build the user input prompt
75
- user_content = f"Job Description:\n{job_description}\n\nResumes:\n"
76
- for idx, resume in enumerate(resumes):
77
- user_content += f"\n---\nResume {idx+1} ({resume['filename']}):\n{resume['content']}\n"
78
-
79
  try:
80
- # You may try "gpt-4-turbo" if "gpt-4o" yields no content.
81
  response = client.chat.completions.create(
82
- model="gpt-4o",
83
- messages=[
84
- {"role": "system", "content": system_prompt},
85
- {"role": "user", "content": user_content}
86
- ],
87
- temperature=0.2,
88
- max_tokens=4096
89
  )
 
90
  except Exception as e:
91
- return [f"⚠️ AI/ML API Error: {e}"] * 3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- # Debug: check response message content before parsing as JSON
94
- try:
95
- resp_content = response.choices[0].message.content.strip()
96
- if not resp_content:
97
- return ["⚠️ AI response is empty. Please verify your prompt and API usage."] * 3
98
- candidates = json.loads(resp_content)
99
- except Exception as e:
100
- # Optionally, you can print(response) to log the raw output for debugging.
101
- return [f"⚠️ AI response parsing error: {e}"] * 3
102
-
103
- try:
104
- # Sort candidates by score descending
105
- candidates = sorted(candidates, key=lambda x: x["score"], reverse=True)
106
- except Exception as e:
107
- return [f"⚠️ Sorting error: {e}"] * 3
108
-
109
- # Build candidate cards for display
110
- cards = []
111
- for idx, candidate in enumerate(candidates[:3]):
112
- strengths = "\n".join([f"- ✅ {s}" for s in candidate.get("strengths", [])])
113
- risks = "\n".join([f"- ⚠️ {r}" for r in candidate.get("risks", [])])
114
- card = f"""
115
- ### {idx+1}. {candidate.get('name', 'Unknown')}
116
- **Strengths:**
117
- {strengths}
118
- **Risks:**
119
- {risks}
120
- **Fit Score:** {candidate.get('score', 'N/A')} ⭐
121
- """
122
- cards.append(card)
123
-
124
- while len(cards) < 3:
125
- cards.append("⚠️ Not enough candidates.")
126
 
127
- return cards
 
128
 
129
- # Gradio UI
130
- with gr.Blocks() as demo:
131
- gr.Markdown("# 📄 SmartHire — AI Job Screening Assistant\nUpload candidate resumes and paste the job description to rank the best fits!")
132
 
133
- with gr.Row():
134
- with gr.Column():
135
- resumes = gr.File(file_types=[".pdf", ".docx"], label="Upload Resumes", file_count="multiple")
136
- jd = gr.Textbox(lines=8, label="Job Description")
137
- min_exp = gr.Number(value=0, label="Minimum Years of Experience (Optional)")
138
- submit = gr.Button("Analyze Candidates ✅")
139
- with gr.Column():
140
- output1 = gr.Markdown()
141
- output2 = gr.Markdown()
142
- output3 = gr.Markdown()
143
-
144
- submit.click(analyze_resumes, inputs=[resumes, jd, min_exp], outputs=[output1, output2, output3])
145
 
146
  demo.launch()
 
1
  import gradio as gr
2
  import os
 
 
3
  import docx2txt
4
+ import PyPDF2
5
  import json
6
+ from openai import OpenAI
7
 
8
+ # Replace this with your actual API key
9
  client = OpenAI(
10
+ base_url="https://api.aimlapi.com/v1",
11
+ api_key=os.getenv("AIML_API_KEY")
12
  )
13
 
14
+ # -------- Resume Parsing --------
15
+ def extract_text_from_pdf(file):
16
  try:
17
+ reader = PyPDF2.PdfReader(file)
18
+ return "\n".join([page.extract_text() or "" for page in reader.pages])
19
+ except Exception:
 
 
 
 
 
20
  return ""
21
 
22
+ def extract_text_from_docx(file):
23
  try:
24
+ return docx2txt.process(file)
25
+ except Exception:
26
  return ""
27
 
28
+ def parse_resume(file):
29
+ ext = os.path.splitext(file.name)[1].lower()
30
+ if ext == ".pdf":
31
+ return extract_text_from_pdf(file)
32
+ elif ext == ".docx":
33
+ return extract_text_from_docx(file)
34
+ else:
35
+ return None
36
+
37
+ # -------- AI Screening --------
38
+ def call_ai_screening_agent(resume_texts, job_description, min_experience):
39
+ prompt = f"""
40
+ You are an AI resume screening assistant. Compare each candidate's resume to the job description below.
41
+ Only include candidates who meet the minimum required years of experience: {min_experience}.
42
+ For each candidate, return:
43
+ - name (from filename or parsed text)
44
+ - strengths (2–5 key strengths)
45
+ - risks (0–3 potential risks)
46
+ - score (from 1 to 10)
47
+
48
+ Job Description:
49
+ \"\"\"{job_description}\"\"\"
50
+
51
+ Resumes:
52
+ {resume_texts}
53
+
54
+ Return a JSON list of candidates as described.
 
 
 
 
 
 
 
 
 
 
 
 
55
  """
 
 
 
 
 
 
56
  try:
 
57
  response = client.chat.completions.create(
58
+ model="gpt-4-turbo",
59
+ messages=[{"role": "user", "content": prompt}],
60
+ temperature=0.2
 
 
 
 
61
  )
62
+ return json.loads(response.choices[0].message.content)
63
  except Exception as e:
64
+ return {"error": str(e)}
65
+
66
+ # -------- Main App Logic --------
67
+ def process_resumes(files, job_description, min_experience):
68
+ if not files:
69
+ return "⚠️ Please upload at least one resume.", None
70
+ if not job_description.strip():
71
+ return "⚠️ Job description cannot be empty.", None
72
+
73
+ parsed_resumes = []
74
+ for file in files:
75
+ content = parse_resume(file)
76
+ if not content or len(content.strip()) == 0:
77
+ continue
78
+ parsed_resumes.append({"name": os.path.basename(file.name), "text": content})
79
+
80
+ if not parsed_resumes:
81
+ return "⚠️ Could not parse any valid resumes.", None
82
+
83
+ resume_texts = "\n\n".join([f"{r['name']}:\n{r['text']}" for r in parsed_resumes])
84
+ results = call_ai_screening_agent(resume_texts, job_description, min_experience)
85
+
86
+ if isinstance(results, dict) and "error" in results:
87
+ return f"⚠️ API Error: {results['error']}", None
88
+
89
+ # Display top 3 candidates
90
+ top_candidates = sorted(results, key=lambda x: x["score"], reverse=True)[:3]
91
+ display_md = ""
92
+ for candidate in top_candidates:
93
+ display_md += f"### {candidate['name']} \n"
94
+ display_md += f"⭐ **Score**: {candidate['score']} \n"
95
+ display_md += "✅ **Strengths**:\n"
96
+ for s in candidate['strengths']:
97
+ display_md += f"- ✅ {s}\n"
98
+ if candidate['risks']:
99
+ display_md += "⚠️ **Risks**:\n"
100
+ for r in candidate['risks']:
101
+ display_md += f"- ⚠️ {r}\n"
102
+ display_md += "\n---\n"
103
+
104
+ return None, display_md
105
+
106
+ # -------- Gradio UI --------
107
+ with gr.Blocks(title="SmartHire - AI Job Screening Assistant") as demo:
108
+ gr.Markdown("# 🤖 SmartHire — AI Job Screening Assistant")
109
+ gr.Markdown("Upload resumes and paste a job description to find the best candidates automatically!")
110
 
111
+ with gr.Row():
112
+ resume_input = gr.File(file_types=[".pdf", ".docx"], file_count="multiple", label="Upload Resumes")
113
+ experience_input = gr.Number(label="Minimum Years of Experience", value=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
+ job_desc_input = gr.Textbox(lines=8, placeholder="Paste the job description here...", label="Job Description")
116
+ process_button = gr.Button("Run Screening")
117
 
118
+ error_output = gr.Textbox(label="Warnings / Errors", visible=False)
119
+ output_md = gr.Markdown()
 
120
 
121
+ process_button.click(
122
+ fn=process_resumes,
123
+ inputs=[resume_input, job_desc_input, experience_input],
124
+ outputs=[error_output, output_md]
125
+ )
 
 
 
 
 
 
 
126
 
127
  demo.launch()