Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib_resume_builder_AIHawk/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ def __init__(self):
self.STYLES_DIRECTORY: Path = None
self.LOG_OUTPUT_FILE_PATH: Path = None
self.API_KEY: str = None
self.model: str = None
self.model_type: str = None
self.html_template = """
<!DOCTYPE html>
<html lang="en">
Expand Down
41 changes: 33 additions & 8 deletions lib_resume_builder_AIHawk/gpt_resume_job_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
from langchain_ollama import ChatOllama
from langchain_text_splitters import TokenTextSplitter
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.embeddings import OpenAIEmbeddings, HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from lib_resume_builder_AIHawk.config import global_config
from dotenv import load_dotenv
Expand All @@ -22,6 +23,7 @@
import re # For regex parsing, especially in `parse_wait_time_from_error_message`
from requests.exceptions import HTTPError as HTTPStatusError # Handling HTTP status errors
import openai
from typing import Union


load_dotenv()
Expand All @@ -48,7 +50,7 @@

class LLMLogger:

def __init__(self, llm: ChatOpenAI):
def __init__(self, llm: Union[ChatOpenAI, ChatOllama]):
self.llm = llm

@staticmethod
Expand Down Expand Up @@ -106,17 +108,17 @@ def log_request(prompts, parsed_reply: Dict[str, Dict]):

class LoggerChatModel:

def __init__(self, llm: ChatOpenAI):
def __init__(self, llm: Union[ChatOpenAI, ChatOllama]):
self.llm = llm
self.logger = logger

def __call__(self, messages: List[Dict[str, str]]) -> str:
max_retries = 15
retry_delay = 10

for attempt in range(max_retries):
try:

reply = self.llm(messages)
reply = self.llm.invoke(input=messages)
parsed_reply = self.parse_llmresult(reply)
LLMLogger.log_request(prompts=messages, parsed_reply=parsed_reply)
return reply
Expand Down Expand Up @@ -178,11 +180,34 @@ def parse_wait_time_from_error_message(self, error_message: str) -> int:


class LLMResumeJobDescription:
def __init__(self, openai_api_key, strings):
self.llm_cheap = LoggerChatModel(ChatOpenAI(model_name="gpt-4o-mini", openai_api_key=openai_api_key, temperature=0.4))
self.llm_embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
def __init__(self, openai_api_key, model, model_type, strings):
self.logger = logger

self.llm_cheap = None
self.llm_embeddings = None
self.strings = strings

if model_type == "openai":
self._initialize_openai_model(model=model, openai_api_key=openai_api_key)
elif model_type == "ollama":
self._initialize_ollama_model(model=model)
else:
raise ValueError("Invalid model type. Please choose either 'openai' or 'ollama'." +
f'instead got, model type: {model_type}, model name: {model}')

def _initialize_openai_model(self, model: str, openai_api_key: str):
self.logger.info("Initializing OpenAI model")

self.llm_cheap = LoggerChatModel(ChatOpenAI(model=model, openai_api_key=openai_api_key, temperature=0.4))
self.llm_embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)

def _initialize_ollama_model(self, model: str):
self.logger.info("Initializing Ollama model")

self.llm_cheap = LoggerChatModel(ChatOllama(model=model, temperature=0.4))
self.llm_embeddings = HuggingFaceEmbeddings()


@staticmethod
def _preprocess_template_string(template: str) -> str:
# Preprocess a template string to remove unnecessary indentation.
Expand Down
6 changes: 5 additions & 1 deletion lib_resume_builder_AIHawk/manager_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@
import webbrowser

class FacadeManager:
def __init__(self, api_key, style_manager, resume_generator, resume_object, log_path):
def __init__(self, api_key, model, model_type, style_manager, resume_generator, resume_object, log_path):
# Ottieni il percorso assoluto della directory della libreria
lib_directory = Path(__file__).resolve().parent
global_config.STRINGS_MODULE_RESUME_PATH = lib_directory / "resume_prompt/strings_feder-cr.py"
global_config.STRINGS_MODULE_RESUME_JOB_DESCRIPTION_PATH = lib_directory / "resume_job_description_prompt/strings_feder-cr.py"
global_config.STRINGS_MODULE_NAME = "strings_feder_cr"
global_config.STYLES_DIRECTORY = lib_directory / "resume_style"
global_config.LOG_OUTPUT_FILE_PATH = log_path

global_config.API_KEY = api_key
global_config.model = model
global_config.model_type = model_type

self.style_manager = style_manager
self.style_manager.set_styles_directory(global_config.STYLES_DIRECTORY)
self.resume_generator = resume_generator
Expand Down
6 changes: 3 additions & 3 deletions lib_resume_builder_AIHawk/resume_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ def _create_resume(self, gpt_answerer: Any, style_path, temp_html_path):

def create_resume(self, style_path, temp_html_file):
strings = load_module(global_config.STRINGS_MODULE_RESUME_PATH, global_config.STRINGS_MODULE_NAME)
gpt_answerer = LLMResumer(global_config.API_KEY, strings)
gpt_answerer = LLMResumeJobDescription(global_config.API_KEY, global_config.model, global_config.model_type, strings)
self._create_resume(gpt_answerer, style_path, temp_html_file)

def create_resume_job_description_url(self, style_path: str, url_job_description: str, temp_html_path):
strings = load_module(global_config.STRINGS_MODULE_RESUME_JOB_DESCRIPTION_PATH, global_config.STRINGS_MODULE_NAME)
gpt_answerer = LLMResumeJobDescription(global_config.API_KEY, strings)
gpt_answerer = LLMResumeJobDescription(global_config.API_KEY, global_config.model, global_config.model_type, strings)
gpt_answerer.set_job_description_from_url(url_job_description)
self._create_resume(gpt_answerer, style_path, temp_html_path)

def create_resume_job_description_text(self, style_path: str, job_description_text: str, temp_html_path):
strings = load_module(global_config.STRINGS_MODULE_RESUME_JOB_DESCRIPTION_PATH, global_config.STRINGS_MODULE_NAME)
gpt_answerer = LLMResumeJobDescription(global_config.API_KEY, strings)
gpt_answerer = LLMResumeJobDescription(global_config.API_KEY, global_config.model, global_config.model_type, strings)
gpt_answerer.set_job_description_from_text(job_description_text)
self._create_resume(gpt_answerer, style_path, temp_html_path)
6 changes: 4 additions & 2 deletions lib_resume_builder_AIHawk/unit-test/pdf_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import unittest
from unittest.mock import patch, MagicMock
from lib_resume_builder_AIHawk import Resume, ResumeGenerator, StyleManager, FacadeManager
from lib_resume_builder_AIHawk import ResumeGenerator, StyleManager, FacadeManager, Resume
from pathlib import Path
import base64
import yaml
Expand All @@ -22,6 +22,8 @@ def setUp(self):

# Extract necessary data
self.llm_api_key = self.secrets['llm_api_key']
self.model = self.config['llm_model']
self.model_type = self.config['llm_model_type']
self.output_path = Path("data_folder/output")

self.plain_text_resume = yaml.dump(self.plain_text_resume, default_flow_style=False)
Expand All @@ -32,7 +34,7 @@ def setUp(self):
print(self.plain_text_resume)
self.resume_object = Resume(self.plain_text_resume)
self.resume_generator_manager = FacadeManager(
self.llm_api_key, self.style_manager, self.resume_generator, self.resume_object, self.output_path
self.llm_api_key, self.model, self.model_type, self.style_manager, self.resume_generator, self.resume_object, self.output_path
)
os.system('cls' if os.name == 'nt' else 'clear')
# Ensure style is selected
Expand Down
6 changes: 3 additions & 3 deletions lib_resume_builder_AIHawk/unit-test/yaml_example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ companyBlacklist:

titleBlacklist:

llm_model_type: openai
llm_model: 'gpt-4o'
llm_api_url: https://api.openai.com/v1
llm_model_type: ollama
llm_model: 'llama3.2:1b'
llm_api_url: 'http://127.0.0.1:11434/'
159 changes: 117 additions & 42 deletions lib_resume_builder_AIHawk/unit-test/yaml_example/plain_text_resume.yaml
Original file line number Diff line number Diff line change
@@ -1,64 +1,139 @@
personal_information:
name: John
surname: Doe
date_of_birth: "1990-01-01"
country: USA
city: New York
address: 123 Main St
name: "solid"
surname: "snake"
date_of_birth: "12/01/1861"
country: "Ireland"
city: "Dublin"
zip_code: "520123"
address: "12 Fox road"
phone_prefix: "+1"
phone: "123-456-7890"
email: john.doe@example.com
linkedin: https://linkedin.com/in/johndoe
github: https://github.com/johndoe
phone: "7819117091"
email: "hi@gmail.com"
github: "https://github.com/lol"
linkedin: "https://www.linkedin.com/in/thezucc/"


education_details:
- degree: B.Sc
university: NYU
gpa: "3.8"
graduation_year: 2012
field_of_study: Computer Science
exam: []
- education_level: "Master's Degree"
institution: "Bob academy"
field_of_study: "Bobs Engineering"
final_evaluation_grade: "4.0"
year_of_completion: "2023"
start_date: "2022"
additional_info:
exam:
Algorithms: "A"
Linear Algebra: "A"
Database Systems: "A"
Operating Systems: "A-"
Web Development: "A"

experience_details:
- position: Software Engineer
company: XYZ Corp
employment_period: 2012-2015
location: New York, USA
industry: IT
- position: "X"
company: "Y."
employment_period: "06/2019 - Present"
location: "San Francisco, CA"
industry: "Technology"
key_responsibilities:
- task: Developed web applications
- responsibility: "Developed web applications using React and Node.js"
- responsibility: "Collaborated with cross-functional teams to design and implement new features"
- responsibility: "Troubleshot and resolved complex software issues"
skills_acquired:
- Python
- JavaScript
- "React"
- "Node.js"
- "Software Troubleshooting"
- position: "Software Developer"
company: "Innovatech"
employment_period: "06/2015 - 12/2017"
location: "Milan, Italy"
industry: "Technology"
key_responsibilities:
- responsibility: "Developed and maintained web applications using modern technologies"
- responsibility: "Collaborated with UX/UI designers to enhance user experience"
- responsibility: "Implemented automated testing procedures to ensure code quality"
skills_acquired:
- "Web development"
- "User experience design"
- "Automated testing"
- position: "Junior Developer"
company: "StartUp Hub"
employment_period: "01/2014 - 05/2015"
location: "Florence, Italy"
industry: "Startups"
key_responsibilities:
- responsibility: "Assisted in the development of mobile applications and web platforms"
- responsibility: "Participated in code reviews and contributed to software design discussions"
- responsibility: "Resolved bugs and implemented feature enhancements"
skills_acquired:
- "Mobile app development"
- "Code reviews"
- "Bug fixing"
projects:
- name: Project A
description: A web application
link: https://github.com/johndoe/project-a
- name: "X"
description: "Y blah blah blah "
link: "https://github.com/haveagoodday"



achievements:
- name: Award A
description: Best Employee of the Year
- name: "Employee of the Month"
description: "Recognized for exceptional performance and contributions to the team."
- name: "Hackathon Winner"
description: "Won first place in a national hackathon competition."

certifications:
- Certified Python Developer
#- "Certified Scrum Master"
#- "AWS Certified Solutions Architect"

languages:
- language: English
proficiency: Fluent
- language: "English"
proficiency: "Fluent"
- language: "Spanish"
proficiency: "Intermediate"

interests:
- Programming
- "Machine Learning"
- "Cybersecurity"
- "Open Source Projects"
- "Digital Marketing"
- "Entrepreneurship"

availability:
notice_period: "1 month"
notice_period: "2 weeks"

salary_expectations:
salary_range_usd: "100,000-120,000"
salary_range_usd: "90000 - 110000"

self_identification:
gender: Male
pronouns: He/Him
gender: "Female"
pronouns: "She/Her"
veteran: "No"
disability: "No"
ethnicity: Caucasian
ethnicity: "Asian"

legal_authorization:
eu_work_authorization: "No"
eu_work_authorization: "Yes"
us_work_authorization: "Yes"
requires_us_visa: "No"
requires_us_sponsorship: "No"
requires_us_sponsorship: "Yes"
requires_eu_visa: "No"
legally_allowed_to_work_in_eu: "No"
legally_allowed_to_work_in_eu: "Yes"
legally_allowed_to_work_in_us: "Yes"
requires_eu_sponsorship: "No"
requires_eu_sponsorship: "No"
canada_work_authorization: "Yes"
requires_canada_visa: "No"
legally_allowed_to_work_in_canada: "Yes"
requires_canada_sponsorship: "No"
uk_work_authorization: "Yes"
requires_uk_visa: "No"
legally_allowed_to_work_in_uk: "Yes"
requires_uk_sponsorship: "No"


work_preferences:
remote_work: "Yes"
in_person_work: "Yes"
open_to_relocation: "Yes"
willing_to_complete_assessments: "Yes"
willing_to_undergo_drug_tests: "Yes"
willing_to_undergo_background_checks: "Yes"
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name='lib_resume_builder_AIHawk',
version='0.1',
version='0.6.0',
description='A package to generate AI-assisted resumes using GPT models',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
Expand All @@ -15,6 +15,7 @@
'langchain-community',
'langchain-core',
'langchain-openai',
'langchain-ollama',
'langchain-text-splitters',
'langsmith',
'openai',
Expand Down