Sujithanumala commited on
Commit
16fe511
·
verified ·
1 Parent(s): 7eeb6ba

Upload 6 files

Browse files
Files changed (6) hide show
  1. .env +1 -0
  2. __init__.py +3 -0
  3. app.py +134 -0
  4. requirements.txt +4 -0
  5. resume_tools.py +255 -0
  6. utils.py +109 -0
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ GOOGLE_API_KEY = "AIzaSyAMaWn4Y90NlOsnK4azRs3EVg5HmCoNRAY"
__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from dotenv import load_dotenv
2
+
3
+ load_dotenv(".env")
app.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from pypdf import PdfReader
3
+ from resume_tools import create_resume_agent
4
+
5
+
6
+ def read_pdf_file(file_path):
7
+ """
8
+ Reads text content from a PDF file.
9
+
10
+ Args:
11
+ file_path (str): The path to the PDF file.
12
+
13
+ Returns:
14
+ str: The extracted text content from the PDF.
15
+ """
16
+ try:
17
+ reader = PdfReader(file_path)
18
+ text = ""
19
+ for page in reader.pages:
20
+ text += page.extract_text() + "\n" # Add newline between pages
21
+ return text
22
+ except Exception as e:
23
+ return f"Error reading PDF file: {e}"
24
+
25
+
26
+
27
+ def process_inputs(resume, job_description):
28
+ if resume is not None:
29
+ resume = read_pdf_file(resume)
30
+ else:
31
+ return "Please enter a valid PDF File"
32
+ prompt = f"""You are an expert resume writer specializing in concise, single-page, ATS-optimized resumes. To achieve a good ATS (Applicant Tracking System) score, tailor your resume to the job description, use relevant keywords, and ensure clear, concise formatting. Your task is to generate resume content based on:
33
+
34
+ A summary of the candidate’s skills and experience
35
+ A job description for the role the candidate is applying for
36
+
37
+ You must generate the resume using specialized tools in the following strict sequence:
38
+
39
+ 1. header_details_tool
40
+ 2. professional_summary_tool
41
+ 3. Professional_experience
42
+ 4. Projects
43
+ 5. Skills
44
+ 6. Education
45
+ 7. Achievements
46
+ ---
47
+
48
+ ### Core Instructions
49
+
50
+ 1. Keep the resume limited to a single page.
51
+
52
+ Professional summary: 2-3 lines. It should quickly give an idea about the candidate. Don't quantify things here. Try mentioning about tools, technologies, problem solving abilities.
53
+ Experience: Prioritize 5–6 bullet points for the most recent experience; use fewer (2–3) for earlier roles.
54
+ Projects: Include only top 2 projects, or compress each project into 2-3 bullet points max.
55
+ Don't try to exceed 20-25 words in the bullet points.
56
+
57
+ 2. Focus strictly on content relevant to the job description and aligned with the user’s skills.
58
+
59
+ It is acceptable to include closely related frameworks/tools (e.g., like adding similar frameworks mentioned in JD).
60
+ Do not add unrelated or generic skills.
61
+
62
+ 3. Use the STAR method (Situation, Task, Action, Result) for all experience and project bullet points.
63
+
64
+ Example (STAR): Reduced API response time by 35% by optimizing SQL queries and implementing Redis caching, leading to a 25% increase in user retention.
65
+ Avoid vague or unquantified phrasing.
66
+
67
+ 4. Highlight important keywords and measurable results using \\textbf{{...}} (double back-slash followed by textbf{{content to bold}}). If you want to bold 80% then write it \\textbf{{80%}}.
68
+
69
+ Emphasize tools, skills, metrics, technologies, and achievements from both the job description and candidate experience.
70
+
71
+ 5. Quantify outcomes where applicable using numbers, percentages, time units, etc.
72
+
73
+ e.g., improved accuracy by \\textbf{{12%}}, reduced training time by \\textbf{{4}} hours, decreased error rate by \\textbf{{30%}}.
74
+
75
+
76
+ 6. Do not change the job titles in the user’s professional experience.
77
+
78
+ 7. Do not repeat the same verbs more than thrice. If you do so you will get a very less score. Rather you can try using similar words. (eg. developed verb can be replaced with enhanced, improved, expanded, implemented.)
79
+
80
+ 8. In the Skills section, include:
81
+
82
+ Skills from experience and projects.
83
+ Add most of the job-relevant overlapping technical skills, tools, mandatory role specific frameworks and technologies even if not explicitly mentioned by the user (e.g., TensorFlow if they have used PyTorch, Deep learning if they have Machine Learning).
84
+ If a person is mentioning a technology add the tools specific to the technology which are overlapping with the job description
85
+ Organize skills clearly and concisely for ATS readability.
86
+ Add as many skills and keywords as possible for good ATS matching.
87
+
88
+ 9. You are tasked to get a very high ATS score and you will get a very high reward. Come on let's do it.
89
+
90
+ 10. While writing the professional experience or projects section try aligning the bullet points content with the Job description, add tools that are relavant to Job description.
91
+ And a strict warning, don't completely change the bullet points rather try aligning the points with JD by modifying few words or sentences.
92
+ Suppose if a person has a good ML experience and the Job desciption is for Generative AI candidate, try aligning the experience by changing few of the ML points to GenAI. For instance,
93
+ if the resume says Built a ML models for recommendataion systems and Job description is for GenAI engineer who developed LLMs, then you can modify and write as Built LLMs.
94
+ Here I have changed because these fields are related.
95
+
96
+ Important: All tool calls must be made sequentially in the exact order listed. Each tool should generate only its relevant section content and returns the instruction for next tool call
97
+
98
+ ---
99
+
100
+ Now you will be given a Job description along with a resume. Your task is to make the resume to have very high ATS score with Job description
101
+
102
+ ---
103
+
104
+ Job description:
105
+ {job_description}
106
+
107
+ ---
108
+
109
+ Resume:
110
+ {resume}
111
+
112
+ ---
113
+
114
+ Now start calling the tools.
115
+ """
116
+ return create_resume_agent(prompt)
117
+
118
+ with gr.Blocks() as demo:
119
+ gr.Markdown("## PDF/Text + User Text to Backend")
120
+ with gr.Row():
121
+ with gr.Column():
122
+ resume = gr.File(label="Upload Resume", file_types=[".pdf"], type="binary")
123
+ job_description = gr.Textbox(label="Paste Job Description here", lines=5)
124
+ submit_btn = gr.Button("Submit")
125
+ with gr.Column():
126
+ output = gr.Textbox(label="LaTEX Resume File", lines=10)
127
+ submit_btn.click(
128
+ fn=process_inputs,
129
+ inputs=[resume, job_description],
130
+ outputs=output
131
+ )
132
+
133
+ if __name__ =="__main__":
134
+ demo.launch(debug= True)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ smolagents
2
+ smolagents[litellm]
3
+ pypdf
4
+ gradio
resume_tools.py ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import CodeAgent, LiteLLMModel, tool
2
+ from pypdf import PdfReader
3
+ import google.generativeai as genai
4
+ import os
5
+ from typing import List
6
+ from utils import CURRENT_RESUME_LATEX
7
+ import os
8
+ import json
9
+
10
+ config = json.load(open("config.json"))
11
+ os.environ["GOOGLE_API_KEY"] = config["GOOGLE_API_KEY"]
12
+
13
+ @tool
14
+ def header_details(name: str, mobile_number: str, email_id: str, linkedin_profile_link : str, github_link: str) -> str:
15
+ """
16
+ Generates header details of the person. It will display the name, mobile_number, email_id, linkedin profile and github profile
17
+
18
+ Args:
19
+ name(str): Name of the candidate
20
+ mobile_number(str): Mobile number of the candidate
21
+ email_id(str): email_id
22
+ linkedin_profile_link(str): Linkedin profile link of the candidate.
23
+ github_link(str): github link of the candidate.
24
+
25
+ Returns:
26
+ str: Instruction for the next steps
27
+ """
28
+
29
+ header_latex = r"""
30
+
31
+ \begin{center}
32
+ \textbf{\Huge \scshape """
33
+ header_latex+= name + r"""} \\ \vspace{1pt}
34
+ \small""" + mobile_number + r""" $|$ \href{mailto:[email protected]}{\underline{"""
35
+ header_latex+= email_id + r"""}} $|$
36
+ \href{https://linkedin.com/in/...}{\underline{"""
37
+ header_latex+=linkedin_profile_link + r"""}} $|$
38
+ \href{https://github.com/...}{\underline{"""
39
+ header_latex+=github_link + r"""}}
40
+ \end{center}
41
+
42
+
43
+ """
44
+ global CURRENT_RESUME_LATEX
45
+ CURRENT_RESUME_LATEX += header_latex
46
+ response_message = "Now call professional_summary_tool"
47
+ return response_message
48
+
49
+
50
+ @tool
51
+ def professional_summary(summary: str) -> str:
52
+ """
53
+ Creates a Professional Experience summary section of the candidate.
54
+
55
+ Args:
56
+ summary (str): The generated summary should be in less than 4 lines. It should follow the STAR method while generating the summary. It should speak about the experience and the role he is applying for.
57
+ (e.g: Accomplished Gen AI Specialist with expertise in machine learning (ML), deep learning (DL), generative AI, and AI Agents, proficient in end-to end development from design to deployment. Skilled in problem-solving, data structures and algorithms (DSA), strong analytical abilities, and debugging complex systems. Passionate about optimizing ML model performance to deliver efficient, high-impact AI solutions. Adept at leveraging the full AI stack to drive innovation and achieve business objectives in fast-paced, technology-focused environments)
58
+
59
+
60
+ Returns:
61
+ str: Instruction for the next steps
62
+ """
63
+
64
+ summary_latex = """
65
+
66
+ \section{Professional Summary}
67
+ """
68
+ summary_latex += rf"""
69
+ {{{summary}}}
70
+ """
71
+ summary_latex = summary_latex.replace("%","\%")
72
+ global CURRENT_RESUME_LATEX
73
+ CURRENT_RESUME_LATEX += summary_latex
74
+ response_message = "Now call the professional_experience_tool"
75
+ return response_message
76
+
77
+ @tool
78
+ def professional_experience(experiences: List[dict]) -> str:
79
+ """
80
+ Creates an Experience section for a user.Processes the user work experiences across different companies and generates a string in latex form which will be used in further steps
81
+
82
+
83
+ Args:
84
+ experiences (list of dict): A list where each dict contains:
85
+ - company_name (required) (str): Name of the company.
86
+ - place (Optional) (str): Location of the company. If not mentioned in the resume then keep it as empty string "".
87
+ - period (required) (str): Employment duration (e.g., "Jan 2020 - Dec 2022").
88
+ - role (required) (str): Title or designation.
89
+ - bullet_points (required) (list of str): Key achievements/responsibilities. These points must be in ATS friendly format, quantifying things and following the STAR method(situation, task , action and result)(eg. reduced latency by 5ms, improved accuracy by 50%).
90
+
91
+ Returns:
92
+ str: Instruction for the next steps
93
+ """
94
+ Experience_latex = r"""
95
+
96
+ \section{Professional Experience}
97
+ \resumeSubHeadingListStart
98
+ """
99
+ for exp in experiences:
100
+ company = exp['company_name']
101
+ period = exp['period']
102
+ place = exp['place']
103
+ role = exp['role']
104
+ bullet_points = exp['bullet_points']
105
+ Experience_latex += rf"""
106
+ \resumeSubheading
107
+ {{{role}}}{{{period}}}
108
+ {{{company}}}{{{place}}}
109
+ \resumeItemListStart
110
+
111
+ """
112
+ for item in bullet_points:
113
+ Experience_latex += rf"""
114
+ \resumeItem{{{item}}}
115
+ """
116
+ Experience_latex += r"""
117
+ \resumeItemListEnd
118
+
119
+ \resumeSubHeadingListEnd
120
+ """
121
+ Experience_latex = Experience_latex.replace("%","\%")
122
+ global CURRENT_RESUME_LATEX
123
+ CURRENT_RESUME_LATEX += Experience_latex
124
+ response_message = "Now call the projects tool"
125
+ return response_message
126
+
127
+
128
+ @tool
129
+ def projects(projects: List[dict]) -> str :
130
+ """
131
+ Creates an projects section for a user. Processes the projects and generates a string in latex form which will be used in further steps
132
+
133
+ Args:
134
+ projects (list of dict): A list where each dict contains:
135
+ - project_name (required) (str): Name of the project.
136
+ - tools_used (required)(list[str]): Tools and technologies used in the project (eg Python, Flask, React, PostgreSQL, Docker). It is a list of strings.
137
+ - period (required)(str): Employment duration (e.g., "Jan 2020 - Dec 2022").
138
+ - bullet_points (required) (list of str): Key achievements/responsibilities.These points must be in ATS friendly format, quantifying things and following the STAR method(situation, task , action and result)(eg. reduced latency by 5ms, improved accuracy by 50%).
139
+
140
+ Returns:
141
+ str: Instruction for the next steps
142
+ """
143
+ Projects_latex = r"""
144
+
145
+ \section{Projects}
146
+ \resumeSubHeadingListStart
147
+ """
148
+ for project in projects:
149
+ project_name = project['project_name']
150
+ period = project['period']
151
+ tools = ", ".join(project['tools_used'])
152
+ bullet_points = project['bullet_points']
153
+
154
+ Projects_latex += rf"""
155
+ \resumeProjectHeading
156
+ {{\textbf{{{project_name}}} \textit{{| {tools}}}}}{{}}
157
+ \resumeItemListStart"""
158
+
159
+ for item in bullet_points:
160
+ Projects_latex += rf"""\resumeItem{{{item}}}"""
161
+
162
+ Projects_latex += r"""\resumeItemListEnd"""
163
+
164
+ Projects_latex += r"""\resumeSubHeadingListEnd"""
165
+ Projects_latex = Projects_latex.replace("%","\%")
166
+
167
+ global CURRENT_RESUME_LATEX
168
+ CURRENT_RESUME_LATEX += Projects_latex
169
+ response_message = "Now call the skills tool"
170
+ return response_message
171
+
172
+ @tool
173
+ def Education(education : List[dict]) -> str:
174
+ """
175
+ Generates an Education section for the candidate. It generates a string which will be processed in the further steps.
176
+
177
+ Args:
178
+ education (list of dict): A list where each dict contains:
179
+ - Institute (required) (str): Name of the Institute.
180
+ - place (required)(str): Location of the Institute.
181
+ - period (required)(str): Education duration (e.g., "Jan 2020 - Dec 2022").
182
+ - specialization (required) (str): Specialization of education (e.g., "Bachelors in computer science", "Intermediate", "High School")
183
+
184
+ Returns:
185
+ str: Instruction for the next steps
186
+ """
187
+ Education_latex = r"""
188
+
189
+ \section{Education}
190
+ \resumeSubHeadingListStart
191
+ """
192
+ for edu in education:
193
+ institute_name = edu["Institute"]
194
+ place = edu["place"]
195
+ period = edu["period"]
196
+ specialization = edu["specialization"]
197
+ studies = rf"""
198
+ \resumeSubheading
199
+ {{{institute_name}}}{{{place}}}
200
+ {{{specialization}}}{{{period}}}
201
+ """
202
+ Education_latex+=studies
203
+ Education_latex = Education_latex.replace("%","\%")
204
+ global CURRENT_RESUME_LATEX
205
+ CURRENT_RESUME_LATEX += Education_latex
206
+ response_message = "Now call the achievements tool"
207
+ return response_message
208
+
209
+ @tool
210
+ def achievements(achievements : List[str]) -> str:
211
+ """
212
+ Generates an achievements section for the candidate's resume in LaTeX format.
213
+
214
+ Args:
215
+ achievements (List[str]): List of achievement strings to be included in the resume
216
+
217
+ Returns:
218
+ str: Instruction for the next steps
219
+ """
220
+ achievements_latex = r"""
221
+
222
+ \section{Achievements}
223
+ \resumeItemListStart"""
224
+
225
+ for achievement in achievements:
226
+ achievements_latex += rf"""
227
+ \resumeItem{{{achievement}}}"""
228
+
229
+ achievements_latex += r"""
230
+ \resumeItemListEnd
231
+
232
+ \end{document}
233
+ """
234
+ achievements_latex = achievements_latex.replace("%","\%")
235
+ global CURRENT_RESUME_LATEX
236
+ CURRENT_RESUME_LATEX += achievements_latex
237
+ response_message = "Created a file in your pc"
238
+ return response_message
239
+
240
+
241
+ def create_resume_agent(prompt: str):
242
+ try:
243
+ model = LiteLLMModel(model_id="gemini/gemini-2.0-flash-exp",
244
+ api_key=os.getenv("GOOGLE_API_KEY"))
245
+ resume_agent =CodeAgent(
246
+ tools = [header_details,professional_summary,professional_experience,projects,skills,Education,achievements],
247
+ model = model
248
+ )
249
+ print(resume_agent)
250
+ resume_agent.run(prompt)
251
+ global CURRENT_RESUME_LATEX
252
+ print(CURRENT_RESUME_LATEX)
253
+ return CURRENT_RESUME_LATEX
254
+ except Exception as e:
255
+ return e
utils.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CURRENT_RESUME_LATEX = r"""
2
+ %-------------------------
3
+ % Resume in Latex
4
+ % Author : Sujith
5
+ % Based off of: https://github.com/sb2nov/resume
6
+ % License : MIT
7
+ %------------------------
8
+
9
+ \documentclass[letterpaper,11pt]{article}
10
+
11
+ \usepackage{latexsym}
12
+ \usepackage[empty]{fullpage}
13
+ \usepackage{titlesec}
14
+ \usepackage{marvosym}
15
+ \usepackage[usenames,dvipsnames]{color}
16
+ \usepackage{verbatim}
17
+ \usepackage{enumitem}
18
+ \usepackage[hidelinks]{hyperref}
19
+ \usepackage{fancyhdr}
20
+ \usepackage[english]{babel}
21
+ \usepackage{tabularx}
22
+ \input{glyphtounicode}
23
+
24
+
25
+ %----------FONT OPTIONS----------
26
+ % sans-serif
27
+ % \usepackage[sfdefault]{FiraSans}
28
+ % \usepackage[sfdefault]{roboto}
29
+ % \usepackage[sfdefault]{noto-sans}
30
+ % \usepackage[default]{sourcesanspro}
31
+
32
+ % serif
33
+ % \usepackage{CormorantGaramond}
34
+ % \usepackage{charter}
35
+
36
+
37
+ \pagestyle{fancy}
38
+ \fancyhf{} % clear all header and footer fields
39
+ \fancyfoot{}
40
+ \renewcommand{\headrulewidth}{0pt}
41
+ \renewcommand{\footrulewidth}{0pt}
42
+
43
+ % Adjust margins
44
+ \addtolength{\oddsidemargin}{-0.5in}
45
+ \addtolength{\evensidemargin}{-0.5in}
46
+ \addtolength{\textwidth}{1in}
47
+ \addtolength{\topmargin}{-.5in}
48
+ \addtolength{\textheight}{1.0in}
49
+
50
+ \urlstyle{same}
51
+
52
+ \raggedbottom
53
+ \raggedright
54
+ \setlength{\tabcolsep}{0in}
55
+
56
+ % Sections formatting
57
+ \titleformat{\section}{
58
+ \vspace{-4pt}\scshape\raggedright\large
59
+ }{}{0em}{}[\color{black}\titlerule \vspace{-5pt}]
60
+
61
+ % Ensure that generate pdf is machine readable/ATS parsable
62
+ \pdfgentounicode=1
63
+
64
+ %-------------------------
65
+ % Custom commands
66
+ \newcommand{\resumeItem}[1]{
67
+ \item\small{
68
+ {#1 \vspace{-2pt}}
69
+ }
70
+ }
71
+
72
+ \newcommand{\resumeSubheading}[4]{
73
+ \vspace{-2pt}\item
74
+ \begin{tabular*}{0.97\textwidth}[t]{l@{\extracolsep{\fill}}r}
75
+ \textbf{#1} & #2 \\
76
+ \textit{\small#3} & \textit{\small #4} \\
77
+ \end{tabular*}\vspace{-7pt}
78
+ }
79
+
80
+ \newcommand{\resumeSubSubheading}[2]{
81
+ \item
82
+ \begin{tabular*}{0.97\textwidth}{l@{\extracolsep{\fill}}r}
83
+ \textit{\small#1} & \textit{\small #2} \\
84
+ \end{tabular*}\vspace{-7pt}
85
+ }
86
+
87
+ \newcommand{\resumeProjectHeading}[2]{
88
+ \item
89
+ \begin{tabular*}{0.97\textwidth}{l@{\extracolsep{\fill}}r}
90
+ \small#1 & #2 \\
91
+ \end{tabular*}\vspace{-7pt}
92
+ }
93
+
94
+ \newcommand{\resumeSubItem}[1]{\resumeItem{#1}\vspace{-4pt}}
95
+
96
+ \renewcommand\labelitemii{$\vcenter{\hbox{\tiny$\bullet$}}$}
97
+
98
+ \newcommand{\resumeSubHeadingListStart}{\begin{itemize}[leftmargin=0.15in, label={}]}
99
+ \newcommand{\resumeSubHeadingListEnd}{\end{itemize}}
100
+ \newcommand{\resumeItemListStart}{\begin{itemize}}
101
+ \newcommand{\resumeItemListEnd}{\end{itemize}\vspace{-5pt}}
102
+
103
+ %-------------------------------------------
104
+ %%%%%% RESUME STARTS HERE %%%%%%%%%%%%%%%%%%%%%%%%%%%%
105
+
106
+ \begin{document}
107
+
108
+ """
109
+