-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.py
322 lines (226 loc) · 13.8 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
import os
import re
import shutil
import json
from dotenv import load_dotenv
from utils.LLM import OpenAIChatAPI, OpenAIBackendAPI, LLMAPI
from utils.general import sanitize_for_latex, load_prompt_string, load_string, parse_json_garbage
from experimental.experimental import generate_artificial_supplement_experiences
from utils.cov import generate_cover_letter as latex_gen, read_json
def generate_resume(info: dict, job_description: str):
out = {}
# Build the contacts section
contact_info = info["contact"]
out["cv"] = contact_info
cv_out = out["cv"]
# Add the new 'sections' attribute
cv_out['sections'] = {}
sections = cv_out['sections']
# Build the summary
sections["summary"] = [info["summary"]]
# Build the education section
education = info["education"]
for e in education:
e["highlights"] = [','.join(e["relevant_coursework"])]
sections["education"] = education
# Build the work experiences section
work_experience = info["work_experiences"]
sections["work_experience"] = work_experience
# Build the personal projects section
if "project_experiences" in info:
personal_projects = info["project_experiences"]
sections["personal_projects"] = personal_projects
# TODO - smaller item: add certifications
if "certificates" in info:
certificates = info["certificates"]
sections["certificates"] = certificates
if "publications" in info:
publications = info["publications"]
sections["publications"] = publications
# Build the skills section
sections["skills"] = info["skills"]
with open("resume_int.json", 'w') as f:
json.dump(out, f)
# Call the general command to render the RESUME in latex
os.system('rendercv render resume_int.json')
def generate_cover_letter(llm: LLMAPI):
llm.prompt_and_response(
"Compose a strong introduction and expression of interest for a cover letter for the job description.")
llm.prompt_and_response(
"Explain how my skills match the requirements for the position")
llm.prompt_and_response(
"Describe my work experiences and explain how they demonstrate my potential for success in the role.")
llm.prompt_and_response(
"Illustrate my adaptability and willingness to learn new skills, emphasizing how these traits make me an excellent candidate for the position."
)
llm.prompt_and_response(
"Describe my passion for the industries, research, and fields relevant to the job and how this passion will contribute to my success in the role."
)
llm.prompt_and_response(
"Provide low-level, specific examples of my strong work ethic and commitment to excellence, demonstrating how these qualities make me a valuable asset for the job position. Delve into what these past highlights establish, and how I'll use this technical experience on the job."
)
llm.prompt_and_response(
"Compose a strong vibrant conclusion, summarizing my qualifications for the position and expressing my eagerness for an interview.")
out = llm.prompt_and_response("""
Compose a complete cover letter for the position, incorporating a strong introduction, a summary of relevant skills and experiences, an explanation of how my skills match the job requirements, a highlight of my most significant achievement, an explanation of my career gap or job switch (if applicable), showcasing my adaptability and willingness to learn, demonstrating my passion for the industry, emphasizing my strong work ethic and commitment, mentioning the value of my professional network, addressing the company’s values and culture, and ending with a powerful conclusion that expresses my eagerness for an interview.
Here is the general format.
Dear Hiring Manager
[YOUR RESPONSE WILL GO HERE]
Sincerely,
Candidate
Write a cover letter by filling in the response section. Start with a sentence explicitly thanking them for taking the time to read the letter before writing the full response. Utilize the responses and the modular content you've
had to write in the previous couple prompts to assemble the letter. Make sure to include a 3-4 sentence paragraph highlighting specific accomplishments, and how I plan to use the knowledge
to bring a key advantage to the job.
Only include the text -- do not include greetings like 'Dear Hiring Manager', or closing / endinging statements like 'Sincerely, Candidate' or anything remotely similar. Just the paragraphs in between that are the actual content.
""")
out = llm.prompt_and_response("""
Here are some concepts that you will need to understand:
When it comes to writing content, two factors are crucial, “perplexity” and “burstiness.” Perplexity measures the complexity of text. Separately, burstiness compares the variations of sentences. Humans tend to write with greater burstiness, for example, with some longer or complex sentences alongside shorter ones. AI sentences tend to be more uniform. Therefore, when writing the following content I am going to ask you to create, I need it to have a good amount of perplexity and burstiness.
Now, pretend you are a persuasive literature writing professor. Using the concepts above, rewrite the cover letter with a low amount of perplexity and a high amount of burstiness, while preserving the actual content.
Proofread the cover letter - tactics like sophisticating the vocabulary to be more precise when possible would definitely help. Make sure to format the letter properly."""
)
with open('cover_letters/content.md', 'w') as f:
print(out, file=f)
latex_gen('let1', read_json('info.json'), read_json(
'./cover_letters/info.json'), './cover_letters/content.md', './output/cover_letter.pdf')
shutil.move("out.pdf", "output/out.pdf")
shutil.rmtree("template/tmp")
def get_resume_in_text(info_dct):
edup = load_prompt_string('prompts/utils/display/education.prompt')
projectp = load_prompt_string('prompts/utils/display/project.prompt')
resumep = load_prompt_string('prompts/utils/display/resume.prompt')
workp = load_prompt_string('prompts/utils/display/work.prompt')
edus = '\n\n'.join([edup.format(
degree_type=edu["study_type"],
university_name=edu["institution"],
grad_year=f'{edu["start_date"]} - {edu["end_date"]}',
coursework=", ".join(edu["relevant_coursework"])
) for edu in info_dct["education"]])
works = '\n\n'.join([workp.format(
company=work["company"],
role=work["position"],
time=f'{work["start_date"]} - {work["end_date"]}',
experience=work["description"],
location=work["location"],
) for work in info_dct["work_experiences"]])
projects = '\n\n'.join([projectp.format(
title=proj["name"],
date=proj["date"],
experience=proj["description"],
) for proj in info_dct["project_experiences"]])
return resumep.format(
education=edus,
works=works,
projects=projects,
summary=info_dct.get("summary", "No summary provided.")
)
def resume_text_optimize(company_name, job_description, resume_string, llm: LLMAPI, ITERATIONS=2):
start_prompt = """
Act as the hiring manager for this job. The company is {company_name}
{job_description}
Here is my resume:
{resume_string}
"""
company_research_prompt = """Think deeply and clearly about the company, and list out your thoughts. Who is this company, in depth? What are their core values?
Then, go into deeper detail about the use cases the company provides solutions for. Step by step, think about what they would be doing internally that might require this job, or what other customers might be wanting the company to do. Finally, think about why the company might be hiring for this job. You will need to keep all
of this in mind for future conversations.
"""
continue_prompt = """As the hiring manager, do you think that you can improve this resume's work experiences and project experiences substantially more to tailor it closer to the company knowledge you found early, and what they may seek through the job description? It's okay if not. I'd prefer you to start with a hard Yes or No. If Yes, list all the feedback you have. That means, that for any description, focus on concisely highlighting the most relevant results first, and then crafting it in a concise manner that highlights impact and displays skills that would be relevant to the job. Make sure to include impact statements with clear, quantiative results. Remember that a resume can be improved if the content in the resume can be adjusted to more closely focus on the task the job description focuses on, or if the description is too high level or does not contain clear action statements with quantative statistics in support. That would mean to remove / streamline irrelevant skills, and concisely elaborate on the more relevant ones"""
implementation_prompt = """Now, show us how you would improve the resume. Rewrite the resume, and make sure to implement all feedback you proposed for the resume descriptions. Now, whenever we refer to resume in future conversations, we are referring to the latest refined version."""
work_json_prompt = load_prompt_string('prompts/json/work.prompt')
project_json_prompt = load_prompt_string('prompts/json/project.prompt')
skills_json_prompt = load_prompt_string('prompts/json/skills.prompt')
summary_json_prompt = load_prompt_string('prompts/json/summary.prompt')
# Refinement loop
p = start_prompt.format(
company_name=company_name,
job_description=job_description, resume_string=resume_string.strip()).strip()
print('Sending, ', p)
llm.prompt_and_response(p)
resp = llm.prompt_and_response(company_research_prompt)
print("Company Research: ")
with open("company_research.txt", "w") as f:
print(resp, file=f)
# Squeeze in some details about the company
# Iterate until we're done
for i in range(ITERATIONS):
continue_response = llm.prompt_and_response(continue_prompt)
print("\n\n")
print(continue_response, continue_response.startswith('Yes'))
# We'll hard prune yesses
if not continue_response.startswith("Yes"):
break
# If yes, implement the feedback.
refined_resume_response = llm.prompt_and_response(
implementation_prompt)
print("REFINED")
print(refined_resume_response)
print("\n\n")
# Post processing
try:
generate_artificial_supplement_experiences(refined_resume_response, llm)
except:
pass
# Turn the optimized work section into JSON
resp = llm.prompt_and_response(work_json_prompt)
# TODO: bake in some more general skills to even out the resume
print(resp)
work_json = parse_json_garbage(resp)
work_json = work_json["works"]
for w in work_json:
w["highlights"] = [sanitize_for_latex(o) for o in w["highlights"]]
# Convert the project section into JSON
resp = llm.prompt_and_response(project_json_prompt)
project_json = parse_json_garbage(resp)
project_json = project_json["projects"]
for p in project_json:
p["highlights"] = [sanitize_for_latex(o) for o in p["highlights"]]
# Convert skills into JSON
resp = llm.prompt_and_response(skills_json_prompt)
skills_json = parse_json_garbage(resp)
skills_json = [{"label": s['topic'], "details": ', '.join(s['name'])}
for s in skills_json["skills"]]
# Convert summary
resp = sanitize_for_latex(llm.prompt_and_response(summary_json_prompt).replace(
"\n", "").strip())
print(resp)
return {"works": work_json, "projects": project_json, "skills": skills_json, "summary": resp}
# LOAD IN ENVIRONMENT VARIABLES
load_dotenv()
# ----- BEGIN USER PARAMETERS -----
# Switch to OpenAIChatAPI() if you want to use the experimental chat API -- note that none of your strings can contain this character: -> " <- .
USE_SELENIUM = True
llm = OpenAIBackendAPI() if not USE_SELENIUM else OpenAIChatAPI()
GENERATE_COVER_LETTER = True
# USER: CHANGE THE JOB NAME FOR MORE SPECIFIC RESEARCH / BETTER RESULES
COMPANY_NAME = os.getenv("JOB_COMPANY_NAME")
JOB_DESCRIPTION = load_string(os.getenv("JOB_DESCRIPTION_FILEPATH"))
DEFAULT_DESCRIPTION = "REPLACE THESE WORDS WITH THE TEXT OF YOUR JOB DESCRIPTION"
assert JOB_DESCRIPTION != DEFAULT_DESCRIPTION, "Only finding the default description! Copy in the actual job's description to that file! "
# Work experiences pipeline
with open('experiences.json', 'r') as f:
info_dct = json.load(f)
# ----- END USER PARAMETERS -----
# Processing
resume_string = get_resume_in_text(info_dct)
optimized_sections = resume_text_optimize(
COMPANY_NAME, JOB_DESCRIPTION, resume_string, llm)
# Post generation
info_dct["work_experiences"] = optimized_sections["works"]
info_dct["project_experiences"] = optimized_sections["projects"]
try:
info_dct["skills"] = optimized_sections["skills"]
except:
print(optimized_sections)
info_dct["summary"] = optimized_sections["summary"]
if os.path.exists('test'):
shutil.rmtree("test")
with open('experiences2.json', 'w') as f:
json.dump(info_dct, f)
print("Generating resume", JOB_DESCRIPTION)
generate_resume(info_dct, JOB_DESCRIPTION)
if GENERATE_COVER_LETTER:
os.makedirs("cover_letters", exist_ok=True)
generate_cover_letter(llm)
print("Done.")
llm.quit()