Resume-screener / app.py
Ahmad-Moiz's picture
Update app.py
d0b1771
raw
history blame
3.91 kB
import streamlit as st
from dotenv import load_dotenv
from pathlib import Path
from typing import Any, Dict, List, Optional
from llama_index.llama_pack.base import BaseLlamaPack
from llama_index.readers import PDFReader
from llama_index.llms.base import LLM
from llama_index.llms import OpenAI
from llama_index import ServiceContext
from llama_index.schema import NodeWithScore
from llama_index.response_synthesizers import TreeSummarize
from pydantic import BaseModel
import os
import pdfplumber
import io
# Load environment variables from .env file
load_dotenv()
# Get OpenAI API key from environment variables
openai_api_key = os.getenv("OPENAI_API_KEY")
QUERY_TEMPLATE = """
You are an expert resume reviewer.
Your job is to decide if the candidate passes the resume screen given the job description and a list of criteria:
### Job Description
{job_description}
### Screening Criteria
{criteria_str}
"""
class CriteriaDecision(BaseModel):
"""The decision made based on a single criterion"""
decision: bool
reasoning: str
class ResumeScreenerDecision(BaseModel):
"""The decision made by the resume screener"""
criteria_decisions: List[CriteriaDecision]
overall_reasoning: str
overall_decision: bool
def _format_criteria_str(criteria: List[str]) -> str:
criteria_str = ""
for criterion in criteria:
criteria_str += f"- {criterion}\n"
return criteria_str
class ResumeScreenerPack(BaseLlamaPack):
def _init_(
self, job_description: str, criteria: List[str], llm: Optional[LLM] = None
) -> None:
self.reader = PDFReader()
llm = llm or OpenAI(model="gpt-4", api_key=openai_api_key)
service_context = ServiceContext.from_defaults(llm=llm)
self.synthesizer = TreeSummarize(
output_cls=ResumeScreenerDecision, service_context=service_context
)
criteria_str = _format_criteria_str(criteria)
self.query = QUERY_TEMPLATE.format(
job_description=job_description, criteria_str=criteria_str
)
def get_modules(self) -> Dict[str, Any]:
"""Get modules."""
return {"reader": self.reader, "synthesizer": self.synthesizer}
def run(self, resume_path: str, *args: Any, **kwargs: Any) -> Any:
"""Run pack."""
docs = self.reader.load_data(Path(resume_path))
output = self.synthesizer.synthesize(
query=self.query,
nodes=[NodeWithScore(node=doc, score=1.0) for doc in docs],
)
return output.response
def main():
st.title("Resume Screener App")
# Sidebar for user input
job_description = st.text_area("Job Description")
criteria = st.text_area("Screening Criteria (separate each criterion by a new line)")
uploaded_file = st.file_uploader("Upload Resume (PDF)", type=["pdf"])
if st.button("Submit"):
if job_description and criteria and uploaded_file:
resume_text = extract_text_from_pdf(uploaded_file)
screener_pack = ResumeScreenerPack(job_description=job_description, criteria=criteria.split("\n"))
with st.spinner("Analyzing the resume..."):
result = screener_pack.run(resume_text)
st.subheader("Screening Results")
st.json(result)
def extract_text_from_pdf(uploaded_file):
if uploaded_file is not None:
try:
# Read PDF content from BytesIO
uploaded_content = io.BytesIO(uploaded_file.read())
with pdfplumber.open(uploaded_content) as pdf:
text = ""
for page in pdf.pages:
text += page.extract_text()
return text
except Exception as e:
st.error(f"Error extracting text from PDF: {str(e)}")
return ""
else:
st.error("Please upload a PDF file.")
return ""
if _name_ == "_main_":
main()