Res-Eval-Demo / app.py
Macmill's picture
Update app.py
f91c0c8 verified
import gradio as gr
import os
import chromadb
from dotenv import load_dotenv
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core.storage.storage_context import StorageContext
from llama_index.llms.google_genai import GoogleGenAI
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
load_dotenv()
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
AVAILABLE_JOBS = [
{
"id": "SWE-001",
"title": "Junior Software Engineer",
"company": "InnovateTech Solutions",
"description": "Seeking a motivated software engineer to help build our next-generation web applications. You will work with a dynamic team on both front-end and back-end development.",
"skills": "Python, JavaScript, React, Django, SQL, Git, REST APIs"
},
{
"id": "DA-002",
"title": "Junior Data Analyst",
"company": "Data Insights Inc.",
"description": "We are looking for a detail-oriented data analyst to help us make sense of customer data. You will be responsible for cleaning, analyzing, and visualizing data to provide actionable insights.",
"skills": "Python, Pandas, NumPy, SQL, Tableau, Power BI, Statistics"
},
{
"id": "DEVOPS-003",
"title": "Associate DevOps Engineer",
"company": "CloudScape Services",
"description": "Join our infrastructure team to help automate and streamline our operations and processes. You will work with CI/CD pipelines, cloud services, and containerization technologies.",
"skills": "AWS, Docker, Kubernetes, Jenkins, Terraform, Bash Scripting, Python"
},
]
llm = None
embed_model = None
DB_PATH = "./chroma_placement_db"
def initialize_models():
"""Initializes the LLM and embedding models at once."""
global llm, embed_model
if llm is None:
if not GEMINI_API_KEY:
raise ValueError("GEMINI_API_KEY not found.")
llm = GoogleGenAI(model_name="models/gemini-2.0-flash", api_key=GEMINI_API_KEY)
if embed_model is None:
embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
# --- 2. Core RAG Functions ---
def run_rag_pipeline(resume_file, query):
"""
A generic RAG pipeline that uses a single, persistent ChromaDB instance
and overwrites the collection on each run to ensure data is fresh.
"""
db = chromadb.PersistentClient(path=DB_PATH)
chroma_collection = db.get_or_create_collection(name="resume_eval_collection")
chroma_collection.delete(where={"doc_id": {"$ne": "dummy"}})
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
documents = SimpleDirectoryReader(input_files=[resume_file.name]).load_data()
index = VectorStoreIndex.from_documents(
documents, storage_context=storage_context, embed_model=embed_model, llm=llm
)
query_engine = index.as_query_engine(similarity_top_k=3, llm=llm)
response = query_engine.query(query)
return str(response)
def evaluate_resume(job_description, resume_file):
"""Function for the 'Resume Evaluator' tab."""
yield {eval_output: gr.Markdown(""), eval_button: gr.Button(value="Processing...", interactive=False)}
if not job_description.strip() or resume_file is None:
yield {eval_output: gr.Markdown("## Error\nPlease provide both a Job Description and a Resume file."),
eval_button: gr.Button(value="Evaluate Resume", interactive=True)}
return
try:
initialize_models()
except ValueError as e:
yield {eval_output: gr.Markdown(f"## Error\n{e}"),
eval_button: gr.Button(value="Evaluate Resume", interactive=True)}
return
custom_query = f"""
As an expert AI HR Analyst, evaluate the provided resume against the following job description.
Job Description:
---
{job_description}
---
Provide a structured analysis:
1. Overall Match Score: Percentage.
2. Strengths: Bulleted list.
3. Weaknesses: Bulleted list.
4. Feedback: Actionable advice.
"""
try:
final_evaluation = run_rag_pipeline(resume_file, custom_query)
except Exception as e:
final_evaluation = f"## Error\nAn unexpected error occurred: {e}"
yield {eval_output: gr.Markdown(final_evaluation),
eval_button: gr.Button(value="Evaluate Resume", interactive=True)}
def recommend_jobs(resume_file):
"""Function for the 'Job Recommender' (Placement Portal) tab."""
yield {recommend_output: gr.Markdown(""),
recommend_button: gr.Button(value="Finding Matches...", interactive=False)}
if resume_file is None:
yield {recommend_output: gr.Markdown("## Error\nPlease upload your Resume file."),
recommend_button: gr.Button(value="Find Best Job Matches", interactive=True)}
return
try:
initialize_models()
except ValueError as e:
yield {recommend_output: gr.Markdown(f"## Error\n{e}"),
recommend_button: gr.Button(value="Find Best Job Matches", interactive=True)}
return
jobs_list_str = "\n\n".join(
[f"ID: {j['id']}, Title: {j['title']}, Company: {j['company']}, Required Skills: {j['skills']}" for j in
AVAILABLE_JOBS])
custom_query = f"""
You are an expert AI Career Counselor. Based on the provided resume, analyze the candidate's skills and experience.
Then, compare them against the following list of available jobs and recommend the top 2-3 best-fitting roles.
For each recommendation, provide a brief, one-sentence explanation of WHY it's a good match.
Available Jobs List:
---
{jobs_list_str}
---
Your response should be a Markdown list of the recommended jobs.
"""
try:
final_recommendation = run_rag_pipeline(resume_file, custom_query)
except Exception as e:
final_recommendation = f"## Error\nAn unexpected error occurred: {e}"
yield {recommend_output: gr.Markdown(final_recommendation),
recommend_button: gr.Button(value="Find Best Job Matches", interactive=True)}
# --- 3. Gradio User Interface ---
with gr.Blocks(theme=gr.themes.Soft(), title="AI Resume & Placement Portal") as iface:
gr.Markdown("# AI Resume Evaluator & Placement Portal")
with gr.Tabs():
with gr.TabItem("Resume Evaluator"):
gr.Markdown(
"Upload your resume and paste a job description to get a detailed evaluation for a specific role.")
with gr.Row():
eval_job_desc_input = gr.Textbox(label="Job Description", placeholder="Paste job description...",
lines=20, scale=1)
eval_resume_input = gr.File(label="Upload Your Resume", file_types=[".pdf", ".docx", ".txt"], scale=1)
eval_button = gr.Button("Evaluate Resume", variant="primary")
gr.Markdown("--- \n ## AI-Powered Evaluation")
eval_output = gr.Markdown(label="Evaluation feedback")
with gr.TabItem("Job Recommender"):
gr.Markdown(
"Upload your resume, and the AI will act as a placement portal, recommending the best-fitting jobs from our internal list.")
with gr.Column():
recommend_resume_input = gr.File(label="Upload Your Resume", file_types=[".pdf", ".docx", ".txt"])
recommend_button = gr.Button("Find Best Job Matches", variant="primary")
gr.Markdown("--- \n ## Recommended Job Matches")
recommend_output = gr.Markdown(label="Job recommendations")
eval_button.click(
fn=evaluate_resume,
inputs=[eval_job_desc_input, eval_resume_input],
outputs=[eval_output, eval_button]
)
recommend_button.click(
fn=recommend_jobs,
inputs=[recommend_resume_input],
outputs=[recommend_output, recommend_button]
)
if __name__ == "__main__":
iface.launch()