Upload 10 files
Browse files- .env +1 -0
- .gitignore +160 -0
- Homepage.py +27 -0
- README.md +77 -10
- icon.png +0 -0
- initialization.py +96 -0
- pages/Behavioral Screen.py +205 -0
- pages/Professional Screen.py +179 -0
- pages/Resume Screen.py +188 -0
- requirements.txt +14 -0
.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
GOOGLE_API_KEY=AIzaSyCA4__JMC_ZIQ9xQegIj5LOMLhSSrn3pMw
|
.gitignore
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
share/python-wheels/
|
| 24 |
+
*.egg-info/
|
| 25 |
+
.installed.cfg
|
| 26 |
+
*.egg
|
| 27 |
+
MANIFEST
|
| 28 |
+
|
| 29 |
+
# PyInstaller
|
| 30 |
+
# Usually these files are written by a python script from a template
|
| 31 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 32 |
+
*.manifest
|
| 33 |
+
*.spec
|
| 34 |
+
|
| 35 |
+
# Installer logs
|
| 36 |
+
pip-log.txt
|
| 37 |
+
pip-delete-this-directory.txt
|
| 38 |
+
|
| 39 |
+
# Unit test / coverage reports
|
| 40 |
+
htmlcov/
|
| 41 |
+
.tox/
|
| 42 |
+
.nox/
|
| 43 |
+
.coverage
|
| 44 |
+
.coverage.*
|
| 45 |
+
.cache
|
| 46 |
+
nosetests.xml
|
| 47 |
+
coverage.xml
|
| 48 |
+
*.cover
|
| 49 |
+
*.py,cover
|
| 50 |
+
.hypothesis/
|
| 51 |
+
.pytest_cache/
|
| 52 |
+
cover/
|
| 53 |
+
|
| 54 |
+
# Translations
|
| 55 |
+
*.mo
|
| 56 |
+
*.pot
|
| 57 |
+
|
| 58 |
+
# Django stuff:
|
| 59 |
+
*.log
|
| 60 |
+
local_settings.py
|
| 61 |
+
db.sqlite3
|
| 62 |
+
db.sqlite3-journal
|
| 63 |
+
|
| 64 |
+
# Flask stuff:
|
| 65 |
+
instance/
|
| 66 |
+
.webassets-cache
|
| 67 |
+
|
| 68 |
+
# Scrapy stuff:
|
| 69 |
+
.scrapy
|
| 70 |
+
|
| 71 |
+
# Sphinx documentation
|
| 72 |
+
docs/_build/
|
| 73 |
+
|
| 74 |
+
# PyBuilder
|
| 75 |
+
.pybuilder/
|
| 76 |
+
target/
|
| 77 |
+
|
| 78 |
+
# Jupyter Notebook
|
| 79 |
+
.ipynb_checkpoints
|
| 80 |
+
|
| 81 |
+
# IPython
|
| 82 |
+
profile_default/
|
| 83 |
+
ipython_config.py
|
| 84 |
+
|
| 85 |
+
# pyenv
|
| 86 |
+
# For a library or package, you might want to ignore these files since the code is
|
| 87 |
+
# intended to run in multiple environments; otherwise, check them in:
|
| 88 |
+
# .python-version
|
| 89 |
+
|
| 90 |
+
# pipenv
|
| 91 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 92 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 93 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 94 |
+
# install all needed dependencies.
|
| 95 |
+
#Pipfile.lock
|
| 96 |
+
|
| 97 |
+
# poetry
|
| 98 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
| 99 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 100 |
+
# commonly ignored for libraries.
|
| 101 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
| 102 |
+
#poetry.lock
|
| 103 |
+
|
| 104 |
+
# pdm
|
| 105 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
| 106 |
+
#pdm.lock
|
| 107 |
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
| 108 |
+
# in version control.
|
| 109 |
+
# https://pdm.fming.dev/#use-with-ide
|
| 110 |
+
.pdm.toml
|
| 111 |
+
|
| 112 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 113 |
+
__pypackages__/
|
| 114 |
+
|
| 115 |
+
# Celery stuff
|
| 116 |
+
celerybeat-schedule
|
| 117 |
+
celerybeat.pid
|
| 118 |
+
|
| 119 |
+
# SageMath parsed files
|
| 120 |
+
*.sage.py
|
| 121 |
+
|
| 122 |
+
# Environments
|
| 123 |
+
.env
|
| 124 |
+
.venv
|
| 125 |
+
env/
|
| 126 |
+
venv/
|
| 127 |
+
ENV/
|
| 128 |
+
env.bak/
|
| 129 |
+
venv.bak/
|
| 130 |
+
|
| 131 |
+
# Spyder project settings
|
| 132 |
+
.spyderproject
|
| 133 |
+
.spyproject
|
| 134 |
+
|
| 135 |
+
# Rope project settings
|
| 136 |
+
.ropeproject
|
| 137 |
+
|
| 138 |
+
# mkdocs documentation
|
| 139 |
+
/site
|
| 140 |
+
|
| 141 |
+
# mypy
|
| 142 |
+
.mypy_cache/
|
| 143 |
+
.dmypy.json
|
| 144 |
+
dmypy.json
|
| 145 |
+
|
| 146 |
+
# Pyre type checker
|
| 147 |
+
.pyre/
|
| 148 |
+
|
| 149 |
+
# pytype static type analyzer
|
| 150 |
+
.pytype/
|
| 151 |
+
|
| 152 |
+
# Cython debug symbols
|
| 153 |
+
cython_debug/
|
| 154 |
+
|
| 155 |
+
# PyCharm
|
| 156 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
| 157 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
| 158 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
| 159 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
| 160 |
+
#.idea/
|
Homepage.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from streamlit_option_menu import option_menu
|
| 3 |
+
from app_utils import switch_page
|
| 4 |
+
import streamlit as st
|
| 5 |
+
from PIL import Image
|
| 6 |
+
|
| 7 |
+
im = Image.open("icon.png")
|
| 8 |
+
st.set_page_config(page_title = "AI Interviewer", layout = "centered",page_icon=im)
|
| 9 |
+
|
| 10 |
+
lan = st.selectbox("#### Language", ["English", "中文"])
|
| 11 |
+
|
| 12 |
+
if lan == "English":
|
| 13 |
+
home_title = "AI Interviewer"
|
| 14 |
+
home_introduction = "Welcome to AI Interviewer, empowering your interview preparation with generative AI."
|
| 15 |
+
|
| 16 |
+
st.markdown(
|
| 17 |
+
"<style>#MainMenu{visibility:hidden;}</style>",
|
| 18 |
+
unsafe_allow_html=True
|
| 19 |
+
)
|
| 20 |
+
st.image(im, width=100)
|
| 21 |
+
st.markdown(f"""# {home_title}""",unsafe_allow_html=True)
|
| 22 |
+
st.markdown("""\n""")
|
| 23 |
+
#st.markdown("#### Greetings")
|
| 24 |
+
st.markdown("Welcome to AI Interviewer! 👏 AI Interviewer is your personal interviewer powered by generative AI that conducts mock interviews."
|
| 25 |
+
"You can upload your resume and enter job descriptions, and AI Interviewer will ask you customized questions. Additionally, you can configure your own Interviewer!")
|
| 26 |
+
st.markdown("""\n""")
|
| 27 |
+
|
README.md
CHANGED
|
@@ -1,12 +1,79 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
# AI Interviewer - Version 0.1.2
|
| 4 |
+
|
| 5 |
+
Welcome to AI Interviewer! 👏 AI Interviewer is a cutting-edge application powered by generative AI designed to conduct mock interviews. With the ability to analyze your uploaded resume and job descriptions, AI Interviewer generates tailored questions to enhance your interview preparation. You even have the flexibility to customize your own interviewing experience!
|
| 6 |
+
|
| 7 |
+
## Table of Contents
|
| 8 |
+
|
| 9 |
+
- [Overview](#overview)
|
| 10 |
+
- [Getting Started](#getting-started)
|
| 11 |
+
- [Features](#features)
|
| 12 |
+
- [Upcoming Updates](#upcoming-updates)
|
| 13 |
+
- [Feedback](#feedback)
|
| 14 |
+
- [Contact](#contact)
|
| 15 |
+
<!-- - [Acknowledgments](#acknowledgments) -->
|
| 16 |
+
|
| 17 |
+
## Overview
|
| 18 |
+
|
| 19 |
+
AI Interviewer aims to revolutionize your interview preparation process. Whether you're seeking to improve your technical skills, communication abilities, or adaptability, this application can assist you. Powered by cutting-edge technology from OpenAI, FAISS, and Langchain, AI Interviewer provides a seamless experience that simulates real interview scenarios.
|
| 20 |
+
|
| 21 |
+
## Getting Started
|
| 22 |
+
|
| 23 |
+
To begin your AI Interviewer experience, follow these simple steps:
|
| 24 |
+
|
| 25 |
+
1. **Select Interview Type:** Choose from the following interview screens:
|
| 26 |
+
- **Homepage:** Overview of AI Interviewer.
|
| 27 |
+
- **Behavioral Screen:** Assess your behavioral skills.
|
| 28 |
+
- **Professional Screen:** Evaluate your technical skills.
|
| 29 |
+
- **Resume Screen:** Review your uploaded resume.
|
| 30 |
+
|
| 31 |
+
2. **Customize Your Experience:** Tailor your interview by uploading your resume and providing job descriptions.
|
| 32 |
+
|
| 33 |
+
3. **Choose Interaction Style:** Opt for your preferred interaction style, whether it's through chat or voice.
|
| 34 |
+
|
| 35 |
+
4. **Start Interviewing:** Begin the interview by introducing yourself and responding to AI-generated questions.
|
| 36 |
+
|
| 37 |
+
## Features
|
| 38 |
+
|
| 39 |
+
- **Personalized Questions:** AI Interviewer generates interview questions customized to your uploaded resume and job descriptions.
|
| 40 |
+
|
| 41 |
+
- **Multiple Screens:** Access different screens for behavioral, professional, and resume-related interview aspects.
|
| 42 |
+
|
| 43 |
+
- **Interactive Experience:** Engage in a conversation with the AI interviewer, enhancing the realism of the interview process.
|
| 44 |
+
|
| 45 |
+
- **Easy Refresh:** Initiate a new interview session simply by refreshing the page.
|
| 46 |
+
|
| 47 |
+
- **Choice of Interaction:** Select between chat-based or voice-based interaction styles for your interviews.
|
| 48 |
+
|
| 49 |
+
## Upcoming Updates
|
| 50 |
+
|
| 51 |
+
We are constantly working to improve AI Interviewer and bring you new features. In the pipeline:
|
| 52 |
+
|
| 53 |
+
- Enhanced AI capabilities for even more realistic interviews.
|
| 54 |
+
- Expanded question database for a wider range of industries and roles.
|
| 55 |
+
- Improved voice interaction for a seamless experience.
|
| 56 |
+
|
| 57 |
+
## Feedback
|
| 58 |
+
|
| 59 |
+
We highly value your feedback! Your insights can help us enhance AI Interviewer. Please take a moment to fill out our [Feedback Form](https://docs.google.com/forms/d/13f4q03bk4lD7sKR7qZ8UM1lQDo6NhRaAKv7uIeXHEaQ/viewform?edit_requested=true).
|
| 60 |
+
|
| 61 |
+
## Contact
|
| 62 |
+
|
| 63 |
+
## Contact
|
| 64 |
+
|
| 65 |
+
- GitHub: [jiatastic](https://github.com/jiatastic)
|
| 66 |
+
|
| 67 |
+
<!-- ## Acknowledgments
|
| 68 |
+
|
| 69 |
+
AI Interviewer is powered by a blend of advanced technologies:
|
| 70 |
+
|
| 71 |
+
- OpenAI: Providing the generative AI capabilities.
|
| 72 |
+
- FAISS: Enhancing search and retrieval capabilities.
|
| 73 |
+
- Langchain: Facilitating natural language interactions.
|
| 74 |
+
|
| 75 |
+
The application is proudly built with [Streamlit](https://streamlit.io/).
|
| 76 |
+
|
| 77 |
---
|
| 78 |
|
| 79 |
+
Remember, AI Interviewer is your partner in preparing for your future interviews. Sharpen your skills, boost your confidence, and seize those career opportunities with confidence! 🚀 -->
|
icon.png
ADDED
|
|
initialization.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from langchain.embeddings import OpenAIEmbeddings
|
| 3 |
+
from langchain.vectorstores import FAISS
|
| 4 |
+
from langchain.text_splitter import NLTKTextSplitter
|
| 5 |
+
from langchain.memory import ConversationBufferMemory
|
| 6 |
+
from langchain.chains import RetrievalQA, ConversationChain
|
| 7 |
+
from prompts.prompts import templates
|
| 8 |
+
from langchain.prompts.prompt import PromptTemplate
|
| 9 |
+
from langchain.chat_models import ChatOpenAI
|
| 10 |
+
from PyPDF2 import PdfReader
|
| 11 |
+
from prompts.prompt_selector import prompt_sector
|
| 12 |
+
def embedding(text):
|
| 13 |
+
"""embeddings"""
|
| 14 |
+
text_splitter = NLTKTextSplitter()
|
| 15 |
+
texts = text_splitter.split_text(text)
|
| 16 |
+
# Create emebeddings
|
| 17 |
+
embeddings = OpenAIEmbeddings()
|
| 18 |
+
docsearch = FAISS.from_texts(texts, embeddings)
|
| 19 |
+
return docsearch
|
| 20 |
+
|
| 21 |
+
def resume_reader(resume):
|
| 22 |
+
pdf_reader = PdfReader(resume)
|
| 23 |
+
text = ""
|
| 24 |
+
for page in pdf_reader.pages:
|
| 25 |
+
text += page.extract_text()
|
| 26 |
+
return text
|
| 27 |
+
|
| 28 |
+
def initialize_session_state(template=None, position=None):
|
| 29 |
+
""" initialize session states """
|
| 30 |
+
if 'jd' in st.session_state:
|
| 31 |
+
st.session_state.docsearch = embedding(st.session_state.jd)
|
| 32 |
+
else:
|
| 33 |
+
st.session_state.docsearch = embedding(resume_reader(st.session_state.resume))
|
| 34 |
+
|
| 35 |
+
#if 'retriever' not in st.session_state:
|
| 36 |
+
st.session_state.retriever = st.session_state.docsearch.as_retriever(search_type="similarity")
|
| 37 |
+
#if 'chain_type_kwargs' not in st.session_state:
|
| 38 |
+
if 'jd' in st.session_state:
|
| 39 |
+
Interview_Prompt = PromptTemplate(input_variables=["context", "question"],
|
| 40 |
+
template=template)
|
| 41 |
+
st.session_state.chain_type_kwargs = {"prompt": Interview_Prompt}
|
| 42 |
+
else:
|
| 43 |
+
st.session_state.chain_type_kwargs = prompt_sector(position, templates)
|
| 44 |
+
#if 'memory' not in st.session_state:
|
| 45 |
+
st.session_state.memory = ConversationBufferMemory()
|
| 46 |
+
# interview history
|
| 47 |
+
#if "history" not in st.session_state:
|
| 48 |
+
st.session_state.history = []
|
| 49 |
+
# token count
|
| 50 |
+
#if "token_count" not in st.session_state:
|
| 51 |
+
st.session_state.token_count = 0
|
| 52 |
+
#if "guideline" not in st.session_state:
|
| 53 |
+
llm = ChatOpenAI(
|
| 54 |
+
model_name="gpt-3.5-turbo",
|
| 55 |
+
temperature=0.6, )
|
| 56 |
+
st.session_state.guideline = RetrievalQA.from_chain_type(
|
| 57 |
+
llm=llm,
|
| 58 |
+
chain_type_kwargs=st.session_state.chain_type_kwargs, chain_type='stuff',
|
| 59 |
+
retriever=st.session_state.retriever, memory=st.session_state.memory).run(
|
| 60 |
+
"Create an interview guideline and prepare only one questions for each topic. Make sure the questions tests the technical knowledge")
|
| 61 |
+
# llm chain and memory
|
| 62 |
+
#if "screen" not in st.session_state:
|
| 63 |
+
llm = ChatOpenAI(
|
| 64 |
+
model_name="gpt-3.5-turbo",
|
| 65 |
+
temperature=0.8, )
|
| 66 |
+
PROMPT = PromptTemplate(
|
| 67 |
+
input_variables=["history", "input"],
|
| 68 |
+
template="""I want you to act as an interviewer strictly following the guideline in the current conversation.
|
| 69 |
+
|
| 70 |
+
Ask me questions and wait for my answers like a real person.
|
| 71 |
+
Do not write explanations.
|
| 72 |
+
Ask question like a real person, only one question at a time.
|
| 73 |
+
Do not ask the same question.
|
| 74 |
+
Do not repeat the question.
|
| 75 |
+
Do ask follow-up questions if necessary.
|
| 76 |
+
You name is GPTInterviewer.
|
| 77 |
+
I want you to only reply as an interviewer.
|
| 78 |
+
Do not write all the conversation at once.
|
| 79 |
+
If there is an error, point it out.
|
| 80 |
+
|
| 81 |
+
Current Conversation:
|
| 82 |
+
{history}
|
| 83 |
+
|
| 84 |
+
Candidate: {input}
|
| 85 |
+
AI: """)
|
| 86 |
+
st.session_state.screen = ConversationChain(prompt=PROMPT, llm=llm,
|
| 87 |
+
memory=st.session_state.memory)
|
| 88 |
+
#if "feedback" not in st.session_state:
|
| 89 |
+
llm = ChatOpenAI(
|
| 90 |
+
model_name = "gpt-3.5-turbo",
|
| 91 |
+
temperature = 0.5,)
|
| 92 |
+
st.session_state.feedback = ConversationChain(
|
| 93 |
+
prompt=PromptTemplate(input_variables = ["history", "input"], template = templates.feedback_template),
|
| 94 |
+
llm=llm,
|
| 95 |
+
memory = st.session_state.memory,
|
| 96 |
+
)
|
pages/Behavioral Screen.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.chains import ConversationChain, RetrievalQA
|
| 9 |
+
from langchain.prompts.prompt import PromptTemplate
|
| 10 |
+
from langchain.text_splitter import NLTKTextSplitter
|
| 11 |
+
from langchain.vectorstores import FAISS
|
| 12 |
+
import nltk
|
| 13 |
+
from prompts.prompts import templates
|
| 14 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 15 |
+
import getpass
|
| 16 |
+
import os
|
| 17 |
+
from langchain_google_genai import GoogleGenerativeAIEmbeddings
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
if "GOOGLE_API_KEY" not in os.environ:
|
| 21 |
+
os.environ["GOOGLE_API_KEY"] = "AIzaSyCA4__JMC_ZIQ9xQegIj5LOMLhSSrn3pMw"
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def load_lottiefile(filepath: str):
|
| 26 |
+
|
| 27 |
+
'''Load lottie animation file'''
|
| 28 |
+
|
| 29 |
+
with open(filepath, "r") as f:
|
| 30 |
+
return json.load(f)
|
| 31 |
+
|
| 32 |
+
st.title("Behavioral Screen")
|
| 33 |
+
st.markdown("""\n""")
|
| 34 |
+
jd = st.text_area("""Please enter the job description here (If you don't have one, enter keywords, such as "communication" or "teamwork" instead): """)
|
| 35 |
+
|
| 36 |
+
@dataclass
|
| 37 |
+
class Message:
|
| 38 |
+
'''dataclass for keeping track of the messages'''
|
| 39 |
+
origin: Literal["human", "ai"]
|
| 40 |
+
message: str
|
| 41 |
+
|
| 42 |
+
def autoplay_audio(file_path: str):
|
| 43 |
+
'''Play audio automatically'''
|
| 44 |
+
def update_audio():
|
| 45 |
+
global global_audio_md
|
| 46 |
+
with open(file_path, "rb") as f:
|
| 47 |
+
data = f.read()
|
| 48 |
+
b64 = base64.b64encode(data).decode()
|
| 49 |
+
global_audio_md = f"""
|
| 50 |
+
<audio controls autoplay="true">
|
| 51 |
+
<source src="data:audio/mp3;base64,{b64}" type="audio/mp3">
|
| 52 |
+
</audio>
|
| 53 |
+
"""
|
| 54 |
+
def update_markdown(audio_md):
|
| 55 |
+
st.markdown(audio_md, unsafe_allow_html=True)
|
| 56 |
+
update_audio()
|
| 57 |
+
update_markdown(global_audio_md)
|
| 58 |
+
|
| 59 |
+
def embeddings(text: str):
|
| 60 |
+
|
| 61 |
+
'''Create embeddings for the job description'''
|
| 62 |
+
|
| 63 |
+
nltk.download('punkt')
|
| 64 |
+
text_splitter = NLTKTextSplitter()
|
| 65 |
+
texts = text_splitter.split_text(text)
|
| 66 |
+
# Create emebeddings
|
| 67 |
+
embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
|
| 68 |
+
docsearch = FAISS.from_texts(texts, embeddings)
|
| 69 |
+
retriever = docsearch.as_retriever(search_tupe='similarity search')
|
| 70 |
+
return retriever
|
| 71 |
+
|
| 72 |
+
def initialize_session_state():
|
| 73 |
+
|
| 74 |
+
'''Initialize session state variables'''
|
| 75 |
+
|
| 76 |
+
if "retriever" not in st.session_state:
|
| 77 |
+
st.session_state.retriever = embeddings(jd)
|
| 78 |
+
if "chain_type_kwargs" not in st.session_state:
|
| 79 |
+
Behavioral_Prompt = PromptTemplate(input_variables=["context", "question"],
|
| 80 |
+
template=templates.behavioral_template)
|
| 81 |
+
st.session_state.chain_type_kwargs = {"prompt": Behavioral_Prompt}
|
| 82 |
+
# interview history
|
| 83 |
+
if "history" not in st.session_state:
|
| 84 |
+
st.session_state.history = []
|
| 85 |
+
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!"))
|
| 86 |
+
# token count
|
| 87 |
+
if "token_count" not in st.session_state:
|
| 88 |
+
st.session_state.token_count = 0
|
| 89 |
+
if "memory" not in st.session_state:
|
| 90 |
+
st.session_state.memory = ConversationBufferMemory()
|
| 91 |
+
if "guideline" not in st.session_state:
|
| 92 |
+
llm = ChatGoogleGenerativeAI(
|
| 93 |
+
model="gemini-pro")
|
| 94 |
+
st.session_state.guideline = RetrievalQA.from_chain_type(
|
| 95 |
+
llm=llm,
|
| 96 |
+
chain_type_kwargs=st.session_state.chain_type_kwargs, chain_type='stuff',
|
| 97 |
+
retriever=st.session_state.retriever, memory=st.session_state.memory).run(
|
| 98 |
+
"Create an interview guideline and prepare total of 8 questions. Make sure the questions tests the soft skills")
|
| 99 |
+
# llm chain and memory
|
| 100 |
+
if "conversation" not in st.session_state:
|
| 101 |
+
llm = ChatGoogleGenerativeAI(
|
| 102 |
+
model="gemini-pro")
|
| 103 |
+
PROMPT = PromptTemplate(
|
| 104 |
+
input_variables=["history", "input"],
|
| 105 |
+
template="""I want you to act as an interviewer strictly following the guideline in the current conversation.
|
| 106 |
+
Candidate has no idea what the guideline is.
|
| 107 |
+
Ask me questions and wait for my answers. Do not write explanations.
|
| 108 |
+
Ask question like a real person, only one question at a time.
|
| 109 |
+
Do not ask the same question.
|
| 110 |
+
Do not repeat the question.
|
| 111 |
+
Do ask follow-up questions if necessary.
|
| 112 |
+
You name is GPTInterviewer.
|
| 113 |
+
I want you to only reply as an interviewer.
|
| 114 |
+
Do not write all the conversation at once.
|
| 115 |
+
If there is an error, point it out.
|
| 116 |
+
|
| 117 |
+
Current Conversation:
|
| 118 |
+
{history}
|
| 119 |
+
|
| 120 |
+
Candidate: {input}
|
| 121 |
+
AI: """)
|
| 122 |
+
st.session_state.conversation = ConversationChain(prompt=PROMPT, llm=llm,
|
| 123 |
+
memory=st.session_state.memory)
|
| 124 |
+
if "feedback" not in st.session_state:
|
| 125 |
+
llm = ChatGoogleGenerativeAI(
|
| 126 |
+
model="gemini-pro")
|
| 127 |
+
st.session_state.feedback = ConversationChain(
|
| 128 |
+
prompt=PromptTemplate(input_variables = ["history", "input"], template = templates.feedback_template),
|
| 129 |
+
llm=llm,
|
| 130 |
+
memory = st.session_state.memory,
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
def answer_call_back():
|
| 134 |
+
|
| 135 |
+
'''callback function for answering user input'''
|
| 136 |
+
|
| 137 |
+
# user input
|
| 138 |
+
human_answer = st.session_state.answer
|
| 139 |
+
st.session_state.history.append(
|
| 140 |
+
Message("human", human_answer)
|
| 141 |
+
)
|
| 142 |
+
# OpenAI answer and save to history
|
| 143 |
+
llm_answer = st.session_state.conversation.run(human_answer)
|
| 144 |
+
st.session_state.history.append(
|
| 145 |
+
Message("ai", llm_answer)
|
| 146 |
+
)
|
| 147 |
+
st.session_state.token_count += len(llm_answer.split())
|
| 148 |
+
return llm_answer
|
| 149 |
+
|
| 150 |
+
### ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
|
| 151 |
+
if jd:
|
| 152 |
+
|
| 153 |
+
initialize_session_state()
|
| 154 |
+
credit_card_placeholder = st.empty()
|
| 155 |
+
col1, col2 = st.columns(2)
|
| 156 |
+
with col1:
|
| 157 |
+
feedback = st.button("Get Interview Feedback")
|
| 158 |
+
with col2:
|
| 159 |
+
guideline = st.button("Show me interview guideline!")
|
| 160 |
+
audio = None
|
| 161 |
+
chat_placeholder = st.container()
|
| 162 |
+
answer_placeholder = st.container()
|
| 163 |
+
|
| 164 |
+
if guideline:
|
| 165 |
+
st.write(st.session_state.guideline)
|
| 166 |
+
if feedback:
|
| 167 |
+
evaluation = st.session_state.feedback.run("please give evalution regarding the interview")
|
| 168 |
+
st.markdown(evaluation)
|
| 169 |
+
st.download_button(label="Download Interview Feedback", data=evaluation, file_name="interview_feedback.txt")
|
| 170 |
+
st.stop()
|
| 171 |
+
else:
|
| 172 |
+
with answer_placeholder:
|
| 173 |
+
voice = 0
|
| 174 |
+
if voice:
|
| 175 |
+
print("voice")
|
| 176 |
+
#st.warning("An UnboundLocalError will occur if the microphone fails to record.")
|
| 177 |
+
else:
|
| 178 |
+
answer = st.chat_input("Your answer")
|
| 179 |
+
if answer:
|
| 180 |
+
st.session_state['answer'] = answer
|
| 181 |
+
audio = answer_call_back()
|
| 182 |
+
with chat_placeholder:
|
| 183 |
+
for answer in st.session_state.history:
|
| 184 |
+
if answer.origin == 'ai':
|
| 185 |
+
if audio:
|
| 186 |
+
with st.chat_message("assistant"):
|
| 187 |
+
st.write(answer.message)
|
| 188 |
+
st.write(audio)
|
| 189 |
+
else:
|
| 190 |
+
with st.chat_message("assistant"):
|
| 191 |
+
st.write(answer.message)
|
| 192 |
+
else:
|
| 193 |
+
with st.chat_message("user"):
|
| 194 |
+
st.write(answer.message)
|
| 195 |
+
|
| 196 |
+
credit_card_placeholder.caption(f"""
|
| 197 |
+
Progress: {int(len(st.session_state.history) / 30 * 100)}% completed.
|
| 198 |
+
""")
|
| 199 |
+
|
| 200 |
+
else:
|
| 201 |
+
st.info("Please submit job description to start interview.")
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
|
pages/Professional Screen.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
|
| 9 |
+
from langchain.chains import ConversationChain, RetrievalQA
|
| 10 |
+
from langchain.prompts.prompt import PromptTemplate
|
| 11 |
+
from langchain.text_splitter import NLTKTextSplitter
|
| 12 |
+
from langchain.vectorstores import FAISS
|
| 13 |
+
import nltk
|
| 14 |
+
from prompts.prompts import templates
|
| 15 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 16 |
+
import getpass
|
| 17 |
+
import os
|
| 18 |
+
from langchain_google_genai import GoogleGenerativeAIEmbeddings
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
if "GOOGLE_API_KEY" not in os.environ:
|
| 22 |
+
os.environ["GOOGLE_API_KEY"] = "AIzaSyCA4__JMC_ZIQ9xQegIj5LOMLhSSrn3pMw"
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
st.title("Professional Screen")
|
| 27 |
+
jd = st.text_area("Please enter the job description here (If you don't have one, enter keywords, such as PostgreSQL or Python instead): ")
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
@dataclass
|
| 31 |
+
class Message:
|
| 32 |
+
"""class for keeping track of interview history."""
|
| 33 |
+
origin: Literal["human", "ai"]
|
| 34 |
+
message: str
|
| 35 |
+
|
| 36 |
+
def save_vector(text):
|
| 37 |
+
"""embeddings"""
|
| 38 |
+
|
| 39 |
+
nltk.download('punkt')
|
| 40 |
+
text_splitter = NLTKTextSplitter()
|
| 41 |
+
texts = text_splitter.split_text(text)
|
| 42 |
+
# Create emebeddings
|
| 43 |
+
embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
|
| 44 |
+
docsearch = FAISS.from_texts(texts, embeddings)
|
| 45 |
+
return docsearch
|
| 46 |
+
|
| 47 |
+
def initialize_session_state_jd():
|
| 48 |
+
""" initialize session states """
|
| 49 |
+
if 'jd_docsearch' not in st.session_state:
|
| 50 |
+
st.session_state.jd_docserch = save_vector(jd)
|
| 51 |
+
if 'jd_retriever' not in st.session_state:
|
| 52 |
+
st.session_state.jd_retriever = st.session_state.jd_docserch.as_retriever(search_type="similarity")
|
| 53 |
+
if 'jd_chain_type_kwargs' not in st.session_state:
|
| 54 |
+
Interview_Prompt = PromptTemplate(input_variables=["context", "question"],
|
| 55 |
+
template=templates.jd_template)
|
| 56 |
+
st.session_state.jd_chain_type_kwargs = {"prompt": Interview_Prompt}
|
| 57 |
+
if 'jd_memory' not in st.session_state:
|
| 58 |
+
st.session_state.jd_memory = ConversationBufferMemory()
|
| 59 |
+
# interview history
|
| 60 |
+
if "jd_history" not in st.session_state:
|
| 61 |
+
st.session_state.jd_history = []
|
| 62 |
+
st.session_state.jd_history.append(Message("ai",
|
| 63 |
+
"Hello, Welcome to the interview. I am your interviewer today. I will ask you professional questions regarding the job description you submitted."
|
| 64 |
+
"Please start by introducting a little bit about yourself. Note: The maximum length of your answer is 4097 tokens!"))
|
| 65 |
+
# token count
|
| 66 |
+
if "token_count" not in st.session_state:
|
| 67 |
+
st.session_state.token_count = 0
|
| 68 |
+
if "jd_guideline" not in st.session_state:
|
| 69 |
+
llm = ChatGoogleGenerativeAI(
|
| 70 |
+
model="gemini-pro")
|
| 71 |
+
st.session_state.jd_guideline = RetrievalQA.from_chain_type(
|
| 72 |
+
llm=llm,
|
| 73 |
+
chain_type_kwargs=st.session_state.jd_chain_type_kwargs, chain_type='stuff',
|
| 74 |
+
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")
|
| 75 |
+
# llm chain and memory
|
| 76 |
+
if "jd_screen" not in st.session_state:
|
| 77 |
+
llm = ChatGoogleGenerativeAI(
|
| 78 |
+
model="gemini-pro")
|
| 79 |
+
PROMPT = PromptTemplate(
|
| 80 |
+
input_variables=["history", "input"],
|
| 81 |
+
template="""I want you to act as an interviewer strictly following the guideline in the current conversation.
|
| 82 |
+
Candidate has no idea what the guideline is.
|
| 83 |
+
Ask me questions and wait for my answers. Do not write explanations.
|
| 84 |
+
Ask question like a real person, only one question at a time.
|
| 85 |
+
Do not ask the same question.
|
| 86 |
+
Do not repeat the question.
|
| 87 |
+
Do ask follow-up questions if necessary.
|
| 88 |
+
You name is GPTInterviewer.
|
| 89 |
+
I want you to only reply as an interviewer.
|
| 90 |
+
Do not write all the conversation at once.
|
| 91 |
+
If there is an error, point it out.
|
| 92 |
+
|
| 93 |
+
Current Conversation:
|
| 94 |
+
{history}
|
| 95 |
+
|
| 96 |
+
Candidate: {input}
|
| 97 |
+
AI: """)
|
| 98 |
+
|
| 99 |
+
st.session_state.jd_screen = ConversationChain(prompt=PROMPT, llm=llm,
|
| 100 |
+
memory=st.session_state.jd_memory)
|
| 101 |
+
if 'jd_feedback' not in st.session_state:
|
| 102 |
+
llm = ChatGoogleGenerativeAI(
|
| 103 |
+
model="gemini-pro")
|
| 104 |
+
st.session_state.jd_feedback = ConversationChain(
|
| 105 |
+
prompt=PromptTemplate(input_variables=["history", "input"], template=templates.feedback_template),
|
| 106 |
+
llm=llm,
|
| 107 |
+
memory=st.session_state.jd_memory,
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
def answer_call_back():
|
| 111 |
+
formatted_history = []
|
| 112 |
+
for message in st.session_state.jd_history:
|
| 113 |
+
if message.origin == "human":
|
| 114 |
+
formatted_message = {"speaker": "user", "text": message.message}
|
| 115 |
+
else:
|
| 116 |
+
formatted_message = {"speaker": "assistant", "text": message.message}
|
| 117 |
+
formatted_history.append(formatted_message)
|
| 118 |
+
|
| 119 |
+
user_answer = st.session_state.get('answer', '')
|
| 120 |
+
|
| 121 |
+
answer = st.session_state.jd_screen.run(input=user_answer, history=formatted_history)
|
| 122 |
+
|
| 123 |
+
if user_answer:
|
| 124 |
+
st.session_state.jd_history.append(Message("human", user_answer))
|
| 125 |
+
if answer:
|
| 126 |
+
st.session_state.jd_history.append(Message("ai", answer))
|
| 127 |
+
|
| 128 |
+
return answer
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
if jd:
|
| 132 |
+
# initialize session states
|
| 133 |
+
initialize_session_state_jd()
|
| 134 |
+
#st.write(st.session_state.jd_guideline)
|
| 135 |
+
credit_card_placeholder = st.empty()
|
| 136 |
+
col1, col2 = st.columns(2)
|
| 137 |
+
with col1:
|
| 138 |
+
feedback = st.button("Get Interview Feedback")
|
| 139 |
+
with col2:
|
| 140 |
+
guideline = st.button("Show me interview guideline!")
|
| 141 |
+
chat_placeholder = st.container()
|
| 142 |
+
answer_placeholder = st.container()
|
| 143 |
+
audio = None
|
| 144 |
+
# if submit email adress, get interview feedback imediately
|
| 145 |
+
if guideline:
|
| 146 |
+
st.write(st.session_state.jd_guideline)
|
| 147 |
+
if feedback:
|
| 148 |
+
evaluation = st.session_state.jd_feedback.run("please give evalution regarding the interview")
|
| 149 |
+
st.markdown(evaluation)
|
| 150 |
+
st.download_button(label="Download Interview Feedback", data=evaluation, file_name="interview_feedback.txt")
|
| 151 |
+
st.stop()
|
| 152 |
+
else:
|
| 153 |
+
with answer_placeholder:
|
| 154 |
+
voice = 0
|
| 155 |
+
if voice:
|
| 156 |
+
print(voice)
|
| 157 |
+
else:
|
| 158 |
+
answer = st.chat_input("Your answer")
|
| 159 |
+
if answer:
|
| 160 |
+
st.session_state['answer'] = answer
|
| 161 |
+
audio = answer_call_back()
|
| 162 |
+
with chat_placeholder:
|
| 163 |
+
for answer in st.session_state.jd_history:
|
| 164 |
+
if answer.origin == 'ai':
|
| 165 |
+
if audio:
|
| 166 |
+
with st.chat_message("assistant"):
|
| 167 |
+
st.write(answer.message)
|
| 168 |
+
st.write(audio)
|
| 169 |
+
else:
|
| 170 |
+
with st.chat_message("assistant"):
|
| 171 |
+
st.write(answer.message)
|
| 172 |
+
else:
|
| 173 |
+
with st.chat_message("user"):
|
| 174 |
+
st.write(answer.message)
|
| 175 |
+
|
| 176 |
+
credit_card_placeholder.caption(f"""
|
| 177 |
+
Progress: {int(len(st.session_state.jd_history) / 30 * 100)}% completed.""")
|
| 178 |
+
else:
|
| 179 |
+
st.info("Please submit a job description to start the interview.")
|
pages/Resume Screen.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# langchain: https://python.langchain.com/
|
| 2 |
+
from dataclasses import dataclass
|
| 3 |
+
import streamlit as st
|
| 4 |
+
from langchain.callbacks import get_openai_callback
|
| 5 |
+
from langchain.memory import ConversationBufferMemory
|
| 6 |
+
from langchain.chains import RetrievalQA, ConversationChain
|
| 7 |
+
from langchain.prompts.prompt import PromptTemplate
|
| 8 |
+
from prompts.prompts import templates
|
| 9 |
+
from typing import Literal
|
| 10 |
+
from langchain.vectorstores import FAISS
|
| 11 |
+
from langchain.text_splitter import NLTKTextSplitter
|
| 12 |
+
from PyPDF2 import PdfReader
|
| 13 |
+
from prompts.prompt_selector import prompt_sector
|
| 14 |
+
from streamlit_lottie import st_lottie
|
| 15 |
+
import json
|
| 16 |
+
from IPython.display import Audio
|
| 17 |
+
import nltk
|
| 18 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
| 19 |
+
import getpass
|
| 20 |
+
import os
|
| 21 |
+
from langchain_google_genai import GoogleGenerativeAIEmbeddings
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
if "GOOGLE_API_KEY" not in os.environ:
|
| 25 |
+
os.environ["GOOGLE_API_KEY"] = "AIzaSyCA4__JMC_ZIQ9xQegIj5LOMLhSSrn3pMw"
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
st.title("Resume Screen")
|
| 30 |
+
|
| 31 |
+
st.session_state.history = []
|
| 32 |
+
|
| 33 |
+
position = st.text_input("Select the position you are applying for :")
|
| 34 |
+
resume = st.file_uploader("Upload your resume", type=["pdf"])
|
| 35 |
+
|
| 36 |
+
#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.")
|
| 37 |
+
|
| 38 |
+
@dataclass
|
| 39 |
+
class Message:
|
| 40 |
+
"""Class for keeping track of interview history."""
|
| 41 |
+
origin: Literal["human", "ai"]
|
| 42 |
+
message: str
|
| 43 |
+
|
| 44 |
+
def save_vector(resume):
|
| 45 |
+
"""embeddings"""
|
| 46 |
+
nltk.download('punkt')
|
| 47 |
+
pdf_reader = PdfReader(resume)
|
| 48 |
+
text = ""
|
| 49 |
+
for page in pdf_reader.pages:
|
| 50 |
+
text += page.extract_text()
|
| 51 |
+
# Split the document into chunks
|
| 52 |
+
text_splitter = NLTKTextSplitter()
|
| 53 |
+
texts = text_splitter.split_text(text)
|
| 54 |
+
embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
|
| 55 |
+
docsearch = FAISS.from_texts(texts, embeddings)
|
| 56 |
+
return docsearch
|
| 57 |
+
|
| 58 |
+
def initialize_session_state_resume():
|
| 59 |
+
# convert resume to embeddings
|
| 60 |
+
if 'docsearch' not in st.session_state:
|
| 61 |
+
st.session_state.docserch = save_vector(resume)
|
| 62 |
+
# retriever for resume screen
|
| 63 |
+
if 'retriever' not in st.session_state:
|
| 64 |
+
st.session_state.retriever = st.session_state.docserch.as_retriever(search_type="similarity")
|
| 65 |
+
# prompt for retrieving information
|
| 66 |
+
if 'chain_type_kwargs' not in st.session_state:
|
| 67 |
+
st.session_state.chain_type_kwargs = prompt_sector(position, templates)
|
| 68 |
+
# interview history
|
| 69 |
+
if "resume_history" not in st.session_state:
|
| 70 |
+
st.session_state.resume_history = []
|
| 71 |
+
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!"))
|
| 72 |
+
# token count
|
| 73 |
+
if "token_count" not in st.session_state:
|
| 74 |
+
st.session_state.token_count = 0
|
| 75 |
+
# memory buffer for resume screen
|
| 76 |
+
if "resume_memory" not in st.session_state:
|
| 77 |
+
st.session_state.resume_memory = ConversationBufferMemory(human_prefix = "Candidate: ", ai_prefix = "Interviewer")
|
| 78 |
+
# guideline for resume screen
|
| 79 |
+
if "resume_guideline" not in st.session_state:
|
| 80 |
+
llm = ChatGoogleGenerativeAI(
|
| 81 |
+
model="gemini-pro")
|
| 82 |
+
|
| 83 |
+
st.session_state.resume_guideline = RetrievalQA.from_chain_type(
|
| 84 |
+
llm=llm,
|
| 85 |
+
chain_type_kwargs=st.session_state.chain_type_kwargs, chain_type='stuff',
|
| 86 |
+
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")
|
| 87 |
+
# llm chain for resume screen
|
| 88 |
+
if "resume_screen" not in st.session_state:
|
| 89 |
+
llm = ChatGoogleGenerativeAI(
|
| 90 |
+
model="gemini-pro")
|
| 91 |
+
|
| 92 |
+
PROMPT = PromptTemplate(
|
| 93 |
+
input_variables=["history", "input"],
|
| 94 |
+
template= """I want you to act as an interviewer strictly following the guideline in the current conversation.
|
| 95 |
+
|
| 96 |
+
Ask me questions and wait for my answers like a human. Do not write explanations.
|
| 97 |
+
Candidate has no assess to the guideline.
|
| 98 |
+
Only ask one question at a time.
|
| 99 |
+
Do ask follow-up questions if you think it's necessary.
|
| 100 |
+
Do not ask the same question.
|
| 101 |
+
Do not repeat the question.
|
| 102 |
+
Candidate has no assess to the guideline.
|
| 103 |
+
You name is GPTInterviewer.
|
| 104 |
+
I want you to only reply as an interviewer.
|
| 105 |
+
Do not write all the conversation at once.
|
| 106 |
+
Candiate has no assess to the guideline.
|
| 107 |
+
|
| 108 |
+
Current Conversation:
|
| 109 |
+
{history}
|
| 110 |
+
|
| 111 |
+
Candidate: {input}
|
| 112 |
+
AI: """)
|
| 113 |
+
st.session_state.resume_screen = ConversationChain(prompt=PROMPT, llm = llm, memory = st.session_state.resume_memory)
|
| 114 |
+
# llm chain for generating feedback
|
| 115 |
+
if "resume_feedback" not in st.session_state:
|
| 116 |
+
llm = ChatGoogleGenerativeAI(
|
| 117 |
+
model="gemini-pro")
|
| 118 |
+
st.session_state.resume_feedback = ConversationChain(
|
| 119 |
+
prompt=PromptTemplate(input_variables=["history","input"], template=templates.feedback_template),
|
| 120 |
+
llm=llm,
|
| 121 |
+
memory=st.session_state.resume_memory,
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
def answer_call_back():
|
| 125 |
+
|
| 126 |
+
'''callback function for answering user input'''
|
| 127 |
+
|
| 128 |
+
# user input
|
| 129 |
+
human_answer = st.session_state.answer
|
| 130 |
+
st.session_state.history.append(
|
| 131 |
+
Message("human", human_answer)
|
| 132 |
+
)
|
| 133 |
+
# OpenAI answer and save to history
|
| 134 |
+
llm_answer = st.session_state.conversation.run(human_answer)
|
| 135 |
+
st.session_state.history.append(
|
| 136 |
+
Message("ai", llm_answer)
|
| 137 |
+
)
|
| 138 |
+
st.session_state.token_count += len(llm_answer.split())
|
| 139 |
+
return llm_answer
|
| 140 |
+
|
| 141 |
+
if position and resume:
|
| 142 |
+
# intialize session state
|
| 143 |
+
initialize_session_state_resume()
|
| 144 |
+
credit_card_placeholder = st.empty()
|
| 145 |
+
col1, col2 = st.columns(2)
|
| 146 |
+
with col1:
|
| 147 |
+
feedback = st.button("Get Interview Feedback")
|
| 148 |
+
with col2:
|
| 149 |
+
guideline = st.button("Show me interview guideline!")
|
| 150 |
+
chat_placeholder = st.container()
|
| 151 |
+
answer_placeholder = st.container()
|
| 152 |
+
audio = None
|
| 153 |
+
# if submit email adress, get interview feedback imediately
|
| 154 |
+
if guideline:
|
| 155 |
+
st.markdown(st.session_state.resume_guideline)
|
| 156 |
+
if feedback:
|
| 157 |
+
evaluation = st.session_state.resume_feedback.run("please give evalution regarding the interview")
|
| 158 |
+
st.markdown(evaluation)
|
| 159 |
+
st.download_button(label="Download Interview Feedback", data=evaluation, file_name="interview_feedback.txt")
|
| 160 |
+
st.stop()
|
| 161 |
+
else:
|
| 162 |
+
with answer_placeholder:
|
| 163 |
+
voice: bool = st.checkbox("I would like to speak with AI Interviewer!")
|
| 164 |
+
if voice:
|
| 165 |
+
print("voice") #st.warning("An UnboundLocalError will occur if the microphone fails to record.")
|
| 166 |
+
else:
|
| 167 |
+
answer = st.chat_input("Your answer")
|
| 168 |
+
if answer:
|
| 169 |
+
st.session_state['answer'] = answer
|
| 170 |
+
audio = answer_call_back()
|
| 171 |
+
|
| 172 |
+
with chat_placeholder:
|
| 173 |
+
for answer in st.session_state.resume_history:
|
| 174 |
+
if answer.origin == 'ai':
|
| 175 |
+
if audio:
|
| 176 |
+
with st.chat_message("assistant"):
|
| 177 |
+
st.write(answer.message)
|
| 178 |
+
st.write(audio)
|
| 179 |
+
else:
|
| 180 |
+
with st.chat_message("assistant"):
|
| 181 |
+
st.write(answer.message)
|
| 182 |
+
else:
|
| 183 |
+
with st.chat_message("user"):
|
| 184 |
+
st.write(answer.message)
|
| 185 |
+
|
| 186 |
+
credit_card_placeholder.caption(f"""
|
| 187 |
+
Progress: {int(len(st.session_state.resume_history) / 30 * 100)}% completed.""")
|
| 188 |
+
|
requirements.txt
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
langchain
|
| 2 |
+
PyPDF2
|
| 3 |
+
openai
|
| 4 |
+
wave
|
| 5 |
+
streamlit==1.25.0
|
| 6 |
+
tiktoken
|
| 7 |
+
nltk
|
| 8 |
+
#azure-cognitiveservices-speech
|
| 9 |
+
audio_recorder_streamlit
|
| 10 |
+
streamlit-option-menu
|
| 11 |
+
streamlit-lottie
|
| 12 |
+
faiss-cpu
|
| 13 |
+
boto3
|
| 14 |
+
Ipython
|