Spaces:
Sleeping
Sleeping
| 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() |