dmurali commited on
Commit
cdce7a3
1 Parent(s): 2d3e8ff

Upload 6 files

Browse files
Files changed (3) hide show
  1. PwsResumeClassifier.py +56 -20
  2. app.py +94 -134
  3. app_funtions.py +150 -0
PwsResumeClassifier.py CHANGED
@@ -25,6 +25,8 @@ class PwsResumeClassifier:
25
  paragraph) the reason why the resume was deemed as a good match and why it got the ranking it did.'''
26
 
27
  def __init__(self, organization_id, project_id, api_key):
 
 
28
  self.client = OpenAI(
29
  organization=organization_id,
30
  project=project_id,
@@ -37,7 +39,6 @@ class PwsResumeClassifier:
37
  instructions=self.instructions,
38
  model='gpt-4-turbo',
39
  tools=[{"type": "file_search"}],
40
- temperature=0.1
41
  )
42
 
43
  self.assistant_id = self.assistant.id
@@ -49,18 +50,18 @@ class PwsResumeClassifier:
49
  file_name in
50
  file_names]
51
 
52
- vector_store = self.client.beta.vector_stores.create(
53
  name="Resumes",
54
  )
55
 
56
  file_batch = self.client.beta.vector_stores.file_batches.upload_and_poll(
57
- vector_store_id=vector_store.id,
58
  files=file_streams
59
  )
60
 
61
  self.assistant = self.client.beta.assistants.update(
62
  assistant_id=self.assistant_id,
63
- tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}},
64
  )
65
 
66
  def get_best_resumes(self, job_description) -> str:
@@ -80,28 +81,63 @@ class PwsResumeClassifier:
80
 
81
  regex_patter = r"【.*?】"
82
  message_content = re.sub(regex_patter, '', message_content)
83
- return message_content
84
 
85
- def enter_live_thread(self):
86
- thread = self.client.beta.threads.create()
87
- while True:
88
- text = input("Enter your query:")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- if text == 'quit':
91
- break
92
 
93
- message = self.client.beta.threads.messages.create(
94
- thread_id=thread.id,
 
95
  role="user",
96
- content=text,
97
  )
98
 
99
  run = self.client.beta.threads.runs.create_and_poll(
100
- thread_id=thread.id, assistant_id=self.assistant_id
 
101
  )
102
 
103
- self.client.beta.threads.delete(thread.id)
104
- messages = list(self.client.beta.threads.messages.list(thread_id=thread.id, run_id=run.id))
105
- message_content = messages[0].content[0].text
106
- print("Response: \n")
107
- print(f"{message_content.value}\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  paragraph) the reason why the resume was deemed as a good match and why it got the ranking it did.'''
26
 
27
  def __init__(self, organization_id, project_id, api_key):
28
+ self.thread_chatbot = None
29
+ self.assistant_chatbot = None
30
  self.client = OpenAI(
31
  organization=organization_id,
32
  project=project_id,
 
39
  instructions=self.instructions,
40
  model='gpt-4-turbo',
41
  tools=[{"type": "file_search"}],
 
42
  )
43
 
44
  self.assistant_id = self.assistant.id
 
50
  file_name in
51
  file_names]
52
 
53
+ self.vector_store = self.client.beta.vector_stores.create(
54
  name="Resumes",
55
  )
56
 
57
  file_batch = self.client.beta.vector_stores.file_batches.upload_and_poll(
58
+ vector_store_id=self.vector_store.id,
59
  files=file_streams
60
  )
61
 
62
  self.assistant = self.client.beta.assistants.update(
63
  assistant_id=self.assistant_id,
64
+ tool_resources={"file_search": {"vector_store_ids": [self.vector_store.id]}},
65
  )
66
 
67
  def get_best_resumes(self, job_description) -> str:
 
81
 
82
  regex_patter = r"【.*?】"
83
  message_content = re.sub(regex_patter, '', message_content)
 
84
 
85
+ description_chatbot = r'''You are an assistant that answers questions regarding a set of resumes and a job
86
+ posting.'''
87
+
88
+ instructions_chatbot = r'''You are provided multiple resumes of applicants applying to a job posting. The job
89
+ description of the posting is: ''' + job_description + '''. The criteria
90
+ for the judging the best matching resume are the following:\n\n
91
+
92
+ 1. How well the applicant's years of experiences matches the years of experience requirement
93
+ in the job posting.\n
94
+ 2. How well the applicant's educational degree(s) match the degree requirements in the job posting.\n
95
+ 3. How well the applicant's skills compare to the skills desired in the job posting.\n
96
+ 4. How well the applicant's previous work experiences have relation to the job posting.\n\n
97
+
98
+ Not all of the criteria above may be relevant to the job posting, but use as many criteria as possible.\n\n. You
99
+ will provide answers to the user's prompts based on the resumes provides, job description, and criteria outlined above.'''
100
+
101
+ self.assistant_chatbot = self.client.beta.assistants.create(
102
+ name="Resume chatbot",
103
+ description=description_chatbot,
104
+ instructions=instructions_chatbot,
105
+ model="gpt-4o",
106
+ tools=[{"type": "file_search"}],
107
+ tool_resources={"file_search": {"vector_store_ids": [self.vector_store.id]}}
108
+ )
109
+ self.thread_chatbot = self.client.beta.threads.create()
110
+
111
+ return message_content
112
 
113
+ def query_assistant(self, user_input):
 
114
 
115
+ try:
116
+ messages = self.client.beta.threads.messages.create(
117
+ thread_id=self.thread_chatbot.id,
118
  role="user",
119
+ content=user_input
120
  )
121
 
122
  run = self.client.beta.threads.runs.create_and_poll(
123
+ thread_id=self.thread_chatbot.id,
124
+ assistant_id=self.assistant_chatbot.id,
125
  )
126
 
127
+ if run.status == 'completed':
128
+ messages = self.client.beta.threads.messages.list(
129
+ thread_id=self.thread_chatbot.id
130
+ )
131
+
132
+ messages = self.client.beta.threads.messages.list(
133
+ thread_id=self.thread_chatbot.id
134
+ )
135
+
136
+ response_text = messages.data[0].content[0].text.value
137
+ regex_patter = r"【.*?】"
138
+ response_text = re.sub(regex_patter, '', response_text)
139
+ return response_text
140
+
141
+ except Exception as e:
142
+ print(e)
143
+ return 'My apologies. We could not fulfill your request due to an error.'
app.py CHANGED
@@ -1,135 +1,95 @@
1
- from PwsResumeClassifier import PwsResumeClassifier as pws
2
- import os
3
- import shutil
4
- import glob
5
- import gradio as gr
6
- from pathlib import Path
7
- from datetime import datetime
8
-
9
-
10
- all_jobs = []
11
- job_names = []
12
- job_descriptions = []
13
- try:
14
- ai = pws(organization_id=os.getenv('OPENAIORG'), project_id=os.getenv('PROJECT'), api_key=os.getenv('APIKEY'))
15
- except Exception as e:
16
- print(e)
17
- print('Could not connect to openai client')
18
-
19
-
20
- def check_password(password):
21
- if password == os.getenv('MILPASSWORD'):
22
-
23
- return [gr.Textbox(visible=False), gr.Button(visible=False), gr.Row.update(visible=False), gr.Row.update(visible=True)]
24
- else:
25
- return [gr.Textbox(visible=True), gr.Button(visible=True), gr.Row.update(visible=True), gr.Row.update(visible=False)]
26
-
27
-
28
- def add_resume(resume_list):
29
- for resume in resume_list:
30
- shutil.copy2(os.path.abspath(resume), os.path.abspath('Resumes'))
31
-
32
-
33
- def remove_resumes(file_explorer):
34
- for file in file_explorer:
35
- os.remove(file)
36
-
37
-
38
- def job_selected(selected_name):
39
-
40
- if selected_name in job_names:
41
- return [selected_name, job_descriptions[job_names.index(selected_name)], gr.Row.update(visible=True),
42
- gr.Markdown()]
43
- elif selected_name == 'Custom':
44
- return ['', '', gr.Row.update(visible=True), gr.Markdown()]
45
- else:
46
- return ['', '', gr.Row.update(visible=False), gr.Markdown()]
47
-
48
-
49
- def send_to_openai(name, description):
50
- try:
51
- print('Sending to AI')
52
- ai.set_vector_store(os.path.abspath('Resumes'))
53
- resumes_output = ai.get_best_resumes('Job Name: ' + name + '. Job Description: ' + description)
54
- response = gr.Markdown('<p>' + resumes_output + '</p>')
55
- return [response, gr.Row.update(visible=True)]
56
- except Exception as e:
57
- response = gr.Markdown('<p>An error occurred. Please try again.</p>')
58
- return [response, gr.Row.update(visible=True)]
59
-
60
-
61
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
62
- files = glob.glob(os.path.abspath('Jobs') + "/*")
63
- for f in files:
64
- with open(f, 'r') as f_job:
65
- job_description = f_job.read()
66
- job_name = Path(f).stem
67
-
68
- job_names.append(job_name)
69
- job_descriptions.append(job_description)
70
-
71
- gr.Markdown("""<h1><center>Resume Processing</center></h1>""")
72
- password_row = gr.Row()
73
- password_text_row = gr.Row(visible=False)
74
- content_row = gr.Row(visible=False)
75
- confirmation_row = gr.Row(visible=False)
76
-
77
- MIL_Password = gr.Textbox(type='password', label="Enter the Password", visible=True)
78
- log_in = gr.Button("Log In", visible=True)
79
- log_in.click(fn=check_password, inputs=MIL_Password,
80
- outputs=[MIL_Password, log_in, password_text_row, content_row])
81
-
82
- with password_text_row:
83
- gr.Markdown("""<h3><center>Incorrect Password. Access Denied.</center></h3>""")
84
-
85
- with content_row:
86
- with gr.Tab('Resumes'):
87
- gr.Markdown("""<h3><center>Resumes to Process:</center></h3>""")
88
- with gr.Row():
89
- with gr.Column():
90
- upload_resume = gr.UploadButton('Upload Resume', file_count="multiple")
91
- with gr.Column():
92
- delete_resume = gr.Button('Delete')
93
-
94
- with gr.Row():
95
-
96
- @gr.render(triggers=[upload_resume.upload, delete_resume.click, log_in.click])
97
- def file_exp():
98
- resume_file_explorer = gr.FileExplorer(
99
- root_dir=os.path.abspath("Resumes"),
100
- label='Resumes',
101
- interactive=True,
102
- elem_id='explorer',
103
- )
104
-
105
- upload_resume.upload(fn=add_resume, inputs=upload_resume)
106
- delete_resume.click(fn=remove_resumes, inputs=resume_file_explorer)
107
- with gr.Tab('OpenAI'):
108
- with gr.Row():
109
- job_select = gr.Dropdown(label='Select Job', choices=job_names + ['Custom'], allow_custom_value=True)
110
- with gr.Row(visible=False) as job_details:
111
- with gr.Column():
112
- job_name_input = gr.Textbox(label='Job Name', lines=1, interactive=True)
113
- job_description_input = gr.Textbox(label='Job Description', lines=5, interactive=True)
114
- get_resumes = gr.Button('Find best resume')
115
- with gr.Row(visible=True) as gpt_response:
116
- best_resumes = gr.Markdown()
117
-
118
- job_select.select(fn=job_selected, inputs=job_select,
119
- outputs=[job_name_input, job_description_input, job_details, best_resumes])
120
- get_resumes.click(send_to_openai, inputs=[job_name_input, job_description_input],
121
- outputs=[best_resumes, gpt_response])
122
-
123
-
124
- if __name__ == '__main__':
125
- # Remove all current resumes from the Resumes folder
126
- files = glob.glob(os.path.abspath('Resumes') + "/*")
127
- for f in files:
128
- os.remove(f)
129
-
130
- # Copy all the defaults resumes to the resume folder
131
- files = glob.glob(os.path.abspath('Default Resumes') + "/*")
132
- for f in files:
133
- shutil.copy(f, os.path.abspath('Resumes'))
134
-
135
  demo.launch()
 
1
+ import app_funtions as appfun
2
+ import os
3
+ import shutil
4
+ import glob
5
+ import gradio as gr
6
+ from pathlib import Path
7
+ from datetime import datetime
8
+
9
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
10
+
11
+ gr.Markdown("""<h1><center>Resume Processing</center></h1>""")
12
+ password_row = gr.Row()
13
+ password_text_row = gr.Row(visible=False)
14
+ content_row = gr.Row(visible=False)
15
+ confirmation_row = gr.Row(visible=False)
16
+
17
+ MIL_Password = gr.Textbox(type='password', label="Enter the Password", visible=True)
18
+ log_in = gr.Button("Log In", visible=True)
19
+ log_in.click(fn=appfun.check_password, inputs=MIL_Password,
20
+ outputs=[MIL_Password, log_in, password_text_row, content_row])
21
+
22
+ with password_text_row:
23
+ gr.Markdown("""<h3><center>Incorrect Password. Access Denied.</center></h3>""")
24
+
25
+ with content_row:
26
+ with gr.Tab('Resume Input'):
27
+ gr.Markdown("""<h3><center>Resumes Input</center></h3>""")
28
+ gr.Markdown("""<p><center>Upload or delete any resumes you would like to have the AI use:</center></p>""")
29
+ with gr.Row():
30
+ with gr.Column():
31
+ upload_resume = gr.UploadButton('Upload Resume', file_count="multiple")
32
+ with gr.Column():
33
+ delete_resume = gr.Button('Delete')
34
+
35
+ with gr.Row():
36
+
37
+ @gr.render(triggers=[upload_resume.upload, delete_resume.click, log_in.click])
38
+ def file_exp():
39
+ resume_file_explorer = gr.FileExplorer(
40
+ root_dir=os.path.abspath("Resumes"),
41
+ label='Resumes',
42
+ interactive=True,
43
+ elem_id='explorer',
44
+ )
45
+ upload_resume.upload(fn=appfun.add_resume, inputs=upload_resume)
46
+ delete_resume.click(fn=appfun.remove_resumes, inputs=resume_file_explorer)
47
+ with gr.Tab('Job Input'):
48
+ gr.Markdown("""<h3><center>Job Input</center></h3>""")
49
+ gr.Markdown("""<p><center>Upload a document you would like to extract job description from. Supported file types are: .pdf,
50
+ .docx, .pptx, .txt.</center></p>""")
51
+ upload_file = gr.File()
52
+ extract_button = gr.Button('Extract')
53
+ extract_completion_message = gr.Markdown("""<p><center></center></p>""")
54
+ with gr.Tab('Rank Resumes'):
55
+ gr.Markdown("""<h3><center>Rank Resumes</center></h3>""")
56
+ gr.Markdown("""<p><center>Choose the job posting of which you would like to get the top 3 resumes for.</center></p>""")
57
+ job_select = gr.Dropdown(label='Select Job', choices=appfun.job_names + ['Custom'], allow_custom_value=True)
58
+ with gr.Row(visible=False) as job_details:
59
+ with gr.Column():
60
+ job_name_input = gr.Textbox(label='Job Name', lines=1, interactive=True)
61
+ job_description_input = gr.Textbox(label='Job Description', lines=5, interactive=True)
62
+ get_resumes = gr.Button('Rank Resumes')
63
+ with gr.Row(visible=True) as gpt_response:
64
+ best_resumes = gr.Markdown()
65
+ with gr.Tab("Chatbot"):
66
+ chatbot = gr.Chatbot(avatar_images=("user.jpeg", "gpt.jpg"), height=750)
67
+ state = gr.State()
68
+ chatbot_textbox = gr.Textbox(label="Input", info="", lines=1,
69
+ placeholder="Please process resumes", scale=1,
70
+ interactive=False)
71
+ chatbot_submit = gr.Button("SEND", interactive=False, scale=1)
72
+ chatbot_submit.click(appfun.my_chatbot, inputs=[chatbot_textbox, state],
73
+ outputs=[chatbot, state, chatbot_textbox])
74
+
75
+
76
+
77
+ job_select.select(fn=appfun.job_selected, inputs=job_select,
78
+ outputs=[job_name_input, job_description_input, job_details, best_resumes])
79
+ get_resumes.click(appfun.send_to_openai, inputs=[job_name_input, job_description_input],
80
+ outputs=[best_resumes, gpt_response, chatbot_textbox, chatbot_submit])
81
+ extract_button.click(fn=appfun.extract_jobs, inputs=upload_file, outputs=[extract_completion_message, job_select])
82
+
83
+
84
+ if __name__ == '__main__':
85
+ # Remove all current resumes from the Resumes folder
86
+ files = glob.glob(os.path.abspath('Resumes') + "/*")
87
+ for f in files:
88
+ os.remove(f)
89
+
90
+ # Copy all the defaults resumes to the resume folder
91
+ files = glob.glob(os.path.abspath('Default Resumes') + "/*")
92
+ for f in files:
93
+ shutil.copy(f, os.path.abspath('Resumes'))
94
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  demo.launch()
app_funtions.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ from PwsResumeClassifier import PwsResumeClassifier as pws
4
+ from openai import OpenAI
5
+ import app_funtions as appfun
6
+ import os
7
+ import shutil
8
+ import glob
9
+ import gradio as gr
10
+ import json
11
+ from pathlib import Path
12
+
13
+ os.environ['PROJECT'] = 'proj_ct77YbVBWyY3cNDoGA4Xkpzk'
14
+ os.environ['OPENAIORG'] = 'org-Qc0wKzpN6ewg8NpSc9s7cTtS'
15
+ os.environ['MILPASSWORD'] = 'MILRESUME'
16
+ os.environ['APIKEY'] = 'sk-proj-24nTWywXfoK0f4fEd2jdT3BlbkFJLaAZTDMkNcY8HP20Asf2'
17
+
18
+ job_names = []
19
+ job_descriptions = []
20
+
21
+ client_pws = pws(organization_id=os.getenv('OPENAIORG'), project_id=os.getenv('PROJECT'), api_key=os.getenv('APIKEY'))
22
+
23
+ client = OpenAI(
24
+ organization=os.getenv('OPENAIORG'),
25
+ project=os.getenv('PROJECT'),
26
+ api_key=os.getenv('APIKEY')
27
+ )
28
+
29
+ def check_password(password):
30
+ if password == os.getenv('MILPASSWORD'):
31
+
32
+ return [gr.Textbox(visible=False), gr.Button(visible=False), gr.Row.update(visible=False), gr.Row.update(visible=True)]
33
+ else:
34
+ return [gr.Textbox(visible=True), gr.Button(visible=True), gr.Row.update(visible=True), gr.Row.update(visible=False)]
35
+
36
+
37
+ def add_resume(resume_list):
38
+ for resume in resume_list:
39
+ shutil.copy2(os.path.abspath(resume), os.path.abspath('Resumes'))
40
+
41
+
42
+ def remove_resumes(file_explorer):
43
+ for file in file_explorer:
44
+ os.remove(file)
45
+
46
+
47
+ def job_selected(selected_name):
48
+ print(job_names)
49
+ if selected_name in job_names:
50
+ return [selected_name, job_descriptions[job_names.index(selected_name)], gr.Row.update(visible=True),
51
+ gr.Markdown()]
52
+ elif selected_name == 'Custom':
53
+ return ['', '', gr.Row.update(visible=True), gr.Markdown()]
54
+ else:
55
+ return ['', '', gr.Row.update(visible=False), gr.Markdown()]
56
+
57
+
58
+ def send_to_openai(name, description):
59
+
60
+ try:
61
+ print('Sending to AI')
62
+ client_pws.set_vector_store(os.path.abspath('Resumes'))
63
+ resumes_output = client_pws.get_best_resumes('Job Name: ' + name + '. Job Description: ' + description)
64
+ response = gr.Markdown('<p>' + resumes_output + '</p>')
65
+ return [response, gr.Row.update(visible=True),
66
+ gr.Textbox(label="Input", info="", lines=1, placeholder="Ask the chatbot.", interactive=True, scale=1),
67
+ gr.Button("SEND", interactive=True, scale=1)]
68
+ except Exception as e:
69
+ response = gr.Markdown('<p>An error occurred. Please try again.</p>')
70
+ return [response, gr.Row.update(visible=True),
71
+ gr.Textbox(label="Input", info="", lines=1, placeholder="Please process resumes", interactive=False, scale=1),
72
+ gr.Button("SEND", interactive=False, scale=1)]
73
+
74
+
75
+ def extract_jobs(filepath):
76
+ try:
77
+ description = r"""You are a document information extraction assistant that extract job names and job description
78
+ precisely and accurately from a provided file."""
79
+
80
+ instructions = r"""You will be provided a document that contains information about one or more job positions.
81
+ You will extract all of these job positions and return them in a JSON Array. Each item in the JSON Array will
82
+ have a field "job_name" for the name of the job position and "job_description" for the description and
83
+ requirements of the job position. Your response
84
+ should only be a JSON Array with all items."""
85
+
86
+ file_streams = [open(filepath, "rb")]
87
+ vector_store = client.beta.vector_stores.create(
88
+ name="Job Description File",
89
+ )
90
+
91
+ file_batch = client.beta.vector_stores.file_batches.upload_and_poll(
92
+ vector_store_id=vector_store.id,
93
+ files=file_streams
94
+ )
95
+
96
+ assistant = client.beta.assistants.create(
97
+ name="Job Description Extract Assistant",
98
+ description=description,
99
+ instructions=instructions,
100
+ model="gpt-4o",
101
+ tools=[{"type": "file_search"}],
102
+ tool_resources={"file_search": {"vector_store_ids": [vector_store.id]}},
103
+ temperature=0
104
+ )
105
+ thread = client.beta.threads.create()
106
+
107
+ messages = client.beta.threads.messages.create(
108
+ thread_id=thread.id,
109
+ role="user",
110
+ content="Please extract all job descriptions"
111
+ )
112
+
113
+ run = client.beta.threads.runs.create_and_poll(
114
+ thread_id=thread.id,
115
+ assistant_id=assistant.id,
116
+ )
117
+
118
+ if run.status == 'completed':
119
+ messages = client.beta.threads.messages.list(
120
+ thread_id=thread.id
121
+ )
122
+
123
+ messages = client.beta.threads.messages.list(
124
+ thread_id=thread.id
125
+ )
126
+ print(messages.data[0].content[0].text.value)
127
+ json_array = messages.data[0].content[0].text.value
128
+ json_array = re.sub(r"^```json", "", json_array)
129
+ json_array = re.sub(r"```$", "", json_array)
130
+ json_array = re.sub(r"【.*?】", "", json_array)
131
+ print(json_array)
132
+ print(json.loads(json_array))
133
+
134
+ for job in json.loads(json_array):
135
+ job_names.append(job['job_name'])
136
+ job_descriptions.append(job['job_description'])
137
+ return [gr.Markdown("""<p><center>Extraction completed successfully</center></p>"""),
138
+ gr.Dropdown(label='Select Job', choices=job_names + ['Custom'], allow_custom_value=True)]
139
+ except Exception as e:
140
+ return [gr.Markdown("""<p><center>Parsing failed. Error message: """ + repr(e) + """</center></p>"""),
141
+ gr.Dropdown(label='Select Job', choices=appfun.job_names + ['Custom'], allow_custom_value=True)]
142
+
143
+
144
+ def my_chatbot(user_input, history):
145
+ text = ""
146
+ history = history or []
147
+ output = client_pws.query_assistant(user_input)
148
+ history.append((user_input, output))
149
+
150
+ return history, history, text