Arjun Singh commited on
Commit
6b6ab10
·
1 Parent(s): 63b71b7

New recruiting agent

Browse files
Files changed (2) hide show
  1. app.py +242 -0
  2. requirements.txt +18 -0
app.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from langchain.document_loaders import PyPDFLoader, UnstructuredFileLoader
3
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
4
+ from langchain.embeddings import HuggingFaceEmbeddings
5
+ from langchain.vectorstores import Chroma
6
+ from langchain.chains import LLMChain
7
+ from langchain_groq import ChatGroq
8
+ from typing import List, Dict
9
+ import os
10
+ import tempfile
11
+
12
+ # Initialize embeddings and vector store
13
+ embeddings = HuggingFaceEmbeddings()
14
+ vector_store = Chroma(embedding_function=embeddings, persist_directory="./chroma_db")
15
+
16
+ # Initialize LLM
17
+ llm = ChatGroq(
18
+ api_key=os.environ["GROQ_API_KEY"],
19
+ model_name="llama-3.1-8b-instant"
20
+ )
21
+
22
+ def process_candidate_submission(resume_file, job_description: str) -> str:
23
+ # Load and process resume
24
+ if resume_file.name.endswith('.pdf'):
25
+ loader = PyPDFLoader(resume_file.name)
26
+ else:
27
+ loader = UnstructuredFileLoader(resume_file.name)
28
+
29
+ resume_doc = loader.load()[0]
30
+
31
+ # Create prompt for cold email generation
32
+ prompt_template = """
33
+ Given the following resume and job description, create a professional cold email:
34
+
35
+ Resume:
36
+ {resume_text}
37
+
38
+ Job Description:
39
+ {job_description}
40
+
41
+ Generate a concise, compelling cold email that highlights the candidate's relevant skills and experience.
42
+ """
43
+
44
+ chain = LLMChain(
45
+ llm=llm,
46
+ prompt=prompt_template
47
+ )
48
+
49
+ response = chain.run(
50
+ resume_text=resume_doc.page_content,
51
+ job_description=job_description
52
+ )
53
+
54
+ return response
55
+
56
+ def store_culture_docs(culture_files: List[tempfile._TemporaryFileWrapper]) -> str:
57
+ """Store company culture documentation in the vector store"""
58
+ text_splitter = RecursiveCharacterTextSplitter(
59
+ chunk_size=1000,
60
+ chunk_overlap=200
61
+ )
62
+
63
+ all_docs = []
64
+ for file in culture_files:
65
+ if file.name.endswith('.pdf'):
66
+ loader = PyPDFLoader(file.name)
67
+ else:
68
+ loader = UnstructuredFileLoader(file.name)
69
+ docs = loader.load()
70
+ splits = text_splitter.split_documents(docs)
71
+ all_docs.extend(splits)
72
+
73
+ vector_store.add_documents(all_docs, collection_name="culture_docs")
74
+ return f"Successfully stored {len(all_docs)} culture document chunks"
75
+
76
+ def store_resumes(resume_files: List[tempfile._TemporaryFileWrapper]) -> str:
77
+ """Store resumes in the vector store"""
78
+ text_splitter = RecursiveCharacterTextSplitter(
79
+ chunk_size=1000,
80
+ chunk_overlap=200
81
+ )
82
+
83
+ all_docs = []
84
+ for file in resume_files:
85
+ if file.name.endswith('.pdf'):
86
+ loader = PyPDFLoader(file.name)
87
+ else:
88
+ loader = UnstructuredFileLoader(file.name)
89
+ docs = loader.load()
90
+ splits = text_splitter.split_documents(docs)
91
+ all_docs.extend(splits)
92
+
93
+ vector_store.add_documents(all_docs, collection_name="resumes")
94
+ return f"Successfully stored {len(all_docs)} resume chunks"
95
+
96
+ def analyze_candidates(job_description: str) -> str:
97
+ """Analyze candidates against job description and culture fit"""
98
+
99
+ # Extract skills from job description
100
+ skills_prompt = """
101
+ Extract the key technical skills and requirements from this job description:
102
+
103
+ {job_description}
104
+
105
+ Return the skills as a comma-separated list.
106
+ """
107
+
108
+ skills_chain = LLMChain(
109
+ llm=llm,
110
+ prompt=skills_prompt
111
+ )
112
+
113
+ skills = skills_chain.run(job_description=job_description)
114
+
115
+ # Query vector store for matching resumes
116
+ results = vector_store.similarity_search(
117
+ job_description,
118
+ k=5,
119
+ collection_name="resumes"
120
+ )
121
+
122
+ # Get culture documentation
123
+ culture_docs = vector_store.similarity_search(
124
+ job_description,
125
+ k=3,
126
+ collection_name="culture_docs"
127
+ )
128
+
129
+ # Analysis prompt
130
+ analysis_prompt = """
131
+ Analyze these candidates for the job position and culture fit.
132
+
133
+ Job Description:
134
+ {job_description}
135
+
136
+ Required Skills:
137
+ {skills}
138
+
139
+ Company Culture Context:
140
+ {culture_docs}
141
+
142
+ Candidate Resumes:
143
+ {resumes}
144
+
145
+ For each candidate, provide:
146
+ 1. Skills match (percentage)
147
+ 2. Culture fit assessment
148
+ 3. Recommendation (move forward/reject)
149
+ 4. Brief explanation
150
+ """
151
+
152
+ analysis_chain = LLMChain(
153
+ llm=llm,
154
+ prompt=analysis_prompt
155
+ )
156
+
157
+ analysis = analysis_chain.run(
158
+ job_description=job_description,
159
+ skills=skills,
160
+ culture_docs="\n".join([doc.page_content for doc in culture_docs]),
161
+ resumes="\n".join([doc.page_content for doc in results])
162
+ )
163
+
164
+ return analysis
165
+
166
+ def create_interface():
167
+ with gr.Blocks() as app:
168
+ gr.Markdown("# AI Recruiter Assistant")
169
+
170
+ with gr.Tabs():
171
+ # Candidate View
172
+ with gr.Tab("Candidate View"):
173
+ with gr.Row():
174
+ resume_upload = gr.File(label="Upload Resume")
175
+ job_desc_input = gr.Textbox(
176
+ label="Paste Job Description",
177
+ lines=10
178
+ )
179
+ generate_btn = gr.Button("Generate Cold Email")
180
+ email_output = gr.Textbox(
181
+ label="Generated Cold Email",
182
+ lines=10
183
+ )
184
+
185
+ generate_btn.click(
186
+ process_candidate_submission,
187
+ inputs=[resume_upload, job_desc_input],
188
+ outputs=email_output
189
+ )
190
+
191
+ # Recruiter View
192
+ with gr.Tab("Recruiter View"):
193
+ with gr.Row():
194
+ culture_docs_upload = gr.File(
195
+ label="Upload Company Culture Documents",
196
+ file_count="multiple"
197
+ )
198
+ store_culture_btn = gr.Button("Store Culture Docs")
199
+ culture_status = gr.Textbox(label="Status")
200
+
201
+ with gr.Row():
202
+ resume_bulk_upload = gr.File(
203
+ label="Upload Resumes",
204
+ file_count="multiple"
205
+ )
206
+ store_resumes_btn = gr.Button("Store Resumes")
207
+ resume_status = gr.Textbox(label="Status")
208
+
209
+ with gr.Row():
210
+ job_desc_recruiter = gr.Textbox(
211
+ label="Paste Job Description",
212
+ lines=10
213
+ )
214
+ analyze_btn = gr.Button("Analyze Candidates")
215
+ analysis_output = gr.Textbox(
216
+ label="Analysis Results",
217
+ lines=20
218
+ )
219
+
220
+ store_culture_btn.click(
221
+ store_culture_docs,
222
+ inputs=culture_docs_upload,
223
+ outputs=culture_status
224
+ )
225
+
226
+ store_resumes_btn.click(
227
+ store_resumes,
228
+ inputs=resume_bulk_upload,
229
+ outputs=resume_status
230
+ )
231
+
232
+ analyze_btn.click(
233
+ analyze_candidates,
234
+ inputs=job_desc_recruiter,
235
+ outputs=analysis_output
236
+ )
237
+
238
+ return app
239
+
240
+ if __name__ == "__main__":
241
+ app = create_interface()
242
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ langchain
2
+ langchain-community
3
+ langchain-groq
4
+ chromadb
5
+ sentence-transformers
6
+ gradio
7
+ unstructured
8
+ pdf2image
9
+ python-magic
10
+ pdfminer.six
11
+ nltk
12
+ transformers
13
+ torch
14
+ numpy
15
+ Pillow
16
+ pypdf
17
+ python-docx
18
+ unstructured[pdf]