Ram-1103 commited on
Commit
d0ca720
Β·
verified Β·
1 Parent(s): 5bf89ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -40
app.py CHANGED
@@ -1,5 +1,6 @@
1
  import gradio as gr
2
  import os
 
3
  from openai import OpenAI
4
 
5
  client = OpenAI(
@@ -7,74 +8,146 @@ client = OpenAI(
7
  api_key=os.environ.get("Rejected_tk"),
8
  )
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  SYSTEM_PROMPT = """You are a brutally honest but helpful senior hiring manager with 15 years of experience.
11
- Your job is to tell candidates EXACTLY why they will be rejected β€” not a vague match score, but a specific, actionable rejection explanation.
12
 
13
- You must output your response in this exact structure:
14
 
15
  ## ❌ Why You Will Likely Be Rejected
16
  [2-3 specific, direct sentences about the core mismatch]
17
 
18
  ## πŸ” Top 3 Skill Gaps
19
- 1. [Gap 1 β€” specific technology or skill missing]
20
- 2. [Gap 2]
21
- 3. [Gap 3]
22
 
23
- ## πŸ“‚ Missing Projects/Experience
24
- - [What kind of project would fill this gap]
25
  - [Another project]
26
  - [Another project]
27
 
28
- ## πŸ”‘ Missing Keywords (for ATS systems)
29
- [comma-separated list of keywords from the job description not present in the resume]
30
-
31
  ## πŸ“… 30-Day Improvement Plan
32
  **Week 1:** [Specific action]
33
  **Week 2:** [Specific action]
34
  **Week 3:** [Specific action]
35
- **Week 4:** [Specific action β€” ideally a project to show]
36
 
37
- ## πŸ’‘ One Honest Verdict
38
- [One sentence: should they apply now, in 3 months, or pivot entirely?]
39
 
40
  Be specific. Name actual technologies. Do not be vague."""
41
 
 
42
 
43
- def analyze(resume_text, job_description):
44
- if not resume_text.strip() or not job_description.strip():
45
- return "⚠️ Please paste both your resume and the job description."
46
- try:
47
- response = client.chat.completions.create(
48
- model="meta-llama/Llama-3.1-8B-Instruct",
49
- messages=[
50
- {"role": "system", "content": SYSTEM_PROMPT},
51
- {"role": "user", "content": f"Resume:\n{resume_text}\n\nJob Description:\n{job_description}\n\nGive the full rejection report."}
52
- ],
53
- max_tokens=1024,
54
- temperature=0.7,
55
- )
56
- return response.choices[0].message.content
57
- except Exception as e:
58
- return f"❌ Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
59
 
 
 
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  EXAMPLE_RESUME = """Name: Priya Sharma
62
  Education: M.S. Computer Science, 2024
63
-
64
  Skills: Python, PyTorch, NLP, Transformers, BERT fine-tuning, HuggingFace, scikit-learn, pandas, numpy
65
-
66
  Projects:
67
  - Sentiment analysis model on Twitter data using BERT
68
  - Named Entity Recognition system for biomedical text
69
  - Research paper: "Improving low-resource NER with cross-lingual transfer"
70
-
71
  Experience:
72
  - ML Research Intern, university NLP lab (6 months)
73
  - TA for Introduction to Machine Learning course"""
74
 
75
  EXAMPLE_JD = """Senior ML Engineer β€” Infrastructure
76
  Company: CloudScale Inc.
77
-
78
  Requirements:
79
  - 3+ years deploying ML models at scale in production
80
  - Strong knowledge of Kubernetes, Docker, MLflow
@@ -100,22 +173,28 @@ with gr.Blocks(title="Rejected Before Applying", css=css) as demo:
100
  <div id='title-block'>
101
  <h1>πŸ’” Rejected Before Applying</h1>
102
  <p>Find out <b>exactly why</b> your resume won't make it β€” before you waste the application.</p>
103
- <p style='color:#666; font-size:0.85em;'>⚑ Powered by Llama-3.1-8B &nbsp;β€’&nbsp; πŸ”’ No data stored &nbsp;β€’&nbsp; 🎯 Brutal honesty guaranteed</p>
104
  </div>
105
  """)
106
 
107
  with gr.Row(equal_height=True):
108
  with gr.Column():
109
- resume_input = gr.Textbox(label="πŸ“„ Your Resume", placeholder="Paste your resume here...", lines=16, value=EXAMPLE_RESUME)
 
 
110
  with gr.Column():
111
- jd_input = gr.Textbox(label="πŸ’Ό Job Description", placeholder="Paste the full job description here...", lines=16, value=EXAMPLE_JD)
 
 
112
 
113
  analyze_btn = gr.Button("πŸ” Analyse My Rejection", variant="primary", size="lg")
114
- gr.HTML("<div style='margin:10px 0 6px 0; color:#888; font-size:0.85em; text-align:center;'>⏳ Analysis takes ~15 seconds β€” hang tight</div>")
115
- output = gr.Markdown(value="*Your rejection report will appear here...*")
 
 
116
 
117
- analyze_btn.click(fn=analyze, inputs=[resume_input, jd_input], outputs=output)
118
 
119
- gr.HTML("<div style='text-align:center; margin-top:24px; color:#555; font-size:0.82em;'>Built for the πŸ€— <b>Build Small Hackathon</b> &nbsp;β€’&nbsp; Small model, real talk</div>")
120
 
121
  demo.launch()
 
1
  import gradio as gr
2
  import os
3
+ import re
4
  from openai import OpenAI
5
 
6
  client = OpenAI(
 
8
  api_key=os.environ.get("Rejected_tk"),
9
  )
10
 
11
+ # ---------- FILE READING ----------
12
+ def read_file(file):
13
+ if file is None:
14
+ return ""
15
+ path = file.name if hasattr(file, "name") else file
16
+ try:
17
+ if path.lower().endswith(".pdf"):
18
+ from pypdf import PdfReader
19
+ reader = PdfReader(path)
20
+ return "\n".join((p.extract_text() or "") for p in reader.pages)
21
+ elif path.lower().endswith(".docx"):
22
+ import docx
23
+ d = docx.Document(path)
24
+ return "\n".join(p.text for p in d.paragraphs)
25
+ else:
26
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
27
+ return f.read()
28
+ except Exception as e:
29
+ return f"[Could not read file: {e}]"
30
+
31
+ # ---------- DETERMINISTIC KEYWORD ENGINE ----------
32
+ # Curated tech/skill vocabulary β€” deterministic, no LLM hallucination
33
+ SKILL_VOCAB = [
34
+ "python","java","go","golang","rust","c++","scala","javascript","typescript","sql",
35
+ "kubernetes","docker","mlflow","airflow","spark","kafka","hadoop","terraform","jenkins",
36
+ "aws","gcp","azure","sagemaker","vertex ai","ec2","s3","lambda","bigquery",
37
+ "pytorch","tensorflow","keras","scikit-learn","sklearn","jax","onnx","tensorrt",
38
+ "horovod","deepspeed","ray","distributed training","quantization","fine-tuning",
39
+ "nlp","bert","transformers","llm","huggingface","computer vision","cnn","rnn","gan",
40
+ "pandas","numpy","data pipeline","etl","feature engineering","ci/cd","mlops","devops",
41
+ "rest api","grpc","microservices","redis","postgresql","mongodb","elasticsearch",
42
+ "git","linux","bash","unit testing","system design","production deployment","monitoring",
43
+ ]
44
+
45
+ def extract_skills(text):
46
+ text_low = text.lower()
47
+ found = set()
48
+ for skill in SKILL_VOCAB:
49
+ if re.search(r"\b" + re.escape(skill) + r"\b", text_low):
50
+ found.add(skill)
51
+ return found
52
+
53
+ def keyword_analysis(resume_text, jd_text):
54
+ resume_skills = extract_skills(resume_text)
55
+ jd_skills = extract_skills(jd_text)
56
+ matched = sorted(jd_skills & resume_skills)
57
+ missing = sorted(jd_skills - resume_skills)
58
+ match_pct = int(100 * len(matched) / len(jd_skills)) if jd_skills else 0
59
+ return matched, missing, match_pct, sorted(resume_skills), sorted(jd_skills)
60
+
61
+ # ---------- LLM PROMPTS ----------
62
  SYSTEM_PROMPT = """You are a brutally honest but helpful senior hiring manager with 15 years of experience.
63
+ Tell candidates EXACTLY why they will be rejected β€” a specific, actionable diagnosis, not a vague match score.
64
 
65
+ Output in this exact structure:
66
 
67
  ## ❌ Why You Will Likely Be Rejected
68
  [2-3 specific, direct sentences about the core mismatch]
69
 
70
  ## πŸ” Top 3 Skill Gaps
71
+ 1. [Gap β€” specific technology]
72
+ 2. [Gap]
73
+ 3. [Gap]
74
 
75
+ ## πŸ“‚ Missing Projects To Build
76
+ - [Project that would fill the biggest gap]
77
  - [Another project]
78
  - [Another project]
79
 
 
 
 
80
  ## πŸ“… 30-Day Improvement Plan
81
  **Week 1:** [Specific action]
82
  **Week 2:** [Specific action]
83
  **Week 3:** [Specific action]
84
+ **Week 4:** [Specific action β€” a portfolio project]
85
 
86
+ ## πŸ’‘ Honest Verdict
87
+ [One sentence: apply now, wait 3 months, or pivot?]
88
 
89
  Be specific. Name actual technologies. Do not be vague."""
90
 
91
+ BULLET_PROMPT = """You are an expert resume writer. Given the candidate's gaps and the target role, write exactly 3 strong, quantified resume bullet points the candidate could truthfully add AFTER building the recommended projects. Each bullet starts with a strong action verb and includes a metric. Output only the 3 bullets, nothing else."""
92
 
93
+ def call_llm(system, user):
94
+ response = client.chat.completions.create(
95
+ model="meta-llama/Llama-3.1-8B-Instruct",
96
+ messages=[{"role": "system", "content": system}, {"role": "user", "content": user}],
97
+ max_tokens=1024, temperature=0.7,
98
+ )
99
+ return response.choices[0].message.content
100
+
101
+ # ---------- MAIN PIPELINE ----------
102
+ def analyze(resume_text, resume_file, jd_text, jd_file):
103
+ # File upload overrides text box if a file is provided
104
+ resume = read_file(resume_file) if resume_file else resume_text
105
+ jd = read_file(jd_file) if jd_file else jd_text
106
+
107
+ if not resume.strip() or not jd.strip():
108
+ return "⚠️ Please provide both a resume and a job description (paste or upload).", ""
109
+
110
+ # 1. Deterministic keyword engine
111
+ matched, missing, match_pct, resume_skills, jd_skills = keyword_analysis(resume, jd)
112
+
113
+ evidence = f"""## πŸ“Š Evidence-Based Match Analysis
114
+
115
+ **🎯 Skill Match Score: {match_pct}%**
116
+
117
+ **βœ… Skills found in BOTH resume & JD ({len(matched)}):**
118
+ {', '.join(matched) if matched else 'None β€” major mismatch'}
119
 
120
+ **❌ Required skills MISSING from your resume ({len(missing)}):**
121
+ {', '.join(missing) if missing else 'None β€” strong overlap!'}
122
 
123
+ ---
124
+ """
125
+
126
+ # 2. LLM rejection report
127
+ user_msg = f"Resume:\n{resume}\n\nJob Description:\n{jd}\n\nDETERMINISTIC ANALYSIS β€” Missing skills: {', '.join(missing)}. Match score: {match_pct}%.\n\nGive the full rejection report grounded in these missing skills."
128
+ report = call_llm(SYSTEM_PROMPT, user_msg)
129
+
130
+ # 3. Resume bullet generator
131
+ bullet_msg = f"Target role skills missing: {', '.join(missing)}. Candidate background: {resume[:600]}. Write the 3 bullets."
132
+ bullets = call_llm(BULLET_PROMPT, bullet_msg)
133
+ bullets_section = f"\n\n---\n## ✍️ 'Fix My Resume' β€” 3 Bullets To Add After Building These Projects\n\n{bullets}"
134
+
135
+ return evidence + report, bullets_section
136
+
137
+ # ---------- EXAMPLES ----------
138
  EXAMPLE_RESUME = """Name: Priya Sharma
139
  Education: M.S. Computer Science, 2024
 
140
  Skills: Python, PyTorch, NLP, Transformers, BERT fine-tuning, HuggingFace, scikit-learn, pandas, numpy
 
141
  Projects:
142
  - Sentiment analysis model on Twitter data using BERT
143
  - Named Entity Recognition system for biomedical text
144
  - Research paper: "Improving low-resource NER with cross-lingual transfer"
 
145
  Experience:
146
  - ML Research Intern, university NLP lab (6 months)
147
  - TA for Introduction to Machine Learning course"""
148
 
149
  EXAMPLE_JD = """Senior ML Engineer β€” Infrastructure
150
  Company: CloudScale Inc.
 
151
  Requirements:
152
  - 3+ years deploying ML models at scale in production
153
  - Strong knowledge of Kubernetes, Docker, MLflow
 
173
  <div id='title-block'>
174
  <h1>πŸ’” Rejected Before Applying</h1>
175
  <p>Find out <b>exactly why</b> your resume won't make it β€” before you waste the application.</p>
176
+ <p style='color:#666; font-size:0.85em;'>πŸ“Š Evidence-based keyword engine + AI hiring manager &nbsp;β€’&nbsp; πŸ”’ No data stored</p>
177
  </div>
178
  """)
179
 
180
  with gr.Row(equal_height=True):
181
  with gr.Column():
182
+ gr.HTML("<b style='color:#ccc;'>πŸ“„ Your Resume</b>")
183
+ resume_input = gr.Textbox(label="Paste resume text", lines=12, value=EXAMPLE_RESUME)
184
+ resume_file = gr.File(label="...or upload (PDF / DOCX / TXT)", file_types=[".pdf", ".docx", ".txt"])
185
  with gr.Column():
186
+ gr.HTML("<b style='color:#ccc;'>πŸ’Ό Job Description</b>")
187
+ jd_input = gr.Textbox(label="Paste JD text", lines=12, value=EXAMPLE_JD)
188
+ jd_file = gr.File(label="...or upload (PDF / DOCX / TXT)", file_types=[".pdf", ".docx", ".txt"])
189
 
190
  analyze_btn = gr.Button("πŸ” Analyse My Rejection", variant="primary", size="lg")
191
+ gr.HTML("<div style='margin:10px 0; color:#888; font-size:0.85em; text-align:center;'>⏳ Takes ~20 seconds β€” runs a keyword engine + 2 AI passes</div>")
192
+
193
+ output = gr.Markdown(value="*Your evidence-based rejection report will appear here...*")
194
+ bullets_out = gr.Markdown()
195
 
196
+ analyze_btn.click(fn=analyze, inputs=[resume_input, resume_file, jd_input, jd_file], outputs=[output, bullets_out])
197
 
198
+ gr.HTML("<div style='text-align:center; margin-top:24px; color:#555; font-size:0.82em;'>Built for the πŸ€— <b>Build Small Hackathon</b> &nbsp;β€’&nbsp; Engineered pipeline, not just a prompt</div>")
199
 
200
  demo.launch()