Ntabukiraniro commited on
Commit
ca7d30b
1 Parent(s): f9d9053

Upload 3 files

Browse files
Files changed (3) hide show
  1. Behavioral Screen.py +232 -0
  2. Professional Screen.py +203 -0
  3. Resume Screen.py +212 -0
Behavioral Screen.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_lottie import st_lottie
3
+ from typing import Literal
4
+ from dataclasses import dataclass
5
+ import json
6
+ import base64
7
+ from langchain.memory import ConversationBufferMemory
8
+ from langchain.callbacks import get_openai_callback
9
+ from langchain.chat_models import ChatOpenAI
10
+ from langchain.chains import ConversationChain, RetrievalQA
11
+ from langchain.prompts.prompt import PromptTemplate
12
+ from langchain.text_splitter import NLTKTextSplitter
13
+ from langchain.embeddings import OpenAIEmbeddings
14
+ from langchain.vectorstores import FAISS
15
+ import nltk
16
+ from prompts.prompts import templates
17
+ # Audio
18
+ from speech_recognition.openai_whisper import save_wav_file, transcribe
19
+ from audio_recorder_streamlit import audio_recorder
20
+ from aws.synthesize_speech import synthesize_speech
21
+ from IPython.display import Audio
22
+
23
+ def load_lottiefile(filepath: str):
24
+
25
+ '''Load lottie animation file'''
26
+
27
+ with open(filepath, "r") as f:
28
+ return json.load(f)
29
+
30
+ st_lottie(load_lottiefile("images/welcome.json"), speed=1, reverse=False, loop=True, quality="high", height=300)
31
+
32
+ #st.markdown("""solutions to potential errors:""")
33
+ with st.expander("""Why did I encounter errors when I tried to talk to the AI Interviewer?"""):
34
+ st.write("""
35
+ This is because the app failed to record. Make sure that your microphone is connected and that you have given permission to the browser to access your microphone.""")
36
+
37
+ st.markdown("""\n""")
38
+ jd = st.text_area("""Please enter the job description here (If you don't have one, enter keywords, such as "communication" or "teamwork" instead): """)
39
+ auto_play = st.checkbox("Let AI interviewer speak! (Please don't switch during the interview)")
40
+ #st.toast("4097 tokens is roughly equivalent to around 800 to 1000 words or 3 minutes of speech. Please keep your answer within this limit.")
41
+
42
+ @dataclass
43
+ class Message:
44
+ '''dataclass for keeping track of the messages'''
45
+ origin: Literal["human", "ai"]
46
+ message: str
47
+
48
+ def autoplay_audio(file_path: str):
49
+ '''Play audio automatically'''
50
+ def update_audio():
51
+ global global_audio_md
52
+ with open(file_path, "rb") as f:
53
+ data = f.read()
54
+ b64 = base64.b64encode(data).decode()
55
+ global_audio_md = f"""
56
+ <audio controls autoplay="true">
57
+ <source src="data:audio/mp3;base64,{b64}" type="audio/mp3">
58
+ </audio>
59
+ """
60
+ def update_markdown(audio_md):
61
+ st.markdown(audio_md, unsafe_allow_html=True)
62
+ update_audio()
63
+ update_markdown(global_audio_md)
64
+
65
+ def embeddings(text: str):
66
+
67
+ '''Create embeddings for the job description'''
68
+
69
+ nltk.download('punkt')
70
+ text_splitter = NLTKTextSplitter()
71
+ texts = text_splitter.split_text(text)
72
+ # Create emebeddings
73
+ embeddings = OpenAIEmbeddings()
74
+ docsearch = FAISS.from_texts(texts, embeddings)
75
+ retriever = docsearch.as_retriever(search_tupe='similarity search')
76
+ return retriever
77
+
78
+ def initialize_session_state():
79
+
80
+ '''Initialize session state variables'''
81
+
82
+ if "retriever" not in st.session_state:
83
+ st.session_state.retriever = embeddings(jd)
84
+ if "chain_type_kwargs" not in st.session_state:
85
+ Behavioral_Prompt = PromptTemplate(input_variables=["context", "question"],
86
+ template=templates.behavioral_template)
87
+ st.session_state.chain_type_kwargs = {"prompt": Behavioral_Prompt}
88
+ # interview history
89
+ if "history" not in st.session_state:
90
+ st.session_state.history = []
91
+ st.session_state.history.append(Message("ai", "Hello there! I am your interviewer today. I will access your soft skills through a series of questions. Let's get started! Please start by saying hello or introducing yourself. Note: The maximum length of your answer is 4097 tokens!"))
92
+ # token count
93
+ if "token_count" not in st.session_state:
94
+ st.session_state.token_count = 0
95
+ if "memory" not in st.session_state:
96
+ st.session_state.memory = ConversationBufferMemory()
97
+ if "guideline" not in st.session_state:
98
+ llm = ChatOpenAI(
99
+ model_name="gpt-3.5-turbo",
100
+ temperature=0.8, )
101
+ st.session_state.guideline = RetrievalQA.from_chain_type(
102
+ llm=llm,
103
+ chain_type_kwargs=st.session_state.chain_type_kwargs, chain_type='stuff',
104
+ retriever=st.session_state.retriever, memory=st.session_state.memory).run(
105
+ "Create an interview guideline and prepare total of 8 questions. Make sure the questions tests the soft skills")
106
+ # llm chain and memory
107
+ if "conversation" not in st.session_state:
108
+ llm = ChatOpenAI(
109
+ model_name = "gpt-3.5-turbo",
110
+ temperature = 0.8,)
111
+ PROMPT = PromptTemplate(
112
+ input_variables=["history", "input"],
113
+ template="""I want you to act as an interviewer strictly following the guideline in the current conversation.
114
+ Candidate has no idea what the guideline is.
115
+ Ask me questions and wait for my answers. Do not write explanations.
116
+ Ask question like a real person, only one question at a time.
117
+ Do not ask the same question.
118
+ Do not repeat the question.
119
+ Do ask follow-up questions if necessary.
120
+ You name is GPTInterviewer.
121
+ I want you to only reply as an interviewer.
122
+ Do not write all the conversation at once.
123
+ If there is an error, point it out.
124
+
125
+ Current Conversation:
126
+ {history}
127
+
128
+ Candidate: {input}
129
+ AI: """)
130
+ st.session_state.conversation = ConversationChain(prompt=PROMPT, llm=llm,
131
+ memory=st.session_state.memory)
132
+ if "feedback" not in st.session_state:
133
+ llm = ChatOpenAI(
134
+ model_name = "gpt-3.5-turbo",
135
+ temperature = 0.5,)
136
+ st.session_state.feedback = ConversationChain(
137
+ prompt=PromptTemplate(input_variables = ["history", "input"], template = templates.feedback_template),
138
+ llm=llm,
139
+ memory = st.session_state.memory,
140
+ )
141
+
142
+ def answer_call_back():
143
+
144
+ '''callback function for answering user input'''
145
+
146
+ with get_openai_callback() as cb:
147
+ # user input
148
+ human_answer = st.session_state.answer
149
+ # transcribe audio
150
+ if voice:
151
+ save_wav_file("temp/audio.wav", human_answer)
152
+ try:
153
+ input = transcribe("temp/audio.wav")
154
+ # save human_answer to history
155
+ except:
156
+ st.session_state.history.append(Message("ai", "Sorry, I didn't get that."))
157
+ return "Please try again."
158
+ else:
159
+ input = human_answer
160
+
161
+ st.session_state.history.append(
162
+ Message("human", input)
163
+ )
164
+ # OpenAI answer and save to history
165
+ llm_answer = st.session_state.conversation.run(input)
166
+ # speech synthesis and speak out
167
+ audio_file_path = synthesize_speech(llm_answer)
168
+ # create audio widget with autoplay
169
+ audio_widget = Audio(audio_file_path, autoplay=True)
170
+ # save audio data to history
171
+ st.session_state.history.append(
172
+ Message("ai", llm_answer)
173
+ )
174
+ st.session_state.token_count += cb.total_tokens
175
+ return audio_widget
176
+
177
+ ### ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
178
+ if jd:
179
+
180
+ initialize_session_state()
181
+ credit_card_placeholder = st.empty()
182
+ col1, col2 = st.columns(2)
183
+ with col1:
184
+ feedback = st.button("Get Interview Feedback")
185
+ with col2:
186
+ guideline = st.button("Show me interview guideline!")
187
+ audio = None
188
+ chat_placeholder = st.container()
189
+ answer_placeholder = st.container()
190
+
191
+ if guideline:
192
+ st.write(st.session_state.guideline)
193
+ if feedback:
194
+ evaluation = st.session_state.feedback.run("please give evalution regarding the interview")
195
+ st.markdown(evaluation)
196
+ st.download_button(label="Download Interview Feedback", data=evaluation, file_name="interview_feedback.txt")
197
+ st.stop()
198
+ else:
199
+ with answer_placeholder:
200
+ voice: bool = st.checkbox("I would like to speak with AI Interviewer!")
201
+ if voice:
202
+ answer = audio_recorder(pause_threshold=2.5, sample_rate=44100)
203
+ #st.warning("An UnboundLocalError will occur if the microphone fails to record.")
204
+ else:
205
+ answer = st.chat_input("Your answer")
206
+ if answer:
207
+ st.session_state['answer'] = answer
208
+ audio = answer_call_back()
209
+ with chat_placeholder:
210
+ for answer in st.session_state.history:
211
+ if answer.origin == 'ai':
212
+ if auto_play and audio:
213
+ with st.chat_message("assistant"):
214
+ st.write(answer.message)
215
+ st.write(audio)
216
+ else:
217
+ with st.chat_message("assistant"):
218
+ st.write(answer.message)
219
+ else:
220
+ with st.chat_message("user"):
221
+ st.write(answer.message)
222
+
223
+ credit_card_placeholder.caption(f"""
224
+ Progress: {int(len(st.session_state.history) / 30 * 100)}% completed.
225
+ """)
226
+
227
+ else:
228
+ st.info("Please submit job description to start interview.")
229
+
230
+
231
+
232
+
Professional Screen.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_lottie import st_lottie
3
+ from typing import Literal
4
+ from dataclasses import dataclass
5
+ import json
6
+ import base64
7
+ from langchain.memory import ConversationBufferMemory
8
+ from langchain.callbacks import get_openai_callback
9
+ from langchain.chat_models import ChatOpenAI
10
+ from langchain.chains import ConversationChain, RetrievalQA
11
+ from langchain.prompts.prompt import PromptTemplate
12
+ from langchain.text_splitter import NLTKTextSplitter
13
+ from langchain.embeddings import OpenAIEmbeddings
14
+ from langchain.vectorstores import FAISS
15
+ import nltk
16
+ from prompts.prompts import templates
17
+ # Audio
18
+ from speech_recognition.openai_whisper import save_wav_file, transcribe
19
+ from audio_recorder_streamlit import audio_recorder
20
+ from aws.synthesize_speech import synthesize_speech
21
+ from IPython.display import Audio
22
+
23
+
24
+ def load_lottiefile(filepath: str):
25
+ with open(filepath, "r") as f:
26
+ return json.load(f)
27
+ st_lottie(load_lottiefile("images/welcome.json"), speed=1, reverse=False, loop=True, quality="high", height=300)
28
+
29
+ #st.markdown("""solutions to potential errors:""")
30
+ with st.expander("""Why did I encounter errors when I tried to talk to the AI Interviewer?"""):
31
+ st.write("""
32
+ This is because the app failed to record. Make sure that your microphone is connected and that you have given permission to the browser to access your microphone.""")
33
+
34
+ jd = st.text_area("Please enter the job description here (If you don't have one, enter keywords, such as PostgreSQL or Python instead): ")
35
+ auto_play = st.checkbox("Let AI interviewer speak! (Please don't switch during the interview)")
36
+
37
+ #st.toast("4097 tokens is roughly equivalent to around 800 to 1000 words or 3 minutes of speech. Please keep your answer within this limit.")
38
+
39
+ @dataclass
40
+ class Message:
41
+ """class for keeping track of interview history."""
42
+ origin: Literal["human", "ai"]
43
+ message: str
44
+
45
+ def save_vector(text):
46
+ """embeddings"""
47
+
48
+ nltk.download('punkt')
49
+ text_splitter = NLTKTextSplitter()
50
+ texts = text_splitter.split_text(text)
51
+ # Create emebeddings
52
+ embeddings = OpenAIEmbeddings()
53
+ docsearch = FAISS.from_texts(texts, embeddings)
54
+ return docsearch
55
+
56
+ def initialize_session_state_jd():
57
+ """ initialize session states """
58
+ if 'jd_docsearch' not in st.session_state:
59
+ st.session_state.jd_docserch = save_vector(jd)
60
+ if 'jd_retriever' not in st.session_state:
61
+ st.session_state.jd_retriever = st.session_state.jd_docserch.as_retriever(search_type="similarity")
62
+ if 'jd_chain_type_kwargs' not in st.session_state:
63
+ Interview_Prompt = PromptTemplate(input_variables=["context", "question"],
64
+ template=templates.jd_template)
65
+ st.session_state.jd_chain_type_kwargs = {"prompt": Interview_Prompt}
66
+ if 'jd_memory' not in st.session_state:
67
+ st.session_state.jd_memory = ConversationBufferMemory()
68
+ # interview history
69
+ if "jd_history" not in st.session_state:
70
+ st.session_state.jd_history = []
71
+ st.session_state.jd_history.append(Message("ai",
72
+ "Hello, Welcome to the interview. I am your interviewer today. I will ask you professional questions regarding the job description you submitted."
73
+ "Please start by introducting a little bit about yourself. Note: The maximum length of your answer is 4097 tokens!"))
74
+ # token count
75
+ if "token_count" not in st.session_state:
76
+ st.session_state.token_count = 0
77
+ if "jd_guideline" not in st.session_state:
78
+ llm = ChatOpenAI(
79
+ model_name = "gpt-3.5-turbo",
80
+ temperature = 0.8,)
81
+ st.session_state.jd_guideline = RetrievalQA.from_chain_type(
82
+ llm=llm,
83
+ chain_type_kwargs=st.session_state.jd_chain_type_kwargs, chain_type='stuff',
84
+ retriever=st.session_state.jd_retriever, memory = st.session_state.jd_memory).run("Create an interview guideline and prepare only one questions for each topic. Make sure the questions tests the technical knowledge")
85
+ # llm chain and memory
86
+ if "jd_screen" not in st.session_state:
87
+ llm = ChatOpenAI(
88
+ model_name="gpt-3.5-turbo",
89
+ temperature=0.8, )
90
+ PROMPT = PromptTemplate(
91
+ input_variables=["history", "input"],
92
+ template="""I want you to act as an interviewer strictly following the guideline in the current conversation.
93
+ Candidate has no idea what the guideline is.
94
+ Ask me questions and wait for my answers. Do not write explanations.
95
+ Ask question like a real person, only one question at a time.
96
+ Do not ask the same question.
97
+ Do not repeat the question.
98
+ Do ask follow-up questions if necessary.
99
+ You name is GPTInterviewer.
100
+ I want you to only reply as an interviewer.
101
+ Do not write all the conversation at once.
102
+ If there is an error, point it out.
103
+
104
+ Current Conversation:
105
+ {history}
106
+
107
+ Candidate: {input}
108
+ AI: """)
109
+
110
+ st.session_state.jd_screen = ConversationChain(prompt=PROMPT, llm=llm,
111
+ memory=st.session_state.jd_memory)
112
+ if 'jd_feedback' not in st.session_state:
113
+ llm = ChatOpenAI(
114
+ model_name="gpt-3.5-turbo",
115
+ temperature=0.8, )
116
+ st.session_state.jd_feedback = ConversationChain(
117
+ prompt=PromptTemplate(input_variables=["history", "input"], template=templates.feedback_template),
118
+ llm=llm,
119
+ memory=st.session_state.jd_memory,
120
+ )
121
+
122
+ def answer_call_back():
123
+ with get_openai_callback() as cb:
124
+ # user input
125
+ human_answer = st.session_state.answer
126
+ # transcribe audio
127
+ if voice:
128
+ save_wav_file("temp/audio.wav", human_answer)
129
+ try:
130
+ input = transcribe("temp/audio.wav")
131
+ # save human_answer to history
132
+ except:
133
+ st.session_state.jd_history.append(Message("ai", "Sorry, I didn't get that."))
134
+ return "Please try again."
135
+ else:
136
+ input = human_answer
137
+
138
+ st.session_state.jd_history.append(
139
+ Message("human", input)
140
+ )
141
+ # OpenAI answer and save to history
142
+ llm_answer = st.session_state.jd_screen.run(input)
143
+ # speech synthesis and speak out
144
+ audio_file_path = synthesize_speech(llm_answer)
145
+ # create audio widget with autoplay
146
+ audio_widget = Audio(audio_file_path, autoplay=True)
147
+ # save audio data to history
148
+ st.session_state.jd_history.append(
149
+ Message("ai", llm_answer)
150
+ )
151
+ st.session_state.token_count += cb.total_tokens
152
+ return audio_widget
153
+
154
+ if jd:
155
+ # initialize session states
156
+ initialize_session_state_jd()
157
+ #st.write(st.session_state.jd_guideline)
158
+ credit_card_placeholder = st.empty()
159
+ col1, col2 = st.columns(2)
160
+ with col1:
161
+ feedback = st.button("Get Interview Feedback")
162
+ with col2:
163
+ guideline = st.button("Show me interview guideline!")
164
+ chat_placeholder = st.container()
165
+ answer_placeholder = st.container()
166
+ audio = None
167
+ # if submit email adress, get interview feedback imediately
168
+ if guideline:
169
+ st.write(st.session_state.jd_guideline)
170
+ if feedback:
171
+ evaluation = st.session_state.jd_feedback.run("please give evalution regarding the interview")
172
+ st.markdown(evaluation)
173
+ st.download_button(label="Download Interview Feedback", data=evaluation, file_name="interview_feedback.txt")
174
+ st.stop()
175
+ else:
176
+ with answer_placeholder:
177
+ voice: bool = st.checkbox("I would like to speak with AI Interviewer")
178
+ if voice:
179
+ answer = audio_recorder(pause_threshold = 2.5, sample_rate = 44100)
180
+ #st.warning("An UnboundLocalError will occur if the microphone fails to record.")
181
+ else:
182
+ answer = st.chat_input("Your answer")
183
+ if answer:
184
+ st.session_state['answer'] = answer
185
+ audio = answer_call_back()
186
+ with chat_placeholder:
187
+ for answer in st.session_state.jd_history:
188
+ if answer.origin == 'ai':
189
+ if auto_play and audio:
190
+ with st.chat_message("assistant"):
191
+ st.write(answer.message)
192
+ st.write(audio)
193
+ else:
194
+ with st.chat_message("assistant"):
195
+ st.write(answer.message)
196
+ else:
197
+ with st.chat_message("user"):
198
+ st.write(answer.message)
199
+
200
+ credit_card_placeholder.caption(f"""
201
+ Progress: {int(len(st.session_state.jd_history) / 30 * 100)}% completed.""")
202
+ else:
203
+ st.info("Please submit a job description to start the interview.")
Resume Screen.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # langchain: https://python.langchain.com/
2
+ from dataclasses import dataclass
3
+ import streamlit as st
4
+ from speech_recognition.openai_whisper import save_wav_file, transcribe
5
+ from audio_recorder_streamlit import audio_recorder
6
+ from langchain.callbacks import get_openai_callback
7
+ from langchain.chat_models import ChatOpenAI
8
+ from langchain.memory import ConversationBufferMemory
9
+ from langchain.chains import RetrievalQA, ConversationChain
10
+ from langchain.prompts.prompt import PromptTemplate
11
+ from prompts.prompts import templates
12
+ from typing import Literal
13
+ from aws.synthesize_speech import synthesize_speech
14
+ from langchain.embeddings import OpenAIEmbeddings
15
+ from langchain.vectorstores import FAISS
16
+ from langchain.text_splitter import NLTKTextSplitter
17
+ from PyPDF2 import PdfReader
18
+ from prompts.prompt_selector import prompt_sector
19
+ from streamlit_lottie import st_lottie
20
+ import json
21
+ from IPython.display import Audio
22
+ import nltk
23
+
24
+
25
+ def load_lottiefile(filepath: str):
26
+ with open(filepath, "r") as f:
27
+ return json.load(f)
28
+ st_lottie(load_lottiefile("images/welcome.json"), speed=1, reverse=False, loop=True, quality="high", height=300)
29
+
30
+ #st.markdown("""solutions to potential errors:""")
31
+ with st.expander("""Why did I encounter errors when I tried to talk to the AI Interviewer?"""):
32
+ st.write("""This is because the app failed to record. Make sure that your microphone is connected and that you have given permission to the browser to access your microphone.""")
33
+ with st.expander("""Why did I encounter errors when I tried to upload my resume?"""):
34
+ st.write("""
35
+ Please make sure your resume is in pdf format. More formats will be supported in the future.
36
+ """)
37
+
38
+ st.markdown("""\n""")
39
+ position = st.selectbox("Select the position you are applying for", ["Data Analyst", "Software Engineer", "Marketing"])
40
+ resume = st.file_uploader("Upload your resume", type=["pdf"])
41
+ auto_play = st.checkbox("Let AI interviewer speak! (Please don't switch during the interview)")
42
+
43
+ #st.toast("4097 tokens is roughly equivalent to around 800 to 1000 words or 3 minutes of speech. Please keep your answer within this limit.")
44
+
45
+ @dataclass
46
+ class Message:
47
+ """Class for keeping track of interview history."""
48
+ origin: Literal["human", "ai"]
49
+ message: str
50
+
51
+ def save_vector(resume):
52
+ """embeddings"""
53
+ nltk.download('punkt')
54
+ pdf_reader = PdfReader(resume)
55
+ text = ""
56
+ for page in pdf_reader.pages:
57
+ text += page.extract_text()
58
+ # Split the document into chunks
59
+ text_splitter = NLTKTextSplitter()
60
+ texts = text_splitter.split_text(text)
61
+
62
+ embeddings = OpenAIEmbeddings()
63
+ docsearch = FAISS.from_texts(texts, embeddings)
64
+ return docsearch
65
+
66
+ def initialize_session_state_resume():
67
+ # convert resume to embeddings
68
+ if 'docsearch' not in st.session_state:
69
+ st.session_state.docserch = save_vector(resume)
70
+ # retriever for resume screen
71
+ if 'retriever' not in st.session_state:
72
+ st.session_state.retriever = st.session_state.docserch.as_retriever(search_type="similarity")
73
+ # prompt for retrieving information
74
+ if 'chain_type_kwargs' not in st.session_state:
75
+ st.session_state.chain_type_kwargs = prompt_sector(position, templates)
76
+ # interview history
77
+ if "resume_history" not in st.session_state:
78
+ st.session_state.resume_history = []
79
+ st.session_state.resume_history.append(Message(origin="ai", message="Hello, I am your interivewer today. I will ask you some questions regarding your resume and your experience. Please start by saying hello or introducing yourself. Note: The maximum length of your answer is 4097 tokens!"))
80
+ # token count
81
+ if "token_count" not in st.session_state:
82
+ st.session_state.token_count = 0
83
+ # memory buffer for resume screen
84
+ if "resume_memory" not in st.session_state:
85
+ st.session_state.resume_memory = ConversationBufferMemory(human_prefix = "Candidate: ", ai_prefix = "Interviewer")
86
+ # guideline for resume screen
87
+ if "resume_guideline" not in st.session_state:
88
+ llm = ChatOpenAI(
89
+ model_name = "gpt-3.5-turbo",
90
+ temperature = 0.5,)
91
+
92
+ st.session_state.resume_guideline = RetrievalQA.from_chain_type(
93
+ llm=llm,
94
+ chain_type_kwargs=st.session_state.chain_type_kwargs, chain_type='stuff',
95
+ retriever=st.session_state.retriever, memory = st.session_state.resume_memory).run("Create an interview guideline and prepare only two questions for each topic. Make sure the questions tests the knowledge")
96
+ # llm chain for resume screen
97
+ if "resume_screen" not in st.session_state:
98
+ llm = ChatOpenAI(
99
+ model_name="gpt-3.5-turbo",
100
+ temperature=0.7, )
101
+
102
+ PROMPT = PromptTemplate(
103
+ input_variables=["history", "input"],
104
+ template= """I want you to act as an interviewer strictly following the guideline in the current conversation.
105
+
106
+ Ask me questions and wait for my answers like a human. Do not write explanations.
107
+ Candidate has no assess to the guideline.
108
+ Only ask one question at a time.
109
+ Do ask follow-up questions if you think it's necessary.
110
+ Do not ask the same question.
111
+ Do not repeat the question.
112
+ Candidate has no assess to the guideline.
113
+ You name is GPTInterviewer.
114
+ I want you to only reply as an interviewer.
115
+ Do not write all the conversation at once.
116
+ Candiate has no assess to the guideline.
117
+
118
+ Current Conversation:
119
+ {history}
120
+
121
+ Candidate: {input}
122
+ AI: """)
123
+ st.session_state.resume_screen = ConversationChain(prompt=PROMPT, llm = llm, memory = st.session_state.resume_memory)
124
+ # llm chain for generating feedback
125
+ if "resume_feedback" not in st.session_state:
126
+ llm = ChatOpenAI(
127
+ model_name="gpt-3.5-turbo",
128
+ temperature=0.5,)
129
+ st.session_state.resume_feedback = ConversationChain(
130
+ prompt=PromptTemplate(input_variables=["history","input"], template=templates.feedback_template),
131
+ llm=llm,
132
+ memory=st.session_state.resume_memory,
133
+ )
134
+
135
+ def answer_call_back():
136
+ with get_openai_callback() as cb:
137
+ human_answer = st.session_state.answer
138
+ if voice:
139
+ save_wav_file("temp/audio.wav", human_answer)
140
+ try:
141
+ input = transcribe("temp/audio.wav")
142
+ # save human_answer to history
143
+ except:
144
+ st.session_state.resume_history.append(Message("ai", "Sorry, I didn't get that."))
145
+ return "Please try again."
146
+ else:
147
+ input = human_answer
148
+ st.session_state.resume_history.append(
149
+ Message("human", input)
150
+ )
151
+ # OpenAI answer and save to history
152
+ llm_answer = st.session_state.resume_screen.run(input)
153
+ # speech synthesis and speak out
154
+ audio_file_path = synthesize_speech(llm_answer)
155
+ # create audio widget with autoplay
156
+ audio_widget = Audio(audio_file_path, autoplay=True)
157
+ # save audio data to history
158
+ st.session_state.resume_history.append(
159
+ Message("ai", llm_answer)
160
+ )
161
+ st.session_state.token_count += cb.total_tokens
162
+ return audio_widget
163
+
164
+ if position and resume:
165
+ # intialize session state
166
+ initialize_session_state_resume()
167
+ credit_card_placeholder = st.empty()
168
+ col1, col2 = st.columns(2)
169
+ with col1:
170
+ feedback = st.button("Get Interview Feedback")
171
+ with col2:
172
+ guideline = st.button("Show me interview guideline!")
173
+ chat_placeholder = st.container()
174
+ answer_placeholder = st.container()
175
+ audio = None
176
+ # if submit email adress, get interview feedback imediately
177
+ if guideline:
178
+ st.markdown(st.session_state.resume_guideline)
179
+ if feedback:
180
+ evaluation = st.session_state.resume_feedback.run("please give evalution regarding the interview")
181
+ st.markdown(evaluation)
182
+ st.download_button(label="Download Interview Feedback", data=evaluation, file_name="interview_feedback.txt")
183
+ st.stop()
184
+ else:
185
+ with answer_placeholder:
186
+ voice: bool = st.checkbox("I would like to speak with AI Interviewer!")
187
+ if voice:
188
+ answer = audio_recorder(pause_threshold=2, sample_rate=44100)
189
+ #st.warning("An UnboundLocalError will occur if the microphone fails to record.")
190
+ else:
191
+ answer = st.chat_input("Your answer")
192
+ if answer:
193
+ st.session_state['answer'] = answer
194
+ audio = answer_call_back()
195
+
196
+ with chat_placeholder:
197
+ for answer in st.session_state.resume_history:
198
+ if answer.origin == 'ai':
199
+ if auto_play and audio:
200
+ with st.chat_message("assistant"):
201
+ st.write(answer.message)
202
+ st.write(audio)
203
+ else:
204
+ with st.chat_message("assistant"):
205
+ st.write(answer.message)
206
+ else:
207
+ with st.chat_message("user"):
208
+ st.write(answer.message)
209
+
210
+ credit_card_placeholder.caption(f"""
211
+ Progress: {int(len(st.session_state.resume_history) / 30 * 100)}% completed.""")
212
+