-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
openai_wrapper.py
138 lines (120 loc) · 6.76 KB
/
openai_wrapper.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
import json
import requests
import prompts
from urllib import parse
from loguru import logger
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from openai import OpenAI
from envs import OPEN_AI_KEY, LaTeX_COMPILER_URL
from models import AIModel
from templates import latex_template
client = OpenAI(api_key=OPEN_AI_KEY) # we recommend using python-dotenv to add OPENAI_API_KEY="My API Key" to your .env file so that your API Key is not stored in source control.
class TailoredResume(BaseModel):
tailored_resume: str
class TailoredCoverLetter(BaseModel):
tailored_coverletter: str
class CustomizedCV(BaseModel):
customized_resume: str
class TailoredCL(BaseModel):
customized_cover_letter: str
class CompanyName(BaseModel):
company_name: str
def create_customized_cv(resume_text: str, job_description_text: str, model=AIModel.gpt_4o_mini):
completion = client.beta.chat.completions.parse(
model=model,
messages=[
{"role": "system", "content": """You will be given a job description. Create a customized resume in LaTeX.
Follow these instructions:
- change the title in a way that matches the job title
- Just focus on the contents. Do not change any settings, such as paper size, style, packages, and so on.
- For each past job in the experience section do not change the company name. Keep a 1-2 sentence about that company description. This should be concise but specific enough to give a good understanding of the past company.
- Make sure the skills in the job description are included in the resume.
- For items (bullet points) of each job rephrase them in a way that matches the language of the job description.
- Make technologies keywords bold. Bold syntax is \\textbf{text}.
- Run through all LaTeX after creation and make sure you're following LaTeX syntax.
- Make sure that each LaTeX instruction is in a single line. Don't put all the code in one line.
Resume LaTeX:""" + resume_text},
{"role": "user", "content": "Job description: "+job_description_text}
],
response_format=CustomizedCV # ensures the out put is a json with the given format. For unsupported models, we can use JSON mode. read here: https://platform.openai.com/docs/guides/structured-outputs/json-mode
)
ai_tailored_cv_response, = json.loads(completion.choices[0].message.content).values() # create the json object and unpack
return ai_tailored_cv_response
def create_customized_cl(resume_text: str, job_description_text: str, model=AIModel.gpt_4o_mini):
completion = client.beta.chat.completions.parse(
model=model,
messages=[
{"role": "system", "content": """You will be given a job description. Create a customized cover letter based on the resume is given.
Follow these instructions:
- The body of it should be at most two paragraphs.
- Focus on my experiences and skills that align with the job description.
- Not overly formal.
Resume:""" + resume_text},
{"role": "user", "content": "Job description: "+job_description_text}
],
response_format=TailoredCL
)
ai_tailored_cl_response, = json.loads(completion.choices[0].message.content).values() # create the json object and unpack
return ai_tailored_cl_response
def ai_prompt(prompt: str, model=AIModel.gpt_4o_mini) -> str:
completion = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt}
],
)
return completion.choices[0].message.content
def create_tailored_plain_resume(resume: str, job_description: str, model=AIModel.gpt_4o_mini) -> str:
completion = client.beta.chat.completions.parse(
model=model,
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompts.create_tailored_resume.format(resume=resume, job_description=job_description)}
],
response_format=TailoredResume
)
tailored_resume = json.loads(completion.choices[0].message.content)["tailored_resume"]
logger.debug(f"The tailored CV plain text is: {tailored_resume}")
return tailored_resume
def covert_plain_resume_to_latex(plain_resume: str, model=AIModel.gpt_4o_mini):
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompts.convert_plain_resume_to_latex.format(resume=plain_resume, latex_template=latex_template)}
]
i = 1
while i < 5: # and error in the code
completion = client.beta.chat.completions.parse(
model=model,
messages=messages,
response_format=TailoredResume
)
tailored_resume = json.loads(completion.choices[0].message.content)["tailored_resume"]
logger.debug(f"The tailored CV Latex code in iteration {i} is: {tailored_resume}")
trimed_tailored_resume = tailored_resume[tailored_resume.find(r"\documentclass"):tailored_resume.rfind(r"\end{document}")+len(r"\end{document}")] # removes possible extra things that AI adds
latex_compiler_reponse = requests.get(url=LaTeX_COMPILER_URL+parse.quote(trimed_tailored_resume))
logger.debug(f"Request url to the LaTeX compiler is: {latex_compiler_reponse.url}")
if not b"error: " in latex_compiler_reponse.content: # there is no error in the compiled code
return latex_compiler_reponse, trimed_tailored_resume
logger.debug(f"There is an error in the latex code: {latex_compiler_reponse.content}")
messages.extend([{"role": "assistant", "content": tailored_resume},
{"role": "user", "content": prompts.fix_latex_error.format(error=latex_compiler_reponse.content)}])
i += 1
return latex_compiler_reponse, trimed_tailored_resume
def create_tailored_plain_coverletter(resume: str, job_description: str, model=AIModel.gpt_4o_mini) -> str:
completion = client.beta.chat.completions.parse(
model=model,
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompts.create_tailored_coverletter_prompt.format(resume=resume, job_description=job_description)}
],
response_format=TailoredCoverLetter
)
return json.loads(completion.choices[0].message.content)["tailored_coverletter"]
def ai_messages(messages: list[tuple[str, str]], model=AIModel.gpt_4o_mini) -> str:
completion = client.chat.completions.create(
model=model,
messages=messages
)
return completion.choices[0].message.content