from openai import OpenAI from dotenv import load_dotenv import os import json import pandas as pd import fitz from langchain_core.messages import HumanMessage, AIMessage, BaseMessage from typing import List,Tuple,TypedDict, Annotated, Sequence import streamlit as st import numpy as np import tiktoken import tkinter as tk from tkinter import filedialog, simpledialog import re from langgraph.graph import StateGraph, state import operator from resume_prompt import resume_prompt from jd_prompt import jd_prompt from grading_prompt import grading_prompt from timings import timings from emails import email df = { "Name":[], "Email id":[], "Contact No.":[], "Resume_Summary": [], "Fitment":{ "Criteria": [], "Score": [], "Weightage": [], "Reasoning": [] }, "Analysis":{ "Strengths": [], "Weaknesses": [], "Risk Areas": [], }, "Score":[], "Candidate Summary":[], "Questions to ask": [], "Timing/Schedule": [], "Email_Draft":[], } load_dotenv() GROQ_API_KEY = os.getenv("GROQ_API_KEY") HELICONE_API_KEY=os.getenv("HELICONE_API_KEY") SysPromptDefault = "You are now in the role of an expert AI." pdf_list = [] jd_2 = None phase_2= False class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], operator.add] def response(message: object, model: object = "llama3-8b-8192", SysPrompt: object = SysPromptDefault, temperature: object = 0.2) -> object: """ :rtype: object """ client = OpenAI( api_key=GROQ_API_KEY, base_url="https://gateway.hconeai.com/openai/v1", default_headers={ "Helicone-Auth": f"Bearer {HELICONE_API_KEY}", "Helicone-Target-Url": "https://api.groq.com" } ) messages = [{"role": "system", "content": SysPrompt}, {"role": "user", "content": message}] response = client.chat.completions.create( model=model, messages=messages, temperature=temperature, frequency_penalty=0.2, ) return response.choices[0].message.content def extract_content(pdf_content: bytes) -> List[str]: """ Takes PDF (bytes) and returns a list of strings containing text from each page. """ pdf_doc = fitz.open(stream=pdf_content, filetype="pdf") pages_content = [] for page_number in range(pdf_doc.page_count): # Extracting text content page = pdf_doc.load_page(page_number) text_content = page.get_text("text").replace("\n", "\t") pages_content.append(text_content) pdf_doc.close() return pages_content def function_info(x): pdf_list = x["messages"] model = "llama3-70b-8192" batch_size = 3 all_responses_json = [] for i in range(0, len(pdf_list), batch_size): batch = pdf_list[i:i + batch_size] print(f"Processing batch from index {i} to {i + batch_size} ") context = "Next file\n\n".join([f"Resume File:\n\n{pdf}\n\n" for pdf in batch]) message = f"RESUMES LIST \n\n{context}\n\n" response_json = extract_json(response(message=message, model=model, SysPrompt=resume_prompt)) all_responses_json.append(response_json) for response_json in all_responses_json: for key,value in response_json.items(): print(key) df["Name"].append(value["name"]) df["Email id"].append(value["email_id"]) df["Contact No."].append(value["contact_no"]) df["Resume_Summary"].append(value["resume_summary"]) x["messages"][-1]= all_responses_json def classify_jd(job_description): model = "llama3-70b-8192" message = f"JOB_DESCRIPTION\n\n{job_description}\n\n" response_str = response(message=message, model=model, SysPrompt=jd_prompt, temperature=0) json_part = extract_json(response_str) return json_part def function_fitment(x): global df global jd_2 jd_2=classify_jd(jd_2) resumes_json={} for json_nest in x["messages"][-1]: resumes_json.update(json_nest) batch_size = 3 resume_keys = list(resumes_json.keys()) model = "llama3-70b-8192" all_responses_json = [] for i in range(0, len(resume_keys), batch_size): batch_keys = resume_keys[i:i + batch_size] print(f"Processing batch from index {i} to {i + batch_size} (keys: {batch_keys})") context = f"\n\nNEXT RESUME:\n\n".join(f"RESUME:{json.dumps(resumes_json[key], indent=4)}" for key in batch_keys) message = f"\n\nJOB_DESCRIPTION:\n\n{json.dumps(jd_2, indent=4)}\n\nRESUMES LIST:\n\n{context}" output_str = response(message=message, model=model, SysPrompt=grading_prompt, temperature=0) output = extract_json(output_str) print(f"Extracted JSON: {output}") all_responses_json.append(output) for response_json in all_responses_json: for key,value in response_json.items(): u = [] v = [] w = [] z = [] for section, values in value.items(): if section!="overall_score" and section!="summary" and section!="strengths" and section!="weaknesses" and section!="risk_areas" and section!="questions": u.append(section) v.append(values["score"]) w.append(values["weightage"]) z.append(values["reasoning"]) if section=='strenghts': df["Analysis"]["Strengths"].append(value["strengths"]) if section=='weaknesses': df["Analysis"]["Weaknesses"].append(value["weaknesses"]) if section=='risk_areas': df["Analysis"]["Risk Areas"].append(value["risk_areas"]) df["Fitment"]["Criteria"].append(u) df["Fitment"]["Score"].append(v) df["Fitment"]["Weightage"].append(w) df["Fitment"]["Reasoning"].append(z) df["Score"].append(value["overall_score"]) df["Questions to ask"].append(value["questions"]) df["Candidate Summary"].append(value["summary"]) df = flatten_dict(df) def flatten_dict(data): length = len(data['Name']) flat_data = [] for i in range(length): entry = { 'Name': data['Name'][i] if i < len(data['Name']) else None, 'Email id': data['Email id'][i] if i < len(data['Email id']) else None, 'Contact No.': data['Contact No.'][i] if i < len(data['Contact No.']) else None, 'Resume_Summary': data['Resume_Summary'][i] if i < len(data['Resume_Summary']) else None, 'Score': data['Score'][i] if i < len(data['Score']) else None, 'Candidate Summary': data['Candidate Summary'][i] if i < len(data['Candidate Summary']) else None, 'Questions to ask': data['Questions to ask'][i] if i < len(data['Questions to ask']) else None, 'Timing/Schedule': None, 'Email_Draft': None, } # Add nested fields for key in ['Criteria', 'Score', 'Weightage', 'Reasoning']: if key in data['Fitment']: if key in ['Score', 'Weightage', 'Reasoning']: v= 0 for a in data["Fitment"]["Criteria"][0]: entry[f"Fitment_{a}_{key}"]=data["Fitment"][key][i][v] if i < len(data["Fitment"][key]) else None v += 1 else: entry[f'Fitment_{key}'] = None for key in ['Strengths', 'Weaknesses', 'Risk Areas']: entry[f'Analysis_{key}'] = data['Analysis'][key][i] if i < len(data['Analysis'][key]) else None flat_data.append(entry) return pd.DataFrame(flat_data) def extract_json(response_str): """Extract the JSON part from the response string and handle comments.""" response_str = re.sub(r'//.*?\n|/\*.*?\*/', '', response_str, flags=re.DOTALL) response_str = re.sub(r'[\x00-\x1F\x7F]', '', response_str) match = re.search(r"\{.*}", response_str, re.DOTALL) if match: json_part = match.group() try: parsed_json = json.loads(json_part) return parsed_json except json.JSONDecodeError as e: print("Invalid JSON detected. Error:", e) print("JSON part:", json_part) else: print("No JSON part found in the response string.") return None st.title('Resume Shortlister') def functions_sort_timings(x): global df df = df.sort_values(by="Score", ascending=False).head(5) input_user = x["messages"][-1] context = f"USER_INPUT:{input_user}\n\nCANDIDATES DETAILS:\n\n" for index, row in df.iterrows(): context += f"Name: {row['Name']}\nResume Summary: {row['Resume_Summary']}\n\n" ans = response(message=context,SysPrompt=timings) response_json = extract_json(ans) df["Timing/Schedule"] = df["Name"].map(response_json) def function_email(x): g = x["messages"][-1] print(g) global jd_2 global df job_role = jd_2["position_role"] job_req = jd_2["requirements"] context = f"JOB DETAILS:{job_role}\nJOB REQUIREMENTS:{job_req}\n\n" for index, row in df.iterrows(): context += f"Name: {row['Name']}\nResume Summary: {row['Resume_Summary']}\n Timinings: {row['Timing/Schedule']}\n\n" ans = response(message=context,SysPrompt=email) st.write(ans) response_json = extract_json(ans) df["Email_Draft"] = df["Name"].map(response_json) workflow = StateGraph(AgentState) workflow.add_node("start",function_info) workflow.add_node("end",function_fitment) workflow.add_edge("start","end") workflow.set_entry_point("start") workflow.set_finish_point("end") app = workflow.compile() approach = StateGraph(AgentState) approach.add_node("timings_sort",functions_sort_timings) approach.add_node("emails_draft",function_email) approach.add_edge("timings_sort","emails_draft") approach.set_entry_point("timings_sort") approach.set_finish_point("emails_draft") app_1 = approach.compile() x = {} pdf = st.file_uploader("Choose a CSV file", accept_multiple_files=True) jd = st.file_uploader("Choose a jd", accept_multiple_files=False) f = st.text_input("Enter the timings for the candidates that would be sorted") if pdf and jd: if st.button("Submit"): for i in range(len(pdf)): pdf_content = pdf[i].read() pdf_list.append(extract_content(pdf_content)) jd_1 = jd.read() jd_2 = extract_content(jd_1) a = app.invoke({"messages":pdf_list}) st.dataframe(df) phase_2 = True if phase_2==True: r = app_1.invoke({"messages":[HumanMessage(f)]}) st.dataframe(df) else: st.write("Make sure you enter both the jd and resumes in pdf format")